View | Details | Raw Unified | Return to bug 241889
Collapse All | Expand All

(-)head/sys/net80211/ieee80211_amrr.c (-8 / +14 lines)
Lines 1-508 Link Here
1
/*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
1
/*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
2
2
3
/*-
3
/*-
4
 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
4
 * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
5
 * Copyright (c) 2006
5
 * Copyright (c) 2006
6
 *	Damien Bergamini <damien.bergamini@free.fr>
6
 *	Damien Bergamini <damien.bergamini@free.fr>
7
 *
7
 *
8
 * Permission to use, copy, modify, and distribute this software for any
8
 * Permission to use, copy, modify, and distribute this software for any
9
 * purpose with or without fee is hereby granted, provided that the above
9
 * purpose with or without fee is hereby granted, provided that the above
10
 * copyright notice and this permission notice appear in all copies.
10
 * copyright notice and this permission notice appear in all copies.
11
 *
11
 *
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
 */
19
 */
20
20
21
#include <sys/cdefs.h>
21
#include <sys/cdefs.h>
22
__FBSDID("$FreeBSD$");
22
__FBSDID("$FreeBSD$");
23
23
24
/*-
24
/*-
25
 * Naive implementation of the Adaptive Multi Rate Retry algorithm:
25
 * Naive implementation of the Adaptive Multi Rate Retry algorithm:
26
 *
26
 *
27
 * "IEEE 802.11 Rate Adaptation: A Practical Approach"
27
 * "IEEE 802.11 Rate Adaptation: A Practical Approach"
28
 *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
28
 *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
29
 *  INRIA Sophia - Projet Planete
29
 *  INRIA Sophia - Projet Planete
30
 *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
30
 *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
31
 */
31
 */
32
#include "opt_wlan.h"
32
#include "opt_wlan.h"
33
33
34
#include <sys/param.h>
34
#include <sys/param.h>
35
#include <sys/kernel.h>
35
#include <sys/kernel.h>
36
#include <sys/malloc.h>
36
#include <sys/malloc.h>
37
#include <sys/module.h>
37
#include <sys/module.h>
38
#include <sys/sbuf.h>
38
#include <sys/sbuf.h>
39
#include <sys/socket.h>
39
#include <sys/socket.h>
40
#include <sys/sysctl.h>
40
#include <sys/sysctl.h>
41
41
42
#include <net/if.h>
42
#include <net/if.h>
43
#include <net/if_var.h>
43
#include <net/if_var.h>
44
#include <net/if_media.h>
44
#include <net/if_media.h>
45
#include <net/ethernet.h>
45
#include <net/ethernet.h>
46
46
47
#ifdef INET
47
#ifdef INET
48
#include <netinet/in.h>
48
#include <netinet/in.h>
49
#include <netinet/if_ether.h>
49
#include <netinet/if_ether.h>
50
#endif
50
#endif
51
51
52
#include <net80211/ieee80211_var.h>
52
#include <net80211/ieee80211_var.h>
53
#include <net80211/ieee80211_ht.h>
53
#include <net80211/ieee80211_ht.h>
54
#include <net80211/ieee80211_amrr.h>
54
#include <net80211/ieee80211_amrr.h>
55
#include <net80211/ieee80211_ratectl.h>
55
#include <net80211/ieee80211_ratectl.h>
56
56
57
#define is_success(amn)	\
57
#define is_success(amn)	\
58
	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
58
	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
59
#define is_failure(amn)	\
59
#define is_failure(amn)	\
60
	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
60
	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
61
#define is_enough(amn)		\
61
#define is_enough(amn)		\
62
	((amn)->amn_txcnt > 10)
62
	((amn)->amn_txcnt > 10)
