2013-08-15 11:51:59

by Vladimir Kondratiev

[permalink] [raw]
Subject: [PATCH v14 0/2] P2P find phase offload

Addressed input collected:

- remove NL80211_FEATURE_P2P_PROBE_RESP_OFFLOAD feature flag because
NL80211_RXMGMT_FLAG_REPLIED flag in cfg80211_rx_mgmt() provide needed
info on per-frame basis
- validate min/max discoverable interval parameters
- use kcalloc to allocate array

Vladimir Kondratiev (2):
cfg80211: add 'flags' to cfg80211_rx_mgmt()
cfg80211: P2P find phase offload

drivers/net/wireless/ath/ath6kl/wmi.c | 7 +-
drivers/net/wireless/ath/wil6210/wmi.c | 2 +-
drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 4 +-
drivers/net/wireless/mwifiex/util.c | 4 +-
include/net/cfg80211.h | 79 +++++++++-
include/uapi/linux/nl80211.h | 27 ++++
net/mac80211/rx.c | 3 +-
net/wireless/mlme.c | 4 +-
net/wireless/nl80211.c | 216 +++++++++++++++++++++++++-
net/wireless/nl80211.h | 2 +-
net/wireless/rdev-ops.h | 19 +++
net/wireless/trace.h | 44 ++++++
12 files changed, 394 insertions(+), 17 deletions(-)

--
1.8.1.2



2013-08-30 11:36:19

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v14 2/2] cfg80211: P2P find phase offload

On Mon, 2013-08-26 at 16:26 +0300, Vladimir Kondratiev wrote:
> On Friday, August 23, 2013 04:16:49 PM Johannes Berg wrote:
> > Ilan also pointed out to me that it might be necessary to document (and
> > enforce?) that scan/sched_scan can't be executed in parallel? Or can
> > they?
>
> Indeed, one can't simultaneously scan and do p2p_find on the same hardware,
> because of contradictionary requirements. I documented it in start_p2p_find:
> > + * P2P find can't run concurrently with ROC or scan,
> > + * conflict with scan detected by cfg80211 and -EBUSY returned;
> > + * and driver should check for ROC and return -EBUSY to indicate
> > conflict.
>
> Shall I elaborate more, what is appropriate location for this explanation?

Ah, I missed that. I was thinking it should be in nl80211.h so that
users of the API would also be told about it.

In fact, it looks like you have no documentation in nl80211.h for the
new commands at all, you should change that anyway. I'm surprised we
never discussed that before, sorry.

> Regarding locking - I feel I need to do it similar to scan. There, we have
> locked inner function that is invoked from cfg80211_wq.

Yes, we put the 'done' completion on the workqueue. However, we don't
have an explicit stop operation for scan, so things are a bit simpler -
scan is triggered and eventually stops. There's no race.

If you do the same, there's a race:

driver stops by itself
driver calls stop
stop queued to workqueue
userspace calls stop
driver stop invoked

This could lead to confusion I think.

> And, overall - maybe reuse scan pattern in other aspects -
> allocate cfg80211_p2p_find_params like it is done for cfg80211_scan_request
> (and rename struct to cfg80211_p2p_find_request).
> instead of wdev->p2p_find_active, have rdev->p2p_find_req - this will sort
> concurrent p2p_find for sibling wdevs
>
> Also, same pattern will make p2p_find API more intuitive if it follows same
> conventions as scan

I guess you can make that work, just due the race above it's a bit
trickier than scan.

johannes


2013-08-15 11:52:18

by Vladimir Kondratiev

[permalink] [raw]
Subject: [PATCH v14 2/2] cfg80211: P2P find phase offload

Allow to implement P2P find phase in the driver/firmware.

Offload scheme designed as follows:

