/* * 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_saveq_age(struct ieee80211_node *ni) { int discard = 0; struct node_powersave_queue *dataq,*mgtq; struct ieee80211vap *vap=ni->ni_vap; dataq = IEEE80211_NODE_SAVEQ_DATAQ(ni); mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); /* XXX racey but good 'nuf? */ if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) != 0) || (IEEE80211_NODE_SAVEQ_QLEN(mgtq) != 0)) { wbuf_t wbuf; IEEE80211_NODE_SAVEQ_LOCK(dataq); while (IEEE80211_NODE_SAVEQ_POLL(dataq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(dataq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(dataq); IEEE80211_NODE_SAVEQ_LOCK(mgtq); while (IEEE80211_NODE_SAVEQ_POLL(mgtq, wbuf) != NULL && wbuf_get_age(wbuf) < IEEE80211_INACT_WAIT) { struct ieee80211_tx_status ts; ts.ts_flags = IEEE80211_TX_ERROR; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard mgt frame, age %u \n", wbuf_get_age(wbuf)); IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, wbuf); ieee80211_release_wbuf(ni,wbuf, &ts); discard++; } if (wbuf != NULL) wbuf_set_age(wbuf, wbuf_get_age(wbuf) - IEEE80211_INACT_WAIT); IEEE80211_NODE_SAVEQ_UNLOCK(mgtq); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "discard %u frames for age \n", discard); IEEE80211_NODE_STAT_ADD(ni, ps_discard, discard); } if ((IEEE80211_NODE_SAVEQ_QLEN(dataq) == 0) && (IEEE80211_NODE_SAVEQ_QLEN(mgtq) == 0)) { if (vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 0); } return discard; }
static int amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, struct ieee80211_node *ni) { int rix = amn->amn_rix; KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); if (is_success(amn)) { amn->amn_success++; if (amn->amn_success >= amn->amn_success_threshold && rix + 1 < ni->ni_rates.rs_nrates) { amn->amn_recovery = 1; amn->amn_success = 0; rix++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR increasing rate %d (txcnt=%d retrycnt=%d)", ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } else { amn->amn_recovery = 0; } } else if (is_failure(amn)) { amn->amn_success = 0; if (rix > 0) { if (amn->amn_recovery) { amn->amn_success_threshold *= 2; if (amn->amn_success_threshold > amrr->amrr_max_success_threshold) amn->amn_success_threshold = amrr->amrr_max_success_threshold; } else { amn->amn_success_threshold = amrr->amrr_min_success_threshold; } rix--; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)", ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL, amn->amn_txcnt, amn->amn_retrycnt); } amn->amn_recovery = 0; } /* reset counters */ amn->amn_txcnt = 0; amn->amn_retrycnt = 0; return rix; }
static void hwmp_peerdown(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_mesh_state *ms = vap->iv_mesh; struct ieee80211_meshperr_ie perr; struct ieee80211_mesh_route *rt; struct ieee80211_hwmp_route *hr; rt = ieee80211_mesh_rt_find(vap, ni->ni_macaddr); if (rt == NULL) return; hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, "%s", "delete route entry"); perr.perr_ttl = ms->ms_ttl; perr.perr_ndests = 1; PERR_DFLAGS(0) = 0; if (hr->hr_seq == 0) PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_USN; PERR_DFLAGS(0) |= IEEE80211_MESHPERR_DFLAGS_RC; IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); PERR_DSEQ(0) = hr->hr_seq; PERR_DRCODE(0) = IEEE80211_REASON_MESH_PERR_DEST_UNREACH; /* NB: flush everything passing through peer */ ieee80211_mesh_rt_flush_peer(vap, ni->ni_macaddr); hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &perr); }
static void amrr_node_init(struct ieee80211_node *ni) { const struct ieee80211_rateset *rs = &ni->ni_rates; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_amrr *amrr = vap->iv_rs; struct ieee80211_amrr_node *amn; if (ni->ni_rctls == NULL) { ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node), M_80211_RATECTL, M_NOWAIT|M_ZERO); if (amn == NULL) { if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " "structure\n"); return; } } else amn = ni->ni_rctls; amn->amn_amrr = amrr; amn->amn_success = 0; amn->amn_recovery = 0; amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; /* pick initial rate */ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72; amn->amn_rix--) ; ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; amn->amn_ticks = ticks; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR initial rate %d", ni->ni_txrate); }
/* * Handle power-save state change in station mode. */ void ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable) { struct ieee80211_node *ni = vap->iv_bss; if (!((enable != 0) ^ ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) != 0))) return; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "sta power save mode %s", enable ? "on" : "off"); if (!enable) { ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; ieee80211_send_nulldata(ieee80211_ref_node(ni)); /* * Flush any queued frames; we can do this immediately * because we know they'll be queued behind the null * data frame we send the ap. * XXX can we use a data frame to take us out of ps? */ if (ni->ni_psq.psq_len != 0) pwrsave_flushq(ni); } else { ni->ni_flags |= IEEE80211_NODE_PWR_MGT; ieee80211_send_nulldata(ieee80211_ref_node(ni)); } }
/* * Validate and strip privacy headers (and trailer) for a * received frame that has the Protected Frame bit set. */ struct ieee80211_key * ieee80211_crypto_decap(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen) { #define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) #define IEEE80211_WEP_MINLEN \ (sizeof(struct ieee80211_frame) + \ IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k; struct ieee80211_frame *wh; const struct ieee80211_cipher *cip; const u_int8_t *ivp; u_int8_t keyid; /* NB: this minimum size data frame could be bigger */ if (skb->len < IEEE80211_WEP_MINLEN) { IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni, "%s: WEP data frame too short, len %u", __func__, skb->len); vap->iv_stats.is_rx_tooshort++; /* XXX need unique stat? */ return NULL; } /* * Locate the key. If unicast and there is no unicast * key then we fall back to the key id in the header. * This assumes unicast keys are only configured when * the key id in the header is meaningless (typically 0). */ wh = (struct ieee80211_frame *) skb->data; ivp = skb->data + hdrlen; keyid = ivp[IEEE80211_WEP_IVLEN]; if (IEEE80211_IS_MULTICAST(wh->i_addr1) || ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) k = &vap->iv_nw_keys[keyid >> 6]; else
static void amrr_node_init(struct ieee80211_node *ni) { const struct ieee80211_rateset *rs = NULL; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_amrr *amrr = vap->iv_rs; struct ieee80211_amrr_node *amn; uint8_t rate; if (ni->ni_rctls == NULL) { ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node), M_80211_RATECTL, M_NOWAIT|M_ZERO); if (amn == NULL) { if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl " "structure\n"); return; } } else amn = ni->ni_rctls; amn->amn_amrr = amrr; amn->amn_success = 0; amn->amn_recovery = 0; amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; rs = ieee80211_ratectl_get_rateset(ni); /* Initial rate - lowest */ rate = rs->rs_rates[0]; /* XXX clear the basic rate flag if it's not 11n */ if (! ieee80211_ratectl_node_is11n(ni)) rate &= IEEE80211_RATE_VAL; /* pick initial rate from the rateset - HT or otherwise */ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; amn->amn_rix--) { /* legacy - anything < 36mbit, stop searching */ /* 11n - stop at MCS4 / MCS12 / MCS28 */ if (ieee80211_ratectl_node_is11n(ni) && (rs->rs_rates[amn->amn_rix] & 0x7) < 4) break; else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) break; rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; } /* if the rate is an 11n rate, ensure the MCS bit is set */ if (ieee80211_ratectl_node_is11n(ni)) rate |= IEEE80211_RATE_MCS; /* Assign initial rate from the rateset */ ni->ni_txrate = rate; amn->amn_ticks = ticks; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "%s: AMRR: nrates=%d, initial rate %d", __func__, rs->rs_nrates, rate); }
/* * Send a Root Annoucement (RANN) to find all the nodes on the mesh. We are * called when the vap is configured as a HWMP RANN root node. */ static void hwmp_rootmode_rann_callout(void *arg) { struct ieee80211vap *vap = (struct ieee80211vap *)arg; struct ieee80211_hwmp_state *hs; struct ieee80211_mesh_state *ms; struct ieee80211_meshrann_ie rann; wlan_serialize_enter(); hs = vap->iv_hwmp; ms = vap->iv_mesh; IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, "%s", "send broadcast RANN"); rann.rann_flags = 0; if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) rann.rann_flags |= IEEE80211_MESHRANN_FLAGS_PR; rann.rann_hopcount = 0; rann.rann_ttl = ms->ms_ttl; IEEE80211_ADDR_COPY(rann.rann_addr, vap->iv_myaddr); rann.rann_seq = ++hs->hs_seq; rann.rann_metric = IEEE80211_MESHLMETRIC_INITIALVAL; vap->iv_stats.is_hwmp_rootrann++; hwmp_send_rann(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &rann); hwmp_rootmode_setup(vap); wlan_serialize_exit(); }
static void hwmp_rootmode_cb(void *arg) { struct ieee80211vap *vap = (struct ieee80211vap *)arg; struct ieee80211_hwmp_state *hs = vap->iv_hwmp; struct ieee80211_mesh_state *ms = vap->iv_mesh; struct ieee80211_meshpreq_ie preq; IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, "%s", "send broadcast PREQ"); preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; if (ms->ms_flags & IEEE80211_MESHFLAGS_PORTAL) preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PR; if (hs->hs_rootmode == IEEE80211_HWMP_ROOTMODE_PROACTIVE) preq.preq_flags |= IEEE80211_MESHPREQ_FLAGS_PP; preq.preq_hopcount = 0; preq.preq_ttl = ms->ms_ttl; preq.preq_id = ++hs->hs_preqid; IEEE80211_ADDR_COPY(preq.preq_origaddr, vap->iv_myaddr); preq.preq_origseq = ++hs->hs_seq; preq.preq_lifetime = ticks_to_msecs(ieee80211_hwmp_roottimeout); preq.preq_metric = IEEE80211_MESHLMETRIC_INITIALVAL; preq.preq_tcount = 1; IEEE80211_ADDR_COPY(PREQ_TADDR(0), broadcastaddr); PREQ_TFLAGS(0) = IEEE80211_MESHPREQ_TFLAGS_TO | IEEE80211_MESHPREQ_TFLAGS_RF; PREQ_TSEQ(0) = 0; vap->iv_stats.is_hwmp_rootreqs++; hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); hwmp_rootmode_setup(vap); }
/* * Handle station leaving bss. */ void ieee80211_node_saveq_cleanup(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; /* vap is already deleted */ if (vap == NULL) return; if (vap->iv_opmode == IEEE80211_M_HOSTAP) { ieee80211_mlme_node_leave(ni); } else { ieee80211node_clear_flag(ni, IEEE80211_NODE_PWR_MGT); #if NOT_YET if (ieee80211node_has_flag(ni, IEEE80211_NODE_UAPSD_TRIG)) { ieee80211node_clear_flag(ni, IEEE80211_NODE_UAPSD_TRIG); IEEE80211_UAPSD_LOCK(ni->ni_ic); ni->ni_ic->ic_uapsdmaxtriggers--; IEEE80211_UAPSD_UNLOCK(ni->ni_ic); } #endif } if ((ieee80211_node_saveq_drain(ni) != 0) && (vap->iv_set_tim != NULL)) vap->iv_set_tim(ni, 0); IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "%s %u sta's in ps mode \n", __func__,vap->iv_ps_sta); }
/* * Move frames from the ps q to the vap's send queue * and/or the driver's send queue; and kick the start * method for each, as appropriate. Note we're careful * to preserve packet ordering here. */ static void pwrsave_flushq(struct ieee80211_node *ni) { struct ieee80211_psq *psq = &ni->ni_psq; struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_psq_head *qhead; struct mbuf *parent_q = NULL, *ifp_q = NULL; struct mbuf *m; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "flush ps queue, %u packets queued", psq->psq_len); IEEE80211_PSQ_LOCK(psq); qhead = &psq->psq_head[0]; /* 802.11 frames */ if (qhead->head != NULL) { /* XXX could dispatch through vap and check M_ENCAP */ /* XXX need different driver interface */ /* XXX bypasses q max and OACTIVE */ parent_q = qhead->head; qhead->head = qhead->tail = NULL; qhead->len = 0; } qhead = &psq->psq_head[1]; /* 802.3 frames */ if (qhead->head != NULL) { /* XXX need different driver interface */ /* XXX bypasses q max and OACTIVE */ ifp_q = qhead->head; qhead->head = qhead->tail = NULL; qhead->len = 0; } psq->psq_len = 0; IEEE80211_PSQ_UNLOCK(psq); /* NB: do this outside the psq lock */ /* XXX packets might get reordered if parent is OACTIVE */ /* parent frames, should be encapsulated */ while (parent_q != NULL) { m = parent_q; parent_q = m->m_nextpkt; m->m_nextpkt = NULL; /* must be encapsulated */ KASSERT((m->m_flags & M_ENCAP), ("%s: parentq with non-M_ENCAP frame!\n", __func__)); (void) ieee80211_parent_xmitpkt(ic, m); } /* VAP frames, aren't encapsulated */ while (ifp_q != NULL) { m = ifp_q; ifp_q = m->m_nextpkt; m->m_nextpkt = NULL; KASSERT((!(m->m_flags & M_ENCAP)), ("%s: vapq with M_ENCAP frame!\n", __func__)); (void) ieee80211_vap_xmitpkt(vap, m); } }
/* * 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; }
void ieee80211_notify_node_auth(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%s", "node auth"); notify_macaddr(ifp, RTM_IEEE80211_AUTH, ni->ni_macaddr); }
static void ieee80211_node_saveq_handle_ps_frames(struct ieee80211_node *ni, wbuf_t wbuf, u_int8_t frame_type) { struct node_powersave_queue *mgtq; wbuf_t tmpwbuf; mgtq = IEEE80211_NODE_SAVEQ_MGMTQ(ni); if (wbuf_is_pwrsaveframe(wbuf)) { /* ps frame (null (or) pspoll frame) */ ++mgtq->nsq_num_ps_frames; } else { /* non ps frame*/ if (mgtq->nsq_num_ps_frames) { struct ieee80211_tx_status ts; struct node_powersave_queue *tmpq, temp_q; tmpq = &temp_q; IEEE80211_NODE_SAVEQ_INIT(tmpq); /* * go through the mgt queue and dump the frames with PS=1(pspoll,null). * accummulate the remaining frames into temp queue. */ for (;;) { IEEE80211_NODE_SAVEQ_DEQUEUE(mgtq, tmpwbuf); if (tmpwbuf == NULL) { break; } if (wbuf_is_pwrsaveframe(tmpwbuf)) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ANY, ni, "%s pwr save q: complete the PS frame with error \n", __func__); ts.ts_flags = IEEE80211_TX_ERROR; ieee80211_release_wbuf(ni,tmpwbuf, &ts); } else { IEEE80211_NODE_SAVEQ_ADD(tmpq,tmpwbuf); } } mgtq->nsq_num_ps_frames = 0; /* * move all the frames from temp queue to mgmt queue. */ for (;;) { IEEE80211_NODE_SAVEQ_DEQUEUE(tmpq, tmpwbuf); if (tmpwbuf == NULL) { break; } IEEE80211_NODE_SAVEQ_ADD(mgtq, tmpwbuf); } } } }
/* * Handle station power-save state change. */ void ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable) { struct ieee80211vap *vap = ni->ni_vap; int update; update = 0; if (enable) { if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { vap->iv_ps_sta++; update = 1; } ni->ni_flags |= IEEE80211_NODE_PWR_MGT; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "power save mode on, %u sta's in ps mode", vap->iv_ps_sta); if (update) vap->iv_update_ps(vap, vap->iv_ps_sta); } else { if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) { vap->iv_ps_sta--; update = 1; } ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); /* NB: order here is intentional so TIM is clear before flush */ if (vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 0); if (update) { /* NB if no sta's in ps, driver should flush mc q */ vap->iv_update_ps(vap, vap->iv_ps_sta); } if (ni->ni_psq.psq_len != 0) pwrsave_flushq(ni); } }
static void hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, const struct ieee80211_frame *wh, const struct ieee80211_meshperr_ie *perr) { struct ieee80211_mesh_state *ms = vap->iv_mesh; struct ieee80211_mesh_route *rt = NULL; struct ieee80211_hwmp_route *hr; struct ieee80211_meshperr_ie pperr; int i, forward = 0; #ifdef IEEE80211_DEBUG char ethstr[ETHER_ADDRSTRLEN + 1]; #endif /* * Acceptance criteria: check if we received a PERR from a * neighbor and forwarding is enabled. */ if (ni == vap->iv_bss || ni->ni_mlstate != IEEE80211_NODE_MESH_ESTABLISHED || !(ms->ms_flags & IEEE80211_MESHFLAGS_FWD)) return; /* * Find all routing entries that match and delete them. */ for (i = 0; i < perr->perr_ndests; i++) { rt = ieee80211_mesh_rt_find(vap, PERR_DADDR(i)); if (rt == NULL) continue; hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); if (!(PERR_DFLAGS(0) & IEEE80211_MESHPERR_DFLAGS_USN) && HWMP_SEQ_GEQ(PERR_DSEQ(i), hr->hr_seq)) { ieee80211_mesh_rt_del(vap, rt->rt_dest); ieee80211_mesh_rt_flush_peer(vap, rt->rt_dest); rt = NULL; forward = 1; } } /* * Propagate the PERR if we previously found it on our routing table. * XXX handle ndest > 1 */ if (forward && perr->perr_ttl > 1) { IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, "propagate PERR from %s", kether_ntoa(wh->i_addr2, ethstr)); memcpy(&pperr, perr, sizeof(*perr)); pperr.perr_ttl--; hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &pperr); } }
void ieee80211_notify_node_leave(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; CURVNET_SET_QUIET(ifp->if_vnet); IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode leave", (ni == vap->iv_bss) ? "bss " : ""); if (ni == vap->iv_bss) { rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0); if_link_state_change(ifp, LINK_STATE_DOWN); } else { /* fire off wireless event station leaving */ notify_macaddr(ifp, RTM_IEEE80211_LEAVE, ni->ni_macaddr); } CURVNET_RESTORE(); }
void ieee80211_notify_node_join(struct ieee80211_node *ni, int newassoc) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; CURVNET_SET_QUIET(ifp->if_vnet); IEEE80211_NOTE(vap, IEEE80211_MSG_NODE, ni, "%snode join", (ni == vap->iv_bss) ? "bss " : ""); if (ni == vap->iv_bss) { notify_macaddr(ifp, newassoc ? RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC, ni->ni_bssid); if_link_state_change(ifp, LINK_STATE_UP); } else { notify_macaddr(ifp, newassoc ? RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN, ni->ni_macaddr); } CURVNET_RESTORE(); }
void ieee80211_amrr_node_init(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn, struct ieee80211_node *ni) { const struct ieee80211_rateset *rs = &ni->ni_rates; amn->amn_amrr = amrr; amn->amn_success = 0; amn->amn_recovery = 0; amn->amn_txcnt = amn->amn_retrycnt = 0; amn->amn_success_threshold = amrr->amrr_min_success_threshold; /* pick initial rate */ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72; amn->amn_rix--) ; ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; amn->amn_ticks = ticks; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "AMRR initial rate %d", ni->ni_txrate); }
/* * Examine and potentially adjust the transmit rate. */ static void ath_rate_ctl(void *arg, struct ieee80211_node *ni) { struct ath_softc *sc = arg; struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni)); int rix; #define is_success(amn) \ (amn->amn_tx_try1_cnt < (amn->amn_tx_try0_cnt/10)) #define is_enough(amn) \ (amn->amn_tx_try0_cnt > 10) #define is_failure(amn) \ (amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3)) rix = amn->amn_rix; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d", amn->amn_tx_try0_cnt, amn->amn_tx_try1_cnt, amn->amn_tx_try2_cnt, amn->amn_tx_try3_cnt, amn->amn_success_threshold); if (is_success (amn) && is_enough (amn)) { amn->amn_success++; if (amn->amn_success == amn->amn_success_threshold && rix + 1 < ni->ni_rates.rs_nrates) { amn->amn_recovery = 1; amn->amn_success = 0; rix++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "increase rate to %d", rix); } else { amn->amn_recovery = 0; } } else if (is_failure (amn)) { amn->amn_success = 0; if (rix > 0) { if (amn->amn_recovery) { /* recovery failure. */ amn->amn_success_threshold *= 2; amn->amn_success_threshold = min (amn->amn_success_threshold, (u_int)ath_rate_max_success_threshold); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "decrease rate recovery thr: %d", amn->amn_success_threshold); } else { /* simple failure. */ amn->amn_success_threshold = ath_rate_min_success_threshold; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "decrease rate normal thr: %d", amn->amn_success_threshold); } amn->amn_recovery = 0; rix--; } else { amn->amn_recovery = 0; } } if (is_enough (amn) || rix != amn->amn_rix) { /* reset counters. */ amn->amn_tx_try0_cnt = 0; amn->amn_tx_try1_cnt = 0; amn->amn_tx_try2_cnt = 0; amn->amn_tx_try3_cnt = 0; amn->amn_tx_failure_cnt = 0; } if (rix != amn->amn_rix) { ath_rate_update(sc, ni, rix); } }
/** * The code below assumes that we are dealing with hardware multi rate retry * I have no idea what will happen if you try to use this module with another * type of hardware. Your machine might catch fire or it might work with * horrible performance... */ static void ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) { struct ath_node *an = ATH_NODE(ni); struct amrr_node *amn = ATH_NODE_AMRR(an); struct ieee80211vap *vap = ni->ni_vap; const HAL_RATE_TABLE *rt = sc->sc_currates; u_int8_t rix; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni, "%s: set xmit rate to %dM", __func__, ni->ni_rates.rs_nrates > 0 ? (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); amn->amn_rix = rate; /* * Before associating a node has no rate set setup * so we can't calculate any transmit codes to use. * This is ok since we should never be sending anything * but management frames and those always go at the * lowest hardware rate. */ if (ni->ni_rates.rs_nrates > 0) { ni->ni_txrate = ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL; amn->amn_tx_rix0 = sc->sc_rixmap[ni->ni_txrate]; amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode; amn->amn_tx_rate0sp = amn->amn_tx_rate0 | rt->info[amn->amn_tx_rix0].shortPreamble; if (sc->sc_mrretry) { amn->amn_tx_try0 = 1; amn->amn_tx_try1 = 1; amn->amn_tx_try2 = 1; amn->amn_tx_try3 = 1; if (--rate >= 0) { rix = sc->sc_rixmap[ ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; amn->amn_tx_rate1 = rt->info[rix].rateCode; amn->amn_tx_rate1sp = amn->amn_tx_rate1 | rt->info[rix].shortPreamble; } else { amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; } if (--rate >= 0) { rix = sc->sc_rixmap[ ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; amn->amn_tx_rate2 = rt->info[rix].rateCode; amn->amn_tx_rate2sp = amn->amn_tx_rate2 | rt->info[rix].shortPreamble; } else { amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; } if (rate > 0) { /* NB: only do this if we didn't already do it above */ amn->amn_tx_rate3 = rt->info[0].rateCode; amn->amn_tx_rate3sp = amn->amn_tx_rate3 | rt->info[0].shortPreamble; } else { amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; } } else { amn->amn_tx_try0 = ATH_TXMAXTRY; /* theorically, these statements are useless because * the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY */ amn->amn_tx_try1 = 0; amn->amn_tx_try2 = 0; amn->amn_tx_try3 = 0; amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0; amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0; amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0; } } node_reset(amn); amn->amn_interval = ath_rateinterval; if (vap->iv_opmode == IEEE80211_M_STA) amn->amn_interval /= 2; amn->amn_interval = (amn->amn_interval * hz) / 1000; }
void ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, const struct ath_rc_series *rc, const struct ath_tx_status *ts, int frame_size, int nframes, int nbad) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct sample_node *sn = ATH_NODE_SAMPLE(an); int final_rix, short_tries, long_tries; const HAL_RATE_TABLE *rt = sc->sc_currates; int status = ts->ts_status; int mrr; final_rix = rt->rateCodeToIndex[ts->ts_rate]; short_tries = ts->ts_shortretry; long_tries = ts->ts_longretry + 1; if (frame_size == 0) /* NB: should not happen */ frame_size = 1500; if (sn->ratemask == 0) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s rate/try %d/%d no rates yet", __func__, bin_to_size(size_to_bin(frame_size)), status ? "FAIL" : "OK", short_tries, long_tries); return; } mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); if (!mrr || ts->ts_finaltsi == 0) { if (!IS_RATE_DEFINED(sn, final_rix)) { badrate(ifp, 0, ts->ts_rate, long_tries, status); return; } /* * Only one rate was used; optimize work. */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d (%d bytes) %s rate/try %d %s/%d/%d nframes/nbad [%d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), frame_size, status ? "FAIL" : "OK", dot11rate(rt, final_rix), dot11rate_label(rt, final_rix), short_tries, long_tries, nframes, nbad); update_stats(sc, an, frame_size, final_rix, long_tries, 0, 0, 0, 0, 0, 0, short_tries, long_tries, status, nframes, nbad); update_ewma_stats(sc, an, frame_size, final_rix, long_tries, 0, 0, 0, 0, 0, 0, short_tries, long_tries, status, nframes, nbad); } else { int finalTSIdx = ts->ts_finaltsi; int i; /* * Process intermediate rates that failed. */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d (%d bytes) finaltsidx %d tries %d %s rate/try [%d %s/%d %d %s/%d %d %s/%d %d %s/%d] nframes/nbad [%d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), frame_size, finalTSIdx, long_tries, status ? "FAIL" : "OK", dot11rate(rt, rc[0].rix), dot11rate_label(rt, rc[0].rix), rc[0].tries, dot11rate(rt, rc[1].rix), dot11rate_label(rt, rc[1].rix), rc[1].tries, dot11rate(rt, rc[2].rix), dot11rate_label(rt, rc[2].rix), rc[2].tries, dot11rate(rt, rc[3].rix), dot11rate_label(rt, rc[3].rix), rc[3].tries, nframes, nbad); for (i = 0; i < 4; i++) { if (rc[i].tries && !IS_RATE_DEFINED(sn, rc[i].rix)) badrate(ifp, 0, rc[i].ratecode, rc[i].tries, status); } /* * NB: series > 0 are not penalized for failure * based on the try counts under the assumption * that losses are often bursty and since we * sample higher rates 1 try at a time doing so * may unfairly penalize them. */ if (rc[0].tries) { update_stats(sc, an, frame_size, rc[0].rix, rc[0].tries, rc[1].rix, rc[1].tries, rc[2].rix, rc[2].tries, rc[3].rix, rc[3].tries, short_tries, long_tries, long_tries > rc[0].tries, nframes, nbad); long_tries -= rc[0].tries; } if (rc[1].tries && finalTSIdx > 0) { update_stats(sc, an, frame_size, rc[1].rix, rc[1].tries, rc[2].rix, rc[2].tries, rc[3].rix, rc[3].tries, 0, 0, short_tries, long_tries, status, nframes, nbad); long_tries -= rc[1].tries; } if (rc[2].tries && finalTSIdx > 1) { update_stats(sc, an, frame_size, rc[2].rix, rc[2].tries, rc[3].rix, rc[3].tries, 0, 0, 0, 0, short_tries, long_tries, status, nframes, nbad); long_tries -= rc[2].tries; } if (rc[3].tries && finalTSIdx > 2) { update_stats(sc, an, frame_size, rc[3].rix, rc[3].tries, 0, 0, 0, 0, 0, 0, short_tries, long_tries, status, nframes, nbad); } update_ewma_stats(sc, an, frame_size, rc[0].rix, rc[0].tries, rc[1].rix, rc[1].tries, rc[2].rix, rc[2].tries, rc[3].rix, rc[3].tries, short_tries, long_tries, long_tries > rc[0].tries, nframes, nbad); } }
static void update_stats(struct ath_softc *sc, struct ath_node *an, int frame_size, int rix0, int tries0, int rix1, int tries1, int rix2, int tries2, int rix3, int tries3, int short_tries, int tries, int status, int nframes, int nbad) { struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); #ifdef IEEE80211_DEBUG const HAL_RATE_TABLE *rt = sc->sc_currates; #endif const int size_bin = size_to_bin(frame_size); const int size = bin_to_size(size_bin); int tt, tries_so_far; int is_ht40 = (an->an_node.ni_chw == 40); if (!IS_RATE_DEFINED(sn, rix0)) return; tt = calc_usecs_unicast_packet(sc, size, rix0, short_tries, MIN(tries0, tries) - 1, is_ht40); tries_so_far = tries0; if (tries1 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix1)) return; tt += calc_usecs_unicast_packet(sc, size, rix1, short_tries, MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40); tries_so_far += tries1; } if (tries2 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix2)) return; tt += calc_usecs_unicast_packet(sc, size, rix2, short_tries, MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40); tries_so_far += tries2; } if (tries3 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix3)) return; tt += calc_usecs_unicast_packet(sc, size, rix3, short_tries, MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40); } if (sn->stats[size_bin][rix0].total_packets < ssc->smoothing_minpackets) { /* just average the first few packets */ int avg_tx = sn->stats[size_bin][rix0].average_tx_time; int packets = sn->stats[size_bin][rix0].total_packets; sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes); } else { /* use a ewma */ sn->stats[size_bin][rix0].average_tx_time = ((sn->stats[size_bin][rix0].average_tx_time * ssc->smoothing_rate) + (tt * (100 - ssc->smoothing_rate))) / 100; } /* * XXX Don't mark the higher bit rates as also having failed; as this * unfortunately stops those rates from being tasted when trying to * TX. This happens with 11n aggregation. */ if (nframes == nbad) { #if 0 int y; #endif sn->stats[size_bin][rix0].successive_failures += nbad; #if 0 for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) { /* * Also say larger packets failed since we * assume if a small packet fails at a * bit-rate then a larger one will also. */ sn->stats[y][rix0].successive_failures += nbad; sn->stats[y][rix0].last_tx = ticks; sn->stats[y][rix0].tries += tries; sn->stats[y][rix0].total_packets += nframes; } #endif } else { sn->stats[size_bin][rix0].packets_acked += (nframes - nbad); sn->stats[size_bin][rix0].successive_failures = 0; } sn->stats[size_bin][rix0].tries += tries; sn->stats[size_bin][rix0].last_tx = ticks; sn->stats[size_bin][rix0].total_packets += nframes; if (rix0 == sn->current_sample_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s sample rate %d %s tries (%d/%d) tt %d avg_tt (%d/%d) nfrm %d nbad %d", __func__, size, status ? "FAIL" : "OK", dot11rate(rt, rix0), dot11rate_label(rt, rix0), short_tries, tries, tt, sn->stats[size_bin][rix0].average_tx_time, sn->stats[size_bin][rix0].perfect_tx_time, nframes, nbad); sn->sample_tt[size_bin] = tt; sn->current_sample_rix[size_bin] = -1; } }
void ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, int shortPreamble, size_t frameLen, u_int8_t *rix0, int *try0, u_int8_t *txrate) { #define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) #define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS) #define RATE(ix) (DOT11RATE(ix) / 2) struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const HAL_RATE_TABLE *rt = sc->sc_currates; const int size_bin = size_to_bin(frameLen); int rix, mrr, best_rix, change_rates; unsigned average_tx_time; ath_rate_update_static_rix(sc, &an->an_node); if (sn->static_rix != -1) { rix = sn->static_rix; *try0 = ATH_TXMAXTRY; goto done; } /* XXX TODO: this doesn't know about 11gn vs 11g protection; teach it */ mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); best_rix = pick_best_rate(an, rt, size_bin, !mrr); if (best_rix >= 0) { average_tx_time = sn->stats[size_bin][best_rix].average_tx_time; } else { average_tx_time = 0; } /* * Limit the time measuring the performance of other tx * rates to sample_rate% of the total transmission time. */ if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) { rix = pick_sample_rate(ssc, an, rt, size_bin); IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s", average_tx_time, sn->sample_tt[size_bin], bin_to_size(size_bin), dot11rate(rt, rix), dot11rate_label(rt, rix), dot11rate(rt, sn->current_rix[size_bin]), dot11rate_label(rt, sn->current_rix[size_bin])); if (rix != sn->current_rix[size_bin]) { sn->current_sample_rix[size_bin] = rix; } else { sn->current_sample_rix[size_bin] = -1; } sn->packets_since_sample[size_bin] = 0; } else { change_rates = 0; if (!sn->packets_sent[size_bin] || best_rix == -1) { /* no packet has been sent successfully yet */ change_rates = 1; if (an->an_node.ni_flags & IEEE80211_NODE_HT) best_rix = ath_rate_pick_seed_rate_ht(sc, an, frameLen); else best_rix = ath_rate_pick_seed_rate_legacy(sc, an, frameLen); } else if (sn->packets_sent[size_bin] < 20) { /* let the bit-rate switch quickly during the first few packets */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: switching quickly..", __func__); change_rates = 1; } else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) { /* min_switch seconds have gone by */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: min_switch %d > ticks_since_switch %d..", __func__, ticks - ssc->min_switch, sn->ticks_since_switch[size_bin]); change_rates = 1; } else if ((! (an->an_node.ni_flags & IEEE80211_NODE_HT)) && (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time)) { /* the current bit-rate is twice as slow as the best one */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: 2x att (= %d) < cur_rix att %d", __func__, 2 * average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time); change_rates = 1; } else if ((an->an_node.ni_flags & IEEE80211_NODE_HT)) { int cur_rix = sn->current_rix[size_bin]; int cur_att = sn->stats[size_bin][cur_rix].average_tx_time; /* * If the node is HT, upgrade it if the MCS rate is * higher and the average tx time is within 20% of * the current rate. It can fail a little. * * This is likely not optimal! */ #if 0 printf("cur rix/att %x/%d, best rix/att %x/%d\n", MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time); #endif if ((MCS(best_rix) > MCS(cur_rix)) && (average_tx_time * 8) <= (cur_att * 10)) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d", __func__, MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att); change_rates = 1; } } sn->packets_since_sample[size_bin]++; if (change_rates) { if (best_rix != sn->current_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d", __func__, bin_to_size(size_bin), RATE(sn->current_rix[size_bin]), sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].perfect_tx_time, RATE(best_rix), sn->stats[size_bin][best_rix].average_tx_time, sn->stats[size_bin][best_rix].perfect_tx_time, sn->packets_since_switch[size_bin], mrr); } sn->packets_since_switch[size_bin] = 0; sn->current_rix[size_bin] = best_rix; sn->ticks_since_switch[size_bin] = ticks; /* * Set the visible txrate for this node. */ an->an_node.ni_txrate = (rt->info[best_rix].phy == IEEE80211_T_HT) ? MCS(best_rix) : DOT11RATE(best_rix); } rix = sn->current_rix[size_bin]; sn->packets_since_switch[size_bin]++; } *try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY; done: KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix)); *rix0 = rix; *txrate = rt->info[rix].rateCode | (shortPreamble ? rt->info[rix].shortPreamble : 0); sn->packets_sent[size_bin]++; #undef DOT11RATE #undef MCS #undef RATE }
/* * Initialize the tables for a node. */ static void ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni) { #define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) #define DOT11RATE(_ix) (rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL) struct ath_node *an = ATH_NODE(ni); const struct ieee80211_txparam *tp = ni->ni_txparms; struct sample_node *sn = ATH_NODE_SAMPLE(an); const HAL_RATE_TABLE *rt = sc->sc_currates; #ifdef IEEE80211_DEBUG char ethstr[ETHER_ADDRSTRLEN + 1]; #endif int x, y, srate, rix; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); KASSERT(sc->sc_curmode < IEEE80211_MODE_MAX+2, ("curmode %u", sc->sc_curmode)); sn->sched = mrr_schedules[sc->sc_curmode]; KASSERT(sn->sched != NULL, ("no mrr schedule for mode %u", sc->sc_curmode)); sn->static_rix = -1; if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is to be used; ucastrate is the IEEE code * for this rate (sans basic bit). Check this against the * negotiated rate set for the node. Note the fixed rate * may not be available for various reasons so we only * setup the static rate index if the lookup is successful. * XXX handle MCS */ for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--) if (RATE(srate) == tp->ucastrate) { sn->static_rix = sc->sc_rixmap[tp->ucastrate]; break; } #ifdef IEEE80211_DEBUG if (sn->static_rix == -1) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, "%s: ucastrate %u not found, nrates %u", __func__, tp->ucastrate, ni->ni_rates.rs_nrates); } #endif } /* * Construct a bitmask of usable rates. This has all * negotiated rates minus those marked by the hal as * to be ignored for doing rate control. */ sn->ratemask = 0; for (x = 0; x < ni->ni_rates.rs_nrates; x++) { rix = sc->sc_rixmap[RATE(x)]; if (rix == 0xff) continue; /* skip rates marked broken by hal */ if (!rt->info[rix].valid) continue; KASSERT(rix < SAMPLE_MAXRATES, ("rate %u has rix %d", RATE(x), rix)); sn->ratemask |= 1<<rix; } #ifdef IEEE80211_DEBUG if (ieee80211_msg(ni->ni_vap, IEEE80211_MSG_RATECTL)) { uint32_t mask; ieee80211_note(ni->ni_vap, "[%s] %s: size 1600 rate/tt", kether_ntoa(ni->ni_macaddr, ethstr), __func__); for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { if ((mask & 1) == 0) continue; kprintf(" %d/%d", DOT11RATE(rix) / 2, calc_usecs_unicast_packet(sc, 1600, rix, 0,0)); } kprintf("\n"); }
void ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, const struct ath_buf *bf) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct sample_node *sn = ATH_NODE_SAMPLE(an); const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; const struct ath_desc *ds0 = &bf->bf_desc[0]; int final_rix, short_tries, long_tries, frame_size; const HAL_RATE_TABLE *rt = sc->sc_currates; int mrr; final_rix = rt->rateCodeToIndex[ts->ts_rate]; short_tries = ts->ts_shortretry; long_tries = ts->ts_longretry + 1; frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */ if (frame_size == 0) /* NB: should not happen */ frame_size = 1500; if (sn->ratemask == 0) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s rate/try %d/%d no rates yet", __func__, bin_to_size(size_to_bin(frame_size)), ts->ts_status ? "FAIL" : "OK", short_tries, long_tries); return; } mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); if (!mrr || ts->ts_finaltsi == 0) { if (!IS_RATE_DEFINED(sn, final_rix)) { badrate(ifp, 0, ts->ts_rate, long_tries, ts->ts_status); return; } /* * Only one rate was used; optimize work. */ IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s rate/try %d/%d/%d", __func__, bin_to_size(size_to_bin(frame_size)), ts->ts_status ? "FAIL" : "OK", final_rix, short_tries, long_tries); update_stats(sc, an, frame_size, final_rix, long_tries, 0, 0, 0, 0, 0, 0, short_tries, long_tries, ts->ts_status); } else { int hwrate0, rix0, tries0; int hwrate1, rix1, tries1; int hwrate2, rix2, tries2; int hwrate3, rix3, tries3; int finalTSIdx = ts->ts_finaltsi; /* * Process intermediate rates that failed. */ if (sc->sc_ah->ah_magic != 0x20065416) { hwrate0 = MS(ds0->ds_ctl3, AR_XmitRate0); hwrate1 = MS(ds0->ds_ctl3, AR_XmitRate1); hwrate2 = MS(ds0->ds_ctl3, AR_XmitRate2); hwrate3 = MS(ds0->ds_ctl3, AR_XmitRate3); } else { hwrate0 = MS(ds0->ds_ctl3, AR5416_XmitRate0); hwrate1 = MS(ds0->ds_ctl3, AR5416_XmitRate1); hwrate2 = MS(ds0->ds_ctl3, AR5416_XmitRate2); hwrate3 = MS(ds0->ds_ctl3, AR5416_XmitRate3); } rix0 = rt->rateCodeToIndex[hwrate0]; tries0 = MS(ds0->ds_ctl2, AR_XmitDataTries0); rix1 = rt->rateCodeToIndex[hwrate1]; tries1 = MS(ds0->ds_ctl2, AR_XmitDataTries1); rix2 = rt->rateCodeToIndex[hwrate2]; tries2 = MS(ds0->ds_ctl2, AR_XmitDataTries2); rix3 = rt->rateCodeToIndex[hwrate3]; tries3 = MS(ds0->ds_ctl2, AR_XmitDataTries3); IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d finaltsidx %d tries %d %s rate/try [%d/%d %d/%d %d/%d %d/%d]", __func__, bin_to_size(size_to_bin(frame_size)), finalTSIdx, long_tries, ts->ts_status ? "FAIL" : "OK", rix0, tries0, rix1, tries1, rix2, tries2, rix3, tries3); if (tries0 && !IS_RATE_DEFINED(sn, rix0)) badrate(ifp, 0, hwrate0, tries0, ts->ts_status); if (tries1 && !IS_RATE_DEFINED(sn, rix1)) badrate(ifp, 1, hwrate1, tries1, ts->ts_status); if (tries2 && !IS_RATE_DEFINED(sn, rix2)) badrate(ifp, 2, hwrate2, tries2, ts->ts_status); if (tries3 && !IS_RATE_DEFINED(sn, rix3)) badrate(ifp, 3, hwrate3, tries3, ts->ts_status); /* * NB: series > 0 are not penalized for failure * based on the try counts under the assumption * that losses are often bursty and since we * sample higher rates 1 try at a time doing so * may unfairly penalize them. */ if (tries0) { update_stats(sc, an, frame_size, rix0, tries0, rix1, tries1, rix2, tries2, rix3, tries3, short_tries, long_tries, long_tries > tries0); long_tries -= tries0; } if (tries1 && finalTSIdx > 0) { update_stats(sc, an, frame_size, rix1, tries1, rix2, tries2, rix3, tries3, 0, 0, short_tries, long_tries, ts->ts_status); long_tries -= tries1; } if (tries2 && finalTSIdx > 1) { update_stats(sc, an, frame_size, rix2, tries2, rix3, tries3, 0, 0, 0, 0, short_tries, long_tries, ts->ts_status); long_tries -= tries2; } if (tries3 && finalTSIdx > 2) { update_stats(sc, an, frame_size, rix3, tries3, 0, 0, 0, 0, 0, 0, short_tries, long_tries, ts->ts_status); } } }
static void update_stats(struct ath_softc *sc, struct ath_node *an, int frame_size, int rix0, int tries0, int rix1, int tries1, int rix2, int tries2, int rix3, int tries3, int short_tries, int tries, int status) { struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); const int size_bin = size_to_bin(frame_size); const int size = bin_to_size(size_bin); int tt, tries_so_far; if (!IS_RATE_DEFINED(sn, rix0)) return; tt = calc_usecs_unicast_packet(sc, size, rix0, short_tries, MIN(tries0, tries) - 1); tries_so_far = tries0; if (tries1 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix1)) return; tt += calc_usecs_unicast_packet(sc, size, rix1, short_tries, MIN(tries1 + tries_so_far, tries) - tries_so_far - 1); tries_so_far += tries1; } if (tries2 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix2)) return; tt += calc_usecs_unicast_packet(sc, size, rix2, short_tries, MIN(tries2 + tries_so_far, tries) - tries_so_far - 1); tries_so_far += tries2; } if (tries3 && tries_so_far < tries) { if (!IS_RATE_DEFINED(sn, rix3)) return; tt += calc_usecs_unicast_packet(sc, size, rix3, short_tries, MIN(tries3 + tries_so_far, tries) - tries_so_far - 1); } if (sn->stats[size_bin][rix0].total_packets < ssc->smoothing_minpackets) { /* just average the first few packets */ int avg_tx = sn->stats[size_bin][rix0].average_tx_time; int packets = sn->stats[size_bin][rix0].total_packets; sn->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+1); } else { /* use a ewma */ sn->stats[size_bin][rix0].average_tx_time = ((sn->stats[size_bin][rix0].average_tx_time * ssc->smoothing_rate) + (tt * (100 - ssc->smoothing_rate))) / 100; } if (status != 0) { int y; sn->stats[size_bin][rix0].successive_failures++; for (y = size_bin+1; y < NUM_PACKET_SIZE_BINS; y++) { /* * Also say larger packets failed since we * assume if a small packet fails at a * bit-rate then a larger one will also. */ sn->stats[y][rix0].successive_failures++; sn->stats[y][rix0].last_tx = ticks; sn->stats[y][rix0].tries += tries; sn->stats[y][rix0].total_packets++; } } else { sn->stats[size_bin][rix0].packets_acked++; sn->stats[size_bin][rix0].successive_failures = 0; } sn->stats[size_bin][rix0].tries += tries; sn->stats[size_bin][rix0].last_tx = ticks; sn->stats[size_bin][rix0].total_packets++; if (rix0 == sn->current_sample_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d %s sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d)", __func__, size, status ? "FAIL" : "OK", rix0, short_tries, tries, tt, sn->stats[size_bin][rix0].average_tx_time, sn->stats[size_bin][rix0].perfect_tx_time); sn->sample_tt[size_bin] = tt; sn->current_sample_rix[size_bin] = -1; } }
void ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, int shortPreamble, size_t frameLen, u_int8_t *rix0, int *try0, u_int8_t *txrate) { #define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL) #define RATE(ix) (DOT11RATE(ix) / 2) struct sample_node *sn = ATH_NODE_SAMPLE(an); struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const HAL_RATE_TABLE *rt = sc->sc_currates; const int size_bin = size_to_bin(frameLen); int rix, mrr, best_rix, change_rates; unsigned average_tx_time; if (sn->static_rix != -1) { rix = sn->static_rix; *try0 = ATH_TXMAXTRY; goto done; } mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT); best_rix = pick_best_rate(sn, rt, size_bin, !mrr); if (best_rix >= 0) { average_tx_time = sn->stats[size_bin][best_rix].average_tx_time; } else { average_tx_time = 0; } /* * Limit the time measuring the performance of other tx * rates to sample_rate% of the total transmission time. */ if (sn->sample_tt[size_bin] < average_tx_time * (sn->packets_since_sample[size_bin]*ssc->sample_rate/100)) { rix = pick_sample_rate(ssc, sn, rt, size_bin); IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "size %u sample rate %d current rate %d", bin_to_size(size_bin), RATE(rix), RATE(sn->current_rix[size_bin])); if (rix != sn->current_rix[size_bin]) { sn->current_sample_rix[size_bin] = rix; } else { sn->current_sample_rix[size_bin] = -1; } sn->packets_since_sample[size_bin] = 0; } else { change_rates = 0; if (!sn->packets_sent[size_bin] || best_rix == -1) { /* no packet has been sent successfully yet */ for (rix = rt->rateCount-1; rix > 0; rix--) { if ((sn->ratemask & (1<<rix)) == 0) continue; /* * Pick the highest rate <= 36 Mbps * that hasn't failed. */ if (DOT11RATE(rix) <= 72 && sn->stats[size_bin][rix].successive_failures == 0) { break; } } change_rates = 1; best_rix = rix; } else if (sn->packets_sent[size_bin] < 20) { /* let the bit-rate switch quickly during the first few packets */ change_rates = 1; } else if (ticks - ssc->min_switch > sn->ticks_since_switch[size_bin]) { /* min_switch seconds have gone by */ change_rates = 1; } else if (2*average_tx_time < sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time) { /* the current bit-rate is twice as slow as the best one */ change_rates = 1; } sn->packets_since_sample[size_bin]++; if (change_rates) { if (best_rix != sn->current_rix[size_bin]) { IEEE80211_NOTE(an->an_node.ni_vap, IEEE80211_MSG_RATECTL, &an->an_node, "%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d", __func__, bin_to_size(size_bin), RATE(sn->current_rix[size_bin]), sn->stats[size_bin][sn->current_rix[size_bin]].average_tx_time, sn->stats[size_bin][sn->current_rix[size_bin]].perfect_tx_time, RATE(best_rix), sn->stats[size_bin][best_rix].average_tx_time, sn->stats[size_bin][best_rix].perfect_tx_time, sn->packets_since_switch[size_bin], mrr); } sn->packets_since_switch[size_bin] = 0; sn->current_rix[size_bin] = best_rix; sn->ticks_since_switch[size_bin] = ticks; /* * Set the visible txrate for this node. */ an->an_node.ni_txrate = DOT11RATE(best_rix); } rix = sn->current_rix[size_bin]; sn->packets_since_switch[size_bin]++; } *try0 = mrr ? sn->sched[rix].t0 : ATH_TXMAXTRY; done: KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix)); *rix0 = rix; *txrate = rt->info[rix].rateCode | (shortPreamble ? rt->info[rix].shortPreamble : 0); sn->packets_sent[size_bin]++; #undef DOT11RATE #undef RATE }
/* * Handle a station leaving an 11g network. */ static void ieee80211_node_leave_11g(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; KASSERT((IEEE80211_IS_CHAN_ANYG(vap->iv_bsschan) || IEEE80211_IS_CHAN_11NG(vap->iv_bsschan)), ("not in 11g, bss %u:0x%x, curmode %u", vap->iv_bsschan->ic_freq, vap->iv_bsschan->ic_flags, ic->ic_curmode)); KASSERT((ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP || ni->ni_vap->iv_opmode == IEEE80211_M_BTAMP), (" node leave in invalid opmode ")); /* * If a long slot station do the slot time bookkeeping. */ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { if (ic->ic_longslotsta) { ic->ic_longslotsta--; IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, "long slot time station leaves, count now %d", ic->ic_longslotsta); } else { #ifdef NOT_YET KASSERT(ic->ic_longslotsta > 0, ("bogus long slot station count %d", ic->ic_longslotsta)); #endif IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, "bogus long slot station count %d", ic->ic_longslotsta); } if (ic->ic_longslotsta == 0) { /* * Re-enable use of short slot time if supported * and not operating in IBSS mode (per spec). */ if (ic->ic_caps & IEEE80211_C_SHSLOT) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: re-enable use of short slot time\n", __func__); ieee80211_set_shortslottime(ic, 1); } } } /* * If a non-ERP station do the protection-related bookkeeping. */ if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { KASSERT(ic->ic_nonerpsta > 0, ("bogus non-ERP station count %d", ic->ic_nonerpsta)); ic->ic_nonerpsta--; IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni, "non-ERP station leaves, count now %d", ic->ic_nonerpsta); if (ic->ic_nonerpsta == 0) { struct ieee80211vap *tmpvap; ieee80211_update_erp_info(vap); /* XXX verify mode? */ if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: re-enable use of short preamble\n", __func__); ic->ic_flags |= IEEE80211_F_SHPREAMBLE; ic->ic_flags &= ~IEEE80211_F_USEBARKER; } TAILQ_FOREACH(tmpvap, &(ic)->ic_vaps, iv_next) ieee80211_vap_erpupdate_set(tmpvap); } } }
/* * Handle a station joining an 11g network. */ static void ieee80211_node_join_11g(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; KASSERT((IEEE80211_IS_CHAN_ANYG(vap->iv_bsschan) || IEEE80211_IS_CHAN_11NG(vap->iv_bsschan)), ("not in 11g, bss %u:0x%x, curmode %u", vap->iv_bsschan->ic_freq, vap->iv_bsschan->ic_flags, ic->ic_curmode)); KASSERT((ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP || ni->ni_vap->iv_opmode == IEEE80211_M_BTAMP), (" node join in invalid opmode ")); /* * Station isn't capable of short slot time. Bump * the count of long slot time stations and disable * use of short slot time. Note that the actual switch * over to long slot time use may not occur until the * next beacon transmission (per sec. 7.3.1.4 of 11g). */ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { ic->ic_longslotsta++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "station needs long slot time, count %d", ic->ic_longslotsta); /* XXX vap's w/ conflicting needs won't work */ if (!IEEE80211_IS_CHAN_108G(vap->iv_bsschan)) { /* * Don't force slot time when switched to turbo * mode as non-ERP stations won't be present; this * need only be done when on the normal G channel. */ ieee80211_set_shortslottime(ic, 0); } } /* * If the new station is not an ERP station * then bump the counter and enable protection * if configured. */ if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) { ic->ic_nonerpsta++; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "station is !ERP, %d non-ERP stations associated", ic->ic_nonerpsta); /* * If protection is configured, enable it. */ if (ic->ic_protmode != IEEE80211_PROT_NONE) { IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, "%s: enable use of protection\n", __func__); ic->ic_flags |= IEEE80211_F_USEPROT; ic->ic_update_protmode(ic); } /* * If station does not support short preamble * then we must enable use of Barker preamble. */ if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "%s", "station needs long preamble"); ic->ic_flags |= IEEE80211_F_USEBARKER; ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; } /* Update ERP element if this is first non ERP station */ if (ic->ic_nonerpsta == 1) { struct ieee80211vap *tmpvap; TAILQ_FOREACH(tmpvap, &(ic)->ic_vaps, iv_next) ieee80211_vap_erpupdate_set(tmpvap); } } else ni->ni_flags |= IEEE80211_NODE_ERP; }