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 * $FreeBSD: head/sys/net80211/ieee80211_ageq.c 195527 2009-07-10 02:19:57Z sam $ 26 * $DragonFly$ 27 */ 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_media.h> 42 #include <net/ethernet.h> 43 #include <net/route.h> 44 45 #include <netproto/802_11/ieee80211_var.h> 46 47 /* 48 * Initialize an ageq. 49 */ 50 void 51 ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name) 52 { 53 memset(aq, 0, sizeof(aq)); 54 aq->aq_maxlen = maxlen; 55 } 56 57 /* 58 * Cleanup an ageq initialized with ieee80211_ageq_init. Note 59 * the queue is assumed empty; this can be done with ieee80211_ageq_drain. 60 */ 61 void 62 ieee80211_ageq_cleanup(struct ieee80211_ageq *aq) 63 { 64 KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len)); 65 } 66 67 /* 68 * Free an mbuf according to ageq rules: if marked as holding 69 * and 802.11 frame then also reclaim a node reference from 70 * the packet header; this handles packets q'd in the tx path. 71 */ 72 static void 73 ageq_mfree(struct mbuf *m) 74 { 75 if (m->m_flags & M_ENCAP) { 76 struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif; 77 ieee80211_free_node(ni); 78 } 79 m->m_nextpkt = NULL; 80 m_freem(m); 81 } 82 83 /* 84 * Free a list of mbufs using ageq rules (see above). 85 */ 86 void 87 ieee80211_ageq_mfree(struct mbuf *m) 88 { 89 struct mbuf *next; 90 91 for (; m != NULL; m = next) { 92 next = m->m_nextpkt; 93 ageq_mfree(m); 94 } 95 } 96 97 /* 98 * Append an mbuf to the ageq and mark it with the specified max age 99 * If the frame is not removed before the age (in seconds) expires 100 * then it is reclaimed (along with any node reference). 101 */ 102 int 103 ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age) 104 { 105 if (__predict_true(aq->aq_len < aq->aq_maxlen)) { 106 if (aq->aq_tail == NULL) { 107 aq->aq_head = m; 108 } else { 109 aq->aq_tail->m_nextpkt = m; 110 age -= M_AGE_GET(aq->aq_head); 111 } 112 KASSERT(age >= 0, ("age %d", age)); 113 M_AGE_SET(m, age); 114 m->m_nextpkt = NULL; 115 aq->aq_tail = m; 116 aq->aq_len++; 117 return 0; 118 } else { 119 /* 120 * No space, drop and cleanup references. 121 */ 122 aq->aq_drops++; 123 /* XXX tail drop? */ 124 ageq_mfree(m); 125 return ENOSPC; 126 } 127 } 128 129 /* 130 * Drain/reclaim all frames from an ageq. 131 */ 132 void 133 ieee80211_ageq_drain(struct ieee80211_ageq *aq) 134 { 135 ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL)); 136 } 137 138 /* 139 * Drain/reclaim frames associated with a specific node from an ageq. 140 */ 141 void 142 ieee80211_ageq_drain_node(struct ieee80211_ageq *aq, 143 struct ieee80211_node *ni) 144 { 145 ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni)); 146 } 147 148 /* 149 * Age frames on the age queue. Ages are stored as time 150 * deltas (in seconds) relative to the head so we can check 151 * and/or adjust only the head of the list. If a frame's age 152 * exceeds the time quanta then remove it. The list of removed 153 * frames is is returned to the caller joined by m_nextpkt. 154 */ 155 struct mbuf * 156 ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta) 157 { 158 struct mbuf *head, **phead; 159 struct mbuf *m; 160 161 phead = &head; 162 if (aq->aq_len != 0) { 163 while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) { 164 if ((aq->aq_head = m->m_nextpkt) == NULL) 165 aq->aq_tail = NULL; 166 KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 167 aq->aq_len--; 168 /* add to private list for return */ 169 *phead = m; 170 phead = &m->m_nextpkt; 171 } 172 if (m != NULL) 173 M_AGE_SUB(m, quanta); 174 } 175 *phead = NULL; 176 return head; 177 } 178 179 /* 180 * Remove all frames matching the specified node identifier 181 * (NULL matches all). Frames are returned as a list joined 182 * by m_nextpkt. 183 */ 184 struct mbuf * 185 ieee80211_ageq_remove(struct ieee80211_ageq *aq, 186 struct ieee80211_node *match) 187 { 188 struct mbuf *m, **prev, *ohead; 189 struct mbuf *head, **phead; 190 191 ohead = aq->aq_head; 192 prev = &aq->aq_head; 193 phead = &head; 194 while ((m = *prev) != NULL) { 195 if (match != NULL && m->m_pkthdr.rcvif != (void *) match) { 196 prev = &m->m_nextpkt; 197 continue; 198 } 199 /* 200 * Adjust q length. 201 */ 202 KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 203 aq->aq_len--; 204 /* 205 * Remove from forward list; tail pointer is harder. 206 */ 207 if (aq->aq_tail == m) { 208 KASSERT(m->m_nextpkt == NULL, ("not last")); 209 if (aq->aq_head == m) { /* list empty */ 210 KASSERT(aq->aq_len == 0, 211 ("not empty, len %d", aq->aq_len)); 212 aq->aq_tail = NULL; 213 } else { /* must be one before */ 214 aq->aq_tail = (struct mbuf *)((uintptr_t)prev - 215 offsetof(struct mbuf, m_nextpkt)); 216 } 217 } 218 *prev = m->m_nextpkt; 219 220 /* add to private list for return */ 221 *phead = m; 222 phead = &m->m_nextpkt; 223 } 224 if (head == ohead && aq->aq_head != NULL) /* correct age */ 225 M_AGE_SET(aq->aq_head, M_AGE_GET(head)); 226 227 *phead = NULL; 228 return head; 229 } 230