From faff7aaa5abbd90893469aae842c74c9474ea3f4 Mon Sep 17 00:00:00 2001 From: Star Chang Date: Mon, 18 Jul 2022 03:43:57 +0000 Subject: wlan_ptracker: Initial version of driver. 1. support tp monitor 2. support event notifier 3. support scenes fsm 4. support dynamic twt setup Bug: 253348062 Test: SST-stability/WiFi regression/WiFi performance Test pass Signed-off-by: Star Chang Change-Id: I9e6df816c55850e03d7167122f658dad8b77c326 --- BUILD.bazel | 20 +++ Kconfig | 16 ++ Makefile | 32 ++++ core.h | 35 +++++ debug.h | 37 +++++ debugfs.c | 194 ++++++++++++++++++++++++ debugfs.h | 58 +++++++ dynamic_twt_manager.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++ dynamic_twt_manager.h | 83 +++++++++++ main.c | 131 ++++++++++++++++ notifier.c | 105 +++++++++++++ notifier.h | 33 ++++ scenes_fsm.c | 258 ++++++++++++++++++++++++++++++++ scenes_fsm.h | 68 +++++++++ tp_monitor.c | 219 +++++++++++++++++++++++++++ tp_monitor.h | 44 ++++++ wlan_ptracker_client.h | 37 +++++ 17 files changed, 1768 insertions(+) create mode 100644 BUILD.bazel create mode 100644 Kconfig create mode 100644 Makefile create mode 100644 core.h create mode 100644 debug.h create mode 100644 debugfs.c create mode 100644 debugfs.h create mode 100644 dynamic_twt_manager.c create mode 100644 dynamic_twt_manager.h create mode 100644 main.c create mode 100644 notifier.c create mode 100644 notifier.h create mode 100644 scenes_fsm.c create mode 100644 scenes_fsm.h create mode 100644 tp_monitor.c create mode 100644 tp_monitor.h create mode 100644 wlan_ptracker_client.h diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..2f49773 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,20 @@ +# NOTE: THIS FILE IS EXPERIMENTAL FOR THE BAZEL MIGRATION AND NOT USED FOR +# YOUR BUILDS CURRENTLY. +# +# It is not yet the source of truth for your build. If you're looking to modify +# the build file, modify the Android.bp file instead. Do *not* modify this file +# unless you have coordinated with the team managing the Soong to Bazel +# migration. + +load("//build/kleaf:kernel.bzl", "kernel_module") + +kernel_module( + name = "wlan_ptracker.cloudripper", + outs = [ + "wlan_ptracker.ko", + ], + kernel_build = "//private/gs-google:cloudripper", + visibility = [ + "//private/gs-google:__pkg__", + ], +) diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..24d6608 --- /dev/null +++ b/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# WiFi Performance Tracker Driver +# + +config WLAN_PTRACKER + bool "WiFi Performance Tracker Driver" + help + WiFi Performance Tracker support. + default y + +config DYNAMIC_TWT_SUPPORT + bool "Dynamic TWT Setup Support " + help + WiFi Performance Tracker support dynamic TWT setup. + default y diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7e3342f --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for WiFi performance tracker driver +# + +obj-$(CONFIG_WLAN_PTRACKER) += wlan_ptracker.o + +# common +wlan_ptracker-$(CONFIG_WLAN_PTRACKER) += main.o tp_monitor.o +wlan_ptracker-$(CONFIG_WLAN_PTRACKER) += notifier.o +wlan_ptracker-$(CONFIG_WLAN_PTRACKER) += scenes_fsm.o + +# debugfs +wlan_ptracker-$(CONFIG_DEBUG_FS) += debugfs.o + +# dynamic twt setup +wlan_ptracker-$(CONFIG_DYNAMIC_TWT_SETUP) += dynamic_twt_manager.o + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build +M ?= $(shell pwd) + +ifeq ($(CONFIG_WLAN_PTRACKER),) +KBUILD_OPTIONS += CONFIG_WLAN_PTRACKER=m +KBUILD_OPTIONS += CONFIG_DYNAMIC_TWT_SETUP=y +endif + +EXTRA_CFLAGS += -I$(KERNEL_SRC)/../google-modules/wlan/wlan_ptracker + +ccflags-y := $(EXTRA_CFLAGS) + +modules modules_install clean: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $(KBUILD_OPTIONS) W=1 $(@) diff --git a/core.h b/core.h new file mode 100644 index 0000000..ae5b988 --- /dev/null +++ b/core.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef _WLAN_PTRACKER_CORE_H +#define _WLAN_PTRACKER_CORE_H + +#include "debugfs.h" +#include "debug.h" +#include "tp_monitor.h" +#include "notifier.h" +#include "scenes_fsm.h" +#include "wlan_ptracker_client.h" +#include "dynamic_twt_manager.h" + +#define DSCP_MASK 0xfc +#define DSCP_MAX (DSCP_MASK + 1) +#define DSCP_SHIFT 2 +#define DSCP_MAP_MAX 10 + +struct wlan_ptracker_core { + struct device device; + struct tp_monitor_stats tp; + struct wlan_ptracker_notifier notifier; + struct wlan_ptracker_debugfs debugfs; + struct wlan_ptracker_fsm fsm; + struct net_device *dev; + struct wlan_ptracker_client *client; + u8 dscp_to_ac[DSCP_MAX]; +}; +#endif /* _WLAN_PTRACKER_CORE_H */ diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..fd44e7d --- /dev/null +++ b/debug.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef _WLAN_PTRACKER_DEBUG_H +#define _WLAN_PTRACKER_DEBUG_H + +#define PTRACKER_PREFIX "wlan_ptracker" + +#define ptracker_err(core, fmt, ...) \ + do { \ + dev_err(&core->device, fmt, ##__VA_ARGS__); \ + } while (0) + +#define ptracker_info(core, fmt, ...) \ + do { \ + dev_info(&core->device, fmt, ##__VA_ARGS__); \ + } while (0) + +#define ptracker_dbg(core, fmt, ...) \ + do { \ + dev_dbg(&core->device, fmt, ##__VA_ARGS__); \ + } while (0) + +#ifdef TP_DEBUG +#define tp_info(tp, fmt, ...) \ + do { \ + if ((tp)->debug && (tp)->dev) \ + dev_info(tp->dev->dev, fmt, ##__VA_ARGS__); \ + } while (0) +#else +#define tp_info(tp, fmt, ...) +#endif + +#endif /* _WLAN_PTRACKER_DEBUG_H */ diff --git a/debugfs.c b/debugfs.c new file mode 100644 index 0000000..3cf4dc4 --- /dev/null +++ b/debugfs.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for WiFi Performance Tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include +#include +#include +#include +#include +#include "core.h" +#include "debugfs.h" + +static const char *const state2str[WLAN_SCENE_MAX] = { + "Idle", "Web", "Youtube", "Low latency", "Throughput" +}; + +#define READ_BUF_SIZE 1024 +static ssize_t action_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) +{ + struct wlan_ptracker_core *core = file->private_data; + char *buf; + int len = 0; + int i; + ssize_t ret; + + buf = vmalloc(READ_BUF_SIZE); + + if (!buf) + return 0; + + len += scnprintf(buf + len, READ_BUF_SIZE - len, + "==== DSCP to AC mapping table ===\n"); + for (i = 0 ; i < DSCP_MAX; i++) { + if (!core->dscp_to_ac[i]) + continue; + len += scnprintf(buf + len, READ_BUF_SIZE - len, + "dscp[%d] : %u\n", i, core->dscp_to_ac[i]); + } + ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); + vfree(buf); + return ret; +} + +static void update_dscp(struct wlan_ptracker_core *core, u32 dscp, u32 ac) +{ + ptracker_info(core, "dscp %d, ac: %d\n", dscp, ac); + if (dscp > DSCP_MASK) + return; + if (ac > WMM_AC_VO) + return; + + core->dscp_to_ac[dscp] = ac; +} + +static ssize_t action_write(struct file *file, + const char __user *buf, size_t len, loff_t *ppos) +{ + struct wlan_ptracker_core *core = file->private_data; + struct wlan_ptracker_debugfs *debugfs = &core->debugfs; + u32 action; + + if (kstrtouint_from_user(buf, len, 10, &action)) + return -EFAULT; + + /* active action */ + switch (action) { + case ACTION_DSCP_UPDATE: + update_dscp(core, debugfs->dscp, debugfs->ac); + break; + default: + ptracker_err(core, "action %d is not supported!\n", action); + return -ENOTSUPP; + } + return 0; +} + +static const struct file_operations dscp_ops = { + .open = simple_open, + .read = action_read, + .write = action_write, + .llseek = generic_file_llseek, +}; + +int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs) +{ + struct wlan_ptracker_core *core = container_of( + debugfs, struct wlan_ptracker_core, debugfs); + + debugfs->root = debugfs_create_dir("wlan_ptracker", NULL); + if (!debugfs->root) + return -ENODEV; + debugfs_create_file("action", 0600, debugfs->root, core, &dscp_ops); + debugfs_create_u32("dscp", 0600, debugfs->root, &debugfs->dscp); + debugfs_create_u32("ac", 0600, debugfs->root, &debugfs->ac); + return 0; +} + +void wlan_ptracker_debugfs_exit(struct wlan_ptracker_debugfs *debugfs) +{ + debugfs_remove_recursive(debugfs->root); + debugfs->root = NULL; +} + +struct history_manager *wlan_ptracker_history_create(int entry_count, int entry_size) +{ + struct history_manager *hm; + + if (entry_count < 0 || entry_size < sizeof(struct history_entry)) + return NULL; + + hm = kzalloc(sizeof(struct history_manager) + entry_size * entry_count, GFP_KERNEL); + if (!hm) + return NULL; + + /* initial manager */ + hm->entry_count = entry_count; + hm->entry_size = entry_size; + hm->cur = 0; + hm->round = 0; + mutex_init(&hm->mutex); + return hm; +} + +void wlan_ptracker_history_destroy(struct history_manager *hm) +{ + if (hm) + kfree(hm); +} + +void * wlan_ptracker_history_store(struct history_manager *hm, u32 state) +{ + struct history_entry *entry; + + if (!hm->entry_count) + return NULL; + + entry = (struct history_entry *)(hm->entries + (hm->cur * hm->entry_size)); + entry->state = state; + entry->valid = true; + ktime_get_real_ts64(&entry->ts); + + /* update dytwt history */ + mutex_lock(&hm->mutex); + hm->cur++; + if (hm->cur / hm->entry_count) + hm->round++; + hm->cur %= hm->entry_count; + mutex_unlock(&hm->mutex); + return entry; +} + +static int history_get_tm(struct history_entry *entry, char *time, size_t len) +{ + struct rtc_time tm; + + rtc_time64_to_tm(entry->ts.tv_sec - (sys_tz.tz_minuteswest * 60), &tm); + return scnprintf(time, len, "%ptRs", &tm); +} + +size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int buf_len) +{ + u8 *ptr; + struct history_entry *cur, *next; + int len = 0; + int i, j; + + len += scnprintf(buf + len, buf_len - len, + "==== %s History ===\n", hm->name); + len += scnprintf(buf + len, buf_len - len, + "round: %d, cur: %d, entry len: %d, size: %d\n", + hm->round, hm->cur, hm->entry_count, hm->entry_size); + + ptr = hm->entries; + for (i = 0 ; i < hm->entry_count; i++) { + cur = (struct history_entry *) ptr; + if (!cur->valid) + break; + j = (i + hm->entry_count + 1) % hm->entry_count; + next = (struct history_entry *)(hm->entries + (j * hm->entry_size)); + len += scnprintf(buf + len, buf_len - len, "%02d: ", i); + len += history_get_tm(cur, buf + len, buf_len - len); + len += scnprintf(buf + len, buf_len - len, "%12s =>", state2str[cur->state]); + if (hm->priv_read) + len += hm->priv_read(cur, next->valid ? next : NULL, buf + len, + buf_len - len); + len += scnprintf(buf + len, buf_len - len, "\n"); + ptr += hm->entry_size; + } + return len; +} diff --git a/debugfs.h b/debugfs.h new file mode 100644 index 0000000..9e9eabf --- /dev/null +++ b/debugfs.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ + +#ifndef _WLAN_PTRACKER_DEBUGFS_H +#define _WLAN_PTRACKER_DEBUGFS_H + +#include +#include +#include + +struct wlan_ptracker_debugfs { + struct dentry *root; + u32 dscp; + u32 ac; + u32 action; + u32 log_level; +}; + +enum { + FEATURE_FLAG_TWT, + FEATURE_FLAG_MAX +}; + +enum { + ACTION_DSCP_UPDATE, + ACTION_MAX, +}; + +struct history_entry { + u32 state; + bool valid; + struct timespec64 ts; +}; + +#define MODULE_NAME_MAX 64 +struct history_manager { + char name[MODULE_NAME_MAX]; + int cur; + int round; + int entry_count; + int entry_size; + struct mutex mutex; + int (*priv_read)(void *cur, void *next, char *buf, int len); + u8 entries[0]; +}; + +extern int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs); +extern void wlan_ptracker_debugfs_exit(struct wlan_ptracker_debugfs *debugfs); +extern struct history_manager *wlan_ptracker_history_create(int entry_count, int entry_size); +extern void wlan_ptracker_history_destroy(struct history_manager *hm); +extern void *wlan_ptracker_history_store(struct history_manager *hm, u32 state); +extern size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int len); + +#endif /* _WLAN_PTRACKER_DEBUGFS_H */ diff --git a/dynamic_twt_manager.c b/dynamic_twt_manager.c new file mode 100644 index 0000000..7f22219 --- /dev/null +++ b/dynamic_twt_manager.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include +#include "core.h" + +static struct dytwt_manager dytwt_mgmt; + +#define dytwt_get_manager() (&dytwt_mgmt) + +static const char *const reason2str[WLAN_PTRACKER_NOTIFY_MAX] = { + "tp", "scene_change", "scene_prep", "suspend", "sta_change", +}; + +#define DYMAIC_TWT_CONFIG_ID 3 +#define TWT_WAKE_DURATION 8192 +#define TWT_IDLE_INTERVAL 512000 +#define TWT_WEB_INTERVAL 106496 +#define TWT_YOUTUBE_INTERVAL 10240 + +static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX] = { + { + .action = TWT_ACTION_SETUP, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + .wake_duration = TWT_WAKE_DURATION, + .wake_interval = TWT_IDLE_INTERVAL, + }, + }, + { + .action = TWT_ACTION_SETUP, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + .wake_duration = TWT_WAKE_DURATION, + .wake_interval = TWT_WEB_INTERVAL, + }, + }, + { + .action = TWT_ACTION_SETUP, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + .wake_duration = TWT_WAKE_DURATION, + .wake_interval = TWT_YOUTUBE_INTERVAL, + }, + }, + { + .action = TWT_ACTION_TEARDOWN, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + }, + }, + { + .action = TWT_ACTION_TEARDOWN, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + }, + }, +}; + +static int dytwt_client_twt_setup(struct wlan_ptracker_client *client, u32 state) +{ + if (!client->dytwt_ops) + return -EINVAL; + + if (!client->dytwt_ops->setup) + return -EINVAL; + + if (state >= WLAN_SCENE_MAX) + return -EINVAL; + + return client->dytwt_ops->setup(client->priv, &dytwt_actions[state].param); +} + +static int dytwt_client_twt_teardown(struct wlan_ptracker_client *client, u32 state) +{ + if (!client->dytwt_ops) + return -EINVAL; + + if (!client->dytwt_ops->teardown) + return -EINVAL; + + if (state >= WLAN_SCENE_MAX) + return -EINVAL; + + return client->dytwt_ops->teardown(client->priv, &dytwt_actions[state].param); +} + +static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) +{ + struct dytwt_cap param; + struct wlan_ptracker_core *core = client->core; + int ret; + + if (!client->dytwt_ops) + return false; + + if (!client->dytwt_ops->get_cap) + return false; + + ret = client->dytwt_ops->get_cap(client->priv, ¶m); + + if (ret) + return false; + + ptracker_dbg(core, "device: %d, peer: %d\n", param.device_cap, param.peer_cap); + return param.peer_cap && param.device_cap; +} + +static int dytwt_client_twt_pwrstates(struct wlan_ptracker_client *client, + struct dytwt_pwr_state *state) +{ + if (!client->dytwt_ops) + return -EINVAL; + + if (!client->dytwt_ops->get_pwrstates) + return -EINVAL; + + return client->dytwt_ops->get_pwrstates(client->priv, state); +} + +static inline void dytwt_record_get_pwr(u64 asleep, u64 awake, u64 *total, int *percent) +{ + /* for percent */ + *total = (asleep + awake) / 100; + *percent = (*total == 0) ? 0 : (asleep / *total); + /* trans 100 us to ms */ + *total /= 10; +} + +static int dytwt_record_priv_read(void *cur, void *next, char *buf, int len) +{ + struct dytwt_entry *c = cur; + struct dytwt_entry *n = next; + int period_percent = 0, total_percent; + u64 period_time = 0, total_time; + + /* get total */ + dytwt_record_get_pwr(c->pwr.asleep, c->pwr.awake, &total_time, &total_percent); + + /* get period */ + if (n) { + u64 awake = n->pwr.awake > c->pwr.awake ? + (n->pwr.awake - c->pwr.awake) : c->pwr.awake; + u64 asleep = n->pwr.asleep > c->pwr.asleep ? + (n->pwr.asleep - c->pwr.asleep) : c->pwr.asleep; + dytwt_record_get_pwr(asleep, awake, &period_time, &period_percent); + } + + return scnprintf(buf, len, + "Applied: %s, Time: %llu (%llu) ms, Percent: %d%% (%d%%) Reason: %s, Rate: %d\n", + c->apply ? "TRUE" : "FALSE", period_time, total_time, period_percent, total_percent, + reason2str[c->reason], c->rate); +} + +static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, + struct dytwt_manager *dytwt, struct wlan_scene_event *msg, bool apply) +{ + struct dytwt_entry *entry; + + /* record assign base*/ + entry = wlan_ptracker_history_store(dytwt->hm, msg->dst); + if (!entry) + return; + /* record private values */ + entry->apply = apply; + entry->reason = msg->reason; + entry->rate = msg->rate; + dytwt_client_twt_pwrstates(client, &entry->pwr); + /* prev will be used for decided teardown or not. */ + dytwt->prev = msg->dst; +} + +#define TWT_HISTORY_BUF_SIZE 10240 +static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + char *buf; + int len; + ssize_t ret; + + buf = vmalloc(TWT_HISTORY_BUF_SIZE); + + if (!buf) + return 0; + + len = wlan_ptracker_history_read(dytwt->hm, buf, TWT_HISTORY_BUF_SIZE); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); + vfree(buf); + return ret; +} + +static void update_twt_flag(struct wlan_ptracker_core *core) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + + if (dytwt->feature_flag & BIT(FEATURE_FLAG_TWT)) + dytwt->feature_flag &= ~BIT(FEATURE_FLAG_TWT); + else + dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); +} + +static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) +{ + struct dytwt_pwr_state pwr_state; + struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_client *client = core->client; + + switch (action) { + case TWT_TEST_SETUP: + dytwt_client_twt_setup(client, dytwt->state); + break; + case TWT_TEST_TEARDOWN: + dytwt_client_twt_teardown(client, dytwt->state); + break; + case TWT_TEST_CAP: + dytwt_client_twt_cap(client); + break; + case TWT_TEST_PWRSTATS: + dytwt_client_twt_pwrstates(client, &pwr_state); + break; + case TWT_TEST_ONOFF: + update_twt_flag(core); + break; + default: + ptracker_err(core, "action %d is not supported!\n", action); + return -ENOTSUPP; + } + return 0; +} + +static ssize_t twt_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + struct wlan_ptracker_core *core = file->private_data; + u32 action; + + if (kstrtouint_from_user(buf, len, 10, &action)) + return -EFAULT; + + return dytwt_debugfs_action(core, action); +} + +static const struct file_operations twt_ops = { + .open = simple_open, + .read = twt_read, + .write = twt_write, + .llseek = generic_file_llseek, +}; + +/* This function is running in thread context */ +#define TWT_WAIT_STA_READY_TIME 1000 +static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) +{ + struct wlan_ptracker_core *core = client->core; + struct wlan_scene_event *msg = &core->fsm.msg; + struct dytwt_scene_action *act; + struct dytwt_manager *dytwt = dytwt_get_manager(); + bool apply = false; + u32 state = msg->dst; + int ret = 0; + + if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) + goto out; + + if (!dytwt_client_twt_cap(client)) { + ptracker_dbg(core, "twt is not supported on device or peer\n"); + goto out; + } + + act = &dytwt_actions[state]; + ptracker_dbg(core, "twt setup for state: %d, reason: %s!\n", + state, reason2str[msg->reason]); + + /* wait for sta ready after connected. */ + if (msg->reason == WLAN_PTRACKER_NOTIFY_STA_CHANGE) + msleep(TWT_WAIT_STA_READY_TIME); + + /* follow action to setup */ + if (act->action == TWT_ACTION_SETUP) { + ret = dytwt_client_twt_setup(client, state); + } else { + /* tear down was apply during state of perpare change. */ + apply = true; + } + apply = ret ? false : true; +out: + /* store record of hostory even twt is not applid! */ + dytwt_mgmt_history_store(client, dytwt, msg, apply); + return ret; +} + +static void dytwt_scene_change_prepare_handler(struct wlan_ptracker_client *client) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + + /* prepare to change state teardown original setup first */ + if (dytwt->prev < WLAN_SCENE_LOW_LATENCY) + dytwt_client_twt_teardown(client, dytwt->prev); +} + +static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct wlan_ptracker_core *core = ptr; + struct wlan_ptracker_client *client = core->client; + + if (!client) + return NOTIFY_OK; + + switch (event) { + case WLAN_PTRACKER_NOTIFY_SCENE_CHANGE: + dytwt_scene_change_handler(client); + break; + case WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE: + dytwt_scene_change_prepare_handler(client); + break; + default: + break; + } + return NOTIFY_OK; +} + +static int dytwt_debugfs_init(struct wlan_ptracker_core *core) +{ + struct wlan_ptracker_debugfs *debugfs = &core->debugfs; + struct dytwt_manager *dytwt = dytwt_get_manager(); + + dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); + dytwt->dir = debugfs_create_dir("twt", debugfs->root); + if (!dytwt->dir) + return -ENODEV; + debugfs_create_file("history", 0600, dytwt->dir, core, &twt_ops); + debugfs_create_u32("state", 0600, dytwt->dir, &dytwt->state); + return 0; +} + +#define DYTWT_RECORD_MAX 50 +static int dytwt_mgmt_init(void) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + struct history_manager *hm; + + if (dytwt->dir) + debugfs_remove_recursive(dytwt->dir); + memset(dytwt, 0, sizeof(*dytwt)); + dytwt->state = WLAN_SCENE_IDLE; + dytwt->prev = WLAN_SCENE_MAX; + hm = wlan_ptracker_history_create(DYTWT_RECORD_MAX, sizeof(struct dytwt_entry)); + if (!hm) + return -ENOMEM; + strncpy(hm->name, "Dynamic TWT Setup", sizeof(hm->name)); + hm->priv_read = dytwt_record_priv_read; + dytwt->hm = hm; + return 0; +} + +static void dytwt_mgmt_exit(void) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + + if (dytwt->dir) + debugfs_remove_recursive(dytwt->dir); + + wlan_ptracker_history_destroy(dytwt->hm); + memset(dytwt, 0, sizeof(*dytwt)); +} + +static struct notifier_block twt_nb = { + .priority = 0, + .notifier_call = dytwt_notifier_handler, +}; + +int dytwt_init(struct wlan_ptracker_core *core) +{ + dytwt_mgmt_init(); + dytwt_debugfs_init(core); + return wlan_ptracker_register_notifier(&core->notifier, &twt_nb); +} + +void dytwt_exit(struct wlan_ptracker_core *core) +{ + dytwt_mgmt_exit(); + return wlan_ptracker_unregister_notifier(&core->notifier, &twt_nb); +} + diff --git a/dynamic_twt_manager.h b/dynamic_twt_manager.h new file mode 100644 index 0000000..fe8b289 --- /dev/null +++ b/dynamic_twt_manager.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef __TP_TRACKER_DYNAMIC_TWT_SETUP_H +#define __TP_TRACKER_DYNAMIC_TWT_SETUP_H + +#include "debugfs.h" + +struct wlan_ptracker_client; +struct wlan_ptracker_core; + +struct dytwt_setup_param { + u8 config_id; + u8 nego_type; + u8 trigger_type; + u32 wake_duration; + u32 wake_interval; +}; + +struct dytwt_cap { + u16 device_cap; + u16 peer_cap; +}; + +struct dytwt_pwr_state { + u64 awake; + u64 asleep; +}; + +struct dytwt_client_ops { + int (*setup)(void *priv, struct dytwt_setup_param *param); + int (*teardown)(void *priv, struct dytwt_setup_param *param); + int (*get_cap)(void *priv, struct dytwt_cap *cap); + int (*get_pwrstates)(void *priv, struct dytwt_pwr_state *state); +}; + +enum { + TWT_ACTION_SETUP, + TWT_ACTION_TEARDOWN, + TWT_ACTION_MAX, +}; + +enum { + TWT_TEST_SETUP, + TWT_TEST_TEARDOWN, + TWT_TEST_CAP, + TWT_TEST_PWRSTATS, + TWT_TEST_ONOFF, + TWT_TEST_MAX, +}; + +struct dytwt_scene_action { + u32 action; + struct dytwt_setup_param param; +}; + +struct dytwt_entry { + /* base should put as first membor */ + struct history_entry base; + bool apply; + u32 rate; + u32 reason; + struct dytwt_pwr_state pwr; +} __align(void *); + + +struct dytwt_manager { + u32 prev; + u32 feature_flag; + u32 state; + struct history_manager *hm; + struct dentry *dir; +}; + +extern int dytwt_init(struct wlan_ptracker_core *core); +extern void dytwt_exit(struct wlan_ptracker_core *core); + +#endif /* __TP_TRACKER_DYNAMIC_TWT_SETUP_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..85f18e7 --- /dev/null +++ b/main.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include +#include +#include +#include +#include +#include "core.h" + + +static struct wlan_ptracker_core ptracker_core; + +#define get_core() (&ptracker_core) + +#define client_to_core(client) \ + ((struct wlan_ptracker_core *)((client)->core)) + + +/* Default mapping rule follow 802.11e */ +static const int dscp_trans[WMM_AC_MAX][DSCP_MAP_MAX] = { + {0, 24, 26, 28, 30, -1}, /* AC_BE */ + {8, 10, 12, 14, 16, 18, 20, 22, -1}, /* AC_BK */ + {32, 34, 36, 38, 40, 46, -1}, /* AC_VI */ + {48, 56, -1}, /* AC_VO */ +}; + +static void dscp_to_ac_init(u8 *dscp_to_ac) +{ + int i, j; + + for (i = 0 ; i < WMM_AC_MAX; i++) { + for (j = 0 ; j < DSCP_MAP_MAX; j++) { + int dscp = dscp_trans[i][j]; + + if (dscp == -1) + break; + dscp_to_ac[dscp] = i; + } + } +} + +static int wlan_ptracker_core_init(struct wlan_ptracker_core *core) +{ + memset(core, 0, sizeof(*core)); + device_initialize(&core->device); + dev_set_name(&core->device, PTRACKER_PREFIX); + device_add(&core->device); + dscp_to_ac_init(core->dscp_to_ac); + wlan_ptracker_debugfs_init(&core->debugfs); + wlan_ptracker_notifier_init(&core->notifier); + scenes_fsm_init(&core->fsm); + dytwt_init(core); + return 0; +} + +static void wlan_ptracker_core_exit(struct wlan_ptracker_core *core) +{ + dytwt_exit(core); + scenes_fsm_exit(&core->fsm); + wlan_ptracker_notifier_exit(&core->notifier); + wlan_ptracker_debugfs_exit(&core->debugfs); + device_del(&core->device); + memset(core, 0, sizeof(struct wlan_ptracker_core)); +} + +static int client_event_handler(void *priv, u32 event) +{ + struct wlan_ptracker_client *client = priv; + struct wlan_ptracker_core *core = client_to_core(client); + + return wlan_ptracker_call_chain(&core->notifier, event, core); +} + +int wlan_ptracker_register_client(struct wlan_ptracker_client *client) +{ + struct wlan_ptracker_core *core = get_core(); + + if (!core->client) { + core->client = client; + client->cb = client_event_handler; + } + return 0; +} +EXPORT_SYMBOL_GPL(wlan_ptracker_register_client); + +void wlan_ptracker_unregister_client(struct wlan_ptracker_client *client) +{ + struct wlan_ptracker_core *core = get_core(); + + if (core->client == client) { + client->cb = NULL; + core->client = NULL; + } +} +EXPORT_SYMBOL_GPL(wlan_ptracker_unregister_client); + +static int __init wlan_ptracker_init(void) +{ + struct wlan_ptracker_core *core = get_core(); + int ret; + + ret = wlan_ptracker_core_init(core); + if (ret) + goto err; + dev_dbg(&core->device, "module init\n"); + return 0; +err: + wlan_ptracker_core_exit(core); + return ret; +} + +static void __exit wlan_ptracker_exit(void) +{ + struct wlan_ptracker_core *core = get_core(); + + dev_dbg(&core->device, "module exit\n"); + wlan_ptracker_core_exit(core); +} + +module_init(wlan_ptracker_init); +module_exit(wlan_ptracker_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Star Chang "); +MODULE_DESCRIPTION("WiFi Performance Tracker"); diff --git a/notifier.c b/notifier.c new file mode 100644 index 0000000..7f9712d --- /dev/null +++ b/notifier.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include "core.h" + +#define notifier_to_core(notifier) \ + container_of(notifier, struct wlan_ptracker_core, notifier) + +#define nb_to_notifier(nb) \ + (container_of(nb, struct wlan_ptracker_notifier, nb)) + +static int up_event_handler(struct wlan_ptracker_core *core, struct net_device *dev) +{ + core->dev = dev; + core->client->core = core; + core->client->priv = dev; + return tp_monitor_init(&core->tp); +} + +static void down_event_handler(struct wlan_ptracker_core *core) +{ + tp_monitor_exit(&core->tp); + core->dev = NULL; + core->client->core = NULL; + core->client->priv = NULL; +} + +static int netdevice_notifier_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct wlan_ptracker_notifier *notifier = nb_to_notifier(nb); + struct wlan_ptracker_core *core = notifier_to_core(notifier); + + if (!core->client) + return NOTIFY_DONE; + + if (strcmp(netdev->name, core->client->ifname)) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UP: + ptracker_info(core, "interface up (%s)\n", netdev->name); + up_event_handler(core, netdev); + break; + case NETDEV_DOWN: + ptracker_info(core, "interface down (%s)\n", netdev->name); + down_event_handler(core); + break; + default: + break; + } + return NOTIFY_OK; +} + +int wlan_ptracker_register_notifier(struct wlan_ptracker_notifier *notifier, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(¬ifier->notifier_head, nb); +} + +void wlan_ptracker_unregister_notifier(struct wlan_ptracker_notifier *notifier, + struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(¬ifier->notifier_head, nb); +} + +int wlan_ptracker_call_chain(struct wlan_ptracker_notifier *notifier, + unsigned long event, void *priv) +{ + struct wlan_ptracker_core *core = priv; + int ret; + + ret = blocking_notifier_call_chain(¬ifier->notifier_head, event, priv); + if (ret & NOTIFY_STOP_MASK) + ptracker_err(core, "notifier chain fail with status %#x\n", ret); + + return notifier_to_errno(ret); +} + +void wlan_ptracker_notifier_init(struct wlan_ptracker_notifier *notifier) +{ + notifier->prev_event = jiffies; + /* register to device notifier */ + notifier->nb.priority = 0; + notifier->nb.notifier_call = netdevice_notifier_handler; + register_netdevice_notifier(¬ifier->nb); + /* init notifier chain to notify plugin modules */ + BLOCKING_INIT_NOTIFIER_HEAD(¬ifier->notifier_head); +} + +void wlan_ptracker_notifier_exit(struct wlan_ptracker_notifier *notifier) +{ + /* reset notifier */ + BLOCKING_INIT_NOTIFIER_HEAD(¬ifier->notifier_head); + /* unregister netdevice notifier*/ + unregister_netdevice_notifier(¬ifier->nb); + notifier->prev_event = 0; +} + diff --git a/notifier.h b/notifier.h new file mode 100644 index 0000000..348ab92 --- /dev/null +++ b/notifier.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef __TP_TRACKER_NOTIFIER_H +#define __TP_TRACKER_NOTIFIER_H + +#include +#include +#include + + +struct wlan_ptracker_notifier { + struct notifier_block nb; + unsigned long prev_event; + struct blocking_notifier_head notifier_head; +}; + +extern void wlan_ptracker_notifier_init(struct wlan_ptracker_notifier *nb); +extern void wlan_ptracker_notifier_exit(struct wlan_ptracker_notifier *nb); + +extern int wlan_ptracker_register_notifier(struct wlan_ptracker_notifier *notifier, + struct notifier_block *nb); +extern void wlan_ptracker_unregister_notifier(struct wlan_ptracker_notifier *notifier, + struct notifier_block *nb); +extern int wlan_ptracker_call_chain(struct wlan_ptracker_notifier *notifier, + unsigned long event, void *priv); + +#endif /* __TP_TRACKER_NOTIFIER_H */ diff --git a/scenes_fsm.c b/scenes_fsm.c new file mode 100644 index 0000000..991863e --- /dev/null +++ b/scenes_fsm.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include "core.h" + +#define fsm_to_core(fsm) \ + (container_of(fsm, struct wlan_ptracker_core, fsm)) + +static const struct wlan_state_condition conditions[FSM_STATE_MAX] = { + { + .scene = WLAN_SCENE_IDLE, + .ac_mask = WMM_AC_ALL_MASK, + .min_tp_threshold = 0, + .max_tp_threshold = 1000, + }, + { + .scene = WLAN_SCENE_WEB, + .ac_mask = WMM_AC_ALL_MASK, + .min_tp_threshold = 1000, + .max_tp_threshold = 10000, + }, + { + .scene = WLAN_SCENE_YOUTUBE, + .ac_mask = WMM_AC_ALL_MASK, + /* Total >= 10 Mbps && < 50 Mbps */ + .min_tp_threshold = 10000, + .max_tp_threshold = 50000, + }, + { + .scene = WLAN_SCENE_LOW_LATENCY, + .ac_mask = BIT(WMM_AC_VO), + /* VO >= 1 Mbps */ + .min_tp_threshold = 1000, + .max_tp_threshold = __INT_MAX__, + }, + { + .scene = WLAN_SCENE_TPUT, + .ac_mask = WMM_AC_ALL_MASK, + /* Total >= 50 Mbps */ + .min_tp_threshold = 50000, + .max_tp_threshold = __INT_MAX__, + }, +}; + +static int fsm_thread(void *param) +{ + struct wlan_ptracker_fsm *fsm = param; + struct wlan_scene_event *msg = &fsm->msg; + struct wlan_ptracker_core *core = fsm_to_core(fsm); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) { + ptracker_info(core, "kthread is stopped\n"); + break; + } + wait_for_completion(&fsm->event); + ptracker_dbg(core, "state: %d, trans state %d -> %d, rate %llu\n", + msg->state, msg->src, msg->dst, msg->rate); + + /* + * Request twice of transmit events are happing then trans state, + * to make sure the state is stable enough. + * first time: confirm is false, send prepare first. + * (ex: twt can tear down original setup first) + * second time: confirm is true and change the state to dst. + */ + if (fsm->confirm) { + wlan_ptracker_call_chain(&core->notifier, + WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, core); + msg->state = msg->dst; + fsm->confirm = false; + } else { + /* call notifier chain */ + wlan_ptracker_call_chain(&core->notifier, + WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, core); + fsm->confirm = true; + } + } + return 0; +} + +static bool scenes_check(u64 rate, const struct wlan_state_condition *cond, + struct wlan_scene_event *msg) +{ + /* change bits rate to Kbits rate */ + u64 krate = rate / 1000; + + if (krate >= cond->min_tp_threshold && krate < cond->max_tp_threshold) { + msg->rate = rate; + return true; + } + return false; +} + +static u32 scenes_condition_get(struct wlan_ptracker_fsm *fsm) +{ + const struct wlan_state_condition *cond; + struct wlan_ptracker_core *core = fsm_to_core(fsm); + struct tp_monitor_stats *stats = &core->tp; + struct wlan_scene_event *msg = &fsm->msg; + int i, j; + + /* check from higher restriction to lower */ + for (i = FSM_STATE_MAX - 1 ; i >= 0 ; i--) { + cond = &fsm->conditions[i]; + if (cond->ac_mask == WMM_AC_ALL_MASK) { + if (scenes_check( + stats->tx[WMM_AC_MAX].rate + stats->rx[WMM_AC_MAX].rate, + cond, msg)) + return cond->scene; + } else { + u64 total_tx = 0; + u64 total_rx = 0; + + for (j = 0 ; j < WMM_AC_MAX; j++) { + if (cond->ac_mask & BIT(j)) { + total_tx += stats->tx[j].rate; + total_rx += stats->rx[j].rate; + } + if (scenes_check(total_tx + total_rx, cond, msg)) + return cond->scene; + } + } + } + return fsm->msg.state; +} + +/* TODO: fine-tune period threshold */ +#define RESET_THRESHOLD 1 +static void scenes_fsm_decision(struct wlan_ptracker_core *core, u32 type) +{ + struct wlan_ptracker_fsm *fsm = &core->fsm; + struct wlan_scene_event *msg = &fsm->msg; + u32 new_state; + bool except = false; + + if (!fsm->fsm_thread) + return; + + /* condition check */ + new_state = scenes_condition_get(fsm); + + /* reset check */ + if (type == WLAN_PTRACKER_NOTIFY_SUSPEND) { + fsm->reset_cnt++; + except = !(fsm->reset_cnt % RESET_THRESHOLD); + } + + /* check state isn't change and not first time do nothing */ + if (new_state == msg->state && type != WLAN_PTRACKER_NOTIFY_STA_CHANGE) + return; + /* new state must higher then current state */ + if (new_state < msg->state && !except) + return; + + ptracker_dbg(core, "type %d, reset_cnt %d, %d -> %d\n", + type, fsm->reset_cnt, msg->state, new_state); + + /* clear reset cnt*/ + fsm->reset_cnt = 0; + /* decide to trans state */ + mutex_lock(&msg->lock); + msg->src = msg->state; + msg->dst = new_state; + msg->reason = type; + mutex_unlock(&msg->lock); + + /* send complete to wake up thread to handle fsm */ + complete(&fsm->event); +} + +static int scene_notifier_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct wlan_ptracker_core *core = ptr; + struct wlan_ptracker_notifier *notifier = &core->notifier; + + /* + * Events of suspen and sta change will block wlan driver + * should not spend too much time. Move complex part to thread handle. + */ + switch (event) { + case WLAN_PTRACKER_NOTIFY_SUSPEND: + ptracker_dbg(core, "update time (%d)\n", + jiffies_to_msecs(jiffies - notifier->prev_event)); + notifier->prev_event = jiffies; + case WLAN_PTRACKER_NOTIFY_STA_CHANGE: + case WLAN_PTRACKER_NOTIFY_TP: + scenes_fsm_decision(core, event); + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block scene_nb = { + .priority = 0, + .notifier_call = scene_notifier_handler, +}; + +int scenes_fsm_init(struct wlan_ptracker_fsm *fsm) +{ + struct wlan_scene_event *msg = &fsm->msg; + struct wlan_ptracker_core *core = fsm_to_core(fsm); + int ret = 0; + + /* assign scenes and conditions */ + fsm->conditions = &conditions[0]; + fsm->reset_cnt = 0; + /* for first link up setting */ + fsm->confirm = true; + /* init msg for receiving event */ + msg->dst = WLAN_SCENE_IDLE; + msg->src = WLAN_SCENE_IDLE; + msg->state = WLAN_SCENE_IDLE; + mutex_init(&msg->lock); + + /*scene event notifier handler from client */ + ret = wlan_ptracker_register_notifier(&core->notifier, &scene_nb); + if (ret) + return ret; + + /* initial thread for listening event */ + init_completion(&fsm->event); + fsm->fsm_thread = kthread_create(fsm_thread, fsm, "wlan_ptracker_thread"); + if (IS_ERR(fsm->fsm_thread)) { + ret = PTR_ERR(fsm->fsm_thread); + fsm->fsm_thread = NULL; + ptracker_err(core, "unable to start kernel thread %d\n", ret); + return ret; + } + wake_up_process(fsm->fsm_thread); + return 0; +} + +void scenes_fsm_exit(struct wlan_ptracker_fsm *fsm) +{ + struct wlan_ptracker_core *core = fsm_to_core(fsm); + + wlan_ptracker_unregister_notifier(&core->notifier, &scene_nb); + if (fsm->fsm_thread) { + int ret = kthread_stop(fsm->fsm_thread); + fsm->fsm_thread = NULL; + if (ret) + ptracker_err(core, "stop thread fail: %d\n", ret); + } + complete(&fsm->event); + fsm->conditions = NULL; + fsm->reset_cnt = 0; +} + diff --git a/scenes_fsm.h b/scenes_fsm.h new file mode 100644 index 0000000..b79a78f --- /dev/null +++ b/scenes_fsm.h @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef __WLAN_SCENES_FSM_H +#define __WLAN_SCENES_FSM_H + +#include +#include +#include + +struct wlan_ptracker_core; + +enum { + WLAN_SCENE_IDLE, + WLAN_SCENE_WEB, + WLAN_SCENE_YOUTUBE, + WLAN_SCENE_LOW_LATENCY, + WLAN_SCENE_TPUT, + WLAN_SCENE_MAX, +}; + +/* follow design spec to define the conditions */ +enum { + FSM_STATE_C0, + FSM_STATE_C1, + FSM_STATE_C2, + FSM_STATE_C3, + FSM_STATE_C4, + FSM_STATE_MAX +}; + +struct wlan_state_condition { + u32 scene; + u32 ac_mask; + /* Kbits */ + u32 min_tp_threshold; + u32 max_tp_threshold; +}; + +#define WMM_AC_ALL_MASK 0xf + +struct wlan_scene_event { + struct mutex lock; + u32 state; + u32 src; + u32 dst; + u32 reason; + u64 rate; +}; + +struct wlan_ptracker_fsm { + int reset_cnt; + bool confirm; + struct completion event; + struct wlan_scene_event msg; + struct task_struct *fsm_thread; + const struct wlan_state_condition *conditions; +}; + +extern int scenes_fsm_init(struct wlan_ptracker_fsm *fsm); +extern void scenes_fsm_exit(struct wlan_ptracker_fsm *fsm); + +#endif /* __WLAN_SCENES_FSM_H */ diff --git a/tp_monitor.c b/tp_monitor.c new file mode 100644 index 0000000..6e04d73 --- /dev/null +++ b/tp_monitor.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#include +#include +#include +#include +#include "core.h" + +#define tp_to_core(_tp) container_of(_tp, struct wlan_ptracker_core, tp) + +static void tp_rate_pps_update(struct tp_monitor_counts *counts) +{ + unsigned long cur_cnt, cur_bytes; + struct tp_monitor_counts *count; + int i; + + for (i = 0 ; i < TPM_SIZE_MAX; i++) { + count = &counts[i]; + cur_bytes = count->packet_bytes; + cur_cnt = count->packet_cnt; + count->rate = (cur_bytes - count->pre_packet_bytes) << 3; + count->pps = cur_cnt - count->pre_packet_cnt; + count->pre_packet_cnt = cur_cnt; + count->pre_packet_bytes = cur_bytes; + } +} + +/* TODO: fine-tune period */ +#define TPM_TIMER_PERIOD 1000 +static void tp_timer_callback(struct timer_list *t) +{ + struct tp_monitor_stats *stats = from_timer(stats, t, tp_timer); + struct wlan_ptracker_core *core = tp_to_core(stats); + + /* update tx */ + tp_rate_pps_update(stats->tx); + /* update rx */ + tp_rate_pps_update(stats->rx); + mod_timer(t, jiffies + msecs_to_jiffies(TPM_TIMER_PERIOD)); + /* adjust scenes */ + wlan_ptracker_call_chain(&core->notifier, WLAN_PTRACKER_NOTIFY_TP, core); +} + +static inline void tp_timer_start(struct tp_monitor_stats *stats) +{ + /* update rate per second */ + timer_setup(&stats->tp_timer, tp_timer_callback, 0); + mod_timer(&stats->tp_timer, jiffies + msecs_to_jiffies(TPM_TIMER_PERIOD)); +} + +static inline void tp_timer_stop(struct tp_monitor_stats *stats) +{ + del_timer_sync(&stats->tp_timer); +} + +static void tp_update_counter(struct wlan_ptracker_core *core, + struct tp_monitor_counts *counts, u8 dscp, struct sk_buff *skb) +{ + u8 wmm_ac = core->dscp_to_ac[dscp]; + + /* update total counters */ + counts[WMM_AC_MAX].packet_cnt++; + counts[WMM_AC_MAX].packet_bytes += skb->len; + /* update ac counters */ + counts[wmm_ac].packet_cnt++; + counts[wmm_ac].packet_bytes += skb->len; +} + +static u32 tp_monitor_nf_input(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct wlan_ptracker_core *core = priv; + struct net_device *dev = skb->dev; + u8 dscp; + + if (dev != core->dev) + goto out; + + dscp = ip_hdr(skb)->version == 4 ? + ipv4_get_dsfield(ip_hdr(skb)) >> DSCP_SHIFT : + ipv6_get_dsfield(ipv6_hdr(skb)) >> DSCP_SHIFT; + + tp_info(&core->tp, "rx packets %s, dscp: %d, ip.ver: %d, len: %d, %d\n", + dev->name, dscp, ip_hdr(skb)->version, skb->len, skb->data_len); + tp_update_counter(core, core->tp.rx, dscp, skb); +out: + return NF_ACCEPT; +} + +static u32 tp_monitor_nf_output(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct wlan_ptracker_core *core = priv; + struct net_device *dev = skb->dev; + u8 dscp; + + if (dev != core->dev) + goto out; + + dscp = ip_hdr(skb)->version == 4 ? + ipv4_get_dsfield(ip_hdr(skb)) >> DSCP_SHIFT : + ipv6_get_dsfield(ipv6_hdr(skb)) >> DSCP_SHIFT; + + tp_info(&core->tp, "tx packets %s, dscp:%d, ip.ver: %d, len: %d\n", + dev->name, dscp, ip_hdr(skb)->version, skb->data_len); + tp_update_counter(core, core->tp.tx, dscp, skb); +out: + return NF_ACCEPT; +} + +static struct nf_hook_ops wlan_ptracker_nfops[] = { + { + .hook = tp_monitor_nf_input, + .pf = NFPROTO_INET, + .hooknum = NF_INET_PRE_ROUTING, + .priority = INT_MAX, + }, + { + .hook = tp_monitor_nf_output, + .pf = NFPROTO_INET, + .hooknum = NF_INET_POST_ROUTING, + .priority = INT_MAX, + }, +}; +#define WLAN_PTRACKER_NF_LEN ARRAY_SIZE(wlan_ptracker_nfops) + +static int tp_show(struct seq_file *s, void *unused) +{ + struct tp_monitor_counts *counter, *counters = s->private; + int i; + + for (i = 0 ; i < TPM_SIZE_MAX; i++) { + counter = &counters[i]; + if (i < WMM_AC_MAX) + seq_printf(s, "AC %d ->\n", i); + else + seq_puts(s, "Total ->\n"); + seq_printf(s, "packet_cnt : %llu\n", counter->packet_cnt); + seq_printf(s, "packet_bytes : %llu\n", counter->packet_bytes); + seq_printf(s, "rate (Kbits) : %llu\n", counter->rate / 1000); + seq_printf(s, "pps : %llu\n", counter->pps); + } + return 0; +} + +static int counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, tp_show, inode->i_private); +} + +static const struct file_operations counter_ops = { + .open = counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int tp_monitor_debugfs_init(struct wlan_ptracker_core *core) +{ + struct wlan_ptracker_debugfs *debugfs = &core->debugfs; + struct tp_monitor_stats *stats = &core->tp; + struct wlan_ptracker_client *client = core->client; + + if (!client) + return 0; + stats->dir = debugfs_create_dir(client->ifname, debugfs->root); + if (!stats->dir) + return -ENODEV; + debugfs_create_u32("log_level", 0600, stats->dir, &stats->debug); + debugfs_create_file("tx", 0400, stats->dir, &stats->tx, &counter_ops); + debugfs_create_file("rx", 0400, stats->dir, &stats->rx, &counter_ops); + return 0; +} + +int tp_monitor_init(struct tp_monitor_stats *stats) +{ + struct wlan_ptracker_core *core = tp_to_core(stats); + struct net *net = dev_net(core->dev); + int err = 0; + int i; + + /* debugfs */ + tp_monitor_debugfs_init(core); + /* assign net_device for ingress check and filter */ + for (i = 0 ; i < WLAN_PTRACKER_NF_LEN; i++) { + wlan_ptracker_nfops[i].dev = core->dev; + wlan_ptracker_nfops[i].priv = core; + } + + /* register hook function to netfilter */ + err = nf_register_net_hooks(net, wlan_ptracker_nfops, WLAN_PTRACKER_NF_LEN); + if (err) + goto out; + + /* start a timer to update rate and pps */ + tp_timer_start(stats); + return 0; +out: + ptracker_err(core, "initial err (%d)\n", err); + return err; +} + +void tp_monitor_exit(struct tp_monitor_stats *stats) +{ + struct wlan_ptracker_core *core = tp_to_core(stats); + struct net *net = dev_net(core->dev); + + if (stats->dir) + debugfs_remove_recursive(stats->dir); + tp_timer_stop(stats); + nf_unregister_net_hooks(net, wlan_ptracker_nfops, WLAN_PTRACKER_NF_LEN); +} + diff --git a/tp_monitor.h b/tp_monitor.h new file mode 100644 index 0000000..05319a1 --- /dev/null +++ b/tp_monitor.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ + +#ifndef __WLAN_TP_MONITOR_H +#define __WLAN_TP_MONITOR_H + +#include + +enum { + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_MAX +}; + +#define TPM_SIZE_MAX (WMM_AC_MAX + 1) + +struct tp_monitor_counts { + u64 packet_cnt; + u64 packet_bytes; + u64 pre_packet_bytes; + u64 pre_packet_cnt; + u64 rate; + u64 pps; +}; + +struct tp_monitor_stats { + struct tp_monitor_counts tx[TPM_SIZE_MAX]; + struct tp_monitor_counts rx[TPM_SIZE_MAX]; + struct timer_list tp_timer; + struct dentry *dir; + u32 debug; +}; + +extern int tp_monitor_init(struct tp_monitor_stats *stats); +extern void tp_monitor_exit(struct tp_monitor_stats *stats); +#endif /* __WLAN_TP_MONITOR_H */ diff --git a/wlan_ptracker_client.h b/wlan_ptracker_client.h new file mode 100644 index 0000000..4933960 --- /dev/null +++ b/wlan_ptracker_client.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Wifi performance tracker + * + * Copyright 2022 Google LLC. + * + * Author: Star Chang + */ +#ifndef __WLAN_PTRACKER_CLIENT_H +#define __WLAN_PTRACKER_CLIENT_H + +#include "dynamic_twt_manager.h" + +#define IFNAME_MAX 16 + +enum { + WLAN_PTRACKER_NOTIFY_TP, + WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, + WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, + WLAN_PTRACKER_NOTIFY_SUSPEND, + WLAN_PTRACKER_NOTIFY_STA_CHANGE, + WLAN_PTRACKER_NOTIFY_MAX, +}; + +struct wlan_ptracker_client { + void *priv; + void *core; + char ifname[IFNAME_MAX]; + struct dytwt_client_ops *dytwt_ops; + int (*cb)(void *priv, u32 event); +}; + +extern int wlan_ptracker_register_client(struct wlan_ptracker_client *client); +extern void wlan_ptracker_unregister_client(struct wlan_ptracker_client *client); + +#endif /*__WLAN_PTRACKER_CLIENT_H*/ + -- cgit v1.2.3 From 4e6c07be722421daa260cb66ffd667f4088d1e83 Mon Sep 17 00:00:00 2001 From: Star Chang Date: Thu, 4 Aug 2022 01:36:15 +0000 Subject: wlan_ptracker: add feature support 1. add debug information. 2. support twt parameters configure. 3. support force scene translate 4. add runtime disable twt setup. Bug: 253348062 Test: ST-stability/WiFi regression/WiFi performance Test pass Signed-off-by: Star Chang Change-Id: I581adca9787bd904ded2c70c38a2af19857daa8b --- core.h | 2 +- debugfs.h | 5 + dynamic_twt_manager.c | 535 +++++++++++++++++++++++++++++++++++++++++-------- dynamic_twt_manager.h | 50 ++++- main.c | 8 +- scenes_fsm.c | 171 ++++++++++++---- scenes_fsm.h | 12 ++ tp_monitor.c | 39 +++- tp_monitor.h | 4 + wlan_ptracker_client.h | 9 +- 10 files changed, 696 insertions(+), 139 deletions(-) diff --git a/core.h b/core.h index ae5b988..7cd8cea 100644 --- a/core.h +++ b/core.h @@ -29,7 +29,7 @@ struct wlan_ptracker_core { struct wlan_ptracker_debugfs debugfs; struct wlan_ptracker_fsm fsm; struct net_device *dev; - struct wlan_ptracker_client *client; + struct wlan_ptracker_client __rcu *client; u8 dscp_to_ac[DSCP_MAX]; }; #endif /* _WLAN_PTRACKER_CORE_H */ diff --git a/debugfs.h b/debugfs.h index 9e9eabf..721cc49 100644 --- a/debugfs.h +++ b/debugfs.h @@ -30,6 +30,11 @@ enum { ACTION_MAX, }; +struct scene_statistic { + u64 awake; + u64 asleep; +}; + struct history_entry { u32 state; bool valid; diff --git a/dynamic_twt_manager.c b/dynamic_twt_manager.c index 7f22219..e3f965a 100644 --- a/dynamic_twt_manager.c +++ b/dynamic_twt_manager.c @@ -7,30 +7,41 @@ * Author: Star Chang */ #include +#include +#include #include "core.h" static struct dytwt_manager dytwt_mgmt; #define dytwt_get_manager() (&dytwt_mgmt) +#define DYMAIC_TWT_CONFIG_ID 3 + +/* for tcp one pair case */ +#define TWT_IDLE_INTERVAL (500 * 1024) /* 512000 */ +#define TWT_IDLE_DURATION (256 * 32) /* 16384 */ +#define TWT_WEB_INTERVAL (104 * 1024) /* 106496 */ +#define TWT_WEB_DURATION (256 * 32) /* 8192 */ +#define TWT_YOUTUBE_INTERVAL (10 * 1024) /* 10240 */ +#define TWT_YOUTUBE_DURATION (256 * 32) /* 8192 */ + +#define TWT_REASON_MAX (WLAN_PTRACKER_NOTIFY_MAX + 1) static const char *const reason2str[WLAN_PTRACKER_NOTIFY_MAX] = { "tp", "scene_change", "scene_prep", "suspend", "sta_change", }; -#define DYMAIC_TWT_CONFIG_ID 3 -#define TWT_WAKE_DURATION 8192 -#define TWT_IDLE_INTERVAL 512000 -#define TWT_WEB_INTERVAL 106496 -#define TWT_YOUTUBE_INTERVAL 10240 +static const char *const state2str[WLAN_SCENE_MAX] = { + "Idle", "Web", "Youtube", "Low latency", "Throughput" +}; -static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX] = { +static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX + 1] = { { .action = TWT_ACTION_SETUP, .param = { .config_id = DYMAIC_TWT_CONFIG_ID, .nego_type = 0, .trigger_type = 0, - .wake_duration = TWT_WAKE_DURATION, + .wake_duration = TWT_IDLE_DURATION, .wake_interval = TWT_IDLE_INTERVAL, }, }, @@ -40,17 +51,17 @@ static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX] = { .config_id = DYMAIC_TWT_CONFIG_ID, .nego_type = 0, .trigger_type = 0, - .wake_duration = TWT_WAKE_DURATION, + .wake_duration = TWT_WEB_DURATION, .wake_interval = TWT_WEB_INTERVAL, }, }, { - .action = TWT_ACTION_SETUP, + .action = TWT_ACTION_TEARDOWN, .param = { .config_id = DYMAIC_TWT_CONFIG_ID, .nego_type = 0, .trigger_type = 0, - .wake_duration = TWT_WAKE_DURATION, + .wake_duration = TWT_YOUTUBE_DURATION, .wake_interval = TWT_YOUTUBE_INTERVAL, }, }, @@ -70,7 +81,19 @@ static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX] = { .trigger_type = 0, }, }, + /* used for force mode */ + { + .action = TWT_ACTION_SETUP, + .param = { + .config_id = DYMAIC_TWT_CONFIG_ID, + .nego_type = 0, + .trigger_type = 0, + .wake_duration = TWT_IDLE_DURATION, + .wake_interval = TWT_IDLE_INTERVAL, + }, + } }; +#define TWT_ACTION_SIZE ARRAY_SIZE(dytwt_actions) static int dytwt_client_twt_setup(struct wlan_ptracker_client *client, u32 state) { @@ -103,7 +126,7 @@ static int dytwt_client_twt_teardown(struct wlan_ptracker_client *client, u32 st static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) { struct dytwt_cap param; - struct wlan_ptracker_core *core = client->core; + struct dytwt_manager *dytwt = dytwt_get_manager(); int ret; if (!client->dytwt_ops) @@ -114,11 +137,29 @@ static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) ret = client->dytwt_ops->get_cap(client->priv, ¶m); + ptracker_dbg(dytwt->core, "%d, %d, %d, %d\n", param.device_cap, param.peer_cap, + param.link_speed, param.rssi); if (ret) return false; - ptracker_dbg(core, "device: %d, peer: %d\n", param.device_cap, param.peer_cap); - return param.peer_cap && param.device_cap; + if (!param.peer_cap || !param.device_cap) { + ptracker_err(dytwt->core, "dytwt is not enabled due to capability: %d, %d\n", + param.device_cap, param.peer_cap); + return false; + } + + if (param.rssi != 0 && param.rssi < dytwt->rssi_threshold) { + ptracker_err(dytwt->core, "dytwt is not enabled due to rssi %d < %d\n", + param.rssi, dytwt->rssi_threshold); + return false; + } + + if (param.link_speed < dytwt->link_threshold) { + ptracker_err(dytwt->core, "dytwt is not enabled due to linkspeed %d < %d\n", + param.link_speed, dytwt->link_threshold); + return false; + } + return true; } static int dytwt_client_twt_pwrstates(struct wlan_ptracker_client *client, @@ -133,6 +174,18 @@ static int dytwt_client_twt_pwrstates(struct wlan_ptracker_client *client, return client->dytwt_ops->get_pwrstates(client->priv, state); } +static int dytwt_client_twt_get_stats(struct wlan_ptracker_client *client, + struct dytwt_stats *stats) +{ + if (!client->dytwt_ops) + return -EINVAL; + + if (!client->dytwt_ops->get_stats) + return -EINVAL; + + return client->dytwt_ops->get_stats(client->priv, stats); +} + static inline void dytwt_record_get_pwr(u64 asleep, u64 awake, u64 *total, int *percent) { /* for percent */ @@ -146,29 +199,54 @@ static int dytwt_record_priv_read(void *cur, void *next, char *buf, int len) { struct dytwt_entry *c = cur; struct dytwt_entry *n = next; - int period_percent = 0, total_percent; - u64 period_time = 0, total_time; - - /* get total */ - dytwt_record_get_pwr(c->pwr.asleep, c->pwr.awake, &total_time, &total_percent); - - /* get period */ - if (n) { - u64 awake = n->pwr.awake > c->pwr.awake ? - (n->pwr.awake - c->pwr.awake) : c->pwr.awake; - u64 asleep = n->pwr.asleep > c->pwr.asleep ? - (n->pwr.asleep - c->pwr.asleep) : c->pwr.asleep; - dytwt_record_get_pwr(asleep, awake, &period_time, &period_percent); + struct dytwt_manager *dytwt = dytwt_get_manager(); + int period_percent, total_percent; + u64 period_time, total_time; + u64 awake, asleep; + + /* next is the current state */ + if (n->pwr.asleep < c->pwr.asleep) { + struct dytwt_pwr_state pwr; + dytwt_client_twt_pwrstates(dytwt->core->client, &pwr); + awake = pwr.awake - c->pwr.awake; + asleep = pwr.asleep - c->pwr.asleep; + /* get total */ + dytwt_record_get_pwr(pwr.asleep, pwr.awake, &total_time, &total_percent); + } else { + /* get period */ + awake = n->pwr.awake - c->pwr.awake; + asleep = n->pwr.asleep - c->pwr.asleep; + /* get total */ + dytwt_record_get_pwr(c->pwr.asleep, c->pwr.awake, &total_time, &total_percent); } + dytwt_record_get_pwr(asleep, awake, &period_time, &period_percent); return scnprintf(buf, len, - "Applied: %s, Time: %llu (%llu) ms, Percent: %d%% (%d%%) Reason: %s, Rate: %d\n", + "Applied: %s, Time: %llu (%llu) ms, Percent: %d%% (%d%%) Reason: %s, Rate: %d", c->apply ? "TRUE" : "FALSE", period_time, total_time, period_percent, total_percent, reason2str[c->reason], c->rate); } -static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, - struct dytwt_manager *dytwt, struct wlan_scene_event *msg, bool apply) +static void dytwt_counter_update(struct dytwt_manager *dytwt, struct dytwt_pwr_state *pwr) +{ + struct dytwt_counters *counter = &dytwt->counters; + struct dytwt_statistic *stat = &counter->scene[dytwt->prev]; + u64 asleep = pwr->asleep - counter->prev_asleep; + u64 awake = pwr->awake - counter->prev_awake; + u64 count = pwr->count - counter->prev_asleep_cnt; + + stat->asleep += asleep; + stat->awake += awake; + counter->prev_asleep = pwr->asleep; + counter->prev_awake = pwr->awake; + counter->prev_asleep_cnt = pwr->count; + counter->total_awake += awake; + counter->total_sleep += asleep; + counter->total_sleep_cnt += count; +} + +static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt, + struct wlan_scene_event *msg, bool apply) { struct dytwt_entry *entry; @@ -181,10 +259,77 @@ static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, entry->reason = msg->reason; entry->rate = msg->rate; dytwt_client_twt_pwrstates(client, &entry->pwr); + dytwt_counter_update(dytwt, &entry->pwr); /* prev will be used for decided teardown or not. */ dytwt->prev = msg->dst; } +/* This function is running in thread context */ +static int _dytwt_scene_change_handler(struct dytwt_manager *dytwt, + struct wlan_ptracker_client *client) +{ + struct wlan_ptracker_core *core = client->core; + struct wlan_scene_event *msg = &core->fsm.msg; + struct dytwt_scene_action *act; + bool apply = false; + u32 state = msg->dst; + int ret = 0; + + if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) + goto out; + + if (!dytwt_client_twt_cap(client)) { + ptracker_dbg(dytwt->core, "twt is not supported on device or peer\n"); + goto out; + } + + act = &dytwt_actions[state]; + + /* follow action to setup */ + if (act->action == TWT_ACTION_SETUP) { + ret = dytwt_client_twt_setup(client, state); + } else { + /* tear down was applied at state of "perpare_change". */ + apply = true; + } + apply = ret ? false : true; +out: + /* store record of history even twt is not applied */ + dytwt_mgmt_history_store(client, dytwt, msg, apply); + ptracker_dbg(dytwt->core, "twt setup for state: %d, reason: %s, ret: %d\n", + state, reason2str[msg->reason], ret); + return ret; +} + +static void dytwt_delay_setup(struct work_struct *work) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = dytwt->core; + struct wlan_ptracker_client *client; + + if (!core) + return; + + rcu_read_lock(); + client = rcu_dereference(core->client); + _dytwt_scene_change_handler(dytwt, client); + rcu_read_unlock(); +} + +#define TWT_WAIT_STA_READY_TIME 1000 +static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = client->core; + struct wlan_scene_event *msg = &core->fsm.msg; + + if (msg->reason == WLAN_PTRACKER_NOTIFY_STA_CONNECT) + schedule_delayed_work(&dytwt->setup_wq, msecs_to_jiffies(TWT_WAIT_STA_READY_TIME)); + else + _dytwt_scene_change_handler(dytwt, client); + return 0; +} + #define TWT_HISTORY_BUF_SIZE 10240 static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -196,7 +341,7 @@ static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, l buf = vmalloc(TWT_HISTORY_BUF_SIZE); if (!buf) - return 0; + return -ENOMEM; len = wlan_ptracker_history_read(dytwt->hm, buf, TWT_HISTORY_BUF_SIZE); ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); @@ -204,28 +349,136 @@ static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, l return ret; } -static void update_twt_flag(struct wlan_ptracker_core *core) +static const struct file_operations twt_ops = { + .open = simple_open, + .read = twt_read, + .llseek = generic_file_llseek, +}; + +static void dytwt_force_twt_setup(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt) +{ + int ret = 0; + bool apply = false; + u32 state = dytwt->state; + struct wlan_scene_event msg; + struct dytwt_scene_action *act = &dytwt_actions[state]; + + switch(act->action) { + case TWT_ACTION_SETUP: + ret = dytwt_client_twt_setup(client, state); + break; + case TWT_ACTION_TEARDOWN: + ret = dytwt_client_twt_teardown(client, state); + break; + default: + break; + } + apply = ret ? false : true; + msg.dst = dytwt->state; + /* store record of history even twt is not applied */ + dytwt_mgmt_history_store(client, dytwt, &msg, apply); +} + +static inline void twt_enable(struct wlan_ptracker_client *client, bool enable) { struct dytwt_manager *dytwt = dytwt_get_manager(); - if (dytwt->feature_flag & BIT(FEATURE_FLAG_TWT)) - dytwt->feature_flag &= ~BIT(FEATURE_FLAG_TWT); - else + if (enable) { dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); + dytwt_scene_change_handler(client); + } else { + dytwt->state = WLAN_SCENE_TPUT; + dytwt_force_twt_setup(client, dytwt); + dytwt->feature_flag &= ~BIT(FEATURE_FLAG_TWT); + } +} + +#define DYTWT_RUNTIME_TIMER 2000 +static void dytwt_runtime(struct work_struct *work) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_scene_action *act; + struct wlan_ptracker_client *client; + + if (!dytwt->core) + goto end; + + if (dytwt->prev == WLAN_SCENE_MAX) + goto end; + + rcu_read_lock(); + client = rcu_dereference(dytwt->core->client); + + if (!client) + goto unlock; + + act = &dytwt_actions[dytwt->prev]; + if (act->action == TWT_ACTION_SETUP && !dytwt_client_twt_cap(client)) { + dytwt->state = WLAN_SCENE_TPUT; + ptracker_dbg(dytwt->core, "teardown twt due to hit threshold\n"); + dytwt_force_twt_setup(client, dytwt); + } +unlock: + rcu_read_unlock(); +end: + schedule_delayed_work(&dytwt->wq, msecs_to_jiffies(DYTWT_RUNTIME_TIMER)); +} + +static void update_twt_flag(struct wlan_ptracker_core *core, struct dytwt_manager *dytwt) +{ + twt_enable(core->client, !(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))); +} + +static void update_twt_parameters(struct dytwt_manager *dytwt) +{ + u32 state = dytwt->state; + struct dytwt_scene_action *cfg_act = &dytwt_actions[WLAN_SCENE_MAX]; + struct dytwt_scene_action *cur_act = &dytwt_actions[state]; + + cur_act->param.wake_duration = cfg_act->param.wake_duration; + cur_act->param.wake_interval = cfg_act->param.wake_interval; + cur_act->action = cfg_act->action; +} + +static void dytwt_stats_dump(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt) +{ + struct dytwt_stats stats; + struct wlan_ptracker_core *core = dytwt->core; + + stats.config_id = DYMAIC_TWT_CONFIG_ID; + dytwt_client_twt_get_stats(client, &stats); + + ptracker_info(core, "rx_ucast_pkts: %d, rx_pkts_retried: %d\n", + stats.rx_ucast_pkts, stats.rx_pkts_retried); + ptracker_info(core, "rx_pkt_sz_avg: %d, rx_pkts_avg: %d\n", + stats.rx_pkt_sz_avg, stats.rx_pkts_avg); + ptracker_info(core, "rx_pkts_min: %d, rx_pkts_max: %d\n", + stats.rx_pkts_min, stats.rx_pkts_max); + ptracker_info(core, "tx_ucast_pkts: %d, tx_failures: %d\n", + stats.tx_ucast_pkts, stats.tx_failures); + ptracker_info(core, "tx_pkt_sz_avg: %d, tx_pkts_avg: %d\n", + stats.tx_pkt_sz_avg, stats.tx_pkts_avg); + ptracker_info(core, "tx_pkts_min: %d, tx_pkts_max: %d\n", + stats.tx_pkts_min, stats.tx_pkts_max); + ptracker_info(core, "sp_seq: %d, eosp_count: %d, eosp_dur_avg: %d\n", + stats.sp_seq, stats.eosp_count, stats.eosp_dur_avg); } static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) { struct dytwt_pwr_state pwr_state; struct dytwt_manager *dytwt = dytwt_get_manager(); - struct wlan_ptracker_client *client = core->client; + struct wlan_ptracker_client *client; + + rcu_read_lock(); + client = rcu_dereference(core->client); + + if (!client) + goto unlock; switch (action) { - case TWT_TEST_SETUP: - dytwt_client_twt_setup(client, dytwt->state); - break; - case TWT_TEST_TEARDOWN: - dytwt_client_twt_teardown(client, dytwt->state); + case TWT_TEST_FORCE_STATE: + dytwt_force_twt_setup(client, dytwt); break; case TWT_TEST_CAP: dytwt_client_twt_cap(client); @@ -234,16 +487,25 @@ static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) dytwt_client_twt_pwrstates(client, &pwr_state); break; case TWT_TEST_ONOFF: - update_twt_flag(core); + update_twt_flag(core, dytwt); + break; + case TWT_TEST_SET_PARAM: + update_twt_parameters(dytwt); + break; + case TWT_TEST_DUMP_STATS: + dytwt_stats_dump(client, dytwt); break; default: ptracker_err(core, "action %d is not supported!\n", action); return -ENOTSUPP; } +unlock: + rcu_read_unlock(); return 0; } -static ssize_t twt_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +static ssize_t twt_params_write(struct file *file, const char __user *buf, size_t len, + loff_t *ppos) { struct wlan_ptracker_core *core = file->private_data; u32 action; @@ -254,71 +516,140 @@ static ssize_t twt_write(struct file *file, const char __user *buf, size_t len, return dytwt_debugfs_action(core, action); } -static const struct file_operations twt_ops = { +static int dytwt_params_read(char *buf, int len) +{ + struct dytwt_scene_action *act; + struct dytwt_setup_param *param; + int count = 0; + int i; + + count += scnprintf(buf + count, len - count, + "===================\n"); + for (i = 0 ; i < TWT_ACTION_SIZE; i++) { + act = &dytwt_actions[i]; + param = &act->param; + count += scnprintf(buf + count, len - count, + "state: %d, action: %d\n", i, act->action); + count += scnprintf(buf + count, len - count, + "config_id: %d, nego_type: %d\n", + param->config_id, param->nego_type); + count += scnprintf(buf + count, len - count, + "wake_interval: %u\n", param->wake_interval); + count += scnprintf(buf + count, len - count, + "wake_duration: %u\n", param->wake_duration); + count += scnprintf(buf + count, len - count, + "===================\n"); + } + return count; +} + +#define TWT_PARAM_BUF_SIZE 1024 +static ssize_t twt_params_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) +{ + char *buf; + int len; + int ret; + + buf = vmalloc(TWT_PARAM_BUF_SIZE); + if (!buf) + return -ENOMEM; + len = dytwt_params_read(buf, TWT_PARAM_BUF_SIZE); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); + vfree(buf); + return ret; +} + +static const struct file_operations twt_params_ops = { .open = simple_open, - .read = twt_read, - .write = twt_write, + .read = twt_params_read, + .write = twt_params_write, .llseek = generic_file_llseek, }; -/* This function is running in thread context */ -#define TWT_WAIT_STA_READY_TIME 1000 -static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) +static int dytwt_statistic_read(char *buf, int len) { - struct wlan_ptracker_core *core = client->core; - struct wlan_scene_event *msg = &core->fsm.msg; - struct dytwt_scene_action *act; struct dytwt_manager *dytwt = dytwt_get_manager(); - bool apply = false; - u32 state = msg->dst; - int ret = 0; - - if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) - goto out; - - if (!dytwt_client_twt_cap(client)) { - ptracker_dbg(core, "twt is not supported on device or peer\n"); - goto out; + struct dytwt_counters *counter = &dytwt->counters; + struct dytwt_statistic *ds; + struct dytwt_pwr_state pwr; + int buf_count = 0; + int i, percent; + u64 total, awake, asleep, count; + + buf_count += scnprintf(buf + buf_count, len - buf_count, + "==== Dynamic TWT Setup Statistics ===\n"); + dytwt_client_twt_pwrstates(dytwt->core->client, &pwr); + for (i = 0 ; i < WLAN_SCENE_MAX; i++) { + ds = &counter->scene[i]; + awake = ds->awake; + asleep = ds->asleep; + if (i == dytwt->prev) { + awake += pwr.awake - counter->prev_awake; + asleep += pwr.asleep - counter->prev_asleep; + } + dytwt_record_get_pwr(asleep, awake, &total, &percent); + buf_count += scnprintf(buf + buf_count, len - buf_count, + "%s, total: %llu, awake: %llu, asleep: %llu (%d%%)\n", state2str[i], total, + awake / 1000, asleep / 1000, percent); } - act = &dytwt_actions[state]; - ptracker_dbg(core, "twt setup for state: %d, reason: %s!\n", - state, reason2str[msg->reason]); + awake = counter->total_awake + pwr.awake - counter->prev_awake; + asleep = counter->total_sleep + pwr.asleep - counter->prev_asleep; + count = counter->total_sleep_cnt + pwr.count - counter->prev_asleep_cnt; + dytwt_record_get_pwr(asleep, awake, &total, &percent); + buf_count += scnprintf(buf + buf_count, len - buf_count, + "All, total: %llu, awake: %llu, asleep: %llu (%d%%), sleep cnt: %llu\n", + total, awake / 1000, asleep / 1000, percent, count); + return buf_count; +} - /* wait for sta ready after connected. */ - if (msg->reason == WLAN_PTRACKER_NOTIFY_STA_CHANGE) - msleep(TWT_WAIT_STA_READY_TIME); +#define TWT_STATISTIC_SIZE 512 +static ssize_t twt_statistic_read(struct file *file, char __user *userbuf, size_t count, + loff_t *ppos) +{ + char *buf; + int len; + int ret; - /* follow action to setup */ - if (act->action == TWT_ACTION_SETUP) { - ret = dytwt_client_twt_setup(client, state); - } else { - /* tear down was apply during state of perpare change. */ - apply = true; - } - apply = ret ? false : true; -out: - /* store record of hostory even twt is not applid! */ - dytwt_mgmt_history_store(client, dytwt, msg, apply); + buf = vmalloc(TWT_STATISTIC_SIZE); + if (!buf) + return -ENOMEM; + + len = dytwt_statistic_read(buf, TWT_STATISTIC_SIZE); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); + vfree(buf); return ret; } +static const struct file_operations twt_statistic_ops = { + .open = simple_open, + .read = twt_statistic_read, + .llseek = generic_file_llseek, +}; + static void dytwt_scene_change_prepare_handler(struct wlan_ptracker_client *client) { struct dytwt_manager *dytwt = dytwt_get_manager(); + u32 prev_state = dytwt->prev; + + if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) + return; /* prepare to change state teardown original setup first */ - if (dytwt->prev < WLAN_SCENE_LOW_LATENCY) + if (dytwt_actions[prev_state].action == TWT_ACTION_SETUP) dytwt_client_twt_teardown(client, dytwt->prev); } static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event, void *ptr) { struct wlan_ptracker_core *core = ptr; - struct wlan_ptracker_client *client = core->client; + struct wlan_ptracker_client *client; + struct dytwt_manager *dytwt = dytwt_get_manager(); + rcu_read_lock(); + client = rcu_dereference(core->client); if (!client) - return NOTIFY_OK; + goto unlock; switch (event) { case WLAN_PTRACKER_NOTIFY_SCENE_CHANGE: @@ -327,9 +658,23 @@ static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event case WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE: dytwt_scene_change_prepare_handler(client); break; + case WLAN_PTRACKER_NOTIFY_STA_CONNECT: + schedule_delayed_work(&dytwt->wq, msecs_to_jiffies(DYTWT_RUNTIME_TIMER)); + break; + case WLAN_PTRACKER_NOTIFY_STA_DISCONNECT: + cancel_delayed_work_sync(&dytwt->wq); + break; + case WLAN_PTRACKER_NOTIFY_DYTWT_ENABLE: + twt_enable(client, true); + break; + case WLAN_PTRACKER_NOTIFY_DYTWT_DISABLE: + twt_enable(client, false); + break; default: break; } +unlock: + rcu_read_unlock(); return NOTIFY_OK; } @@ -337,18 +682,28 @@ static int dytwt_debugfs_init(struct wlan_ptracker_core *core) { struct wlan_ptracker_debugfs *debugfs = &core->debugfs; struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_scene_action *act = &dytwt_actions[WLAN_SCENE_MAX]; dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); dytwt->dir = debugfs_create_dir("twt", debugfs->root); if (!dytwt->dir) return -ENODEV; - debugfs_create_file("history", 0600, dytwt->dir, core, &twt_ops); - debugfs_create_u32("state", 0600, dytwt->dir, &dytwt->state); + + debugfs_create_file("history", 0666, dytwt->dir, core, &twt_ops); + debugfs_create_file("statistics", 0666, dytwt->dir, core, &twt_statistic_ops); + debugfs_create_file("twt_params", 0666, dytwt->dir, core, &twt_params_ops); + debugfs_create_u32("state", 0666, dytwt->dir, &dytwt->state); + debugfs_create_u32("wake_interval", 0666, dytwt->dir, &act->param.wake_interval); + debugfs_create_u32("wake_duration", 0666, dytwt->dir, &act->param.wake_duration); + debugfs_create_u32("action", 0666, dytwt->dir, &act->action); + debugfs_create_u32("feature_flag", 0666, dytwt->dir, &dytwt->feature_flag); return 0; } -#define DYTWT_RECORD_MAX 50 -static int dytwt_mgmt_init(void) +#define TWT_DEFAULT_MIN_LINK_SPEED (90000) +#define TWT_DEFAULT_MIN_RSSI (-70) +#define DYTWT_RECORD_MAX 30 +static int dytwt_mgmt_init(struct wlan_ptracker_core *core) { struct dytwt_manager *dytwt = dytwt_get_manager(); struct history_manager *hm; @@ -358,6 +713,11 @@ static int dytwt_mgmt_init(void) memset(dytwt, 0, sizeof(*dytwt)); dytwt->state = WLAN_SCENE_IDLE; dytwt->prev = WLAN_SCENE_MAX; + dytwt->core = core; + dytwt->link_threshold = TWT_DEFAULT_MIN_LINK_SPEED; + dytwt->rssi_threshold = TWT_DEFAULT_MIN_RSSI; + INIT_DELAYED_WORK(&dytwt->wq, dytwt_runtime); + INIT_DELAYED_WORK(&dytwt->setup_wq, dytwt_delay_setup); hm = wlan_ptracker_history_create(DYTWT_RECORD_MAX, sizeof(struct dytwt_entry)); if (!hm) return -ENOMEM; @@ -371,6 +731,8 @@ static void dytwt_mgmt_exit(void) { struct dytwt_manager *dytwt = dytwt_get_manager(); + cancel_delayed_work_sync(&dytwt->wq); + cancel_delayed_work_sync(&dytwt->setup_wq); if (dytwt->dir) debugfs_remove_recursive(dytwt->dir); @@ -385,7 +747,7 @@ static struct notifier_block twt_nb = { int dytwt_init(struct wlan_ptracker_core *core) { - dytwt_mgmt_init(); + dytwt_mgmt_init(core); dytwt_debugfs_init(core); return wlan_ptracker_register_notifier(&core->notifier, &twt_nb); } @@ -395,4 +757,3 @@ void dytwt_exit(struct wlan_ptracker_core *core) dytwt_mgmt_exit(); return wlan_ptracker_unregister_notifier(&core->notifier, &twt_nb); } - diff --git a/dynamic_twt_manager.h b/dynamic_twt_manager.h index fe8b289..a7ecd55 100644 --- a/dynamic_twt_manager.h +++ b/dynamic_twt_manager.h @@ -25,11 +25,33 @@ struct dytwt_setup_param { struct dytwt_cap { u16 device_cap; u16 peer_cap; + u32 rssi; + int link_speed; }; struct dytwt_pwr_state { u64 awake; u64 asleep; + u64 count; +}; + +struct dytwt_stats { + u32 config_id; + u32 sp_seq; /* sequence number of the service period */ + u32 tx_ucast_pkts; /* Number of unicast Tx packets in TWT SPs */ + u32 tx_pkts_min; /* Minimum number of Tx packets in a TWT SP */ + u32 tx_pkts_max; /* Maximum number of Tx packets in a TWT SP */ + u32 tx_pkts_avg; /* Average number of Tx packets in each TWT SP */ + u32 tx_failures; /* Tx packets failure count */ + u32 rx_ucast_pkts; /* Number of unicast Rx packets in TWT SPs */ + u32 rx_pkts_min; /* Minimum number of Rx packets in a TWT SP */ + u32 rx_pkts_max; /* Maximum number of Rx packets in a TWT SP */ + u32 rx_pkts_avg; /* Average number of Rx packets in each TWT SP */ + u32 rx_pkts_retried; /* retried Rx packets count */ + u32 tx_pkt_sz_avg; /* Average Tx packet size in TWT SPs */ + u32 rx_pkt_sz_avg; /* Average Rx Packet size in TWT SPs */ + u32 eosp_dur_avg; /* Average Wake duration in SPs ended due to EOSP */ + u32 eosp_count; /* Count of TWT SPs ended due to EOSP */ }; struct dytwt_client_ops { @@ -37,6 +59,7 @@ struct dytwt_client_ops { int (*teardown)(void *priv, struct dytwt_setup_param *param); int (*get_cap)(void *priv, struct dytwt_cap *cap); int (*get_pwrstates)(void *priv, struct dytwt_pwr_state *state); + int (*get_stats)(void *priv, struct dytwt_stats *stats); }; enum { @@ -46,11 +69,12 @@ enum { }; enum { - TWT_TEST_SETUP, - TWT_TEST_TEARDOWN, + TWT_TEST_FORCE_STATE = 1, TWT_TEST_CAP, TWT_TEST_PWRSTATS, TWT_TEST_ONOFF, + TWT_TEST_SET_PARAM, + TWT_TEST_DUMP_STATS, TWT_TEST_MAX, }; @@ -68,12 +92,34 @@ struct dytwt_entry { struct dytwt_pwr_state pwr; } __align(void *); +struct dytwt_statistic { + u64 awake; + u64 asleep; +}; + +#define DYTWT_COUNTER_MAX 6 +#define DYTWT_COUNTER_TOTAL 5 +struct dytwt_counters { + u64 total_awake; + u64 total_sleep; + u64 total_sleep_cnt; + u64 prev_awake; + u64 prev_asleep; + u64 prev_asleep_cnt; + struct dytwt_statistic scene[DYTWT_COUNTER_MAX]; +}; struct dytwt_manager { u32 prev; u32 feature_flag; u32 state; struct history_manager *hm; + u32 rssi_threshold; + u32 link_threshold; + struct delayed_work wq; + struct delayed_work setup_wq; + struct wlan_ptracker_core *core; + struct dytwt_counters counters; struct dentry *dir; }; diff --git a/main.c b/main.c index 85f18e7..dc3eeed 100644 --- a/main.c +++ b/main.c @@ -82,7 +82,9 @@ int wlan_ptracker_register_client(struct wlan_ptracker_client *client) struct wlan_ptracker_core *core = get_core(); if (!core->client) { - core->client = client; + rcu_read_lock(); + rcu_assign_pointer(core->client, client); + rcu_read_unlock(); client->cb = client_event_handler; } return 0; @@ -95,7 +97,9 @@ void wlan_ptracker_unregister_client(struct wlan_ptracker_client *client) if (core->client == client) { client->cb = NULL; - core->client = NULL; + rcu_read_lock(); + rcu_assign_pointer(core->client, NULL); + rcu_read_unlock(); } } EXPORT_SYMBOL_GPL(wlan_ptracker_unregister_client); diff --git a/scenes_fsm.c b/scenes_fsm.c index 991863e..768c75d 100644 --- a/scenes_fsm.c +++ b/scenes_fsm.c @@ -6,12 +6,12 @@ * * Author: Star Chang */ +#include #include "core.h" -#define fsm_to_core(fsm) \ - (container_of(fsm, struct wlan_ptracker_core, fsm)) +#define fsm_to_core(fsm) (container_of(fsm, struct wlan_ptracker_core, fsm)) -static const struct wlan_state_condition conditions[FSM_STATE_MAX] = { +static struct wlan_state_condition conditions[FSM_STATE_MAX] = { { .scene = WLAN_SCENE_IDLE, .ac_mask = WMM_AC_ALL_MASK, @@ -22,14 +22,13 @@ static const struct wlan_state_condition conditions[FSM_STATE_MAX] = { .scene = WLAN_SCENE_WEB, .ac_mask = WMM_AC_ALL_MASK, .min_tp_threshold = 1000, - .max_tp_threshold = 10000, + .max_tp_threshold = 9000, }, { .scene = WLAN_SCENE_YOUTUBE, .ac_mask = WMM_AC_ALL_MASK, - /* Total >= 10 Mbps && < 50 Mbps */ - .min_tp_threshold = 10000, - .max_tp_threshold = 50000, + .min_tp_threshold = 9000, + .max_tp_threshold = 60000, }, { .scene = WLAN_SCENE_LOW_LATENCY, @@ -41,8 +40,7 @@ static const struct wlan_state_condition conditions[FSM_STATE_MAX] = { { .scene = WLAN_SCENE_TPUT, .ac_mask = WMM_AC_ALL_MASK, - /* Total >= 50 Mbps */ - .min_tp_threshold = 50000, + .min_tp_threshold = 60000, .max_tp_threshold = __INT_MAX__, }, }; @@ -53,7 +51,7 @@ static int fsm_thread(void *param) struct wlan_scene_event *msg = &fsm->msg; struct wlan_ptracker_core *core = fsm_to_core(fsm); - while (1) { + while (fsm->thread_run) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) { ptracker_info(core, "kthread is stopped\n"); @@ -61,26 +59,14 @@ static int fsm_thread(void *param) } wait_for_completion(&fsm->event); ptracker_dbg(core, "state: %d, trans state %d -> %d, rate %llu\n", - msg->state, msg->src, msg->dst, msg->rate); - - /* - * Request twice of transmit events are happing then trans state, - * to make sure the state is stable enough. - * first time: confirm is false, send prepare first. - * (ex: twt can tear down original setup first) - * second time: confirm is true and change the state to dst. - */ - if (fsm->confirm) { - wlan_ptracker_call_chain(&core->notifier, - WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, core); - msg->state = msg->dst; - fsm->confirm = false; - } else { - /* call notifier chain */ - wlan_ptracker_call_chain(&core->notifier, + msg->state, msg->src, msg->dst, msg->rate); + wlan_ptracker_call_chain(&core->notifier, WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, core); - fsm->confirm = true; - } + fsm->confirm = true; + wlan_ptracker_call_chain(&core->notifier, + WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, core); + msg->state = msg->dst; + fsm->confirm = false; } return 0; } @@ -149,15 +135,20 @@ static void scenes_fsm_decision(struct wlan_ptracker_core *core, u32 type) /* reset check */ if (type == WLAN_PTRACKER_NOTIFY_SUSPEND) { fsm->reset_cnt++; - except = !(fsm->reset_cnt % RESET_THRESHOLD); + except = (fsm->reset_cnt >= RESET_THRESHOLD) ? true : false; } /* check state isn't change and not first time do nothing */ - if (new_state == msg->state && type != WLAN_PTRACKER_NOTIFY_STA_CHANGE) + if (new_state == msg->state && + type != WLAN_PTRACKER_NOTIFY_STA_CONNECT) return; /* new state must higher then current state */ - if (new_state < msg->state && !except) + if (new_state < msg->state && !except) { + ptracker_dbg(core, + "state not change since new state %d < old state %d and reset_cnt is %d\n", + new_state, msg->state, fsm->reset_cnt); return; + } ptracker_dbg(core, "type %d, reset_cnt %d, %d -> %d\n", type, fsm->reset_cnt, msg->state, new_state); @@ -180,6 +171,7 @@ static int scene_notifier_handler(struct notifier_block *nb, { struct wlan_ptracker_core *core = ptr; struct wlan_ptracker_notifier *notifier = &core->notifier; + struct wlan_ptracker_fsm *fsm = &core->fsm; /* * Events of suspen and sta change will block wlan driver @@ -187,11 +179,14 @@ static int scene_notifier_handler(struct notifier_block *nb, */ switch (event) { case WLAN_PTRACKER_NOTIFY_SUSPEND: +#ifdef TP_DEBUG ptracker_dbg(core, "update time (%d)\n", jiffies_to_msecs(jiffies - notifier->prev_event)); +#endif notifier->prev_event = jiffies; - case WLAN_PTRACKER_NOTIFY_STA_CHANGE: + case WLAN_PTRACKER_NOTIFY_STA_CONNECT: case WLAN_PTRACKER_NOTIFY_TP: + fsm->confirm = true; scenes_fsm_decision(core, event); break; default: @@ -205,6 +200,107 @@ static struct notifier_block scene_nb = { .notifier_call = scene_notifier_handler, }; +static int scene_cond_set(struct wlan_ptracker_fsm *fsm) +{ + struct wlan_state_condition *param = &conditions[fsm->state]; + + param->ac_mask = fsm->ac_mask; + param->max_tp_threshold = fsm->max_tput; + param->min_tp_threshold = fsm->min_tput; + return 0; +} + +static int scene_debugfs_action(struct wlan_ptracker_core *core, u32 action) +{ + struct wlan_ptracker_fsm *fsm = &core->fsm; + switch (action) { + case SCENE_TEST_SET_PARAM: + scene_cond_set(fsm); + break; + default: + ptracker_err(core, "action %d is not supported\n", action); + break; + } + return 0; +} + +static ssize_t scene_params_write(struct file *file, + const char __user *buf, size_t len, loff_t *ppos) +{ + struct wlan_ptracker_core *core = file->private_data; + u32 action; + + if (kstrtouint_from_user(buf, len, 10, &action)) + return -EFAULT; + + /* active action */ + scene_debugfs_action(core, action); + return 0; +} + +static int _scene_params_read(char *buf, int len) +{ + struct wlan_state_condition *param; + int count = 0; + int i; + + count += scnprintf(buf + count, len - count, + "===================\n"); + for (i = 0 ; i < FSM_STATE_MAX; i++) { + param = &conditions[i]; + count += scnprintf(buf + count, len - count, + "state: %d, ac_mask: %#0X\n", i, param->ac_mask); + count += scnprintf(buf + count, len - count, + "min_tp_threshold: %u\n", param->min_tp_threshold); + count += scnprintf(buf + count, len - count, + "max_tp_threshold: %u\n", param->max_tp_threshold); + count += scnprintf(buf + count, len - count, + "===================\n"); + } + return count; +} + +#define SCENE_PARAM_BUF_SIZE 1024 +static ssize_t scene_params_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char *buf; + int len; + int ret; + + buf = vmalloc(SCENE_PARAM_BUF_SIZE); + if (!buf) + return -ENOMEM; + len = _scene_params_read(buf, SCENE_PARAM_BUF_SIZE); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); + vfree(buf); + return ret; +} + +static const struct file_operations scene_params_ops = { + .open = simple_open, + .read = scene_params_read, + .write = scene_params_write, + .llseek = generic_file_llseek, +}; + +static int scene_debugfs_init(struct wlan_ptracker_core *core) +{ + struct wlan_ptracker_debugfs *debugfs = &core->debugfs; + struct wlan_ptracker_fsm *fsm = &core->fsm; + + fsm->dir = debugfs_create_dir("scene", debugfs->root); + if (!fsm->dir) + return -ENODEV; + + debugfs_create_file("scene_params", 0600, fsm->dir, core, &scene_params_ops); + debugfs_create_u32("state", 0600, fsm->dir, &fsm->state); + debugfs_create_u32("min_tput", 0600, fsm->dir, &fsm->min_tput); + debugfs_create_u32("max_tput", 0600, fsm->dir, &fsm->max_tput); + debugfs_create_u32("ac_mask", 0600, fsm->dir, &fsm->ac_mask); + return 0; +} + int scenes_fsm_init(struct wlan_ptracker_fsm *fsm) { struct wlan_scene_event *msg = &fsm->msg; @@ -221,6 +317,7 @@ int scenes_fsm_init(struct wlan_ptracker_fsm *fsm) msg->src = WLAN_SCENE_IDLE; msg->state = WLAN_SCENE_IDLE; mutex_init(&msg->lock); + scene_debugfs_init(core); /*scene event notifier handler from client */ ret = wlan_ptracker_register_notifier(&core->notifier, &scene_nb); @@ -236,6 +333,7 @@ int scenes_fsm_init(struct wlan_ptracker_fsm *fsm) ptracker_err(core, "unable to start kernel thread %d\n", ret); return ret; } + fsm->thread_run = true; wake_up_process(fsm->fsm_thread); return 0; } @@ -244,15 +342,18 @@ void scenes_fsm_exit(struct wlan_ptracker_fsm *fsm) { struct wlan_ptracker_core *core = fsm_to_core(fsm); + if (fsm->dir) + debugfs_remove_recursive(fsm->dir); + wlan_ptracker_unregister_notifier(&core->notifier, &scene_nb); + fsm->thread_run = false; + complete(&fsm->event); if (fsm->fsm_thread) { int ret = kthread_stop(fsm->fsm_thread); fsm->fsm_thread = NULL; if (ret) ptracker_err(core, "stop thread fail: %d\n", ret); } - complete(&fsm->event); fsm->conditions = NULL; fsm->reset_cnt = 0; } - diff --git a/scenes_fsm.h b/scenes_fsm.h index b79a78f..49b1a23 100644 --- a/scenes_fsm.h +++ b/scenes_fsm.h @@ -34,6 +34,11 @@ enum { FSM_STATE_MAX }; +enum { + SCENE_TEST_SET_PARAM, + SCENE_TEST_MAX, +}; + struct wlan_state_condition { u32 scene; u32 ac_mask; @@ -56,10 +61,17 @@ struct wlan_scene_event { struct wlan_ptracker_fsm { int reset_cnt; bool confirm; + bool thread_run; struct completion event; struct wlan_scene_event msg; struct task_struct *fsm_thread; const struct wlan_state_condition *conditions; + /* debug usage */ + struct dentry *dir; + u32 state; + u32 min_tput; + u32 max_tput; + u32 ac_mask; }; extern int scenes_fsm_init(struct wlan_ptracker_fsm *fsm); diff --git a/tp_monitor.c b/tp_monitor.c index 6e04d73..0fac073 100644 --- a/tp_monitor.c +++ b/tp_monitor.c @@ -28,6 +28,12 @@ static void tp_rate_pps_update(struct tp_monitor_counts *counts) count->pps = cur_cnt - count->pre_packet_cnt; count->pre_packet_cnt = cur_cnt; count->pre_packet_bytes = cur_bytes; +#ifdef TP_DEBUG + count->max_packet_bytes = max(count->max_packet_bytes, count->packet_bytes); + count->max_packet_cnt = max(count->max_packet_cnt, count->packet_cnt); + count->max_pps = max(count->max_pps, count->pps); + count->max_rate = max(count->max_rate, count->rate); +#endif /* TP_DEBUG */ } } @@ -141,10 +147,14 @@ static int tp_show(struct seq_file *s, void *unused) seq_printf(s, "AC %d ->\n", i); else seq_puts(s, "Total ->\n"); - seq_printf(s, "packet_cnt : %llu\n", counter->packet_cnt); - seq_printf(s, "packet_bytes : %llu\n", counter->packet_bytes); - seq_printf(s, "rate (Kbits) : %llu\n", counter->rate / 1000); - seq_printf(s, "pps : %llu\n", counter->pps); + seq_printf(s, "packet_cnt : %llu (%llu)\n", + counter->packet_cnt, counter->max_packet_cnt); + seq_printf(s, "packet_bytes : %llu (%llu)\n", + counter->packet_bytes, counter->max_packet_bytes); + seq_printf(s, "rate (Kbits) : %llu (%llu)\n", + counter->rate / 1000, counter->max_rate / 1000); + seq_printf(s, "pps : %llu (%llu)\n", + counter->pps, counter->pps); } return 0; } @@ -165,17 +175,26 @@ static int tp_monitor_debugfs_init(struct wlan_ptracker_core *core) { struct wlan_ptracker_debugfs *debugfs = &core->debugfs; struct tp_monitor_stats *stats = &core->tp; - struct wlan_ptracker_client *client = core->client; + struct wlan_ptracker_client *client; + int ret = 0; - if (!client) - return 0; + rcu_read_lock(); + client = rcu_dereference(core->client); + if (!client) { + ret = -ENODEV; + goto out; + } stats->dir = debugfs_create_dir(client->ifname, debugfs->root); - if (!stats->dir) - return -ENODEV; + if (!stats->dir) { + ret = -ENODEV; + goto out; + } debugfs_create_u32("log_level", 0600, stats->dir, &stats->debug); debugfs_create_file("tx", 0400, stats->dir, &stats->tx, &counter_ops); debugfs_create_file("rx", 0400, stats->dir, &stats->rx, &counter_ops); - return 0; +out: + rcu_read_unlock(); + return ret; } int tp_monitor_init(struct tp_monitor_stats *stats) diff --git a/tp_monitor.h b/tp_monitor.h index 05319a1..1694099 100644 --- a/tp_monitor.h +++ b/tp_monitor.h @@ -29,6 +29,10 @@ struct tp_monitor_counts { u64 pre_packet_cnt; u64 rate; u64 pps; + u64 max_pps; + u64 max_packet_cnt; + u64 max_packet_bytes; + u64 max_rate; }; struct tp_monitor_stats { diff --git a/wlan_ptracker_client.h b/wlan_ptracker_client.h index 4933960..c4cc3aa 100644 --- a/wlan_ptracker_client.h +++ b/wlan_ptracker_client.h @@ -18,10 +18,16 @@ enum { WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, WLAN_PTRACKER_NOTIFY_SUSPEND, - WLAN_PTRACKER_NOTIFY_STA_CHANGE, + WLAN_PTRACKER_NOTIFY_STA_CONNECT, + WLAN_PTRACKER_NOTIFY_STA_DISCONNECT, + WLAN_PTRACKER_NOTIFY_DYTWT_ENABLE, + WLAN_PTRACKER_NOTIFY_DYTWT_DISABLE, WLAN_PTRACKER_NOTIFY_MAX, }; +/* backword compatible */ +#define WLAN_PTRACKER_NOTIFY_SUSPEN WLAN_PTRACKER_NOTIFY_SUSPEND + struct wlan_ptracker_client { void *priv; void *core; @@ -34,4 +40,3 @@ extern int wlan_ptracker_register_client(struct wlan_ptracker_client *client); extern void wlan_ptracker_unregister_client(struct wlan_ptracker_client *client); #endif /*__WLAN_PTRACKER_CLIENT_H*/ - -- cgit v1.2.3 From a8878c34c747769c7a069910d26d94de4efac03c Mon Sep 17 00:00:00 2001 From: Star Chang Date: Tue, 27 Sep 2022 03:50:03 +0000 Subject: wlan_ptracker: add dumpstate support 1. add support dumpstate 2. fix kernel exception issue Bug: 253348062 Test: ST-stability/WiFi regression/WiFi performance Test pass Signed-off-by: Star Chang Change-Id: I55610531fbe21f870d28605b077b3adce742ed47 --- debugfs.c | 21 ++++++- debugfs.h | 3 + dynamic_twt_manager.c | 158 +++++++++++++++++++++++++++++++++++++------------- dynamic_twt_manager.h | 15 ++++- notifier.c | 4 ++ scenes_fsm.c | 13 +---- scenes_fsm.h | 2 - 7 files changed, 160 insertions(+), 56 deletions(-) diff --git a/debugfs.c b/debugfs.c index 3cf4dc4..6fe6d99 100644 --- a/debugfs.c +++ b/debugfs.c @@ -85,6 +85,20 @@ static const struct file_operations dscp_ops = { .llseek = generic_file_llseek, }; +static int wlan_ptracker_sysfs_init(struct wlan_ptracker_debugfs *debugfs) +{ + debugfs->kobj = kobject_create_and_add("wlan_ptracker", NULL); + if (!debugfs->kobj) + return -ENODEV; + return 0; +} + +static void wlan_ptracker_sysfs_exit(struct wlan_ptracker_debugfs *debugfs) +{ + if (debugfs->kobj) + kobject_put(debugfs->kobj); +} + int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs) { struct wlan_ptracker_core *core = container_of( @@ -96,6 +110,7 @@ int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs) debugfs_create_file("action", 0600, debugfs->root, core, &dscp_ops); debugfs_create_u32("dscp", 0600, debugfs->root, &debugfs->dscp); debugfs_create_u32("ac", 0600, debugfs->root, &debugfs->ac); + wlan_ptracker_sysfs_init(debugfs); return 0; } @@ -103,6 +118,7 @@ void wlan_ptracker_debugfs_exit(struct wlan_ptracker_debugfs *debugfs) { debugfs_remove_recursive(debugfs->root); debugfs->root = NULL; + wlan_ptracker_sysfs_exit(debugfs); } struct history_manager *wlan_ptracker_history_create(int entry_count, int entry_size) @@ -179,14 +195,13 @@ size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int buf cur = (struct history_entry *) ptr; if (!cur->valid) break; - j = (i + hm->entry_count + 1) % hm->entry_count; + j = (i + 1) % hm->entry_count; next = (struct history_entry *)(hm->entries + (j * hm->entry_size)); len += scnprintf(buf + len, buf_len - len, "%02d: ", i); len += history_get_tm(cur, buf + len, buf_len - len); len += scnprintf(buf + len, buf_len - len, "%12s =>", state2str[cur->state]); if (hm->priv_read) - len += hm->priv_read(cur, next->valid ? next : NULL, buf + len, - buf_len - len); + len += hm->priv_read(cur, next, buf + len, buf_len - len); len += scnprintf(buf + len, buf_len - len, "\n"); ptr += hm->entry_size; } diff --git a/debugfs.h b/debugfs.h index 721cc49..2d4bfd4 100644 --- a/debugfs.h +++ b/debugfs.h @@ -11,9 +11,12 @@ #include #include #include +#include +#include struct wlan_ptracker_debugfs { struct dentry *root; + struct kobject *kobj; u32 dscp; u32 ac; u32 action; diff --git a/dynamic_twt_manager.c b/dynamic_twt_manager.c index e3f965a..2cf7aa3 100644 --- a/dynamic_twt_manager.c +++ b/dynamic_twt_manager.c @@ -18,16 +18,25 @@ static struct dytwt_manager dytwt_mgmt; #define DYMAIC_TWT_CONFIG_ID 3 /* for tcp one pair case */ -#define TWT_IDLE_INTERVAL (500 * 1024) /* 512000 */ -#define TWT_IDLE_DURATION (256 * 32) /* 16384 */ -#define TWT_WEB_INTERVAL (104 * 1024) /* 106496 */ -#define TWT_WEB_DURATION (256 * 32) /* 8192 */ -#define TWT_YOUTUBE_INTERVAL (10 * 1024) /* 10240 */ -#define TWT_YOUTUBE_DURATION (256 * 32) /* 8192 */ - -#define TWT_REASON_MAX (WLAN_PTRACKER_NOTIFY_MAX + 1) -static const char *const reason2str[WLAN_PTRACKER_NOTIFY_MAX] = { - "tp", "scene_change", "scene_prep", "suspend", "sta_change", +#define TWT_IDLE_INTERVAL (500 * 1024) /* 512000 */ +#define TWT_IDLE_DURATION (768 * 32) /* 24576 */ +#define TWT_WEB_INTERVAL (104 * 1024) /* 106496 */ +#define TWT_WEB_DURATION (256 * 32) /* 8192 */ +#define TWT_YOUTUBE_INTERVAL (10 * 1024) /* 10240 */ +#define TWT_YOUTUBE_DURATION (256 * 32) /* 8192 */ + +/* define reason*/ +enum { + TWT_SETUP_REASON_FRAMEWORK = WLAN_PTRACKER_NOTIFY_MAX, + TWT_SETUP_REASON_FORCE, + TWT_SETUP_REASON_RUNTIME, + TWT_SETUP_REASON_MAX, +}; + +static const char *const reason2str[TWT_SETUP_REASON_MAX] = { + "tp", "scene_change", "scene_prep", "suspend", "sta_connect", + "sta_discont", "dytwt_enable", "dytwt_disable", "framework", + "force", "runtime", }; static const char *const state2str[WLAN_SCENE_MAX] = { @@ -97,7 +106,7 @@ static struct dytwt_scene_action dytwt_actions[WLAN_SCENE_MAX + 1] = { static int dytwt_client_twt_setup(struct wlan_ptracker_client *client, u32 state) { - if (!client->dytwt_ops) + if (!client->dytwt_ops || !client->priv) return -EINVAL; if (!client->dytwt_ops->setup) @@ -111,7 +120,7 @@ static int dytwt_client_twt_setup(struct wlan_ptracker_client *client, u32 state static int dytwt_client_twt_teardown(struct wlan_ptracker_client *client, u32 state) { - if (!client->dytwt_ops) + if (!client->dytwt_ops || !client->priv) return -EINVAL; if (!client->dytwt_ops->teardown) @@ -119,7 +128,6 @@ static int dytwt_client_twt_teardown(struct wlan_ptracker_client *client, u32 st if (state >= WLAN_SCENE_MAX) return -EINVAL; - return client->dytwt_ops->teardown(client->priv, &dytwt_actions[state].param); } @@ -129,7 +137,7 @@ static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) struct dytwt_manager *dytwt = dytwt_get_manager(); int ret; - if (!client->dytwt_ops) + if (!client->dytwt_ops || !client->priv) return false; if (!client->dytwt_ops->get_cap) @@ -165,7 +173,7 @@ static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) static int dytwt_client_twt_pwrstates(struct wlan_ptracker_client *client, struct dytwt_pwr_state *state) { - if (!client->dytwt_ops) + if (!client->dytwt_ops || !client->priv) return -EINVAL; if (!client->dytwt_ops->get_pwrstates) @@ -177,7 +185,7 @@ static int dytwt_client_twt_pwrstates(struct wlan_ptracker_client *client, static int dytwt_client_twt_get_stats(struct wlan_ptracker_client *client, struct dytwt_stats *stats) { - if (!client->dytwt_ops) + if (!client->dytwt_ops || !client->priv) return -EINVAL; if (!client->dytwt_ops->get_stats) @@ -186,6 +194,18 @@ static int dytwt_client_twt_get_stats(struct wlan_ptracker_client *client, return client->dytwt_ops->get_stats(client->priv, stats); } +static int dytwt_client_twt_get_status(struct wlan_ptracker_client *client, + struct dytwt_status *status) +{ + if (!client->dytwt_ops || !client->priv) + return -EINVAL; + + if (!client->dytwt_ops->get_status) + return -EINVAL; + + return client->dytwt_ops->get_status(client->priv, status); +} + static inline void dytwt_record_get_pwr(u64 asleep, u64 awake, u64 *total, int *percent) { /* for percent */ @@ -220,7 +240,6 @@ static int dytwt_record_priv_read(void *cur, void *next, char *buf, int len) dytwt_record_get_pwr(c->pwr.asleep, c->pwr.awake, &total_time, &total_percent); } dytwt_record_get_pwr(asleep, awake, &period_time, &period_percent); - return scnprintf(buf, len, "Applied: %s, Time: %llu (%llu) ms, Percent: %d%% (%d%%) Reason: %s, Rate: %d", c->apply ? "TRUE" : "FALSE", period_time, total_time, period_percent, total_percent, @@ -245,8 +264,8 @@ static void dytwt_counter_update(struct dytwt_manager *dytwt, struct dytwt_pwr_s counter->total_sleep_cnt += count; } -static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt, - struct wlan_scene_event *msg, bool apply) +static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, + struct dytwt_manager *dytwt, struct wlan_scene_event *msg, bool apply, u32 reason) { struct dytwt_entry *entry; @@ -256,7 +275,7 @@ static void dytwt_mgmt_history_store(struct wlan_ptracker_client *client, struct return; /* record private values */ entry->apply = apply; - entry->reason = msg->reason; + entry->reason = reason; entry->rate = msg->rate; dytwt_client_twt_pwrstates(client, &entry->pwr); dytwt_counter_update(dytwt, &entry->pwr); @@ -282,20 +301,15 @@ static int _dytwt_scene_change_handler(struct dytwt_manager *dytwt, ptracker_dbg(dytwt->core, "twt is not supported on device or peer\n"); goto out; } - act = &dytwt_actions[state]; /* follow action to setup */ - if (act->action == TWT_ACTION_SETUP) { + if (act->action == TWT_ACTION_SETUP) ret = dytwt_client_twt_setup(client, state); - } else { - /* tear down was applied at state of "perpare_change". */ - apply = true; - } apply = ret ? false : true; out: /* store record of history even twt is not applied */ - dytwt_mgmt_history_store(client, dytwt, msg, apply); + dytwt_mgmt_history_store(client, dytwt, msg, apply, msg->reason); ptracker_dbg(dytwt->core, "twt setup for state: %d, reason: %s, ret: %d\n", state, reason2str[msg->reason], ret); return ret; @@ -312,11 +326,16 @@ static void dytwt_delay_setup(struct work_struct *work) rcu_read_lock(); client = rcu_dereference(core->client); + if (!client) + goto end; + /* for first time update value is required*/ + dytwt->twt_cap = dytwt_client_twt_cap(client); _dytwt_scene_change_handler(dytwt, client); +end: rcu_read_unlock(); } -#define TWT_WAIT_STA_READY_TIME 1000 +#define TWT_WAIT_STA_READY_TIME 2000 static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) { struct dytwt_manager *dytwt = dytwt_get_manager(); @@ -355,7 +374,8 @@ static const struct file_operations twt_ops = { .llseek = generic_file_llseek, }; -static void dytwt_force_twt_setup(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt) +static void dytwt_force_twt_setup(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt, + u32 reason) { int ret = 0; bool apply = false; @@ -376,10 +396,10 @@ static void dytwt_force_twt_setup(struct wlan_ptracker_client *client, struct dy apply = ret ? false : true; msg.dst = dytwt->state; /* store record of history even twt is not applied */ - dytwt_mgmt_history_store(client, dytwt, &msg, apply); + dytwt_mgmt_history_store(client, dytwt, &msg, apply, reason); } -static inline void twt_enable(struct wlan_ptracker_client *client, bool enable) +static inline void twt_enable(struct wlan_ptracker_client *client, bool enable, u32 reason) { struct dytwt_manager *dytwt = dytwt_get_manager(); @@ -388,7 +408,7 @@ static inline void twt_enable(struct wlan_ptracker_client *client, bool enable) dytwt_scene_change_handler(client); } else { dytwt->state = WLAN_SCENE_TPUT; - dytwt_force_twt_setup(client, dytwt); + dytwt_force_twt_setup(client, dytwt, reason); dytwt->feature_flag &= ~BIT(FEATURE_FLAG_TWT); } } @@ -413,10 +433,12 @@ static void dytwt_runtime(struct work_struct *work) goto unlock; act = &dytwt_actions[dytwt->prev]; - if (act->action == TWT_ACTION_SETUP && !dytwt_client_twt_cap(client)) { + /* update twt_cap periodically */ + dytwt->twt_cap = dytwt_client_twt_cap(client); + if (act->action == TWT_ACTION_SETUP && !dytwt->twt_cap) { dytwt->state = WLAN_SCENE_TPUT; ptracker_dbg(dytwt->core, "teardown twt due to hit threshold\n"); - dytwt_force_twt_setup(client, dytwt); + dytwt_force_twt_setup(client, dytwt, TWT_SETUP_REASON_RUNTIME); } unlock: rcu_read_unlock(); @@ -426,7 +448,8 @@ end: static void update_twt_flag(struct wlan_ptracker_core *core, struct dytwt_manager *dytwt) { - twt_enable(core->client, !(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))); + twt_enable(core->client, !(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT)), + TWT_SETUP_REASON_FORCE); } static void update_twt_parameters(struct dytwt_manager *dytwt) @@ -464,6 +487,21 @@ static void dytwt_stats_dump(struct wlan_ptracker_client *client, struct dytwt_m stats.sp_seq, stats.eosp_count, stats.eosp_dur_avg); } +static void dytwt_status_dump(struct wlan_ptracker_client *client, struct dytwt_manager *dytwt) +{ + struct dytwt_status status; + + status.config_id = DYMAIC_TWT_CONFIG_ID; + dytwt_client_twt_get_status(client, &status); + + ptracker_info(dytwt->core, "config_id: %d, flow_id: %d, flow_flags: %x\n", + status.config_id, status.flow_id, status.flow_flags); + ptracker_info(dytwt->core, "setup_cmd: %d, channel: %d, nego_type: %d\n", + status.setup_cmd, status.channel, status.nego_type); + ptracker_info(dytwt->core, "wake_dur: %d, wake_int: %d\n", + status.wake_dur, status.wake_int); +} + static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) { struct dytwt_pwr_state pwr_state; @@ -478,7 +516,7 @@ static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) switch (action) { case TWT_TEST_FORCE_STATE: - dytwt_force_twt_setup(client, dytwt); + dytwt_force_twt_setup(client, dytwt, TWT_SETUP_REASON_FORCE); break; case TWT_TEST_CAP: dytwt_client_twt_cap(client); @@ -495,6 +533,9 @@ static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) case TWT_TEST_DUMP_STATS: dytwt_stats_dump(client, dytwt); break; + case TWT_TEST_DUMP_STATUS: + dytwt_status_dump(client, dytwt); + break; default: ptracker_err(core, "action %d is not supported!\n", action); return -ENOTSUPP; @@ -635,7 +676,10 @@ static void dytwt_scene_change_prepare_handler(struct wlan_ptracker_client *clie if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) return; - /* prepare to change state teardown original setup first */ + /* + * prepare to change state, teardown the original setup first. + * This change is not recorded in history. + */ if (dytwt_actions[prev_state].action == TWT_ACTION_SETUP) dytwt_client_twt_teardown(client, dytwt->prev); } @@ -665,10 +709,10 @@ static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event cancel_delayed_work_sync(&dytwt->wq); break; case WLAN_PTRACKER_NOTIFY_DYTWT_ENABLE: - twt_enable(client, true); + twt_enable(client, true, TWT_SETUP_REASON_FRAMEWORK); break; case WLAN_PTRACKER_NOTIFY_DYTWT_DISABLE: - twt_enable(client, false); + twt_enable(client, false, TWT_SETUP_REASON_FRAMEWORK); break; default: break; @@ -678,6 +722,35 @@ unlock: return NOTIFY_OK; } +static ssize_t dytwt_dumpstate_statistic(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return dytwt_statistic_read(buf, PAGE_SIZE); +} + +static ssize_t dytwt_dumpstate_history(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct dytwt_manager *dytwt = dytwt_get_manager(); + return wlan_ptracker_history_read(dytwt->hm, buf, PAGE_SIZE); +} + +static struct kobj_attribute attr_twt_history = + __ATTR(twt_history, 0664, dytwt_dumpstate_history, NULL); + +static struct kobj_attribute attr_twt_statistic = + __ATTR(twt_statistic, 0664, dytwt_dumpstate_statistic, NULL); + +static struct attribute *default_file_attrs[] = { + &attr_twt_history.attr, + &attr_twt_statistic.attr, + NULL, +}; + +static const struct attribute_group attr_group = { + .attrs = default_file_attrs, +}; + static int dytwt_debugfs_init(struct wlan_ptracker_core *core) { struct wlan_ptracker_debugfs *debugfs = &core->debugfs; @@ -697,17 +770,22 @@ static int dytwt_debugfs_init(struct wlan_ptracker_core *core) debugfs_create_u32("wake_duration", 0666, dytwt->dir, &act->param.wake_duration); debugfs_create_u32("action", 0666, dytwt->dir, &act->action); debugfs_create_u32("feature_flag", 0666, dytwt->dir, &dytwt->feature_flag); + if (debugfs->kobj) + sysfs_create_group(debugfs->kobj, &attr_group); return 0; } -#define TWT_DEFAULT_MIN_LINK_SPEED (90000) +#define TWT_DEFAULT_MIN_LINK_SPEED (180000) #define TWT_DEFAULT_MIN_RSSI (-70) #define DYTWT_RECORD_MAX 30 static int dytwt_mgmt_init(struct wlan_ptracker_core *core) { struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_debugfs *debugfs = &core->debugfs; struct history_manager *hm; + if (debugfs->kobj) + sysfs_remove_group(debugfs->kobj, &attr_group); if (dytwt->dir) debugfs_remove_recursive(dytwt->dir); memset(dytwt, 0, sizeof(*dytwt)); diff --git a/dynamic_twt_manager.h b/dynamic_twt_manager.h index a7ecd55..43cdf21 100644 --- a/dynamic_twt_manager.h +++ b/dynamic_twt_manager.h @@ -35,6 +35,17 @@ struct dytwt_pwr_state { u64 count; }; +struct dytwt_status { + u32 config_id; + u32 flow_id; + u32 flow_flags; + u32 setup_cmd; + u32 channel; + u32 nego_type; + u32 wake_dur; + u32 wake_int; +}; + struct dytwt_stats { u32 config_id; u32 sp_seq; /* sequence number of the service period */ @@ -60,6 +71,7 @@ struct dytwt_client_ops { int (*get_cap)(void *priv, struct dytwt_cap *cap); int (*get_pwrstates)(void *priv, struct dytwt_pwr_state *state); int (*get_stats)(void *priv, struct dytwt_stats *stats); + int (*get_status)(void *priv, struct dytwt_status *status); }; enum { @@ -75,6 +87,7 @@ enum { TWT_TEST_ONOFF, TWT_TEST_SET_PARAM, TWT_TEST_DUMP_STATS, + TWT_TEST_DUMP_STATUS, TWT_TEST_MAX, }; @@ -116,6 +129,7 @@ struct dytwt_manager { struct history_manager *hm; u32 rssi_threshold; u32 link_threshold; + bool twt_cap; struct delayed_work wq; struct delayed_work setup_wq; struct wlan_ptracker_core *core; @@ -125,5 +139,4 @@ struct dytwt_manager { extern int dytwt_init(struct wlan_ptracker_core *core); extern void dytwt_exit(struct wlan_ptracker_core *core); - #endif /* __TP_TRACKER_DYNAMIC_TWT_SETUP_H */ diff --git a/notifier.c b/notifier.c index 7f9712d..01dd768 100644 --- a/notifier.c +++ b/notifier.c @@ -18,16 +18,20 @@ static int up_event_handler(struct wlan_ptracker_core *core, struct net_device * { core->dev = dev; core->client->core = core; + dev_hold(dev); core->client->priv = dev; return tp_monitor_init(&core->tp); } static void down_event_handler(struct wlan_ptracker_core *core) { + struct net_device *dev = core->dev; tp_monitor_exit(&core->tp); core->dev = NULL; core->client->core = NULL; core->client->priv = NULL; + if (dev) + dev_put(dev); } static int netdevice_notifier_handler(struct notifier_block *nb, diff --git a/scenes_fsm.c b/scenes_fsm.c index 768c75d..6bb920c 100644 --- a/scenes_fsm.c +++ b/scenes_fsm.c @@ -58,15 +58,12 @@ static int fsm_thread(void *param) break; } wait_for_completion(&fsm->event); + ptracker_dbg(core, "state: %d, trans state %d -> %d, rate %llu\n", msg->state, msg->src, msg->dst, msg->rate); - wlan_ptracker_call_chain(&core->notifier, - WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, core); - fsm->confirm = true; - wlan_ptracker_call_chain(&core->notifier, - WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, core); + wlan_ptracker_call_chain(&core->notifier, WLAN_PTRACKER_NOTIFY_SCENE_CHANGE_PREPARE, core); + wlan_ptracker_call_chain(&core->notifier, WLAN_PTRACKER_NOTIFY_SCENE_CHANGE, core); msg->state = msg->dst; - fsm->confirm = false; } return 0; } @@ -171,7 +168,6 @@ static int scene_notifier_handler(struct notifier_block *nb, { struct wlan_ptracker_core *core = ptr; struct wlan_ptracker_notifier *notifier = &core->notifier; - struct wlan_ptracker_fsm *fsm = &core->fsm; /* * Events of suspen and sta change will block wlan driver @@ -186,7 +182,6 @@ static int scene_notifier_handler(struct notifier_block *nb, notifier->prev_event = jiffies; case WLAN_PTRACKER_NOTIFY_STA_CONNECT: case WLAN_PTRACKER_NOTIFY_TP: - fsm->confirm = true; scenes_fsm_decision(core, event); break; default: @@ -310,8 +305,6 @@ int scenes_fsm_init(struct wlan_ptracker_fsm *fsm) /* assign scenes and conditions */ fsm->conditions = &conditions[0]; fsm->reset_cnt = 0; - /* for first link up setting */ - fsm->confirm = true; /* init msg for receiving event */ msg->dst = WLAN_SCENE_IDLE; msg->src = WLAN_SCENE_IDLE; diff --git a/scenes_fsm.h b/scenes_fsm.h index 49b1a23..e7aa284 100644 --- a/scenes_fsm.h +++ b/scenes_fsm.h @@ -60,7 +60,6 @@ struct wlan_scene_event { struct wlan_ptracker_fsm { int reset_cnt; - bool confirm; bool thread_run; struct completion event; struct wlan_scene_event msg; @@ -76,5 +75,4 @@ struct wlan_ptracker_fsm { extern int scenes_fsm_init(struct wlan_ptracker_fsm *fsm); extern void scenes_fsm_exit(struct wlan_ptracker_fsm *fsm); - #endif /* __WLAN_SCENES_FSM_H */ -- cgit v1.2.3 From 7cffa0f2c8dc09e110f88fa2634e100a3719f763 Mon Sep 17 00:00:00 2001 From: Star Chang Date: Tue, 1 Nov 2022 10:43:50 +0000 Subject: wlan_ptracker: refactor core and dytwt data allocated at runtime. Bug: 253348062 Test: ST-stability/WiFi regression/WiFi performance Test pass Signed-off-by: Star Chang Change-Id: Idbbdd9a1eaa8bf1207278b8dc0a198d8ae70efbb --- core.h | 3 +- debugfs.c | 59 +++++++++++--- debugfs.h | 15 +++- dynamic_twt_manager.c | 217 +++++++++++++++++++++++++++++--------------------- dynamic_twt_manager.h | 7 ++ main.c | 64 ++++++--------- notifier.c | 8 +- scenes_fsm.c | 1 - tp_monitor.c | 20 ++--- 9 files changed, 225 insertions(+), 169 deletions(-) diff --git a/core.h b/core.h index 7cd8cea..8c43e98 100644 --- a/core.h +++ b/core.h @@ -29,7 +29,8 @@ struct wlan_ptracker_core { struct wlan_ptracker_debugfs debugfs; struct wlan_ptracker_fsm fsm; struct net_device *dev; - struct wlan_ptracker_client __rcu *client; + struct dytwt_manager *dytwt; + struct wlan_ptracker_client *client; u8 dscp_to_ac[DSCP_MAX]; }; #endif /* _WLAN_PTRACKER_CORE_H */ diff --git a/debugfs.c b/debugfs.c index 6fe6d99..6c52393 100644 --- a/debugfs.c +++ b/debugfs.c @@ -75,7 +75,7 @@ static ssize_t action_write(struct file *file, ptracker_err(core, "action %d is not supported!\n", action); return -ENOTSUPP; } - return 0; + return len; } static const struct file_operations dscp_ops = { @@ -85,18 +85,56 @@ static const struct file_operations dscp_ops = { .llseek = generic_file_llseek, }; +static ssize_t ptracker_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct wlan_ptracker_debugfs *debugfs = container_of(kobj, struct wlan_ptracker_debugfs, + kobj); + struct ptracker_kobj_attr *ptracker_attr = container_of(attr, struct ptracker_kobj_attr, + attr); + int ret = -EIO; + + if (ptracker_attr->show) + ret = ptracker_attr->show(debugfs, buf); + return ret; +} + +static ssize_t ptracker_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct wlan_ptracker_debugfs *debugfs = + container_of(kobj, struct wlan_ptracker_debugfs, kobj); + struct ptracker_kobj_attr *ptracker_attr = + container_of(attr, struct ptracker_kobj_attr, attr); + int ret = -EIO; + + if (ptracker_attr->store) + ret = ptracker_attr->store(debugfs, buf, count); + return ret; +} + +static struct sysfs_ops ptracker_sysfs_ops = { + .show = ptracker_sysfs_show, + .store = ptracker_sysfs_store, +}; + +static struct kobj_type ptracker_ktype = { + .sysfs_ops = &ptracker_sysfs_ops, +}; + static int wlan_ptracker_sysfs_init(struct wlan_ptracker_debugfs *debugfs) { - debugfs->kobj = kobject_create_and_add("wlan_ptracker", NULL); - if (!debugfs->kobj) - return -ENODEV; - return 0; + int ret; + + ret = kobject_init_and_add(&debugfs->kobj, &ptracker_ktype, NULL, PTRACKER_PREFIX); + if (ret) + kobject_put(&debugfs->kobj); + return ret; } static void wlan_ptracker_sysfs_exit(struct wlan_ptracker_debugfs *debugfs) { - if (debugfs->kobj) - kobject_put(debugfs->kobj); + kobject_del(&debugfs->kobj); + kobject_put(&debugfs->kobj); } int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs) @@ -104,7 +142,7 @@ int wlan_ptracker_debugfs_init(struct wlan_ptracker_debugfs *debugfs) struct wlan_ptracker_core *core = container_of( debugfs, struct wlan_ptracker_core, debugfs); - debugfs->root = debugfs_create_dir("wlan_ptracker", NULL); + debugfs->root = debugfs_create_dir(PTRACKER_PREFIX, NULL); if (!debugfs->root) return -ENODEV; debugfs_create_file("action", 0600, debugfs->root, core, &dscp_ops); @@ -177,7 +215,8 @@ static int history_get_tm(struct history_entry *entry, char *time, size_t len) return scnprintf(time, len, "%ptRs", &tm); } -size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int buf_len) +size_t wlan_ptracker_history_read(struct wlan_ptracker_core *core, struct history_manager *hm, + char *buf, int buf_len) { u8 *ptr; struct history_entry *cur, *next; @@ -201,7 +240,7 @@ size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int buf len += history_get_tm(cur, buf + len, buf_len - len); len += scnprintf(buf + len, buf_len - len, "%12s =>", state2str[cur->state]); if (hm->priv_read) - len += hm->priv_read(cur, next, buf + len, buf_len - len); + len += hm->priv_read(core, cur, next, buf + len, buf_len - len); len += scnprintf(buf + len, buf_len - len, "\n"); ptr += hm->entry_size; } diff --git a/debugfs.h b/debugfs.h index 2d4bfd4..7b2e282 100644 --- a/debugfs.h +++ b/debugfs.h @@ -14,15 +14,23 @@ #include #include +struct wlan_ptracker_core; + struct wlan_ptracker_debugfs { struct dentry *root; - struct kobject *kobj; + struct kobject kobj; u32 dscp; u32 ac; u32 action; u32 log_level; }; +struct ptracker_kobj_attr { + struct attribute attr; + ssize_t (*show)(struct wlan_ptracker_debugfs *, char *); + ssize_t (*store)(struct wlan_ptracker_debugfs *, const char *, size_t count); +}; + enum { FEATURE_FLAG_TWT, FEATURE_FLAG_MAX @@ -52,7 +60,7 @@ struct history_manager { int entry_count; int entry_size; struct mutex mutex; - int (*priv_read)(void *cur, void *next, char *buf, int len); + int (*priv_read)(struct wlan_ptracker_core *core, void *cur, void *next, char *buf, int len); u8 entries[0]; }; @@ -61,6 +69,7 @@ extern void wlan_ptracker_debugfs_exit(struct wlan_ptracker_debugfs *debugfs); extern struct history_manager *wlan_ptracker_history_create(int entry_count, int entry_size); extern void wlan_ptracker_history_destroy(struct history_manager *hm); extern void *wlan_ptracker_history_store(struct history_manager *hm, u32 state); -extern size_t wlan_ptracker_history_read(struct history_manager *hm, char *buf, int len); +extern size_t wlan_ptracker_history_read(struct wlan_ptracker_core *core, + struct history_manager *hm, char *buf, int len); #endif /* _WLAN_PTRACKER_DEBUGFS_H */ diff --git a/dynamic_twt_manager.c b/dynamic_twt_manager.c index 2cf7aa3..1701a57 100644 --- a/dynamic_twt_manager.c +++ b/dynamic_twt_manager.c @@ -11,10 +11,6 @@ #include #include "core.h" -static struct dytwt_manager dytwt_mgmt; - -#define dytwt_get_manager() (&dytwt_mgmt) - #define DYMAIC_TWT_CONFIG_ID 3 /* for tcp one pair case */ @@ -134,7 +130,8 @@ static int dytwt_client_twt_teardown(struct wlan_ptracker_client *client, u32 st static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) { struct dytwt_cap param; - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = client->core; + struct dytwt_manager *dytwt = core->dytwt; int ret; if (!client->dytwt_ops || !client->priv) @@ -145,13 +142,13 @@ static bool dytwt_client_twt_cap(struct wlan_ptracker_client *client) ret = client->dytwt_ops->get_cap(client->priv, ¶m); - ptracker_dbg(dytwt->core, "%d, %d, %d, %d\n", param.device_cap, param.peer_cap, + ptracker_dbg(core, "%d, %d, %d, %d\n", param.device_cap, param.peer_cap, param.link_speed, param.rssi); if (ret) return false; if (!param.peer_cap || !param.device_cap) { - ptracker_err(dytwt->core, "dytwt is not enabled due to capability: %d, %d\n", + ptracker_err(core, "dytwt is not enabled due to capability: %d, %d\n", param.device_cap, param.peer_cap); return false; } @@ -215,11 +212,11 @@ static inline void dytwt_record_get_pwr(u64 asleep, u64 awake, u64 *total, int * *total /= 10; } -static int dytwt_record_priv_read(void *cur, void *next, char *buf, int len) +static int dytwt_record_priv_read(struct wlan_ptracker_core *core, void *cur, void *next, + char *buf, int len) { struct dytwt_entry *c = cur; struct dytwt_entry *n = next; - struct dytwt_manager *dytwt = dytwt_get_manager(); int period_percent, total_percent; u64 period_time, total_time; u64 awake, asleep; @@ -227,7 +224,7 @@ static int dytwt_record_priv_read(void *cur, void *next, char *buf, int len) /* next is the current state */ if (n->pwr.asleep < c->pwr.asleep) { struct dytwt_pwr_state pwr; - dytwt_client_twt_pwrstates(dytwt->core->client, &pwr); + dytwt_client_twt_pwrstates(core->client, &pwr); awake = pwr.awake - c->pwr.awake; asleep = pwr.asleep - c->pwr.asleep; /* get total */ @@ -317,29 +314,24 @@ out: static void dytwt_delay_setup(struct work_struct *work) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_manager *dytwt = container_of(work, struct dytwt_manager, setup_wq.work); struct wlan_ptracker_core *core = dytwt->core; struct wlan_ptracker_client *client; if (!core) return; - rcu_read_lock(); - client = rcu_dereference(core->client); - if (!client) - goto end; + client = core->client; /* for first time update value is required*/ dytwt->twt_cap = dytwt_client_twt_cap(client); _dytwt_scene_change_handler(dytwt, client); -end: - rcu_read_unlock(); } #define TWT_WAIT_STA_READY_TIME 2000 static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) { - struct dytwt_manager *dytwt = dytwt_get_manager(); struct wlan_ptracker_core *core = client->core; + struct dytwt_manager *dytwt = core->dytwt; struct wlan_scene_event *msg = &core->fsm.msg; if (msg->reason == WLAN_PTRACKER_NOTIFY_STA_CONNECT) @@ -352,7 +344,8 @@ static int dytwt_scene_change_handler(struct wlan_ptracker_client *client) #define TWT_HISTORY_BUF_SIZE 10240 static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = file->private_data; + struct dytwt_manager *dytwt = core->dytwt; char *buf; int len; ssize_t ret; @@ -362,7 +355,7 @@ static ssize_t twt_read(struct file *file, char __user *userbuf, size_t count, l if (!buf) return -ENOMEM; - len = wlan_ptracker_history_read(dytwt->hm, buf, TWT_HISTORY_BUF_SIZE); + len = wlan_ptracker_history_read(core, dytwt->hm, buf, TWT_HISTORY_BUF_SIZE); ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); vfree(buf); return ret; @@ -401,7 +394,8 @@ static void dytwt_force_twt_setup(struct wlan_ptracker_client *client, struct dy static inline void twt_enable(struct wlan_ptracker_client *client, bool enable, u32 reason) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = client->core; + struct dytwt_manager *dytwt = core->dytwt; if (enable) { dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); @@ -416,7 +410,7 @@ static inline void twt_enable(struct wlan_ptracker_client *client, bool enable, #define DYTWT_RUNTIME_TIMER 2000 static void dytwt_runtime(struct work_struct *work) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_manager *dytwt = container_of(work, struct dytwt_manager, wq.work); struct dytwt_scene_action *act; struct wlan_ptracker_client *client; @@ -426,12 +420,7 @@ static void dytwt_runtime(struct work_struct *work) if (dytwt->prev == WLAN_SCENE_MAX) goto end; - rcu_read_lock(); - client = rcu_dereference(dytwt->core->client); - - if (!client) - goto unlock; - + client = dytwt->core->client; act = &dytwt_actions[dytwt->prev]; /* update twt_cap periodically */ dytwt->twt_cap = dytwt_client_twt_cap(client); @@ -440,8 +429,6 @@ static void dytwt_runtime(struct work_struct *work) ptracker_dbg(dytwt->core, "teardown twt due to hit threshold\n"); dytwt_force_twt_setup(client, dytwt, TWT_SETUP_REASON_RUNTIME); } -unlock: - rcu_read_unlock(); end: schedule_delayed_work(&dytwt->wq, msecs_to_jiffies(DYTWT_RUNTIME_TIMER)); } @@ -505,14 +492,8 @@ static void dytwt_status_dump(struct wlan_ptracker_client *client, struct dytwt_ static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) { struct dytwt_pwr_state pwr_state; - struct dytwt_manager *dytwt = dytwt_get_manager(); - struct wlan_ptracker_client *client; - - rcu_read_lock(); - client = rcu_dereference(core->client); - - if (!client) - goto unlock; + struct dytwt_manager *dytwt = core->dytwt; + struct wlan_ptracker_client *client = core->client; switch (action) { case TWT_TEST_FORCE_STATE: @@ -537,11 +518,9 @@ static int dytwt_debugfs_action(struct wlan_ptracker_core *core, u32 action) dytwt_status_dump(client, dytwt); break; default: - ptracker_err(core, "action %d is not supported!\n", action); + ptracker_err(core, "action %d is not supported\n", action); return -ENOTSUPP; } -unlock: - rcu_read_unlock(); return 0; } @@ -554,7 +533,8 @@ static ssize_t twt_params_write(struct file *file, const char __user *buf, size_ if (kstrtouint_from_user(buf, len, 10, &action)) return -EFAULT; - return dytwt_debugfs_action(core, action); + dytwt_debugfs_action(core, action); + return len; } static int dytwt_params_read(char *buf, int len) @@ -607,9 +587,9 @@ static const struct file_operations twt_params_ops = { .llseek = generic_file_llseek, }; -static int dytwt_statistic_read(char *buf, int len) +static int dytwt_statistic_read(struct wlan_ptracker_core *core, char *buf, int len) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_manager *dytwt = core->dytwt; struct dytwt_counters *counter = &dytwt->counters; struct dytwt_statistic *ds; struct dytwt_pwr_state pwr; @@ -648,6 +628,7 @@ static int dytwt_statistic_read(char *buf, int len) static ssize_t twt_statistic_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { + struct wlan_ptracker_core *core = file->private_data; char *buf; int len; int ret; @@ -656,7 +637,7 @@ static ssize_t twt_statistic_read(struct file *file, char __user *userbuf, size_ if (!buf) return -ENOMEM; - len = dytwt_statistic_read(buf, TWT_STATISTIC_SIZE); + len = dytwt_statistic_read(core, buf, TWT_STATISTIC_SIZE); ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); vfree(buf); return ret; @@ -670,7 +651,8 @@ static const struct file_operations twt_statistic_ops = { static void dytwt_scene_change_prepare_handler(struct wlan_ptracker_client *client) { - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct wlan_ptracker_core *core = client->core; + struct dytwt_manager *dytwt = core->dytwt; u32 prev_state = dytwt->prev; if (!(dytwt->feature_flag & BIT(FEATURE_FLAG_TWT))) @@ -687,13 +669,8 @@ static void dytwt_scene_change_prepare_handler(struct wlan_ptracker_client *clie static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event, void *ptr) { struct wlan_ptracker_core *core = ptr; - struct wlan_ptracker_client *client; - struct dytwt_manager *dytwt = dytwt_get_manager(); - - rcu_read_lock(); - client = rcu_dereference(core->client); - if (!client) - goto unlock; + struct wlan_ptracker_client *client = core->client; + struct dytwt_manager *dytwt = core->dytwt; switch (event) { case WLAN_PTRACKER_NOTIFY_SCENE_CHANGE: @@ -717,29 +694,24 @@ static int dytwt_notifier_handler(struct notifier_block *nb, unsigned long event default: break; } -unlock: - rcu_read_unlock(); return NOTIFY_OK; } -static ssize_t dytwt_dumpstate_statistic(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) +static ssize_t dytwt_dumpstate_statistic(struct dytwt_manager *dytwt, char *buf) { - return dytwt_statistic_read(buf, PAGE_SIZE); + return dytwt_statistic_read(dytwt->core, buf, PAGE_SIZE); } -static ssize_t dytwt_dumpstate_history(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) +static ssize_t dytwt_dumpstate_history(struct dytwt_manager *dytwt, char *buf) { - struct dytwt_manager *dytwt = dytwt_get_manager(); - return wlan_ptracker_history_read(dytwt->hm, buf, PAGE_SIZE); + return wlan_ptracker_history_read(dytwt->core, dytwt->hm, buf, PAGE_SIZE); } -static struct kobj_attribute attr_twt_history = - __ATTR(twt_history, 0664, dytwt_dumpstate_history, NULL); +static struct dytwt_kobj_attr attr_twt_history = + __ATTR(history, 0664, dytwt_dumpstate_history, NULL); -static struct kobj_attribute attr_twt_statistic = - __ATTR(twt_statistic, 0664, dytwt_dumpstate_statistic, NULL); +static struct dytwt_kobj_attr attr_twt_statistic = + __ATTR(statistic, 0664, dytwt_dumpstate_statistic, NULL); static struct attribute *default_file_attrs[] = { &attr_twt_history.attr, @@ -747,14 +719,66 @@ static struct attribute *default_file_attrs[] = { NULL, }; -static const struct attribute_group attr_group = { - .attrs = default_file_attrs, +static ssize_t dytwt_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct dytwt_manager *dytwt; + struct dytwt_kobj_attr *dytwt_attr; + int ret = -EIO; + + dytwt = container_of(kobj, struct dytwt_manager, kobj); + dytwt_attr = container_of(attr, struct dytwt_kobj_attr, attr); + + if (dytwt_attr->show) + ret = dytwt_attr->show(dytwt, buf); + return ret; +} + +static ssize_t dytwt_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct dytwt_manager *dytwt; + struct dytwt_kobj_attr *dytwt_attr; + int ret = -EIO; + + dytwt = container_of(kobj, struct dytwt_manager, kobj); + dytwt_attr = container_of(attr, struct dytwt_kobj_attr, attr); + + if (dytwt_attr->show) + ret = dytwt_attr->store(dytwt, buf, count); + return ret; + +} + +static struct sysfs_ops dytwt_sysfs_ops = { + .show = dytwt_sysfs_show, + .store = dytwt_sysfs_store, +}; + +static struct kobj_type dytwt_ktype = { + .sysfs_ops = &dytwt_sysfs_ops, + .default_attrs = default_file_attrs, }; +static int dytwt_sysfs_init(struct dytwt_manager *dytwt, struct wlan_ptracker_debugfs *debugfs) +{ + int ret; + + ret = kobject_init_and_add(&dytwt->kobj, &dytwt_ktype, &debugfs->kobj, "twt"); + if (ret) + kobject_put(&dytwt->kobj); + return ret; +} + +static void dytwt_sysfs_exit(struct dytwt_manager *dytwt) +{ + kobject_del(&dytwt->kobj); + kobject_put(&dytwt->kobj); +} + static int dytwt_debugfs_init(struct wlan_ptracker_core *core) { struct wlan_ptracker_debugfs *debugfs = &core->debugfs; - struct dytwt_manager *dytwt = dytwt_get_manager(); + struct dytwt_manager *dytwt = core->dytwt; struct dytwt_scene_action *act = &dytwt_actions[WLAN_SCENE_MAX]; dytwt->feature_flag |= BIT(FEATURE_FLAG_TWT); @@ -770,25 +794,28 @@ static int dytwt_debugfs_init(struct wlan_ptracker_core *core) debugfs_create_u32("wake_duration", 0666, dytwt->dir, &act->param.wake_duration); debugfs_create_u32("action", 0666, dytwt->dir, &act->action); debugfs_create_u32("feature_flag", 0666, dytwt->dir, &dytwt->feature_flag); - if (debugfs->kobj) - sysfs_create_group(debugfs->kobj, &attr_group); + dytwt_sysfs_init(dytwt, debugfs); return 0; } +static void dytwt_debugfs_exit(struct dytwt_manager *dytwt) +{ + if (dytwt->dir) + debugfs_remove_recursive(dytwt->dir); + dytwt_sysfs_exit(dytwt); +} + #define TWT_DEFAULT_MIN_LINK_SPEED (180000) #define TWT_DEFAULT_MIN_RSSI (-70) #define DYTWT_RECORD_MAX 30 -static int dytwt_mgmt_init(struct wlan_ptracker_core *core) +static struct dytwt_manager *dytwt_mgmt_init(struct wlan_ptracker_core *core) { - struct dytwt_manager *dytwt = dytwt_get_manager(); - struct wlan_ptracker_debugfs *debugfs = &core->debugfs; struct history_manager *hm; + struct dytwt_manager *dytwt = kzalloc(sizeof(struct dytwt_manager), GFP_KERNEL); + + if (!dytwt) + return NULL; - if (debugfs->kobj) - sysfs_remove_group(debugfs->kobj, &attr_group); - if (dytwt->dir) - debugfs_remove_recursive(dytwt->dir); - memset(dytwt, 0, sizeof(*dytwt)); dytwt->state = WLAN_SCENE_IDLE; dytwt->prev = WLAN_SCENE_MAX; dytwt->core = core; @@ -797,25 +824,23 @@ static int dytwt_mgmt_init(struct wlan_ptracker_core *core) INIT_DELAYED_WORK(&dytwt->wq, dytwt_runtime); INIT_DELAYED_WORK(&dytwt->setup_wq, dytwt_delay_setup); hm = wlan_ptracker_history_create(DYTWT_RECORD_MAX, sizeof(struct dytwt_entry)); - if (!hm) - return -ENOMEM; + if (!hm) { + kfree(dytwt); + return NULL; + } strncpy(hm->name, "Dynamic TWT Setup", sizeof(hm->name)); hm->priv_read = dytwt_record_priv_read; dytwt->hm = hm; - return 0; + + return dytwt; } -static void dytwt_mgmt_exit(void) +static void dytwt_mgmt_exit(struct dytwt_manager *dytwt) { - struct dytwt_manager *dytwt = dytwt_get_manager(); - cancel_delayed_work_sync(&dytwt->wq); cancel_delayed_work_sync(&dytwt->setup_wq); - if (dytwt->dir) - debugfs_remove_recursive(dytwt->dir); - wlan_ptracker_history_destroy(dytwt->hm); - memset(dytwt, 0, sizeof(*dytwt)); + kfree(dytwt); } static struct notifier_block twt_nb = { @@ -825,13 +850,21 @@ static struct notifier_block twt_nb = { int dytwt_init(struct wlan_ptracker_core *core) { - dytwt_mgmt_init(core); + core->dytwt = dytwt_mgmt_init(core); dytwt_debugfs_init(core); return wlan_ptracker_register_notifier(&core->notifier, &twt_nb); } void dytwt_exit(struct wlan_ptracker_core *core) { - dytwt_mgmt_exit(); - return wlan_ptracker_unregister_notifier(&core->notifier, &twt_nb); + struct dytwt_manager *dytwt = core->dytwt; + + core->dytwt = NULL; + wlan_ptracker_unregister_notifier(&core->notifier, &twt_nb); + + if (!dytwt) + return; + + dytwt_debugfs_exit(dytwt); + dytwt_mgmt_exit(dytwt); } diff --git a/dynamic_twt_manager.h b/dynamic_twt_manager.h index 43cdf21..495329b 100644 --- a/dynamic_twt_manager.h +++ b/dynamic_twt_manager.h @@ -134,9 +134,16 @@ struct dytwt_manager { struct delayed_work setup_wq; struct wlan_ptracker_core *core; struct dytwt_counters counters; + struct kobject kobj; struct dentry *dir; }; +struct dytwt_kobj_attr { + struct attribute attr; + ssize_t (*show)(struct dytwt_manager *, char *); + ssize_t (*store)(struct dytwt_manager *, const char *, size_t count); +}; + extern int dytwt_init(struct wlan_ptracker_core *core); extern void dytwt_exit(struct wlan_ptracker_core *core); #endif /* __TP_TRACKER_DYNAMIC_TWT_SETUP_H */ diff --git a/main.c b/main.c index dc3eeed..f0d3ad5 100644 --- a/main.c +++ b/main.c @@ -13,14 +13,7 @@ #include #include "core.h" - -static struct wlan_ptracker_core ptracker_core; - -#define get_core() (&ptracker_core) - -#define client_to_core(client) \ - ((struct wlan_ptracker_core *)((client)->core)) - +#define client_to_core(client) ((struct wlan_ptracker_core *)((client)->core)) /* Default mapping rule follow 802.11e */ static const int dscp_trans[WMM_AC_MAX][DSCP_MAP_MAX] = { @@ -45,9 +38,15 @@ static void dscp_to_ac_init(u8 *dscp_to_ac) } } -static int wlan_ptracker_core_init(struct wlan_ptracker_core *core) +static struct wlan_ptracker_core *wlan_ptracker_core_init(struct wlan_ptracker_client *client) { - memset(core, 0, sizeof(*core)); + struct wlan_ptracker_core *core; + + core = kzalloc(sizeof(struct wlan_ptracker_core), GFP_KERNEL); + if (!core) + return NULL; + + core->client = client; device_initialize(&core->device); dev_set_name(&core->device, PTRACKER_PREFIX); device_add(&core->device); @@ -56,7 +55,7 @@ static int wlan_ptracker_core_init(struct wlan_ptracker_core *core) wlan_ptracker_notifier_init(&core->notifier); scenes_fsm_init(&core->fsm); dytwt_init(core); - return 0; + return core; } static void wlan_ptracker_core_exit(struct wlan_ptracker_core *core) @@ -66,7 +65,7 @@ static void wlan_ptracker_core_exit(struct wlan_ptracker_core *core) wlan_ptracker_notifier_exit(&core->notifier); wlan_ptracker_debugfs_exit(&core->debugfs); device_del(&core->device); - memset(core, 0, sizeof(struct wlan_ptracker_core)); + kfree(core); } static int client_event_handler(void *priv, u32 event) @@ -79,52 +78,35 @@ static int client_event_handler(void *priv, u32 event) int wlan_ptracker_register_client(struct wlan_ptracker_client *client) { - struct wlan_ptracker_core *core = get_core(); - - if (!core->client) { - rcu_read_lock(); - rcu_assign_pointer(core->client, client); - rcu_read_unlock(); - client->cb = client_event_handler; - } + client->core = wlan_ptracker_core_init(client); + if (!client->core) + return -ENOMEM; + client->cb = client_event_handler; return 0; } EXPORT_SYMBOL_GPL(wlan_ptracker_register_client); void wlan_ptracker_unregister_client(struct wlan_ptracker_client *client) { - struct wlan_ptracker_core *core = get_core(); + struct wlan_ptracker_core *core = client_to_core(client); - if (core->client == client) { - client->cb = NULL; - rcu_read_lock(); - rcu_assign_pointer(core->client, NULL); - rcu_read_unlock(); - } + if (!core) + return; + client->cb = NULL; + client->core = NULL; + wlan_ptracker_core_exit(core); } EXPORT_SYMBOL_GPL(wlan_ptracker_unregister_client); static int __init wlan_ptracker_init(void) { - struct wlan_ptracker_core *core = get_core(); - int ret; - - ret = wlan_ptracker_core_init(core); - if (ret) - goto err; - dev_dbg(&core->device, "module init\n"); + pr_debug("module init: %s\n", PTRACKER_PREFIX); return 0; -err: - wlan_ptracker_core_exit(core); - return ret; } static void __exit wlan_ptracker_exit(void) { - struct wlan_ptracker_core *core = get_core(); - - dev_dbg(&core->device, "module exit\n"); - wlan_ptracker_core_exit(core); + pr_debug("module exit: %s\n", PTRACKER_PREFIX); } module_init(wlan_ptracker_init); diff --git a/notifier.c b/notifier.c index 01dd768..fb950b8 100644 --- a/notifier.c +++ b/notifier.c @@ -8,16 +8,13 @@ */ #include "core.h" -#define notifier_to_core(notifier) \ - container_of(notifier, struct wlan_ptracker_core, notifier) +#define notifier_to_core(notifier) container_of(notifier, struct wlan_ptracker_core, notifier) -#define nb_to_notifier(nb) \ - (container_of(nb, struct wlan_ptracker_notifier, nb)) +#define nb_to_notifier(nb) container_of(nb, struct wlan_ptracker_notifier, nb) static int up_event_handler(struct wlan_ptracker_core *core, struct net_device *dev) { core->dev = dev; - core->client->core = core; dev_hold(dev); core->client->priv = dev; return tp_monitor_init(&core->tp); @@ -28,7 +25,6 @@ static void down_event_handler(struct wlan_ptracker_core *core) struct net_device *dev = core->dev; tp_monitor_exit(&core->tp); core->dev = NULL; - core->client->core = NULL; core->client->priv = NULL; if (dev) dev_put(dev); diff --git a/scenes_fsm.c b/scenes_fsm.c index 6bb920c..622e13f 100644 --- a/scenes_fsm.c +++ b/scenes_fsm.c @@ -52,7 +52,6 @@ static int fsm_thread(void *param) struct wlan_ptracker_core *core = fsm_to_core(fsm); while (fsm->thread_run) { - set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) { ptracker_info(core, "kthread is stopped\n"); break; diff --git a/tp_monitor.c b/tp_monitor.c index 0fac073..3937506 100644 --- a/tp_monitor.c +++ b/tp_monitor.c @@ -175,26 +175,16 @@ static int tp_monitor_debugfs_init(struct wlan_ptracker_core *core) { struct wlan_ptracker_debugfs *debugfs = &core->debugfs; struct tp_monitor_stats *stats = &core->tp; - struct wlan_ptracker_client *client; - int ret = 0; + struct wlan_ptracker_client *client = core->client; - rcu_read_lock(); - client = rcu_dereference(core->client); - if (!client) { - ret = -ENODEV; - goto out; - } stats->dir = debugfs_create_dir(client->ifname, debugfs->root); - if (!stats->dir) { - ret = -ENODEV; - goto out; - } + if (!stats->dir) + return -ENODEV; + debugfs_create_u32("log_level", 0600, stats->dir, &stats->debug); debugfs_create_file("tx", 0400, stats->dir, &stats->tx, &counter_ops); debugfs_create_file("rx", 0400, stats->dir, &stats->rx, &counter_ops); -out: - rcu_read_unlock(); - return ret; + return 0; } int tp_monitor_init(struct tp_monitor_stats *stats) -- cgit v1.2.3