2013-02-04 12:50:11

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv8 0/3] Add DFS master ability

This is patch series version 8 of the DFS master ability, based on Victor
Goldenshteins original work. See changelog in the various patches for
details. The main changes to v7 are:

* export channel state to userspace
* add event when NOP is over
* use wdev->channel instead of wdev->preset_chandef.chan for CAC
* remove NL80211_FEATURE_DFS
* many small fixes
* claim authorship for patches (but leave note for victors original patches
- please mail me if you find this inappropriate)
* rebased on latest master, of course

As always, thanks a lot to Johannes and Zefir for review and support.
I've verified the implementation (again) with ath9k a patched hostapd (patches
to be released later). This patchset is more an RFC with a few open questions,
any comment/review/etc are very much appreciated.

iw patches for interface combinations and DFS state handling are attached as well.

One open issue remains: On my system, DFS channels are flagged with NO_IBSS and
DISABLED, this is due to my country code setting to 00. I can manually circumvent
that, but should we find a proper solution for this? This regularity intersection
gives me quite a bit of a headache ...

Simon Wunderlich (3):
nl80211/cfg80211: add radar detection command/event
mac80211: add radar detection command/event
nl80211: allow DFS in start_ap

include/net/cfg80211.h | 46 +++++++++++++++
include/net/mac80211.h | 16 ++++++
include/uapi/linux/nl80211.h | 58 +++++++++++++++++++
net/mac80211/cfg.c | 36 +++++++++++-
net/mac80211/chan.c | 33 +++++++++++
net/mac80211/ieee80211_i.h | 11 ++++
net/mac80211/iface.c | 12 ++++
net/mac80211/mlme.c | 32 +++++++++++
net/mac80211/scan.c | 3 +
net/mac80211/trace.h | 19 ++++++
net/wireless/chan.c | 129 ++++++++++++++++++++++++++++++++++++++++-
net/wireless/core.c | 3 +
net/wireless/core.h | 18 ++++++
net/wireless/mlme.c | 127 +++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.c | 130 ++++++++++++++++++++++++++++++++++++++++--
net/wireless/nl80211.h | 7 +++
net/wireless/reg.c | 3 +
net/wireless/scan.c | 10 ----
net/wireless/trace.h | 33 +++++++++++
19 files changed, 708 insertions(+), 18 deletions(-)

--
1.7.10.4



2013-02-06 17:23:50

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv8 1/3] nl80211/cfg80211: add radar detection command/event

On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:

> /**
> + * cfg80211_chandef_dfs_required - checks if radar detection
> + * is required on any of the channels

pretty sure this isn't valid kernel-doc, I think it has to fit into one
line

> @@ -3663,6 +3694,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
> gfp_t gfp);
>
> /**
> + * cfg80211_radar_event - radar detection event
> + * @dev: network device
> + * @chandef: chandef for the current channel
> + * @event: type of event
> + * @gfp: context flags
> + *
> + * This function is called when a radar is detected or a CAC event occured

occurred :)


> @@ -2014,6 +2029,7 @@ enum nl80211_band_attr {
> * (100 * dBm).
> * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
> * currently defined
> + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS

missed docs for _DFS_TIME, maybe should put it before _MAX :)
also, what values does this take?

> +/**
> + * enum nl80211_dfs_state - DFS states for channels
> + *
> + * Channel states used by the DFS code.
> + *
> + * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
> + * check (CAC) must be performed before using it for AP or IBSS.
> + * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
> + * is therefore marked as not available.
> + * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
> + */
> +
> +enum nl80211_dfs_state {
> + NL80211_DFS_USABLE = 0,

I don't really see a reason for explicit values?

> +static inline int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)

no need for inline

> +int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
> + const struct cfg80211_chan_def *chandef)
> +{
> + int width;
> + int r;
> +
> + if (WARN_ON(!cfg80211_chandef_valid(chandef)))
> + return -EINVAL;
> +
> + width = cfg80211_chandef_get_width(chandef);
> + if (width < 0)
> + return -EINVAL;
> +
> + r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
> + width);
> + if (r)
> + return r;
> +
> + if (!chandef->center_freq2)
> + return 0;
> +
> + return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
> + width);
> +}

If you don't export it why is it in the public header file -- either you
need to export it as well or it doesn't need to be in cfg80211.h :)

> @@ -454,6 +462,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
> chan, chanmode, 0);
> }
>
> +static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
> +{
> + unsigned long end = jiffies;
> +
> + if (end >= start)
> + return jiffies_to_msecs(end - start);
> +
> + return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
> +}

That seems a bit magic, like it should be hidden away in jiffies.h. Is
there really no function already to do something like this?

> +static void cfg80211_notify_nop_ended(struct cfg80211_registered_device *rdev,
> + struct ieee80211_channel *c)

indentation :)

> +{
> + struct wireless_dev *wdev;
> + struct cfg80211_chan_def chandef;
> +
> + cfg80211_chandef_create(&chandef, c, NL80211_CHAN_NO_HT);
> +
> + mutex_lock(&rdev->devlist_mtx);
> + list_for_each_entry(wdev, &rdev->wdev_list, list) {
> + if (!wdev->netdev)
> + continue;
> +
> + if (!netif_running(wdev->netdev))
> + continue;
> +
> + nl80211_radar_notify(rdev, &chandef,
> + NL80211_RADAR_NOP_FINISHED,
> + wdev->netdev, GFP_ATOMIC);
> + }

Why for all interfaces? This is multicast anyway, so why not just send
the event without an interface index instead?

> --- a/net/wireless/scan.c
> +++ b/net/wireless/scan.c
> @@ -1155,16 +1155,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
> }
> }
>
> -static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
> -{
> - unsigned long end = jiffies;
> -
> - if (end >= start)
> - return jiffies_to_msecs(end - start);
> -
> - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
> -}

Oh, heh. Oh well ...

I think I should probably look at locking too ...

johannes


2013-02-04 12:50:11

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv8 3/3] nl80211: allow DFS in start_ap

This patch enables DFS within nl80211/cfg80211. start_ap now checks if
DFS is allowed on the channel with the selected channel width.

Signed-off-by: Simon Wunderlich <[email protected]>
---
Changes to PATCHv7:
* set radar required in AP params

Changes to PATCHv6:
* let reg_can_beacon() and others check for channel states, simplify
start_ap
* check using cfg80211_chandef_dfs_required

Changes to PATCHv5:
* check radar detection width
---
net/wireless/nl80211.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 00e6599..2a71935 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2714,6 +2714,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_ap_settings params;
int err;
+ u8 radar_detect_width = 0;

if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
@@ -2832,9 +2833,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
return -EINVAL;

