Commit | Line | Data |
---|---|---|
1c8284eb MD |
1 | /* |
2 | * Copyright (C) 2008 Mathieu Desnoyers | |
3 | * | |
4 | * Dual LGPL v2.1/GPL v2 license. | |
5 | */ | |
6 | ||
7 | #include <linux/module.h> | |
8 | #include <linux/marker.h> | |
9 | #include <linux/uaccess.h> | |
10 | #include <linux/gfp.h> | |
11 | #include <linux/fs.h> | |
12 | #include <linux/debugfs.h> | |
13 | #include <linux/slab.h> | |
14 | ||
15 | #include "ltt-type-serializer.h" | |
16 | ||
17 | #define LTT_WRITE_EVENT_FILE "write_event" | |
18 | ||
19 | DEFINE_MARKER(userspace, event, "string %s"); | |
20 | static struct dentry *ltt_event_file; | |
21 | ||
22 | /** | |
23 | * write_event - write a userspace string into the trace system | |
24 | * @file: file pointer | |
25 | * @user_buf: user string | |
26 | * @count: length to copy, including the final NULL | |
27 | * @ppos: unused | |
28 | * | |
29 | * Copy a string into a trace event, in channel "userspace", event "event". | |
30 | * Copies until either \n or \0 is reached. | |
31 | * On success, returns the number of bytes copied from the source, including the | |
32 | * \n or \0 character (if there was one in the count range). It cannot return | |
33 | * more than count. | |
34 | * Inspired from tracing_mark_write implementation from Steven Rostedt and | |
35 | * Ingo Molnar. | |
36 | */ | |
37 | static | |
38 | ssize_t write_event(struct file *file, const char __user *user_buf, | |
39 | size_t count, loff_t *ppos) | |
40 | { | |
41 | struct marker *marker; | |
42 | char *buf, *end; | |
43 | long copycount; | |
44 | ssize_t ret; | |
45 | ||
46 | buf = kmalloc(count + 1, GFP_KERNEL); | |
47 | if (!buf) { | |
48 | ret = -ENOMEM; | |
49 | goto string_out; | |
50 | } | |
51 | copycount = strncpy_from_user(buf, user_buf, count); | |
52 | if (copycount < 0) { | |
53 | ret = -EFAULT; | |
54 | goto string_err; | |
55 | } | |
56 | /* Cut from the first nil or newline. */ | |
57 | buf[copycount] = '\0'; | |
58 | end = strchr(buf, '\n'); | |
59 | if (end) { | |
60 | *end = '\0'; | |
61 | copycount = end - buf; | |
62 | } | |
63 | /* Add final \0 to copycount */ | |
64 | copycount++; | |
65 | marker = &GET_MARKER(userspace, event); | |
66 | ltt_specialized_trace(marker, marker->single.probe_private, buf, | |
67 | copycount, sizeof(char)); | |
68 | /* If there is no \0 nor \n in count, do not return a larger value */ | |
69 | ret = min_t(size_t, copycount, count); | |
70 | string_err: | |
71 | kfree(buf); | |
72 | string_out: | |
73 | return ret; | |
74 | } | |
75 | ||
76 | static const struct file_operations ltt_userspace_operations = { | |
77 | .write = write_event, | |
78 | }; | |
79 | ||
80 | static int __init ltt_userspace_init(void) | |
81 | { | |
82 | struct dentry *ltt_root_dentry; | |
83 | int err = 0; | |
84 | ||
85 | ltt_root_dentry = get_ltt_root(); | |
86 | if (!ltt_root_dentry) { | |
87 | err = -ENOENT; | |
88 | goto err_no_root; | |
89 | } | |
90 | ||
91 | ltt_event_file = debugfs_create_file(LTT_WRITE_EVENT_FILE, | |
92 | S_IWUGO, | |
93 | ltt_root_dentry, | |
94 | NULL, | |
95 | <t_userspace_operations); | |
96 | if (IS_ERR(ltt_event_file) || !ltt_event_file) { | |
97 | printk(KERN_ERR | |
98 | "ltt_userspace_init: failed to create file %s\n", | |
99 | LTT_WRITE_EVENT_FILE); | |
100 | err = -EPERM; | |
101 | goto err_no_file; | |
102 | } | |
103 | ||
104 | return err; | |
105 | err_no_file: | |
106 | put_ltt_root(); | |
107 | err_no_root: | |
108 | return err; | |
109 | } | |
110 | ||
111 | static void __exit ltt_userspace_exit(void) | |
112 | { | |
113 | debugfs_remove(ltt_event_file); | |
114 | put_ltt_root(); | |
115 | } | |
116 | ||
117 | module_init(ltt_userspace_init); | |
118 | module_exit(ltt_userspace_exit); | |
119 | ||
120 | MODULE_LICENSE("GPL and additional rights"); | |
121 | MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>"); | |
122 | MODULE_DESCRIPTION("Linux Trace Toolkit Userspace Event"); |