Merge branch 'tizen_2.4' into tizen_2.4_dev
[kernel/swap-modules.git] / us_manager / sspt / sspt_page.c
1 /*
2  *  Dynamic Binary Instrumentation Module based on KProbes
3  *  modules/driver/sspt/sspt_page.c
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * Copyright (C) Samsung Electronics, 2013
20  *
21  * 2013         Vyacheslav Cherkashin <v.cherkashin@samsung.com>
22  *
23  */
24
25 #include "sspt.h"
26 #include "sspt_page.h"
27 #include "sspt_file.h"
28 #include "ip.h"
29 #include <us_manager/probes/use_probes.h>
30 #include <linux/slab.h>
31 #include <linux/list.h>
32
33 /**
34  * @brief Create sspt_page struct
35  *
36  * @param offset File ofset
37  * @return Pointer to the created sspt_page struct
38  */
39 struct sspt_page *sspt_page_create(unsigned long offset)
40 {
41         struct sspt_page *obj = kmalloc(sizeof(*obj), GFP_ATOMIC);
42         if (obj) {
43                 INIT_LIST_HEAD(&obj->ip_list_inst);
44                 INIT_LIST_HEAD(&obj->ip_list_no_inst);
45                 obj->offset = offset;
46                 spin_lock_init(&obj->lock);
47                 obj->file = NULL;
48                 INIT_HLIST_NODE(&obj->hlist);
49         }
50
51         return obj;
52 }
53
54 /**
55  * @brief Remove sspt_page struct
56  *
57  * @param page remove object
58  * @return Void
59  */
60 void sspt_page_free(struct sspt_page *page)
61 {
62         struct us_ip *ip, *n;
63
64         list_for_each_entry_safe(ip, n, &page->ip_list_inst, list) {
65                 list_del(&ip->list);
66                 free_ip(ip);
67         }
68
69         list_for_each_entry_safe(ip, n, &page->ip_list_no_inst, list) {
70                 list_del(&ip->list);
71                 free_ip(ip);
72         }
73
74         kfree(page);
75 }
76
77 static void sspt_list_add_ip(struct sspt_page *page, struct us_ip *ip)
78 {
79         list_add(&ip->list, &page->ip_list_no_inst);
80 }
81
82 static void sspt_list_del_ip(struct us_ip *ip)
83 {
84         list_del(&ip->list);
85 }
86
87 /**
88  * @brief Add instruction pointer to sspt_page
89  *
90  * @param page Pointer to the sspt_page struct
91  * @param ip Pointer to the us_ip struct
92  * @return Void
93  */
94 void sspt_add_ip(struct sspt_page *page, struct us_ip *ip)
95 {
96         ip->offset &= ~PAGE_MASK;
97         ip->page = page;
98         sspt_list_add_ip(page, ip);
99 }
100
101 /**
102  * @brief Del instruction pointer from sspt_page
103  *
104  * @param ip Pointer to the us_ip struct
105  * @return Void
106  */
107 void sspt_del_ip(struct us_ip *ip)
108 {
109         sspt_list_del_ip(ip);
110         free_ip(ip);
111 }
112
113 /**
114  * @brief Check if probes are set on the page
115  *
116  * @param page Pointer to the sspt_page struct
117  * @return
118  *       - 0 - false
119  *       - 1 - true
120  */
121 int sspt_page_is_installed(struct sspt_page *page)
122 {
123         int empty;
124
125         spin_lock(&page->lock);
126         empty = list_empty(&page->ip_list_inst);
127         spin_unlock(&page->lock);
128
129         return !empty;
130 }
131
132 /**
133  * @brief Install probes on the page
134  *
135  * @param page Pointer to the sspt_page struct
136  * @param file Pointer to the sspt_file struct
137  * @return Error code
138  */
139 int sspt_register_page(struct sspt_page *page, struct sspt_file *file)
140 {
141         int err = 0;
142         struct us_ip *ip, *n;
143         struct list_head ip_list_tmp;
144
145         spin_lock(&page->lock);
146         if (list_empty(&page->ip_list_no_inst)) {
147                 struct task_struct *task = page->file->proc->task;
148
149                 printk(KERN_INFO "page %lx in %s task[tgid=%u, pid=%u] "
150                        "already installed\n",
151                        page->offset, file->dentry->d_iname,
152                        task->tgid, task->pid);
153                 goto unlock;
154         }
155
156         INIT_LIST_HEAD(&ip_list_tmp);
157         list_replace_init(&page->ip_list_no_inst, &ip_list_tmp);
158         spin_unlock(&page->lock);
159
160         list_for_each_entry_safe(ip, n, &ip_list_tmp, list) {
161                 /* set virtual address */
162                 ip->orig_addr = file->vm_start + page->offset + ip->offset;
163
164                 err = sspt_register_usprobe(ip);
165                 if (err) {
166                         list_del(&ip->list);
167                         free_ip(ip);
168                         continue;
169                 }
170         }
171
172         spin_lock(&page->lock);
173         list_splice(&ip_list_tmp, &page->ip_list_inst);
174
175 unlock:
176         spin_unlock(&page->lock);
177
178         return 0;
179 }
180
181 /**
182  * @brief Uninstall probes on the page
183  *
184  * @param page Pointer to the sspt_page struct
185  * @param flag Action for probes
186  * @param task Pointer to the task_struct struct
187  * @return Error code
188  */
189 int sspt_unregister_page(struct sspt_page *page,
190                          enum US_FLAGS flag,
191                          struct task_struct *task)
192 {
193         int err = 0;
194         struct us_ip *ip;
195         struct list_head ip_list_tmp, *head;
196
197         spin_lock(&page->lock);
198         if (list_empty(&page->ip_list_inst)) {
199                 spin_unlock(&page->lock);
200                 return 0;
201         }
202
203         INIT_LIST_HEAD(&ip_list_tmp);
204         list_replace_init(&page->ip_list_inst, &ip_list_tmp);
205
206         spin_unlock(&page->lock);
207
208         list_for_each_entry(ip, &ip_list_tmp, list) {
209                 err = sspt_unregister_usprobe(task, ip, flag);
210                 if (err != 0) {
211                         /* TODO: ERROR */
212                         break;
213                 }
214         }
215
216         head = (flag == US_DISARM) ?
217                 &page->ip_list_inst : &page->ip_list_no_inst;
218
219         spin_lock(&page->lock);
220
221         list_splice(&ip_list_tmp, head);
222         spin_unlock(&page->lock);
223
224         return err;
225 }
226
227 void sspt_page_on_each_ip(struct sspt_page *page,
228                           void (*func)(struct us_ip *, void *), void *data)
229 {
230         struct us_ip *ip;
231
232         spin_lock(&page->lock);
233         list_for_each_entry(ip, &page->ip_list_inst, list)
234                 func(ip, data);
235
236         spin_unlock(&page->lock);
237 }