+ err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
+ if (err < 0)
+ return -EINVAL;
+ if (err) {
+ radar_detect_width = BIT(params.chandef.width);
+ params.radar_required = true;
+ }
+
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
- CHAN_MODE_SHARED);
+ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+ params.chandef.chan,
+ CHAN_MODE_SHARED,
+ radar_detect_width);
mutex_unlock(&rdev->devlist_mtx);

if (err)
--
1.7.10.4


2013-02-04 12:50:11

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCH 1/2] iw: add radar detect widths to phy info

Signed-off-by: Simon Wunderlich <[email protected]>
---
info.c | 22 +++++++++++++++++++++-
interface.c | 2 +-
iw.h | 1 +
3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/info.c b/info.c
index 08dbfc0..14db37f 100644
--- a/info.c
+++ b/info.c
@@ -245,6 +245,7 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+ [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
};
struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
@@ -296,11 +297,30 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
}
printf(",\n\t\t ");

- printf("total <= %d, #channels <= %d%s\n",
+ printf("total <= %d, #channels <= %d%s",
nla_get_u32(tb_comb[NL80211_IFACE_COMB_MAXNUM]),
nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]),
tb_comb[NL80211_IFACE_COMB_STA_AP_BI_MATCH] ?
", STA/AP BI must match" : "");
+ if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) {
+ unsigned long widths = nla_get_u32(tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]);
+
+ if (widths) {
+ int width;
+ bool first = true;
+
+ printf(", radar detect widths: {");
+ for (width = 0; width < 32; width++)
+ if (widths & (1 << width)) {
+ printf("%s %s",
+ first ? "":",",
+ channel_width_name(width));
+ first = false;
+ }
+ printf(" }\n");
+ }
+ }
+ printf("\n");
broken_combination:
;
}
diff --git a/interface.c b/interface.c
index 26e72dc..b52c8dd 100644
--- a/interface.c
+++ b/interface.c
@@ -262,7 +262,7 @@ static char *channel_type_name(enum nl80211_channel_type channel_type)
}
}

-static char *channel_width_name(enum nl80211_chan_width width)
+char *channel_width_name(enum nl80211_chan_width width)
{
switch (width) {
case NL80211_CHAN_WIDTH_20_NOHT:
diff --git a/iw.h b/iw.h
index 3e83bd2..d56e358 100644
--- a/iw.h
+++ b/iw.h
@@ -145,6 +145,7 @@ void print_ampdu_spacing(__u8 spacing);
void print_ht_capability(__u16 cap);
void print_vht_info(__u32 capa, const __u8 *mcs);

+char *channel_width_name(enum nl80211_chan_width width);
const char *iftype_name(enum nl80211_iftype iftype);
const char *command_name(enum nl80211_commands cmd);
int ieee80211_channel_to_frequency(int chan);
--
1.7.10.4


2013-02-06 17:33:15

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv8 2/3] mac80211: add radar detection command/event

On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:

> *
> * @channel: the channel to tune to
> * @channel_type: the channel (HT) type
> + * @radar_enabled: whether radar detection is enabled on this channel

There's only one channel for ieee80211_conf ;-)

> /**
> + * ieee80211_radar_detected - inform a configured connection that
> + * radar was detected on the current channel
> + *
> + * @vif: &struct ieee80211_vif pointer from the add_interface callback.
> + * @gfp: context flags.
> + */
> +void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp);

Given the way this works in cfg80211 and my comment there, it seems
pointless to report per vif, but rather should be per HW?

> + res = ieee80211_vif_use_channel(sdata, chandef,
> + IEEE80211_CHANCTX_SHARED);
> + if (res)
> + return -EBUSY;

return res? This really can't fail here (except for memory allocation
etc.) so -EBUSY is a bit odd.

> @@ -753,6 +753,9 @@ struct ieee80211_sub_if_data {
> int user_power_level; /* in dBm */
> int ap_power_level; /* in dBm */
>
> + bool radar_required;
> + struct delayed_work dfs_cac_timer_work;

Does the work struct make sense here? It seems like an inherently global
operation, so should that be in ieee80211_local? We should check anyway
if radar detection is requested when it's already running, I guess.

> +++ b/net/mac80211/iface.c
> @@ -817,6 +817,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
>
> cancel_work_sync(&sdata->recalc_smps);
>
> + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);

OTOH, I guess if the interface is going away then you'd want to stop
radar detection if it was done for that interface, so in that sense it
makes sense per interface.

> @@ -1583,6 +1592,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
> spin_lock_init(&sdata->cleanup_stations_lock);
> INIT_LIST_HEAD(&sdata->cleanup_stations);
> INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
> + INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
> + ieee80211_dfs_cac_timer_work);
> +

unneeded blank line

> +void ieee80211_dfs_cac_timer_work(struct work_struct *work)
> +{
> + struct delayed_work *delayed_work =
> + container_of(work, struct delayed_work, work);
> + struct ieee80211_sub_if_data *sdata =
> + container_of(delayed_work, struct ieee80211_sub_if_data,
> + dfs_cac_timer_work);
> +
> + rtnl_lock();
> + ieee80211_vif_release_channel(sdata);
> + cfg80211_radar_event(sdata->dev, &sdata->vif.bss_conf.chandef,
> + NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
> + rtnl_unlock();
> +}

Did you test your code with lockdep enabled? I'm almost certain using
rtnl_lock() isn't allowed on mac80211's workqueue.

> +void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp)

That "gfp" argument is misleading, ...

> +{
> + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
> +
> + trace_api_radar_detected(sdata);
> +
> + /* may happen to devices which have currently no BSS configured */
> + if (!cfg80211_chandef_valid(&sdata->vif.bss_conf.chandef))
> + return;
> +
> + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);

... since you do something that requires being able to sleep, hence the
only useful argument you could pass as gfp is GFP_KERNEL. However, not
being able to calls this from softirq or so is probably not desirable
for many drivers?

johannes


2013-02-04 12:50:11

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCH 2/2] iw: print DFS states for channels if available

Signed-off-by: Simon Wunderlich <[email protected]>
---
info.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/info.c b/info.c
index 14db37f..bcf36d7 100644
--- a/info.c
+++ b/info.c
@@ -49,6 +49,20 @@ static char *cipher_name(__u32 c)
}
}

+static char *dfs_state_name(enum nl80211_dfs_state state)
+{
+ switch (state) {
+ case NL80211_DFS_USABLE:
+ return "usable";
+ case NL80211_DFS_AVAILABLE:
+ return "available";
+ case NL80211_DFS_UNAVAILABLE:
+ return "unavailable";
+ default:
+ return "unknown";
+ }
+}
+
static int print_phy_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
@@ -147,6 +161,17 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
next:
if (open)
printf(")");
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+ enum nl80211_dfs_state state = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+ unsigned long time;
+
+ printf(" DFS state: %s", dfs_state_name(state));
+ if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]) {
+ time = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_TIME]);
+ printf(" (for %lu sec)", time/1000);
+ }
+ }
+
printf("\n");
}