- Driver provide methods start_p2p_find and stop_p2p_find in the cfg80211_ops;
- wpa_supplicant analyses methods supported to discover p2p offload support;
to perform p2p scan, wpa_supplicant:
- perform legacy scan, through driver's cfg80211_ops 'scan' method
- configure rx management filter to get probe-request and probe-response frames
- start p2p find via driver's cfg80211_ops start_p2p_find method
- driver start p2p find with hardware and notify wpa_supplicant with
cfg80211_p2p_find_notify_start()
- driver/firmware toggle search/listen states. Received probe-request and
probe-response frames passed to the wpa_supplicant via cfg80211_rx_mgmt;
replied by driver/firmware probe-request frames indicated with
NL80211_RXMGMT_FLAG_REPLIED flag
- when wpa_supplicant wants to stop p2p find, it calls driver's
cfg80211_ops stop_p2p_find method. Alternatively, driver/firmware may decide
to stop p2p find. In all cases, driver notifies wpa_supplicant using
cfg80211_p2p_find_notify_end()

All driver to user space communication done through nl80211 layer.

Offloaded P2P find does not support variations like progressive scan.

Signed-off-by: Vladimir Kondratiev <[email protected]>
---
include/net/cfg80211.h | 76 ++++++++++++++++
include/uapi/linux/nl80211.h | 10 +++
net/wireless/nl80211.c | 210 +++++++++++++++++++++++++++++++++++++++++++
net/wireless/rdev-ops.h | 19 ++++
net/wireless/trace.h | 44 +++++++++
5 files changed, 359 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index e1ee9d3..c831f8b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1924,6 +1924,36 @@ struct cfg80211_update_ft_ies_params {
};

/**
+ * struct cfg80211_p2p_find_params - parameters for P2P find
+ * @probe_ie: extra IE's for probe frames
+ * @probe_ie_len: length, bytes, of @probe_ie
+ * @probe_resp_ie: extra IE's for probe response frames
+ * @probe_resp_ie_len: length, bytes, of @probe_resp_ie
+ * Driver/firmware may add additional IE's as well as modify
+ * provided ones; typical IE's to be added are
+ * WLAN_EID_EXT_SUPP_RATES, WLAN_EID_DS_PARAMS,
+ * WLAN_EID_HT_CAPABILITY.
+ * @min_discoverable_interval: and
+ * @max_discoverable_interval: min/max for random multiplier of 100TU's
+ * for the listen state duration
+ * @listen_channel: channel to listen on; not NULL
+ * @n_channels: number of channels to operate on
+ * @channels: channels to operate on; non-empty list
+ */
+struct cfg80211_p2p_find_params {
+ const u8 *probe_ie;
+ size_t probe_ie_len;
+ const u8 *probe_resp_ie;
+ size_t probe_resp_ie_len;
+ u32 min_discoverable_interval;
+ u32 max_discoverable_interval;
+ struct ieee80211_channel *listen_channel;
+
+ int n_channels;
+ struct ieee80211_channel **channels;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -2165,6 +2195,24 @@ struct cfg80211_update_ft_ies_params {
* @set_coalesce: Set coalesce parameters.
*
* @channel_switch: initiate channel-switch procedure (with CSA)
+ *
+ * @start_p2p_find: start P2P find phase
+ * Parameters include IEs for probe/probe-resp frames;
+ * and channels to operate on.
+ * Parameters are not retained after call, driver need to copy data if
+ * it need it later.
+ * P2P find can't run concurrently with ROC or scan,
+ * conflict with scan detected by cfg80211 and -EBUSY returned;
+ * and driver should check for ROC and return -EBUSY to indicate conflict.
+ * While performing P2P discovery, driver should report received
+ * probe-request and probe-response frames via cfg80211_rx_mgmt
+ * accordingly to the rx mgmt filter, as set by mgmt_frame_register().
+ * If device indicates NL80211_FEATURE_P2P_PROBE_RESP_OFFLOAD, it may
+ * reply some matching probes and replied probes may be not reported to
+ * the upper layers; otherwise it must not reply any probe.
+ * @stop_p2p_find: stop P2P find phase
+ * After stopping p2p find, driver should call
+ * cfg80211_p2p_find_notify_end() to inform upper layers
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2406,6 +2454,12 @@ struct cfg80211_ops {
int (*channel_switch)(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_csa_settings *params);
+
+ int (*start_p2p_find)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_p2p_find_params *params);
+ void (*stop_p2p_find)(struct wiphy *wiphy,
+ struct wireless_dev *wdev);
};

/*
@@ -3062,6 +3116,7 @@ struct wireless_dev {
struct mutex mtx;

bool use_4addr, p2p_started;
+ bool p2p_find_active; /* protected by rtnl */

u8 address[ETH_ALEN] __aligned(sizeof(u16));

@@ -4376,6 +4431,27 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
*/
void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);