63
63
64
static void	amrr_setinterval(const struct ieee80211vap *, int);
64
static void	amrr_setinterval(const struct ieee80211vap *, int);
65
static void	amrr_init(struct ieee80211vap *);
65
static void	amrr_init(struct ieee80211vap *);
66
static void	amrr_deinit(struct ieee80211vap *);
66
static void	amrr_deinit(struct ieee80211vap *);
67
static void	amrr_node_init(struct ieee80211_node *);
67
static void	amrr_node_init(struct ieee80211_node *);
68
static void	amrr_node_deinit(struct ieee80211_node *);
68
static void	amrr_node_deinit(struct ieee80211_node *);
69
static int	amrr_update(struct ieee80211_amrr *,
69
static int	amrr_update(struct ieee80211_amrr *,
70
    			struct ieee80211_amrr_node *, struct ieee80211_node *);
70
    			struct ieee80211_amrr_node *, struct ieee80211_node *);
71
static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
71
static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
72
static void	amrr_tx_complete(const struct ieee80211_node *,
72
static void	amrr_tx_complete(const struct ieee80211_node *,
73
			const struct ieee80211_ratectl_tx_status *);
73
			const struct ieee80211_ratectl_tx_status *);
74
static void	amrr_tx_update_cb(void *, struct ieee80211_node *);
74
static void	amrr_tx_update_cb(void *, struct ieee80211_node *);
75
static void	amrr_tx_update(struct ieee80211vap *vap,
75
static void	amrr_tx_update(struct ieee80211vap *vap,
76
			struct ieee80211_ratectl_tx_stats *);
76
			struct ieee80211_ratectl_tx_stats *);
77
static void	amrr_sysctlattach(struct ieee80211vap *,
77
static void	amrr_sysctlattach(struct ieee80211vap *,
78
			struct sysctl_ctx_list *, struct sysctl_oid *);
78
			struct sysctl_ctx_list *, struct sysctl_oid *);
79
static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
79
static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
80
80
81
/* number of references from net80211 layer */
81
/* number of references from net80211 layer */
82
static	int nrefs = 0;
82
static	int nrefs = 0;
83
83
84
static const struct ieee80211_ratectl amrr = {
84
static const struct ieee80211_ratectl amrr = {
85
	.ir_name	= "amrr",
85
	.ir_name	= "amrr",
86
	.ir_attach	= NULL,
86
	.ir_attach	= NULL,
87
	.ir_detach	= NULL,
87
	.ir_detach	= NULL,
88
	.ir_init	= amrr_init,
88
	.ir_init	= amrr_init,
89
	.ir_deinit	= amrr_deinit,
89
	.ir_deinit	= amrr_deinit,
90
	.ir_node_init	= amrr_node_init,
90
	.ir_node_init	= amrr_node_init,
91
	.ir_node_deinit	= amrr_node_deinit,
91
	.ir_node_deinit	= amrr_node_deinit,
92
	.ir_rate	= amrr_rate,
92
	.ir_rate	= amrr_rate,
93
	.ir_tx_complete	= amrr_tx_complete,
93
	.ir_tx_complete	= amrr_tx_complete,
94
	.ir_tx_update	= amrr_tx_update,
94
	.ir_tx_update	= amrr_tx_update,
95
	.ir_setinterval	= amrr_setinterval,
95
	.ir_setinterval	= amrr_setinterval,
96
	.ir_node_stats	= amrr_node_stats,
96
	.ir_node_stats	= amrr_node_stats,
97
};
97
};
98
IEEE80211_RATECTL_MODULE(amrr, 1);
98
IEEE80211_RATECTL_MODULE(amrr, 1);
99
IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
99
IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
100
100
101
static void
101
static void
102
amrr_setinterval(const struct ieee80211vap *vap, int msecs)
102
amrr_setinterval(const struct ieee80211vap *vap, int msecs)
103
{
103
{
104
	struct ieee80211_amrr *amrr = vap->iv_rs;
104
	struct ieee80211_amrr *amrr = vap->iv_rs;
105
105
106
	if (!amrr)
106
	if (!amrr)
107
		return;
107
		return;
108
108
109
	if (msecs < 100)
109
	if (msecs < 100)
110
		msecs = 100;
110
		msecs = 100;
111
	amrr->amrr_interval = msecs_to_ticks(msecs);
111
	amrr->amrr_interval = msecs_to_ticks(msecs);
112
}
112
}
113
113
114
static void
114
static void
115
amrr_init(struct ieee80211vap *vap)
115
amrr_init(struct ieee80211vap *vap)
116
{
116
{
117
	struct ieee80211_amrr *amrr;
117
	struct ieee80211_amrr *amrr;
118
118
119
	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
119
	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
120
120
121
	nrefs++;		/* XXX locking */
121
	nrefs++;		/* XXX locking */
122
	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
122
	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
123
	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
123
	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
124
	if (amrr == NULL) {
124
	if (amrr == NULL) {
125
		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
125
		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
126
		return;
126
		return;
127
	}
127
	}
128
	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
128
	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
129
	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
129
	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
130
	amrr_setinterval(vap, 500 /* ms */);
130
	amrr_setinterval(vap, 500 /* ms */);
131
	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
131
	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
132
}
132
}
133
133
134
static void
134
static void
135
amrr_deinit(struct ieee80211vap *vap)
135
amrr_deinit(struct ieee80211vap *vap)
136
{
136
{
137
	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
137
	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
138
	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
138
	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
139
	nrefs--;		/* XXX locking */
139
	nrefs--;		/* XXX locking */
140
}
140
}
141
141
142
/*
142
/*
143
 * Return whether 11n rates are possible.
143
 * Return whether 11n rates are possible.
144
 *
144
 *
145
 * Some 11n devices may return HT information but no HT rates.
145
 * Some 11n devices may return HT information but no HT rates.
146
 * Thus, we shouldn't treat them as an 11n node.
146
 * Thus, we shouldn't treat them as an 11n node.
147
 */
