From cc3872a02985b7c8ea7a9e83802a2f836de5ac8d 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 (cherry picked from commit faff7aaa5abbd90893469aae842c74c9474ea3f4) --- 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