+/**
+ * cfg80211_p2p_find_notify_start - report p2p find phase started
+ * @wdev: the wireless device reporting the event
+ * @gfp: allocation flags
+ */
+void cfg80211_p2p_find_notify_start(struct wireless_dev *wdev, gfp_t gfp);
+
+/**
+ * cfg80211_p2p_find_notify_end - report p2p find phase ended
+ * @wdev: the wireless device reporting the event
+ * @gfp: allocation flags
+ *
+ * p2p find phase may be ended either unsolicited or in response to
+ * ops->stop_p2p_find. If called out of context of ops->stop_p2p_find,
+ * should be protected with rtnl_lock()
+ *
+ * In any case, if @start_p2p_find from driver's struct cfg80211_ops called,
+ * @cfg80211_p2p_find_notify_end should be eventually called
+ */
+void cfg80211_p2p_find_notify_end(struct wireless_dev *wdev, gfp_t gfp);
+
/* Logging, debugging and troubleshooting/diagnostic helpers. */

/* wiphy_printk helpers, similar to dev_printk */
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index fc1665e..5a00bd61 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -853,6 +853,9 @@ enum nl80211_commands {

NL80211_CMD_CHANNEL_SWITCH,

+ NL80211_CMD_START_P2P_FIND,
+ NL80211_CMD_STOP_P2P_FIND,
+
/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
@@ -1497,6 +1500,10 @@ enum nl80211_commands {
* As specified in the enum nl80211_rxmgmt_flags.
* Passed from cfg80211_rx_mgmt()
*
+ * @NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL:
+ * @NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL: min/max discoverable interval
+ * for the p2p find, multiple of 100 TUs, represented as u32
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1807,6 +1814,9 @@ enum nl80211_attrs {

NL80211_ATTR_RXMGMT_FLAGS,

+ NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL,
+ NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL,
+
/* 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 8dbb289..d2a85f1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -354,6 +354,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[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 },
+ [NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL] = { .type = NLA_U32 },
};

/* policy for the key attributes */
@@ -1431,6 +1433,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
+ CMD(start_p2p_find, START_P2P_FIND);
+ CMD(stop_p2p_find, STOP_P2P_FIND);
}

#ifdef CONFIG_NL80211_TESTMODE
@@ -8762,6 +8766,157 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
return 0;
}

+static int p2p_find_handle_channel(struct wiphy *wiphy,
+ struct cfg80211_p2p_find_params *params,
+ u32 freq)
+{
+ struct ieee80211_channel *chan = ieee80211_get_channel(wiphy, freq);
+
+ if (!chan)
+ return -EINVAL;
+
+ /* ignore disabled channels */
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return 0;
+
+ params->channels[params->n_channels++] = chan;
+ return 0;
+}
+
+static int nl80211_start_p2p_find(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ struct wiphy *wiphy = &rdev->wiphy;
+ /*
+ * Defaults, as defined in the spec
+ * Ref: Wi-Fi Peer-to-Peer (P2P) Technical Specification v1.1
+ * Clause: 3.1.2.1.3 Find Phase
+ */
+ struct cfg80211_p2p_find_params params = {
+ .min_discoverable_interval = 1,
+ .max_discoverable_interval = 3,
+ };
+ /* P2P spec defines social channels 1,6,11 @2.4GHz and 2 @60GHz */
+ static u32 social_freqs[] = {2412, 2437, 2462, 60480};
+ struct nlattr *attr_freq = info->attrs[NL80211_ATTR_SCAN_FREQUENCIES];
+ struct nlattr *attr_l_freq = info->attrs[NL80211_ATTR_WIPHY_FREQ];
+ struct nlattr *attr;
+ int err, tmp, n_channels;
+ struct ieee80211_channel *chan;
+
+ if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->start_p2p_find || !rdev->ops->stop_p2p_find)
+ return -EOPNOTSUPP;
+
+ if (!attr_l_freq)
+ return -EINVAL;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]))
+ return -EINVAL;
+
+ if (rdev->scan_req)
+ return -EBUSY;
+
+ if (wdev->p2p_find_active)
+ return -EBUSY;
+
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(attr_l_freq));
+ if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+ params.listen_channel = chan;
+
+ attr = info->attrs[NL80211_ATTR_MIN_DISCOVERABLE_INTERVAL];
+ if (attr)
+ params.min_discoverable_interval = nla_get_u32(attr);
+
+ attr = info->attrs[NL80211_ATTR_MAX_DISCOVERABLE_INTERVAL];
+ if (attr)
+ params.max_discoverable_interval = nla_get_u32(attr);
+
+ if (params.min_discoverable_interval >
+ params.max_discoverable_interval)
+ return -EINVAL;
+
+ if (attr_freq) {
+ n_channels = validate_scan_freqs(attr_freq);
+ if (!n_channels)
+ return -EINVAL;
+ } else {
+ n_channels = ARRAY_SIZE(social_freqs);
+ }
+ params.channels = kcalloc(n_channels, sizeof(*params.channels),
+ GFP_KERNEL);
+ if (!params.channels)
+ return -ENOMEM;
+
+ if (attr_freq) {
+ /* user specified, bail out if channel not found */
+ nla_for_each_nested(attr, attr_freq, tmp) {
+ err = p2p_find_handle_channel(wiphy, &params,
+ nla_get_u32(attr));
+ if (err)
+ goto out_free;
+ }
+ } else { /* all supported social channels */
+ /* ignore errors for non-existing channels */
+ for (tmp = 0; tmp < ARRAY_SIZE(social_freqs); tmp++) {
+ p2p_find_handle_channel(wiphy, &params,
+ social_freqs[tmp]);
+ }
+ }
+ if (!params.n_channels) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ attr = info->attrs[NL80211_ATTR_IE];
+ if (attr) {
+ params.probe_ie_len = nla_len(attr);
+ params.probe_ie = nla_data(attr);
+ }
+
+ attr = info->attrs[NL80211_ATTR_IE_PROBE_RESP];
+ if (attr) {
+ params.probe_resp_ie_len = nla_len(attr);
+ params.probe_resp_ie = nla_data(attr);
+ }
+
+ wdev->p2p_find_active = true;
+ err = rdev_start_p2p_find(rdev, wdev, &params);
+ if (err)
+ wdev->p2p_find_active = false;
+
+out_free:
+ kfree(params.channels);
+
+ return err;
+}
+
+static int nl80211_stop_p2p_find(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->start_p2p_find || !rdev->ops->stop_p2p_find)
+ return -EOPNOTSUPP;
+
+ if (!wdev->p2p_find_active)
+ return -ENOENT;
+
+ rdev_stop_p2p_find(rdev, wdev);
+
+ return 0;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -9435,6 +9590,22 @@ static struct genl_ops nl80211_ops[] = {
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_START_P2P_FIND,
+ .doit = nl80211_start_p2p_find,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_STOP_P2P_FIND,
+ .doit = nl80211_stop_p2p_find,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
.doit = nl80211_get_protocol_features,
.policy = nl80211_policy,
@@ -11126,6 +11297,45 @@ void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
}
EXPORT_SYMBOL(cfg80211_tdls_oper_request);