147
 */
148
static int
148
static int
149
amrr_node_is_11n(struct ieee80211_node *ni)
149
amrr_node_is_11n(struct ieee80211_node *ni)
150
{
150
{
151
151
152
	if (ni->ni_chan == NULL)
152
	if (ni->ni_chan == NULL)
153
		return (0);
153
		return (0);
154
	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
154
	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
155
		return (0);
155
		return (0);
156
	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
156
	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
157
		return (0);
157
		return (0);
158
	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
158
	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
159
}
159
}
160
160
161
static void
161
static void
162
amrr_node_init(struct ieee80211_node *ni)
162
amrr_node_init(struct ieee80211_node *ni)
163
{
163
{
164
	const struct ieee80211_rateset *rs = NULL;
164
	const struct ieee80211_rateset *rs = NULL;
165
	struct ieee80211vap *vap = ni->ni_vap;
165
	struct ieee80211vap *vap = ni->ni_vap;
166
	struct ieee80211_amrr *amrr = vap->iv_rs;
166
	struct ieee80211_amrr *amrr = vap->iv_rs;
167
	struct ieee80211_amrr_node *amn;
167
	struct ieee80211_amrr_node *amn;
168
	uint8_t rate;
168
	uint8_t rate;
169
169
170
	if (!amrr) {
170
	if (!amrr) {
171
		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
171
		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
172
		    "per-node structure allocation skipped\n");
172
		    "per-node structure allocation skipped\n");
173
		return;
173
		return;
174
	}
174
	}
175
175
176
	if (ni->ni_rctls == NULL) {
176
	if (ni->ni_rctls == NULL) {
177
		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
177
		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
178
		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
178
		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
179
		if (amn == NULL) {
179
		if (amn == NULL) {
180
			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
180
			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
181
			    "structure\n");
181
			    "structure\n");
182
			return;
182
			return;
183
		}
183
		}
184
	} else
184
	} else
185
		amn = ni->ni_rctls;
185
		amn = ni->ni_rctls;
186
	amn->amn_amrr = amrr;
186
	amn->amn_amrr = amrr;
187
	amn->amn_success = 0;
187
	amn->amn_success = 0;
