xref: /dragonfly/sys/netproto/802_11/wlan/ieee80211_ageq.c (revision 4f655ef568316df0b575f05cebf37546415c0d24)
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$");
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 #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 <netproto/802_11/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