--
1.7.10.4


2013-02-07 11:01:00

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv8 1/3] nl80211/cfg80211: add radar detection command/event

On Wed, Feb 06, 2013 at 06:24:08PM +0100, Johannes Berg wrote:
> On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:
>
> > /**
> > + * cfg80211_chandef_dfs_required - checks if radar detection
> > + * is required on any of the channels
>
> pretty sure this isn't valid kernel-doc, I think it has to fit into one
> line
>
OK
> > @@ -3663,6 +3694,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
> > gfp_t gfp);
> >
> > /**
> > + * cfg80211_radar_event - radar detection event
> > + * @dev: network device
> > + * @chandef: chandef for the current channel
> > + * @event: type of event
> > + * @gfp: context flags
> > + *
> > + * This function is called when a radar is detected or a CAC event occured
>
> occurred :)
>
>
OK
> > @@ -2014,6 +2029,7 @@ enum nl80211_band_attr {
> > * (100 * dBm).
> > * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
> > * currently defined
> > + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
>
> missed docs for _DFS_TIME, maybe should put it before _MAX :)
> also, what values does this take?
>
Yeah, I'll fix that. States are the DFS states below.

> > +/**
> > + * enum nl80211_dfs_state - DFS states for channels
> > + *
> > + * Channel states used by the DFS code.
> > + *
> > + * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
> > + * check (CAC) must be performed before using it for AP or IBSS.
> > + * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
> > + * is therefore marked as not available.
> > + * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
> > + */
> > +
> > +enum nl80211_dfs_state {
> > + NL80211_DFS_USABLE = 0,
>
> I don't really see a reason for explicit values?
>

You've suggested explicit values yourself last time [1], or did I misunderstand?

[1] http://article.gmane.org/gmane.linux.kernel.wireless.general/103278
"Should UNAVAILABLE be = 0, so that's the default?" (usable should be the default, btw)

> > +static inline int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
>
> no need for inline
>
OK

> > +int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
> > + const struct cfg80211_chan_def *chandef)
> > +{
> > + int width;
> > + int r;
> > +
> > + if (WARN_ON(!cfg80211_chandef_valid(chandef)))
> > + return -EINVAL;
> > +
> > + width = cfg80211_chandef_get_width(chandef);
> > + if (width < 0)
> > + return -EINVAL;
> > +
> > + r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
> > + width);
> > + if (r)
> > + return r;
> > +
> > + if (!chandef->center_freq2)
> > + return 0;
> > +
> > + return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
> > + width);
> > +}
>
> If you don't export it why is it in the public header file -- either you
> need to export it as well or it doesn't need to be in cfg80211.h :)
>

right, will move it into net/wireless/core.h

> > @@ -454,6 +462,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
> > chan, chanmode, 0);
> > }
> >
> > +static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
> > +{
> > + unsigned long end = jiffies;
> > +
> > + if (end >= start)
> > + return jiffies_to_msecs(end - start);
> > +
> > + return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
> > +}
>
> That seems a bit magic, like it should be hidden away in jiffies.h. Is
> there really no function already to do something like this?
>

I've just moved this magic function from scan.c to core.h because I need it too.
It appears another driver (drivers/net/wireless/ipw2x00) also implements this
privately. We can suggest this patch for include/linux/jiffies.h if you prefer.

> > +static void cfg80211_notify_nop_ended(struct cfg80211_registered_device *rdev,
> > + struct ieee80211_channel *c)
>
> indentation :)
>

OK

> > +{
> > + struct wireless_dev *wdev;
> > + struct cfg80211_chan_def chandef;
> > +
> > + cfg80211_chandef_create(&chandef, c, NL80211_CHAN_NO_HT);
> > +
> > + mutex_lock(&rdev->devlist_mtx);
> > + list_for_each_entry(wdev, &rdev->wdev_list, list) {
> > + if (!wdev->netdev)
> > + continue;
> > +
> > + if (!netif_running(wdev->netdev))
> > + continue;
> > +
> > + nl80211_radar_notify(rdev, &chandef,
> > + NL80211_RADAR_NOP_FINISHED,
> > + wdev->netdev, GFP_ATOMIC);
> > + }
>
> Why for all interfaces? This is multicast anyway, so why not just send
> the event without an interface index instead?
>

Hmm. I'll try. ;)

> > --- a/net/wireless/scan.c
> > +++ b/net/wireless/scan.c
> > @@ -1155,16 +1155,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
> > }
> > }
> >
> > -static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
> > -{
> > - unsigned long end = jiffies;
> > -
> > - if (end >= start)
> > - return jiffies_to_msecs(end - start);
> > -
> > - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
> > -}
>
> Oh, heh. Oh well ...

:P

>
> I think I should probably look at locking too ...

Would be a good idea. I'll verify with lockdep enabled too, didn't do it so far.

Thanks!
Simon


Attachments:
(No filename) (5.07 kB)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-02-07 11:07:31

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv8 1/3] nl80211/cfg80211: add radar detection command/event

On Thu, 2013-02-07 at 12:00 +0100, Simon Wunderlich wrote:

> > > + * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
> > > + * check (CAC) must be performed before using it for AP or IBSS.
> > > + * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
> > > + * is therefore marked as not available.
> > > + * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
> > > + */
> > > +
> > > +enum nl80211_dfs_state {
> > > + NL80211_DFS_USABLE = 0,
> >
> > I don't really see a reason for explicit values?
> >
>
> You've suggested explicit values yourself last time [1], or did I misunderstand?
>
> [1] http://article.gmane.org/gmane.linux.kernel.wireless.general/103278
> "Should UNAVAILABLE be = 0, so that's the default?" (usable should be the default, btw)

Ah, no that was a misunderstanding then. Maybe it would've been clearer
if I'd said "Should UNAVAILABLE be first (=0), so that's the default?"
But if usable should be the default, that should be first. Anyway I
don't care much, we do rely on this enum behaviour a lot though so I
don't think we need explicit values.

> I've just moved this magic function from scan.c to core.h because I need it too.
> It appears another driver (drivers/net/wireless/ipw2x00) also implements this
> privately. We can suggest this patch for include/linux/jiffies.h if you prefer.

Maybe better do it afterwards, unless you don't mind waiting another few
months...? :)

johannes


2013-02-04 12:50:11

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv8 2/3] mac80211: add radar detection command/event

