Add ieee80211_ageq; a facility for staging packets that require

long-term work before they can be serviced.  Packets are tagged and
assigned an age (in seconds) at the point they are added to the
queue.  If a packet is not retrieved before it's age expires it is
reclaimed.  Tagging can take two forms: a reference to an ieee80211_node
(as happens in the tx path) or an opaque token in cases where there
is no reference or the node structure is not stable (i.e. it's going
to be destroyed).

o add ic_stageq to replace the per-node wds staging queue used for
  dynamic wds
o add ieee80211_mac_hash for building ageq tokens; this computes a
  32-bit hash from an 802.11 mac address (copied from the bridge)
o while here fix a stray ';' noticed in IEEE80211_PSQ_INIT

Reviewed by:	rpaulo
Approved by:	re (kensmith)
This commit is contained in:
Sam Leffler 2009-07-05 18:17:37 +00:00
parent 96831ae59b
commit 5b16c28c42
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=195379
8 changed files with 391 additions and 140 deletions

View File

@ -268,6 +268,7 @@ ieee80211_ifattach(struct ieee80211com *ic,
ic->ic_update_mcast = null_update_mcast;
ic->ic_update_promisc = null_update_promisc;
ic->ic_hash_key = arc4random();
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
ic->ic_lintval = ic->ic_bintval;
ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
@ -1568,3 +1569,39 @@ ieee80211_media2rate(int mword)
ieeerates[IFM_SUBTYPE(mword)] : 0;
#undef N
}
/*
* The following hash function is adapted from "Hash Functions" by Bob Jenkins
* ("Algorithm Alley", Dr. Dobbs Journal, September 1997).
*/
#define mix(a, b, c) \
do { \
a -= b; a -= c; a ^= (c >> 13); \
b -= c; b -= a; b ^= (a << 8); \
c -= a; c -= b; c ^= (b >> 13); \
a -= b; a -= c; a ^= (c >> 12); \
b -= c; b -= a; b ^= (a << 16); \
c -= a; c -= b; c ^= (b >> 5); \
a -= b; a -= c; a ^= (c >> 3); \
b -= c; b -= a; b ^= (a << 10); \
c -= a; c -= b; c ^= (b >> 15); \
} while (/*CONSTCOND*/0)
uint32_t
ieee80211_mac_hash(const struct ieee80211com *ic,
const uint8_t addr[IEEE80211_ADDR_LEN])
{
uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key;
b += addr[5] << 8;
b += addr[4];
a += addr[3] << 24;
a += addr[2] << 16;
a += addr[1] << 8;
a += addr[0];
mix(a, b, c);
return c;
}
#undef mix

View File

