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

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.net


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5b8285cc
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -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
+31 −0
Original line number Diff line number Diff line
/* 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
+24 −9
Original line number Diff line number Diff line
@@ -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;

+56 −3
Original line number Diff line number Diff line
@@ -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));