net: extend drop reasons for multiple subsystems
authorJohannes Berg <johannes.berg@intel.com>
Wed, 19 Apr 2023 12:52:53 +0000 (14:52 +0200)
committerJakub Kicinski <kuba@kernel.org>
Fri, 21 Apr 2023 03:20:49 +0000 (20:20 -0700)
Extend drop reasons to make them usable by subsystems
other than core by reserving the high 16 bits for a
new subsystem ID, of which 0 of course is used for the
existing reasons immediately.

To still be able to have string reasons, restructure
that code a bit to make the loopup under RCU, the only
user of this (right now) is drop_monitor.

Link: https://lore.kernel.org/netdev/00659771ed54353f92027702c5bbb84702da62ce.camel@sipsolutions.net
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/dropreason-core.h
include/net/dropreason.h [new file with mode: 0644]
net/core/drop_monitor.c
net/core/skbuff.c

index ade6d5b..a2b953b 100644 (file)
@@ -340,12 +340,20 @@ enum skb_drop_reason {
         */
        SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST,
        /**
-        * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be
-        * used as a real 'reason'
+        * @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
+        * shouldn't be used as a real 'reason' - only for tracing code gen
         */
        SKB_DROP_REASON_MAX,
+
+       /**
+        * @SKB_DROP_REASON_SUBSYS_MASK: subsystem mask in drop reasons,
+        * see &enum skb_drop_reason_subsys
+        */
+       SKB_DROP_REASON_SUBSYS_MASK = 0xffff0000,
 };
 
+#define SKB_DROP_REASON_SUBSYS_SHIFT   16
+
 #define SKB_DR_INIT(name, reason)                              \
        enum skb_drop_reason name = SKB_DROP_REASON_##reason
 #define SKB_DR(name)                                           \
@@ -359,6 +367,4 @@ enum skb_drop_reason {
                        SKB_DR_SET(name, reason);               \
        } while (0)
 
-extern const char * const drop_reasons[];
-
 #endif
diff --git a/include/net/dropreason.h b/include/net/dropreason.h
new file mode 100644 (file)
index 0000000..f0f2378
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_DROPREASON_H
+#define _LINUX_DROPREASON_H
+#include <net/dropreason-core.h>
+
+/**
+ * enum skb_drop_reason_subsys - subsystem tag for (extended) drop reasons
+ */
+enum skb_drop_reason_subsys {
+       /** @SKB_DROP_REASON_SUBSYS_CORE: core drop reasons defined above */
+       SKB_DROP_REASON_SUBSYS_CORE,
+
+       /** @SKB_DROP_REASON_SUBSYS_NUM: number of subsystems defined */
+       SKB_DROP_REASON_SUBSYS_NUM
+};
+
+struct drop_reason_list {
+       const char * const *reasons;
+       size_t n_reasons;
+};
+
+/* Note: due to dynamic registrations, access must be under RCU */
+extern const struct drop_reason_list __rcu *
+drop_reasons_by_subsys[SKB_DROP_REASON_SUBSYS_NUM];
+
+void drop_reasons_register_subsys(enum skb_drop_reason_subsys subsys,
+                                 const struct drop_reason_list *list);
+void drop_reasons_unregister_subsys(enum skb_drop_reason_subsys subsys);
+
+#endif
index 5a782d1..aff31cd 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/workqueue.h>
 #include <linux/netlink.h>
 #include <linux/net_dropmon.h>
+#include <linux/bitfield.h>
 #include <linux/percpu.h>
 #include <linux/timer.h>
 #include <linux/bitops.h>
@@ -29,6 +30,7 @@
 #include <net/genetlink.h>
 #include <net/netevent.h>
 #include <net/flow_offload.h>
+#include <net/dropreason.h>
 #include <net/devlink.h>
 
 #include <trace/events/skb.h>
@@ -504,8 +506,6 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
        if (!nskb)
                return;
 
-       if (unlikely(reason >= SKB_DROP_REASON_MAX || reason <= 0))
-               reason = SKB_DROP_REASON_NOT_SPECIFIED;
        cb = NET_DM_SKB_CB(nskb);
        cb->reason = reason;
        cb->pc = location;
@@ -552,9 +552,9 @@ static size_t net_dm_in_port_size(void)
 }
 
 #define NET_DM_MAX_SYMBOL_LEN 40
+#define NET_DM_MAX_REASON_LEN 50
 
-static size_t net_dm_packet_report_size(size_t payload_len,
-                                       enum skb_drop_reason reason)
+static size_t net_dm_packet_report_size(size_t payload_len)
 {
        size_t size;
 
@@ -576,7 +576,7 @@ static size_t net_dm_packet_report_size(size_t payload_len,
               /* NET_DM_ATTR_PROTO */
               nla_total_size(sizeof(u16)) +
               /* NET_DM_ATTR_REASON */
-              nla_total_size(strlen(drop_reasons[reason]) + 1) +
+              nla_total_size(NET_DM_MAX_REASON_LEN + 1) +
               /* NET_DM_ATTR_PAYLOAD */
               nla_total_size(payload_len);
 }
@@ -610,6 +610,8 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
                                     size_t payload_len)
 {
        struct net_dm_skb_cb *cb = NET_DM_SKB_CB(skb);
+       const struct drop_reason_list *list = NULL;
+       unsigned int subsys, subsys_reason;
        char buf[NET_DM_MAX_SYMBOL_LEN];
        struct nlattr *attr;
        void *hdr;
@@ -627,9 +629,24 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
                              NET_DM_ATTR_PAD))
                goto nla_put_failure;
 
