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