188
	amn->amn_recovery = 0;
188
	amn->amn_recovery = 0;
189
	amn->amn_txcnt = amn->amn_retrycnt = 0;
189
	amn->amn_txcnt = amn->amn_retrycnt = 0;
190
	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
190
	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
191
191
192
	/* 11n or not? Pick the right rateset */
192
	/* 11n or not? Pick the right rateset */
193
	if (amrr_node_is_11n(ni)) {
193
	if (amrr_node_is_11n(ni)) {
194
		/* XXX ew */
194
		/* XXX ew */
195
		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
195
		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
196
		    "%s: 11n node", __func__);
196
		    "%s: 11n node", __func__);
197
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
197
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
198
	} else {
198
	} else {
199
		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
199
		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
200
		    "%s: non-11n node", __func__);
200
		    "%s: non-11n node", __func__);
201
		rs = &ni->ni_rates;
201
		rs = &ni->ni_rates;
202
	}
202
	}
203
203
204
	/* Initial rate - lowest */
204
	/* Initial rate - lowest */
205
	rate = rs->rs_rates[0];
205
	rate = rs->rs_rates[0];
206
206
207
	/* XXX clear the basic rate flag if it's not 11n */
207
	/* XXX clear the basic rate flag if it's not 11n */
208
	if (! amrr_node_is_11n(ni))
208
	if (! amrr_node_is_11n(ni))
209
		rate &= IEEE80211_RATE_VAL;
209
		rate &= IEEE80211_RATE_VAL;
210
210
211
	/* pick initial rate from the rateset - HT or otherwise */
211
	/* pick initial rate from the rateset - HT or otherwise */
212
	/* Pick something low that's likely to succeed */
212
	/* Pick something low that's likely to succeed */
213
	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
213
	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
214
	    amn->amn_rix--) {
214
	    amn->amn_rix--) {
215
		/* legacy - anything < 36mbit, stop searching */
215
		/* legacy - anything < 36mbit, stop searching */
216
		/* 11n - stop at MCS4 */
216
		/* 11n - stop at MCS4 */
217
		if (amrr_node_is_11n(ni)) {
217
		if (amrr_node_is_11n(ni)) {
218
			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
218
			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
219
				break;
219
				break;
220
		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
220
		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
221
			break;
221
			break;
222
	}
222
	}
223
	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
223
	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
224
224
225
	/* if the rate is an 11n rate, ensure the MCS bit is set */
225
	/* if the rate is an 11n rate, ensure the MCS bit is set */
226
	if (amrr_node_is_11n(ni))
226
	if (amrr_node_is_11n(ni))
227
		rate |= IEEE80211_RATE_MCS;
227
		rate |= IEEE80211_RATE_MCS;
228
228
229
	/* Assign initial rate from the rateset */
229
	/* Assign initial rate from the rateset */
230
	ni->ni_txrate = rate;
230
	ni->ni_txrate = rate;
231
	amn->amn_ticks = ticks;
231
	amn->amn_ticks = ticks;
232
232
233
	/* XXX TODO: we really need a rate-to-string method */
233
	/* XXX TODO: we really need a rate-to-string method */
234
	/* XXX TODO: non-11n rate should be divided by two.. */
234
	/* XXX TODO: non-11n rate should be divided by two.. */
235
	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
235
	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
236
	    "AMRR: nrates=%d, initial rate %s%d",
236
	    "AMRR: nrates=%d, initial rate %s%d",
237
	    rs->rs_nrates,
237
	    rs->rs_nrates,
238
	    amrr_node_is_11n(ni) ? "MCS " : "",
238
	    amrr_node_is_11n(ni) ? "MCS " : "",
239
	    rate & IEEE80211_RATE_VAL);
239
	    rate & IEEE80211_RATE_VAL);
240
}
240
}
241
241
242
static void
242
static void
243
amrr_node_deinit(struct ieee80211_node *ni)
243
amrr_node_deinit(struct ieee80211_node *ni)
244
{
244
{
245
	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
245
	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
246
}
246
}
247
247
248
static int
248
static int
249
amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
249
amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
250
    struct ieee80211_node *ni)