+static
+void cfg80211_p2p_find_notify(struct wireless_dev *wdev, int cmd, gfp_t gfp)
+{
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+
+ trace_cfg80211_p2p_find_notify(wdev, cmd);
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
+void cfg80211_p2p_find_notify_start(struct wireless_dev *wdev, gfp_t gfp)
+{
+ cfg80211_p2p_find_notify(wdev, NL80211_CMD_START_P2P_FIND, gfp);
+}
+EXPORT_SYMBOL(cfg80211_p2p_find_notify_start);
+
+void cfg80211_p2p_find_notify_end(struct wireless_dev *wdev, gfp_t gfp)
+{
+ ASSERT_RTNL();
+ cfg80211_p2p_find_notify(wdev, NL80211_CMD_STOP_P2P_FIND, gfp);
+ wdev->p2p_find_active = false;
+}
+EXPORT_SYMBOL(cfg80211_p2p_find_notify_end);
+
static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state,
void *_notify)
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index de870d4..6c3f463 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -935,4 +935,23 @@ static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
return ret;
}

+static inline int rdev_start_p2p_find(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_p2p_find_params *params)
+{
+ int ret;
+ trace_rdev_start_p2p_find(&rdev->wiphy, wdev, params);
+ ret = rdev->ops->start_p2p_find(&rdev->wiphy, wdev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_stop_p2p_find(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_stop_p2p_find(&rdev->wiphy, wdev);
+ rdev->ops->stop_p2p_find(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index f0ebdcd..ec5b0cd 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1874,6 +1874,36 @@ TRACE_EVENT(rdev_channel_switch,
__entry->counter_offset_presp)
);

+TRACE_EVENT(rdev_start_p2p_find,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_p2p_find_params *params),
+ TP_ARGS(wiphy, wdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u32, min_di)
+ __field(u32, max_di)
+ __field(u32, listen_freq)
+ __field(int, n_channels)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->min_di = params->min_discoverable_interval;
+ __entry->max_di = params->max_discoverable_interval;
+ __entry->listen_freq = params->listen_channel->center_freq;
+ __entry->n_channels = params->n_channels;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", listen %d MHz, disc. int. [%d..%d], n_channels %d",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->listen_freq,
+ __entry->min_di, __entry->max_di, __entry->n_channels)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_find,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
@@ -2557,6 +2587,20 @@ TRACE_EVENT(cfg80211_ft_event,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
);

+TRACE_EVENT(cfg80211_p2p_find_notify,
+ TP_PROTO(struct wireless_dev *wdev, int cmd),
+ TP_ARGS(wdev, cmd),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(int, cmd)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cmd = cmd;
+ ),
+ TP_printk(WDEV_PR_FMT ", cmd: %d", WDEV_PR_ARG, __entry->cmd)
+);
+
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */

#undef TRACE_INCLUDE_PATH
--
1.8.1.2


2013-08-23 14:07:16

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v14 1/2] cfg80211: add 'flags' to cfg80211_rx_mgmt()

