summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Tranchetti <quic_stranche@quicinc.com>2022-05-02 14:33:25 -0700
committerSean Tranchetti <quic_stranche@quicinc.com>2022-05-10 15:41:38 -0700
commitcf5990b0df8c2770e3e596cde5838ffd9691af56 (patch)
treef08931d91012165830b25d8c8226d375a55c147f
parent8638373d042b77f142d82371edfd7a7b31f186e3 (diff)
downloaddatarmnet-cf5990b0df8c2770e3e596cde5838ffd9691af56.tar.gz
rmnet_core: Add generic module framework
Adds a simple, tracepoint inspired hook definiton framework to centralize all calls to rmnet support modules. Modules can set and unset these hooks freely, and modules may call one another's hooks without being forced to link against any module other than the core rmnet module. Change-Id: I949f7a17c95003e0c79262c6611b0602c7189e0c Signed-off-by: Sean Tranchetti <quic_stranche@quicinc.com>
-rw-r--r--core/Kbuild1
-rw-r--r--core/Makefile1
-rw-r--r--core/rmnet_hook.h108
-rw-r--r--core/rmnet_module.c100
-rw-r--r--core/rmnet_module.h130
5 files changed, 340 insertions, 0 deletions
diff --git a/core/Kbuild b/core/Kbuild
index 748941f..aa7c8bd 100644
--- a/core/Kbuild
+++ b/core/Kbuild
@@ -16,6 +16,7 @@ rmnet_core-y := \
rmnet_genl.o \
rmnet_map_command.o \
rmnet_map_data.o \
+ rmnet_module.o \
rmnet_vnd.o
rmnet_core-y += \
diff --git a/core/Makefile b/core/Makefile
index 270b99c..9d3f6fd 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -18,6 +18,7 @@ rmnet_core-y += rmnet_config.o \
rmnet_handlers.o \
rmnet_map_command.o \
rmnet_map_data.o \
+ rmnet_module.o \
rmnet_vnd.o \
dfc_qmap.c \
dfc_qmi.c \
diff --git a/core/rmnet_hook.h b/core/rmnet_hook.h
new file mode 100644
index 0000000..93d88ef
--- /dev/null
+++ b/core/rmnet_hook.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#if !defined(__RMNET_HOOKS__) || defined(__RMNET_HOOK_MULTIREAD__)
+#define __RMNET_HOOKS__
+
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include "rmnet_descriptor.h"
+
+RMNET_MODULE_HOOK(offload_ingress,
+ RMNET_MODULE_HOOK_NUM(OFFLOAD_INGRESS),
+ RMNET_MODULE_HOOK_PROTOCOL(struct list_head *desc_list,
+ struct rmnet_port *port),
+ RMNET_MODULE_HOOK_ARGS(desc_list, port),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(offload_chain_end,
+ RMNET_MODULE_HOOK_NUM(OFFLOAD_CHAIN_END),
+ RMNET_MODULE_HOOK_PROTOCOL(void),
+ RMNET_MODULE_HOOK_ARGS(),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(shs_skb_entry,
+ RMNET_MODULE_HOOK_NUM(SHS_SKB_ENTRY),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb,
+ struct rmnet_shs_clnt_s *cfg),
+ RMNET_MODULE_HOOK_ARGS(skb, cfg),
+ RMNET_MODULE_HOOK_RETURN_TYPE(int)
+);
+
+RMNET_MODULE_HOOK(shs_switch,
+ RMNET_MODULE_HOOK_NUM(SHS_SWITCH),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb,
+ struct rmnet_shs_clnt_s *cfg),
+ RMNET_MODULE_HOOK_ARGS(skb, cfg),
+ RMNET_MODULE_HOOK_RETURN_TYPE(int)
+);
+
+RMNET_MODULE_HOOK(perf_tether_ingress,
+ RMNET_MODULE_HOOK_NUM(PERF_TETHER_INGRESS),
+ RMNET_MODULE_HOOK_PROTOCOL(struct tcphdr *tp,
+ struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(tp, skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(perf_tether_egress,
+ RMNET_MODULE_HOOK_NUM(PERF_TETHER_EGRESS),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(perf_tether_cmd,
+ RMNET_MODULE_HOOK_NUM(PERF_TETHER_CMD),
+ RMNET_MODULE_HOOK_PROTOCOL(u8 message, u64 val),
+ RMNET_MODULE_HOOK_ARGS(message, val),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(perf_ingress,
+ RMNET_MODULE_HOOK_NUM(PERF_INGRESS),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(int)
+);
+
+RMNET_MODULE_HOOK(perf_egress,
+ RMNET_MODULE_HOOK_NUM(PERF_EGRESS),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(aps_pre_queue,
+ RMNET_MODULE_HOOK_NUM(APS_PRE_QUEUE),
+ RMNET_MODULE_HOOK_PROTOCOL(struct net_device *dev, struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(dev, skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+RMNET_MODULE_HOOK(aps_post_queue,
+ RMNET_MODULE_HOOK_NUM(APS_POST_QUEUE),
+ RMNET_MODULE_HOOK_PROTOCOL(struct net_device *dev, struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(dev, skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(int)
+);
+
+RMNET_MODULE_HOOK(wlan_flow_match,
+ RMNET_MODULE_HOOK_NUM(WLAN_FLOW_MATCH),
+ RMNET_MODULE_HOOK_PROTOCOL(struct sk_buff *skb),
+ RMNET_MODULE_HOOK_ARGS(skb),
+ RMNET_MODULE_HOOK_RETURN_TYPE(void)
+);
+
+#endif
diff --git a/core/rmnet_module.c b/core/rmnet_module.c
new file mode 100644
index 0000000..2ece83b
--- /dev/null
+++ b/core/rmnet_module.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "rmnet_module.h"
+
+struct rmnet_module_hook_info {
+ void *func __rcu;
+};
+
+static struct rmnet_module_hook_info
+rmnet_module_hooks[__RMNET_MODULE_NUM_HOOKS];
+
+void
+rmnet_module_hook_register(const struct rmnet_module_hook_register_info *info,
+ int hook_count)
+{
+ struct rmnet_module_hook_info *hook_info;
+ int i;
+
+ for (i = 0; i < hook_count; i++) {
+ int hook = info[i].hooknum;
+
+ if (hook < __RMNET_MODULE_NUM_HOOKS) {
+ hook_info = &rmnet_module_hooks[hook];
+ rcu_assign_pointer(hook_info->func, info[i].func);
+ }
+ }
+}
+EXPORT_SYMBOL(rmnet_module_hook_register);
+
+bool rmnet_module_hook_is_set(int hook)
+{
+ if (hook >= __RMNET_MODULE_NUM_HOOKS)
+ return false;
+
+ return rcu_dereference(rmnet_module_hooks[hook].func) != NULL;
+}
+EXPORT_SYMBOL(rmnet_module_hook_is_set);
+
+void
+rmnet_module_hook_unregister_no_sync(const struct rmnet_module_hook_register_info *info,
+ int hook_count)
+{
+ struct rmnet_module_hook_info *hook_info;
+ int i;
+
+ for (i = 0; i < hook_count; i++) {
+ int hook = info[i].hooknum;
+
+ if (hook < __RMNET_MODULE_NUM_HOOKS) {
+ hook_info = &rmnet_module_hooks[hook];
+ rcu_assign_pointer(hook_info->func, NULL);
+ }
+ }
+}
+EXPORT_SYMBOL(rmnet_module_hook_unregister_no_sync);
+
+#define __RMNET_HOOK_DEFINE(call, hook_num, proto, args, ret_type) \
+int rmnet_module_hook_##call( \
+__RMNET_HOOK_PROTO(RMNET_HOOK_PARAMS(proto), ret_type) \
+) \
+{ \
+ ret_type (*__func)(proto); \
+ struct rmnet_module_hook_info *__info = \
+ &rmnet_module_hooks[hook_num]; \
+ int __ret = 0; \
+\
+ rcu_read_lock(); \
+ __func = rcu_dereference(__info->func); \
+ if (__func) { \
+ RMNET_HOOK_IF_NON_VOID_TYPE(ret_type)( ret_type __rc = ) \
+ __func(args); \
+ __ret = 1; \
+\
+ RMNET_HOOK_IF_NON_VOID_TYPE(ret_type)( if (__ret_code) \
+ *__ret_code = __rc; )\
+ } \
+\
+ rcu_read_unlock(); \
+ return __ret; \
+} \
+EXPORT_SYMBOL(rmnet_module_hook_##call);
+
+#undef RMNET_MODULE_HOOK
+#define RMNET_MODULE_HOOK(call, hook_num, proto, args, ret_type) \
+__RMNET_HOOK_DEFINE(call, hook_num, RMNET_HOOK_PARAMS(proto), \
+ RMNET_HOOK_PARAMS(args), ret_type)
+
+#define __RMNET_HOOK_MULTIREAD__
+#include "rmnet_hook.h"
+
diff --git a/core/rmnet_module.h b/core/rmnet_module.h
new file mode 100644
index 0000000..4a893da
--- /dev/null
+++ b/core/rmnet_module.h
@@ -0,0 +1,130 @@
+/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __RMNET_MODULE_H__
+#define __RMNET_MODULE_H__
+
+#include <linux/rcupdate.h>
+
+enum {
+ RMNET_MODULE_HOOK_OFFLOAD_INGRESS,
+ RMNET_MODULE_HOOK_OFFLOAD_CHAIN_END,
+ RMNET_MODULE_HOOK_SHS_SKB_ENTRY,
+ RMNET_MODULE_HOOK_SHS_SWITCH,
+ RMNET_MODULE_HOOK_PERF_TETHER_INGRESS,
+ RMNET_MODULE_HOOK_PERF_TETHER_EGRESS,
+ RMNET_MODULE_HOOK_PERF_TETHER_CMD,
+ RMNET_MODULE_HOOK_PERF_INGRESS,
+ RMNET_MODULE_HOOK_PERF_EGRESS,
+ RMNET_MODULE_HOOK_APS_PRE_QUEUE,
+ RMNET_MODULE_HOOK_APS_POST_QUEUE,
+ RMNET_MODULE_HOOK_WLAN_FLOW_MATCH,
+ __RMNET_MODULE_NUM_HOOKS,
+};
+
+struct rmnet_module_hook_register_info {
+ int hooknum;
+ void *func;
+};
+
+void
+rmnet_module_hook_register(const struct rmnet_module_hook_register_info *info,
+ int hook_count);
+bool rmnet_module_hook_is_set(int hook);
+void
+rmnet_module_hook_unregister_no_sync(const struct rmnet_module_hook_register_info *info,
+ int hook_count);
+static inline void
+rmnet_module_hook_unregister(const struct rmnet_module_hook_register_info *info,
+ int hook_count)
+{
+ rmnet_module_hook_unregister_no_sync(info, hook_count);
+ synchronize_rcu();
+}
+
+/* Dummy macro. Can use kernel version later */
+#define __CAT(a, b) a ## b
+#define CAT(a, b) __CAT(a, b)
+
+#define RMNET_HOOK_PARAMS(args...) args
+
+#define RMNET_MODULE_HOOK_NUM(__hook) CAT(RMNET_MODULE_HOOK_, __hook)
+#define RMNET_MODULE_HOOK_PROTOCOL(proto...) proto
+#define RMNET_MODULE_HOOK_ARGS(args...) args
+#define RMNET_MODULE_HOOK_RETURN_TYPE(type) type
+
+/* A ...lovely... framework for checking if the argument passed in to a function
+ * macro is a pair of parentheses.
+ * If so, resolve to 1. Otherwise, 0.
+ *
+ * The idea here is that you pass the argument along with a "test" macro to
+ * a "checker" macro. If the argument IS a pair of parentheses, this will cause
+ * the tester macro to expand into multiple arguments.
+ *
+ * The key is that "checker" macro just returns the second argument it receives.
+ * So have the "tester" macro expand to a set of arguments that makes 1 the
+ * second argument, or 0 if it doesn't expand.
+ */
+#define __RMNET_HOOK_SECOND_ARG(_, arg, ...) arg
+#define RMNET_HOOK_PARENTHESES_CHECKER(args...) \
+ __RMNET_HOOK_SECOND_ARG(args, 0, )
+#define __RMNET_HOOK_PARENTHESES_TEST(arg) arg, 1,
+#define __RMNET_HOOK_IS_PARENTHESES_TEST(...) __RMNET_HOOK_PARENTHESES_TEST(XXX)
+#define RMNET_HOOK_IS_PARENTHESES(arg) \
+ RMNET_HOOK_PARENTHESES_CHECKER(__RMNET_HOOK_IS_PARENTHESES_TEST arg)
+
+/* So what's the point of the above stuff, you ask?
+ *
+ * CPP can't actually do type checking ;). But what we CAN do is something
+ * like this to determine if the type passed in is void. If so, this will
+ * expand to (), causing the RMNET_HOOK_IS_PARENTHESES check to resolve to 1,
+ * but if not, then the check resolves to 0.
+ */
+#define __RMNET_HOOK_CHECK_TYPE_IS_void(arg) arg
+#define RMNET_HOOK_TYPE_IS_VOID(type) \
+ RMNET_HOOK_IS_PARENTHESES( __RMNET_HOOK_CHECK_TYPE_IS_ ## type (()) )
+
+/* And now, we have some logic macros. The main macro will resolve
+ * to one of the branches depending on the bool value passed in.
+ */
+#define __IF_0(t_path, e_path...) e_path
+#define __IF_1(t_path, e_path...) t_path
+#define IF(arg) CAT(__IF_, arg)
+#define __NOT_1 0
+#define __NOT_0 1
+#define NOT(arg) CAT(__NOT_, arg)
+
+/* And now we combine this all, for a purely function macro way of splitting
+ * return type handling...
+ *
+ * ...all to circumvent that you can't actually add #if conditionals in macro
+ * expansions. It would have been much simpler that way. ;)
+ */
+#define RMNET_HOOK_IF_NON_VOID_TYPE(type) \
+ IF(NOT(RMNET_HOOK_TYPE_IS_VOID(type)))
+
+#define __RMNET_HOOK_PROTO(proto, ret_type)\
+RMNET_HOOK_IF_NON_VOID_TYPE(ret_type) \
+ (RMNET_HOOK_PARAMS(ret_type *__ret_code, proto), \
+ RMNET_HOOK_PARAMS(proto))
+
+#define __RMNET_HOOK_DECLARE(call, proto, ret_type) \
+int rmnet_module_hook_##call( \
+__RMNET_HOOK_PROTO(RMNET_HOOK_PARAMS(proto), ret_type));
+
+#undef RMNET_MODULE_HOOK
+#define RMNET_MODULE_HOOK(call, hook_num, proto, args, ret_type) \
+__RMNET_HOOK_DECLARE(call, RMNET_HOOK_PARAMS(proto), ret_type)
+
+#include "rmnet_hook.h"
+
+#endif