/*
 * 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;
	}
}
Exemplo n.º 3
0
/*
 * 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;
}
Exemplo n.º 4
0
/*
 * 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;
}