On Thu, 2013-08-15 at 14:51 +0300, Vladimir Kondratiev wrote:
> Flags intended to report various auxiliary information.
> Introduced flag NL80211_RXMGMT_FLAG_REPLIED to report
> whether frame was replied by the device/driver.

Applied, with some small changes.

johannes


2013-08-26 13:26:14

by Vladimir Kondratiev

[permalink] [raw]
Subject: Re: [PATCH v14 2/2] cfg80211: P2P find phase offload

On Friday, August 23, 2013 04:16:49 PM Johannes Berg wrote:
> Ilan also pointed out to me that it might be necessary to document (and
> enforce?) that scan/sched_scan can't be executed in parallel? Or can
> they?

Indeed, one can't simultaneously scan and do p2p_find on the same hardware,
because of contradictionary requirements. I documented it in start_p2p_find:
> + * P2P find can't run concurrently with ROC or scan,
> + * conflict with scan detected by cfg80211 and -EBUSY returned;
> + * and driver should check for ROC and return -EBUSY to indicate
> conflict.

Shall I elaborate more, what is appropriate location for this explanation?

Regarding locking - I feel I need to do it similar to scan. There, we have
locked inner function that is invoked from cfg80211_wq.

And, overall - maybe reuse scan pattern in other aspects -
allocate cfg80211_p2p_find_params like it is done for cfg80211_scan_request
(and rename struct to cfg80211_p2p_find_request).
instead of wdev->p2p_find_active, have rdev->p2p_find_req - this will sort
concurrent p2p_find for sibling wdevs

Also, same pattern will make p2p_find API more intuitive if it follows same
conventions as scan

Comments?

2013-08-23 14:16:52

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v14 2/2] cfg80211: P2P find phase offload

On Thu, 2013-08-15 at 14:51 +0300, Vladimir Kondratiev wrote:

[...]

Generally, I think this looks good now. Some small comments:

> - configure rx management filter to get probe-request and probe-response frames

This is misleading, there's no "filter" that the supplicant can
configure. It can (and should) subscribe to these frames, but that's not
really the same thing.

