From e6de4b10ad95033408bec5b47297939eb8b7ade8 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Thu, 19 Mar 2020 22:12:10 +0100 Subject: [PATCH] frame-xchg: Add facility to keep retransmitting after ACK In some cases a P2P peer will ACK our frame but not reply on the first attempt, and other implementations seem to handle this by going back to retransmitting the frame at a high rate until it gets ACKed again, at which point they will again give the peer a longer time to tx the response frame. Implement the same logic here by adding a retries_on_ack parameter that takes the number of additional times we want to restart the normal retransmit counter after we received no response frame on the first attempt. So passing 0 maintains the current behaviour, 1 for 1 extra attempt, etc. In effect we may retransmit a frame about 15 * (retry_on_ack + 1) * times. The kernel/driver retransmits a frame a number of times if there's no ACK (I've seen about 20 normally) at a high frequency, if that fails we retry the whole process 15 times inside frame-xchg.c and if we still get no ACK at any point, we give up. If we do get an ACK, we wait for a response frame and if we don't get that we will optionally reset the retry counter and restart the whole thing retry_on_ack times. --- src/frame-xchg.c | 62 +++++++++++++++++++++++++++++++++--------------- src/frame-xchg.h | 4 ++-- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/frame-xchg.c b/src/frame-xchg.c index d69fff7c..26163e61 100644 --- a/src/frame-xchg.c +++ b/src/frame-xchg.c @@ -107,6 +107,7 @@ struct frame_xchg_data { unsigned int retry_cnt; unsigned int retry_interval; unsigned int resp_timeout; + unsigned int retries_on_ack; bool in_frame_cb : 1; bool stale : 1; }; @@ -726,6 +727,9 @@ static bool frame_watch_remove_by_handler(uint64_t wdev_id, uint32_t group_id, } static void frame_xchg_tx_retry(struct frame_xchg_data *fx); +static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu, + const void *body, size_t body_len, + int rssi, void *user_data); static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, const void *body, size_t body_len, int rssi, void *user_data); @@ -805,12 +809,20 @@ static void frame_xchg_timeout_cb(struct l_timeout *timeout, frame_xchg_tx_retry(fx); } -static void frame_xchg_resp_timeout_cb(struct l_timeout *timeout, +static void frame_xchg_listen_end_cb(struct l_timeout *timeout, void *user_data) { struct frame_xchg_data *fx = user_data; - frame_xchg_done(fx, 0); + if (!fx->retries_on_ack) { + frame_xchg_done(fx, 0); + return; + } + + l_timeout_remove(fx->timeout); + fx->retries_on_ack--; + fx->retry_cnt = 0; + frame_xchg_tx_retry(fx); } static void frame_xchg_tx_status(struct frame_xchg_data *fx, bool acked) @@ -850,17 +862,20 @@ static void frame_xchg_tx_status(struct frame_xchg_data *fx, bool acked) fx->have_cookie = false; l_debug("Processing an early frame"); - frame_xchg_resp_cb(fx->early_frame.mpdu, fx->early_frame.body, - fx->early_frame.body_len, - fx->early_frame.rssi, fx); - frame_xchg_done(fx, 0); + if (frame_xchg_resp_handle(fx->early_frame.mpdu, + fx->early_frame.body, + fx->early_frame.body_len, + fx->early_frame.rssi, fx)) + return; + + frame_xchg_listen_end_cb(NULL, fx); return; } /* Txed frame ACKed, listen for response frames */ fx->timeout = l_timeout_create_ms(fx->resp_timeout, - frame_xchg_resp_timeout_cb, fx, + frame_xchg_listen_end_cb, fx, frame_xchg_timeout_destroy); } @@ -946,9 +961,9 @@ static void frame_xchg_tx_retry(struct frame_xchg_data *fx) fx->retry_cnt++; } -static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, - const void *body, size_t body_len, - int rssi, void *user_data) +static bool frame_xchg_resp_handle(const struct mmpdu_header *mpdu, + const void *body, size_t body_len, + int rssi, void *user_data) { struct frame_xchg_data *fx = user_data; const struct l_queue_entry *entry; @@ -957,10 +972,10 @@ static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, l_debug(""); if (memcmp(mpdu->address_1, fx->tx_mpdu->address_2, 6)) - return; + return false; if (memcmp(mpdu->address_2, fx->tx_mpdu->address_1, 6)) - return; + return false; /* * Is the received frame's BSSID same as the transmitted frame's @@ -970,7 +985,7 @@ static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, */ if (memcmp(mpdu->address_3, fx->tx_mpdu->address_3, 6) && !util_mem_is_zero(mpdu->address_3, 6)) - return; + return false; for (entry = l_queue_get_entries(fx->rx_watches); entry; entry = entry->next) { @@ -994,18 +1009,18 @@ static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, * to just exit without touching anything. */ if (!fx->in_frame_cb) - return; + return true; fx->in_frame_cb = false; if (done || fx->stale) { fx->cb = NULL; frame_xchg_done(fx, 0); - return; + return true; } } - return; + return false; early_frame: /* @@ -1016,13 +1031,21 @@ early_frame: * Save the response frame to be processed in the Tx done callback. */ if (fx->early_frame.mpdu) - return; + return false; hdr_len = (const uint8_t *) body - (const uint8_t *) mpdu; fx->early_frame.mpdu = l_memdup(mpdu, body_len + hdr_len); fx->early_frame.body = (const uint8_t *) fx->early_frame.mpdu + hdr_len; fx->early_frame.body_len = body_len; fx->early_frame.rssi = rssi; + return false; +} + +static void frame_xchg_resp_cb(const struct mmpdu_header *mpdu, + const void *body, size_t body_len, + int rssi, void *user_data) +{ + frame_xchg_resp_handle(mpdu, body, body_len, rssi, user_data); } static bool frame_xchg_match(const void *a, const void *b) @@ -1050,8 +1073,8 @@ static bool frame_xchg_match(const void *a, const void *b) */ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, unsigned int retry_interval, unsigned int resp_timeout, - uint32_t group_id, frame_xchg_cb_t cb, void *user_data, - va_list resp_args) + unsigned int retries_on_ack, uint32_t group_id, + frame_xchg_cb_t cb, void *user_data, va_list resp_args) { struct frame_xchg_data *fx; size_t frame_len; @@ -1095,6 +1118,7 @@ void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, fx->freq = freq; fx->retry_interval = retry_interval; fx->resp_timeout = resp_timeout; + fx->retries_on_ack = retries_on_ack; fx->cb = cb; fx->user_data = user_data; fx->group_id = group_id; diff --git a/src/frame-xchg.h b/src/frame-xchg.h index 1855492c..93d528ae 100644 --- a/src/frame-xchg.h +++ b/src/frame-xchg.h @@ -45,6 +45,6 @@ bool frame_watch_wdev_remove(uint64_t wdev_id); void frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq, unsigned int retry_interval, unsigned int resp_timeout, - uint32_t group_id, frame_xchg_cb_t cb, void *user_data, - va_list resp_args); + unsigned int retries_on_ack, uint32_t group_id, + frame_xchg_cb_t cb, void *user_data, va_list resp_args); void frame_xchg_stop(uint64_t wdev_id);