Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/mac80211/mesh_sync.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright 2011-2012, Pavel Zubarev <[email protected]>
4
* Copyright 2011-2012, Marco Porsch <[email protected]>
5
* Copyright 2011-2012, cozybit Inc.
6
* Copyright (C) 2021,2023 Intel Corporation
7
*/
8
9
#include "ieee80211_i.h"
10
#include "mesh.h"
11
#include "driver-ops.h"
12
13
/* This is not in the standard. It represents a tolerable tsf drift below
14
* which we do no TSF adjustment.
15
*/
16
#define TOFFSET_MINIMUM_ADJUSTMENT 10
17
18
/* This is not in the standard. It is a margin added to the
19
* Toffset setpoint to mitigate TSF overcorrection
20
* introduced by TSF adjustment latency.
21
*/
22
#define TOFFSET_SET_MARGIN 20
23
24
/* This is not in the standard. It represents the maximum Toffset jump above
25
* which we'll invalidate the Toffset setpoint and choose a new setpoint. This
26
* could be, for instance, in case a neighbor is restarted and its TSF counter
27
* reset.
28
*/
29
#define TOFFSET_MAXIMUM_ADJUSTMENT 800 /* 0.8 ms */
30
31
struct sync_method {
32
u8 method;
33
struct ieee80211_mesh_sync_ops ops;
34
};
35
36
/**
37
* mesh_peer_tbtt_adjusting - check if an mp is currently adjusting its TBTT
38
*
39
* @cfg: mesh config element from the mesh peer (or %NULL)
40
*
41
* Returns: If the mesh peer is currently adjusting its TBTT
42
*/
43
static bool mesh_peer_tbtt_adjusting(const struct ieee80211_meshconf_ie *cfg)
44
{
45
return cfg &&
46
(cfg->meshconf_cap & IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING);
47
}
48
49
void mesh_sync_adjust_tsf(struct ieee80211_sub_if_data *sdata)
50
{
51
struct ieee80211_local *local = sdata->local;
52
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
53
/* sdata->vif.bss_conf.beacon_int in 1024us units, 0.04% */
54
u64 beacon_int_fraction = sdata->vif.bss_conf.beacon_int * 1024 / 2500;
55
u64 tsf;
56
u64 tsfdelta;
57
58
spin_lock_bh(&ifmsh->sync_offset_lock);
59
if (ifmsh->sync_offset_clockdrift_max < beacon_int_fraction) {
60
msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting\n",
61
(long long) ifmsh->sync_offset_clockdrift_max);
62
tsfdelta = -ifmsh->sync_offset_clockdrift_max;
63
ifmsh->sync_offset_clockdrift_max = 0;
64
} else {
65
msync_dbg(sdata, "TSF : max clockdrift=%lld; adjusting by %llu\n",
66
(long long) ifmsh->sync_offset_clockdrift_max,
67
(unsigned long long) beacon_int_fraction);
68
tsfdelta = -beacon_int_fraction;
69
ifmsh->sync_offset_clockdrift_max -= beacon_int_fraction;
70
}
71
spin_unlock_bh(&ifmsh->sync_offset_lock);
72
73
if (local->ops->offset_tsf) {
74
drv_offset_tsf(local, sdata, tsfdelta);
75
} else {
76
tsf = drv_get_tsf(local, sdata);
77
if (tsf != -1ULL)
78
drv_set_tsf(local, sdata, tsf + tsfdelta);
79
}
80
}
81
82
static void
83
mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype,
84
struct ieee80211_mgmt *mgmt, unsigned int len,
85
const struct ieee80211_meshconf_ie *mesh_cfg,
86
struct ieee80211_rx_status *rx_status)
87
{
88
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
89
struct ieee80211_local *local = sdata->local;
90
struct sta_info *sta;
91
u64 t_t, t_r;
92
93
WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
94
95
/* standard mentions only beacons */
96
if (stype != IEEE80211_STYPE_BEACON)
97
return;
98
99
/*
100
* Get time when timestamp field was received. If we don't
101
* have rx timestamps, then use current tsf as an approximation.
102
* drv_get_tsf() must be called before entering the rcu-read
103
* section.
104
*/
105
if (ieee80211_have_rx_timestamp(rx_status))
106
t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
107
len + FCS_LEN, 24);
108
else
109
t_r = drv_get_tsf(local, sdata);
110
111
rcu_read_lock();
112
sta = sta_info_get(sdata, mgmt->sa);
113
if (!sta)
114
goto no_sync;
115
116
/* check offset sync conditions (13.13.2.2.1)
117
*
118
* TODO also sync to
119
* dot11MeshNbrOffsetMaxNeighbor non-peer non-MBSS neighbors
120
*/
121
122
if (mesh_peer_tbtt_adjusting(mesh_cfg)) {
123
msync_dbg(sdata, "STA %pM : is adjusting TBTT\n",
124
sta->sta.addr);
125
goto no_sync;
126
}
127
128
/* Timing offset calculation (see 13.13.2.2.2) */
129
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
130
sta->mesh->t_offset = t_t - t_r;
131
132
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
133
s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset;
134
msync_dbg(sdata,
135
"STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n",
136
sta->sta.addr, (long long) sta->mesh->t_offset,
137
(long long) sta->mesh->t_offset_setpoint,
138
(long long) t_clockdrift);
139
140
if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
141
t_clockdrift < -TOFFSET_MAXIMUM_ADJUSTMENT) {
142
msync_dbg(sdata,
143
"STA %pM : t_clockdrift=%lld too large, setpoint reset\n",
144
sta->sta.addr,
145
(long long) t_clockdrift);
146
clear_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
147
goto no_sync;
148
}
149
150
spin_lock_bh(&ifmsh->sync_offset_lock);
151
if (t_clockdrift > ifmsh->sync_offset_clockdrift_max)
152
ifmsh->sync_offset_clockdrift_max = t_clockdrift;
153
spin_unlock_bh(&ifmsh->sync_offset_lock);
154
} else {
155
sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN;
156
set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
157
msync_dbg(sdata,
158
"STA %pM : offset was invalid, t_offset=%lld\n",
159
sta->sta.addr,
160
(long long) sta->mesh->t_offset);
161
}
162
163
no_sync:
164
rcu_read_unlock();
165
}
166
167
static void mesh_sync_offset_adjust_tsf(struct ieee80211_sub_if_data *sdata,
168
struct beacon_data *beacon)
169
{
170
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
171
172
WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET);
173
WARN_ON(!rcu_read_lock_held());
174
175
spin_lock_bh(&ifmsh->sync_offset_lock);
176
177
if (ifmsh->sync_offset_clockdrift_max > TOFFSET_MINIMUM_ADJUSTMENT) {
178
/* Since adjusting the tsf here would
179
* require a possibly blocking call
180
* to the driver tsf setter, we punt
181
* the tsf adjustment to the mesh tasklet
182
*/
183
msync_dbg(sdata,
184
"TSF : kicking off TSF adjustment with clockdrift_max=%lld\n",
185
ifmsh->sync_offset_clockdrift_max);
186
set_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags);
187
} else {
188
msync_dbg(sdata,
189
"TSF : max clockdrift=%lld; too small to adjust\n",
190
(long long)ifmsh->sync_offset_clockdrift_max);
191
ifmsh->sync_offset_clockdrift_max = 0;
192
}
193
spin_unlock_bh(&ifmsh->sync_offset_lock);
194
}
195
196
static const struct sync_method sync_methods[] = {
197
{
198
.method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
199
.ops = {
200
.rx_bcn_presp = &mesh_sync_offset_rx_bcn_presp,
201
.adjust_tsf = &mesh_sync_offset_adjust_tsf,
202
}
203
},
204
};
205
206
const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
207
{
208
int i;
209
210
for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
211
if (sync_methods[i].method == method)
212
return &sync_methods[i].ops;
213
}
214
return NULL;
215
}
216
217