> - start p2p find via driver's cfg80211_ops start_p2p_find method
> - driver start p2p find with hardware and notify wpa_supplicant with
> cfg80211_p2p_find_notify_start()
> - driver/firmware toggle search/listen states. Received probe-request and
> probe-response frames passed to the wpa_supplicant via cfg80211_rx_mgmt;
> replied by driver/firmware probe-request frames indicated with
> NL80211_RXMGMT_FLAG_REPLIED flag

ANSWERED now

> Alternatively, driver/firmware may decide to stop p2p find.

This is somewhat problematic, see below.

> All driver to user space communication done through nl80211 layer.

Well, umm, ok. That seems kinda obvious :)


> @@ -3062,6 +3116,7 @@ struct wireless_dev {
> struct mutex mtx;
>
> bool use_4addr, p2p_started;
> + bool p2p_find_active; /* protected by rtnl */

Should that really be in the wdev, i.e. is it OK to run p2p_find on
multiple wdevs of the same wiphy? That seems odd.

> +/**
> + * cfg80211_p2p_find_notify_end - report p2p find phase ended
> + * @wdev: the wireless device reporting the event
> + * @gfp: allocation flags
> + *
> + * p2p find phase may be ended either unsolicited or in response to
> + * ops->stop_p2p_find. If called out of context of ops->stop_p2p_find,
> + * should be protected with rtnl_lock()

I think this is problematic, how are drivers going to be able to
guarantee this? They'd most likely call this from some RX context if the
firmware gives up, or firmware restart context, and not really know
whether the RTNL is held or not? Then drivers will have to make it
async, but then we might just as well make it async in cfg80211, no?

Thinking about this, I'm also starting to think that requiring this to
be called when the stop_p2p_find() operation is called is pretty
pointless, as cfg80211 can just do it itself? If the driver still calls
it, it can obviously suppress the extra event.

> +void cfg80211_p2p_find_notify_end(struct wireless_dev *wdev, gfp_t gfp);

That gfp argument is also fairly useless, if you must hold the RTNL it's
likely that you're able to sleep, unless doing something 'exotic' like
rtnl_lock()
rcu_read_lock()
...
p2p_find_notify_end()

but that would be strange and not usually needed?

> + if (!wdev->p2p_find_active)
> + return -ENOENT;
> +
> + rdev_stop_p2p_find(rdev, wdev);

Here you could do something like

if p2p_find_active
send_event()
p2p_find_active = false

so drivers don't have to call it?

> +void cfg80211_p2p_find_notify_end(struct wireless_dev *wdev, gfp_t gfp)
> +{
> + ASSERT_RTNL();

In fact, ASSERT_RTNL() can only be called when able to sleep, because it
attempts to lock or so ... unless that changed to lockdep recently?


Ilan also pointed out to me that it might be necessary to document (and
enforce?) that scan/sched_scan can't be executed in parallel? Or can
they?

johannes


2013-08-15 11:52:22

by Vladimir Kondratiev

[permalink] [raw]
Subject: [PATCH v14 1/2] cfg80211: add 'flags' to cfg80211_rx_mgmt()

Flags intended to report various auxiliary information.
Introduced flag NL80211_RXMGMT_FLAG_REPLIED to report
whether frame was replied by the device/driver.

Signed-off-by: Vladimir Kondratiev <[email protected]>
---
drivers/net/wireless/ath/ath6kl/wmi.c | 7 +++----
drivers/net/wireless/ath/wil6210/wmi.c | 2 +-
drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 4 ++--
drivers/net/wireless/mwifiex/util.c | 4 ++--
include/net/cfg80211.h | 3 ++-
include/uapi/linux/nl80211.h | 17 +++++++++++++++++
net/mac80211/rx.c | 3 +--
net/wireless/mlme.c | 4 ++--
net/wireless/nl80211.c | 6 ++++--
net/wireless/nl80211.h | 2 +-
10 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 87aefb4..546d5da 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -568,8 +568,8 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len,
dlen, freq, vif->probe_req_report);

if (vif->probe_req_report || vif->nw_type == AP_NETWORK)
- cfg80211_rx_mgmt(&vif->wdev, freq, 0,
- ev->data, dlen, GFP_ATOMIC);
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0,
+ GFP_ATOMIC);

return 0;
}
@@ -608,8 +608,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len,
return -EINVAL;
}
ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
- cfg80211_rx_mgmt(&vif->wdev, freq, 0,
- ev->data, dlen, GFP_ATOMIC);
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, GFP_ATOMIC);

return 0;
}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 5220f15..063963e 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -339,7 +339,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
}
} else {
cfg80211_rx_mgmt(wil->wdev, freq, signal,
- (void *)rx_mgmt_frame, d_len, GFP_KERNEL);
+ (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL);
}
}

diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index 79555f0..d7a9745 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -1430,7 +1430,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
IEEE80211_BAND_5GHZ);

wdev = &ifp->vif->wdev;
- cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len,
+ cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0,
GFP_ATOMIC);

kfree(mgmt_frame);
@@ -1895,7 +1895,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ);

- cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len,
+ cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0,
GFP_ATOMIC);

brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n",
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index e57ac0d..5d9e150 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -171,8 +171,8 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv,
rx_pd->rx_pkt_length = cpu_to_le16(pkt_len);

cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq,
- CAL_RSSI(rx_pd->snr, rx_pd->nf),
- skb->data, pkt_len, GFP_ATOMIC);
+ CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len,
+ 0, GFP_ATOMIC);

return 0;
}
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index b7495c7..e1ee9d3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4055,6 +4055,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
* @sig_dbm: signal strength in mBm, or 0 if unknown
* @buf: Management frame (header + body)
* @len: length of the frame data
+ * @flags: flags, as defined in enum nl80211_rxmgmt_flags
* @gfp: context flags
*
* This function is called whenever an Action frame is received for a station
@@ -4066,7 +4067,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
* driver is responsible for rejecting the frame.
*/
bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm,
- const u8 *buf, size_t len, gfp_t gfp);
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp);