250
    struct ieee80211_node *ni)
251
{
251
{
252
	int rix = amn->amn_rix;
252
	int rix = amn->amn_rix;
253
	const struct ieee80211_rateset *rs = NULL;
253
	const struct ieee80211_rateset *rs = NULL;
254
254
255
	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
255
	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
256
256
257
	/* 11n or not? Pick the right rateset */
257
	/* 11n or not? Pick the right rateset */
258
	if (amrr_node_is_11n(ni)) {
258
	if (amrr_node_is_11n(ni)) {
259
		/* XXX ew */
259
		/* XXX ew */
260
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
260
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
261
	} else {
261
	} else {
262
		rs = &ni->ni_rates;
262
		rs = &ni->ni_rates;
263
	}
263
	}
264
264
265
	/* XXX TODO: we really need a rate-to-string method */
265
	/* XXX TODO: we really need a rate-to-string method */
266
	/* XXX TODO: non-11n rate should be divided by two.. */
266
	/* XXX TODO: non-11n rate should be divided by two.. */
267
	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
267
	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
268
	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
268
	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
269
	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
269
	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
270
	    amn->amn_txcnt,
270
	    amn->amn_txcnt,
271
	    amn->amn_retrycnt);
271
	    amn->amn_retrycnt);
272
272
273
	/*
273
	/*
274
	 * XXX This is totally bogus for 11n, as although high MCS
274
	 * XXX This is totally bogus for 11n, as although high MCS
275
	 * rates for each stream may be failing, the next stream
275
	 * rates for each stream may be failing, the next stream
276
	 * should be checked.
276
	 * should be checked.
277
	 *
277
	 *
278
	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
278
	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
279
	 * MCS23, we should skip 6/7 and try 8 onwards.
279
	 * MCS23, we should skip 6/7 and try 8 onwards.
280
	 */
280
	 */
281
	if (is_success(amn)) {
281
	if (is_success(amn)) {
282
		amn->amn_success++;
282
		amn->amn_success++;
283
		if (amn->amn_success >= amn->amn_success_threshold &&
283
		if (amn->amn_success >= amn->amn_success_threshold &&
284
		    rix + 1 < rs->rs_nrates) {
284
		    rix + 1 < rs->rs_nrates) {
285
			amn->amn_recovery = 1;
285
			amn->amn_recovery = 1;
286
			amn->amn_success = 0;
286
			amn->amn_success = 0;
287
			rix++;
287
			rix++;
288
			/* XXX TODO: we really need a rate-to-string method */
288
			/* XXX TODO: we really need a rate-to-string method */
289
			/* XXX TODO: non-11n rate should be divided by two.. */
289
			/* XXX TODO: non-11n rate should be divided by two.. */
290
			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
290
			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
291
			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
291
			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
292
			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
292
			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
293
			    amn->amn_txcnt, amn->amn_retrycnt);
293
			    amn->amn_txcnt, amn->amn_retrycnt);
294
		} else {
294
		} else {
295
			amn->amn_recovery = 0;
295
			amn->amn_recovery = 0;
296
		}
296
		}
