wifi: mac80211: add hardware timestamps for RX and TX
authorAvraham Stern <avraham.stern@intel.com>
Wed, 26 Jan 2022 09:15:31 +0000 (11:15 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 22 Jul 2022 12:28:29 +0000 (14:28 +0200)
When the low level driver reports hardware timestamps for frame
TX status or frame RX, pass the timestamps to cfg80211.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/rx.c
net/mac80211/status.c

index 6f856da..e9f8be7 100644 (file)
  * via the usual ieee80211_tx_dequeue).
  */
 
+/**
+ * DOC: HW timestamping
+ *
+ * Timing Measurement and Fine Timing Measurement require accurate timestamps
+ * of the action frames TX/RX and their respective acks.
+ *
+ * To report hardware timestamps for Timing Measurement or Fine Timing
+ * Measurement frame RX, the low level driver should set the SKB's hwtstamp
+ * field to the frame RX timestamp and report the ack TX timestamp in the
+ * ieee80211_rx_status struct.
+ *
+ * Similarly, To report hardware timestamps for Timing Measurement or Fine
+ * Timing Measurement frame TX, the driver should set the SKB's hwtstamp field
+ * to the frame TX timestamp and report the ack RX timestamp in the
+ * ieee80211_tx_status struct.
+ */
 struct device;
 
 /**
@@ -1176,12 +1192,16 @@ struct ieee80211_rate_status {
  * @rates: Mrr stages that were used when sending the packet
  * @n_rates: Number of mrr stages (count of instances for @rates)
  * @free_list: list where processed skbs are stored to be free'd by the driver
+ * @ack_hwtstamp: Hardware timestamp of the received ack in nanoseconds
+ *     Only needed for Timing measurement and Fine timing measurement action
+ *     frames. Only reported by devices that have timestamping enabled.
  */
 struct ieee80211_tx_status {
        struct ieee80211_sta *sta;
        struct ieee80211_tx_info *info;
        struct sk_buff *skb;
        struct ieee80211_rate_status *rates;
+       ktime_t ack_hwtstamp;
        u8 n_rates;
 
        struct list_head *free_list;
@@ -1419,6 +1439,9 @@ enum mac80211_rx_encoding {
  *     (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
  * @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is
  *     needed only for beacons and probe responses that update the scan cache.
+ * @ack_tx_hwtstamp: Hardware timestamp for the ack TX in nanoseconds. Only
+ *     needed for Timing measurement and Fine timing measurement action frames.
+ *     Only reported by devices that have timestamping enabled.
  * @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use
  *     it but can store it and pass it back to the driver for synchronisation
  * @band: the active band when this frame was received
@@ -1452,7 +1475,10 @@ enum mac80211_rx_encoding {
  */
 struct ieee80211_rx_status {
        u64 mactime;
-       u64 boottime_ns;
+       union {
+               u64 boottime_ns;
+               ktime_t ack_tx_hwtstamp;
+       };
        u32 device_timestamp;
        u32 ampdu_reference;
        u32 flag;
index 9054a1e..ef9c2fc 100644 (file)
@@ -3644,7 +3644,11 @@ static ieee80211_rx_result debug_noinline
 ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
-       int sig = 0;
+       struct cfg80211_rx_info info = {
+               .freq = ieee80211_rx_status_to_khz(status),
+               .buf = rx->skb->data,
+               .len = rx->skb->len
+       };
 
        /* skip known-bad action frames and return them in the next handler */
        if (status->rx_flags & IEEE80211_RX_MALFORMED_ACTION_FRM)
@@ -3659,11 +3663,15 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 
        if (ieee80211_hw_check(&rx->local->hw, SIGNAL_DBM) &&
            !(status->flag & RX_FLAG_NO_SIGNAL_VAL))
-               sig = status->signal;
+               info.sig_dbm = status->signal;
+
+       if (ieee80211_is_timing_measurement(rx->skb) ||
+           ieee80211_is_ftm(rx->skb)) {
+               info.rx_tstamp = ktime_to_ns(skb_hwtstamps(rx->skb)->hwtstamp);
+               info.ack_tstamp = ktime_to_ns(status->ack_tx_hwtstamp);
+       }
 
-       if (cfg80211_rx_mgmt_khz(&rx->sdata->wdev,
-                                ieee80211_rx_status_to_khz(status), sig,
-                                rx->skb->data, rx->skb->len, 0)) {
+       if (cfg80211_rx_mgmt_ext(&rx->sdata->wdev, &info)) {
                if (rx->sta)
                        rx->sta->deflink.rx_stats.packets++;
                dev_kfree_skb(rx->skb);
@@ -4758,8 +4766,10 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
        }
 
        if (!consume) {
-               skb = skb_copy(skb, GFP_ATOMIC);
-               if (!skb) {
+               struct skb_shared_hwtstamps *shwt;
+
+               rx->skb = skb_copy(skb, GFP_ATOMIC);
+               if (!rx->skb) {
                        if (net_ratelimit())
                                wiphy_debug(local->hw.wiphy,
                                        "failed to copy skb for %s\n",
@@ -4767,7 +4777,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
                        return true;
                }
 
-               rx->skb = skb;
+               /* skb_copy() does not copy the hw timestamps, so copy it
+                * explicitly
+                */
+               shwt = skb_hwtstamps(rx->skb);
+               shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
        }
 
        if (unlikely(link_sta)) {
index fad457f..8e77fd2 100644 (file)
@@ -624,9 +624,11 @@ ieee80211_sdata_from_skb(struct ieee80211_local *local, struct sk_buff *skb)
 }
 
 static void ieee80211_report_ack_skb(struct ieee80211_local *local,
-                                    struct ieee80211_tx_info *info,
-                                    bool acked, bool dropped)
+                                    struct sk_buff *orig_skb,
+                                    bool acked, bool dropped,
+                                    ktime_t ack_hwtstamp)
 {
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(orig_skb);
        struct sk_buff *skb;
        unsigned long flags;
 
@@ -643,6 +645,19 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
                struct ieee80211_hdr *hdr = (void *)skb->data;
                bool is_valid_ack_signal =
                        !!(info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID);
+               struct cfg80211_tx_status status = {
+                       .cookie = cookie,
+                       .buf = skb->data,
+                       .len = skb->len,
+                       .ack = acked,
+               };
+
+               if (ieee80211_is_timing_measurement(orig_skb) ||
+                   ieee80211_is_ftm(orig_skb)) {
+                       status.tx_tstamp =
+                               ktime_to_ns(skb_hwtstamps(orig_skb)->hwtstamp);
+                       status.ack_tstamp = ktime_to_ns(ack_hwtstamp);
+               }
 
                rcu_read_lock();
                sdata = ieee80211_sdata_from_skb(local, skb);
@@ -662,9 +677,9 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
                                                      is_valid_ack_signal,
                                                      GFP_ATOMIC);
                        else if (ieee80211_is_mgmt(hdr->frame_control))
-                               cfg80211_mgmt_tx_status(&sdata->wdev, cookie,
-                                                       skb->data, skb->len,
-                                                       acked, GFP_ATOMIC);
+                               cfg80211_mgmt_tx_status_ext(&sdata->wdev,
+                                                           &status,
+                                                           GFP_ATOMIC);
                        else
                                pr_warn("Unknown status report in ack skb\n");
 
@@ -681,7 +696,8 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local,
 }
 
 static void ieee80211_report_used_skb(struct ieee80211_local *local,
-                                     struct sk_buff *skb, bool dropped)
+                                     struct sk_buff *skb, bool dropped,
+                                     ktime_t ack_hwtstamp)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        u16 tx_time_est = ieee80211_info_get_tx_time_est(info);
@@ -744,7 +760,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
 
                rcu_read_unlock();
        } else if (info->ack_frame_id) {
-               ieee80211_report_ack_skb(local, info, acked, dropped);
+               ieee80211_report_ack_skb(local, skb, acked, dropped,
+                                        ack_hwtstamp);
        }
 
        if (!dropped && skb->destructor) {
@@ -1038,7 +1055,7 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
                          jiffies + msecs_to_jiffies(10));
        }
 
-       ieee80211_report_used_skb(local, skb, false);
+       ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp);
 
        /* this was a transmitted frame, but now we want to reuse it */
        skb_orphan(skb);
@@ -1201,7 +1218,7 @@ free:
        if (!skb)
                return;
 
-       ieee80211_report_used_skb(local, skb, false);
+       ieee80211_report_used_skb(local, skb, false, status->ack_hwtstamp);
        if (status->free_list)
                list_add_tail(&skb->list, status->free_list);
        else
@@ -1262,8 +1279,9 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
 void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       ktime_t kt = ktime_set(0, 0);
 
-       ieee80211_report_used_skb(local, skb, true);
+       ieee80211_report_used_skb(local, skb, true, kt);
        dev_kfree_skb_any(skb);
 }
 EXPORT_SYMBOL(ieee80211_free_txskb);