+       rcu_read_lock();
+       subsys = u32_get_bits(cb->reason, SKB_DROP_REASON_SUBSYS_MASK);
+       if (subsys < SKB_DROP_REASON_SUBSYS_NUM)
+               list = rcu_dereference(drop_reasons_by_subsys[subsys]);
+       subsys_reason = cb->reason & ~SKB_DROP_REASON_SUBSYS_MASK;
+       if (!list ||
+           subsys_reason >= list->n_reasons ||
+           !list->reasons[subsys_reason] ||
+           strlen(list->reasons[subsys_reason]) > NET_DM_MAX_REASON_LEN) {
+               list = rcu_dereference(drop_reasons_by_subsys[SKB_DROP_REASON_SUBSYS_CORE]);
+               subsys_reason = SKB_DROP_REASON_NOT_SPECIFIED;
+       }
        if (nla_put_string(msg, NET_DM_ATTR_REASON,
-                          drop_reasons[cb->reason]))
+                          list->reasons[subsys_reason])) {
+               rcu_read_unlock();
                goto nla_put_failure;
+       }
+       rcu_read_unlock();
 
        snprintf(buf, sizeof(buf), "%pS", cb->pc);
        if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf))
@@ -687,9 +704,7 @@ static void net_dm_packet_report(struct sk_buff *skb)
        if (net_dm_trunc_len)
                payload_len = min_t(size_t, net_dm_trunc_len, payload_len);
 
-       msg = nlmsg_new(net_dm_packet_report_size(payload_len,
-                                                 NET_DM_SKB_CB(skb)->reason),
-                       GFP_KERNEL);
+       msg = nlmsg_new(net_dm_packet_report_size(payload_len), GFP_KERNEL);
        if (!msg)
                goto out;
 
index 8764653..0d99880 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/scatterlist.h>
 #include <linux/errqueue.h>
 #include <linux/prefetch.h>
+#include <linux/bitfield.h>
 #include <linux/if_vlan.h>
 #include <linux/mpls.h>
 #include <linux/kcov.h>
@@ -72,6 +73,7 @@
 #include <net/mptcp.h>
 #include <net/mctp.h>
 #include <net/page_pool.h>
+#include <net/dropreason.h>
 
 #include <linux/uaccess.h>
 #include <trace/events/skb.h>
@@ -122,11 +124,59 @@ EXPORT_SYMBOL(sysctl_max_skb_frags);
 
 #undef FN
 #define FN(reason) [SKB_DROP_REASON_##reason] = #reason,
-const char * const drop_reasons[] = {
+static const char * const drop_reasons[] = {
        [SKB_CONSUMED] = "CONSUMED",
        DEFINE_DROP_REASON(FN, FN)
 };
-EXPORT_SYMBOL(drop_reasons);
+
+static const struct drop_reason_list drop_reasons_core = {
+       .reasons = drop_reasons,
+       .n_reasons = ARRAY_SIZE(drop_reasons),
+};
+
+const struct drop_reason_list __rcu *
+drop_reasons_by_subsys[SKB_DROP_REASON_SUBSYS_NUM] = {
+       [SKB_DROP_REASON_SUBSYS_CORE] = RCU_INITIALIZER(&drop_reasons_core),
+};
+EXPORT_SYMBOL(drop_reasons_by_subsys);
+
+/**
+ * drop_reasons_register_subsys - register another drop reason subsystem
+ * @subsys: the subsystem to register, must not be the core
+ * @list: the list of drop reasons within the subsystem, must point to
+ *     a statically initialized list
+ */
+void drop_reasons_register_subsys(enum skb_drop_reason_subsys subsys,
+                                 const struct drop_reason_list *list)
+{
+       if (WARN(subsys <= SKB_DROP_REASON_SUBSYS_CORE ||
+                subsys >= ARRAY_SIZE(drop_reasons_by_subsys),
+                "invalid subsystem %d\n", subsys))
+               return;
+
+       /* must point to statically allocated memory, so INIT is OK */
+       RCU_INIT_POINTER(drop_reasons_by_subsys[subsys], list);
+}
+EXPORT_SYMBOL_GPL(drop_reasons_register_subsys);
+
+/**
+ * drop_reasons_unregister_subsys - unregister a drop reason subsystem
+ * @subsys: the subsystem to remove, must not be the core
+ *
+ * Note: This will synchronize_rcu() to ensure no users when it returns.
+ */
+void drop_reasons_unregister_subsys(enum skb_drop_reason_subsys subsys)
+{
+       if (WARN(subsys <= SKB_DROP_REASON_SUBSYS_CORE ||
+                subsys >= ARRAY_SIZE(drop_reasons_by_subsys),
+                "invalid subsystem %d\n", subsys))
+               return;
+
+       RCU_INIT_POINTER(drop_reasons_by_subsys[subsys], NULL);
+
+       synchronize_rcu();
+}
+EXPORT_SYMBOL_GPL(drop_reasons_unregister_subsys);
 
 /**
  *     skb_panic - private function for out-of-line support
@@ -986,7 +1036,10 @@ bool __kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason)
        if (unlikely(!skb_unref(skb)))
                return false;
 
-       DEBUG_NET_WARN_ON_ONCE(reason <= 0 || reason >= SKB_DROP_REASON_MAX);
+       DEBUG_NET_WARN_ON_ONCE(reason == SKB_NOT_DROPPED_YET ||
+                              u32_get_bits(reason,
+                                           SKB_DROP_REASON_SUBSYS_MASK) >=
+                               SKB_DROP_REASON_SUBSYS_NUM);
 
        if (reason == SKB_CONSUMED)
                trace_consume_skb(skb, __builtin_return_address(0));