Add command to trigger radar detection in the driver/FW.
Once radar detection is started it should continuously
monitor for radars as long as the channel active.
If radar is detected usermode notified with 'radar
detected' event.

Scanning and remain on channel functionality must be disabled
while doing radar detection/scanning, and vice versa.

based on original patch by Victor Goldenshtein <[email protected]>

Signed-off-by: Simon Wunderlich <[email protected]>
---
Changes to PATCHv7:

* remove typo in mac80211.h (Zefir)
* remove comment about weather channels
* add break in ieee80211_recalc_radar_chanctx()
* reword comment in ieee80211_radar_detected() about virgin interfaces
* remove cfg80211_chandef_dfs_required dependency

Changes to PATCHv6:
* squash "check radar interaction with scan and roc" patch
* add radar detection flag to chanctx conf
* remove mac80211 radar detection command for drivers
* remove trace.h chandef macros and drv_start_radar_detection command
* use chandef in radar_detected_notification, remove channel argument
(mac80211 already knows it)
* add timer for channel availability check notification to cfg80211
* add vif channel release
* check for dfs required using cfg80211 function

Changes to PATCHv5:
* use local instead of sdata->local
* change API to chandef
---
include/net/mac80211.h | 16 ++++++++++++++++
net/mac80211/cfg.c | 36 +++++++++++++++++++++++++++++++++++-
net/mac80211/chan.c | 33 +++++++++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 11 +++++++++++
net/mac80211/iface.c | 12 ++++++++++++
net/mac80211/mlme.c | 32 ++++++++++++++++++++++++++++++++
net/mac80211/scan.c | 3 +++
net/mac80211/trace.h | 19 +++++++++++++++++++
8 files changed, 161 insertions(+), 1 deletion(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 5c98d65..a2a632b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -147,10 +147,12 @@ struct ieee80211_low_level_stats {
* enum ieee80211_chanctx_change - change flag for channel context
* @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
*/
enum ieee80211_chanctx_change {
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
+ IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
};

/**
@@ -165,6 +167,7 @@ enum ieee80211_chanctx_change {
* @rx_chains_dynamic: The number of RX chains that must be enabled
* after RTS/CTS handshake to receive SMPS MIMO transmissions;
* this will always be >= @rx_chains_static.
+ * @radar_enabled: whether radar detection is enabled on this channel.
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *), size is determined in hw information.
*/
@@ -173,6 +176,8 @@ struct ieee80211_chanctx_conf {

u8 rx_chains_static, rx_chains_dynamic;

+ bool radar_enabled;
+
u8 drv_priv[0] __aligned(sizeof(void *));
};

@@ -950,6 +955,7 @@ enum ieee80211_smps_mode {
*
* @channel: the channel to tune to
* @channel_type: the channel (HT) type
+ * @radar_enabled: whether radar detection is enabled on this channel
*
* @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
* (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
@@ -976,6 +982,7 @@ struct ieee80211_conf {

struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
+ bool radar_enabled;
enum ieee80211_smps_mode smps_mode;
};

@@ -3953,6 +3960,15 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
gfp_t gfp);

/**
+ * ieee80211_radar_detected - inform a configured connection that
+ * radar was detected on the current channel
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @gfp: context flags.
+ */
+void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp);
+
+/**
* ieee80211_chswitch_done - Complete channel switch process
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @success: make the channel switch successful or not
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 661b878..b7ace3c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -922,6 +922,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
/* TODO: make hostapd tell us what it wants */
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
+ sdata->radar_required = params->radar_required;

err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
@@ -1796,6 +1797,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
/* can mesh use other SMPS modes? */
sdata->smps_mode = IEEE80211_SMPS_OFF;
sdata->needed_rx_chains = sdata->local->rx_chains;
+ sdata->radar_required = true;

err = ieee80211_vif_use_channel(sdata, &setup->chandef,
IEEE80211_CHANCTX_SHARED);
@@ -2368,7 +2370,9 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
INIT_LIST_HEAD(&roc->dependents);

/* if there's one pending or we're scanning, queue this one */
- if (!list_empty(&local->roc_list) || local->scanning)
+ if (!list_empty(&local->roc_list) ||
+ local->scanning ||
+ local->radar_detect_enabled)
goto out_check_combine;

/* if not HW assist, just queue & schedule work */
@@ -2618,6 +2622,35 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
return ieee80211_cancel_roc(local, cookie, false);
}

+static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ unsigned long timeout;
+ int res;
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ /* whatever, but channel contexts should not complain about that one */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = local->rx_chains;
+ sdata->radar_required = true;
+
+ res = ieee80211_vif_use_channel(sdata, chandef,
+ IEEE80211_CHANCTX_SHARED);
+ if (res)
+ return -EBUSY;
+
+ timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+ ieee80211_queue_delayed_work(&sdata->local->hw,
+ &sdata->dfs_cac_timer_work, timeout);
+
+ 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,
@@ -3322,4 +3355,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_et_stats = ieee80211_get_et_stats,
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
+ .start_radar_detection = ieee80211_start_radar_detection,
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 1bfe0a8..82ab435 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -180,6 +180,7 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
if (ctx->refcount > 0) {
ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
}
}

@@ -212,6 +213,37 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
ieee80211_free_chanctx(local, ctx);
}

+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx)
+{
+ struct ieee80211_sub_if_data *sdata;
+ bool radar_enabled = false;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->radar_required) {
+ radar_enabled = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (radar_enabled == chanctx->conf.radar_enabled)
+ return;
+
+ chanctx->conf.radar_enabled = radar_enabled;
+ local->radar_detect_enabled = chanctx->conf.radar_enabled;
+
+ if (!local->use_chanctx) {
+ local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ }
+
+ drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
+}
+
void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx)
{
@@ -336,6 +368,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
}

ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
out:
mutex_unlock(&local->chanctx_mtx);
return ret;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 5fba867..6637f12 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -753,6 +753,9 @@ struct ieee80211_sub_if_data {
int user_power_level; /* in dBm */
int ap_power_level; /* in dBm */

+ bool radar_required;
+ struct delayed_work dfs_cac_timer_work;
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -972,6 +975,9 @@ struct ieee80211_local {
/* wowlan is enabled -- don't reconfig on resume */
bool wowlan;

+ /* DFS/radar detection is enabled */
+ bool radar_detect_enabled;
+
/* number of RX chains the hardware has */
u8 rx_chains;

@@ -1639,6 +1645,11 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);

void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx);
+
+void ieee80211_dfs_cac_timer(unsigned long data);
+void ieee80211_dfs_cac_timer_work(struct work_struct *work);

#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 0a36dc6..132f708 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -817,6 +817,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,

