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