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