297
	} else if (is_failure(amn)) {
297
	} else if (is_failure(amn)) {
298
		amn->amn_success = 0;
298
		amn->amn_success = 0;
299
		if (rix > 0) {
299
		if (rix > 0) {
300
			if (amn->amn_recovery) {
300
			if (amn->amn_recovery) {
301
				amn->amn_success_threshold *= 2;
301
				amn->amn_success_threshold *= 2;
302
				if (amn->amn_success_threshold >
302
				if (amn->amn_success_threshold >
303
				    amrr->amrr_max_success_threshold)
303
				    amrr->amrr_max_success_threshold)
304
					amn->amn_success_threshold =
304
					amn->amn_success_threshold =
305
					    amrr->amrr_max_success_threshold;
305
					    amrr->amrr_max_success_threshold;
306
			} else {
306
			} else {
307
				amn->amn_success_threshold =
307
				amn->amn_success_threshold =
308
				    amrr->amrr_min_success_threshold;
308
				    amrr->amrr_min_success_threshold;
309
			}
309
			}
310
			rix--;
310
			rix--;
311
			/* XXX TODO: we really need a rate-to-string method */
311
			/* XXX TODO: we really need a rate-to-string method */
312
			/* XXX TODO: non-11n rate should be divided by two.. */
312
			/* XXX TODO: non-11n rate should be divided by two.. */
313
			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
313
			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
314
			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
314
			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
315
			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
315
			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
316
			    amn->amn_txcnt, amn->amn_retrycnt);
316
			    amn->amn_txcnt, amn->amn_retrycnt);
317
		}
317
		}
318
		amn->amn_recovery = 0;
318
		amn->amn_recovery = 0;
319
	}
319
	}
320
320
321
	/* reset counters */
321
	/* reset counters */
322
	amn->amn_txcnt = 0;
322
	amn->amn_txcnt = 0;
323
	amn->amn_retrycnt = 0;
323
	amn->amn_retrycnt = 0;
324
324
325
	return rix;
325
	return rix;
326
}
326
}
327
327
328
/*
328
/*
329
 * Return the rate index to use in sending a data frame.
329
 * Return the rate index to use in sending a data frame.
330
 * Update our internal state if it's been long enough.
330
 * Update our internal state if it's been long enough.
331
 * If the rate changes we also update ni_txrate to match.
331
 * If the rate changes we also update ni_txrate to match.
332
 */
332
 */
333
static int
333
static int
334
amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
334
amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
335
{
335
{
336
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
336
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
337
	struct ieee80211_amrr *amrr;
337
	struct ieee80211_amrr *amrr;
338
	const struct ieee80211_rateset *rs = NULL;
338
	const struct ieee80211_rateset *rs = NULL;
339
	int rix;
339
	int rix;
340
340
341
	/* XXX should return -1 here, but drivers may not expect this... */
341
	/* XXX should return -1 here, but drivers may not expect this... */
342
	if (!amn)
342
	if (!amn)
343
	{
343
	{
344
		ni->ni_txrate = ni->ni_rates.rs_rates[0];
344
		ni->ni_txrate = ni->ni_rates.rs_rates[0];
345
		return 0;
345
		return 0;
346
	}
346
	}
347
347
348
	amrr = amn->amn_amrr;
348
	amrr = amn->amn_amrr;
349
349
350
	/* 11n or not? Pick the right rateset */
350
	/* 11n or not? Pick the right rateset */
351
	if (amrr_node_is_11n(ni)) {
351
	if (amrr_node_is_11n(ni)) {
352
		/* XXX ew */
352
		/* XXX ew */
353
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
353
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
354
	} else {
354
	} else {
355
		rs = &ni->ni_rates;
355
		rs = &ni->ni_rates;
356
	}
356
	}
357
357
358
	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
358
	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
359
		rix = amrr_update(amrr, amn, ni);
359
		rix = amrr_update(amrr, amn, ni);
360
		if (rix != amn->amn_rix) {
360
		if (rix != amn->amn_rix) {
361
			/* update public rate */
361
			/* update public rate */
362
			ni->ni_txrate = rs->rs_rates[rix];
362
			ni->ni_txrate = rs->rs_rates[rix];
363
			/* XXX strip basic rate flag from txrate, if non-11n */
363
			/* XXX strip basic rate flag from txrate, if non-11n */
364
			if (amrr_node_is_11n(ni))
364
			if (amrr_node_is_11n(ni))
365
				ni->ni_txrate |= IEEE80211_RATE_MCS;
365
				ni->ni_txrate |= IEEE80211_RATE_MCS;
366
			else
366
			else
367
				ni->ni_txrate &= IEEE80211_RATE_VAL;
367
				ni->ni_txrate &= IEEE80211_RATE_VAL;
368
			amn->amn_rix = rix;
368
			amn->amn_rix = rix;
369
		}
369
		}
370
		amn->amn_ticks = ticks;
370
		amn->amn_ticks = ticks;
371
	} else
371
	} else
