aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Leffler <sam@FreeBSD.org>2009-07-05 18:17:37 +0000
committerSam Leffler <sam@FreeBSD.org>2009-07-05 18:17:37 +0000
commit5b16c28c42e1a6521f4b4cbea5ef810f46a9515f (patch)
treeffdb2fa7b2375a389404116eb68c653e193a6558
parent96831ae59b2ea8f3a617d18b889028ac80ea0d5e (diff)
Notes
-rw-r--r--sys/net80211/ieee80211.c37
-rw-r--r--sys/net80211/ieee80211_ageq.c236
-rw-r--r--sys/net80211/ieee80211_ageq.h54
-rw-r--r--sys/net80211/ieee80211_freebsd.h43
-rw-r--r--sys/net80211/ieee80211_node.c19
-rw-r--r--sys/net80211/ieee80211_node.h2
-rw-r--r--sys/net80211/ieee80211_var.h5
-rw-r--r--sys/net80211/ieee80211_wds.c135
8 files changed, 391 insertions, 140 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index ee2854b381a7..a8879972d01e 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -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
diff --git a/sys/net80211/ieee80211_ageq.c b/sys/net80211/ieee80211_ageq.c
new file mode 100644
index 000000000000..b7aad74d7eac
--- /dev/null
+++ b/sys/net80211/ieee80211_ageq.c
@@ -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;
+}
diff --git a/sys/net80211/ieee80211_ageq.h b/sys/net80211/ieee80211_ageq.h
new file mode 100644
index 000000000000..fdcf071aa022
--- /dev/null
+++ b/sys/net80211/ieee80211_ageq.h
@@ -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_ */
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 846bb95b04e7..f3a224c5ead7 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -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.
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 68e3fa46d96c..7fae394e1bcf 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -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 */
@@ -947,6 +952,11 @@ node_cleanup(struct ieee80211_node *ni)
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
* should always be removed from the table so not found but
@@ -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);
}
@@ -1017,11 +1026,6 @@ node_age(struct ieee80211_node *ni)
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);
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 0b09453e7de0..6d267591a1d4 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -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);
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 8859c2fe0795..833c696527c4 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -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,
diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c
index 18c4544e0889..807648c6f462 100644
--- a/sys/net80211/ieee80211_wds.c
+++ b/sys/net80211/ieee80211_wds.c
@@ -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,91 +316,24 @@ 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.
*/
static int