Skip to content

Commit 0ef5892

Browse files
committed
kprobe: monitor NAPI by counting callers of do_current_softirqs
In the RT kernel, do_current_softirqs() may be invoked two ways. The first way is when a hard IRQ handler exits and calls __local_bh_enable(). The second is when ksoftirqd runs all the softirqs that have not been raised by a hard IRQ (e.g. tasklets), or have been raised when hard IRQs are disabled, which is true in the case of NAPI processing. This kprobe counts the occurrences of the two execution paths for the NET_RX_SOFTIRQ on the processor on which the network IRQ is running in order to determine what fraction of packet handling occurs via NAPI and what via hard IRQ handling. Signed-off-by: Alison Chaiken <alison@she-devel.com>
1 parent 0045f04 commit 0ef5892

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

include/linux/kprobes.h

+2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ struct kprobe {
9696

9797
/* number of times the kprobe is hit */
9898
unsigned int call_count;
99+
unsigned int ksoftirqd_count;
100+
unsigned int local_bh_enable_count;
99101

100102
/*
101103
* ... called if executing addr causes a fault (eg. page fault).

samples/kprobes/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
# then to use one (as root): insmod <module_name.ko>
33

44
obj-$(CONFIG_SAMPLE_KPROBES) += kprobe_example.o jprobe_example.o \
5-
kp_ksoft.o kp_napi_complete_done.o
5+
kp_ksoft.o kp_napi_complete_done.o \
6+
kp_do_current_softirqs.o
67
obj-$(CONFIG_SAMPLE_KRETPROBES) += kretprobe_example.o
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* kp_do_current_softirqs.c
3+
* Based on kernel's kprobe_example.c. Determine if do_currrent_softirqs()
4+
* is run from ksoftirqd or is invoked after __local_bh_enable().
5+
*
6+
* Alison Chaiken, alison@she-devel.com
7+
*/
8+
9+
#include <linux/kernel.h>
10+
#include <linux/module.h>
11+
#include <linux/kprobes.h>
12+
#include <asm/ptrace.h>
13+
#include <linux/moduleparam.h>
14+
#include <linux/interrupt.h>
15+
16+
static unsigned long proc_mode;
17+
unsigned int id;
18+
static int eth_irq_procid = 9;
19+
static int can_irq_procid = 10;
20+
module_param(eth_irq_procid, int, 0);
21+
module_param(can_irq_procid, int, 0);
22+
MODULE_PARM_DESC(eth_irq_procid, "Set to the number of the core where eth IRQ runs.");
23+
MODULE_PARM_DESC(can_irq_procid, "Set to the number of the core where CAN IRQ runs.");
24+
MODULE_DESCRIPTION("Determine which function invokes do_current_softirqs.");
25+
MODULE_LICENSE("GPL");
26+
27+
/* For each probe you need to allocate a kprobe structure */
28+
static struct kprobe kp = {
29+
.symbol_name = "do_current_softirqs",
30+
};
31+
32+
/* kprobe pre_handler: called just before the probed instruction is executed */
33+
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
34+
{
35+
int raised;
36+
struct thread_info *ti;
37+
struct task_struct *task;
38+
39+
id = smp_processor_id();
40+
41+
raised = __ffs(current->softirqs_raised);
42+
43+
/* change id to that where the eth IRQ is pinned */
44+
if ((raised == NET_RX_SOFTIRQ) && (id == eth_irq_procid)) {
45+
ti = current_thread_info();
46+
task = ti->task;
47+
pr_debug("task->comm is %s\n", task->comm);
48+
49+
if (strstr(task->comm, "ksoftirq"))
50+
p->ksoftirqd_count++;
51+
if (strstr(task->comm, "irq/"))
52+
p->local_bh_enable_count++;
53+
}
54+
#ifdef CONFIG_CAN
55+
/* change id to that where the CAN IRQ is pinned */
56+
if ((raised == NET_RX_SOFTIRQ) && (id == can_irq_procid))
57+
WARN_ONCE(1, "CAN NAPI.\n");
58+
#endif
59+
return 0;
60+
}
61+
62+
/* kprobe post_handler: called after the probed instruction is executed */
63+
static void handler_post(struct kprobe *p, struct pt_regs *regs,
64+
unsigned long flags)
65+
{
66+
proc_mode = processor_mode(regs);
67+
68+
#ifdef CONFIG_ARM
69+
if (p->call_count == 1)
70+
pr_info("%s post_handler: p->addr = 0x%p, lr = 0x%lx, interrupts enabled: %s, IRQ_MODE %s\n",
71+
p->symbol_name, p->addr, regs->ARM_lr,
72+
interrupts_enabled(regs) ? "yes" : "no",
73+
(proc_mode == IRQ_MODE) ? "ON" : "OFF");
74+
#endif
75+
}
76+
77+
/*
78+
* fault_handler: this is called if an exception is generated for any
79+
* instruction within the pre- or post-handler, or when Kprobes
80+
* single-steps the probed instruction.
81+
*/
82+
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
83+
{
84+
pr_info("fault_handler: p->addr = 0x%p, trap #%dn",
85+
p->addr, trapnr);
86+
/* Return 0 because we don't handle the fault. */
87+
return 0;
88+
}
89+
90+
static int __init kprobe_init(void)
91+
{
92+
int ret;
93+
94+
kp.pre_handler = handler_pre;
95+
kp.post_handler = handler_post;
96+
kp.fault_handler = handler_fault;
97+
98+
ret = register_kprobe(&kp);
99+
if (ret < 0) {
100+
pr_info("register_kprobe failed, returned %d\n", ret);
101+
return ret;
102+
}
103+
pr_info("Planted kprobe at %p\n", kp.addr);
104+
return 0;
105+
}
106+
107+
static void __exit kprobe_exit(void)
108+
{
109+
pr_info("%s: %u from ksofirqd, %u from __local_bh_enable\n",
110+
kp.symbol_name, kp.ksoftirqd_count,
111+
kp.local_bh_enable_count);
112+
unregister_kprobe(&kp);
113+
pr_info("kprobe at %p unregistered\n", kp.addr);
114+
}
115+
116+
module_init(kprobe_init)
117+
module_exit(kprobe_exit)
118+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)