diff options
-rw-r--r-- | sys/net80211/ieee80211_ht.h | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_output.c | 123 | ||||
-rw-r--r-- | sys/net80211/ieee80211_sta.c | 6 | ||||
-rw-r--r-- | sys/net80211/ieee80211_superg.c | 198 | ||||
-rw-r--r-- | sys/net80211/ieee80211_superg.h | 23 |
6 files changed, 286 insertions, 72 deletions
diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index e2b331854dff..c0464119aa7c 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -65,6 +65,10 @@ struct ieee80211_tx_ampdu { #define IEEE80211_AMPDU_RUNNING(tap) \ (((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0) +/* return non-zero if AMPDU tx for the TID was NACKed */ +#define IEEE80211_AMPDU_NACKED(tap)\ + (!! ((tap)->txa_flags & IEEE80211_AGGR_NAK)) + /* return non-zero if AMPDU tx for the TID is running or started */ #define IEEE80211_AMPDU_REQUESTED(tap) \ (((tap)->txa_flags & \ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 520687b0a4e0..66afb7401069 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1014,8 +1014,8 @@ node_cleanup(struct ieee80211_node *ni) if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_ht_node_cleanup(ni); #ifdef IEEE80211_SUPPORT_SUPERG - else if (ni->ni_ath_flags & IEEE80211_NODE_ATH) - ieee80211_ff_node_cleanup(ni); + /* Always do FF node cleanup; for A-MSDU */ + ieee80211_ff_node_cleanup(ni); #endif #ifdef IEEE80211_SUPPORT_MESH /* diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index b2580d1a5c85..1882a2634401 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -191,51 +191,68 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, * otherwise unable to establish a BA stream. */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && - (m->m_flags & M_EAPOL) == 0) { - int tid = WME_AC_TO_TID(M_WME_GETAC(m)); - struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; - - ieee80211_txampdu_count_packet(tap); - if (IEEE80211_AMPDU_RUNNING(tap)) { - /* - * Operational, mark frame for aggregation. - * - * XXX do tx aggregation here - */ - m->m_flags |= M_AMPDU_MPDU; - } else if (!IEEE80211_AMPDU_REQUESTED(tap) && - ic->ic_ampdu_enable(ni, tap)) { - /* - * Not negotiated yet, request service. - */ - ieee80211_ampdu_request(ni, tap); - /* XXX hold frame for reply? */ + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) { + if ((m->m_flags & M_EAPOL) == 0) { + int tid = WME_AC_TO_TID(M_WME_GETAC(m)); + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; + + ieee80211_txampdu_count_packet(tap); + if (IEEE80211_AMPDU_RUNNING(tap)) { + /* + * Operational, mark frame for aggregation. + * + * XXX do tx aggregation here + */ + m->m_flags |= M_AMPDU_MPDU; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { + /* + * Not negotiated yet, request service. + */ + ieee80211_ampdu_request(ni, tap); + /* XXX hold frame for reply? */ + } } } +#ifdef IEEE80211_SUPPORT_SUPERG /* - * XXX If we aren't doing AMPDU TX then we /could/ do - * fast-frames encapsulation, however right now this - * output logic doesn't handle that case. + * Check for AMSDU/FF; queue for aggregation * - * So we'll be limited to "fast-frames" xmit for non-11n STA - * and "no fast frames" xmit for 11n STAs. - * It'd be nice to eventually test fast-frames out by - * gracefully falling from failing A-MPDU transmission - * (driver says no, fail to negotiate it with peer) to - * using fast-frames. + * Note: we don't bother trying to do fast frames or + * A-MSDU encapsulation for 802.3 drivers. Now, we + * likely could do it for FF (because it's a magic + * atheros tunnel LLC type) but I don't think we're going + * to really need to. For A-MSDU we'd have to set the + * A-MSDU QoS bit in the wifi header, so we just plain + * can't do it. * - * Note: we can actually put A-MSDU's inside an A-MPDU, - * so hopefully we can figure out how to make that particular - * combination work right. + * Strictly speaking, we could actually /do/ A-MSDU / FF + * with A-MPDU together which for certain circumstances + * is beneficial (eg A-MSDU of TCK ACKs.) However, + * I'll ignore that for now so existing behaviour is maintained. + * Later on it would be good to make "amsdu + ampdu" configurable. */ -#ifdef IEEE80211_SUPPORT_SUPERG - else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { - m = ieee80211_ff_check(ni, m); - if (m == NULL) { - /* NB: any ni ref held on stageq */ - return (0); + else if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + if ((! mcast) && ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: amsdu_check queued frame\n", + __func__); + return (0); + } + } else if ((! mcast) && IEEE80211_ATH_CAP(vap, ni, + IEEE80211_NODE_FF)) { + m = ieee80211_ff_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: ff_check queued frame\n", + __func__); + return (0); + } } } #endif /* IEEE80211_SUPPORT_SUPERG */ @@ -1229,6 +1246,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, ieee80211_seq seqno; int meshhdrsize, meshae; uint8_t *qos; + int is_amsdu = 0; IEEE80211_TX_LOCK_ASSERT(ic); @@ -1383,9 +1401,19 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, } else { #ifdef IEEE80211_SUPPORT_SUPERG /* - * Aggregated frame. + * Aggregated frame. Check if it's for AMSDU or FF. + * + * XXX TODO: IEEE80211_NODE_AMSDU* isn't implemented + * anywhere for some reason. But, since 11n requires + * AMSDU RX, we can just assume "11n" == "AMSDU". */ - m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, "%s: called; M_FF\n", __func__); + if (ieee80211_amsdu_tx_ok(ni)) { + m = ieee80211_amsdu_encap(vap, m, hdrspace + meshhdrsize, key); + is_amsdu = 1; + } else { + m = ieee80211_ff_encap(vap, m, hdrspace + meshhdrsize, key); + } if (m == NULL) #endif goto bad; @@ -1521,6 +1549,13 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, qos[1] = 0; wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + /* + * If this is an A-MSDU then ensure we set the + * relevant field. + */ + if (is_amsdu) + qos[0] |= IEEE80211_QOS_AMSDU; + if ((m->m_flags & M_AMPDU_MPDU) == 0) { /* * NB: don't assign a sequence # to potential @@ -1544,6 +1579,14 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); + + /* + * XXX TODO: we shouldn't allow EAPOL, etc that would + * be forced to be non-QoS traffic to be A-MSDU encapsulated. + */ + if (is_amsdu) + printf("%s: XXX ERROR: is_amsdu set; not QoS!\n", + __func__); } diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 16d801cf0fcc..9c1707b90a82 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -1703,12 +1703,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ieee80211_setup_basic_htrates(ni, htinfo); ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); - } else { -#ifdef IEEE80211_SUPPORT_SUPERG - if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_ATH)) - ieee80211_ff_node_init(ni); -#endif } + /* * Configure state now that we are associated. * diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c index 8be098a0fcac..1879d6204c2b 100644 --- a/sys/net80211/ieee80211_superg.c +++ b/sys/net80211/ieee80211_superg.c @@ -99,18 +99,22 @@ ieee80211_superg_attach(struct ieee80211com *ic) { struct ieee80211_superg *sg; - if (ic->ic_caps & IEEE80211_C_FF) { - sg = (struct ieee80211_superg *) IEEE80211_MALLOC( - sizeof(struct ieee80211_superg), M_80211_VAP, - IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); - if (sg == NULL) { - printf("%s: cannot allocate SuperG state block\n", - __func__); - return; - } - ic->ic_superg = sg; + sg = (struct ieee80211_superg *) IEEE80211_MALLOC( + sizeof(struct ieee80211_superg), M_80211_VAP, + IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); + if (sg == NULL) { + printf("%s: cannot allocate SuperG state block\n", + __func__); + return; } - ieee80211_ffagemax = msecs_to_ticks(150); + ic->ic_superg = sg; + + /* + * Default to not being so aggressive for FF/AMSDU + * aging, otherwise we may hold a frame around + * for way too long before we expire it out. + */ + ieee80211_ffagemax = msecs_to_ticks(2); } void @@ -353,16 +357,15 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, goto bad; } m1->m_nextpkt = NULL; + /* - * Include fast frame headers in adjusting header layout. + * Adjust to include 802.11 header requirement. */ KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!")); ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t)); - m1 = ieee80211_mbuf_adjust(vap, - hdrspace + sizeof(struct llc) + sizeof(uint32_t) + 2 + - sizeof(struct ether_header), - key, m1); + m1 = ieee80211_mbuf_adjust(vap, hdrspace, key, m1); if (m1 == NULL) { + printf("%s: failed initial mbuf_adjust\n", __func__); /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ m_freem(m2); goto bad; @@ -370,17 +373,15 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, /* * Copy second frame's Ethernet header out of line - * and adjust for encapsulation headers. Note that - * we make room for padding in case there isn't room + * and adjust for possible padding in case there isn't room * at the end of first frame. */ KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); - m2 = ieee80211_mbuf_adjust(vap, - ATH_FF_MAX_HDR_PAD + sizeof(struct ether_header), - NULL, m2); + m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2); if (m2 == NULL) { /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + printf("%s: failed second \n", __func__); goto bad; } @@ -418,9 +419,8 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, } /* - * Now, stick 'em together and prepend the tunnel headers; - * first the Atheros tunnel header (all zero for now) and - * then a special fast frame LLC. + * A-MSDU's are just appended; the "I'm A-MSDU!" bit is in the + * QoS header. * * XXX optimize by prepending together */ @@ -454,6 +454,7 @@ ieee80211_ff_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, return m1; bad: + vap->iv_stats.is_ff_encapfail++; if (m1 != NULL) m_freem(m1); if (m2 != NULL) @@ -461,6 +462,114 @@ bad: return NULL; } +/* + * A-MSDU encapsulation. + * + * This assumes just two frames for now, since we're borrowing the + * same queuing code and infrastructure as fast-frames. + * + * There must be two packets chained with m_nextpkt. + * We do header adjustment for each, and then concatenate the mbuf chains + * to form a single frame for transmission. + */ +struct mbuf * +ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, int hdrspace, + struct ieee80211_key *key) +{ + struct mbuf *m2; + struct ether_header eh1, eh2; + struct mbuf *m; + int pad; + + m2 = m1->m_nextpkt; + if (m2 == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, + "%s: only one frame\n", __func__); + goto bad; + } + m1->m_nextpkt = NULL; + + /* + * Include A-MSDU header in adjusting header layout. + */ + KASSERT(m1->m_len >= sizeof(eh1), ("no ethernet header!")); + ETHER_HEADER_COPY(&eh1, mtod(m1, caddr_t)); + m1 = ieee80211_mbuf_adjust(vap, + hdrspace + sizeof(struct llc) + sizeof(uint32_t) + + sizeof(struct ether_header), + key, m1); + if (m1 == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + m_freem(m2); + goto bad; + } + + /* + * Copy second frame's Ethernet header out of line + * and adjust for encapsulation headers. Note that + * we make room for padding in case there isn't room + * at the end of first frame. + */ + KASSERT(m2->m_len >= sizeof(eh2), ("no ethernet header!")); + ETHER_HEADER_COPY(&eh2, mtod(m2, caddr_t)); + m2 = ieee80211_mbuf_adjust(vap, 4, NULL, m2); + if (m2 == NULL) { + /* NB: ieee80211_mbuf_adjust handles msgs+statistics */ + goto bad; + } + + /* + * Now do tunnel encapsulation. First, each + * frame gets a standard encapsulation. + */ + m1 = ieee80211_ff_encap1(vap, m1, &eh1); + if (m1 == NULL) + goto bad; + m2 = ieee80211_ff_encap1(vap, m2, &eh2); + if (m2 == NULL) + goto bad; + + /* + * Pad leading frame to a 4-byte boundary. If there + * is space at the end of the first frame, put it + * there; otherwise prepend to the front of the second + * frame. We know doing the second will always work + * because we reserve space above. We prefer appending + * as this typically has better DMA alignment properties. + */ + for (m = m1; m->m_next != NULL; m = m->m_next) + ; + pad = roundup2(m1->m_pkthdr.len, 4) - m1->m_pkthdr.len; + if (pad) { + if (M_TRAILINGSPACE(m) < pad) { /* prepend to second */ + m2->m_data -= pad; + m2->m_len += pad; + m2->m_pkthdr.len += pad; + } else { /* append to first */ + m->m_len += pad; + m1->m_pkthdr.len += pad; + } + } + + /* + * Now, stick 'em together. + */ + m->m_next = m2; /* NB: last mbuf from above */ + m1->m_pkthdr.len += m2->m_pkthdr.len; + + vap->iv_stats.is_amsdu_encap++; + + return m1; +bad: + vap->iv_stats.is_amsdu_encapfail++; + if (m1 != NULL) + m_freem(m1); + if (m2 != NULL) + m_freem(m2); + return NULL; +} + + static void ff_transmit(struct ieee80211_node *ni, struct mbuf *m) { @@ -605,6 +714,7 @@ ff_approx_txtime(struct ieee80211_node *ni, struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; uint32_t framelen; + uint32_t frame_time; /* * Approximate the frame length to be transmitted. A swag to add @@ -621,7 +731,21 @@ ff_approx_txtime(struct ieee80211_node *ni, framelen += 24; if (m2 != NULL) framelen += m2->m_pkthdr.len; - return ieee80211_compute_duration(ic->ic_rt, framelen, ni->ni_txrate, 0); + + /* + * For now, we assume non-shortgi, 20MHz, just because I want to + * at least test 802.11n. + */ + if (ni->ni_txrate & IEEE80211_RATE_MCS) + frame_time = ieee80211_compute_duration_ht(framelen, + ni->ni_txrate, + IEEE80211_HT_RC_2_STREAMS(ni->ni_txrate), + 0, /* isht40 */ + 0); /* isshortgi */ + else + frame_time = ieee80211_compute_duration(ic->ic_rt, framelen, + ni->ni_txrate, 0); + return (frame_time); } /* @@ -753,6 +877,30 @@ ieee80211_ff_check(struct ieee80211_node *ni, struct mbuf *m) return mstaged; } +struct mbuf * +ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m) +{ + /* + * XXX TODO: actually enforce the node support + * and HTCAP requirements for the maximum A-MSDU + * size. + */ + + /* First: software A-MSDU transmit? */ + if (! ieee80211_amsdu_tx_ok(ni)) + return (m); + + /* Next - EAPOL? Nope, don't aggregate; we don't QoS encap them */ + if (m->m_flags & (M_EAPOL | M_MCAST | M_BCAST)) + return (m); + + /* Next - needs to be a data frame, non-broadcast, etc */ + if (ETHER_IS_MULTICAST(mtod(m, struct ether_header *)->ether_dhost)) + return (m); + + return (ieee80211_ff_check(ni, m)); +} + void ieee80211_ff_node_init(struct ieee80211_node *ni) { diff --git a/sys/net80211/ieee80211_superg.h b/sys/net80211/ieee80211_superg.h index 4df138632fcb..ced3c9b68243 100644 --- a/sys/net80211/ieee80211_superg.h +++ b/sys/net80211/ieee80211_superg.h @@ -82,6 +82,27 @@ int ieee80211_parse_athparams(struct ieee80211_node *, uint8_t *, void ieee80211_ff_node_init(struct ieee80211_node *); void ieee80211_ff_node_cleanup(struct ieee80211_node *); +static inline int +ieee80211_amsdu_tx_ok(struct ieee80211_node *ni) +{ + + /* First: software A-MSDU transmit? */ + if ((ni->ni_ic->ic_caps & IEEE80211_C_SWAMSDUTX) == 0) + return (0); + + /* Next: does the VAP have AMSDU TX enabled? */ + if ((ni->ni_vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX) == 0) + return (0); + + /* Next: 11n node? (assumed that A-MSDU TX to HT nodes is ok */ + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) + return (0); + + /* ok, we can at least /do/ AMSDU to this node */ + return (1); +} + +struct mbuf * ieee80211_amsdu_check(struct ieee80211_node *ni, struct mbuf *m); struct mbuf *ieee80211_ff_check(struct ieee80211_node *, struct mbuf *); void ieee80211_ff_age(struct ieee80211com *, struct ieee80211_stageq *, int quanta); @@ -122,6 +143,8 @@ ieee80211_ff_age_all(struct ieee80211com *ic, int quanta) struct mbuf *ieee80211_ff_encap(struct ieee80211vap *, struct mbuf *, int, struct ieee80211_key *); +struct mbuf * ieee80211_amsdu_encap(struct ieee80211vap *vap, struct mbuf *m1, + int hdrspace, struct ieee80211_key *key); struct mbuf *ieee80211_ff_decap(struct ieee80211_node *, struct mbuf *); |