Skip to content

Commit

Permalink
net: extend drop reasons for multiple subsystems
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg authored and Jakub Kicinski committed Apr 21, 2023
1 parent 5b8285c commit 071c0fc
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 16 deletions.
14 changes: 10 additions & 4 deletions include/net/dropreason-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand All @@ -359,6 +367,4 @@ enum skb_drop_reason {
SKB_DR_SET(name, reason); \
} while (0)

extern const char * const drop_reasons[];

#endif
31 changes: 31 additions & 0 deletions include/net/dropreason.h
Original file line number Diff line number Diff line change
@@ -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
33 changes: 24 additions & 9 deletions net/core/drop_monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand All @@ -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))
Expand Down Expand Up @@ -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;

Expand Down
59 changes: 56 additions & 3 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand Down

0 comments on commit 071c0fc

Please sign in to comment.