1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/module.h> 3 #include <linux/kthread.h> 4 5 /* 6 * Any file that uses trace points, must include the header. 7 * But only one file, must include the header by defining 8 * CREATE_TRACE_POINTS first. This will make the C code that 9 * creates the handles for the trace points. 10 */ 11 #define CREATE_TRACE_POINTS 12 #include "trace-events-sample.h" 13 14 static const char *random_strings[] = { 15 "Mother Goose", 16 "Snoopy", 17 "Gandalf", 18 "Frodo", 19 "One ring to rule them all" 20 }; 21 22 static void do_simple_thread_func(int cnt, const char *fmt, ...) 23 { 24 unsigned long bitmask[1] = {0xdeadbeefUL}; 25 va_list va; 26 int array[6]; 27 int len = cnt % 5; 28 int i; 29 30 set_current_state(TASK_INTERRUPTIBLE); 31 schedule_timeout(HZ); 32 33 for (i = 0; i < len; i++) 34 array[i] = i + 1; 35 array[i] = 0; 36 37 va_start(va, fmt); 38 39 /* Silly tracepoints */ 40 trace_foo_bar("hello", cnt, array, random_strings[len], 41 current->cpus_ptr, fmt, &va); 42 43 va_end(va); 44 45 trace_foo_with_template_simple("HELLO", cnt); 46 47 trace_foo_bar_with_cond("Some times print", cnt); 48 49 trace_foo_with_template_cond("prints other times", cnt); 50 51 trace_foo_with_template_print("I have to be different", cnt); 52 53 trace_foo_rel_loc("Hello __rel_loc", cnt, bitmask, current->cpus_ptr); 54 } 55 56 static void simple_thread_func(int cnt) 57 { 58 do_simple_thread_func(cnt, "iter=%d", cnt); 59 } 60 61 static int simple_thread(void *arg) 62 { 63 int cnt = 0; 64 65 while (!kthread_should_stop()) 66 simple_thread_func(cnt++); 67 68 return 0; 69 } 70 71 static struct task_struct *simple_tsk; 72 static struct task_struct *simple_tsk_fn; 73 74 static void simple_thread_func_fn(int cnt) 75 { 76 set_current_state(TASK_INTERRUPTIBLE); 77 schedule_timeout(HZ); 78 79 /* More silly tracepoints */ 80 trace_foo_bar_with_fn("Look at me", cnt); 81 trace_foo_with_template_fn("Look at me too", cnt); 82 } 83 84 static int simple_thread_fn(void *arg) 85 { 86 int cnt = 0; 87 88 while (!kthread_should_stop()) 89 simple_thread_func_fn(cnt++); 90 91 return 0; 92 } 93 94 static DEFINE_MUTEX(thread_mutex); 95 static int simple_thread_cnt; 96 97 static struct foo_timer_data *foo_timer_data; 98 99 static void sample_timer_cb(struct timer_list *t) 100 { 101 struct foo_timer_data *data = container_of(t, struct foo_timer_data, timer); 102 103 get_cpu(); 104 trace_foo_timer_fn(data); 105 (*this_cpu_ptr(data->counter))++; 106 put_cpu(); 107 108 mod_timer(t, jiffies + HZ); 109 } 110 111 int foo_bar_reg(void) 112 { 113 mutex_lock(&thread_mutex); 114 if (simple_thread_cnt++) 115 goto out; 116 117 pr_info("Starting thread for foo_bar_fn\n"); 118 /* 119 * We shouldn't be able to start a trace when the module is 120 * unloading (there's other locks to prevent that). But 121 * for consistency sake, we still take the thread_mutex. 122 */ 123 simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); 124 if (IS_ERR_OR_NULL(simple_tsk_fn)) { 125 pr_err("Failed to create simple_thread_fn\n"); 126 simple_tsk_fn = NULL; 127 } 128 out: 129 mutex_unlock(&thread_mutex); 130 return 0; 131 } 132 133 void foo_bar_unreg(void) 134 { 135 mutex_lock(&thread_mutex); 136 if (--simple_thread_cnt) 137 goto out; 138 139 pr_info("Killing thread for foo_bar_fn\n"); 140 if (simple_tsk_fn) 141 kthread_stop(simple_tsk_fn); 142 simple_tsk_fn = NULL; 143 out: 144 mutex_unlock(&thread_mutex); 145 } 146 147 static int __init trace_event_init(void) 148 { 149 foo_timer_data = kzalloc_obj(*foo_timer_data, GFP_KERNEL); 150 if (!foo_timer_data) 151 return -ENOMEM; 152 153 foo_timer_data->name = "sample_timer_counter"; 154 foo_timer_data->counter = alloc_percpu(int); 155 if (!foo_timer_data->counter) { 156 kfree(foo_timer_data); 157 return -ENOMEM; 158 } 159 160 timer_setup(&foo_timer_data->timer, sample_timer_cb, 0); 161 mod_timer(&foo_timer_data->timer, jiffies + HZ); 162 163 simple_tsk = kthread_run(simple_thread, NULL, "event-sample"); 164 if (IS_ERR(simple_tsk)) { 165 timer_shutdown_sync(&foo_timer_data->timer); 166 free_percpu(foo_timer_data->counter); 167 kfree(foo_timer_data); 168 return PTR_ERR(simple_tsk); 169 } 170 171 return 0; 172 } 173 174 static void __exit trace_event_exit(void) 175 { 176 kthread_stop(simple_tsk); 177 mutex_lock(&thread_mutex); 178 if (simple_tsk_fn) 179 kthread_stop(simple_tsk_fn); 180 simple_tsk_fn = NULL; 181 mutex_unlock(&thread_mutex); 182 183 timer_shutdown_sync(&foo_timer_data->timer); 184 free_percpu(foo_timer_data->counter); 185 kfree(foo_timer_data); 186 } 187 188 module_init(trace_event_init); 189 module_exit(trace_event_exit); 190 191 MODULE_AUTHOR("Steven Rostedt"); 192 MODULE_DESCRIPTION("trace-events-sample"); 193 MODULE_LICENSE("GPL"); 194