When processing an ADDBA request, iwm(4) runs a task which sends a command to the firmware and waits for confirmation. This command can fail and there's currently no way we can recover from such an error.
With the diff below, drivers can return EBUSY from their ic_ampdu_rx_start() handler which tells the stack not to send a confirmation just yet. The stack provides functions which the driver can call to accept or refuse the request. There is no functional change yet. This just shuffles code around so drivers may insert themselves into the process. ok? Index: ieee80211_input.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v retrieving revision 1.179 diff -u -p -r1.179 ieee80211_input.c --- ieee80211_input.c 20 Sep 2016 13:24:42 -0000 1.179 +++ ieee80211_input.c 20 Sep 2016 19:23:47 -0000 @@ -2424,8 +2424,9 @@ ieee80211_recv_addba_req(struct ieee8021 const struct ieee80211_frame *wh; const u_int8_t *frm; struct ieee80211_rx_ba *ba; - u_int16_t params, ssn, bufsz, timeout, status; + u_int16_t params, ssn, bufsz, timeout; u_int8_t token, tid; + int err; if (!(ni->ni_flags & IEEE80211_NODE_HT)) { DPRINTF(("received ADDBA req from non-HT STA %s\n", @@ -2467,32 +2468,33 @@ ieee80211_recv_addba_req(struct ieee8021 ieee80211_ba_move_window(ic, ni, tid, ssn); return; } + + /* If the driver does not support A-MPDU, refuse the request. */ + if (ic->ic_ampdu_rx_start == NULL) + goto refuse; + /* if PBAC required but RA does not support it, refuse request */ if ((ic->ic_flags & IEEE80211_F_PBAR) && (!(ni->ni_flags & IEEE80211_NODE_MFP) || - !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))) { - status = IEEE80211_STATUS_REFUSED; - goto resp; - } + !(ni->ni_rsncaps & IEEE80211_RSNCAP_PBAC))) + goto refuse; /* * If the TID for which the Block Ack agreement is requested is * configured with a no-ACK policy, refuse the agreement. */ - if (ic->ic_tid_noack & (1 << tid)) { - status = IEEE80211_STATUS_REFUSED; - goto resp; - } + if (ic->ic_tid_noack & (1 << tid)) + goto refuse; + /* check that we support the requested Block Ack Policy */ if (!(ic->ic_htcaps & IEEE80211_HTCAP_DELAYEDBA) && - !(params & IEEE80211_ADDBA_BA_POLICY)) { - status = IEEE80211_STATUS_INVALID_PARAM; - goto resp; - } + !(params & IEEE80211_ADDBA_BA_POLICY)) + goto refuse; /* setup Block Ack agreement */ ba->ba_state = IEEE80211_BA_INIT; ba->ba_timeout_val = timeout * IEEE80211_DUR_TU; ba->ba_ni = ni; + ba->ba_token = token; timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba); timeout_set(&ba->ba_gap_to, ieee80211_input_ba_gap_timeout, ba); ba->ba_winsize = bufsz; @@ -2506,31 +2508,61 @@ ieee80211_recv_addba_req(struct ieee8021 /* allocate and setup our reordering buffer */ ba->ba_buf = malloc(IEEE80211_BA_MAX_WINSZ * sizeof(*ba->ba_buf), M_DEVBUF, M_NOWAIT | M_ZERO); - if (ba->ba_buf == NULL) { - status = IEEE80211_STATUS_REFUSED; - goto resp; - } + if (ba->ba_buf == NULL) + goto refuse; + ba->ba_head = 0; /* notify drivers of this new Block Ack agreement */ - if (ic->ic_ampdu_rx_start == NULL || - ic->ic_ampdu_rx_start(ic, ni, tid) != 0) { + err = ic->ic_ampdu_rx_start(ic, ni, tid); + if (err == EBUSY) { + /* driver will accept or refuse agreement when done */ + return; + } else if (err) { /* driver failed to setup, rollback */ - free(ba->ba_buf, M_DEVBUF, 0); - ba->ba_buf = NULL; - status = IEEE80211_STATUS_REFUSED; - goto resp; - } + ieee80211_addba_req_refuse(ic, ni, tid); + } else + ieee80211_addba_req_accept(ic, ni, tid); + return; + + refuse: + /* MLME-ADDBA.response */ + IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA, + IEEE80211_ACTION_ADDBA_RESP, + IEEE80211_STATUS_REFUSED << 16 | token << 8 | tid); +} + +void +ieee80211_addba_req_accept(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; + ba->ba_state = IEEE80211_BA_AGREED; ic->ic_stats.is_ht_rx_ba_agreements++; /* start Block Ack inactivity timer */ if (ba->ba_timeout_val != 0) timeout_add_usec(&ba->ba_to, ba->ba_timeout_val); - status = IEEE80211_STATUS_SUCCESS; - resp: + + /* MLME-ADDBA.response */ + IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA, + IEEE80211_ACTION_ADDBA_RESP, + IEEE80211_STATUS_SUCCESS << 16 | ba->ba_token << 8 | tid); +} + +void +ieee80211_addba_req_refuse(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; + + free(ba->ba_buf, M_DEVBUF, 0); + ba->ba_buf = NULL; + /* MLME-ADDBA.response */ IEEE80211_SEND_ACTION(ic, ni, IEEE80211_CATEG_BA, - IEEE80211_ACTION_ADDBA_RESP, status << 16 | token << 8 | tid); + IEEE80211_ACTION_ADDBA_RESP, + IEEE80211_STATUS_REFUSED << 16 | ba->ba_token << 8 | tid); } /*- Index: ieee80211_node.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v retrieving revision 1.62 diff -u -p -r1.62 ieee80211_node.h --- ieee80211_node.h 20 Sep 2016 13:24:42 -0000 1.62 +++ ieee80211_node.h 20 Sep 2016 19:13:35 -0000 @@ -153,6 +153,8 @@ struct ieee80211_rx_ba { uint16_t ba_missedsn; /* Window moves forward after this many frames have missed it. */ #define IEEE80211_BA_MAX_WINMISS 8 + + uint8_t ba_token; }; /* Index: ieee80211_proto.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_proto.h,v retrieving revision 1.41 diff -u -p -r1.41 ieee80211_proto.h --- ieee80211_proto.h 5 Jan 2016 18:41:16 -0000 1.41 +++ ieee80211_proto.h 20 Sep 2016 19:23:25 -0000 @@ -161,5 +161,10 @@ extern int ieee80211_addba_request(struc struct ieee80211_node *, u_int16_t, u_int8_t); extern void ieee80211_delba_request(struct ieee80211com *, struct ieee80211_node *, u_int16_t, u_int8_t, u_int8_t); +extern void ieee80211_addba_req_accept(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +extern void ieee80211_addba_req_refuse(struct ieee80211com *, + struct ieee80211_node *, uint8_t); + #endif /* _NET80211_IEEE80211_PROTO_H_ */