cancel_work_sync(&sdata->recalc_smps);

+ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+ /* only inform about abort cac if it was started before. */
+ if (sdata->wdev.cac_started) {
+ ieee80211_vif_release_channel(sdata);
+ cfg80211_radar_event(sdata->dev, &sdata->wdev.preset_chandef,
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ }
+
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
@@ -1583,6 +1592,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
spin_lock_init(&sdata->cleanup_stations_lock);
INIT_LIST_HEAD(&sdata->cleanup_stations);
INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+ INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
+ ieee80211_dfs_cac_timer_work);
+

for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 5913fb9..9e40c3e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1244,6 +1244,21 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
}

+void ieee80211_dfs_cac_timer_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work =
+ container_of(work, struct delayed_work, work);
+ struct ieee80211_sub_if_data *sdata =
+ container_of(delayed_work, struct ieee80211_sub_if_data,
+ dfs_cac_timer_work);
+
+ rtnl_lock();
+ ieee80211_vif_release_channel(sdata);
+ cfg80211_radar_event(sdata->dev, &sdata->vif.bss_conf.chandef,
+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ rtnl_unlock();
+}
+
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -4117,3 +4132,20 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ trace_api_radar_detected(sdata);
+
+ /* may happen to devices which have currently no BSS configured */
+ if (!cfg80211_chandef_valid(&sdata->vif.bss_conf.chandef))
+ return;
+
+ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+ cfg80211_radar_event(sdata->dev, &sdata->vif.bss_conf.chandef,
+ NL80211_RADAR_DETECTED, gfp);
+}
+EXPORT_SYMBOL(ieee80211_radar_detected);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 607684c..100ab2d 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -356,6 +356,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
static bool ieee80211_can_scan(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
+ if (local->radar_detect_enabled)
+ return false;
+
if (!list_empty(&local->roc_list))
return false;

diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 6ca53d6..2a78e2c 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1860,6 +1860,25 @@ TRACE_EVENT(drv_set_default_unicast_key,
LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
);

+TRACE_EVENT(api_radar_detected,
+ TP_PROTO(struct ieee80211_sub_if_data *sdata),
+
+ TP_ARGS(sdata),
+
+ TP_STRUCT__entry(
+ VIF_ENTRY
+ ),
+
+ TP_fast_assign(
+ VIF_ASSIGN;
+ ),
+
+ TP_printk(
+ VIF_PR_FMT " radar detected",
+ VIF_PR_ARG
+ )
+);
+
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
--
1.7.10.4


2013-02-06 17:35:52

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv8 2/3] mac80211: add radar detection command/event

On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:

> +++ b/net/mac80211/cfg.c

> @@ -1796,6 +1797,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
> /* can mesh use other SMPS modes? */
> sdata->smps_mode = IEEE80211_SMPS_OFF;
> sdata->needed_rx_chains = sdata->local->rx_chains;
> + sdata->radar_required = true;

err?

johannes


2013-02-07 11:08:52

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv8 2/3] mac80211: add radar detection command/event

On Wed, Feb 06, 2013 at 06:33:35PM +0100, Johannes Berg wrote:
> On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:
>
> > *
> > * @channel: the channel to tune to
> > * @channel_type: the channel (HT) type
> > + * @radar_enabled: whether radar detection is enabled on this channel
>
> There's only one channel for ieee80211_conf ;-)
>

OK

> > /**
> > + * ieee80211_radar_detected - inform a configured connection that
> > + * radar was detected on the current channel
> > + *
> > + * @vif: &struct ieee80211_vif pointer from the add_interface callback.
> > + * @gfp: context flags.
> > + */
> > +void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp);
>
> Given the way this works in cfg80211 and my comment there, it seems
> pointless to report per vif, but rather should be per HW?
>

Yeah ... might be true. userspace can see for itself if it is affected
(for the case we use 40 MHz channel and a 20 MHz user is not interested
in problems on the extension channels)

> > + res = ieee80211_vif_use_channel(sdata, chandef,
> > + IEEE80211_CHANCTX_SHARED);
> > + if (res)
> > + return -EBUSY;
>
> return res? This really can't fail here (except for memory allocation
> etc.) so -EBUSY is a bit odd.
>

OK

