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