2020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2121 */
2222
23+ #include <linux/ctype.h>
2324#include <linux/init.h>
2425#include <linux/module.h>
2526#include <linux/proc_fs.h>
27+ #include <linux/seq_file.h>
28+ #include <linux/slab.h>
2629#include <linux/byteorder/generic.h>
2730
2831#include <lttng-events.h>
3942#include <instrumentation/events/lttng-module/lttng-test.h>
4043
4144DEFINE_TRACE (lttng_test_filter_event );
45+ DEFINE_TRACE (lttng_test_event_a );
46+ DEFINE_TRACE (lttng_test_event_b );
4247
4348#define LTTNG_TEST_FILTER_EVENT_FILE "lttng-test-filter-event"
49+ #define LTTNG_TEST_PROCFS_DIR "lttng-test"
50+ #define LTTNG_TEST_GENERATE_EVENTS "gen-kernel-events"
4451
4552#define LTTNG_WRITE_COUNT_MAX 64
53+ #define LTTNG_TEST_SYM_LEN 40
54+
55+ /* Max number of events to generate in one call to avoid DoS */
56+ #define LTTNG_TEST_EVENT_MAX 100
57+
58+ /* The max command length is 3 symbols and 2 spaces */
59+ #define LTTNG_TEST_CMD_LEN ((LTTNG_TEST_SYM_LEN * 3) + 2)
4660
4761static struct proc_dir_entry * lttng_test_filter_event_dentry ;
62+ static struct proc_dir_entry * lttng_test_dentry ;
63+ static struct proc_dir_entry * lttng_test_gen_events_dentry ;
64+
65+ /**
66+ * lttng_test_method: defines a method, its argument and a run callback.
67+ * method_name: Name of the method
68+ * arg_name: Name of the argument (for usage message)
69+ * (*run): Function to call
70+ */
71+ struct lttng_test_method {
72+ char method_name [LTTNG_TEST_SYM_LEN ];
73+ char arg_name [LTTNG_TEST_SYM_LEN ];
74+ int (* run )(char * arg , int id );
75+ };
4876
4977static
5078void trace_test_event (unsigned int nr_iter )
@@ -94,13 +122,266 @@ static const struct file_operations lttng_test_filter_event_operations = {
94122 .write = lttng_test_filter_event_write ,
95123};
96124
125+ /**
126+ * event_a: triggers $arg lttng_test_event_a events
127+ * @arg: string containing the number of event to generate
128+ * @id: Unique identifier of the caller
129+ *
130+ * Returns 0 on success and a negative value on parsing error
131+ */
132+ static int event_a (char * arg , int id )
133+ {
134+ int i , ret ;
135+ unsigned long iter ;
136+
137+ ret = kstrtoul (arg , 10 , & iter );
138+ if (ret ) {
139+ printk (KERN_ERR "Failed to parse %s arg" , __func__ );
140+ goto error ;
141+ }
142+
143+ if (iter > LTTNG_TEST_EVENT_MAX ) {
144+ printk (KERN_ERR "Number of iteration (%lu) exceeding maximum " , iter );
145+ ret = - EDOM ;
146+ goto error ;
147+ }
148+
149+ ret = 0 ;
150+ for (i = 0 ; i < iter ; i ++ ) {
151+ trace_lttng_test_event_a (id );
152+ }
153+
154+ error :
155+ return ret ;
156+ }
157+
158+ /**
159+ * event_b: triggers $arg lttng_test_event_b events
160+ * @arg: string containing the number of event to generate
161+ * @id: Unique identifier of the caller
162+ *
163+ * Returns 0 on success and a negative value on parsing error
164+ */
165+ static int event_b (char * arg , int id )
166+ {
167+ int i , ret ;
168+ unsigned long iter ;
169+
170+ ret = kstrtoul (arg , 10 , & iter );
171+ if (ret ) {
172+ printk (KERN_ERR "Failed to parse %s arg" , __func__ );
173+ goto error ;
174+ }
175+
176+ if (iter > LTTNG_TEST_EVENT_MAX ) {
177+ printk (KERN_ERR "Number of iteration (%lu) exceeding maximum " , iter );
178+ ret = - EDOM ;
179+ goto error ;
180+ }
181+
182+ ret = 0 ;
183+ for (i = 0 ; i < iter ; i ++ ) {
184+ trace_lttng_test_event_b (id );
185+ }
186+
187+ error :
188+ return ret ;
189+ }
190+
191+ /**
192+ * Array of method descriptions.
193+ */
194+ static struct lttng_test_method test_methods [] = {
195+ {"event_a" , "count" , & event_a },
196+ {"event_b" , "count" , & event_b },
197+ };
198+
199+ /*
200+ * Returns the number of space-delimited words in a given string. Returns -1, if
201+ * non-ascii byte is found.
202+ */
203+ static int count_word_in_str (char * str , size_t len )
204+ {
205+ int i , count , prev_is_space ;
206+
207+ count = 0 ;
208+ prev_is_space = 1 ;
209+ for (i = 0 ; i < len && str [i ] != '\0' ; i ++ ) {
210+ if (!isascii (str [i ])) {
211+ count = -1 ;
212+ break ;
213+ }
214+
215+ switch (str [i ]) {
216+ case ' ' :
217+ prev_is_space = 1 ;
218+ break ;
219+ default :
220+ if (prev_is_space ) {
221+ count ++ ;
222+ prev_is_space = 0 ;
223+ }
224+ break ;
225+ }
226+ }
227+ return count ;
228+ }
229+
230+ /**
231+ * lttng_test_gen_events_write: run the speficied method with the specified
232+ * argument. The accepted syntax is:`<method_name> <arg> <unique-id>`
233+ * @file: file pointer
234+ * @user_buf: user string
235+ * @count: length to copy
236+ * @ppos: current position in the file
237+ *
238+ * Return -1 on error, with EFAULT errno. Returns count on success to report a
239+ * successful write to the caller.
240+ *
241+ */
242+ static
243+ ssize_t lttng_test_gen_events_write (struct file * file , const char __user * user_buf ,
244+ size_t count , loff_t * ppos )
245+ {
246+ char * buf , * ptr ;
247+ char * user_method , * user_arg , * user_id ;
248+ int i , ret ;
249+ size_t buf_len ;
250+ unsigned long id ;
251+
252+ if (count > LTTNG_TEST_CMD_LEN ) {
253+ ret = - EINVAL ;
254+ goto kmalloc_err ;
255+ }
256+ buf_len = count ;
257+
258+ buf = kmalloc (sizeof (char ) * count , GFP_KERNEL );
259+ if (!buf ) {
260+ ret = - ENOMEM ;
261+ goto kmalloc_err ;
262+ }
263+ ret = copy_from_user (buf , user_buf , count );
264+ if (ret ) {
265+ printk (KERN_ERR "copy from user error\n" );
266+ ret = - EFAULT ;
267+ goto error ;
268+ }
269+
270+ buf [count - 1 ] = '\0' ;
271+
272+ /* Check that the string is of the right format */
273+ if (count_word_in_str (buf , count ) != 3 ) {
274+ printk (KERN_ERR "lttng_gen_kernel_event wrong input format" );
275+ ret = - EINVAL ;
276+ goto error ;
277+ }
278+
279+ /* Extract the three fields for the user request */
280+ ptr = buf ;
281+ user_method = strsep (& ptr , " " );
282+ if (!user_method ) {
283+ printk (KERN_ERR "strsep returned NULL" );
284+ ret = - EINVAL ;
285+ goto error ;
286+ }
287+
288+ if (strlen (user_method ) >= LTTNG_TEST_SYM_LEN ) {
289+ ret = - EINVAL ;
290+ goto error ;
291+ }
292+
293+ user_arg = strsep (& ptr , " " );
294+ if (!user_arg ) {
295+ printk (KERN_ERR "strsep returned NULL" );
296+ ret = - EINVAL ;
297+ goto error ;
298+ }
299+
300+ if (strlen (user_arg ) >= LTTNG_TEST_SYM_LEN ) {
301+ ret = - EINVAL ;
302+ goto error ;
303+ }
304+
305+ user_id = ptr ;
306+ if (strlen (user_id ) >= (LTTNG_TEST_SYM_LEN )) {
307+ ret = - EINVAL ;
308+ goto error ;
309+ }
310+ /* Convert the uuid from string to a int */
311+ ret = kstrtoul (user_id , 10 , & id );
312+ if (ret ) {
313+ printk (KERN_ERR "Failed to parse method uuid" );
314+ ret = - EINVAL ;
315+ goto error ;
316+ }
317+
318+ /*
319+ * Iterate over all the methods and compare the name of the
320+ * method and the string from the user
321+ */
322+ ret = - EINVAL ;
323+ for (i = 0 ; i < ARRAY_SIZE (test_methods ); i ++ ) {
324+ if (strcmp (user_method , test_methods [i ].method_name ) != 0 ) {
325+ continue ;
326+ }
327+
328+ /*
329+ * Once a match is found, the method is called
330+ * and the function returns
331+ */
332+ ret = test_methods [i ].run (user_arg , id );
333+
334+ /*
335+ * If the call is successful, return the number
336+ * of count passed to write to return success to
337+ * the caller.
338+ */
339+ if (!ret ) {
340+ ret = count ;
341+ }
342+ break ;
343+ }
344+ error :
345+ kfree (buf );
346+ kmalloc_err :
347+ return ret ;
348+ }
349+
350+ static int lttng_test_gen_events_show (struct seq_file * m , void * v )
351+ {
352+ int i ;
353+ seq_printf (m , "Available methods:\n" );
354+ for (i = 0 ; i < ARRAY_SIZE (test_methods ); i ++ ) {
355+ seq_printf (m , "%s %s unique-id\n" , test_methods [i ].method_name ,
356+ test_methods [i ].arg_name );
357+ }
358+ return 0 ;
359+ }
360+
361+ static int lttng_test_gen_events_open (struct inode * inode , struct file * file )
362+ {
363+ return single_open (file , lttng_test_gen_events_show , NULL );
364+ }
365+
366+ static const struct file_operations lttng_test_gen_events_operations = {
367+ .write = lttng_test_gen_events_write ,
368+ .open = lttng_test_gen_events_open ,
369+ .read = seq_read ,
370+ .llseek = seq_lseek ,
371+ .release = single_release ,
372+ };
373+
97374static
98375int __init lttng_test_init (void )
99376{
100377 int ret = 0 ;
101378
102379 (void ) wrapper_lttng_fixup_sig (THIS_MODULE );
103380 wrapper_vmalloc_sync_all ();
381+ /*
382+ * The lttng_test_filter_event file is left in `/proc`
383+ * for backward compatibility reason.
384+ */
104385 lttng_test_filter_event_dentry =
105386 proc_create_data (LTTNG_TEST_FILTER_EVENT_FILE ,
106387 S_IRUGO | S_IWUGO , NULL ,
@@ -110,12 +391,34 @@ int __init lttng_test_init(void)
110391 ret = - ENOMEM ;
111392 goto error ;
112393 }
394+ /* Create lttng-test proc directory */
395+ lttng_test_dentry = proc_mkdir (LTTNG_TEST_PROCFS_DIR , NULL );
396+ if (!lttng_test_dentry ) {
397+ printk (KERN_ERR "Error creating LTTng test directory\n" );
398+ ret = - ENOMEM ;
399+ goto error_proc_mkdir ;
400+ }
401+
402+ lttng_test_gen_events_dentry =
403+ proc_create_data (LTTNG_TEST_GENERATE_EVENTS ,
404+ S_IRUGO | S_IWUGO , lttng_test_dentry ,
405+ & lttng_test_gen_events_operations , NULL );
406+ if (!lttng_test_gen_events_dentry ) {
407+ printk (KERN_ERR "Error creating LTTng gen-events file\n" );
408+ ret = - ENOMEM ;
409+ goto error_proc_gen_events ;
410+ }
411+
113412 ret = __lttng_events_init__lttng_test ();
114413 if (ret )
115414 goto error_events ;
116415 return ret ;
117416
118417error_events :
418+ remove_proc_entry (LTTNG_TEST_GENERATE_EVENTS , lttng_test_dentry );
419+ error_proc_gen_events :
420+ remove_proc_entry (LTTNG_TEST_PROCFS_DIR , NULL );
421+ error_proc_mkdir :
119422 remove_proc_entry (LTTNG_TEST_FILTER_EVENT_FILE , NULL );
120423error :
121424 return ret ;
@@ -129,6 +432,10 @@ void __exit lttng_test_exit(void)
129432 __lttng_events_exit__lttng_test ();
130433 if (lttng_test_filter_event_dentry )
131434 remove_proc_entry (LTTNG_TEST_FILTER_EVENT_FILE , NULL );
435+ if (lttng_test_gen_events_dentry && lttng_test_dentry )
436+ remove_proc_entry (LTTNG_TEST_GENERATE_EVENTS , lttng_test_dentry );
437+ if (lttng_test_dentry )
438+ remove_proc_entry (LTTNG_TEST_PROCFS_DIR , NULL );
132439}
133440
134441module_exit (lttng_test_exit );
0 commit comments