> > @@ -753,6 +753,9 @@ struct ieee80211_sub_if_data {
> > int user_power_level; /* in dBm */
> > int ap_power_level; /* in dBm */
> >
> > + bool radar_required;
> > + struct delayed_work dfs_cac_timer_work;
>
> Does the work struct make sense here? It seems like an inherently global
> operation, so should that be in ieee80211_local? We should check anyway
> if radar detection is requested when it's already running, I guess.
>
> > +++ b/net/mac80211/iface.c
> > @@ -817,6 +817,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
> >
> > cancel_work_sync(&sdata->recalc_smps);
> >
> > + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
>
> OTOH, I guess if the interface is going away then you'd want to stop
> radar detection if it was done for that interface, so in that sense it
> makes sense per interface.
>

For the CAC I would like to keep it like this - it is used per interface. For NOP
stuff it must be per wiphy to enable channels later, and it is implemented like that.

> > @@ -1583,6 +1592,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
> > spin_lock_init(&sdata->cleanup_stations_lock);
> > INIT_LIST_HEAD(&sdata->cleanup_stations);
> > INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
> > + INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
> > + ieee80211_dfs_cac_timer_work);
> > +
>
> unneeded blank line
>
OK
> > +void ieee80211_dfs_cac_timer_work(struct work_struct *work)
> > +{
> > + struct delayed_work *delayed_work =
> > + container_of(work, struct delayed_work, work);
> > + struct ieee80211_sub_if_data *sdata =
> > + container_of(delayed_work, struct ieee80211_sub_if_data,
> > + dfs_cac_timer_work);
> > +
> > + rtnl_lock();
> > + ieee80211_vif_release_channel(sdata);
> > + cfg80211_radar_event(sdata->dev, &sdata->vif.bss_conf.chandef,
> > + NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
> > + rtnl_unlock();
> > +}
>
> Did you test your code with lockdep enabled? I'm almost certain using
> rtnl_lock() isn't allowed on mac80211's workqueue.
>

Nope, I didn't test. Will do that.

> > +void ieee80211_radar_detected(struct ieee80211_vif *vif, gfp_t gfp)
>
> That "gfp" argument is misleading, ...
>
> > +{
> > + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
> > +
> > + trace_api_radar_detected(sdata);
> > +
> > + /* may happen to devices which have currently no BSS configured */
> > + if (!cfg80211_chandef_valid(&sdata->vif.bss_conf.chandef))
> > + return;
> > +
> > + cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
>
> ... since you do something that requires being able to sleep, hence the
> only useful argument you could pass as gfp is GFP_KERNEL. However, not
> being able to calls this from softirq or so is probably not desirable
> for many drivers?

Hmm ... yeah that's right. Calling it from softirqs is probably desireable. Maybe
another workqueue or just use cancel_delayed_work() ... I'll think about that.

Thanks,
Simon


Attachments:
(No filename) (4.16 kB)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-02-06 17:35:57

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv8 3/3] nl80211: allow DFS in start_ap

On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:

> + err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
> + if (err < 0)
> + return -EINVAL;

return err; ?

> + if (err) {
> + radar_detect_width = BIT(params.chandef.width);
> + params.radar_required = true;

It seems you should probably just squash this into patch 1 since you
already use it in mac80211 in patch 2 but only patch 3 makes it valid?
seems a bit odd

johannes


2013-02-07 11:11:20

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv8 3/3] nl80211: allow DFS in start_ap

On Wed, Feb 06, 2013 at 06:36:20PM +0100, Johannes Berg wrote:
> On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:
>
> > + err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
> > + if (err < 0)
> > + return -EINVAL;
>
> return err; ?
>
OK
> > + if (err) {
> > + radar_detect_width = BIT(params.chandef.width);
> > + params.radar_required = true;
>
> It seems you should probably just squash this into patch 1 since you
> already use it in mac80211 in patch 2 but only patch 3 makes it valid?
> seems a bit odd

Yeah, patch 1 was becoming bigger and patch 3 smaller ... will just
squash it.

Thanks,
Simon


Attachments:
(No filename) (638.00 B)
signature.asc (198.00 B)
Digital signature
Download all attachments

2013-02-04 12:50:12

by Simon Wunderlich

[permalink] [raw]
Subject: [PATCHv8 1/3] nl80211/cfg80211: add radar detection command/event

Add new NL80211_CMD_RADAR_DETECT, which starts the Channel Availability
Check (CAC). This command will also notify the usermode about events
(CAC finished, CAC aborted, radar detected, NOP finished).
Once radar detection has started it should continuously
monitor for radars as long as the channel is active.

based on original patch by Victor Goldenshtein <[email protected]>

Signed-off-by: Simon Wunderlich <[email protected]>
---

Changes to PATCHv7:
* initialize ieee80211_dfs_state enum with numbers
* fix documentation for dfs_state_entered
* move NL80211_DFS_MIN_CAC_TIME_MS/NL80211_DFS_MIN_NOP_TIME_MS to
cfg80211.h
* remove NL80211_FEATURE_DFS
* fix cfg80211_chandef_get_width return value
* beautify cfg80211_dfs_channels_update_work
* fix nl80211_start_radar_detection return code for dfs_required
* fix rdev->devlist_mtx for cac_started
* add cac_started_time and use it for CAC
* remove wrong check in nl80211_join_ibss()
* add warning for not defined event in cfg80211_radar_event()
* unexport cfg80211_chandef_dfs_required()
* use wdev->channel instead of wdev->preset_chandef.chan for CAC
* report dfs state and how long it is active to userspace
* add event when NOP has been finished

Changes to PATCHv6:
* use chandef in radar_detected_notify()
* add channel states, and handle them in cfg80211
* add Channel Switch Announcement (CAC) and Non-Occupancy Period (NOP)
handling to cfg80211
* add cfg80211_chandef_dfs_required function
* add tracing for wireless event/functions

Changes to PATCHv5:
* remove unused cac_type
* fix doc for cac_started
* remove dfs_nlportid
---
include/net/cfg80211.h | 46 +++++++++++++++
include/uapi/linux/nl80211.h | 58 +++++++++++++++++++
net/wireless/chan.c | 129 +++++++++++++++++++++++++++++++++++++++++-
net/wireless/core.c | 3 +
net/wireless/core.h | 18 ++++++
net/wireless/mlme.c | 127 +++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.c | 115 ++++++++++++++++++++++++++++++++++++-
net/wireless/nl80211.h | 7 +++
net/wireless/reg.c | 3 +
net/wireless/scan.c | 10 ----
net/wireless/trace.h | 33 +++++++++++
11 files changed, 534 insertions(+), 15 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 36e076e..33f0638 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -113,6 +113,9 @@ enum ieee80211_channel_flags {
#define IEEE80211_CHAN_NO_HT40 \
(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)

+#define IEEE80211_DFS_MIN_CAC_TIME_MS 60000
+#define IEEE80211_DFS_MIN_NOP_TIME_MS (30 * 60 * 1000)
+
/**
* struct ieee80211_channel - channel definition
*
@@ -133,6 +136,9 @@ enum ieee80211_channel_flags {
* to enable this, this is useful only on 5 GHz band.
* @orig_mag: internal use
* @orig_mpwr: internal use
+ * @dfs_state: current state of this channel. Only relevant if radar is required
+ * on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
*/
struct ieee80211_channel {
enum ieee80211_band band;
@@ -145,6 +151,8 @@ struct ieee80211_channel {
bool beacon_found;
u32 orig_flags;
int orig_mag, orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned long dfs_state_entered;
};

/**
@@ -412,6 +420,16 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
u32 prohibited_flags);

/**
+ * cfg80211_chandef_dfs_required - checks if radar detection
+ * is required on any of the channels
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ */
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *c);
+
+/**
* enum survey_info_flags - survey information flags
*
* @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in
@@ -568,6 +586,7 @@ struct cfg80211_acl_data {
* @p2p_opp_ps: P2P opportunistic PS
* @acl: ACL configuration used by the drivers which has support for
* MAC address based access control
+ * @radar_required: set if radar detection is required
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@@ -585,6 +604,7 @@ struct cfg80211_ap_settings {
u8 p2p_ctwindow;
bool p2p_opp_ps;
const struct cfg80211_acl_data *acl;
+ bool radar_required;
};

/**
@@ -1826,6 +1846,8 @@ struct cfg80211_gtk_rekey_data {
* this new list replaces the existing one. Driver has to clear its ACL
* when number of MAC addresses entries is passed as 0. Drivers which
* advertise the support for MAC based ACL have to implement this callback.
+ *
+ * @start_radar_detection: Start radar detection in the driver.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2049,6 +2071,10 @@ struct cfg80211_ops {

int (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_acl_data *params);
+
+ int (*start_radar_detection)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef);
};

/*
@@ -2621,6 +2647,8 @@ struct cfg80211_cached_keys;
* beacons, 0 when not valid
* @address: The address for this device, valid only if @netdev is %NULL
* @p2p_started: true if this is a P2P Device that has been started
+ * @cac_started: true if DFS channel availability check has been started
+ * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
*/
struct wireless_dev {
struct wiphy *wiphy;
@@ -2672,6 +2700,9 @@ struct wireless_dev {

u32 ap_unexpected_nlportid;

+ bool cac_started;
+ unsigned long cac_start_time;
+
#ifdef CONFIG_CFG80211_WEXT
/* wext data */
struct {
@@ -3663,6 +3694,21 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
gfp_t gfp);

/**
+ * cfg80211_radar_event - radar detection event
+ * @dev: network device
+ * @chandef: chandef for the current channel
+ * @event: type of event
+ * @gfp: context flags
+ *
+ * This function is called when a radar is detected or a CAC event occured
+ * on the current channel. This must be called to notify the completion of
+ * a CAC process, also by full-MAC drivers.
+ */
+void cfg80211_radar_event(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
+/**
* cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
* @dev: network device
* @peer: peer's MAC address
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 5b7dbc1..4c8159b 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -597,6 +597,14 @@
* command is used in AP/P2P GO mode. Driver has to make sure to clear its
* ACL list during %NL80211_CMD_STOP_AP.
*
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ * a radar is detected or the channel availability scan (CAC) has finished
+ * or was aborted, or a radar was detected, usermode will be notified with
+ * this event. This command is also used to notify userspace about radars
+ * while operating on this channel.
+ * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ * event.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -749,6 +757,8 @@ enum nl80211_commands {

NL80211_CMD_SET_MAC_ACL,

+ NL80211_CMD_RADAR_DETECT,
+
/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
@@ -1336,6 +1346,9 @@ enum nl80211_commands {
* number of MAC addresses that a device can support for MAC
* ACL.
*
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ * contains a value of enum nl80211_radar_event (u32).
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1614,6 +1627,8 @@ enum nl80211_attrs {

NL80211_ATTR_MAC_ACL_MAX,

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

__NL80211_ATTR_AFTER_LAST,
@@ -2014,6 +2029,7 @@ enum nl80211_band_attr {
* (100 * dBm).
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
*/
enum nl80211_frequency_attr {
@@ -2024,6 +2040,8 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_NO_IBSS,
NL80211_FREQUENCY_ATTR_RADAR,
NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+ NL80211_FREQUENCY_ATTR_DFS_STATE,
+ NL80211_FREQUENCY_ATTR_DFS_TIME,

/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -3323,4 +3341,44 @@ enum nl80211_acl_policy {
NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
};

+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ * now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ * the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ * change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ * over, channel becomes usable.
+ */
+enum nl80211_radar_event {
+ NL80211_RADAR_DETECTED,
+ NL80211_RADAR_CAC_FINISHED,
+ NL80211_RADAR_CAC_ABORTED,
+ NL80211_RADAR_NOP_FINISHED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * check (CAC) must be performed before using it for AP or IBSS.
+ * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * is therefore marked as not available.
+ * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+
+enum nl80211_dfs_state {
+ NL80211_DFS_USABLE = 0,
+ NL80211_DFS_UNAVAILABLE = 1,
+ NL80211_DFS_AVAILABLE = 2,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 396373f..b7bc951 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -147,6 +147,32 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
}
}

+static inline int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
+{
+ int width;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ width = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ width = 160;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+ return width;
+}
+
const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
const struct cfg80211_chan_def *c2)
@@ -192,6 +218,93 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
}
EXPORT_SYMBOL(cfg80211_chandef_compatible);

+static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
+ u32 bandwidth,
+ enum nl80211_dfs_state dfs_state)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ c->dfs_state = dfs_state;
+ c->dfs_state_entered = jiffies;
+ }
+}
+
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state)
+{
+ int width;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return;
+
+ cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
+ width, dfs_state);
+
+ if (!chandef->center_freq2)
+ return;
+ cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
+ width, dfs_state);
+}
+
+static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR)
+ return 1;
+ }
+ return 0;
+}
+
+
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return -EINVAL;
+
+ r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
+ width);
+ if (r)
+ return r;
+
+ if (!chandef->center_freq2)
+ return 0;
+
+ return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
+ width);
+}
+
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
u32 prohibited_flags)
@@ -203,7 +316,16 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
- if (!c || c->flags & prohibited_flags)
+ if (!c)
+ return false;
+
+ /* check for radar flags */
+ if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
+ (c->dfs_state != NL80211_DFS_AVAILABLE))
+ return false;
+
+ /* check for the other flags */
+ if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
return false;
}

