aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mesh_sync.c
diff options
context:
space:
mode:
authorJavier Cardona <javier@cozybit.com>2012-03-31 14:31:32 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-10 15:20:31 -0400
commitdbf498fbafa2c23139d5a990e94ed78bafbbea19 (patch)
tree723a2ed4575ab2178cbd210d85a19d5c968a95a1 /net/mac80211/mesh_sync.c
parent9bdd3a6bf8513a0a9eda031d15b36e4677854243 (diff)
mac80211: Implement mesh synchronization framework
This patch adds MBSS extensible synchronization framework (Sec. 13.13.2 of IEEE Std. 802.11-2012). The framework is implemented via an ops table which defines the following functions: rx_bcn_presp() - this is called every time a mesh beacon is received. adjust_tbtt() - this is called immediately before a beacon is about to be transmitted. The default neighbor offset synchronization defined in the standard is implemented. We also provide template functions for vendor specific methods. When neighbor offset synchronization is active (which is the default) mesh neighbors in the same MBSS will track timing offsets to each other and compensate clock drift. In our tests we observed that this mesh synchronization implementation successfully corrected drifts between stations of ~2PPM while introducing a jitter of ~20us. It is also possible to test this framework on mac80211_hwsim simulated phys to see how it behaves under different topologies, over poor links, etc. Signed-off-by: Marco Porsch <marco.porsch@s2005.tu-chemnitz.de> Signed-off-by: Pavel Zubarev <pavel.zubarev@gmail.com> Signed-off-by: Javier Cardona <javier@cozybit.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mesh_sync.c')
-rw-r--r--net/mac80211/mesh_sync.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
new file mode 100644
index 000000000000..f78b0139856f
--- /dev/null
+++ b/net/mac80211/mesh_sync.c
@@ -0,0 +1,296 @@
1/*
2 * Copyright 2011-2012, Pavel Zubarev <pavel.zubarev@gmail.com>
3 * Copyright 2011-2012, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
4 * Copyright 2011-2012, cozybit Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include "ieee80211_i.h"
12#include "mesh.h"
13#include "driver-ops.h"
14
15#ifdef CONFIG_MAC80211_VERBOSE_MESH_SYNC_DEBUG
16#define msync_dbg(fmt, args...) \
17 printk(KERN_DEBUG "Mesh sync (%s): " fmt "\n", sdata->name, ##args)
18#else
19#define msync_dbg(fmt, args...) do { (void)(0); } while (0)
20#endif
21
22/* This is not in the standard. It represents a tolerable tbtt drift below
23 * which we do no TSF adjustment.
24 */
25#define TBTT_MINIMUM_ADJUSTMENT 10
26
27struct sync_method {
28 u8 method;
29 struct ieee80211_mesh_sync_ops ops;
30};
31
32/**
33 * mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
34 *
35 * @ie: information elements of a management frame from the mesh peer
36 */
37static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
38{
39 return (ie->mesh_config->meshconf_cap &
40 MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
41}
42
43void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
44{
45 struct ieee80211_local *local = sdata->local;
46 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
47 /* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */
48 u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500;
49 u64 tsf;
50 u64 tsfdelta;
51
52 spin_lock_bh(&ifmsh->sync_offset_lock);
53
54 if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
55 msync_dbg("TBTT : max clockdrift=%lld; adjusting",
56 (long long) ifmsh->sync_offset_clockdrift_max);
57 tsfdelta = -ifmsh->sync_offset_clockdrift_max;
58 ifmsh->sync_offset_clockdrift_max = 0;
59 } else {
60 msync_dbg("TBTT : max clockdrift=%lld; adjusting by %llu",
61 (long long) ifmsh->sync_offset_clockdrift_max,
62 (unsigned long long) beacon_int_fraction);
63 tsfdelta = -beacon_int_fraction;
64 ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction;
65 }
66
67 tsf = drv_get_tsf(local, sdata);
68 if (tsf != -1ULL)
69 drv_set_tsf(local, sdata, tsf + tsfdelta);
70 spin_unlock_bh(&ifmsh->sync_offset_lock);
71}
72
73static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
74 u16 stype,
75 struct ieee80211_mgmt *mgmt,
76 struct ieee802_11_elems *elems,
77 struct ieee80211_rx_status *rx_status)
78{
79 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
80 struct ieee80211_local *local = sdata->local;
81 struct sta_info *sta;
82 u64 t_t, t_r;
83
84 WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
85
86 /* standard mentions only beacons */
87 if (stype != IEEE80211_STYPE_BEACON)
88 return;
89
90 /* The current tsf is a first approximation for the timestamp
91 * for the received beacon. Further down we try to get a
92 * better value from the rx_status->mactime field if
93 * available. Also we have to call drv_get_tsf() before
94 * entering the rcu-read section.*/
95 t_r = drv_get_tsf(local, sdata);
96
97 rcu_read_lock();
98 sta = sta_info_get(sdata, mgmt->sa);
99 if (!sta)
100 goto no_sync;
101
102 /* check offset sync conditions (13.13.2.2.1)
103 *
104 * TODO also sync to
105 * dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
106 */
107
108 if (elems->mesh_config && mesh_peer_tbtt_adjusting(elems)) {
109 clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
110 msync_dbg("STA %pM : is adjusting TBTT", sta->sta.addr);
111 goto no_sync;
112 }
113
114 if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
115 /*
116 * The mactime is defined as the time the first data symbol
117 * of the frame hits the PHY, and the timestamp of the beacon
118 * is defined as "the time that the data symbol containing the
119 * first bit of the timestamp is transmitted to the PHY plus
120 * the transmitting STA's delays through its local PHY from the
121 * MAC-PHY interface to its interface with the WM" (802.11
122 * 11.1.2)
123 *
124 * T_r, in 13.13.2.2.2, is just defined as "the frame reception
125 * time" but we unless we interpret that time to be the same
126 * time of the beacon timestamp, the offset calculation will be
127 * off. Below we adjust t_r to be "the time at which the first
128 * symbol of the timestamp element in the beacon is received".
129 * This correction depends on the rate.
130 *
131 * Based on similar code in ibss.c
132 */
133 int rate;
134
135 if (rx_status->flag & RX_FLAG_HT) {
136 /* TODO:
137 * In principle there could be HT-beacons (Dual Beacon
138 * HT Operation options), but for now ignore them and
139 * just use the primary (i.e. non-HT) beacons for
140 * synchronization.
141 * */
142 goto no_sync;
143 } else
144 rate = local->hw.wiphy->bands[rx_status->band]->
145 bitrates[rx_status->rate_idx].bitrate;
146
147 /* 24 bytes of header * 8 bits/byte *
148 * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
149 t_r = rx_status->mactime + (24 * 8 * 10 / rate);
150 }
151
152 /* Timing offset calculation (see 13.13.2.2.2) */
153 t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
154 sta->t_offset = t_t - t_r;
155
156 if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
157 s64 t_clockdrift = sta->t_offset_setpoint
158 - sta->t_offset;
159
160 msync_dbg("STA %pM : sta->t_offset=%lld,"
161 " sta->t_offset_setpoint=%lld,"
162 " t_clockdrift=%lld",
163 sta->sta.addr,
164 (long long) sta->t_offset,
165 (long long)
166 sta->t_offset_setpoint,
167 (long long) t_clockdrift);
168 rcu_read_unlock();
169
170 spin_lock_bh(&ifmsh->sync_offset_lock);
171 if (t_clockdrift >
172 ifmsh->sync_offset_clockdrift_max)
173 ifmsh->sync_offset_clockdrift_max
174 = t_clockdrift;
175 spin_unlock_bh(&ifmsh->sync_offset_lock);
176
177 } else {
178 sta->t_offset_setpoint = sta->t_offset;
179 set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
180 msync_dbg("STA %pM : offset was invalid, "
181 " sta->t_offset=%lld",
182 sta->sta.addr,
183 (long long) sta->t_offset);
184 rcu_read_unlock();
185 }
186 return;
187
188no_sync:
189 rcu_read_unlock();
190}
191
192static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
193{
194 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
195
196 WARN_ON(ifmsh->mesh_sp_id
197 != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
198 BUG_ON(!rcu_read_lock_held());
199
200 spin_lock_bh(&ifmsh->sync_offset_lock);
201
202 if (ifmsh->sync_offset_clockdrift_max >
203 TBTT_MINIMUM_ADJUSTMENT) {
204 /* Since ajusting the tsf here would
205 * require a possibly blocking call
206 * to the driver tsf setter, we punt
207 * the tsf adjustment to the mesh tasklet
208 */
209 msync_dbg("TBTT : kicking off TBTT "
210 "adjustment with "
211 "clockdrift_max=%lld",
212 ifmsh->sync_offset_clockdrift_max);
213 set_bit(MESH_WORK_DRIFT_ADJUST,
214 &ifmsh->wrkq_flags);
215 } else {
216 msync_dbg("TBTT : max clockdrift=%lld; "
217 "too small to adjust",
218 (long long)
219 ifmsh->sync_offset_clockdrift_max);
220 ifmsh->sync_offset_clockdrift_max = 0;
221 }
222 spin_unlock_bh(&ifmsh->sync_offset_lock);
223}
224
225static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata)
226{
227 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
228 u8 offset;
229
230 if (!ifmsh->ie || !ifmsh->ie_len)
231 return NULL;
232
233 offset = ieee80211_ie_split_vendor(ifmsh->ie,
234 ifmsh->ie_len, 0);
235
236 if (!offset)
237 return NULL;
238
239 return ifmsh->ie + offset + 2;
240}
241
242static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
243 u16 stype,
244 struct ieee80211_mgmt *mgmt,
245 struct ieee802_11_elems *elems,
246 struct ieee80211_rx_status *rx_status)
247{
248 const u8 *oui;
249
250 WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
251 msync_dbg("called mesh_sync_vendor_rx_bcn_presp");
252 oui = mesh_get_vendor_oui(sdata);
253 /* here you would implement the vendor offset tracking for this oui */
254}
255
256static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
257{
258 const u8 *oui;
259
260 WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
261 msync_dbg("called mesh_sync_vendor_adjust_tbtt");
262 oui = mesh_get_vendor_oui(sdata);
263 /* here you would implement the vendor tsf adjustment for this oui */
264}
265
266/* global variable */
267static struct sync_method sync_methods[] = {
268 {
269 .method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
270 .ops = {
271 .rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
272 .adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
273 }
274 },
275 {
276 .method = IEEE80211_SYNC_METHOD_VENDOR,
277 .ops = {
278 .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp,
279 .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt,
280 }
281 },
282};
283
284struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
285{
286 struct ieee80211_mesh_sync_ops *ops = NULL;
287 u8 i;
288
289 for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
290 if (sync_methods[i].method == method) {
291 ops = &sync_methods[i].ops;
292 break;
293 }
294 }
295 return ops;
296}