@ -0,0 +1,236 @@
/*-
* Copyright (c) 2009 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* IEEE 802.11 age queue support.
*/
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net80211/ieee80211_var.h>
/*
* Initialize an ageq.
*/
void
ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name)
{
memset(aq, 0, sizeof(aq));
aq->aq_maxlen = maxlen;
IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */
}
/*
* Cleanup an ageq initialized with ieee80211_ageq_init. Note
* the queue is assumed empty; this can be done with ieee80211_ageq_drain.
*/
void
ieee80211_ageq_cleanup(struct ieee80211_ageq *aq)
{
KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len));
IEEE80211_AGEQ_DESTROY(aq); /* OS-dependent cleanup */
}
/*
* Free an mbuf according to ageq rules: if marked as holding
* and 802.11 frame then also reclaim a node reference from
* the packet header; this handles packets q'd in the tx path.
*/
static void
ageq_mfree(struct mbuf *m)
{
if (m->m_flags & M_ENCAP) {
struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
ieee80211_free_node(ni);
}
m->m_nextpkt = NULL;
m_freem(m);
}
/*
* Free a list of mbufs using ageq rules (see above).
*/
void
ieee80211_ageq_mfree(struct mbuf *m)
{
struct mbuf *next;
for (; m != NULL; m = next) {
next = m->m_nextpkt;
ageq_mfree(m);
}
}
/*
* Append an mbuf to the ageq and mark it with the specified max age
* If the frame is not removed before the age (in seconds) expires
* then it is reclaimed (along with any node reference).
*/
int
ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age)
{
IEEE80211_AGEQ_LOCK(aq);
if (__predict_true(aq->aq_len < aq->aq_maxlen)) {
if (aq->aq_tail == NULL) {
aq->aq_head = m;
} else {
aq->aq_tail->m_nextpkt = m;
age -= M_AGE_GET(aq->aq_head);
}
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
aq->aq_tail = m;
aq->aq_len++;
IEEE80211_AGEQ_UNLOCK(aq);
return 0;
} else {
/*
* No space, drop and cleanup references.
*/
aq->aq_drops++;
IEEE80211_AGEQ_UNLOCK(aq);
/* XXX tail drop? */
ageq_mfree(m);
return ENOSPC;
}
}
/*
* Drain/reclaim all frames from an ageq.
*/
void
ieee80211_ageq_drain(struct ieee80211_ageq *aq)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
}
/*
* Drain/reclaim frames associated with a specific node from an ageq.
*/
void
ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
struct ieee80211_node *ni)
{
ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
}
/*
* Age frames on the age queue. Ages are stored as time
* deltas (in seconds) relative to the head so we can check
* and/or adjust only the head of the list. If a frame's age
* exceeds the time quanta then remove it. The list of removed
* frames is is returned to the caller joined by m_nextpkt.
*/
struct mbuf *
ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta)
{
struct mbuf *head, **phead;
struct mbuf *m;
phead = &head;
if (aq->aq_len != 0) {
IEEE80211_AGEQ_LOCK(aq);
while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) {
if ((aq->aq_head = m->m_nextpkt) == NULL)
aq->aq_tail = NULL;
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
/* add to private list for return */
*phead = m;
phead = &m->m_nextpkt;
}
if (m != NULL)
M_AGE_SUB(m, quanta);
IEEE80211_AGEQ_UNLOCK(aq);
}
*phead = NULL;
return head;
}
/*
* Remove all frames matching the specified node identifier
* (NULL matches all). Frames are returned as a list joined
* by m_nextpkt.
*/
struct mbuf *
ieee80211_ageq_remove(struct ieee80211_ageq *aq,
struct ieee80211_node *match)
{
struct mbuf *m, **prev, *ohead;
struct mbuf *head, **phead;
IEEE80211_AGEQ_LOCK(aq);
ohead = aq->aq_head;
prev = &aq->aq_head;
phead = &head;
while ((m = *prev) != NULL) {
if (match != NULL && m->m_pkthdr.rcvif != (void *) match) {
prev = &m->m_nextpkt;
continue;
}
/*
* Adjust q length.
*/
KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
aq->aq_len--;
/*
* Remove from forward list; tail pointer is harder.
*/
*prev = m->m_nextpkt;
if (aq->aq_tail == m) {
KASSERT(m->m_nextpkt == NULL, ("not last"));
if (aq->aq_head == m) { /* list empty */
KASSERT(aq->aq_len == 0,
("not empty, len %d", aq->aq_len));
aq->aq_tail = NULL;
} else { /* must be one before */
aq->aq_tail = (struct mbuf *)((uintptr_t)prev -
offsetof(struct mbuf, m_nextpkt));
}
}
/* add to private list for return */
*phead = m;
phead = &m->m_nextpkt;
}
if (head == ohead && aq->aq_head != NULL) /* correct age */
M_AGE_SET(aq->aq_head, M_AGE_GET(head));
IEEE80211_AGEQ_UNLOCK(aq);
*phead = NULL;
return head;
}

View File

@ -0,0 +1,54 @@
/*-
* Copyright (c) 2009 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _NET80211_IEEE80211_STAGEQ_H_
#define _NET80211_IEEE80211_STAGEQ_H_
struct ieee80211_node;
struct mbuf;
struct ieee80211_ageq {
ieee80211_ageq_lock_t aq_lock;
int aq_len; /* # items on queue */
int aq_maxlen; /* max queue length */
int aq_drops; /* frames dropped */
struct mbuf *aq_head; /* frames linked w/ m_nextpkt */
struct mbuf *aq_tail; /* last frame in queue */
};
void ieee80211_ageq_init(struct ieee80211_ageq *, int maxlen,
const char *name);
void ieee80211_ageq_cleanup(struct ieee80211_ageq *);
void ieee80211_ageq_mfree(struct mbuf *);
int ieee80211_ageq_append(struct ieee80211_ageq *, struct mbuf *,
int age);
void ieee80211_ageq_drain(struct ieee80211_ageq *);
void ieee80211_ageq_drain_node(struct ieee80211_ageq *,
struct ieee80211_node *);
struct mbuf *ieee80211_ageq_age(struct ieee80211_ageq *, int quanta);
struct mbuf *ieee80211_ageq_remove(struct ieee80211_ageq *,
struct ieee80211_node *match);
#endif /* _NET80211_IEEE80211_STAGEQ_H_ */

View File

