Index: sys/net80211/ieee80211_freebsd.h =================================================================== --- sys/net80211/ieee80211_freebsd.h +++ sys/net80211/ieee80211_freebsd.h @@ -272,6 +272,7 @@ #define time_before_eq(a,b) time_after_eq(b,a) struct mbuf *ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen); +struct mbuf *ieee80211_mbuf_defrag(struct mbuf *, int, int); /* tx path usage */ #define M_ENCAP M_PROTO1 /* 802.11 encap done */ Index: sys/net80211/ieee80211_freebsd.c =================================================================== --- sys/net80211/ieee80211_freebsd.c +++ sys/net80211/ieee80211_freebsd.c @@ -384,6 +384,41 @@ return m; } +struct mbuf * +ieee80211_mbuf_defrag(struct mbuf *m0, int reserved, int maxfrags) +{ + struct mbuf *m; + int curfrags; + + curfrags = 0; + for (m = m0; m != NULL; m = m->m_next) + curfrags++; + + if (curfrags <= maxfrags) + return (m0); /* Nothing to do. */ + + /* Do not free the reserved space. */ + M_PREPEND(m0, reserved, M_NOWAIT); + + /* Workaround m_collapse(9) limitations. */ + if (maxfrags == 1 || + (maxfrags == 2 && (m0->m_pkthdr.len - m0->m_len) > MCLBYTES)) + m = m_defrag(m0, M_NOWAIT); + else + m = m_collapse(m0, M_NOWAIT, maxfrags); + + if (m == NULL) { + m_freem(m0); + return (NULL); + } + m0 = m; + + /* Revert modifications made by M_PREPEND(). */ + m_adj(m0, reserved); + + return (m0); +} + #ifndef __NO_STRICT_ALIGNMENT /* * Re-align the payload in the mbuf. This is mainly used (right now) Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c +++ sys/net80211/ieee80211_output.c @@ -1080,7 +1080,9 @@ struct ieee80211_key *key, struct mbuf *m) { #define TO_BE_RECLAIMED (sizeof(struct ether_header) - sizeof(struct llc)) - int needed_space = vap->iv_ic->ic_headroom + hdrsize; + struct ieee80211com *ic = vap->iv_ic; + int needed_space = ic->ic_headroom + hdrsize; + int reserved_space; if (key != NULL) { /* XXX belongs in crypto code? */ @@ -1111,7 +1113,8 @@ * 802.11 header and any crypto header. */ /* XXX check trailing space and copy instead? */ - if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) { + reserved_space = needed_space - TO_BE_RECLAIMED; + if (M_LEADINGSPACE(m) < reserved_space) { struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type); if (n == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, @@ -1148,6 +1151,16 @@ n->m_next = m; m = n; } + + if (ic->ic_seglimit != 0) { + /* Adjust length of the mbuf chain. */ + m = ieee80211_mbuf_defrag(m, reserved_space, ic->ic_seglimit); + if (m == NULL) { + vap->iv_stats.is_tx_nobuf++; + return NULL; + } + } + return m; #undef TO_BE_RECLAIMED } Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h +++ sys/net80211/ieee80211_var.h @@ -164,6 +164,7 @@ uint16_t ic_lintval; /* listen interval */ uint16_t ic_holdover; /* PM hold over duration */ uint16_t ic_txpowlimit; /* global tx power limit */ + uint16_t ic_seglimit; /* maximum scatter/gather */ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; /*