@@ -344,7 +466,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
- if (wdev->beacon_interval) {
+ if (wdev->cac_started) {
+ *chan = wdev->channel;
+ *chanmode = CHAN_MODE_SHARED;
+ } else if (wdev->beacon_interval) {
*chan = wdev->channel;
*chanmode = CHAN_MODE_SHARED;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 40dbe37..d1d850f7 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -324,6 +324,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
+ INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
+ cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT
rdev->wiphy.wext = &cfg80211_wext_handler;
#endif
@@ -695,6 +697,7 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->scan_done_wk);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
+ cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);

if (rdev->wowlan && rdev->ops->set_wakeup)
rdev_set_wakeup(rdev, false);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 8396f76..cac722b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -87,6 +87,8 @@ struct cfg80211_registered_device {

struct cfg80211_wowlan *wowlan;

+ struct delayed_work dfs_update_channels_wk;
+
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __aligned(NETDEV_ALIGN);
@@ -427,6 +429,12 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct ieee80211_channel *chan,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_dfs_state dfs_state);
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work);
+

static inline int
cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
@@ -454,6 +462,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
chan, chanmode, 0);
}

+static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
+{
+ unsigned long end = jiffies;
+
+ if (end >= start)
+ return jiffies_to_msecs(end - start);
+
+ return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
+}
+
void
cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 461e692..a21c108 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -987,3 +987,130 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void cfg80211_notify_nop_ended(struct cfg80211_registered_device *rdev,
+ struct ieee80211_channel *c)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_chan_def chandef;
+
+ cfg80211_chandef_create(&chandef, c, NL80211_CHAN_NO_HT);
+
+ mutex_lock(&rdev->devlist_mtx);
+ list_for_each_entry(wdev, &rdev->wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
+
+ if (!netif_running(wdev->netdev))
+ continue;
+
+ nl80211_radar_notify(rdev, &chandef,
+ NL80211_RADAR_NOP_FINISHED,
+ wdev->netdev, GFP_ATOMIC);
+ }
+ mutex_unlock(&rdev->devlist_mtx);
+}
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work;
+ struct cfg80211_registered_device *rdev;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *c;
+ struct wiphy *wiphy;
+ bool check_again = false;
+ unsigned long timeout, next_time = 0;
+ int bandid, i;
+
+ delayed_work = container_of(work, struct delayed_work, work);
+ rdev = container_of(delayed_work, struct cfg80211_registered_device,
+ dfs_update_channels_wk);
+ wiphy = &rdev->wiphy;
+
+ mutex_lock(&cfg80211_mutex);
+ for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
+ sband = wiphy->bands[bandid];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ c = &sband->channels[i];
+
+ if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
+ continue;
+
+ timeout = c->dfs_state_entered +
+ IEEE80211_DFS_MIN_NOP_TIME_MS;
+
+ if (time_after_eq(jiffies, timeout)) {
+ c->dfs_state = NL80211_DFS_USABLE;
+ cfg80211_notify_nop_ended(rdev, c);
+ continue;
+ }
+
+ if (!check_again)
+ next_time = timeout - jiffies;
+ else
+ next_time = min(next_time, timeout - jiffies);
+ check_again = true;
+ }
+ }
+ mutex_unlock(&cfg80211_mutex);
+
+ /* reschedule if there are other channels waiting to be cleared again */
+ if (check_again)
+ queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
+ next_time);
+}
+
+
+void cfg80211_radar_event(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event,
+ gfp_t gfp)
+{
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ unsigned long timeout;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return;
+
+ trace_cfg80211_radar_event(netdev, chandef, event);
+
+ switch (event) {
+ case NL80211_RADAR_DETECTED:
+ /*
+ * only set the chandef supplied channel to unavailable, in
+ * case the radar is detected on only one of multiple channels
+ * spanned by the chandef.
+ */
+ cfg80211_set_dfs_state(wiphy, chandef,
+ NL80211_DFS_UNAVAILABLE);
+
+ timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
+ queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
+ timeout);
+ break;
+ case NL80211_RADAR_CAC_FINISHED:
+ timeout = msecs_to_jiffies(wdev->cac_start_time +
+ IEEE80211_DFS_MIN_CAC_TIME_MS);
+ WARN_ON(!time_after_eq(jiffies, timeout));
+ cfg80211_set_dfs_state(wiphy, chandef,
+ NL80211_DFS_AVAILABLE);
+ break;
+ case NL80211_RADAR_CAC_ABORTED:
+ /* Shouldn't happen if CAC was never started before. */
+ WARN_ON(!wdev->cac_started);
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ wdev->cac_started = false;
+
+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
+}
+EXPORT_SYMBOL(cfg80211_radar_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b5978ab..00e6599 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -531,9 +531,16 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_RADAR) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
- goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_RADAR)) {
+ u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+ chan->dfs_state))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time))
+ goto nla_put_failure;
+ }