/**
* cfg80211_mgmt_tx_status - notification of TX status for management frame
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 1f42bc3..fc1665e 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1493,6 +1493,10 @@ enum nl80211_commands {
* @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
* field in the probe response (%NL80211_ATTR_PROBE_RESP).
*
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ * As specified in the enum nl80211_rxmgmt_flags.
+ * Passed from cfg80211_rx_mgmt()
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1801,6 +1805,8 @@ enum nl80211_attrs {
NL80211_ATTR_CSA_C_OFF_BEACON,
NL80211_ATTR_CSA_C_OFF_PRESP,

+ NL80211_ATTR_RXMGMT_FLAGS,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
@@ -3901,4 +3907,15 @@ enum nl80211_crit_proto_id {
/* maximum duration for critical protocol measures */
#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */

+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_REPLIED: frame was replied by device/driver.
+ */
+enum nl80211_rxmgmt_flags {
+ NL80211_RXMGMT_FLAG_REPLIED = 1 << 0,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 6b85f95..6698a34 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2684,8 +2684,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
sig = status->signal;

if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig,
- rx->skb->data, rx->skb->len,
- GFP_ATOMIC)) {
+ rx->skb->data, rx->skb->len, 0, GFP_ATOMIC)) {
if (rx->sta)
rx->sta->rx_packets++;
dev_kfree_skb(rx->skb);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index bfac5e1..8d49c1c 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -621,7 +621,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
}

bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
- const u8 *buf, size_t len, gfp_t gfp)
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp)
{
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
@@ -664,7 +664,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
/* Indicate the received Action frame to user space */
if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
freq, sig_mbm,
- buf, len, gfp))
+ buf, len, flags, gfp))
continue;

result = true;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index adf1e98..8dbb289 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10442,7 +10442,7 @@ EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlportid,
int freq, int sig_dbm,
- const u8 *buf, size_t len, gfp_t gfp)
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp)
{
struct net_device *netdev = wdev->netdev;
struct sk_buff *msg;
@@ -10465,7 +10465,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
(sig_dbm &&
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
- nla_put(msg, NL80211_ATTR_FRAME, len, buf))
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+ (flags &&
+ nla_put_u32(msg, NL80211_ATTR_RXMGMT_FLAGS, flags)))
goto nla_put_failure;

genlmsg_end(msg, hdr);
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 44341bf..2c0f2b3 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -66,7 +66,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlpid,
int freq, int sig_dbm,
- const u8 *buf, size_t len, gfp_t gfp);
+ const u8 *buf, size_t len, u32 flags, gfp_t gfp);

void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
--
1.8.1.2