372
		rix = amn->amn_rix;
372
		rix = amn->amn_rix;
373
	return rix;
373
	return rix;
374
}
374
}
375
375
376
/*
376
/*
377
 * Update statistics with tx complete status.  Ok is non-zero
377
 * Update statistics with tx complete status.  Ok is non-zero
378
 * if the packet is known to be ACK'd.  Retries has the number
378
 * if the packet is known to be ACK'd.  Retries has the number
379
 * retransmissions (i.e. xmit attempts - 1).
379
 * retransmissions (i.e. xmit attempts - 1).
380
 */
380
 */
381
static void
381
static void
382
amrr_tx_complete(const struct ieee80211_node *ni,
382
amrr_tx_complete(const struct ieee80211_node *ni,
383
    const struct ieee80211_ratectl_tx_status *status)
383
    const struct ieee80211_ratectl_tx_status *status)
384
{
384
{
385
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
385
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
386
	int retries;
386
	int retries;
387
387
388
	if (!amn)
388
	if (!amn)
389
		return;
389
		return;
390
390
391
	retries = 0;
391
	retries = 0;
392
	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
392
	if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
393
		retries = status->long_retries;
393
		retries = status->long_retries;
394
394
395
	amn->amn_txcnt++;
395
	amn->amn_txcnt++;
396
	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
396
	if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
397
		amn->amn_success++;
397
		amn->amn_success++;
398
	amn->amn_retrycnt += retries;
398
	amn->amn_retrycnt += retries;
399
}
399
}
400
400
401
static void
401
static void
402
amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
402
amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
403
{
403
{
404
	struct ieee80211_ratectl_tx_stats *stats = arg;
404
	struct ieee80211_ratectl_tx_stats *stats = arg;
405
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
405
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
406
	int txcnt, success, retrycnt;
406
	int txcnt, success, retrycnt;
407
407
408
	if (!amn)
408
	if (!amn)
409
		return;
409
		return;
410
410
411
	txcnt = stats->nframes;
411
	txcnt = stats->nframes;
412
	success = stats->nsuccess;
412
	success = stats->nsuccess;
413
	retrycnt = 0;
413
	retrycnt = 0;
414
	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
414
	if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
415
		retrycnt = stats->nretries;
415
		retrycnt = stats->nretries;
416
416
417
	amn->amn_txcnt += txcnt;
417
	amn->amn_txcnt += txcnt;
418
	amn->amn_success += success;
418
	amn->amn_success += success;
419
	amn->amn_retrycnt += retrycnt;
419
	amn->amn_retrycnt += retrycnt;
420
}
420
}
421
421
422
/*
422
/*
423
 * Set tx count/retry statistics explicitly.  Intended for
423
 * Set tx count/retry statistics explicitly.  Intended for
424
 * drivers that poll the device for statistics maintained
424
 * drivers that poll the device for statistics maintained
425
 * in the device.
425
 * in the device.
426
 */
426
 */
427
static void
427
static void
428
amrr_tx_update(struct ieee80211vap *vap,
428
amrr_tx_update(struct ieee80211vap *vap,
429
    struct ieee80211_ratectl_tx_stats *stats)
429
    struct ieee80211_ratectl_tx_stats *stats)
430
{
430
{
431
431
432
	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
432
	if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
433
		amrr_tx_update_cb(stats, stats->ni);
433
		amrr_tx_update_cb(stats, stats->ni);
434
	else {
434
	else {
435
		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
435
		ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
436
		    amrr_tx_update_cb, stats);
436
		    amrr_tx_update_cb, stats);
437
	}
437
	}
