xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_ageq.c (revision 32176cfd8803dac7f65c423373f231a378375c86)
1*32176cfdSRui Paulo /*-
2*32176cfdSRui Paulo  * Copyright (c) 2009 Sam Leffler, Errno Consulting
3*32176cfdSRui Paulo  * All rights reserved.
4*32176cfdSRui Paulo  *
5*32176cfdSRui Paulo  * Redistribution and use in source and binary forms, with or without
6*32176cfdSRui Paulo  * modification, are permitted provided that the following conditions
7*32176cfdSRui Paulo  * are met:
8*32176cfdSRui Paulo  * 1. Redistributions of source code must retain the above copyright
9*32176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer.
10*32176cfdSRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
11*32176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer in the
12*32176cfdSRui Paulo  *    documentation and/or other materials provided with the distribution.
13*32176cfdSRui Paulo  *
14*32176cfdSRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*32176cfdSRui Paulo  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16*32176cfdSRui Paulo  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17*32176cfdSRui Paulo  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18*32176cfdSRui Paulo  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19*32176cfdSRui Paulo  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20*32176cfdSRui Paulo  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21*32176cfdSRui Paulo  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22*32176cfdSRui Paulo  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23*32176cfdSRui Paulo  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24*32176cfdSRui Paulo  *
25*32176cfdSRui Paulo  * $FreeBSD: head/sys/net80211/ieee80211_ageq.c 195527 2009-07-10 02:19:57Z sam $
26*32176cfdSRui Paulo  * $DragonFly$
27*32176cfdSRui Paulo  */
28*32176cfdSRui Paulo 
29*32176cfdSRui Paulo /*
30*32176cfdSRui Paulo  * IEEE 802.11 age queue support.
31*32176cfdSRui Paulo  */
32*32176cfdSRui Paulo #include "opt_wlan.h"
33*32176cfdSRui Paulo 
34*32176cfdSRui Paulo #include <sys/param.h>
35*32176cfdSRui Paulo #include <sys/systm.h>
36*32176cfdSRui Paulo #include <sys/kernel.h>
37*32176cfdSRui Paulo 
38*32176cfdSRui Paulo #include <sys/socket.h>
39*32176cfdSRui Paulo 
40*32176cfdSRui Paulo #include <net/if.h>
41*32176cfdSRui Paulo #include <net/if_media.h>
42*32176cfdSRui Paulo #include <net/ethernet.h>
43*32176cfdSRui Paulo #include <net/route.h>
44*32176cfdSRui Paulo 
45*32176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
46*32176cfdSRui Paulo 
47*32176cfdSRui Paulo /*
48*32176cfdSRui Paulo  * Initialize an ageq.
49*32176cfdSRui Paulo  */
50*32176cfdSRui Paulo void
51*32176cfdSRui Paulo ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name)
52*32176cfdSRui Paulo {
53*32176cfdSRui Paulo 	memset(aq, 0, sizeof(aq));
54*32176cfdSRui Paulo 	aq->aq_maxlen = maxlen;
55*32176cfdSRui Paulo 	IEEE80211_AGEQ_INIT(aq, name);		/* OS-dependent setup */
56*32176cfdSRui Paulo }
57*32176cfdSRui Paulo 
58*32176cfdSRui Paulo /*
59*32176cfdSRui Paulo  * Cleanup an ageq initialized with ieee80211_ageq_init.  Note
60*32176cfdSRui Paulo  * the queue is assumed empty; this can be done with ieee80211_ageq_drain.
61*32176cfdSRui Paulo  */
62*32176cfdSRui Paulo void
63*32176cfdSRui Paulo ieee80211_ageq_cleanup(struct ieee80211_ageq *aq)
64*32176cfdSRui Paulo {
65*32176cfdSRui Paulo 	KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len));
66*32176cfdSRui Paulo 	IEEE80211_AGEQ_DESTROY(aq);		/* OS-dependent cleanup */
67*32176cfdSRui Paulo }
68*32176cfdSRui Paulo 
69*32176cfdSRui Paulo /*
70*32176cfdSRui Paulo  * Free an mbuf according to ageq rules: if marked as holding
71*32176cfdSRui Paulo  * and 802.11 frame then also reclaim a node reference from
72*32176cfdSRui Paulo  * the packet header; this handles packets q'd in the tx path.
73*32176cfdSRui Paulo  */
74*32176cfdSRui Paulo static void
75*32176cfdSRui Paulo ageq_mfree(struct mbuf *m)
76*32176cfdSRui Paulo {
77*32176cfdSRui Paulo 	if (m->m_flags & M_ENCAP) {
78*32176cfdSRui Paulo 		struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif;
79*32176cfdSRui Paulo 		ieee80211_free_node(ni);
80*32176cfdSRui Paulo 	}
81*32176cfdSRui Paulo 	m->m_nextpkt = NULL;
82*32176cfdSRui Paulo 	m_freem(m);
83*32176cfdSRui Paulo }
84*32176cfdSRui Paulo 
85*32176cfdSRui Paulo /*
86*32176cfdSRui Paulo  * Free a list of mbufs using ageq rules (see above).
87*32176cfdSRui Paulo  */
88*32176cfdSRui Paulo void
89*32176cfdSRui Paulo ieee80211_ageq_mfree(struct mbuf *m)
90*32176cfdSRui Paulo {
91*32176cfdSRui Paulo 	struct mbuf *next;
92*32176cfdSRui Paulo 
93*32176cfdSRui Paulo 	for (; m != NULL; m = next) {
94*32176cfdSRui Paulo 		next = m->m_nextpkt;
95*32176cfdSRui Paulo 		ageq_mfree(m);
96*32176cfdSRui Paulo 	}
97*32176cfdSRui Paulo }
98*32176cfdSRui Paulo 
99*32176cfdSRui Paulo /*
100*32176cfdSRui Paulo  * Append an mbuf to the ageq and mark it with the specified max age
101*32176cfdSRui Paulo  * If the frame is not removed before the age (in seconds) expires
102*32176cfdSRui Paulo  * then it is reclaimed (along with any node reference).
103*32176cfdSRui Paulo  */
104*32176cfdSRui Paulo int
105*32176cfdSRui Paulo ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age)
106*32176cfdSRui Paulo {
107*32176cfdSRui Paulo 	IEEE80211_AGEQ_LOCK(aq);
108*32176cfdSRui Paulo 	if (__predict_true(aq->aq_len < aq->aq_maxlen)) {
109*32176cfdSRui Paulo 		if (aq->aq_tail == NULL) {
110*32176cfdSRui Paulo 			aq->aq_head = m;
111*32176cfdSRui Paulo 		} else {
112*32176cfdSRui Paulo 			aq->aq_tail->m_nextpkt = m;
113*32176cfdSRui Paulo 			age -= M_AGE_GET(aq->aq_head);
114*32176cfdSRui Paulo 		}
115*32176cfdSRui Paulo 		KASSERT(age >= 0, ("age %d", age));
116*32176cfdSRui Paulo 		M_AGE_SET(m, age);
117*32176cfdSRui Paulo 		m->m_nextpkt = NULL;
118*32176cfdSRui Paulo 		aq->aq_tail = m;
119*32176cfdSRui Paulo 		aq->aq_len++;
120*32176cfdSRui Paulo 		IEEE80211_AGEQ_UNLOCK(aq);
121*32176cfdSRui Paulo 		return 0;
122*32176cfdSRui Paulo 	} else {
123*32176cfdSRui Paulo 		/*
124*32176cfdSRui Paulo 		 * No space, drop and cleanup references.
125*32176cfdSRui Paulo 		 */
126*32176cfdSRui Paulo 		aq->aq_drops++;
127*32176cfdSRui Paulo 		IEEE80211_AGEQ_UNLOCK(aq);
128*32176cfdSRui Paulo 		/* XXX tail drop? */
129*32176cfdSRui Paulo 		ageq_mfree(m);
130*32176cfdSRui Paulo 		return ENOSPC;
131*32176cfdSRui Paulo 	}
132*32176cfdSRui Paulo }
133*32176cfdSRui Paulo 
134*32176cfdSRui Paulo /*
135*32176cfdSRui Paulo  * Drain/reclaim all frames from an ageq.
136*32176cfdSRui Paulo  */
137*32176cfdSRui Paulo void
138*32176cfdSRui Paulo ieee80211_ageq_drain(struct ieee80211_ageq *aq)
139*32176cfdSRui Paulo {
140*32176cfdSRui Paulo 	ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL));
141*32176cfdSRui Paulo }
142*32176cfdSRui Paulo 
143*32176cfdSRui Paulo /*
144*32176cfdSRui Paulo  * Drain/reclaim frames associated with a specific node from an ageq.
145*32176cfdSRui Paulo  */
146*32176cfdSRui Paulo void
147*32176cfdSRui Paulo ieee80211_ageq_drain_node(struct ieee80211_ageq *aq,
148*32176cfdSRui Paulo 	struct ieee80211_node *ni)
149*32176cfdSRui Paulo {
150*32176cfdSRui Paulo 	ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni));
151*32176cfdSRui Paulo }
152*32176cfdSRui Paulo 
153*32176cfdSRui Paulo /*
154*32176cfdSRui Paulo  * Age frames on the age queue.  Ages are stored as time
155*32176cfdSRui Paulo  * deltas (in seconds) relative to the head so we can check
156*32176cfdSRui Paulo  * and/or adjust only the head of the list.  If a frame's age
157*32176cfdSRui Paulo  * exceeds the time quanta then remove it.  The list of removed
158*32176cfdSRui Paulo  * frames is is returned to the caller joined by m_nextpkt.
159*32176cfdSRui Paulo  */
160*32176cfdSRui Paulo struct mbuf *
161*32176cfdSRui Paulo ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta)
162*32176cfdSRui Paulo {
163*32176cfdSRui Paulo 	struct mbuf *head, **phead;
164*32176cfdSRui Paulo 	struct mbuf *m;
165*32176cfdSRui Paulo 
166*32176cfdSRui Paulo 	phead = &head;
167*32176cfdSRui Paulo 	if (aq->aq_len != 0) {
168*32176cfdSRui Paulo 		IEEE80211_AGEQ_LOCK(aq);
169*32176cfdSRui Paulo 		while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) {
170*32176cfdSRui Paulo 			if ((aq->aq_head = m->m_nextpkt) == NULL)
171*32176cfdSRui Paulo 				aq->aq_tail = NULL;
172*32176cfdSRui Paulo 			KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
173*32176cfdSRui Paulo 			aq->aq_len--;
174*32176cfdSRui Paulo 			/* add to private list for return */
175*32176cfdSRui Paulo 			*phead = m;
176*32176cfdSRui Paulo 			phead = &m->m_nextpkt;
177*32176cfdSRui Paulo 		}
178*32176cfdSRui Paulo 		if (m != NULL)
179*32176cfdSRui Paulo 			M_AGE_SUB(m, quanta);
180*32176cfdSRui Paulo 		IEEE80211_AGEQ_UNLOCK(aq);
181*32176cfdSRui Paulo 	}
182*32176cfdSRui Paulo 	*phead = NULL;
183*32176cfdSRui Paulo 	return head;
184*32176cfdSRui Paulo }
185*32176cfdSRui Paulo 
186*32176cfdSRui Paulo /*
187*32176cfdSRui Paulo  * Remove all frames matching the specified node identifier
188*32176cfdSRui Paulo  * (NULL matches all).  Frames are returned as a list joined
189*32176cfdSRui Paulo  * by m_nextpkt.
190*32176cfdSRui Paulo  */
191*32176cfdSRui Paulo struct mbuf *
192*32176cfdSRui Paulo ieee80211_ageq_remove(struct ieee80211_ageq *aq,
193*32176cfdSRui Paulo 	struct ieee80211_node *match)
194*32176cfdSRui Paulo {
195*32176cfdSRui Paulo 	struct mbuf *m, **prev, *ohead;
196*32176cfdSRui Paulo 	struct mbuf *head, **phead;
197*32176cfdSRui Paulo 
198*32176cfdSRui Paulo 	IEEE80211_AGEQ_LOCK(aq);
199*32176cfdSRui Paulo 	ohead = aq->aq_head;
200*32176cfdSRui Paulo 	prev = &aq->aq_head;
201*32176cfdSRui Paulo 	phead = &head;
202*32176cfdSRui Paulo 	while ((m = *prev) != NULL) {
203*32176cfdSRui Paulo 		if (match != NULL && m->m_pkthdr.rcvif != (void *) match) {
204*32176cfdSRui Paulo 			prev = &m->m_nextpkt;
205*32176cfdSRui Paulo 			continue;
206*32176cfdSRui Paulo 		}
207*32176cfdSRui Paulo 		/*
208*32176cfdSRui Paulo 		 * Adjust q length.
209*32176cfdSRui Paulo 		 */
210*32176cfdSRui Paulo 		KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len));
211*32176cfdSRui Paulo 		aq->aq_len--;
212*32176cfdSRui Paulo 		/*
213*32176cfdSRui Paulo 		 * Remove from forward list; tail pointer is harder.
214*32176cfdSRui Paulo 		 */
215*32176cfdSRui Paulo 		if (aq->aq_tail == m) {
216*32176cfdSRui Paulo 			KASSERT(m->m_nextpkt == NULL, ("not last"));
217*32176cfdSRui Paulo 			if (aq->aq_head == m) {		/* list empty */
218*32176cfdSRui Paulo 				KASSERT(aq->aq_len == 0,
219*32176cfdSRui Paulo 				    ("not empty, len %d", aq->aq_len));
220*32176cfdSRui Paulo 				aq->aq_tail = NULL;
221*32176cfdSRui Paulo 			} else {			/* must be one before */
222*32176cfdSRui Paulo 				aq->aq_tail = (struct mbuf *)((uintptr_t)prev -
223*32176cfdSRui Paulo 				    offsetof(struct mbuf, m_nextpkt));
224*32176cfdSRui Paulo 			}
225*32176cfdSRui Paulo 		}
226*32176cfdSRui Paulo 		*prev = m->m_nextpkt;
227*32176cfdSRui Paulo 
228*32176cfdSRui Paulo 		/* add to private list for return */
229*32176cfdSRui Paulo 		*phead = m;
230*32176cfdSRui Paulo 		phead = &m->m_nextpkt;
231*32176cfdSRui Paulo 	}
232*32176cfdSRui Paulo 	if (head == ohead && aq->aq_head != NULL)	/* correct age */
233*32176cfdSRui Paulo 		M_AGE_SET(aq->aq_head, M_AGE_GET(head));
234*32176cfdSRui Paulo 	IEEE80211_AGEQ_UNLOCK(aq);
235*32176cfdSRui Paulo 
236*32176cfdSRui Paulo 	*phead = NULL;
237*32176cfdSRui Paulo 	return head;
238*32176cfdSRui Paulo }
239