xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_ageq.c (revision 4f655ef568316df0b575f05cebf37546415c0d24)
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