132176cfdSRui Paulo /*-
232176cfdSRui Paulo * Copyright (c) 2009 Sam Leffler, Errno Consulting
332176cfdSRui Paulo * All rights reserved.
432176cfdSRui Paulo *
532176cfdSRui Paulo * Redistribution and use in source and binary forms, with or without
632176cfdSRui Paulo * modification, are permitted provided that the following conditions
732176cfdSRui Paulo * are met:
832176cfdSRui Paulo * 1. Redistributions of source code must retain the above copyright
932176cfdSRui Paulo * notice, this list of conditions and the following disclaimer.
1032176cfdSRui Paulo * 2. Redistributions in binary form must reproduce the above copyright
1132176cfdSRui Paulo * notice, this list of conditions and the following disclaimer in the
1232176cfdSRui Paulo * documentation and/or other materials provided with the distribution.
1332176cfdSRui Paulo *
1432176cfdSRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1532176cfdSRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1632176cfdSRui Paulo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1732176cfdSRui Paulo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1832176cfdSRui Paulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1932176cfdSRui Paulo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2032176cfdSRui Paulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2132176cfdSRui Paulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2232176cfdSRui Paulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2332176cfdSRui Paulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2432176cfdSRui Paulo */
2532176cfdSRui Paulo
26085ff963SMatthew Dillon #include <sys/cdefs.h>
27085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
28085ff963SMatthew Dillon
2932176cfdSRui Paulo /*
3032176cfdSRui Paulo * IEEE 802.11 age queue support.
3132176cfdSRui Paulo */
3232176cfdSRui Paulo #include "opt_wlan.h"
3332176cfdSRui Paulo
3432176cfdSRui Paulo #include <sys/param.h>
3532176cfdSRui Paulo #include <sys/systm.h>
3632176cfdSRui Paulo #include <sys/kernel.h>
37*4f655ef5SMatthew Dillon #include <sys/malloc.h>
3832176cfdSRui Paulo
3932176cfdSRui Paulo #include <sys/socket.h>
4032176cfdSRui Paulo
4132176cfdSRui Paulo #include <net/if.h>
42085ff963SMatthew Dillon #include <net/if_var.h>
4332176cfdSRui Paulo #include <net/if_media.h>
4432176cfdSRui Paulo #include <net/ethernet.h>
4532176cfdSRui Paulo
4632176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
4732176cfdSRui Paulo
4832176cfdSRui Paulo /*
4932176cfdSRui Paulo * Initialize an ageq.
5032176cfdSRui Paulo */
5132176cfdSRui Paulo void
ieee80211_ageq_init(struct ieee80211_ageq * aq,int maxlen,const char * name)5232176cfdSRui Paulo ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name)
5332176cfdSRui Paulo {
547c48796cSSascha Wildner memset(aq, 0, sizeof(*aq));
5532176cfdSRui Paulo aq->aq_maxlen = maxlen;
56085ff963SMatthew Dillon IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */
5732176cfdSRui Paulo }
5832176cfdSRui Paulo
5932176cfdSRui Paulo /*
6032176cfdSRui Paulo * Cleanup an ageq initialized with ieee80211_ageq_init. Note
6132176cfdSRui Paulo * the queue is assumed empty; this can be done with ieee80211_ageq_drain.
6232176cfdSRui Paulo */
6332176cfdSRui Paulo void
ieee80211_ageq_cleanup(struct ieee80211_ageq * aq)6432176cfdSRui Paulo ieee80211_ageq_cleanup(struct ieee80211_ageq *aq)
6532176cfdSRui Paulo {
6632176cfdSRui Paulo KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len));
67085ff963SMatthew Dillon IEEE80211_AGEQ_DESTROY(aq); /* OS-dependent cleanup */
6832176cfdSRui Paulo }
6932176cfdSRui Paulo
7032176cfdSRui Paulo /*
7132176cfdSRui Paulo * Free an mbuf according to ageq rules: if marked as holding
7232176cfdSRui Paulo * and 802.11 frame then also reclaim a node reference from
7332176cfdSRui Paulo * the packet header; this handles packets q'd in the tx path.
7432176cfdSRui Paulo */
7532176cfdSRui Paulo static void
ageq_mfree(struct mbuf * m)7632176cfdSRui Paulo ageq_mfree(struct mbuf *m)
7732176cfdSRui Paulo {
7832176cfdSRui Paulo if (m->m_flags & M_ENCAP) {
7932176cfdSRui Paulo struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
8032176cfdSRui Paulo ieee80211_free_node(ni);
8132176cfdSRui Paulo }
8232176cfdSRui Paulo m->m_nextpkt = NULL;
8332176cfdSRui Paulo m_freem(m);
8432176cfdSRui Paulo }
8532176cfdSRui Paulo
8632176cfdSRui Paulo /*
8732176cfdSRui Paulo * Free a list of mbufs using ageq rules (see above).
8832176cfdSRui Paulo */
8932176cfdSRui Paulo void
ieee80211_ageq_mfree(struct mbuf * m)9032176cfdSRui Paulo ieee80211_ageq_mfree(struct mbuf *m)
9132176cfdSRui Paulo {
9232176cfdSRui Paulo struct mbuf *next;
9332176cfdSRui Paulo
9432176cfdSRui Paulo for (; m != NULL; m = next) {
9532176cfdSRui Paulo next = m->m_nextpkt;
9632176cfdSRui Paulo ageq_mfree(m);
9732176cfdSRui Paulo }
9832176cfdSRui Paulo }
9932176cfdSRui Paulo
10032176cfdSRui Paulo /*
10132176cfdSRui Paulo * Append an mbuf to the ageq and mark it with the specified max age
10232176cfdSRui Paulo * If the frame is not removed before the age (in seconds) expires
10332176cfdSRui Paulo * then it is reclaimed (along with any node reference).
10432176cfdSRui Paulo */
10532176cfdSRui Paulo int
ieee80211_ageq_append(struct ieee80211_ageq * aq,struct mbuf * m,int age)10632176cfdSRui Paulo ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age)
10732176cfdSRui Paulo {
108085ff963SMatthew Dillon IEEE80211_AGEQ_LOCK(aq);
10932176cfdSRui Paulo if (__predict_true(aq->aq_len < aq->aq_maxlen)) {
11032176cfdSRui Paulo if (aq->aq_tail == NULL) {
11132176cfdSRui Paulo aq->aq_head = m;
11232176cfdSRui Paulo } else {
11332176cfdSRui Paulo aq->aq_tail->m_nextpkt = m;
11432176cfdSRui Paulo age -= M_AGE_GET(aq->aq_head);
11532176cfdSRui Paulo }
11632176cfdSRui Paulo KASSERT(age >= 0, ("age %d", age));
11732176cfdSRui Paulo M_AGE_SET(m, age);
11832176cfdSRui Paulo m->m_nextpkt = NULL;
11932176cfdSRui Paulo aq->aq_tail = m;
12032176cfdSRui Paulo aq->aq_len++;
121085ff963SMatthew Dillon IEEE80211_AGEQ_UNLOCK(aq);
12232176cfdSRui Paulo return 0;
12332176cfdSRui Paulo } else {
12432176cfdSRui Paulo /*
12532176cfdSRui Paulo * No space, drop and cleanup references.
12632176cfdSRui Paulo */
12732176cfdSRui Paulo aq->aq_drops++;
128085ff963SMatthew Dillon IEEE80211_AGEQ_UNLOCK(aq);
12932176cfdSRui Paulo /* XXX tail drop? */
13032176cfdSRui Paulo ageq_mfree(m);
13132176cfdSRui Paulo return ENOSPC;
13232176cfdSRui Paulo }
13332176cfdSRui Paulo }
13432176cfdSRui Paulo
13532176cfdSRui Paulo /*
13632176cfdSRui Paulo * Drain/reclaim all frames from an ageq.
13732176cfdSRui Paulo */
13832176cfdSRui Paulo void
ieee80211_ageq_drain(struct ieee80211_ageq * aq)13932176cfdSRui Paulo ieee80211_ageq_drain(struct ieee80211_ageq *aq)
14032176cfdSRui Paulo {
14132176cfdSRui Paulo ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
14232176cfdSRui Paulo }
14332176cfdSRui Paulo
14432176cfdSRui Paulo /*
14532176cfdSRui Paulo * Drain/reclaim frames associated with a specific node from an ageq.
14632176cfdSRui Paulo */
14732176cfdSRui Paulo void
ieee80211_ageq_drain_node(struct ieee80211_ageq * aq,struct ieee80211_node * ni)14832176cfdSRui Paulo ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
14932176cfdSRui Paulo struct ieee80211_node *ni)
15032176cfdSRui Paulo {
15132176cfdSRui Paulo ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
15232176cfdSRui Paulo }
15332176cfdSRui Paulo
15432176cfdSRui Paulo /*
15532176cfdSRui Paulo * Age frames on the age queue. Ages are stored as time
15632176cfdSRui Paulo * deltas (in seconds) relative to the head so we can check
15732176cfdSRui Paulo * and/or adjust only the head of the list. If a frame's age
15832176cfdSRui Paulo * exceeds the time quanta then remove it. The list of removed
159085ff963SMatthew Dillon * frames is returned to the caller joined by m_nextpkt.
16032176cfdSRui Paulo */
16132176cfdSRui Paulo struct mbuf *
ieee80211_ageq_age(struct ieee80211_ageq * aq,int quanta)16232176cfdSRui Paulo ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta)
16332176cfdSRui Paulo {
16432176cfdSRui Paulo struct mbuf *head, **phead;
16532176cfdSRui Paulo struct mbuf *m;
16632176cfdSRui Paulo
16732176cfdSRui Paulo phead = &head;
16832176cfdSRui Paulo if (aq->aq_len != 0) {
169085ff963SMatthew Dillon IEEE80211_AGEQ_LOCK(aq);
17032176cfdSRui Paulo while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) {
17132176cfdSRui Paulo if ((aq->aq_head = m->m_nextpkt) == NULL)
17232176cfdSRui Paulo aq->aq_tail = NULL;
17332176cfdSRui Paulo KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
17432176cfdSRui Paulo aq->aq_len--;
17532176cfdSRui Paulo /* add to private list for return */
17632176cfdSRui Paulo *phead = m;
17732176cfdSRui Paulo phead = &m->m_nextpkt;
17832176cfdSRui Paulo }
17932176cfdSRui Paulo if (m != NULL)
18032176cfdSRui Paulo M_AGE_SUB(m, quanta);
181085ff963SMatthew Dillon IEEE80211_AGEQ_UNLOCK(aq);
18232176cfdSRui Paulo }
18332176cfdSRui Paulo *phead = NULL;
18432176cfdSRui Paulo return head;
18532176cfdSRui Paulo }
18632176cfdSRui Paulo
18732176cfdSRui Paulo /*
18832176cfdSRui Paulo * Remove all frames matching the specified node identifier
18932176cfdSRui Paulo * (NULL matches all). Frames are returned as a list joined
19032176cfdSRui Paulo * by m_nextpkt.
19132176cfdSRui Paulo */
19232176cfdSRui Paulo struct mbuf *
ieee80211_ageq_remove(struct ieee80211_ageq * aq,struct ieee80211_node * match)19332176cfdSRui Paulo ieee80211_ageq_remove(struct ieee80211_ageq *aq,
19432176cfdSRui Paulo struct ieee80211_node *match)
19532176cfdSRui Paulo {
19632176cfdSRui Paulo struct mbuf *m, **prev, *ohead;
19732176cfdSRui Paulo struct mbuf *head, **phead;
19832176cfdSRui Paulo
199085ff963SMatthew Dillon IEEE80211_AGEQ_LOCK(aq);
20032176cfdSRui Paulo ohead = aq->aq_head;
20132176cfdSRui Paulo prev = &aq->aq_head;
20232176cfdSRui Paulo phead = &head;
20332176cfdSRui Paulo while ((m = *prev) != NULL) {
20432176cfdSRui Paulo if (match != NULL && m->m_pkthdr.rcvif != (void *) match) {
20532176cfdSRui Paulo prev = &m->m_nextpkt;
20632176cfdSRui Paulo continue;
20732176cfdSRui Paulo }
20832176cfdSRui Paulo /*
20932176cfdSRui Paulo * Adjust q length.
21032176cfdSRui Paulo */
21132176cfdSRui Paulo KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
21232176cfdSRui Paulo aq->aq_len--;
21332176cfdSRui Paulo /*
21432176cfdSRui Paulo * Remove from forward list; tail pointer is harder.
21532176cfdSRui Paulo */
21632176cfdSRui Paulo if (aq->aq_tail == m) {
21732176cfdSRui Paulo KASSERT(m->m_nextpkt == NULL, ("not last"));
21832176cfdSRui Paulo if (aq->aq_head == m) { /* list empty */
21932176cfdSRui Paulo KASSERT(aq->aq_len == 0,
22032176cfdSRui Paulo ("not empty, len %d", aq->aq_len));
22132176cfdSRui Paulo aq->aq_tail = NULL;
22232176cfdSRui Paulo } else { /* must be one before */
22332176cfdSRui Paulo aq->aq_tail = (struct mbuf *)((uintptr_t)prev -
22432176cfdSRui Paulo offsetof(struct mbuf, m_nextpkt));
22532176cfdSRui Paulo }
22632176cfdSRui Paulo }
22732176cfdSRui Paulo *prev = m->m_nextpkt;
22832176cfdSRui Paulo
22932176cfdSRui Paulo /* add to private list for return */
23032176cfdSRui Paulo *phead = m;
23132176cfdSRui Paulo phead = &m->m_nextpkt;
23232176cfdSRui Paulo }
23332176cfdSRui Paulo if (head == ohead && aq->aq_head != NULL) /* correct age */
23432176cfdSRui Paulo M_AGE_SET(aq->aq_head, M_AGE_GET(head));
235085ff963SMatthew Dillon IEEE80211_AGEQ_UNLOCK(aq);
23632176cfdSRui Paulo
23732176cfdSRui Paulo *phead = NULL;
23832176cfdSRui Paulo return head;
23932176cfdSRui Paulo }
240