@ -100,25 +100,12 @@ typedef struct {
#define IEEE80211_NODE_ITERATE_UNLOCK(_nt) \
mtx_unlock(IEEE80211_NODE_ITERATE_LOCK_OBJ(_nt))
#define _AGEQ_ENQUEUE(_ifq, _m, _qlen, _age) do { \
(_m)->m_nextpkt = NULL; \
if ((_ifq)->ifq_tail != NULL) { \
_age -= M_AGE_GET((_ifq)->ifq_head); \
(_ifq)->ifq_tail->m_nextpkt = (_m); \
} else { \
(_ifq)->ifq_head = (_m); \
} \
M_AGE_SET(_m, _age); \
(_ifq)->ifq_tail = (_m); \
(_qlen) = ++(_ifq)->ifq_len; \
} while (0)
/*
* Power-save queue definitions.
*/
typedef struct mtx ieee80211_psq_lock_t;
#define IEEE80211_PSQ_INIT(_psq, _name) \
mtx_init(&(_psq)->psq_lock, _name, "802.11 ps q", MTX_DEF);
mtx_init(&(_psq)->psq_lock, _name, "802.11 ps q", MTX_DEF)
#define IEEE80211_PSQ_DESTROY(_psq) mtx_destroy(&(_psq)->psq_lock)
#define IEEE80211_PSQ_LOCK(_psq) mtx_lock(&(_psq)->psq_lock)
#define IEEE80211_PSQ_UNLOCK(_psq) mtx_unlock(&(_psq)->psq_lock)
@ -137,24 +124,16 @@ typedef struct mtx ieee80211_psq_lock_t;
IF_UNLOCK(ifq); \
} while (0)
#endif /* IF_PREPEND_LIST */
/* XXX temporary */
#define IEEE80211_NODE_WDSQ_INIT(_ni, _name) do { \
mtx_init(&(_ni)->ni_wdsq.ifq_mtx, _name, "802.11 wds queue", MTX_DEF);\
(_ni)->ni_wdsq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE; \
} while (0)
#define IEEE80211_NODE_WDSQ_DESTROY(_ni) do { \
mtx_destroy(&(_ni)->ni_wdsq.ifq_mtx); \
} while (0)
#define IEEE80211_NODE_WDSQ_QLEN(_ni) _IF_QLEN(&(_ni)->ni_wdsq)
#define IEEE80211_NODE_WDSQ_LOCK(_ni) IF_LOCK(&(_ni)->ni_wdsq)
#define IEEE80211_NODE_WDSQ_UNLOCK(_ni) IF_UNLOCK(&(_ni)->ni_wdsq)
#define _IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(_ni, _m) do { \
_IF_DEQUEUE(&(_ni)->ni_wdsq, m); \
} while (0)
#define _IEEE80211_NODE_WDSQ_ENQUEUE(_ni, _m, _qlen, _age) do { \
_AGEQ_ENQUEUE(&ni->ni_wdsq, _m, _qlen, _age); \
} while (0)
/*
* Age queue definitions.
*/
typedef struct mtx ieee80211_ageq_lock_t;
#define IEEE80211_AGEQ_INIT(_aq, _name) \
mtx_init(&(_aq)->aq_lock, _name, "802.11 age q", MTX_DEF)
#define IEEE80211_AGEQ_DESTROY(_aq) mtx_destroy(&(_aq)->aq_lock)
#define IEEE80211_AGEQ_LOCK(_aq) mtx_lock(&(_aq)->aq_lock)
#define IEEE80211_AGEQ_UNLOCK(_aq) mtx_unlock(&(_aq)->aq_lock)
/*
* 802.1x MAC ACL database locking definitions.

View File

@ -99,6 +99,9 @@ MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
void
ieee80211_node_attach(struct ieee80211com *ic)
{
/* XXX really want maxlen enforced per-sta */
ieee80211_ageq_init(&ic->ic_stageq, ic->ic_max_keyix * 8,
"802.11 staging q");
ieee80211_node_table_init(ic, &ic->ic_sta, "station",
IEEE80211_INACT_INIT, ic->ic_max_keyix);
callout_init(&ic->ic_inact, CALLOUT_MPSAFE);
@ -127,6 +130,7 @@ ieee80211_node_detach(struct ieee80211com *ic)
callout_drain(&ic->ic_inact);
ieee80211_node_table_cleanup(&ic->ic_sta);
ieee80211_ageq_cleanup(&ic->ic_stageq);
}
void
@ -927,6 +931,7 @@ node_cleanup(struct ieee80211_node *ni)
{
#define N(a) (sizeof(a)/sizeof(a[0]))
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int i;
/* NB: preserve ni_table */
@ -946,6 +951,11 @@ node_cleanup(struct ieee80211_node *ni)
else if (ni->ni_ath_flags & IEEE80211_NODE_ATH)
ieee80211_ff_node_cleanup(ni);
#endif
/*
* Clear any staging queue entries.
*/
ieee80211_ageq_drain_node(&ic->ic_stageq, ni);
/*
* Clear AREF flag that marks the authorization refcnt bump
* has happened. This is probably not needed as the node
@ -999,7 +1009,6 @@ node_free(struct ieee80211_node *ni)
ic->ic_node_cleanup(ni);
ieee80211_ies_cleanup(&ni->ni_ies);
ieee80211_psq_cleanup(&ni->ni_psq);
IEEE80211_NODE_WDSQ_DESTROY(ni);
free(ni, M_80211_NODE);
}
@ -1016,11 +1025,6 @@ node_age(struct ieee80211_node *ni)
if (ieee80211_node_psq_age(ni) != 0 &&
ni->ni_psq.psq_len == 0 && vap->iv_set_tim != NULL)
vap->iv_set_tim(ni, 0);
/*
* Age frames on the wds pending queue.
*/
if (IEEE80211_NODE_WDSQ_QLEN(ni) != 0)
ieee80211_node_wdsq_age(ni);
/*
* Age out HT resources (e.g. frames on the
* A-MPDU reorder queues).
@ -1086,7 +1090,6 @@ ieee80211_alloc_node(struct ieee80211_node_table *nt,
ni->ni_inact = ni->ni_inact_reload;
ni->ni_ath_defkeyix = 0x7fff;
ieee80211_psq_init(&ni->ni_psq, "unknown");
IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
IEEE80211_NODE_LOCK(nt);
TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
@ -1136,7 +1139,6 @@ ieee80211_tmp_node(struct ieee80211vap *vap,
ni->ni_txpower = bss->ni_txpower;
/* XXX optimize away */
ieee80211_psq_init(&ni->ni_psq, "unknown");
IEEE80211_NODE_WDSQ_INIT(ni, "unknown");
} else {
/* XXX msg */
vap->iv_stats.is_rx_nodealloc++;
@ -2075,6 +2077,7 @@ ieee80211_node_timeout(void *arg)
if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0) {
ieee80211_scan_timeout(ic);
ieee80211_timeout_stations(ic);
ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT);
IEEE80211_LOCK(ic);
ieee80211_erp_timeout(ic);

