ad2e2e24c9fb3dff427e82f549c8e94aa1e5ae51
[librecmc/librecmc.git] /
1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Thu, 14 Sep 2023 13:17:16 +0200
3 Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
4  shutdown
5
6 Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
7
8 Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
9 Signed-off-by: Felix Fietkau <nbd@nbd.name>
10 ---
11
12 --- a/include/net/cfg80211.h
13 +++ b/include/net/cfg80211.h
14 @@ -175,6 +175,8 @@ enum ieee80211_channel_flags {
15   * @dfs_state: current state of this channel. Only relevant if radar is required
16   *     on this channel.
17   * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
18 + * @dfs_state_last_available: timestamp (jiffies) of the last time when the
19 + *     channel was available.
20   * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
21   */
22  struct ieee80211_channel {
23 @@ -191,6 +193,7 @@ struct ieee80211_channel {
24         int orig_mag, orig_mpwr;
25         enum nl80211_dfs_state dfs_state;
26         unsigned long dfs_state_entered;
27 +       unsigned long dfs_state_last_available;
28         unsigned int dfs_cac_ms;
29  };
30  
31 --- a/net/wireless/ap.c
32 +++ b/net/wireless/ap.c
33 @@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cf
34         if (!wdev->links[link_id].ap.beacon_interval)
35                 return -ENOENT;
36  
37 +       cfg80211_update_last_available(wdev->wiphy,
38 +                                      &wdev->links[link_id].ap.chandef);
39 +
40         err = rdev_stop_ap(rdev, dev, link_id);
41         if (!err) {
42                 wdev->conn_owner_nlportid = 0;
43 @@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cf
44                 if (notify)
45                         nl80211_send_ap_stopped(wdev);
46  
47 -               /* Should we apply the grace period during beaconing interface
48 -                * shutdown also?
49 -                */
50                 cfg80211_sched_dfs_chan_update(rdev);
51         }
52  
53 --- a/net/wireless/chan.c
54 +++ b/net/wireless/chan.c
55 @@ -461,6 +461,8 @@ static void cfg80211_set_chans_dfs_state
56  
57                 c->dfs_state = dfs_state;
58                 c->dfs_state_entered = jiffies;
59 +               if (dfs_state == NL80211_DFS_AVAILABLE)
60 +                       c->dfs_state_last_available = jiffies;
61         }
62  }
63  
64 @@ -873,6 +875,49 @@ static bool cfg80211_get_chans_dfs_avail
65         return true;
66  }
67  
68 +static void
69 +__cfg80211_update_last_available(struct wiphy *wiphy,
70 +                                        u32 center_freq,
71 +                                        u32 bandwidth)
72 +{
73 +       struct ieee80211_channel *c;
74 +       u32 freq, start_freq, end_freq;
75 +
76 +       start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
77 +       end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
78 +
79 +       /*
80 +        * Check entire range of channels for the bandwidth.
81 +        * If any channel in between is disabled or has not
82 +        * had gone through CAC return false
83 +        */
84 +       for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
85 +               c = ieee80211_get_channel_khz(wiphy, freq);
86 +               if (!c)
87 +                       return;
88 +
89 +               c->dfs_state_last_available = jiffies;
90 +       }
91 +}
92 +
93 +void cfg80211_update_last_available(struct wiphy *wiphy,
94 +                                   const struct cfg80211_chan_def *chandef)
95 +{
96 +       int width;
97 +
98 +       width = cfg80211_chandef_get_width(chandef);
99 +       if (width < 0)
100 +               return;
101 +
102 +       __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
103 +                                                width);
104 +       if (chandef->width != NL80211_CHAN_WIDTH_80P80)
105 +           return;
106 +
107 +       __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
108 +                                                width);
109 +}
110 +
111  static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
112                                 const struct cfg80211_chan_def *chandef)
113  {
114 --- a/net/wireless/core.h
115 +++ b/net/wireless/core.h
116 @@ -477,6 +477,8 @@ void cfg80211_set_dfs_state(struct wiphy
117                             enum nl80211_dfs_state dfs_state);
118  
119  void cfg80211_dfs_channels_update_work(struct work_struct *work);
120 +void cfg80211_update_last_available(struct wiphy *wiphy,
121 +                                   const struct cfg80211_chan_def *chandef);
122  
123  unsigned int
124  cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
125 --- a/net/wireless/mlme.c
126 +++ b/net/wireless/mlme.c
127 @@ -891,6 +891,8 @@ void cfg80211_dfs_channels_update_work(s
128                         if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
129                                 time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
130                                 radar_event = NL80211_RADAR_NOP_FINISHED;
131 +                               timeout = c->dfs_state_entered +
132 +                                         msecs_to_jiffies(time_dfs_update);
133                         } else {
134                                 if (regulatory_pre_cac_allowed(wiphy) ||
135                                     cfg80211_any_wiphy_oper_chan(wiphy, c))
136 @@ -898,11 +900,10 @@ void cfg80211_dfs_channels_update_work(s
137  
138                                 time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
139                                 radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
140 +                               timeout = c->dfs_state_last_available +
141 +                                         msecs_to_jiffies(time_dfs_update);
142                         }
143  
144 -                       timeout = c->dfs_state_entered +
145 -                                 msecs_to_jiffies(time_dfs_update);
146 -
147                         if (time_after_eq(jiffies, timeout)) {
148                                 c->dfs_state = NL80211_DFS_USABLE;
149                                 c->dfs_state_entered = jiffies;