b8c2b7723ef0566dd78d6ebf80a4dcddeae8ab23
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / swap / preload / preload_threads.c
1 #include <linux/kernel.h>
2 #include <linux/sched.h>
3 #include <linux/slab.h>
4 #include <linux/spinlock.h>
5 #include <linux/mm.h>
6 #include <linux/mman.h>
7 #include <linux/list.h>
8 #include <task_data/task_data.h>
9 #include "preload.h"
10 #include "preload_threads.h"
11 #include "preload_debugfs.h"
12 #include "preload_pd.h"
13
14 struct preload_td {
15         struct list_head slots;
16         unsigned long flags;
17 };
18
19 struct thread_slot {
20         struct list_head list;
21         struct list_head disabled_addrs;
22
23         unsigned long caller;
24         unsigned char call_type;
25         bool drop;   /* TODO Workaround, remove when will be possible to install
26                       * several us probes at the same addr. */
27 };
28
29 struct disabled_addr {
30         struct list_head list;
31         unsigned long addr;
32 };
33
34 static inline struct preload_td *get_preload_td(struct task_struct *task)
35 {
36         struct preload_td *td = NULL;
37         int ok;
38
39         td = swap_task_data_get(task, &ok);
40         WARN(!ok, "Preload td[%d/%d] seems corrupted", task->tgid, task->pid);
41
42         if (!td) {
43                 td = kzalloc(sizeof(*td), GFP_ATOMIC);
44                 WARN(!td, "Failed to allocate preload_td");
45
46                 if (td) {
47                         INIT_LIST_HEAD(&td->slots);
48                         /* We use SWAP_TD_FREE flag, i.e. the data will be
49                          * kfree'd by task_data module. */
50                         swap_task_data_set(task, td, SWAP_TD_FREE);
51                 }
52         }
53
54         return td;
55 }
56
57 unsigned long get_preload_flags(struct task_struct *task)
58 {
59         struct preload_td *td = get_preload_td(task);
60
61         if (td == NULL)
62                 return 0;
63
64         return td->flags;
65 }
66
67 void set_preload_flags(struct task_struct *task,
68                        unsigned long flags)
69 {
70         struct preload_td *td = get_preload_td(task);
71
72         if (td == NULL) {
73                 printk(KERN_ERR "%s: invalid arguments\n", __FUNCTION__);
74                 return;
75         }
76
77         td->flags = flags;
78 }
79
80
81 static inline bool __is_addr_found(struct disabled_addr *da,
82                                    unsigned long addr)
83 {
84         if (da->addr == addr)
85                 return true;
86
87         return false;
88 }
89
90 static inline void __remove_from_disable_list(struct disabled_addr *da)
91 {
92         list_del(&da->list);
93         kfree(da);
94 }
95
96 static inline void __remove_whole_disable_list(struct thread_slot *slot)
97 {
98         struct disabled_addr *da, *n;
99
100         list_for_each_entry_safe(da, n, &slot->disabled_addrs, list)
101                 __remove_from_disable_list(da);
102 }
103
104 static inline void __init_slot(struct thread_slot *slot)
105 {
106         slot->caller = 0;
107         slot->call_type = 0;
108         slot->drop = false;
109         INIT_LIST_HEAD(&slot->disabled_addrs);
110 }
111
112 static inline void __reinit_slot(struct thread_slot *slot)
113 {
114         __remove_whole_disable_list(slot);
115         __init_slot(slot);
116 }
117
118 static inline void __set_slot(struct thread_slot *slot,
119                               struct task_struct *task, unsigned long caller,
120                               unsigned char call_type, bool drop)
121 {
122         slot->caller = caller;
123         slot->call_type = call_type;
124         slot->drop = drop;
125 }
126
127 static inline int __add_to_disable_list(struct thread_slot *slot,
128                                         unsigned long disable_addr)
129 {
130         struct disabled_addr *da = kmalloc(sizeof(*da), GFP_ATOMIC);
131
132         if (da == NULL)
133                 return -ENOMEM;
134
135         INIT_LIST_HEAD(&da->list);
136         da->addr = disable_addr;
137         list_add_tail(&da->list, &slot->disabled_addrs);
138
139         return 0;
140 }
141
142 static inline struct disabled_addr *__find_disabled_addr(struct thread_slot *slot,
143                                                          unsigned long addr)
144 {
145         struct disabled_addr *da;
146
147         list_for_each_entry(da, &slot->disabled_addrs, list)
148                 if (__is_addr_found(da, addr))
149                         return da;
150
151         return NULL;
152 }
153
154 /* Adds a new slot */
155 static inline struct thread_slot *__grow_slot(void)
156 {
157         struct thread_slot *tmp = kmalloc(sizeof(*tmp), GFP_ATOMIC);
158
159         if (tmp == NULL)
160                 return NULL;
161
162         INIT_LIST_HEAD(&tmp->list);
163         __init_slot(tmp);
164
165         return tmp;
166 }
167
168 /* Free slot */
169 static void __clean_slot(struct thread_slot *slot)
170 {
171         list_del(&slot->list);
172         kfree(slot);
173 }
174
175 /* There is no list_last_entry in Linux 3.10 */
176 #ifndef list_last_entry
177 #define list_last_entry(ptr, type, member) \
178         list_entry((ptr)->prev, type, member)
179 #endif /* list_last_entry */
180
181 static inline struct thread_slot *__get_task_slot(struct task_struct *task)
182 {
183         struct preload_td *td = get_preload_td(task);
184
185         if (td == NULL)
186                 return NULL;
187
188         return list_empty(&td->slots) ? NULL :
189                 list_last_entry(&td->slots, struct thread_slot, list);
190 }
191
192
193
194
195 int preload_threads_set_data(struct task_struct *task, unsigned long caller,
196                              unsigned char call_type,
197                              unsigned long disable_addr, bool drop)
198 {
199         struct preload_td *td = get_preload_td(task);
200         struct thread_slot *slot;
201         int ret = 0;
202
203         slot = __grow_slot();
204         if (slot == NULL) {
205                 ret = -ENOMEM;
206                 goto set_data_done;
207         }
208
209         if ((disable_addr != 0) &&
210             (__add_to_disable_list(slot, disable_addr) != 0)) {
211                 printk(KERN_ERR PRELOAD_PREFIX "Cannot alloc memory!\n");
212                 ret = -ENOMEM;
213                 goto set_data_done;
214         }
215
216         __set_slot(slot, task, caller, call_type, drop);
217         list_add_tail(&slot->list, &td->slots);
218
219 set_data_done:
220         return ret;
221 }
222
223 int preload_threads_get_caller(struct task_struct *task, unsigned long *caller)
224 {
225         struct thread_slot *slot;
226         int ret = 0;
227
228         slot = __get_task_slot(task);
229         if (slot != NULL) {
230                 *caller = slot->caller;
231                 goto get_caller_done;
232         }
233
234         /* If we're here - slot was not found */
235         ret = -EINVAL;
236
237 get_caller_done:
238         return ret;
239 }
240
241 int preload_threads_get_call_type(struct task_struct *task,
242                                   unsigned char *call_type)
243 {
244         struct thread_slot *slot;
245         int ret = 0;
246
247         slot = __get_task_slot(task);
248         if (slot != NULL) {
249                 *call_type = slot->call_type;
250                 goto get_call_type_done;
251         }
252
253         /* If we're here - slot was not found */
254         ret = -EINVAL;
255
256 get_call_type_done:
257         return ret;
258 }
259
260 int preload_threads_get_drop(struct task_struct *task, bool *drop)
261 {
262         struct thread_slot *slot;
263         int ret = 0;
264
265         slot = __get_task_slot(task);
266         if (slot != NULL) {
267                 *drop = slot->drop;
268                 goto get_drop_done;
269         }
270
271         /* If we're here - slot was not found */
272         ret = -EINVAL;
273
274 get_drop_done:
275         return ret;
276 }
277
278 bool preload_threads_check_disabled_probe(struct task_struct *task,
279                                           unsigned long addr)
280 {
281         struct thread_slot *slot;
282         bool ret = false;
283
284         slot = __get_task_slot(task);
285         if (slot != NULL)
286                 ret = __find_disabled_addr(slot, addr) == NULL ? false : true;
287
288         return ret;
289 }
290
291 void preload_threads_enable_probe(struct task_struct *task, unsigned long addr)
292 {
293         struct thread_slot *slot;
294         struct disabled_addr *da;
295
296         slot = __get_task_slot(task);
297         if (slot == NULL) {
298                 printk(KERN_ERR PRELOAD_PREFIX "Error! Slot not found!\n");
299                 goto enable_probe_failed;
300         }
301
302         da = __find_disabled_addr(slot, addr);
303         if (da != NULL)
304                 __remove_from_disable_list(da);
305
306 enable_probe_failed:
307         return; /* make gcc happy: cannot place label right before '}' */
308 }
309
310 int preload_threads_put_data(struct task_struct *task)
311 {
312         struct thread_slot *slot;
313         int ret = 0;
314
315         slot = __get_task_slot(task);
316         if (slot != NULL) {
317                 __reinit_slot(slot);
318                 __clean_slot(slot); /* remove from list */
319                 goto put_data_done;
320         }
321
322 put_data_done:
323         return ret;
324 }
325
326 int preload_threads_init(void)
327 {
328         return 0;
329 }
330
331 void preload_threads_exit(void)
332 {
333 }