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_ */

Reply via email to