* Re: [PATCH 44/50] sound: usb: caiaq: spin_lock in complete() cleanup
From: Daniel Mack @ 2013-07-11 14:06 UTC (permalink / raw)
To: Ming Lei
Cc: Greg Kroah-Hartman, linux-usb, Oliver Neukum, Alan Stern,
linux-input, linux-bluetooth, netdev, linux-wireless, linux-media,
alsa-devel, Jaroslav Kysela, Takashi Iwai
In-Reply-To: <1373533573-12272-45-git-send-email-ming.lei@canonical.com>
On 11.07.2013 11:06, Ming Lei wrote:
> Complete() will be run with interrupt enabled, so change to
> spin_lock_irqsave().
>
> Cc: Daniel Mack <zonque@gmail.com>
> Cc: Jaroslav Kysela <perex@perex.cz>
> Cc: Takashi Iwai <tiwai@suse.de>
> Cc: alsa-devel@alsa-project.org
> Signed-off-by: Ming Lei <ming.lei@canonical.com>
Sound right to me, thanks.
Acked-by: Daniel Mack <zonque@gmail.com>
> ---
> sound/usb/caiaq/audio.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
> index 7103b09..e5675ab 100644
> --- a/sound/usb/caiaq/audio.c
> +++ b/sound/usb/caiaq/audio.c
> @@ -672,10 +672,11 @@ static void read_completed(struct urb *urb)
> offset += len;
>
> if (len > 0) {
> - spin_lock(&cdev->spinlock);
> + unsigned long flags;
> + spin_lock_irqsave(&cdev->spinlock, flags);
> fill_out_urb(cdev, out, &out->iso_frame_desc[outframe]);
> read_in_urb(cdev, urb, &urb->iso_frame_desc[frame]);
> - spin_unlock(&cdev->spinlock);
> + spin_unlock_irqrestore(&cdev->spinlock, flags);
> check_for_elapsed_periods(cdev, cdev->sub_playback);
> check_for_elapsed_periods(cdev, cdev->sub_capture);
> send_it = 1;
>
^ permalink raw reply
* [PATCHv5 0/4] add master channel switch announcement support
From: Simon Wunderlich @ 2013-07-11 14:09 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Mathias Kretschmer, Simon Wunderlich
This is the 5th edition of the CSA support patch series. This patchset adds
generic channel switch support for AP. This is required for DFS operation
(e.g. Wi-Fi Alliance requires this for 802.11h certification). This will also
be required for IBSS-DFS later.
Changes from PATCHv4:
* finally remove use_chanctx check (Michal)
* squashed the chanctx channel change patch into the "main" channel switch command patch,
as channel change command now checks if a CSA is currently in operation and therefore
now depends on that.
* add tracing for nl80211 part (Johannes)
* removed BSS_CHANGED_CHANNEL (Johannes)
* fix rcu_read lock (Johannes)
* check for csa in chanctx change channel (Johannes)
* ... and a few more minor changes
The rough design is:
* userspace asks kernel to switch a channel using the new NL80211_CMD_CHANNEL_SWITCH
command. It supplies IE information for the time while staying on the old channel and
announcing the switch, and IE information for after the switch to the new channel.
* IE information contains the beacon and optionally probe responses, which should
include (E)CSA IEs for the CSA case. Furthermore an offset is provided (for beacon
and probe response) to point to the counter field within the channel switch IEs.
* The driver gets the new beacons passed and must set them, and decrement the
counter field. When it reaches 0, the channel is changed and userspace notified.
As always, any comments are appreciated.
Cheers,
Simon
Simon Wunderlich (4):
nl80211/cfg80211: add channel switch command
mac80211: add functions to duplicate a cfg80211_beacon
mac80211: add channel switch command and beacon callbacks
ath9k: enable CSA functionality in ath9k
drivers/net/wireless/ath/ath9k/ath9k.h | 2 +
drivers/net/wireless/ath/ath9k/beacon.c | 21 ++++
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/main.c | 17 +++
drivers/net/wireless/ath/ath9k/xmit.c | 2 +
include/net/cfg80211.h | 30 +++++
include/net/mac80211.h | 36 ++++++
include/uapi/linux/nl80211.h | 29 +++++
net/mac80211/cfg.c | 185 ++++++++++++++++++++++++++++++-
net/mac80211/chan.c | 58 ++++++++++
net/mac80211/driver-ops.h | 13 +++
net/mac80211/ieee80211_i.h | 18 +++
net/mac80211/iface.c | 9 ++
net/mac80211/trace.h | 26 +++++
net/mac80211/tx.c | 78 +++++++++++++
net/wireless/nl80211.c | 124 ++++++++++++++++++++-
net/wireless/rdev-ops.h | 12 ++
net/wireless/trace.h | 33 ++++++
18 files changed, 691 insertions(+), 3 deletions(-)
--
1.7.10.4
^ permalink raw reply
* [PATCHv5 1/4] nl80211/cfg80211: add channel switch command
From: Simon Wunderlich @ 2013-07-11 14:09 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1373551749-5107-1-git-send-email-siwu@hrz.tu-chemnitz.de>
To allow channel switch announcements within beacons, add
the channel switch command to nl80211/cfg80211. This is
implementation is intended for AP and (later) IBSS mode.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to PATCHv4:
* add tracing
* rename csa_ies to csa_attrs
Changes to PATCHv2:
* define csa_ies as static to save stack size
* add wiphy flag and cmd for channel_switch
* move NL80211_ATTR_CSA_C_OFF_BEACON into csa_ies
Changes to RFCv1:
* accept and pass CSA IEs and after-change IEs
* use parameter structure instead of individual parameters
---
include/net/cfg80211.h | 30 ++++++++++
include/uapi/linux/nl80211.h | 29 ++++++++++
net/wireless/nl80211.c | 124 +++++++++++++++++++++++++++++++++++++++++-
net/wireless/rdev-ops.h | 12 ++++
net/wireless/trace.h | 33 +++++++++++
5 files changed, 227 insertions(+), 1 deletion(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index aeaf6df..9aa7918 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -666,6 +666,30 @@ struct cfg80211_ap_settings {
};
/**
+ * struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offset_beacon: offset for the counter within the beacon (tail)
+ * @counter_offset_presp: offset for the counter within the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_beacon_data beacon_csa;
+ u16 counter_offset_beacon, counter_offset_presp;
+ struct cfg80211_beacon_data beacon_after;
+ bool radar_required;
+ bool block_tx;
+ u8 count;
+};
+
+/**
* enum station_parameters_apply_mask - station parameter values to apply
* @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
* @STATION_PARAM_APPLY_CAPABILITY: apply new capability
@@ -2376,6 +2400,9 @@ struct cfg80211_ops {
struct wireless_dev *wdev);
int (*set_coalesce)(struct wiphy *wiphy,
struct cfg80211_coalesce *coalesce);
+ int (*channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params);
};
/*
@@ -2441,6 +2468,8 @@ struct cfg80211_ops {
* @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
* @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
+ * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
+ * beaconing mode (AP, IBSS, Mesh, ...).
*/
enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
@@ -2465,6 +2494,7 @@ enum wiphy_flags {
WIPHY_FLAG_OFFCHAN_TX = BIT(20),
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23),
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index eb68735..8d07ee0 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -676,6 +676,16 @@
* @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
* @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
*
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ * the new channel information (Channel Switch Announcement - CSA)
+ * in the beacon for some time (as defined in the
+ * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ * new channel. Userspace provides the new channel information (using
+ * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ * other station that transmission must be blocked until the channel
+ * switch is complete.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -841,6 +851,7 @@ enum nl80211_commands {
NL80211_CMD_GET_COALESCE,
NL80211_CMD_SET_COALESCE,
+ NL80211_CMD_CHANNEL_SWITCH,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1469,6 +1480,18 @@ enum nl80211_commands {
*
* @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
*
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ * field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1771,6 +1794,12 @@ enum nl80211_attrs {
NL80211_ATTR_COALESCE_RULE,
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CSA_C_OFF_BEACON,
+ NL80211_ATTR_CSA_C_OFF_PRESP,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a9444cd..f08b058 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+ [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
};
/* policy for the key attributes */
@@ -1422,6 +1427,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (state->split) {
CMD(crit_proto_start, CRIT_PROTOCOL_START);
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+ if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+ CMD(channel_switch, CHANNEL_SWITCH);
}
#ifdef CONFIG_NL80211_TESTMODE
@@ -5611,6 +5618,113 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
return err;
}
+static int nl80211_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_csa_settings params;
+ /* csa_attrs is defined static to avoid waste of stack size - this
+ * function is called under RTNL lock, so this should not be a problem.
+ */
+ static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+ u8 radar_detect_width = 0;
+ int err;
+
+ if (!rdev->ops->channel_switch ||
+ !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ /* may add IBSS support later */
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+ !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+ return -EINVAL;
+
+ /* only important for AP, IBSS and mesh create IEs internally */
+ if (!info->attrs[NL80211_ATTR_CSA_IES])
+ return -EINVAL;
+
+ /* useless if AP is not running */
+ if (!wdev->beacon_interval)
+ return -EINVAL;
+
+ params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+
+ err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
+ if (err)
+ return err;
+
+ err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+ info->attrs[NL80211_ATTR_CSA_IES],
+ nl80211_policy);
+ if (err)
+ return err;
+
+ err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
+ if (err)
+ return err;
+
+ if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+ return -EINVAL;
+
+ params.counter_offset_beacon =
+ nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+ if (params.counter_offset_beacon > params.beacon_csa.tail_len)
+ return -EINVAL;
+
+ /* sanity check - counters should be the same */
+ if (params.beacon_csa.tail[params.counter_offset_beacon] !=
+ params.count)
+ return -EINVAL;
+
+ if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+ params.counter_offset_presp =
+ nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+ if (params.counter_offset_presp >
+ params.beacon_csa.probe_resp_len)
+ return -EINVAL;
+
+ if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
+ params.count)
+ return -EINVAL;
+ }
+
+ err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
+ if (err)
+ return err;
+
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
+ return -EINVAL;
+
+ err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
+ if (err < 0)
+ return err;
+
+ if (err)
+ radar_detect_width = BIT(params.chandef.width);
+ else
+ radar_detect_width = 0;
+
+ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+ params.chandef.chan,
+ CHAN_MODE_SHARED,
+ radar_detect_width);
+ if (err)
+ return err;
+
+ if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+ params.block_tx = true;
+
+ return rdev_channel_switch(rdev, dev, ¶ms);
+}
+
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
u32 seq, int flags,
struct cfg80211_registered_device *rdev,
@@ -9359,7 +9473,15 @@ static struct genl_ops nl80211_ops[] = {
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WIPHY |
NL80211_FLAG_NEED_RTNL,
- }
+ },
+ {
+ .cmd = NL80211_CMD_CHANNEL_SWITCH,
+ .doit = nl80211_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 9f15f0a..de870d4 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -923,4 +923,16 @@ static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev,
trace_rdev_return_void(&rdev->wiphy);
}
+static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ int ret;
+
+ trace_rdev_channel_switch(&rdev->wiphy, dev, params);
+ ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 09af6eb..f0ebdcd 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1841,6 +1841,39 @@ TRACE_EVENT(rdev_crit_proto_stop,
WIPHY_PR_ARG, WDEV_PR_ARG)
);
+TRACE_EVENT(rdev_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_csa_settings *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(u16, counter_offset_beacon)
+ __field(u16, counter_offset_presp)
+ __field(bool, radar_required)
+ __field(bool, block_tx)
+ __field(u8, count)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(¶ms->chandef);
+ __entry->counter_offset_beacon = params->counter_offset_beacon;
+ __entry->counter_offset_presp = params->counter_offset_presp;
+ __entry->radar_required = params->radar_required;
+ __entry->block_tx = params->block_tx;
+ __entry->count = params->count;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", block_tx: %d, count: %u, radar_required: %d"
+ ", counter offsets (beacon/presp): %u/%u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->block_tx, __entry->count, __entry->radar_required,
+ __entry->counter_offset_beacon,
+ __entry->counter_offset_presp)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
--
1.7.10.4
^ permalink raw reply related
* [PATCHv5 2/4] mac80211: add functions to duplicate a cfg80211_beacon
From: Simon Wunderlich @ 2013-07-11 14:09 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1373551749-5107-1-git-send-email-siwu@hrz.tu-chemnitz.de>
For the channel switch announcement, it is required to set a new beacon
after the driver arrived on the new channel. The new beacon will be set
by nl80211, but is required to duplicate the beacon as the original
memory within the netlink message will be gone.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to PATCHv2:
* allocate only one memory chunk
---
net/mac80211/cfg.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b82fff6..8e6c935 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2775,6 +2775,62 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
return 0;
}
+static struct cfg80211_beacon_data *
+cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+{
+ struct cfg80211_beacon_data *new_beacon;
+ u8 *pos;
+ int len;
+
+ len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
+ beacon->proberesp_ies_len + beacon->assocresp_ies_len +
+ beacon->probe_resp_len;
+
+ new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+ if (!new_beacon)
+ return NULL;
+
+ pos = (u8 *)(new_beacon + 1);
+ if (beacon->head_len) {
+ new_beacon->head_len = beacon->head_len;
+ new_beacon->head = pos;
+ memcpy(pos, beacon->head, beacon->head_len);
+ pos += beacon->head_len;
+ }
+ if (beacon->tail_len) {
+ new_beacon->tail_len = beacon->tail_len;
+ new_beacon->tail = pos;
+ memcpy(pos, beacon->tail, beacon->tail_len);
+ pos += beacon->tail_len;
+ }
+ if (beacon->beacon_ies_len) {
+ new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+ new_beacon->beacon_ies = pos;
+ memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+ pos += beacon->beacon_ies_len;
+ }
+ if (beacon->proberesp_ies_len) {
+ new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+ new_beacon->proberesp_ies = pos;
+ memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+ pos += beacon->proberesp_ies_len;
+ }
+ if (beacon->assocresp_ies_len) {
+ new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+ new_beacon->assocresp_ies = pos;
+ memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+ pos += beacon->assocresp_ies_len;
+ }
+ if (beacon->probe_resp_len) {
+ new_beacon->probe_resp_len = beacon->probe_resp_len;
+ beacon->probe_resp = pos;
+ memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+ pos += beacon->probe_resp_len;
+ }
+
+ return new_beacon;
+}
+
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait, const u8 *buf, size_t len,
--
1.7.10.4
^ permalink raw reply related
* [PATCHv5 4/4] ath9k: enable CSA functionality in ath9k
From: Simon Wunderlich @ 2013-07-11 14:09 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1373551749-5107-1-git-send-email-siwu@hrz.tu-chemnitz.de>
CSA is only enabled for one interface, but the same limitation applies
for mac80211 too. It checks whether the beacon has been sent (different
approaches for non-EDMA-enabled and EDMA-enabled devices), and completes
the channel switch after that.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to PATCHv2:
* announce support for channel switch
Changes to PATCHv1:
* complete channel switch after the last beacon has been sent
---
drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++
drivers/net/wireless/ath/ath9k/beacon.c | 21 +++++++++++++++++++++
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/main.c | 17 +++++++++++++++++
drivers/net/wireless/ath/ath9k/xmit.c | 2 ++
5 files changed, 43 insertions(+)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 579ed9c..3745487 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -424,6 +424,7 @@ void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif);
void ath9k_set_beacon(struct ath_softc *sc);
+bool ath9k_csa_is_finished(struct ath_softc *sc);
/*******************/
/* Link Monitoring */
@@ -751,6 +752,7 @@ struct ath_softc {
#endif
struct ath_descdma txsdma;
+ struct ieee80211_vif *csa_vif;
struct ath_ant_comb ant_comb;
u8 ant_tx, ant_rx;
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index fd1eeba..89c2003 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -310,6 +310,23 @@ void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
(unsigned long long)tsfadjust, avp->av_bslot);
}
+bool ath9k_csa_is_finished(struct ath_softc *sc)
+{
+ struct ieee80211_vif *vif;
+
+ vif = sc->csa_vif;
+ if (!vif || !vif->csa_active)
+ return false;
+
+ if (!ieee80211_csa_is_complete(vif))
+ return false;
+
+ ieee80211_csa_finish(vif);
+
+ sc->csa_vif = NULL;
+ return true;
+}
+
void ath9k_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
@@ -355,6 +372,10 @@ void ath9k_beacon_tasklet(unsigned long data)
return;
}
+ /* EDMA devices check that in the tx completion function. */
+ if (!edma && ath9k_csa_is_finished(sc))
+ return;
+
slot = ath9k_beacon_choose_slot(sc);
vif = sc->beacon.bslot[slot];
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index f359b0d..3ce83c4 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -807,6 +807,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
#ifdef CONFIG_PM_SLEEP
if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d1ce3da..6844968 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1026,6 +1026,9 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_remove_slot(sc, vif);
+ if (sc->csa_vif == vif)
+ sc->csa_vif = NULL;
+
ath9k_ps_wakeup(sc);
ath9k_calculate_summary_state(hw, NULL);
ath9k_ps_restore(sc);
@@ -2319,6 +2322,19 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
clear_bit(SC_OP_SCANNING, &sc->sc_flags);
}
+static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ath_softc *sc = hw->priv;
+
+ /* mac80211 does not support CSA in multi-if cases (yet) */
+ if (WARN_ON(sc->csa_vif))
+ return;
+
+ sc->csa_vif = vif;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -2366,4 +2382,5 @@ struct ieee80211_ops ath9k_ops = {
#endif
.sw_scan_start = ath9k_sw_scan_start,
.sw_scan_complete = ath9k_sw_scan_complete,
+ .channel_switch_beacon = ath9k_channel_switch_beacon,
};
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index eab0fcb..cec670d 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2282,6 +2282,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
if (ts.qid == sc->beacon.beaconq) {
sc->beacon.tx_processed = true;
sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
+
+ ath9k_csa_is_finished(sc);
continue;
}
--
1.7.10.4
^ permalink raw reply related
* [PATCHv5 3/4] mac80211: add channel switch command and beacon callbacks
From: Simon Wunderlich @ 2013-07-11 14:09 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Mathias Kretschmer, Simon Wunderlich
In-Reply-To: <1373551749-5107-1-git-send-email-siwu@hrz.tu-chemnitz.de>
The count field in CSA must be decremented with each beacon
transmitted. This patch implements the functionality for drivers
using ieee80211_beacon_get(). Other drivers must call back manually
after reaching count == 0.
This patch also contains the handling and finish worker for the channel
switch command, and mac80211/chanctx code to allow to change a channel
definition of an active channel context.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
---
Changes to PATCHv4:
* squashed the patch "allow chanctx to change channels", as this now
checks for csa_active
* add comment that the chanctx channel change is designed for CSA only
* remove BSS_CHANGED_CHANNEL
* remove check for use_chanctx
* fix rcu_read_lock leak
* set csa_active to false after completing callback
Changes to PATCHv3:
* forbid adding interfaces while CSA is running
* forbid calling CSA multiple times
* check for multiple channel contexts
* abort channel switch when going down while doing CSA
Changes to PATCHv2:
* fix documentation and style stuff
* update tracing
* change csa_active to bool
* check if csa_active when assigning beacons
* fix locking and access
* use new ieee80211_vif_change_channel to change the channel
Changes to PATCHv1:
* don't switch when CAC is active
* add hw to parameters for driver
Changes to RFCv1:
* use beacons as supplied from nl80211 without generating/parsing them
* update beacons using offsets
* report back by calling the channel switch event in cfg80211
---
include/net/mac80211.h | 36 +++++++++++++
net/mac80211/cfg.c | 129 +++++++++++++++++++++++++++++++++++++++++++-
net/mac80211/chan.c | 58 ++++++++++++++++++++
net/mac80211/driver-ops.h | 13 +++++
net/mac80211/ieee80211_i.h | 18 +++++++
net/mac80211/iface.c | 9 ++++
net/mac80211/trace.h | 26 +++++++++
net/mac80211/tx.c | 78 +++++++++++++++++++++++++++
8 files changed, 365 insertions(+), 2 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 3124036..cb0c94f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -152,11 +152,13 @@ struct ieee80211_low_level_stats {
* @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
* @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
* @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
+ * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel
*/
enum ieee80211_chanctx_change {
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
+ IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3)
};
/**
@@ -1084,6 +1086,7 @@ enum ieee80211_vif_flags {
* @addr: address of this interface
* @p2p: indicates whether this AP or STA interface is a p2p
* interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on
* @driver_flags: flags/capabilities the driver has for this interface,
* these need to be set (or cleared) when the interface is added
* or, if supported by the driver, the interface type is changed
@@ -1106,6 +1109,7 @@ struct ieee80211_vif {
struct ieee80211_bss_conf bss_conf;
u8 addr[ETH_ALEN];
bool p2p;
+ bool csa_active;
u8 cab_queue;
u8 hw_queue[IEEE80211_NUM_ACS];
@@ -2637,6 +2641,16 @@ enum ieee80211_roc_type {
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
* Currently, this is only called for managed or P2P client interfaces.
* This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch to a new channel.
+ * Beacons are modified to include CSA or ECSA IEs before calling this
+ * function. The corresponding count fields in these IEs must be
+ * decremented, and when they reach zero the driver must call
+ * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
+ * get the csa counter decremented by mac80211, but must check if it is
+ * zero using ieee80211_csa_is_complete() after the beacon has been
+ * transmitted and then call ieee80211_csa_finish().
+ *
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -2824,6 +2838,9 @@ struct ieee80211_ops {
struct ieee80211_vif *vif,
struct inet6_dev *idev);
#endif
+ void (*channel_switch_beacon)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef);
};
/**
@@ -3319,6 +3336,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
}
/**
+ * ieee80211_csa_finish - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hit zero, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_csa_finish(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_is_complete - find out if counters reached zero
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * This function returns whether the channel switch counters reached zero.
+ */
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
+
+
+/**
* ieee80211_proberesp_get - retrieve a Probe Response template
* @hw: pointer obtained from ieee80211_alloc_hw().
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 8e6c935..e3f598bd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -860,8 +860,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
return 0;
}
-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_beacon_data *params)
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_beacon_data *params)
{
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
@@ -1024,6 +1024,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ /* don't allow changing the beacon while CSA is in place - offset
+ * of channel switch counter may change
+ */
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
old = rtnl_dereference(sdata->u.ap.beacon);
if (!old)
return -ENOENT;
@@ -1048,6 +1054,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
return -ENOENT;
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ /* abort any running channel switch */
+ sdata->vif.csa_active = false;
+ cancel_work_sync(&sdata->csa_finalize_work);
+
/* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
@@ -2831,6 +2841,120 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
return new_beacon;
}
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ csa_finalize_work);
+ struct ieee80211_local *local = sdata->local;
+ int err, changed;
+
+ if (!ieee80211_sdata_running(sdata))
+ return;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+ return;
+
+ sdata->radar_required = sdata->csa_radar_required;
+ err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
+ &changed);
+ if (WARN_ON(err < 0))
+ return;
+
+ err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+ if (err < 0)
+ return;
+
+ changed |= err;
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+ sdata->vif.csa_active = false;
+
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+}
+
+static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
+ int err, num_chanctx;
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ if (sdata->wdev.cac_started)
+ return -EBUSY;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+
+ /* don't handle for multi-VIF cases */
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ if (chanctx->refcount > 1) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+ num_chanctx = 0;
+ list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+ num_chanctx++;
+
+ if (num_chanctx > 1) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+ rcu_read_unlock();
+
+ /* don't allow another channel switch if one is already active. */
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
+ /* only handle AP for now. */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after);
+ if (!sdata->u.ap.next_beacon)
+ return -ENOMEM;
+
+ sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
+ sdata->csa_counter_offset_presp = params->counter_offset_presp;
+ sdata->csa_radar_required = params->radar_required;
+
+ if (params->block_tx)
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+
+ err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
+ if (err < 0)
+ return err;
+
+ local->csa_chandef = params->chandef;
+ sdata->vif.csa_active = true;
+
+ ieee80211_bss_info_change_notify(sdata, err);
+ drv_channel_switch_beacon(sdata, ¶ms->chandef);
+
+ return 0;
+}
+
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait, const u8 *buf, size_t len,
@@ -3548,4 +3672,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
+ .channel_switch = ieee80211_channel_switch,
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 03e8d2e..3a4764b 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
return ret;
}
+int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+ int ret;
+ u32 chanctx_changed = 0;
+
+ /* should never be called if not performing a channel switch. */
+ if (WARN_ON(!sdata->vif.csa_active))
+ return -EINVAL;
+
+ if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (!conf) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+ if (ctx->refcount != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+ chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+ *changed |= BSS_CHANGED_BANDWIDTH;
+ }
+
+ sdata->vif.bss_conf.chandef = *chandef;
+ ctx->conf.def = *chandef;
+
+ chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+ drv_change_chanctx(local, ctx, chanctx_changed);
+
+ if (!local->use_chanctx) {
+ local->_oper_chandef = *chandef;
+ ieee80211_hw_config(local, 0);
+ }
+
+ ieee80211_recalc_chanctx_chantype(local, ctx);
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
+
+ ret = 0;
+ out:
+ mutex_unlock(&local->chanctx_mtx);
+ return ret;
+}
+
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
u32 *changed)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index b931c96..b3ea11f 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
}
#endif
+static inline void
+drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ if (local->ops->channel_switch_beacon) {
+ trace_drv_channel_switch_beacon(local, sdata, chandef);
+ local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
+ chandef);
+ }
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 23a191b..31ca8d2 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -259,6 +259,8 @@ struct ieee80211_if_ap {
struct beacon_data __rcu *beacon;
struct probe_resp __rcu *probe_resp;
+ /* to be used after channel switch. */
+ struct cfg80211_beacon_data *next_beacon;
struct list_head vlans;
struct ps_data ps;
@@ -716,6 +718,11 @@ struct ieee80211_sub_if_data {
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+ struct work_struct csa_finalize_work;
+ int csa_counter_offset_beacon;
+ int csa_counter_offset_presp;
+ bool csa_radar_required;
+
/* used to reconfigure hardware SM PS */
struct work_struct recalc_smps;
@@ -1373,6 +1380,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
void ieee80211_sw_roc_work(struct work_struct *work);
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+/* channel switch handling */
+void ieee80211_csa_finalize_work(struct work_struct *work);
+
/* interface handling */
int ieee80211_iface_init(void);
void ieee80211_iface_exit(void);
@@ -1394,6 +1404,9 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_beacon_data *params);
+
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
@@ -1658,6 +1671,11 @@ int __must_check
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
u32 *changed);
+/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
+int __must_check
+ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 093c34a..187c161 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -274,6 +274,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
if (iftype == NL80211_IFTYPE_ADHOC &&
nsdata->vif.type == NL80211_IFTYPE_ADHOC)
return -EBUSY;
+ /*
+ * will not add another interface while any channel
+ * switch is active.
+ */
+ if (nsdata->vif.csa_active)
+ return -EBUSY;
/*
* The remaining checks are only performed for interfaces
@@ -804,6 +810,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_work_sync(&local->dynamic_ps_enable_work);
cancel_work_sync(&sdata->recalc_smps);
+ sdata->vif.csa_active = false;
+ cancel_work_sync(&sdata->csa_finalize_work);
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
@@ -1267,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+ INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index c215fafd7..1aba645 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected,
)
);
+TRACE_EVENT(drv_channel_switch_beacon,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_chan_def *chandef),
+
+ TP_ARGS(local, sdata, chandef),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANDEF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANDEF_ASSIGN(chandef);
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
+ )
+);
+
+
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f82301b..b66f767 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2335,6 +2335,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
return 0;
}
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ ieee80211_queue_work(&sdata->local->hw,
+ &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
+ struct beacon_data *beacon)
+{
+ struct probe_resp *resp;
+ int counter_offset_beacon = sdata->csa_counter_offset_beacon;
+ int counter_offset_presp = sdata->csa_counter_offset_presp;
+
+ /* warn if the driver did not check for/react to csa completeness */
+ if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
+ return;
+
+ ((u8 *)beacon->tail)[counter_offset_beacon]--;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ counter_offset_presp) {
+ rcu_read_lock();
+ resp = rcu_dereference(sdata->u.ap.probe_resp);
+
+ /* if nl80211 accepted the offset, this should not happen. */
+ if (WARN_ON(!resp)) {
+ rcu_read_unlock();
+ return;
+ }
+ resp->data[counter_offset_presp]--;
+ rcu_read_unlock();
+ }
+}
+
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct beacon_data *beacon = NULL;
+ u8 *beacon_data;
+ size_t beacon_data_len;
+ int counter_beacon = sdata->csa_counter_offset_beacon;
+ int ret = false;
+
+ if (!ieee80211_sdata_running(sdata))
+ return false;
+
+ rcu_read_lock();
+ if (vif->type == NL80211_IFTYPE_AP) {
+ struct ieee80211_if_ap *ap = &sdata->u.ap;
+
+ beacon = rcu_dereference(ap->beacon);
+ if (WARN_ON(!beacon || !beacon->tail))
+ goto out;
+ beacon_data = beacon->tail;
+ beacon_data_len = beacon->tail_len;
+ } else {
+ WARN_ON(1);
+ goto out;
+ }
+
+ if (WARN_ON(counter_beacon > beacon_data_len))
+ goto out;
+
+ if (beacon_data[counter_beacon] == 0)
+ ret = true;
+ out:
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_csa_is_complete);
+
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 *tim_offset, u16 *tim_length)
@@ -2365,6 +2440,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct beacon_data *beacon = rcu_dereference(ap->beacon);
if (beacon) {
+ if (sdata->vif.csa_active)
+ ieee80211_update_csa(sdata, beacon);
+
/*
* headroom, head length,
* tail length and maximum TIM length
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH 45/50] sound: usb: usx2y: spin_lock in complete() cleanup
From: Ming Lei @ 2013-07-11 14:13 UTC (permalink / raw)
To: Takashi Iwai
Cc: Sergei Shtylyov, Greg Kroah-Hartman, linux-usb, Oliver Neukum,
Alan Stern, linux-input, linux-bluetooth, netdev, linux-wireless,
linux-media, alsa-devel, Jaroslav Kysela
In-Reply-To: <s5hip0hb3y6.wl%tiwai@suse.de>
On Thu, Jul 11, 2013 at 9:50 PM, Takashi Iwai <tiwai@suse.de> wrote:
> At Thu, 11 Jul 2013 17:08:30 +0400,
> Sergei Shtylyov wrote:
>>
>> On 11-07-2013 13:06, Ming Lei wrote:
>>
>> > Complete() will be run with interrupt enabled, so change to
>> > spin_lock_irqsave().
>>
>> Changelog doesn't match the patch.
>
> Yep, but moreover...
>
>> > Cc: Jaroslav Kysela <perex@perex.cz>
>> > Cc: Takashi Iwai <tiwai@suse.de>
>> > Cc: alsa-devel@alsa-project.org
>> > Signed-off-by: Ming Lei <ming.lei@canonical.com>
>> > ---
>> > sound/usb/usx2y/usbusx2yaudio.c | 4 ++++
>> > 1 file changed, 4 insertions(+)
>>
>> > diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
>> > index 4967fe9..e2ee893 100644
>> > --- a/sound/usb/usx2y/usbusx2yaudio.c
>> > +++ b/sound/usb/usx2y/usbusx2yaudio.c
>> > @@ -273,7 +273,11 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y)
>> > struct snd_usX2Y_substream *subs = usX2Y->subs[s];
>> > if (subs) {
>> > if (atomic_read(&subs->state) >= state_PRERUNNING) {
>> > + unsigned long flags;
>> > +
>> > + local_irq_save(flags);
>> > snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
>> > + local_irq_restore(flags);
>> > }
>
> ... actually this snd_pcm_stop() call should have been covered by
> snd_pcm_stream_lock(). Maybe it'd be enough to have a single patch
> together with the change, i.e. wrapping with
> snd_pcm_stream_lock_irqsave().
Please use snd_pcm_stream_lock_irqsave() so that I can avoid sending
out similar patch later, :-)
>
> I'll prepare the patch for 3.11 independently from your patch series,
> so please drop this one.
OK, thanks for dealing with that.
>
>
> BTW, the word "cleanup" in the subject is inappropriate. This is
> rather a fix together with the core change.
It is a cleanup since the patchset only addresses lock problem which
is caused by the tasklet conversion.
>
> And, I wonder whether we can take a safer approach. When the caller
> condition changed, we often introduced either a different ops
> (e.g. ioctl case) or a flag for the new condition, at least during the
> transition period.
Interrupt is't enabled until all current drivers are cleaned up, so it is really
safe, please see patch [2].
>
> Last but not least, is a conversion to tasklet really preferred?
> tasklet is rather an obsoleted infrastructure nowadays, and people
> don't recommend to use it any longer, AFAIK.
We discussed the problem in the below link previously[1], Steven
and Thomas suggested to use threaded irq handler, but which
may degrade USB mass storage performance, so we have to
take tasklet now until we rewrite transport part of USB mass storage
driver.
Also the conversion[2] has avoided the tasklet spin lock problem
already.
[1], http://marc.info/?t=137079119200001&r=1&w=2
[2], http://marc.info/?l=linux-usb&m=137286326726326&w=2
Thanks,
--
Ming Lei
^ permalink raw reply
* RE: [PATCH] mwifiex: don't ignore SDIO interrupts during shutdown
From: Amitkumar Karwar @ 2013-07-11 14:17 UTC (permalink / raw)
To: 'Daniel Drake'
Cc: Bing Zhao, linux-wireless@vger.kernel.org, linville@tuxdriver.com
In-Reply-To: <CAMLZHHR_8Zsp2xe=9x4oJ1Rj7XLGOEza3r0uPt_Ht8TDC781aQ@mail.gmail.com>
Hi Daniel,
Thanks for your elaborative comments. We can go with your patch which fixes the issue in a cleaner way. I ran some tests and verified remove path with it.
>Unless there is a reason to do otherwise, the norm here would be to
>tightly couple the hardware bits that enable interrupts to the Linux
>IRQ handler registration. i.e. sdio_claim_irq should be called
>immediately before mwifiex_sdio_enable_host_int, and sdio_release_irq
>should be called immediately after mwifiex_sdio_disable_host_int.
>Right now these 2 steps are very separate (sdio_release_irq is being
>called far too late, long after the point when the driver could sanely
>handle an interrupt).
Now if_ops.disable_int call is a bit closer to sdio_release_irq (in if_ops.unregister).
>Although you would never expect an interrupt to arrive after
>mwifiex_sdio_disable_host_int() has been called, the cleanliness and
>readability has value here, and maybe damaged hardware would misbehave
>and fire an interrupt anyway, which would cause the driver to go
>wrong.
mwifiex_main_process() which may go into an infinite loop is not called in this case now.
>Secondly, it should be symmetrical. If the core mwifiex driver is the
>thing that enables interrupts, it should be the component responsible
>for disabling them as well. With your patch, the core mwifiex driver
>enables interrupts, but it is up to the sdio sub-driver to disable
>them.
if_ops.disable_int call takes care of it.
Regards,
Amitkumar Karwar
^ permalink raw reply
* Re: [PATCH 45/50] sound: usb: usx2y: spin_lock in complete() cleanup
From: Takashi Iwai @ 2013-07-11 14:34 UTC (permalink / raw)
To: Ming Lei
Cc: Sergei Shtylyov, Greg Kroah-Hartman, linux-usb, Oliver Neukum,
Alan Stern, linux-input, linux-bluetooth, netdev, linux-wireless,
linux-media, alsa-devel, Jaroslav Kysela
In-Reply-To: <CACVXFVPuv4VD_xCocP2QVOqUqAXm9gjwgwcRn6y=3qXPntOxCQ@mail.gmail.com>
At Thu, 11 Jul 2013 22:13:35 +0800,
Ming Lei wrote:
>
> On Thu, Jul 11, 2013 at 9:50 PM, Takashi Iwai <tiwai@suse.de> wrote:
> > At Thu, 11 Jul 2013 17:08:30 +0400,
> > Sergei Shtylyov wrote:
> >>
> >> On 11-07-2013 13:06, Ming Lei wrote:
> >>
> >> > Complete() will be run with interrupt enabled, so change to
> >> > spin_lock_irqsave().
> >>
> >> Changelog doesn't match the patch.
> >
> > Yep, but moreover...
> >
> >> > Cc: Jaroslav Kysela <perex@perex.cz>
> >> > Cc: Takashi Iwai <tiwai@suse.de>
> >> > Cc: alsa-devel@alsa-project.org
> >> > Signed-off-by: Ming Lei <ming.lei@canonical.com>
> >> > ---
> >> > sound/usb/usx2y/usbusx2yaudio.c | 4 ++++
> >> > 1 file changed, 4 insertions(+)
> >>
> >> > diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
> >> > index 4967fe9..e2ee893 100644
> >> > --- a/sound/usb/usx2y/usbusx2yaudio.c
> >> > +++ b/sound/usb/usx2y/usbusx2yaudio.c
> >> > @@ -273,7 +273,11 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y)
> >> > struct snd_usX2Y_substream *subs = usX2Y->subs[s];
> >> > if (subs) {
> >> > if (atomic_read(&subs->state) >= state_PRERUNNING) {
> >> > + unsigned long flags;
> >> > +
> >> > + local_irq_save(flags);
> >> > snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
> >> > + local_irq_restore(flags);
> >> > }
> >
> > ... actually this snd_pcm_stop() call should have been covered by
> > snd_pcm_stream_lock(). Maybe it'd be enough to have a single patch
> > together with the change, i.e. wrapping with
> > snd_pcm_stream_lock_irqsave().
>
> Please use snd_pcm_stream_lock_irqsave() so that I can avoid sending
> out similar patch later, :-)
>
> >
> > I'll prepare the patch for 3.11 independently from your patch series,
> > so please drop this one.
>
> OK, thanks for dealing with that.
>
> >
> >
> > BTW, the word "cleanup" in the subject is inappropriate. This is
> > rather a fix together with the core change.
>
> It is a cleanup since the patchset only addresses lock problem which
> is caused by the tasklet conversion.
Well, the conversion to irqsave() is needed for the future drop of
irq_save() in the caller, right? Then this isn't a cleanup but a
preparation for movement ahead.
> > And, I wonder whether we can take a safer approach. When the caller
> > condition changed, we often introduced either a different ops
> > (e.g. ioctl case) or a flag for the new condition, at least during the
> > transition period.
>
> Interrupt is't enabled until all current drivers are cleaned up, so it is really
> safe, please see patch [2].
OK.
> > Last but not least, is a conversion to tasklet really preferred?
> > tasklet is rather an obsoleted infrastructure nowadays, and people
> > don't recommend to use it any longer, AFAIK.
>
> We discussed the problem in the below link previously[1], Steven
> and Thomas suggested to use threaded irq handler, but which
> may degrade USB mass storage performance, so we have to
> take tasklet now until we rewrite transport part of USB mass storage
> driver.
>
> Also the conversion[2] has avoided the tasklet spin lock problem
> already.
OK, good to know.
thanks,
Takashi
> [1], http://marc.info/?t=137079119200001&r=1&w=2
> [2], http://marc.info/?l=linux-usb&m=137286326726326&w=2
>
> Thanks,
> --
> Ming Lei
>
^ permalink raw reply
* Re: [PATCH] mwifiex: don't ignore SDIO interrupts during shutdown
From: Daniel Drake @ 2013-07-11 14:38 UTC (permalink / raw)
To: Amitkumar Karwar
Cc: Bing Zhao, linux-wireless@vger.kernel.org, linville@tuxdriver.com
In-Reply-To: <5FF020A1CFFEEC49BD1E09530C4FF5951035636367@SC-VEXCH1.marvell.com>
On Thu, Jul 11, 2013 at 8:17 AM, Amitkumar Karwar <akarwar@marvell.com> wrote:
> Hi Daniel,
>
> Thanks for your elaborative comments. We can go with your patch which fixes the issue in a cleaner way. I ran some tests and verified remove path with it.
Hmm. Now that I heard back from you and Bing, that interrupts at this
point are totally unexpected, I would prefer to see the interrupt
handler being disabled at the appropriate time, I think we can do
better than my original patch. Let me see if I can find some time
today/tomorrow to explore a better approach.
Daniel
^ permalink raw reply
* Re: [PATCH 45/50] sound: usb: usx2y: spin_lock in complete() cleanup
From: Ming Lei @ 2013-07-11 14:52 UTC (permalink / raw)
To: Takashi Iwai
Cc: Sergei Shtylyov, Greg Kroah-Hartman, linux-usb, Oliver Neukum,
Alan Stern, linux-input, linux-bluetooth, netdev, linux-wireless,
linux-media, alsa-devel, Jaroslav Kysela
In-Reply-To: <s5h7ggx413u.wl%tiwai@suse.de>
On Thu, Jul 11, 2013 at 10:34 PM, Takashi Iwai <tiwai@suse.de> wrote:
> At Thu, 11 Jul 2013 22:13:35 +0800,
> Ming Lei wrote:
>>
>> On Thu, Jul 11, 2013 at 9:50 PM, Takashi Iwai <tiwai@suse.de> wrote:
>> > At Thu, 11 Jul 2013 17:08:30 +0400,
>> > Sergei Shtylyov wrote:
>> >>
>> >> On 11-07-2013 13:06, Ming Lei wrote:
>> >>
>> >> > Complete() will be run with interrupt enabled, so change to
>> >> > spin_lock_irqsave().
>> >>
>> >> Changelog doesn't match the patch.
>> >
>> > Yep, but moreover...
>> >
>> >> > Cc: Jaroslav Kysela <perex@perex.cz>
>> >> > Cc: Takashi Iwai <tiwai@suse.de>
>> >> > Cc: alsa-devel@alsa-project.org
>> >> > Signed-off-by: Ming Lei <ming.lei@canonical.com>
>> >> > ---
>> >> > sound/usb/usx2y/usbusx2yaudio.c | 4 ++++
>> >> > 1 file changed, 4 insertions(+)
>> >>
>> >> > diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c
>> >> > index 4967fe9..e2ee893 100644
>> >> > --- a/sound/usb/usx2y/usbusx2yaudio.c
>> >> > +++ b/sound/usb/usx2y/usbusx2yaudio.c
>> >> > @@ -273,7 +273,11 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y)
>> >> > struct snd_usX2Y_substream *subs = usX2Y->subs[s];
>> >> > if (subs) {
>> >> > if (atomic_read(&subs->state) >= state_PRERUNNING) {
>> >> > + unsigned long flags;
>> >> > +
>> >> > + local_irq_save(flags);
>> >> > snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
>> >> > + local_irq_restore(flags);
>> >> > }
>> >
>> > ... actually this snd_pcm_stop() call should have been covered by
>> > snd_pcm_stream_lock(). Maybe it'd be enough to have a single patch
>> > together with the change, i.e. wrapping with
>> > snd_pcm_stream_lock_irqsave().
>>
>> Please use snd_pcm_stream_lock_irqsave() so that I can avoid sending
>> out similar patch later, :-)
>>
>> >
>> > I'll prepare the patch for 3.11 independently from your patch series,
>> > so please drop this one.
>>
>> OK, thanks for dealing with that.
>>
>> >
>> >
>> > BTW, the word "cleanup" in the subject is inappropriate. This is
>> > rather a fix together with the core change.
>>
>> It is a cleanup since the patchset only addresses lock problem which
>> is caused by the tasklet conversion.
>
> Well, the conversion to irqsave() is needed for the future drop of
> irq_save() in the caller, right? Then this isn't a cleanup but a
> preparation for movement ahead.
Sounds more accurate, and I will change the title in next round, :-)
Thanks,
--
Ming Lei
^ permalink raw reply
* [PATCH 0/5] wil6210 updates - modified
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
Hi John,
Please ignore previous submission titled "[PATCH 0/3] wil6210 updates"
I found better solution for one of patches there. To avoid mis-interpretation,
resending all relevant patches. It is:
- Sync with latest firmware updates
- simple bug fixes and some optimization, all for data path
- prepare for various offloads - WIP by Erez
Vladimir Kondratiev (5):
wil6210: Align WMI header with latest FW
wil6210: fix wrong index in wil_vring_free
wil6210: Optimize Tx completion
wil6210: Introduce struct for sw context
wil6210: fix subtle race in wil_tx_vring
drivers/net/wireless/ath/wil6210/debugfs.c | 4 +-
drivers/net/wireless/ath/wil6210/trace.h | 22 +++++----
drivers/net/wireless/ath/wil6210/txrx.c | 79 +++++++++++++++++-------------
drivers/net/wireless/ath/wil6210/wil6210.h | 27 ++++++++--
drivers/net/wireless/ath/wil6210/wmi.c | 14 ++++--
5 files changed, 92 insertions(+), 54 deletions(-)
--
1.8.1.2
^ permalink raw reply
* [PATCH 2/5] wil6210: fix wrong index in wil_vring_free
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
In-Reply-To: <1373555021-11763-1-git-send-email-qca_vkondrat@qca.qualcomm.com>
When destroying Rx vring, branch for Rx used wrong Tx descriptor:
while SW context was taken for "head", HW descriptor was, by mistake,
taken from "tail"
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
---
drivers/net/wireless/ath/wil6210/txrx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index d240b24..8fde73a 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -133,7 +133,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
} else { /* rx */
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d =
- &vring->va[vring->swtail].rx;
+ &vring->va[vring->swhead].rx;
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
--
1.8.1.2
^ permalink raw reply related
* [PATCH 3/5] wil6210: Optimize Tx completion
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
In-Reply-To: <1373555021-11763-1-git-send-email-qca_vkondrat@qca.qualcomm.com>
No need to modify HW descriptor, as it will be re-initialized on Tx.
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
---
drivers/net/wireless/ath/wil6210/txrx.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 8fde73a..dd5f35e 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -855,10 +855,12 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
} else {
dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
- d->dma.addr.addr_low = 0;
- d->dma.addr.addr_high = 0;
- d->dma.length = 0;
- d->dma.status = TX_DMA_STATUS_DU;
+ /*
+ * There is no need to touch HW descriptor:
+ * - ststus bit TX_DMA_STATUS_DU is set by design,
+ * so hardware will not try to process this desc.,
+ * - rest of descriptor will be initialized on Tx.
+ */
vring->swtail = wil_vring_next_tail(vring);
done++;
}
--
1.8.1.2
^ permalink raw reply related
* [PATCH 4/5] wil6210: Introduce struct for sw context
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
In-Reply-To: <1373555021-11763-1-git-send-email-qca_vkondrat@qca.qualcomm.com>
Enable adding more data to the SW context.
For now, add flag "mapped_as_page", to separate decisions on free-ing skb
and type of DMA mapping.
This allows linking skb itself to any descriptor of fragmented skb.
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
---
drivers/net/wireless/ath/wil6210/debugfs.c | 4 +--
drivers/net/wireless/ath/wil6210/txrx.c | 58 +++++++++++++++++-------------
drivers/net/wireless/ath/wil6210/wil6210.h | 10 +++++-
3 files changed, 44 insertions(+), 28 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index e8308ec..971ce46 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -51,7 +51,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
- "S" : (vring->ctx[i] ? "H" : "h"));
+ "S" : (vring->ctx[i].skb ? "H" : "h"));
}
seq_printf(s, "\n");
}
@@ -406,7 +406,7 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
volatile struct vring_tx_desc *d =
&(vring->va[dbg_txdesc_index].tx);
volatile u32 *u = (volatile u32 *)d;
- struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
+ struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index dd5f35e..44cdd2a 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -70,7 +70,7 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
vring->swhead = 0;
vring->swtail = 0;
- vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+ vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL);
if (!vring->ctx) {
vring->va = NULL;
return -ENOMEM;
@@ -108,39 +108,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
- struct sk_buff *skb;
u16 dmalen;
+ struct wil_ctx *ctx;
if (tx) {
struct vring_tx_desc dd, *d = ⅆ
volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
+ ctx = &vring->ctx[vring->swtail];
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
- skb = vring->ctx[vring->swtail];
- if (skb) {
- dma_unmap_single(dev, pa, dmalen,
- DMA_TO_DEVICE);
- dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
+ if (vring->ctx[vring->swtail].mapped_as_page) {
dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE);
+ } else {
+ dma_unmap_single(dev, pa, dmalen,
+ DMA_TO_DEVICE);
}
+ if (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d =
&vring->va[vring->swhead].rx;
+ ctx = &vring->ctx[vring->swhead];
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
- skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
- kfree_skb(skb);
+ kfree_skb(ctx->skb);
wil_vring_advance_head(vring, 1);
}
}
@@ -187,7 +187,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = cpu_to_le16(sz);
*_d = *d;
- vring->ctx[i] = skb;
+ vring->ctx[i].skb = skb;
return 0;
}
@@ -352,11 +352,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL;
}
- skb = vring->ctx[vring->swhead];
+ skb = vring->ctx[vring->swhead].skb;
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
- vring->ctx[vring->swhead] = NULL;
+ vring->ctx[vring->swhead].skb = NULL;
wil_vring_advance_head(vring, 1);
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
@@ -703,7 +703,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len, vring_index);
- vring->ctx[i] = NULL;
+ vring->ctx[i].mapped_as_page = 1;
*_d = *d;
}
/* for the last seg only */
@@ -724,7 +724,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
* to prevent skb release before accounting
* in case of immediate "tx done"
*/
- vring->ctx[i] = skb_get(skb);
+ vring->ctx[i].skb = skb_get(skb);
return 0;
dma_error:
@@ -732,6 +732,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) {
u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[i];
i = (swhead + f) % vring->size;
_d = &(vring->va[i].tx);
@@ -739,10 +740,15 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
_d->dma.status = TX_DMA_STATUS_DU;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
- if (vring->ctx[i])
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
- else
+ if (ctx->mapped_as_page)
dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
+ if (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
+
+ memset(ctx, 0, sizeof(*ctx));
}
return -EINVAL;
@@ -821,8 +827,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
&vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = ⅆ
dma_addr_t pa;
- struct sk_buff *skb;
u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ struct sk_buff *skb = ctx->skb;
*d = *_d;
@@ -840,7 +847,11 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
(const void *)d, sizeof(*d), false);
pa = wil_desc_addr(&d->dma.addr);
- skb = vring->ctx[vring->swtail];
+ if (ctx->mapped_as_page)
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
+ else
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
+
if (skb) {
if (d->dma.error == 0) {
ndev->stats.tx_packets++;
@@ -849,12 +860,9 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++;
}
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
+ memset(ctx, 0, sizeof(*ctx));
/*
* There is no need to touch HW descriptor:
* - ststus bit TX_DMA_STATUS_DU is set by design,
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 129c480..c4a5163 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -183,6 +183,14 @@ struct pending_wmi_event {
} __packed event;
};
+/**
+ * struct wil_ctx - software context for Vring descriptor
+ */
+struct wil_ctx {
+ struct sk_buff *skb;
+ u8 mapped_as_page:1;
+};
+
union vring_desc;
struct vring {
@@ -192,7 +200,7 @@ struct vring {
u32 swtail;
u32 swhead;
u32 hwtail; /* write here to inform hw */
- void **ctx; /* void *ctx[size] - software context */
+ struct wil_ctx *ctx; /* ctx[size] - software context */
};
enum { /* for wil6210_priv.status */
--
1.8.1.2
^ permalink raw reply related
* [PATCH 5/5] wil6210: fix subtle race in wil_tx_vring
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
In-Reply-To: <1373555021-11763-1-git-send-email-qca_vkondrat@qca.qualcomm.com>
Finish all SW context modifications prior to notifying hardware
It used to be race condition: if HW finish Tx and issue Tx completion IRQ very fast,
prior to SW context update in wil_tx_vring, Tx completion will mis-handle descriptor, as
SW part will have no skb pointer stored.
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
---
drivers/net/wireless/ath/wil6210/txrx.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 44cdd2a..2a9d56a 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -712,6 +712,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
*_d = *d;
+ /* hold reference to skb
+ * to prevent skb release before accounting
+ * in case of immediate "tx done"
+ */
+ vring->ctx[i].skb = skb_get(skb);
+
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
@@ -720,11 +726,6 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
- /* hold reference to skb
- * to prevent skb release before accounting
- * in case of immediate "tx done"
- */
- vring->ctx[i].skb = skb_get(skb);
return 0;
dma_error:
--
1.8.1.2
^ permalink raw reply related
* Re: [ath9k-devel] [PATCH] ath10k: Fix crash when using v1 hardware.
From: Ben Greear @ 2013-07-11 15:05 UTC (permalink / raw)
To: Kalle Valo; +Cc: ath10k, linux-wireless
In-Reply-To: <87y59d5tgu.fsf@kamboji.qca.qualcomm.com>
On 07/11/2013 02:36 AM, Kalle Valo wrote:
> greearb@candelatech.com writes:
>
>> From: Ben Greear <greearb@candelatech.com>
>>
>> I put a v1 NIC from an TP-LINK AC 1750 AP in
>> a 64-bit PC, and the OS crashes on bootup. I'm not
>> sure how broken my hardware is (possibly completely non
>> functional), but at least with this patch it will no longer
>> crash the OS. Not sure it ever got far enough to try,
>> but I also do not have firmware for the NIC.
>>
>> With this patch I get this info on module load:
>>
>> ath10k_pci 0000:05:00.0: BAR 0: assigned [mem 0xf4400000-0xf45fffff 64bit]
>> ath10k_pci 0000:05:00.0: BAR 0: error updating (0xf4400004 != 0xffffffff)
>> ath10k_pci 0000:05:00.0: BAR 0: error updating (high 0x000000 != 0xffffffff)
>> ath10k_pci 0000:05:00.0: Refused to change power state, currently in D3
>> ath10k: MSI-X interrupt handling (8 intrs)
>> ath10k: Unable to wakeup target
>> ath10k: target takes too long to wake up (awake count 1)
>> ath10k: src_ring ffff88020c0d0a00: write_index is out of bounds: 4294967295 nentries_mask: 15.
>> ath10k: dest_ring ffff88020db2c000: write_index is out of bounds: 4294967295 nentries_mask: 511.
>> ath10k: dest_ring ffff880210d56400: write_index is out of bounds: 4294967295 nentries_mask: 31.
>> ath10k: src_ring ffff880210d57600: write_index is out of bounds: 4294967295 nentries_mask: 31.
>> ath10k: src_ring ffff88020fe70000: write_index is out of bounds: 4294967295 nentries_mask: 2047.
>> ath10k: src_ring ffff880212989b40: write_index is out of bounds: 4294967295 nentries_mask: 1.
>> ath10k: dest_ring ffff880212989960: write_index is out of bounds: 4294967295 nentries_mask: 1.
>> ath10k: Failed to get pcie state addr: -5
>> ath10k: early firmware event indicated
>> ------------[ cut here ]------------
>> WARNING: at /home/greearb/git/linux.wireless-testing/drivers/net/wireless/ath/ath10k/ce.c:771 ath10k_ce_per_engine_service+0x53/0x1b4 [ath10k_pci]()
>> ....
>> (it hits the warning case about 5-6 times and then seems to quiesce OK).
>
> I haven't seen this myself so it might be a hw problem, but difficult to
> say.
>
>> + /* On v1 hardware at least, setup can fail, causing ce_id_state to
>> + * be cleaned up, but this method is still called a few times. Check
>> + * for NULL here so we don't crash. Probably a better fix is to stop
>> + * the ath10k_pci_ce_tasklet sooner.
>> + */
>> + if (WARN_ONCE(!ce_state, "ce_id_to_state[%i] is NULL\n", ce_id))
>> + return;
>> +
>> + ctrl_addr = ce_state->ctrl_addr;
>> +
>
> The tests you add look like workarounds. I would prefer to try fix these
> by going to the source of the problem. Maybe we should add
> ath10k_pci_wake() and ath10k_do_pci_wake()?
These are work-arounds, but you should not let a bad piece of hardware/firmware crash
the entire OS just because you don't want to do sanity checking on the
values you get from the firmware. Perhaps there is a better fix for the
code above, but the warning splat should still provide incentive to get
it right, while not crashing the OS in the meantime.
> Can you enable few debug logs, like ATH10K_DBG_PCI, and post them? That
> would give more hint there things are going wrong.
Yes, I can do that.
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [ath9k-devel] [PATCH] ath10k: Fix crash when using v1 hardware.
From: Ben Greear @ 2013-07-11 15:06 UTC (permalink / raw)
To: Kalle Valo; +Cc: ath10k, linux-wireless
In-Reply-To: <87txk15te4.fsf@kamboji.qca.qualcomm.com>
On 07/11/2013 02:37 AM, Kalle Valo wrote:
> Ben Greear <greearb@candelatech.com> writes:
>
>> Actually, I have no idea what type of hardware this is. It was
>> suggested earlier to me that this AP had a v1 hardware in it, but
>> lspci shows this fairly unpromising thing, and the ath10k driver
>> appears to call 003c the V2 hardware....
>>
>> 05:00.0 Network controller: Atheros Communications Inc. Device 003c (rev ff) (prog-if ff)
>> !!! Unknown header type 7f
>> Kernel modules: ath10k_pci
>
> That unknown header type looks scary. I'm starting to suspect even more
> that you have a hw problem of some sort.
I tried two different NICS extracted from two different APs and the behaviour
is the same, so it is probably not just a manufacturing error.
Please note that I do NOT have v1 firmware, though from what I could tell,
this NIC was showing up as v2 hardware anyway...
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* [PATCH 1/5] wil6210: Align WMI header with latest FW
From: Vladimir Kondratiev @ 2013-07-11 15:03 UTC (permalink / raw)
To: John W . Linville
Cc: Kirshenbaum Erez, Vladimir Kondratiev, linux-wireless,
Luis R . Rodriguez
In-Reply-To: <1373555021-11763-1-git-send-email-qca_vkondrat@qca.qualcomm.com>
FW guys changed header structure; align driver code
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
---
drivers/net/wireless/ath/wil6210/trace.h | 22 +++++++++++++---------
drivers/net/wireless/ath/wil6210/wil6210.h | 17 ++++++++++++++---
drivers/net/wireless/ath/wil6210/wmi.c | 14 +++++++++-----
3 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h
index eff1239..e59239d 100644
--- a/drivers/net/wireless/ath/wil6210/trace.h
+++ b/drivers/net/wireless/ath/wil6210/trace.h
@@ -37,36 +37,40 @@ static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
DECLARE_EVENT_CLASS(wil6210_wmi,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len),
+ TP_ARGS(wmi, buf, buf_len),
TP_STRUCT__entry(
+ __field(u8, mid)
__field(u16, id)
+ __field(u32, timestamp)
__field(u16, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
- __entry->id = id;
+ __entry->mid = wmi->mid;
+ __entry->id = le16_to_cpu(wmi->id);
+ __entry->timestamp = le32_to_cpu(wmi->timestamp);
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
- "id 0x%04x len %d",
- __entry->id, __entry->buf_len
+ "MID %d id 0x%04x len %d timestamp %d",
+ __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len)
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len)
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
);
#define WIL6210_MSG_MAX (200)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 44fdab5..129c480 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -156,11 +156,22 @@ struct wil6210_mbox_hdr {
/* max. value for wil6210_mbox_hdr.len */
#define MAX_MBOXITEM_SIZE (240)
+/**
+ * struct wil6210_mbox_hdr_wmi - WMI header
+ *
+ * @mid: MAC ID
+ * 00 - default, created by FW
+ * 01..0f - WiFi ports, driver to create
+ * 10..fe - debug
+ * ff - broadcast
+ * @id: command/event ID
+ * @timestamp: FW fills for events, free-running msec timer
+ */
struct wil6210_mbox_hdr_wmi {
- u8 reserved0[2];
+ u8 mid;
+ u8 reserved;
__le16 id;
- __le16 info1; /* bits [0..3] - device_id, rest - unused */
- u8 reserved1[2];
+ __le32 timestamp;
} __packed;
struct pending_wmi_event {
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index dc8059a..a62511a 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -172,8 +172,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
.len = cpu_to_le16(sizeof(cmd.wmi) + len),
},
.wmi = {
+ .mid = 0,
.id = cpu_to_le16(cmdid),
- .info1 = 0,
},
};
struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
@@ -248,7 +248,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
- trace_wil6210_wmi_cmd(cmdid, buf, len);
+ trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -640,9 +640,13 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
- u16 id = le16_to_cpu(evt->event.wmi.id);
- wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
- trace_wil6210_wmi_event(id, &evt->event.wmi, len);
+ struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
+ u16 id = le16_to_cpu(wmi->id);
+ u32 tstamp = le32_to_cpu(wmi->timestamp);
+ wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
+ id, wmi->mid, tstamp);
+ trace_wil6210_wmi_event(wmi, &wmi[1],
+ len - sizeof(*wmi));
}
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
--
1.8.1.2
^ permalink raw reply related
* Re: [WT PATCH 6/6] mac80211: Tell user why beacons fail to parse.
From: Ben Greear @ 2013-07-11 15:10 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1373533184.8201.8.camel@jlt4.sipsolutions.net>
On 07/11/2013 01:59 AM, Johannes Berg wrote:
> On Sat, 2013-06-29 at 15:58 -0700, greearb@candelatech.com wrote:
>> From: Ben Greear <greearb@candelatech.com>
>>
>> Should help better debug dodgy APs and such.
>
> This isn't a bad idea, but I think instead of storing the message:
>
>> @@ -110,6 +110,7 @@ struct ieee80211_bss {
>>
>> /* Keep track of what bits of information we have valid info for. */
>> u8 valid_data;
>> + char corrupt_elems_msg[80];
>
> you should store a "what's bad" type field and the broken IE number or
> so, to reduce memory usage
I thought of this, but the problem is then you cannot tell the details
(for instance the actual lengths when length is bad, the ID that is duplicated, etc).
I figure if we are going to provide the info to the user, we might as well
be specific about it.
>> + snprintf(elems->parse_err_msg,
>> + sizeof(elems->parse_err_msg),
>> + "seen id: %i already", id);
>
> Your snprintf() usage is also unsafe.
What is unsafe about it?
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* Re: [WT PATCH 6/6] mac80211: Tell user why beacons fail to parse.
From: Johannes Berg @ 2013-07-11 15:17 UTC (permalink / raw)
To: Ben Greear; +Cc: linux-wireless
In-Reply-To: <51DECAFD.8070608@candelatech.com>
On Thu, 2013-07-11 at 08:10 -0700, Ben Greear wrote:
> >> /* Keep track of what bits of information we have valid info for. */
> >> u8 valid_data;
> >> + char corrupt_elems_msg[80];
> >
> > you should store a "what's bad" type field and the broken IE number or
> > so, to reduce memory usage
>
> I thought of this, but the problem is then you cannot tell the details
> (for instance the actual lengths when length is bad, the ID that is duplicated, etc).
> I figure if we are going to provide the info to the user, we might as well
> be specific about it.
It doesn't seem so difficult?
enum ies_parse_error {
NO_ERROR,
BAD_IE_LEN, // uses eid+len
DUPLICATE_IE, // uses eid
...
} parse_error;
int parse_error_eid, parse_error_len;
> >> + snprintf(elems->parse_err_msg,
> >> + sizeof(elems->parse_err_msg),
> >> + "seen id: %i already", id);
> >
> > Your snprintf() usage is also unsafe.
>
> What is unsafe about it?
using printk("%s", result) is unsafe due to potentially missing
NUL-termination.
johannes
^ permalink raw reply
* Re: [WT PATCH 4/6] mac80211: Add per-sdata station hash, and sdata hash.
From: Ben Greear @ 2013-07-11 15:29 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <1373532936.8201.5.camel@jlt4.sipsolutions.net>
On 07/11/2013 01:55 AM, Johannes Berg wrote:
> On Sat, 2013-06-29 at 15:58 -0700, greearb@candelatech.com wrote:
>> From: Ben Greear <greearb@candelatech.com>
>>
>> Add sdata hash (based on sdata->vif.addr) to local
>> structure.
>>
>> Add sta_vhash (based on sta->sta.addr) to sdata struct.
>>
>> Make STA_HASH give a better hash spread more often.
>>
>> Use new hashes where we can. Might be able to completely
>> get rid of the local->sta_hash, but didn't want to try that
>> quite yet.
>>
>> This significantly improves performance when using lots
>> of station VIFs connected to the same AP. It will likely
>> help other cases where the old hash logic failed to create
>> a decent spread.
>
> I think this is too much code for a corner case unlikely to happen
> outside of your specific scenario, so I'm not taking this either.
>
> I also don't like maintaining two separate hash tables and all that.
>
> I'd reconsider if you actually remove the hash entirely, but that'll be
> tricky to walk the station list and will quite possibly make the RX path
> there more expensive?
Remove local->sta_hash ?
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* [PATCH] mac80211: fix regression when initializing ibss wmm params
From: Simon Wunderlich @ 2013-07-11 16:07 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Simon Wunderlich
There appear to be two regressions in ibss.c when calling
ieee80211_sta_def_wmm_params():
* the second argument should be a rate length, not a rate array. This
was introduced by my commit "mac80211: select and adjust bitrates
according to channel mode"
* the third argument is not initialized (anymore), making further
checks within this function useless.
Since ieee80211_sta_def_wmm_params() is only used by ibss anyway,
remove the function entirely and handle the operating mode decision
immediately.
Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
---
net/mac80211/ibss.c | 12 ++++++++++--
net/mac80211/ieee80211_i.h | 3 ---
net/mac80211/util.c | 26 --------------------------
3 files changed, 10 insertions(+), 31 deletions(-)
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 449702d..83197c3 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -49,9 +49,9 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
struct cfg80211_bss *bss;
u32 bss_change, rate_flags, rates = 0, rates_added = 0;
- u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
struct cfg80211_chan_def chandef;
enum nl80211_bss_scan_width scan_width;
+ bool have_higher_than_11mbit = false;
struct beacon_data *presp;
int frame_len;
int shift;
@@ -149,6 +149,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < sband->n_bitrates; i++) {
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
continue;
+ if (sband->bitrates[i].bitrate > 110)
+ have_higher_than_11mbit = true;
rates |= BIT(i);
rates_n++;
@@ -270,11 +272,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
bss_change |= BSS_CHANGED_ERP_SLOT;
+ /* cf. IEEE 802.11 9.2.12 */
+ if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ else
+ sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change);
- ieee80211_sta_def_wmm_params(sdata, rates, supp_rates);
+ ieee80211_set_wmm_default(sdata, true);
ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 23a191b..3d32df1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1615,9 +1615,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
u32 ratemask, bool directed, u32 tx_flags,
struct ieee80211_channel *channel, bool scan);
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
- const size_t supp_rates_len,
- const u8 *supp_rates);
u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
enum ieee80211_band band, u32 *basic_rates);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 1e45891c..d23c5a7 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1073,32 +1073,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
}
}
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
- const size_t supp_rates_len,
- const u8 *supp_rates)
-{
- struct ieee80211_chanctx_conf *chanctx_conf;
- int i, have_higher_than_11mbit = 0;
-
- /* cf. IEEE 802.11 9.2.12 */
- for (i = 0; i < supp_rates_len; i++)
- if ((supp_rates[i] & 0x7f) * 5 > 110)
- have_higher_than_11mbit = 1;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
- if (chanctx_conf &&
- chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
- have_higher_than_11mbit)
- sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
- else
- sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- rcu_read_unlock();
-
- ieee80211_set_wmm_default(sdata, true);
-}
-
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *da,
--
1.7.10.4
^ permalink raw reply related
* Help adding trace events to xHCI
From: Sarah Sharp @ 2013-07-11 16:20 UTC (permalink / raw)
To: Xenia Ragiadakou
Cc: OPW Kernel Interns List, linux-usb, linux-wireless, Kalle Valo
In-Reply-To: <51DB0257.1010709@gmail.com>
On Mon, Jul 08, 2013 at 09:17:59PM +0300, Xenia Ragiadakou wrote:
> Hi Sarah,
Hi Xenia,
Comments below.
(Mentors and wireless folks, we're struggling a bit with adding trace
events to the xHCI USB host controller driver. I'm trying to look at
the ath6kl driver trace events as an example. We could use some help
and/or advice.)
> lets say that we want the tracepoint function to have the prototype:
>
> void trace_cmd_address_device(const char *fmt, ...).
>
> That internally will be implemented as:
>
> void trace_cmd_address_device(const char *fmt, ...)
> {
> if (trace event cmd_address_device is enabled) do {
> [...some other code will run here]
> (void(*)(const char *fmt, ...)) callback (fmt, '
> ' <-- we need to pass args here);
> }
> }
>
> callback will be the function that we actually intend to run and
> which we define when we define the trace_event. This function
> we want to create a string from fmt and the args and copy it
> into the ring buffer.
>
> The only way to pass the args into this function is by doing
> the following:
>
> Note: remember that the callback and tracepoint functions must
> have the same prototype.
>
> void trace_cmd_address_device(void *ptr, const char *fmt, ...)
> {
> if (trace event cmd_address_device is enabled) do {
> [...some other code will run here]
> va_list args;
> va_start(args, fmt);
> (void(*)(void *ptr, const char *fmt, ...))
> callback (args, fmt);
> va_end(args);
> }
> }
Right, that looks pretty similar to what's defined in
drivers/net/wireless/ath/ath6kl/trace.h. (Note that I'm working on the
very latest Linux kernel, so you may need to `git fetch` Greg KH's USB
tree, and rebase against the usb-next branch.)
> But, that cannot be done because the current tracing framework does not
> give a means to add code outside the callback.
I'm confused about why you need to add more code. Here's how I
understand the code works, so please correct me if I misinterpreted the
code.
The ath driver defines a new trace event class, ath6kl_log_event.
Various types of tracepoints, like trace_ath6kl_log_warn, use that event
class. Wrappers like ath6kl_warn() call those trace points, passing it
a struct va_format on the stack:
int ath6kl_warn(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
trace_ath6kl_log_warn(&vaf);
va_end(args);
return ret;
}
EXPORT_SYMBOL(ath6kl_warn);
trace_ath6kl_log_warn() uses the log event class:
DEFINE_EVENT(ath6kl_log_event, ath6kl_log_warn,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DECLARE_EVENT_CLASS(ath6kl_log_event,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf),
TP_STRUCT__entry(
__dynamic_array(char, msg, ATH6KL_MSG_MAX)
),
TP_fast_assign(
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
ATH6KL_MSG_MAX,
vaf->fmt,
*vaf->va) >= ATH6KL_MSG_MAX);
),
TP_printk("%s", __get_str(msg))
);
And then the code in files like drivers/net/wireless/ath6kl/cfg80211.c
can simply make what look like printk calls to ath6kl_warn:
ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
I think that we can apply a similar technique to define trace events for
the xhci debugging that's printed when we're issuing a Set Address
command. Something like:
int xhci_debug_address(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = xhci_printk(KERN_WARNING, "%pV", &vaf);
trace_xhci_dbg_address(&vaf);
va_end(args);
return ret;
}
EXPORT_SYMBOL(xhci_debug_address);
DEFINE_EVENT(xhci_log_event, xhci_dbg_address,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DECLARE_EVENT_CLASS(xhci_log_event,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf),
TP_STRUCT__entry(
__dynamic_array(char, msg, ATH6KL_MSG_MAX)
),
TP_fast_assign(
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
ATH6KL_MSG_MAX,
vaf->fmt,
*vaf->va) >= ATH6KL_MSG_MAX);
),
TP_printk("%s", __get_str(msg))
);
And then code in xhci_address_device() in drivers/usb/host/xhci.c can do
things like:
xhci_debug_address(xhci, "Bad Slot ID %d\n", udev->slot_id);
And we can define similar trace event classes for the various xHCI
commands or ring debugging, so that we can enable or disable trace
points individually for different parts of the xHCI driver.
Xenia, if this all something you've tried already, I apologize. :) I'm
just trying to understand how the trace event system works, and figure
out what the code should look like for the xHCI driver.
> The thing that i was trying to do, was to pass the pointer to the
> fmt on the stack.
> Something, like:
>
> void trace_cmd_address_device(const char *fmt, ...)
> {
> if (trace event cmd_address_device is enabled) do {
> [...some other code will run here]
> (void(*)(const char *fmt, ...)) callback ((const
> char *)&fmt);
> }
> }
>
> and then inside the callback to do:
>
> va_list args;
> va_start(args, *(char **)fmt);
> vsnprintf(msg_string, mesg_max_len, *(char **)fmt, args);
> va_end(args);
>
> That would have worked if the tracepoint was just :
>
> void trace_cmd_address_device(const char *fmt, ...)
> {
> if (trace event cmd_address_device is enabled) do {
> (void(*)(const char *fmt, ...)) callback ((const
> char *)&fmt);
> }
> }
>
> But when there are other function calls before the callback call, I don't
> no why but i cannot track anymore the position of the args following the
> fmt argumenent in the stack with the pointer to fmt.
I'm actually wondering if the call to ath6kl_printk is somehow necessary
in order to be able to pass arguments on the stack. Perhaps you should
try defining a similar function for xHCI and see if that helps?
int ath6kl_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int rtn;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
rtn = printk("%sath6kl: %pV", level, &vaf);
va_end(args);
return rtn;
}
EXPORT_SYMBOL(ath6kl_printk);
> Anyway, something dirty like that will not enter the kernel but i will try
> to do stack debugging on an example program to see why that happens.
I would suggest just copy-pasting parts of the ath6kl trace code into
the xHCI driver, and changing one of the xhci_dbg() calls to use that
code, and see if it works. If it doesn't work, send out an RFC patch
(using the Cc list I've used), and we'll try to figure out what's going
wrong.
Sarah Sharp
^ permalink raw reply
* Re: Help adding trace events to xHCI
From: Arend van Spriel @ 2013-07-11 17:08 UTC (permalink / raw)
To: Sarah Sharp
Cc: Xenia Ragiadakou, OPW Kernel Interns List, linux-usb,
linux-wireless, Kalle Valo
In-Reply-To: <20130711162002.GA5240@xanatos>
On 07/11/2013 06:20 PM, Sarah Sharp wrote:
> On Mon, Jul 08, 2013 at 09:17:59PM +0300, Xenia Ragiadakou wrote:
>> But when there are other function calls before the callback call, I don't
>> no why but i cannot track anymore the position of the args following the
>> fmt argumenent in the stack with the pointer to fmt.
>
> I'm actually wondering if the call to ath6kl_printk is somehow necessary
> in order to be able to pass arguments on the stack. Perhaps you should
> try defining a similar function for xHCI and see if that helps?
The ath6klk_printk() is not related to the trace function. It is a
separate code path to get the message in the kernel log. I have seen
these constructs before (and implemented it in brcmfmac) but it seems
not very efficient when tracing as the printk can affect run-time behaviour.
> int ath6kl_printk(const char *level, const char *fmt, ...)
> {
> struct va_format vaf;
> va_list args;
> int rtn;
>
> va_start(args, fmt);
>
> vaf.fmt = fmt;
> vaf.va = &args;
>
> rtn = printk("%sath6kl: %pV", level, &vaf);
>
> va_end(args);
>
> return rtn;
> }
> EXPORT_SYMBOL(ath6kl_printk);
>
>> Anyway, something dirty like that will not enter the kernel but i will try
>> to do stack debugging on an example program to see why that happens.
>
> I would suggest just copy-pasting parts of the ath6kl trace code into
> the xHCI driver, and changing one of the xhci_dbg() calls to use that
> code, and see if it works. If it doesn't work, send out an RFC patch
> (using the Cc list I've used), and we'll try to figure out what's going
> wrong.
The biggest challenge in adding tracepoints is identifying what you want
to trace. While tracing debug messages can be convenient the real
strength is in tracing code artifacts like for USB the thing that comes
to my mind first is defining tracepoint for the urb and there are
probably other internals that are informational.
Regards,
Arend
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox