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> 415b16c28cSSam Leffler #include <net/if_media.h> 425b16c28cSSam Leffler #include <net/ethernet.h> 435b16c28cSSam Leffler 445b16c28cSSam Leffler #include <net80211/ieee80211_var.h> 455b16c28cSSam Leffler 465b16c28cSSam Leffler /* 475b16c28cSSam Leffler * Initialize an ageq. 485b16c28cSSam Leffler */ 495b16c28cSSam Leffler void 505b16c28cSSam Leffler ieee80211_ageq_init(struct ieee80211_ageq *aq, int maxlen, const char *name) 515b16c28cSSam Leffler { 525b16c28cSSam Leffler memset(aq, 0, sizeof(aq)); 535b16c28cSSam Leffler aq->aq_maxlen = maxlen; 545b16c28cSSam Leffler IEEE80211_AGEQ_INIT(aq, name); /* OS-dependent setup */ 555b16c28cSSam Leffler } 565b16c28cSSam Leffler 575b16c28cSSam Leffler /* 585b16c28cSSam Leffler * Cleanup an ageq initialized with ieee80211_ageq_init. Note 595b16c28cSSam Leffler * the queue is assumed empty; this can be done with ieee80211_ageq_drain. 605b16c28cSSam Leffler */ 615b16c28cSSam Leffler void 625b16c28cSSam Leffler ieee80211_ageq_cleanup(struct ieee80211_ageq *aq) 635b16c28cSSam Leffler { 645b16c28cSSam Leffler KASSERT(aq->aq_len == 0, ("%d frames on ageq", aq->aq_len)); 655b16c28cSSam Leffler IEEE80211_AGEQ_DESTROY(aq); /* OS-dependent cleanup */ 665b16c28cSSam Leffler } 675b16c28cSSam Leffler 685b16c28cSSam Leffler /* 695b16c28cSSam Leffler * Free an mbuf according to ageq rules: if marked as holding 705b16c28cSSam Leffler * and 802.11 frame then also reclaim a node reference from 715b16c28cSSam Leffler * the packet header; this handles packets q'd in the tx path. 725b16c28cSSam Leffler */ 735b16c28cSSam Leffler static void 745b16c28cSSam Leffler ageq_mfree(struct mbuf *m) 755b16c28cSSam Leffler { 765b16c28cSSam Leffler if (m->m_flags & M_ENCAP) { 775b16c28cSSam Leffler struct ieee80211_node *ni = (void *) m->m_pkthdr.rcvif; 785b16c28cSSam Leffler ieee80211_free_node(ni); 795b16c28cSSam Leffler } 805b16c28cSSam Leffler m->m_nextpkt = NULL; 815b16c28cSSam Leffler m_freem(m); 825b16c28cSSam Leffler } 835b16c28cSSam Leffler 845b16c28cSSam Leffler /* 855b16c28cSSam Leffler * Free a list of mbufs using ageq rules (see above). 865b16c28cSSam Leffler */ 875b16c28cSSam Leffler void 885b16c28cSSam Leffler ieee80211_ageq_mfree(struct mbuf *m) 895b16c28cSSam Leffler { 905b16c28cSSam Leffler struct mbuf *next; 915b16c28cSSam Leffler 925b16c28cSSam Leffler for (; m != NULL; m = next) { 935b16c28cSSam Leffler next = m->m_nextpkt; 945b16c28cSSam Leffler ageq_mfree(m); 955b16c28cSSam Leffler } 965b16c28cSSam Leffler } 975b16c28cSSam Leffler 985b16c28cSSam Leffler /* 995b16c28cSSam Leffler * Append an mbuf to the ageq and mark it with the specified max age 1005b16c28cSSam Leffler * If the frame is not removed before the age (in seconds) expires 1015b16c28cSSam Leffler * then it is reclaimed (along with any node reference). 1025b16c28cSSam Leffler */ 1035b16c28cSSam Leffler int 1045b16c28cSSam Leffler ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age) 1055b16c28cSSam Leffler { 1065b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1075b16c28cSSam Leffler if (__predict_true(aq->aq_len < aq->aq_maxlen)) { 1085b16c28cSSam Leffler if (aq->aq_tail == NULL) { 1095b16c28cSSam Leffler aq->aq_head = m; 1105b16c28cSSam Leffler } else { 1115b16c28cSSam Leffler aq->aq_tail->m_nextpkt = m; 1125b16c28cSSam Leffler age -= M_AGE_GET(aq->aq_head); 1135b16c28cSSam Leffler } 1145b16c28cSSam Leffler KASSERT(age >= 0, ("age %d", age)); 1155b16c28cSSam Leffler M_AGE_SET(m, age); 1165b16c28cSSam Leffler m->m_nextpkt = NULL; 1175b16c28cSSam Leffler aq->aq_tail = m; 1185b16c28cSSam Leffler aq->aq_len++; 1195b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1205b16c28cSSam Leffler return 0; 1215b16c28cSSam Leffler } else { 1225b16c28cSSam Leffler /* 1235b16c28cSSam Leffler * No space, drop and cleanup references. 1245b16c28cSSam Leffler */ 1255b16c28cSSam Leffler aq->aq_drops++; 1265b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1275b16c28cSSam Leffler /* XXX tail drop? */ 1285b16c28cSSam Leffler ageq_mfree(m); 1295b16c28cSSam Leffler return ENOSPC; 1305b16c28cSSam Leffler } 1315b16c28cSSam Leffler } 1325b16c28cSSam Leffler 1335b16c28cSSam Leffler /* 1345b16c28cSSam Leffler * Drain/reclaim all frames from an ageq. 1355b16c28cSSam Leffler */ 1365b16c28cSSam Leffler void 1375b16c28cSSam Leffler ieee80211_ageq_drain(struct ieee80211_ageq *aq) 1385b16c28cSSam Leffler { 1395b16c28cSSam Leffler ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, NULL)); 1405b16c28cSSam Leffler } 1415b16c28cSSam Leffler 1425b16c28cSSam Leffler /* 1435b16c28cSSam Leffler * Drain/reclaim frames associated with a specific node from an ageq. 1445b16c28cSSam Leffler */ 1455b16c28cSSam Leffler void 1465b16c28cSSam Leffler ieee80211_ageq_drain_node(struct ieee80211_ageq *aq, 1475b16c28cSSam Leffler struct ieee80211_node *ni) 1485b16c28cSSam Leffler { 1495b16c28cSSam Leffler ieee80211_ageq_mfree(ieee80211_ageq_remove(aq, ni)); 1505b16c28cSSam Leffler } 1515b16c28cSSam Leffler 1525b16c28cSSam Leffler /* 1535b16c28cSSam Leffler * Age frames on the age queue. Ages are stored as time 1545b16c28cSSam Leffler * deltas (in seconds) relative to the head so we can check 1555b16c28cSSam Leffler * and/or adjust only the head of the list. If a frame's age 1565b16c28cSSam Leffler * exceeds the time quanta then remove it. The list of removed 1575b16c28cSSam Leffler * frames is is returned to the caller joined by m_nextpkt. 1585b16c28cSSam Leffler */ 1595b16c28cSSam Leffler struct mbuf * 1605b16c28cSSam Leffler ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta) 1615b16c28cSSam Leffler { 1625b16c28cSSam Leffler struct mbuf *head, **phead; 1635b16c28cSSam Leffler struct mbuf *m; 1645b16c28cSSam Leffler 1655b16c28cSSam Leffler phead = &head; 1665b16c28cSSam Leffler if (aq->aq_len != 0) { 1675b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1685b16c28cSSam Leffler while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) { 1695b16c28cSSam Leffler if ((aq->aq_head = m->m_nextpkt) == NULL) 1705b16c28cSSam Leffler aq->aq_tail = NULL; 1715b16c28cSSam Leffler KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 1725b16c28cSSam Leffler aq->aq_len--; 1735b16c28cSSam Leffler /* add to private list for return */ 1745b16c28cSSam Leffler *phead = m; 1755b16c28cSSam Leffler phead = &m->m_nextpkt; 1765b16c28cSSam Leffler } 1775b16c28cSSam Leffler if (m != NULL) 1785b16c28cSSam Leffler M_AGE_SUB(m, quanta); 1795b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 1805b16c28cSSam Leffler } 1815b16c28cSSam Leffler *phead = NULL; 1825b16c28cSSam Leffler return head; 1835b16c28cSSam Leffler } 1845b16c28cSSam Leffler 1855b16c28cSSam Leffler /* 1865b16c28cSSam Leffler * Remove all frames matching the specified node identifier 1875b16c28cSSam Leffler * (NULL matches all). Frames are returned as a list joined 1885b16c28cSSam Leffler * by m_nextpkt. 1895b16c28cSSam Leffler */ 1905b16c28cSSam Leffler struct mbuf * 1915b16c28cSSam Leffler ieee80211_ageq_remove(struct ieee80211_ageq *aq, 1925b16c28cSSam Leffler struct ieee80211_node *match) 1935b16c28cSSam Leffler { 1945b16c28cSSam Leffler struct mbuf *m, **prev, *ohead; 1955b16c28cSSam Leffler struct mbuf *head, **phead; 1965b16c28cSSam Leffler 1975b16c28cSSam Leffler IEEE80211_AGEQ_LOCK(aq); 1985b16c28cSSam Leffler ohead = aq->aq_head; 1995b16c28cSSam Leffler prev = &aq->aq_head; 2005b16c28cSSam Leffler phead = &head; 2015b16c28cSSam Leffler while ((m = *prev) != NULL) { 2025b16c28cSSam Leffler if (match != NULL && m->m_pkthdr.rcvif != (void *) match) { 2035b16c28cSSam Leffler prev = &m->m_nextpkt; 2045b16c28cSSam Leffler continue; 2055b16c28cSSam Leffler } 2065b16c28cSSam Leffler /* 2075b16c28cSSam Leffler * Adjust q length. 2085b16c28cSSam Leffler */ 2095b16c28cSSam Leffler KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); 2105b16c28cSSam Leffler aq->aq_len--; 2115b16c28cSSam Leffler /* 2125b16c28cSSam Leffler * Remove from forward list; tail pointer is harder. 2135b16c28cSSam Leffler */ 2145b16c28cSSam Leffler if (aq->aq_tail == m) { 2155b16c28cSSam Leffler KASSERT(m->m_nextpkt == NULL, ("not last")); 2165b16c28cSSam Leffler if (aq->aq_head == m) { /* list empty */ 2175b16c28cSSam Leffler KASSERT(aq->aq_len == 0, 2185b16c28cSSam Leffler ("not empty, len %d", aq->aq_len)); 2195b16c28cSSam Leffler aq->aq_tail = NULL; 2205b16c28cSSam Leffler } else { /* must be one before */ 2215b16c28cSSam Leffler aq->aq_tail = (struct mbuf *)((uintptr_t)prev - 2225b16c28cSSam Leffler offsetof(struct mbuf, m_nextpkt)); 2235b16c28cSSam Leffler } 2245b16c28cSSam Leffler } 225f6c09dd6SSam Leffler *prev = m->m_nextpkt; 226f6c09dd6SSam Leffler 2275b16c28cSSam Leffler /* add to private list for return */ 2285b16c28cSSam Leffler *phead = m; 2295b16c28cSSam Leffler phead = &m->m_nextpkt; 2305b16c28cSSam Leffler } 2315b16c28cSSam Leffler if (head == ohead && aq->aq_head != NULL) /* correct age */ 2325b16c28cSSam Leffler M_AGE_SET(aq->aq_head, M_AGE_GET(head)); 2335b16c28cSSam Leffler IEEE80211_AGEQ_UNLOCK(aq); 2345b16c28cSSam Leffler 2355b16c28cSSam Leffler *phead = NULL; 2365b16c28cSSam Leffler return head; 2375b16c28cSSam Leffler } 238