2 * Copyright (c) 2009 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
30 * IEEE 802.11 age queue support.
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
39 #include <sys/socket.h>
42 #include <net/if_var.h>
43 #include <net/if_media.h>
44 #include <net/ethernet.h>
46 #include <netproto/802_11/ieee80211_var.h>
52 ieee80211_ageq_init(struct ieee80211_ageq
*aq
, int maxlen
, const char *name
)
54 memset(aq
, 0, sizeof(*aq
));
55 aq
->aq_maxlen
= maxlen
;
56 IEEE80211_AGEQ_INIT(aq
, name
); /* OS-dependent setup */
60 * Cleanup an ageq initialized with ieee80211_ageq_init. Note
61 * the queue is assumed empty; this can be done with ieee80211_ageq_drain.
64 ieee80211_ageq_cleanup(struct ieee80211_ageq
*aq
)
66 KASSERT(aq
->aq_len
== 0, ("%d frames on ageq", aq
->aq_len
));
67 IEEE80211_AGEQ_DESTROY(aq
); /* OS-dependent cleanup */
71 * Free an mbuf according to ageq rules: if marked as holding
72 * and 802.11 frame then also reclaim a node reference from
73 * the packet header; this handles packets q'd in the tx path.
76 ageq_mfree(struct mbuf
*m
)
78 if (m
->m_flags
& M_ENCAP
) {
79 struct ieee80211_node
*ni
= (void *) m
->m_pkthdr
.rcvif
;
80 ieee80211_free_node(ni
);
87 * Free a list of mbufs using ageq rules (see above).
90 ieee80211_ageq_mfree(struct mbuf
*m
)
94 for (; m
!= NULL
; m
= next
) {
101 * Append an mbuf to the ageq and mark it with the specified max age
102 * If the frame is not removed before the age (in seconds) expires
103 * then it is reclaimed (along with any node reference).
106 ieee80211_ageq_append(struct ieee80211_ageq
*aq
, struct mbuf
*m
, int age
)
108 IEEE80211_AGEQ_LOCK(aq
);
109 if (__predict_true(aq
->aq_len
< aq
->aq_maxlen
)) {
110 if (aq
->aq_tail
== NULL
) {
113 aq
->aq_tail
->m_nextpkt
= m
;
114 age
-= M_AGE_GET(aq
->aq_head
);
116 KASSERT(age
>= 0, ("age %d", age
));
121 IEEE80211_AGEQ_UNLOCK(aq
);
125 * No space, drop and cleanup references.
128 IEEE80211_AGEQ_UNLOCK(aq
);
136 * Drain/reclaim all frames from an ageq.
139 ieee80211_ageq_drain(struct ieee80211_ageq
*aq
)
141 ieee80211_ageq_mfree(ieee80211_ageq_remove(aq
, NULL
));
145 * Drain/reclaim frames associated with a specific node from an ageq.
148 ieee80211_ageq_drain_node(struct ieee80211_ageq
*aq
,
149 struct ieee80211_node
*ni
)
151 ieee80211_ageq_mfree(ieee80211_ageq_remove(aq
, ni
));
155 * Age frames on the age queue. Ages are stored as time
156 * deltas (in seconds) relative to the head so we can check
157 * and/or adjust only the head of the list. If a frame's age
158 * exceeds the time quanta then remove it. The list of removed
159 * frames is returned to the caller joined by m_nextpkt.
162 ieee80211_ageq_age(struct ieee80211_ageq
*aq
, int quanta
)
164 struct mbuf
*head
, **phead
;
168 if (aq
->aq_len
!= 0) {
169 IEEE80211_AGEQ_LOCK(aq
);
170 while ((m
= aq
->aq_head
) != NULL
&& M_AGE_GET(m
) < quanta
) {
171 if ((aq
->aq_head
= m
->m_nextpkt
) == NULL
)
173 KASSERT(aq
->aq_len
> 0, ("aq len %d", aq
->aq_len
));
175 /* add to private list for return */
177 phead
= &m
->m_nextpkt
;
180 M_AGE_SUB(m
, quanta
);
181 IEEE80211_AGEQ_UNLOCK(aq
);
188 * Remove all frames matching the specified node identifier
189 * (NULL matches all). Frames are returned as a list joined
193 ieee80211_ageq_remove(struct ieee80211_ageq
*aq
,
194 struct ieee80211_node
*match
)
196 struct mbuf
*m
, **prev
, *ohead
;
197 struct mbuf
*head
, **phead
;
199 IEEE80211_AGEQ_LOCK(aq
);
203 while ((m
= *prev
) != NULL
) {
204 if (match
!= NULL
&& m
->m_pkthdr
.rcvif
!= (void *) match
) {
205 prev
= &m
->m_nextpkt
;
211 KASSERT(aq
->aq_len
> 0, ("aq len %d", aq
->aq_len
));
214 * Remove from forward list; tail pointer is harder.
216 if (aq
->aq_tail
== m
) {
217 KASSERT(m
->m_nextpkt
== NULL
, ("not last"));
218 if (aq
->aq_head
== m
) { /* list empty */
219 KASSERT(aq
->aq_len
== 0,
220 ("not empty, len %d", aq
->aq_len
));
222 } else { /* must be one before */
223 aq
->aq_tail
= (struct mbuf
*)((uintptr_t)prev
-
224 offsetof(struct mbuf
, m_nextpkt
));
227 *prev
= m
->m_nextpkt
;
229 /* add to private list for return */
231 phead
= &m
->m_nextpkt
;
233 if (head
== ohead
&& aq
->aq_head
!= NULL
) /* correct age */
234 M_AGE_SET(aq
->aq_head
, M_AGE_GET(head
));
235 IEEE80211_AGEQ_UNLOCK(aq
);