Commit 071c0fc6 authored by Johannes Berg's avatar Johannes Berg Committed by Jakub Kicinski

net: extend drop reasons for multiple subsystems

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.netSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5b8285cc
...@@ -340,12 +340,20 @@ enum skb_drop_reason { ...@@ -340,12 +340,20 @@ enum skb_drop_reason {
*/ */
SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST, SKB_DROP_REASON_IPV6_NDISC_NS_OTHERHOST,
/** /**
* @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
* used as a real 'reason' * shouldn't be used as a real 'reason' - only for tracing code gen
*/ */
SKB_DROP_REASON_MAX, 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) \ #define SKB_DR_INIT(name, reason) \
enum skb_drop_reason name = SKB_DROP_REASON_##reason enum skb_drop_reason name = SKB_DROP_REASON_##reason
#define SKB_DR(name) \ #define SKB_DR(name) \
...@@ -359,6 +367,4 @@ enum skb_drop_reason { ...@@ -359,6 +367,4 @@ enum skb_drop_reason {
SKB_DR_SET(name, reason); \ SKB_DR_SET(name, reason); \
} while (0) } while (0)
extern const char * const drop_reasons[];
#endif #endif
/* 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
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/net_dropmon.h> #include <linux/net_dropmon.h>
#include <linux/bitfield.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/netevent.h> #include <net/netevent.h>
#include <net/flow_offload.h> #include <net/flow_offload.h>
#include <net/dropreason.h>
#include <net/devlink.h> #include <net/devlink.h>
#include <trace/events/skb.h> #include <trace/events/skb.h>
...@@ -504,8 +506,6 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore, ...@@ -504,8 +506,6 @@ static void net_dm_packet_trace_kfree_skb_hit(void *ignore,
if (!nskb) if (!nskb)
return; return;
if (unlikely(reason >= SKB_DROP_REASON_MAX || reason <= 0))
reason = SKB_DROP_REASON_NOT_SPECIFIED;
cb = NET_DM_SKB_CB(nskb); cb = NET_DM_SKB_CB(nskb);
cb->reason = reason; cb->reason = reason;
cb->pc = location; cb->pc = location;
...@@ -552,9 +552,9 @@ static size_t net_dm_in_port_size(void) ...@@ -552,9 +552,9 @@ static size_t net_dm_in_port_size(void)
} }
#define NET_DM_MAX_SYMBOL_LEN 40 #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, static size_t net_dm_packet_report_size(size_t payload_len)
enum skb_drop_reason reason)
{ {
size_t size; size_t size;
...@@ -576,7 +576,7 @@ static size_t net_dm_packet_report_size(size_t payload_len, ...@@ -576,7 +576,7 @@ static size_t net_dm_packet_report_size(size_t payload_len,
/* NET_DM_ATTR_PROTO */ /* NET_DM_ATTR_PROTO */
nla_total_size(sizeof(u16)) + nla_total_size(sizeof(u16)) +
/* NET_DM_ATTR_REASON */ /* 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 */ /* NET_DM_ATTR_PAYLOAD */
nla_total_size(payload_len); nla_total_size(payload_len);
} }
...@@ -610,6 +610,8 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, ...@@ -610,6 +610,8 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
size_t payload_len) size_t payload_len)
{ {
struct net_dm_skb_cb *cb = NET_DM_SKB_CB(skb); 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]; char buf[NET_DM_MAX_SYMBOL_LEN];
struct nlattr *attr; struct nlattr *attr;
void *hdr; void *hdr;
...@@ -627,9 +629,24 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb, ...@@ -627,9 +629,24 @@ static int net_dm_packet_report_fill(struct sk_buff *msg, struct sk_buff *skb,
NET_DM_ATTR_PAD)) NET_DM_ATTR_PAD))
goto nla_put_failure; 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, if (nla_put_string(msg, NET_DM_ATTR_REASON,
drop_reasons[cb->reason])) list->reasons[subsys_reason])) {
rcu_read_unlock();
goto nla_put_failure; goto nla_put_failure;
}
rcu_read_unlock();
snprintf(buf, sizeof(buf), "%pS", cb->pc); snprintf(buf, sizeof(buf), "%pS", cb->pc);
if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf)) if (nla_put_string(msg, NET_DM_ATTR_SYMBOL, buf))
...@@ -687,9 +704,7 @@ static void net_dm_packet_report(struct sk_buff *skb) ...@@ -687,9 +704,7 @@ static void net_dm_packet_report(struct sk_buff *skb)
if (net_dm_trunc_len) if (net_dm_trunc_len)
payload_len = min_t(size_t, net_dm_trunc_len, payload_len); payload_len = min_t(size_t, net_dm_trunc_len, payload_len);
msg = nlmsg_new(net_dm_packet_report_size(payload_len, msg = nlmsg_new(net_dm_packet_report_size(payload_len), GFP_KERNEL);
NET_DM_SKB_CB(skb)->reason),
GFP_KERNEL);
if (!msg) if (!msg)
goto out; goto out;
......
...@@ -58,6 +58,7 @@ ...@@ -58,6 +58,7 @@
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/errqueue.h> #include <linux/errqueue.h>
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <linux/bitfield.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/mpls.h> #include <linux/mpls.h>
#include <linux/kcov.h> #include <linux/kcov.h>
...@@ -72,6 +73,7 @@ ...@@ -72,6 +73,7 @@
#include <net/mptcp.h> #include <net/mptcp.h>
#include <net/mctp.h> #include <net/mctp.h>
#include <net/page_pool.h> #include <net/page_pool.h>
#include <net/dropreason.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <trace/events/skb.h> #include <trace/events/skb.h>
...@@ -122,11 +124,59 @@ EXPORT_SYMBOL(sysctl_max_skb_frags); ...@@ -122,11 +124,59 @@ EXPORT_SYMBOL(sysctl_max_skb_frags);
#undef FN #undef FN
#define FN(reason) [SKB_DROP_REASON_##reason] = #reason, #define FN(reason) [SKB_DROP_REASON_##reason] = #reason,
const char * const drop_reasons[] = { static const char * const drop_reasons[] = {
[SKB_CONSUMED] = "CONSUMED", [SKB_CONSUMED] = "CONSUMED",
DEFINE_DROP_REASON(FN, FN) 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 * 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) ...@@ -986,7 +1036,10 @@ bool __kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason)
if (unlikely(!skb_unref(skb))) if (unlikely(!skb_unref(skb)))
return false; 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) if (reason == SKB_CONSUMED)
trace_consume_skb(skb, __builtin_return_address(0)); trace_consume_skb(skb, __builtin_return_address(0));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment