summaryrefslogtreecommitdiff
path: root/tp_monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'tp_monitor.c')
-rw-r--r--tp_monitor.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/tp_monitor.c b/tp_monitor.c
new file mode 100644
index 0000000..3937506
--- /dev/null
+++ b/tp_monitor.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Wifi performance tracker
+ *
+ * Copyright 2022 Google LLC.
+ *
+ * Author: Star Chang <starchang@google.com>
+ */
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/debugfs.h>
+#include <net/dsfield.h>
+#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;
+#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 */
+ }
+}
+
+/* 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 (%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;
+}
+
+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;
+
+ 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);
+}
+