438
}
438
}
439
439
440
static int
440
static int
441
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
441
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
442
{
442
{
443
	struct ieee80211vap *vap = arg1;
443
	struct ieee80211vap *vap = arg1;
444
	struct ieee80211_amrr *amrr = vap->iv_rs;
444
	struct ieee80211_amrr *amrr = vap->iv_rs;
445
	int msecs, error;
445
	int msecs, error;
446
446
447
	if (!amrr)
447
	if (!amrr)
448
		return ENOMEM;
448
		return ENOMEM;
449
449
450
	msecs = ticks_to_msecs(amrr->amrr_interval);
450
	msecs = ticks_to_msecs(amrr->amrr_interval);
451
	error = sysctl_handle_int(oidp, &msecs, 0, req);
451
	error = sysctl_handle_int(oidp, &msecs, 0, req);
452
	if (error || !req->newptr)
452
	if (error || !req->newptr)
453
		return error;
453
		return error;
454
	amrr_setinterval(vap, msecs);
454
	amrr_setinterval(vap, msecs);
455
	return 0;
455
	return 0;
456
}
456
}
457
457
458
static void
458
static void
459
amrr_sysctlattach(struct ieee80211vap *vap,
459
amrr_sysctlattach(struct ieee80211vap *vap,
460
    struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
460
    struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
461
{
461
{
462
	struct ieee80211_amrr *amrr = vap->iv_rs;
462
	struct ieee80211_amrr *amrr = vap->iv_rs;
463
463
464
	if (!amrr)
464
	if (!amrr)
465
		return;
465
		return;
466
466
467
	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
467
	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
468
	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
468
	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
469
	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
469
	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
470
	/* XXX bounds check values */
470
	/* XXX bounds check values */
471
	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
471
	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
472
	    "amrr_max_sucess_threshold", CTLFLAG_RW,
472
	    "amrr_max_sucess_threshold", CTLFLAG_RW,
473
	    &amrr->amrr_max_success_threshold, 0, "");
473
	    &amrr->amrr_max_success_threshold, 0, "");
474
	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
474
	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
475
	    "amrr_min_sucess_threshold", CTLFLAG_RW,
475
	    "amrr_min_sucess_threshold", CTLFLAG_RW,
476
	    &amrr->amrr_min_success_threshold, 0, "");
476
	    &amrr->amrr_min_success_threshold, 0, "");
477
}
477
}
478
478
479
static void
479
static void
480
amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
480
amrr_print_node_rate(struct ieee80211_amrr_node *amn,
481
    struct ieee80211_node *ni, struct sbuf *s)
481
{
482
{
482
	int rate;
483
	int rate;
483
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
484
	struct ieee80211_rateset *rs;
484
	struct ieee80211_rateset *rs;
485
485
486
	/* XXX TODO: check locking? */
487
488
	if (!amn)
489
		return;
490
491
	/* XXX TODO: this should be a method */
492
	if (amrr_node_is_11n(ni)) {
486
	if (amrr_node_is_11n(ni)) {
493
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
487
		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
494
		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
488
		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
495
		sbuf_printf(s, "rate: MCS %d\n", rate);
489
		sbuf_printf(s, "rate: MCS %d\n", rate);
496
	} else {
490
	} else {
497
		rs = &ni->ni_rates;
491
		rs = &ni->ni_rates;
498
		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
492
		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
499
		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
493
		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
500
	}
494
	}
495
}
501
496
497
static void
498
amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
499
{
500
	struct ieee80211_amrr_node *amn = ni->ni_rctls;
501
502
	/* XXX TODO: check locking? */
503
504
	if (!amn)
505
		return;
506
507
	amrr_print_node_rate(amn, ni, s);
502
	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
508
	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
503
	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
509
	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
504
	sbuf_printf(s, "success: %u\n", amn->amn_success);
510
	sbuf_printf(s, "success: %u\n", amn->amn_success);
505
	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
511
	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
506
	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
512
	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
507
	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
513
	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
508
}
514
}

Return to bug 241889