15b16c28cSSam Leffler /*- 25b16c28cSSam Leffler * Copyright (c) 2009 Sam Leffler, Errno Consulting 35b16c28cSSam Leffler * All rights reserved. 45b16c28cSSam Leffler * 55b16c28cSSam Leffler * Redistribution and use in source and binary forms, with or without 65b16c28cSSam Leffler * modification, are permitted provided that the following conditions 75b16c28cSSam Leffler * are met: 85b16c28cSSam Leffler * 1. Redistributions of source code must retain the above copyright 95b16c28cSSam Leffler * notice, this list of conditions and the following disclaimer. 105b16c28cSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 115b16c28cSSam Leffler * notice, this list of conditions and the following disclaimer in the 125b16c28cSSam Leffler * documentation and/or other materials provided with the distribution. 135b16c28cSSam Leffler * 145b16c28cSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 155b16c28cSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 165b16c28cSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 175b16c28cSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 185b16c28cSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 195b16c28cSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 205b16c28cSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 215b16c28cSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 225b16c28cSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 235b16c28cSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 245b16c28cSSam Leffler */ 255b16c28cSSam Leffler 265b16c28cSSam Leffler #include <sys/cdefs.h> 275b16c28cSSam Leffler __FBSDID("$FreeBSD$"); 285b16c28cSSam Leffler 295b16c28cSSam Leffler /* 305b16c28cSSam Leffler * IEEE 802.11 age queue support. 315b16c28cSSam Leffler */ 325b16c28cSSam Leffler #include "opt_wlan.h" 335b16c28cSSam Leffler 345b16c28cSSam Leffler #include <sys/param.h> 355b16c28cSSam Leffler #include <sys/systm.h> 365b16c28cSSam Leffler #include <sys/kernel.h> 375b16c28cSSam Leffler 385b16c28cSSam Leffler #include <sys/socket.h> 395b16c28cSSam Leffler 405b16c28cSSam Leffler #include <net/if.h> 41*76039bc8SGleb Smirnoff #include <net/if_var.h> 425b16c28cSSam Leffler #include <net/if_media.h> 435b16c28cSSam Leffler #include <net/ethernet.h> 445b16c28cSSam Leffler 455b16c28cSSam Leffler #include <net80211/ieee80211_var.h> 465b16c28cSSam Leffler 475b16c28cSSam Leffler /* 485b16c28cSSam Leffler * Initialize an ageq. 495b16c28cSSam Leffler */ 505b16c28cSSam Leffler void 515b16c28cSSam Leffler ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name) 525b16c28cSSam Leffler { 53d91933f4SKevin Lo memset(aq, 0, sizeof(*aq)); 545b16c28cSSam Leffler aq->aq_maxlen = maxlen; 555b16c28cSSam Leffler IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */ 565b16c28cSSam Leffler } 575b16c28cSSam Leffler 585b16c28cSSam Leffler /* 595b16c28cSSam Leffler * Cleanup an ageq initialized with ieee80211_ageq_init. Note 605b16c28cSSam Leffler * the queue is assumed empty; this can be done with ieee80211_ageq_drain. 615b16c28cSSam Leffler */ 625b16c28cSSam Leffler void 635b16c28cSSam Leffler ieee80211_ageq_cleanup(struct ieee80211_ageq *aq) 645b16c28cSSam Leffler { 655b16c28cSSam Leffler KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len)); 665b16c28cSSam Leffler IEEE80211_AGEQ_DESTROY(aq); /* OS-dependent cleanup */ 675b16c28cSSam Leffler } 685b16c28cSSam Leffler 695b16c28cSSam Leffler /* 705b16c28cSSam Leffler * Free an mbuf according to ageq rules: if marked as holding 715b16c28cSSam Leffler * and 802.11 frame then also reclaim a node reference from 725b16c28cSSam Leffler * the packet header; this handles packets q'd in the tx path. 735b16c28cSSam Leffler */ 745b16c28cSSam Leffler static void 755b16c28cSSam Leffler ageq_mfree(struct mbuf *m) 765b16c28cSSam Leffler { 775b16c28cSSam Leffler if (m->m_flags & M_ENCAP) { 785b16c28cSSam Leffler struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif; 795b16c28cSSam Leffler ieee80211_free_node(ni); 805b16c28cSSam Leffler } 815b16c28cSSam Leffler m->m_nextpkt = NULL; 825b16c28cSSam Leffler m_freem(m); 835b16c28cSSam Leffler } 845b16c28cSSam Leffler 855b16c28cSSam Leffler /* 865b16c28cSSam Leffler * Free a list of mbufs using ageq rules (see above). 875b16c28cSSam Leffler */ 885b16c28cSSam Leffler void 895b16c28cSSam Leffler ieee80211_ageq_mfree(struct mbuf *m) 905b16c28cSSam Leffler { 915b16c28cSSam Leffler struct mbuf *next; 925b16c28cSSam Leffler 935b16c28cSSam Leffler for (; m != NULL; m = next) { 945b16c28cSSam Leffler next = m->m_nextpkt; 955b16c28cSSam Leffler ageq_mfree(m); 965b16c28cSSam Leffler } 975b16c28cSSam Leffler } 985b16c28cSSam Leffler 995b16c28cSSam Leffler /* 1005b16c28cSSam Leffler * Append an mbuf to the ageq and mark it with the specified max age 1015b16c28cSSam Leffler * If the frame is not removed before the age (in seconds) expires 1025b16c28cSSam Leffler * then it is reclaimed (along with any node reference). 1035b16c28cSSam Leffler */ 1045b16c28cSSam Leffler int 1055b16c28cSSam Leffler ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age) 1065b16c28cSSam Leffler { 1075b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1085b16c28cSSam Leffler if (__predict_true(aq->aq_len < aq->aq_maxlen)) { 1095b16c28cSSam Leffler if (aq->aq_tail == NULL) { 1105b16c28cSSam Leffler aq->aq_head = m; 1115b16c28cSSam Leffler } else { 1125b16c28cSSam Leffler aq->aq_tail->m_nextpkt = m; 1135b16c28cSSam Leffler age -= M_AGE_GET(aq->aq_head); 1145b16c28cSSam Leffler } 1155b16c28cSSam Leffler KASSERT(age >= 0, ("age %d", age)); 1165b16c28cSSam Leffler M_AGE_SET(m, age); 1175b16c28cSSam Leffler m->m_nextpkt = NULL; 1185b16c28cSSam Leffler aq->aq_tail = m; 1195b16c28cSSam Leffler aq->aq_len++; 1205b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1215b16c28cSSam Leffler return 0; 1225b16c28cSSam Leffler } else { 1235b16c28cSSam Leffler /* 1245b16c28cSSam Leffler * No space, drop and cleanup references. 1255b16c28cSSam Leffler */ 1265b16c28cSSam Leffler aq->aq_drops++; 1275b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1285b16c28cSSam Leffler /* XXX tail drop? */ 1295b16c28cSSam Leffler ageq_mfree(m); 1305b16c28cSSam Leffler return ENOSPC; 1315b16c28cSSam Leffler } 1325b16c28cSSam Leffler } 1335b16c28cSSam Leffler 1345b16c28cSSam Leffler /* 1355b16c28cSSam Leffler * Drain/reclaim all frames from an ageq. 1365b16c28cSSam Leffler */ 1375b16c28cSSam Leffler void 1385b16c28cSSam Leffler ieee80211_ageq_drain(struct ieee80211_ageq *aq) 1395b16c28cSSam Leffler { 1405b16c28cSSam Leffler ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL)); 1415b16c28cSSam Leffler } 1425b16c28cSSam Leffler 1435b16c28cSSam Leffler /* 1445b16c28cSSam Leffler * Drain/reclaim frames associated with a specific node from an ageq. 1455b16c28cSSam Leffler */ 1465b16c28cSSam Leffler void 1475b16c28cSSam Leffler ieee80211_ageq_drain_node(struct ieee80211_ageq *aq, 1485b16c28cSSam Leffler struct ieee80211_node *ni) 1495b16c28cSSam Leffler { 1505b16c28cSSam Leffler ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni)); 1515b16c28cSSam Leffler } 1525b16c28cSSam Leffler 1535b16c28cSSam Leffler /* 1545b16c28cSSam Leffler * Age frames on the age queue. Ages are stored as time 1555b16c28cSSam Leffler * deltas (in seconds) relative to the head so we can check 1565b16c28cSSam Leffler * and/or adjust only the head of the list. If a frame's age 1575b16c28cSSam Leffler * exceeds the time quanta then remove it. The list of removed 158974206cfSRebecca Cran * frames is returned to the caller joined by m_nextpkt. 1595b16c28cSSam Leffler */ 1605b16c28cSSam Leffler struct mbuf * 1615b16c28cSSam Leffler ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta) 1625b16c28cSSam Leffler { 1635b16c28cSSam Leffler struct mbuf *head, **phead; 1645b16c28cSSam Leffler struct mbuf *m; 1655b16c28cSSam Leffler 1665b16c28cSSam Leffler phead = &head; 1675b16c28cSSam Leffler if (aq->aq_len != 0) { 1685b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1695b16c28cSSam Leffler while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) { 1705b16c28cSSam Leffler if ((aq->aq_head = m->m_nextpkt) == NULL) 1715b16c28cSSam Leffler aq->aq_tail = NULL; 1725b16c28cSSam Leffler KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 1735b16c28cSSam Leffler aq->aq_len--; 1745b16c28cSSam Leffler /* add to private list for return */ 1755b16c28cSSam Leffler *phead = m; 1765b16c28cSSam Leffler phead = &m->m_nextpkt; 1775b16c28cSSam Leffler } 1785b16c28cSSam Leffler if (m != NULL) 1795b16c28cSSam Leffler M_AGE_SUB(m, quanta); 1805b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1815b16c28cSSam Leffler } 1825b16c28cSSam Leffler *phead = NULL; 1835b16c28cSSam Leffler return head; 1845b16c28cSSam Leffler } 1855b16c28cSSam Leffler 1865b16c28cSSam Leffler /* 1875b16c28cSSam Leffler * Remove all frames matching the specified node identifier 1885b16c28cSSam Leffler * (NULL matches all). Frames are returned as a list joined 1895b16c28cSSam Leffler * by m_nextpkt. 1905b16c28cSSam Leffler */ 1915b16c28cSSam Leffler struct mbuf * 1925b16c28cSSam Leffler ieee80211_ageq_remove(struct ieee80211_ageq *aq, 1935b16c28cSSam Leffler struct ieee80211_node *match) 1945b16c28cSSam Leffler { 1955b16c28cSSam Leffler struct mbuf *m, **prev, *ohead; 1965b16c28cSSam Leffler struct mbuf *head, **phead; 1975b16c28cSSam Leffler 1985b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1995b16c28cSSam Leffler ohead = aq->aq_head; 2005b16c28cSSam Leffler prev = &aq->aq_head; 2015b16c28cSSam Leffler phead = &head; 2025b16c28cSSam Leffler while ((m = *prev) != NULL) { 2035b16c28cSSam Leffler if (match != NULL && m->m_pkthdr.rcvif != (void *) match) { 2045b16c28cSSam Leffler prev = &m->m_nextpkt; 2055b16c28cSSam Leffler continue; 2065b16c28cSSam Leffler } 2075b16c28cSSam Leffler /* 2085b16c28cSSam Leffler * Adjust q length. 2095b16c28cSSam Leffler */ 2105b16c28cSSam Leffler KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 2115b16c28cSSam Leffler aq->aq_len--; 2125b16c28cSSam Leffler /* 2135b16c28cSSam Leffler * Remove from forward list; tail pointer is harder. 2145b16c28cSSam Leffler */ 2155b16c28cSSam Leffler if (aq->aq_tail == m) { 2165b16c28cSSam Leffler KASSERT(m->m_nextpkt == NULL, ("not last")); 2175b16c28cSSam Leffler if (aq->aq_head == m) { /* list empty */ 2185b16c28cSSam Leffler KASSERT(aq->aq_len == 0, 2195b16c28cSSam Leffler ("not empty, len %d", aq->aq_len)); 2205b16c28cSSam Leffler aq->aq_tail = NULL; 2215b16c28cSSam Leffler } else { /* must be one before */ 2225b16c28cSSam Leffler aq->aq_tail = (struct mbuf *)((uintptr_t)prev - 2235b16c28cSSam Leffler offsetof(struct mbuf, m_nextpkt)); 2245b16c28cSSam Leffler } 2255b16c28cSSam Leffler } 226f6c09dd6SSam Leffler *prev = m->m_nextpkt; 227f6c09dd6SSam Leffler 2285b16c28cSSam Leffler /* add to private list for return */ 2295b16c28cSSam Leffler *phead = m; 2305b16c28cSSam Leffler phead = &m->m_nextpkt; 2315b16c28cSSam Leffler } 2325b16c28cSSam Leffler if (head == ohead && aq->aq_head != NULL) /* correct age */ 2335b16c28cSSam Leffler M_AGE_SET(aq->aq_head, M_AGE_GET(head)); 2345b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 2355b16c28cSSam Leffler 2365b16c28cSSam Leffler *phead = NULL; 2375b16c28cSSam Leffler return head; 2385b16c28cSSam Leffler } 239