-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathblock_led_trigger.c
129 lines (102 loc) · 3.79 KB
/
block_led_trigger.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <linux/init.h>
#include <linux/kallsyms.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/stringify.h>
#include <trace/events/block.h>
#define MAX_DEVICES 16
DEFINE_LED_TRIGGER(ledtrig_block);
static bool invert_brightness = false;
module_param(invert_brightness, bool, S_IRUGO | S_IWUSR);
static unsigned long blink_delay_ms = 30;
module_param(blink_delay_ms, ulong, S_IRUGO | S_IWUSR);
static unsigned int device_count = 1;
static dev_t device_dev[MAX_DEVICES];
static char *device_devstr[MAX_DEVICES] = { "8:0" };
module_param_array_named(devices, device_devstr, charp, &device_count, 0);
// Hack to gain access to the "block_rq_issue" tracepoint (see Makefile)
#ifdef __tracepoint_block_rq_issue_address
# if __tracepoint_block_rq_issue_address != 0
asm(".equ __tracepoint_block_rq_issue, " __stringify(__tracepoint_block_rq_issue_address));
# else
# error __tracepoint_block_rq_issue address is zero (are you non-root?)
# endif
#else
# error __tracepoint_block_rq_issue address could not be detected (see Makefile)
#endif
// Tracepoint callback: called every time an I/O request is sent to a block device
static void trace_rq_issue(void *ignore, struct request *rq)
{
bool device_matched = false;
int i;
// Do not attempt to dereference null requests
if (rq == NULL || rq->q == NULL || rq->q->disk == NULL)
return;
// Filter out requests to devices that are not listed in the "devices" parameter
for (i = 0; i < device_count && !device_matched; i++)
{
if (MKDEV(rq->q->disk->major, rq->q->disk->first_minor) == device_dev[i])
device_matched = true;
}
if (!device_matched)
return;
// Let's blink!
led_trigger_blink_oneshot(ledtrig_block, &blink_delay_ms, &blink_delay_ms, invert_brightness);
}
static bool __init validate_tracepoint_address(void)
{
static char expected[] = "__tracepoint_block_rq_issue+0x0/";
static char actual[sizeof(expected)];
printk(KERN_INFO "block_led_trigger: hardcoded address %p resolves to %pS\n", &__tracepoint_block_rq_issue, &__tracepoint_block_rq_issue);
snprintf(actual, sizeof(actual), "%pS", &__tracepoint_block_rq_issue);
return memcmp(actual, expected, sizeof(expected)) == 0;
}
static int __init block_led_trigger_init(void)
{
int ret, i;
printk(KERN_DEBUG "block_led_trigger: loading\n");
if (!validate_tracepoint_address())
{
printk(KERN_ERR "block_led_trigger: please recompile this module against the running kernel\n");
return -EFAULT;
}
// Parse device strings (such as "8:0") into device major/minor numbers
for (i = 0; i < device_count; i++)
{
unsigned int major, minor;
int endpos;
if (sscanf(device_devstr[i], "%u:%u%n", &major, &minor, &endpos) != 2 || device_devstr[i][endpos] != '\0')
{
printk(KERN_ERR "block_led_trigger: %s cannot be parsed as a device in MAJOR:MINOR format\n",
device_devstr[i]);
return -EINVAL;
}
device_dev[i] = MKDEV(major, minor);
}
// Initialize LED trigger
led_trigger_register_simple("block-activity", &ledtrig_block);
led_trigger_event(ledtrig_block, invert_brightness ? LED_FULL : LED_OFF);
// Register our tracepoint
ret = register_trace_block_rq_issue(trace_rq_issue, NULL);
if (ret != 0)
{
printk(KERN_ERR "block_led_trigger: register_trace_block_rq_issue failed, return code: %d\n",
ret);
return ret;
}
printk(KERN_INFO "block_led_trigger: loaded successfully\n");
return 0;
}
static void __exit block_led_trigger_exit(void)
{
unregister_trace_block_rq_issue(trace_rq_issue, NULL);
tracepoint_synchronize_unregister();
led_trigger_unregister_simple(ledtrig_block);
printk(KERN_INFO "block_led_trigger: unloaded\n");
}
module_init(block_led_trigger_init);
module_exit(block_led_trigger_exit);
MODULE_AUTHOR("Fabio D'Urso <[email protected]>");
MODULE_DESCRIPTION("Block device activity LED trigger");
MODULE_LICENSE("GPL");