From 0ea10177f50e89e54e8f8019b12586998f16a594 Mon Sep 17 00:00:00 2001 From: Yu Wang Date: Fri, 2 Jun 2017 13:40:52 +0800 Subject: [PATCH 1/2] qcacld-3.0: add device attribute 'tsf' It will show the current host time and the corresponding tsf when cat '/sys/class/net//tsf' in the following format: Change-Id: I91332da950840433cf1b7e71f4add2249c8c21c4 CRs-Fixed: 2057693 --- core/hdd/src/wlan_hdd_tsf.c | 118 ++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/core/hdd/src/wlan_hdd_tsf.c b/core/hdd/src/wlan_hdd_tsf.c index 89658935acd5..53dc753b11ea 100644 --- a/core/hdd/src/wlan_hdd_tsf.c +++ b/core/hdd/src/wlan_hdd_tsf.c @@ -494,6 +494,34 @@ static inline int hdd_64bit_plus(uint64_t x, int64_t y, uint64_t *ret) return 0; } +static inline int hdd_uint64_plus(uint64_t x, uint64_t y, uint64_t *ret) +{ + if (!ret) + return -EINVAL; + + if (x > (U64_MAX - y)) { + *ret = 0; + return -EINVAL; + } + + *ret = x + y; + return 0; +} + +static inline int hdd_uint64_minus(uint64_t x, uint64_t y, uint64_t *ret) +{ + if (!ret) + return -EINVAL; + + if (x < y) { + *ret = 0; + return -EINVAL; + } + + *ret = x - y; + return 0; +} + static inline int32_t hdd_get_hosttime_from_targettime( hdd_adapter_t *adapter, uint64_t target_time, uint64_t *host_time) @@ -532,6 +560,37 @@ static inline int32_t hdd_get_hosttime_from_targettime( return ret; } +static inline int32_t hdd_get_targettime_from_hosttime( + hdd_adapter_t *adapter, uint64_t host_time, + uint64_t *target_time) +{ + int32_t ret = -EINVAL; + bool in_cap_state; + + if (!adapter || host_time == 0) + return ret; + + in_cap_state = hdd_tsf_is_in_cap(adapter); + if (in_cap_state) + qdf_spin_lock_bh(&adapter->host_target_sync_lock); + + if (host_time < adapter->last_host_time) + ret = hdd_uint64_minus(adapter->last_target_time, + (adapter->last_host_time - host_time) / + HOST_TO_TARGET_TIME_RATIO, + target_time); + else + ret = hdd_uint64_plus(adapter->last_target_time, + (host_time - adapter->last_host_time) / + HOST_TO_TARGET_TIME_RATIO, + target_time); + + if (in_cap_state) + qdf_spin_unlock_bh(&adapter->host_target_sync_lock); + + return ret; +} + static inline uint64_t hdd_get_monotonic_host_time(void) { struct timespec ts; @@ -540,6 +599,53 @@ static inline uint64_t hdd_get_monotonic_host_time(void) return timespec_to_ns(&ts); } +static ssize_t __hdd_wlan_tsf_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + hdd_station_ctx_t *hdd_sta_ctx; + hdd_adapter_t *adapter; + ssize_t size; + uint64_t host_time, target_time; + + struct net_device *net_dev = container_of(dev, struct net_device, dev); + + adapter = (hdd_adapter_t *)(netdev_priv(net_dev)); + if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC) + return scnprintf(buf, PAGE_SIZE, "Invalid device\n"); + + if (!hdd_get_th_sync_status(adapter)) + return scnprintf(buf, PAGE_SIZE, + "TSF sync is not initialized\n"); + + hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); + if (eConnectionState_Associated != hdd_sta_ctx->conn_info.connState) + return scnprintf(buf, PAGE_SIZE, "NOT connected\n"); + + host_time = hdd_get_monotonic_host_time(); + if (hdd_get_targettime_from_hosttime(adapter, host_time, + &target_time)) + size = scnprintf(buf, PAGE_SIZE, "Invalid timestamp\n"); + else + size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu %pM\n", + buf, target_time, host_time, + hdd_sta_ctx->conn_info.bssId.bytes); + return size; +} + +static ssize_t hdd_wlan_tsf_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + cds_ssr_protect(__func__); + ret = __hdd_wlan_tsf_show(dev, attr, buf); + cds_ssr_unprotect(__func__); + + return ret; +} + +static DEVICE_ATTR(tsf, 0400, hdd_wlan_tsf_show, NULL); + static void hdd_capture_tsf_timer_expired_handler(void *arg) { uint32_t tsf_op_resp; @@ -589,6 +695,7 @@ static enum hdd_tsf_op_result hdd_tsf_sync_init(hdd_adapter_t *adapter) { QDF_STATUS ret; hdd_context_t *hddctx; + struct net_device *net_dev; if (!adapter) return HDD_TSF_OP_FAIL; @@ -622,6 +729,9 @@ static enum hdd_tsf_op_result hdd_tsf_sync_init(hdd_adapter_t *adapter) goto fail; } + net_dev = adapter->dev; + if (net_dev) + device_create_file(&net_dev->dev, &dev_attr_tsf); hdd_set_th_sync_status(adapter, true); return HDD_TSF_OP_SUCC; @@ -634,6 +744,7 @@ static enum hdd_tsf_op_result hdd_tsf_sync_deinit(hdd_adapter_t *adapter) { QDF_STATUS ret; hdd_context_t *hddctx; + struct net_device *net_dev; if (!adapter) return HDD_TSF_OP_FAIL; @@ -645,6 +756,13 @@ static enum hdd_tsf_op_result hdd_tsf_sync_deinit(hdd_adapter_t *adapter) hdd_set_th_sync_status(adapter, false); + net_dev = adapter->dev; + if (net_dev) { + struct device *dev = &net_dev->dev; + + device_remove_file(dev, &dev_attr_tsf); + } + ret = qdf_mc_timer_destroy(&adapter->host_target_sync_timer); if (ret != QDF_STATUS_SUCCESS) hdd_err("Failed to destroy timer, ret: %d", ret); From 70757d54d113fea7b1405c312e1d0a6fcebf321f Mon Sep 17 00:00:00 2001 From: Yu Wang Date: Thu, 8 Jun 2017 12:15:42 +0800 Subject: [PATCH 2/2] qcacld-3.0: fix a potential spinlock lockup issue When time stamping RX packets, spinlock host_target_sync_lock will be hold if it's now in TSF capturing state; if TSF-captured IRQ happened at this moment, it will also try to get host_target_sync_lock; if it's handled on the same CPU, lockup will happen. In the use-case, to update the current host time, it's no need to get the spinlock in this TSF-captured IRQ context, so move host-time updating out of the lock. Change-Id: I87205d5935bd2063c80ce7cf767cbc36dde55236 CRs-Fixed: 2057693 --- core/hdd/src/wlan_hdd_tsf.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/hdd/src/wlan_hdd_tsf.c b/core/hdd/src/wlan_hdd_tsf.c index 53dc753b11ea..edb8a7557e1b 100644 --- a/core/hdd/src/wlan_hdd_tsf.c +++ b/core/hdd/src/wlan_hdd_tsf.c @@ -422,11 +422,21 @@ static void hdd_update_timestamp(hdd_adapter_t *adapter, if (!adapter) return; + /* host time is updated in IRQ context, it's always before target time, + * and so no need to try update last_host_time at present; + * since the interval of capturing TSF + * (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC) is long enough, host and target + * time are updated in pairs, and one by one, we can return here to + * avoid requiring spin lock, and to speed up the IRQ processing. + */ + if (host_time > 0) { + adapter->cur_host_time = host_time; + return; + } + qdf_spin_lock_bh(&adapter->host_target_sync_lock); if (target_time > 0) adapter->cur_target_time = target_time; - if (host_time > 0) - adapter->cur_host_time = host_time; sync_status = hdd_check_timestamp_status(adapter->last_target_time, adapter->last_host_time,