View File

@ -193,8 +193,6 @@ struct ieee80211_node {
struct ieee80211_nodestats ni_stats; /* per-node statistics */
struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
/* XXX move to vap? */
struct ifqueue ni_wdsq; /* wds pending queue */
uint64_t ni_spare[4];
};
MALLOC_DECLARE(M_80211_NODE);

View File

@ -44,6 +44,7 @@
#include <net80211/_ieee80211.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ageq.h>
#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_dfs.h>
#include <net80211/ieee80211_ioctl.h> /* for ieee80211_stats */
@ -194,6 +195,8 @@ struct ieee80211com {
/* NB: this is the union of all vap stations/neighbors */
int ic_max_keyix; /* max h/w key index */
struct ieee80211_node_table ic_sta; /* stations/neighbors */
struct ieee80211_ageq ic_stageq; /* frame staging queue */
uint32_t ic_hash_key; /* random key for mac hash */
/* XXX multi-bss: split out common/vap parts */
struct ieee80211_wme_state ic_wme; /* WME/WMM state */
@ -659,6 +662,8 @@ struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
int ieee, int flags);
int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
uint32_t ieee80211_mac_hash(const struct ieee80211com *,
const uint8_t addr[IEEE80211_ADDR_LEN]);
void ieee80211_radiotap_attach(struct ieee80211com *,
struct ieee80211_radiotap_header *th, int tlen,

View File

@ -97,6 +97,28 @@ wds_vattach(struct ieee80211vap *vap)
vap->iv_opdetach = wds_vdetach;
}
static void
wds_flush(struct ieee80211_node *ni)
{
struct ieee80211com *ic = ni->ni_ic;
struct mbuf *m, *next;
int8_t rssi, nf;
m = ieee80211_ageq_remove(&ic->ic_stageq,
(void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr));
if (m == NULL)
return;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_WDS, ni,
"%s", "flush wds queue");
ic->ic_node_getsignal(ni, &rssi, &nf);
for (; m != NULL; m = next) {
next = m->m_nextpkt;
m->m_nextpkt = NULL;
ieee80211_input(ni, m, rssi, nf);
}
}
static int
ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
{
@ -195,26 +217,10 @@ ieee80211_create_wds(struct ieee80211vap *vap, struct ieee80211_channel *chan)
}
/*
* Flush pending frames now that were setup.
* Flush any pending frames now that were setup.
*/
if (ni != NULL && IEEE80211_NODE_WDSQ_QLEN(ni) != 0) {
int8_t rssi, nf;
IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
"flush wds queue, %u packets queued",
IEEE80211_NODE_WDSQ_QLEN(ni));
ic->ic_node_getsignal(ni, &rssi, &nf);
for (;;) {
struct mbuf *m;
IEEE80211_NODE_WDSQ_LOCK(ni);
_IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
IEEE80211_NODE_WDSQ_UNLOCK(ni);
if (m == NULL)
break;
ieee80211_input(ni, m, rssi, nf);
}
}
if (ni != NULL)
wds_flush(ni);
return (ni == NULL ? ENOENT : 0);
}
@ -310,90 +316,23 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m)
void
ieee80211_dwds_discover(struct ieee80211_node *ni, struct mbuf *m)
{
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
int qlen, age;
IEEE80211_NODE_WDSQ_LOCK(ni);
if (!_IF_QFULL(&ni->ni_wdsq)) {
/*
* Tag the frame with it's expiry time and insert
* it in the queue. The aging interval is 4 times
* the listen interval specified by the station.
* Frames that sit around too long are reclaimed
* using this information.
*/
/* XXX handle overflow? */
/* XXX per/vap beacon interval? */
/* NB: TU -> secs */
age = ((ni->ni_intval * ic->ic_lintval) << 2) / 1024;
_IEEE80211_NODE_WDSQ_ENQUEUE(ni, m, qlen, age);
IEEE80211_NODE_WDSQ_UNLOCK(ni);
IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
"save frame, %u now queued", qlen);
} else {
vap->iv_stats.is_dwds_qdrop++;
_IF_DROP(&ni->ni_wdsq);
IEEE80211_NODE_WDSQ_UNLOCK(ni);
IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_WDS,
mtod(m, struct ieee80211_frame *), "wds data",
"pending q overflow, drops %d (len %d)",
ni->ni_wdsq.ifq_drops, ni->ni_wdsq.ifq_len);
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_dumppkts(vap))
ieee80211_dump_pkt(ic, mtod(m, caddr_t),
m->m_len, -1, -1);
#endif
/* XXX tail drop? */
m_freem(m);
}
/*
* Save the frame with an aging interval 4 times
* the listen interval specified by the station.
* Frames that sit around too long are reclaimed
* using this information.
* XXX handle overflow?
* XXX per/vap beacon interval?
*/
m->m_pkthdr.rcvif = (void *)(uintptr_t)
ieee80211_mac_hash(ic, ni->ni_macaddr);
(void) ieee80211_ageq_append(&ic->ic_stageq, m,
((ni->ni_intval * ic->ic_lintval) << 2) / 1024);
ieee80211_notify_wds_discover(ni);
}
/*
* Age frames on the WDS pending queue. The aging interval is
* 4 times the listen interval specified by the station. This
* number is factored into the age calculations when the frame
* is placed on the queue. We store ages as time differences
* so we can check and/or adjust only the head of the list.
* If a frame's age exceeds the threshold then discard it.
* The number of frames discarded is returned to the caller.
*/
int
ieee80211_node_wdsq_age(struct ieee80211_node *ni)
{
#ifdef IEEE80211_DEBUG
struct ieee80211vap *vap = ni->ni_vap;
#endif
struct mbuf *m;
int discard = 0;
IEEE80211_NODE_WDSQ_LOCK(ni);
while (_IF_POLL(&ni->ni_wdsq, m) != NULL &&
M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
"discard frame, age %u", M_AGE_GET(m));
/* XXX could be optimized */
_IEEE80211_NODE_WDSQ_DEQUEUE_HEAD(ni, m);
m_freem(m);
discard++;
}
if (m != NULL)
M_AGE_SUB(m, IEEE80211_INACT_WAIT);
IEEE80211_NODE_WDSQ_UNLOCK(ni);
IEEE80211_NOTE(vap, IEEE80211_MSG_WDS, ni,
"discard %u frames for age", discard);
#if 0
IEEE80211_NODE_STAT_ADD(ni, wds_discard, discard);
#endif
return discard;
}
/*
* IEEE80211_M_WDS vap state machine handler.
*/