if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
DBM_TO_MBM(chan->max_power)))
@@ -4977,6 +4984,54 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
return err;
}

+static int nl80211_start_radar_detection(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_chan_def chandef;
+ int err;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ if (wdev->cac_started)
+ return -EBUSY;
+
+ err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
+ if (err < 0)
+ return err;
+
+ if (err == 0)
+ return -EINVAL;
+
+ if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+ return -EINVAL;
+
+ if (!rdev->ops->start_radar_detection)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&rdev->devlist_mtx);
+ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+ chandef.chan, CHAN_MODE_SHARED,
+ BIT(chandef.width));
+ if (err)
+ goto err_locked;
+
+ err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
+ if (!err) {
+ wdev->channel = chandef.chan;
+ wdev->cac_started = true;
+ wdev->cac_start_time = jiffies;
+ }
+err_locked:
+ mutex_unlock(&rdev->devlist_mtx);
+
+ return err;
+}
+
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
u32 seq, int flags,
struct cfg80211_registered_device *rdev,
@@ -7992,6 +8047,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_RADAR_DETECT,
+ .doit = nl80211_start_radar_detection,
+ .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 = {
@@ -9189,6 +9252,52 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
}

void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event,
+ struct net_device *netdev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct wireless_dev *wdev = netdev->ieee80211_ptr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+ nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
+ goto nla_put_failure;
+
+ /* reason is unspecified, just notify that CAC has failed. */
+ if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
+ goto nla_put_failure;
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
u32 num_packets, gfp_t gfp)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 2acba84..b061da4 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -108,6 +108,13 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
enum nl80211_cqm_rssi_threshold_event rssi_event,
gfp_t gfp);
+
+void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event,
+ struct net_device *netdev, gfp_t gfp);
+
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index de02d63..7abf6b9 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -884,6 +884,9 @@ static void handle_channel(struct wiphy *wiphy,
return;
}

+ chan->dfs_state = NL80211_DFS_USABLE;
+ chan->dfs_state_entered = jiffies;
+
chan->beacon_found = false;
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain =
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 45f1618..85ce680 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1155,16 +1155,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
}
}

-static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
-{
- unsigned long end = jiffies;
-
- if (end >= start)
- return jiffies_to_msecs(end - start);
-
- return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
-}
-
static char *
ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
struct cfg80211_internal_bss *bss, char *current_ev,
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 8bc5531..a106ea8 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -2051,6 +2051,21 @@ TRACE_EVENT(cfg80211_reg_can_beacon,
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);

+TRACE_EVENT(cfg80211_chandef_dfs_required,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
TRACE_EVENT(cfg80211_ch_switch_notify,
TP_PROTO(struct net_device *netdev,
struct cfg80211_chan_def *chandef),
@@ -2067,6 +2082,24 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);

+TRACE_EVENT(cfg80211_radar_event,
+ TP_PROTO(struct net_device *netdev, struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event evt),
+ TP_ARGS(netdev, chandef, evt),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(enum nl80211_radar_event, evt)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ __entry->evt = evt;
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT " event: %d",
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG, __entry->evt)
+);
+
DECLARE_EVENT_CLASS(cfg80211_rx_evt,
TP_PROTO(struct net_device *netdev, const u8 *addr),
TP_ARGS(netdev, addr),
--
1.7.10.4


2013-02-07 11:10:17

by Simon Wunderlich

[permalink] [raw]
Subject: Re: [PATCHv8 2/3] mac80211: add radar detection command/event

On Wed, Feb 06, 2013 at 06:36:13PM +0100, Johannes Berg wrote:
> On Mon, 2013-02-04 at 13:49 +0100, Simon Wunderlich wrote:
>
> > +++ b/net/mac80211/cfg.c
>
> > @@ -1796,6 +1797,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
> > /* can mesh use other SMPS modes? */
> > sdata->smps_mode = IEEE80211_SMPS_OFF;
> > sdata->needed_rx_chains = sdata->local->rx_chains;
> > + sdata->radar_required = true;
>
> err?

Oops ... will remove that. Sorry!
Simon


Attachments:
(No filename) (494.00 B)
signature.asc (198.00 B)
Digital signature
Download all attachments