/* * Age frames on the age queue. Ages are stored as time * deltas (in seconds) relative to the head so we can check * and/or adjust only the head of the list. If a frame's age * exceeds the time quanta then remove it. The list of removed * frames is is returned to the caller joined by m_nextpkt. */ struct mbuf * ieee80211_ageq_age(struct ieee80211_ageq *aq, int quanta) { struct mbuf *head, **phead; struct mbuf *m; phead = &head; if (aq->aq_len != 0) { IEEE80211_AGEQ_LOCK(aq); while ((m = aq->aq_head) != NULL && M_AGE_GET(m) < quanta) { if ((aq->aq_head = m->m_nextpkt) == NULL) aq->aq_tail = NULL; KASSERT(aq->aq_len > 0, ("aq len %d", aq->aq_len)); aq->aq_len--; /* add to private list for return */ *phead = m; phead = &m->m_nextpkt; } if (m != NULL) M_AGE_SUB(m, quanta); IEEE80211_AGEQ_UNLOCK(aq); } *phead = NULL; return head; }
/* * Append an mbuf to the ageq and mark it with the specified max age * If the frame is not removed before the age (in seconds) expires * then it is reclaimed (along with any node reference). */ int ieee80211_ageq_append(struct ieee80211_ageq *aq, struct mbuf *m, int age) { IEEE80211_AGEQ_LOCK(aq); if (__predict_true(aq->aq_len < aq->aq_maxlen)) { if (aq->aq_tail == NULL) { aq->aq_head = m; } else { aq->aq_tail->m_nextpkt = m; age -= M_AGE_GET(aq->aq_head); } KASSERT(age >= 0, ("age %d", age)); M_AGE_SET(m, age); m->m_nextpkt = NULL; aq->aq_tail = m; aq->aq_len++; IEEE80211_AGEQ_UNLOCK(aq); return 0; } else { /* * No space, drop and cleanup references. */ aq->aq_drops++; IEEE80211_AGEQ_UNLOCK(aq); /* XXX tail drop? */ ageq_mfree(m); return ENOSPC; } }
/* * Age frames on the power save queue. The aging interval is * 4 times the listen interval specified by the station. This * number is factored into the age calculations when the frame * is placed on the queue. We store ages as time differences * so we can check and/or adjust only the head of the list. * If a frame's age exceeds the threshold then discard it. * The number of frames discarded is returned so the caller * can check if it needs to adjust the tim. */ int ieee80211_node_psq_age(struct ieee80211_node *ni) { struct ieee80211_psq *psq = &ni->ni_psq; int discard = 0; if (psq->psq_len != 0) { #ifdef IEEE80211_DEBUG struct ieee80211vap *vap = ni->ni_vap; #endif struct ieee80211_psq_head *qhead; struct mbuf *m; IEEE80211_PSQ_LOCK(psq); qhead = &psq->psq_head[0]; again: while ((m = qhead->head) != NULL && M_AGE_GET(m) < IEEE80211_INACT_WAIT) { IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard frame, age %u", M_AGE_GET(m)); if ((qhead->head = m->m_nextpkt) == NULL) qhead->tail = NULL; KASSERT(qhead->len > 0, ("qhead len %d", qhead->len)); qhead->len--; KASSERT(psq->psq_len > 0, ("psq len %d", psq->psq_len)); psq->psq_len--; psq_mfree(m); discard++; } if (qhead == &psq->psq_head[0]) { /* Algol-68 style for loop */ qhead = &psq->psq_head[1]; goto again; } if (m != NULL) M_AGE_SUB(m, IEEE80211_INACT_WAIT); IEEE80211_PSQ_UNLOCK(psq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard %u frames for age", discard); IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); } return discard; }
/* * Save an outbound packet for a node in power-save sleep state. * The new packet is placed on the node's saved queue, and the TIM * is changed, if necessary. */ int ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211_psq *psq = &ni->ni_psq; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_psq_head *qhead; int qlen, age; IEEE80211_PSQ_LOCK(psq); if (psq->psq_len >= psq->psq_maxlen) { psq->psq_drops++; IEEE80211_PSQ_UNLOCK(psq); IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "pwr save q overflow, drops %d (size %d)", psq->psq_drops, psq->psq_len); #ifdef IEEE80211_DEBUG if (ieee80211_msg_dumppkts(vap)) ieee80211_dump_pkt(ni->ni_ic, mtod(m, caddr_t), m->m_len, -1, -1); #endif psq_mfree(m); return ENOSPC; } /* * Tag the frame with it's expiry time and insert it in * the appropriate queue. The aging interval is 4 times * the listen interval specified by the station. Frames * that sit around too long are reclaimed using this * information. */ /* TU -> secs. XXX handle overflow? */ age = IEEE80211_TU_TO_MS((ni->ni_intval * ic->ic_bintval) << 2) / 1000; /* * Encapsulated frames go on the high priority queue, * other stuff goes on the low priority queue. We use * this to order frames returned out of the driver * ahead of frames we collect in ieee80211_start. */ if (m->m_flags & M_ENCAP) qhead = &psq->psq_head[0]; else qhead = &psq->psq_head[1]; if (qhead->tail == NULL) { struct mbuf *mh; qhead->head = m; /* * Take care to adjust age when inserting the first * frame of a queue and the other queue already has * frames. We need to preserve the age difference * relationship so ieee80211_node_psq_age works. */ if (qhead == &psq->psq_head[1]) { mh = psq->psq_head[0].head; if (mh != NULL) age-= M_AGE_GET(mh); } else { mh = psq->psq_head[1].head; if (mh != NULL) { int nage = M_AGE_GET(mh) - age; /* XXX is clamping to zero good 'nuf? */ M_AGE_SET(mh, nage < 0 ? 0 : nage); } } } else { qhead->tail->m_nextpkt = m; age -= M_AGE_GET(qhead->head); } KASSERT(age >= 0, ("age %d", age)); M_AGE_SET(m, age); m->m_nextpkt = NULL; qhead->tail = m; qhead->len++; qlen = ++(psq->psq_len); IEEE80211_PSQ_UNLOCK(psq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "save frame with age %d, %u now queued", age, qlen); if (qlen == 1 && vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 1); return 0; }