Merge branch 'tizen_2.4_dev' into tizen
[kernel/swap-modules.git] / preload / preload_control.c
1 #include <linux/slab.h>
2 #include <linux/spinlock.h>
3 #include <linux/limits.h>
4 #include <linux/list.h>
5
6 #include <us_manager/sspt/sspt_ip.h>
7
8 #include "preload.h"
9 #include "preload_module.h"
10 #include "preload_control.h"
11 #include "preload_probe.h"
12
13 struct bin_desc {
14         struct list_head list;
15         struct dentry *dentry;
16         char *filename;
17 };
18
19 struct list_desc {
20         struct list_head list;
21         rwlock_t lock;
22         int cnt;
23 };
24
25 static struct list_desc target = {
26         .list = LIST_HEAD_INIT(target.list),
27         .lock = __RW_LOCK_UNLOCKED(&target.lock),
28         .cnt = 0
29 };
30
31 static struct list_desc ignored = {
32         .list = LIST_HEAD_INIT(ignored.list),
33         .lock = __RW_LOCK_UNLOCKED(&ignored.lock),
34         .cnt = 0
35 };
36
37 static inline struct task_struct *__get_task_struct(void)
38 {
39         return current;
40 }
41
42 static struct bin_desc *__alloc_binary(struct dentry *dentry, char *name,
43                                        int namelen)
44 {
45         struct bin_desc *p = NULL;
46
47         p = kzalloc(sizeof(*p), GFP_KERNEL);
48         if (!p)
49                 return NULL;
50
51         INIT_LIST_HEAD(&p->list);
52         p->filename = kmalloc(namelen + 1, GFP_KERNEL);
53         if (!p->filename)
54                 goto fail;
55         memcpy(p->filename, name, namelen);
56         p->filename[namelen] = '\0';
57         p->dentry = dentry;
58
59         return p;
60 fail:
61         kfree(p);
62         return NULL;
63 }
64
65 static void __free_binary(struct bin_desc *p)
66 {
67         kfree(p->filename);
68         kfree(p);
69 }
70
71 static void __free_binaries(struct list_desc *tl)
72 {
73         struct bin_desc *p, *n;
74         struct list_head rm_head;
75
76         INIT_LIST_HEAD(&rm_head);
77         write_lock(&tl->lock);
78         list_for_each_entry_safe(p, n, &tl->list, list) {
79                 list_move(&p->list, &rm_head);
80         }
81         tl->cnt = 0;
82         write_unlock(&tl->lock);
83
84         list_for_each_entry_safe(p, n, &rm_head, list) {
85                 list_del(&p->list);
86                 put_dentry(p->dentry);
87                 __free_binary(p);
88         }
89 }
90
91 static bool __check_dentry_already_exist(struct dentry *dentry,
92                                          struct list_desc *tl)
93 {
94         struct bin_desc *p;
95         bool ret = false;
96
97         read_lock(&tl->lock);
98         list_for_each_entry(p, &tl->list, list) {
99                 if (p->dentry == dentry) {
100                         ret = true;
101                         goto out;
102                 }
103         }
104 out:
105         read_unlock(&tl->lock);
106
107         return ret;
108 }
109
110 static int __add_binary(struct dentry *dentry, char *filename,
111                         struct list_desc *tl)
112 {
113         struct bin_desc *p;
114         size_t len;
115
116         if (__check_dentry_already_exist(dentry, tl)) {
117                 printk(PRELOAD_PREFIX "Binary already exist\n");
118                 return EALREADY;
119         }
120
121         /* Filename should be < PATH_MAX */
122         len = strnlen(filename, PATH_MAX);
123         if (len == PATH_MAX)
124                 return -EINVAL;
125
126         p = __alloc_binary(dentry, filename, len);
127         if (!p)
128                 return -ENOMEM;
129
130         write_lock(&tl->lock);
131         list_add_tail(&p->list, &tl->list);
132         tl->cnt++;
133         write_unlock(&tl->lock);
134
135         return 0;
136 }
137
138 static struct dentry *__get_caller_dentry(struct task_struct *task,
139                                           unsigned long caller)
140 {
141         struct vm_area_struct *vma = NULL;
142
143         if (unlikely(task->mm == NULL))
144                 goto get_caller_dentry_fail;
145
146         vma = find_vma_intersection(task->mm, caller, caller + 1);
147         if (unlikely(vma == NULL || vma->vm_file == NULL))
148                 goto get_caller_dentry_fail;
149
150         return vma->vm_file->f_path.dentry;
151
152 get_caller_dentry_fail:
153
154         return NULL;
155 }
156
157 static bool __check_if_instrumented(struct task_struct *task,
158                                     struct dentry *dentry)
159 {
160         return __check_dentry_already_exist(dentry, &target);
161 }
162
163 static bool __is_instrumented(void *caller)
164 {
165         struct task_struct *task = __get_task_struct();
166         struct dentry *caller_dentry = __get_caller_dentry(task,
167                                                            (unsigned long) caller);
168
169         if (caller_dentry == NULL)
170                 return false;
171
172         return __check_if_instrumented(task, caller_dentry);
173 }
174
175 static unsigned int __get_names(struct list_desc *tl, char ***filenames_p)
176 {
177         unsigned int i, ret = 0;
178         struct bin_desc *p;
179         char **a = NULL;
180
181         read_lock(&tl->lock);
182         if (tl->cnt == 0)
183                 goto out;
184
185         a = kmalloc(sizeof(*a) * tl->cnt, GFP_KERNEL);
186         if (!a)
187                 goto out;
188
189         i = 0;
190         list_for_each_entry(p, &tl->list, list) {
191                 if (i >= tl->cnt)
192                         break;
193                 a[i++] = p->filename;
194         }
195
196         *filenames_p = a;
197         ret = i;
198 out:
199         read_unlock(&tl->lock);
200         return ret;
201 }
202
203
204 /* Called only form handlers. If we're there, then it is instrumented. */
205 enum preload_call_type pc_call_type_always_inst(void *caller)
206 {
207         if (__is_instrumented(caller))
208                 return INTERNAL_CALL;
209
210         return EXTERNAL_CALL;
211
212 }
213
214 enum preload_call_type pc_call_type(struct sspt_ip *ip, void *caller)
215 {
216         if (__is_instrumented(caller))
217                 return INTERNAL_CALL;
218
219         if (ip->desc->info.pl_i.flags & SWAP_PRELOAD_ALWAYS_RUN)
220                 return EXTERNAL_CALL;
221
222         return NOT_INSTRUMENTED;
223 }
224
225 int pc_add_instrumented_binary(char *filename)
226 {
227         struct dentry *dentry = get_dentry(filename);
228         int res = 0;
229
230         if (dentry == NULL)
231                 return -EINVAL;
232
233         res = __add_binary(dentry, filename, &target);
234         if (res != 0)
235                 put_dentry(dentry);
236
237         return res > 0 ? 0 : res;
238 }
239
240 int pc_clean_instrumented_bins(void)
241 {
242         __free_binaries(&target);
243
244         return 0;
245 }
246
247 int pc_add_ignored_binary(char *filename)
248 {
249         struct dentry *dentry = get_dentry(filename);
250         int res = 0;
251
252         if (dentry == NULL)
253                 return -EINVAL;
254
255         res = __add_binary(dentry, filename, &ignored);
256         if (res != 0)
257                 put_dentry(dentry);
258
259         return res > 0 ? 0 : res;
260 }
261
262 int pc_clean_ignored_bins(void)
263 {
264         __free_binaries(&ignored);
265
266         return 0;
267 }
268
269 unsigned int pc_get_target_names(char ***filenames_p)
270 {
271         return __get_names(&target, filenames_p);
272 }
273
274 void pc_release_target_names(char ***filenames_p)
275 {
276         kfree(*filenames_p);
277 }
278
279 unsigned int pc_get_ignored_names(char ***filenames_p)
280 {
281         return __get_names(&ignored, filenames_p);
282 }
283
284 void pc_release_ignored_names(char ***filenames_p)
285 {
286         kfree(*filenames_p);
287 }
288
289 bool pc_check_dentry_is_ignored(struct dentry *dentry)
290 {
291         struct bin_desc *p;
292         bool ret = false;
293
294         if (dentry == NULL)
295                 return false;
296
297         read_lock(&ignored.lock);
298
299         list_for_each_entry(p, &ignored.list, list) {
300                 if (p->dentry == dentry) {
301                         ret = true;
302                         break;
303                 }
304         }
305
306         read_unlock(&ignored.lock);
307
308         return ret;
309 }
310
311 int pc_init(void)
312 {
313         return 0;
314 }
315
316 void pc_exit(void)
317 {
318         __free_binaries(&target);
319         __free_binaries(&ignored);
320 }