[FIX] Preload: use task data for storing slots 23/44423/5
authorVasiliy Ulyanov <v.ulyanov@samsung.com>
Tue, 21 Jul 2015 11:54:46 +0000 (14:54 +0300)
committerVasiliy Ulyanov <v.ulyanov@samsung.com>
Tue, 21 Jul 2015 17:36:38 +0000 (20:36 +0300)
Probe slots need to be organized like a per-thread stack in order
to perform function entry/exit checks properly. The former single-list
implementation does not work as expected.

Change-Id: I16152ffb91ab0c5670c750e7cea6b566f59f9724
Signed-off-by: Vasiliy Ulyanov <v.ulyanov@samsung.com>
preload/preload_module.c
preload/preload_threads.c
preload/preload_threads.h

index 0e02a08..8895ec8 100644 (file)
@@ -136,23 +136,6 @@ void put_dentry(struct dentry *dentry)
        dput(dentry);
 }
 
-static inline unsigned long get_preload_flags(struct task_struct *task)
-{
-       unsigned long flags;
-       int ok;
-
-       flags = (unsigned long)swap_task_data_get(task, &ok);
-       WARN(!ok, "Preload flags(%08lx) seem corrupted", flags);
-
-       return (ok ? flags: 0);
-}
-
-static inline void set_preload_flags(struct task_struct *task,
-                                    unsigned long flags)
-{
-       swap_task_data_set(task, (void *)flags, 0);
-}
-
 static inline void __prepare_ujump(struct uretprobe_instance *ri,
                                   struct pt_regs *regs,
                                   unsigned long vaddr)
index 6a67829..360e354 100644 (file)
@@ -1,27 +1,30 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
 #include <linux/list.h>
+#include <task_data/task_data.h>
 #include "preload.h"
 #include "preload_threads.h"
 #include "preload_debugfs.h"
 #include "preload_patcher.h"
 #include "preload_pd.h"
 
-enum {
-       DEFAULT_SLOTS_CNT = 10,
+struct preload_td {
+       struct list_head slots;
+       unsigned long flags;
 };
 
 struct thread_slot {
        struct list_head list;
-       struct task_struct *task;
-       struct list_head disabled_addrs;          /* No use for spinlock - called only
-                                                in one thread */
+       struct list_head disabled_addrs;
+
        unsigned long caller;
        unsigned char call_type;
        bool drop;   /* TODO Workaround, remove when will be possible to install
-                    * several us probes at the same addr. */
+                     * several us probes at the same addr. */
 };
 
 struct disabled_addr {
@@ -29,46 +32,41 @@ struct disabled_addr {
        unsigned long addr;
 };
 
-static LIST_HEAD(thread_slot_list);
-static spinlock_t slock;
-
-
-static inline void __lock_init(void)
+static inline struct preload_td *get_preload_td(struct task_struct *task)
 {
-       spin_lock_init(&slock);
-}
+       struct preload_td *td = NULL;
+       int ok;
 
-static inline void __lock(void)
-{
-       spin_lock(&slock);
-}
+       td = swap_task_data_get(task, &ok);
+       WARN(!ok, "Preload td[%d/%d] seems corrupted", task->tgid, task->pid);
 
-static inline void __unlock(void)
-{
-       spin_unlock(&slock);
-}
+       if (!td) {
+               td = kzalloc(sizeof(*td), GFP_ATOMIC);
+               WARN(!td, "Failed to allocate preload_td");
 
+               if (td) {
+                       INIT_LIST_HEAD(&td->slots);
+                       /* We use SWAP_TD_FREE flag, i.e. the data will be
+                        * kfree'd by task_data module. */
+                       swap_task_data_set(task, td, SWAP_TD_FREE);
+               }
+       }
 
+       return td;
+}
 
-/* Checks slot for task */
-static inline bool __is_slot_for_task(struct thread_slot *slot,
-                                     struct task_struct *task)
+unsigned long get_preload_flags(struct task_struct *task)
 {
-       if (slot->task == task)
-               return true;
-
-       return false;
+       return get_preload_td(task)->flags;
 }
 
-/* Checks slot if it is free */
-static inline bool __is_slot_free(struct thread_slot *slot)
+void set_preload_flags(struct task_struct *task,
+                      unsigned long flags)
 {
-       if (slot->task == NULL)
-               return true;
-
-       return false;
+       get_preload_td(task)->flags = flags;
 }
 
+
 static inline bool __is_addr_found(struct disabled_addr *da,
                                   unsigned long addr)
 {
@@ -94,7 +92,6 @@ static inline void __remove_whole_disable_list(struct thread_slot *slot)
 
 static inline void __init_slot(struct thread_slot *slot)
 {
-       slot->task = NULL;
        slot->caller = 0;
        slot->call_type = 0;
        slot->drop = false;
@@ -111,7 +108,6 @@ static inline void __set_slot(struct thread_slot *slot,
                              struct task_struct *task, unsigned long caller,
                              unsigned char call_type, bool drop)
 {
-       slot->task = task;
        slot->caller = caller;
        slot->call_type = call_type;
        slot->drop = drop;
@@ -154,7 +150,6 @@ static inline struct thread_slot *__grow_slot(void)
 
        INIT_LIST_HEAD(&tmp->list);
        __init_slot(tmp);
-       list_add_tail(&tmp->list, &thread_slot_list);
 
        return tmp;
 }
@@ -166,25 +161,18 @@ static void __clean_slot(struct thread_slot *slot)
        kfree(slot);
 }
 
-/* Free all slots. This and all the previous slot functions should be called
-   in locks. */
-static void __clean_all(void)
-{
-       struct thread_slot *slot, *n;
-
-       list_for_each_entry_safe(slot, n, &thread_slot_list, list)
-               __clean_slot(slot);
-}
+/* There is no list_last_entry in Linux 3.10 */
+#ifndef list_last_entry
+#define list_last_entry(ptr, type, member) \
+       list_entry((ptr)->prev, type, member)
+#endif /* list_last_entry */
 
 static inline struct thread_slot *__get_task_slot(struct task_struct *task)
 {
-       struct thread_slot *slot;
-
-       list_for_each_entry(slot, &thread_slot_list, list)
-               if (__is_slot_for_task(slot, task))
-                       return slot;
+       struct preload_td *td = get_preload_td(task);
 
-       return NULL;
+       return list_empty(&td->slots) ? NULL :
+               list_last_entry(&td->slots, struct thread_slot, list);
 }
 
 
@@ -194,35 +182,27 @@ int preload_threads_set_data(struct task_struct *task, unsigned long caller,
                             unsigned char call_type,
                             unsigned long disable_addr, bool drop)
 {
+       struct preload_td *td = get_preload_td(task);
        struct thread_slot *slot;
        int ret = 0;
 
-       __lock();
-
-       list_for_each_entry(slot, &thread_slot_list, list) {
-               if (__is_slot_free(slot)) {
-                       __set_slot(slot, task, caller, call_type, drop);
-                       if ((disable_addr != 0) && 
-                           (__add_to_disable_list(slot, disable_addr) != 0)) {
-                               printk(PRELOAD_PREFIX "Cannot alloc memory!\n");
-                               ret = -ENOMEM;
-                       }
-                       goto set_data_done;
-               }
-       }
-
-       /* If there is no empty slots - grow */
        slot = __grow_slot();
        if (slot == NULL) {
                ret = -ENOMEM;
                goto set_data_done;
        }
 
+       if ((disable_addr != 0) &&
+           (__add_to_disable_list(slot, disable_addr) != 0)) {
+               printk(KERN_ERR PRELOAD_PREFIX "Cannot alloc memory!\n");
+               ret = -ENOMEM;
+               goto set_data_done;
+       }
+
        __set_slot(slot, task, caller, call_type, drop);
+       list_add_tail(&slot->list, &td->slots);
 
 set_data_done:
-       __unlock();
-
        return ret;
 }
 
@@ -231,20 +211,16 @@ int preload_threads_get_caller(struct task_struct *task, unsigned long *caller)
        struct thread_slot *slot;
        int ret = 0;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot != NULL) {
-                       *caller = slot->caller;
-                       goto get_caller_done;
+               *caller = slot->caller;
+               goto get_caller_done;
        }
 
        /* If we're here - slot was not found */
        ret = -EINVAL;
 
 get_caller_done:
-       __unlock();
-
        return ret;
 }
 
@@ -254,8 +230,6 @@ int preload_threads_get_call_type(struct task_struct *task,
        struct thread_slot *slot;
        int ret = 0;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot != NULL) {
                *call_type = slot->call_type;
@@ -266,8 +240,6 @@ int preload_threads_get_call_type(struct task_struct *task,
        ret = -EINVAL;
 
 get_call_type_done:
-       __unlock();
-
        return ret;
 }
 
@@ -276,8 +248,6 @@ int preload_threads_get_drop(struct task_struct *task, bool *drop)
        struct thread_slot *slot;
        int ret = 0;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot != NULL) {
                *drop = slot->drop;
@@ -288,8 +258,6 @@ int preload_threads_get_drop(struct task_struct *task, bool *drop)
        ret = -EINVAL;
 
 get_drop_done:
-       __unlock();
-
        return ret;
 }
 
@@ -299,14 +267,10 @@ bool preload_threads_check_disabled_probe(struct task_struct *task,
        struct thread_slot *slot;
        bool ret = false;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot != NULL)
                ret = __find_disabled_addr(slot, addr) == NULL ? false : true;
 
-       __unlock();
-
        return ret;
 }
 
@@ -315,11 +279,9 @@ void preload_threads_enable_probe(struct task_struct *task, unsigned long addr)
        struct thread_slot *slot;
        struct disabled_addr *da;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot == NULL) {
-               printk(PRELOAD_PREFIX "Error! Slot not found!\n");
+               printk(KERN_ERR PRELOAD_PREFIX "Error! Slot not found!\n");
                goto enable_probe_failed;
        }
 
@@ -328,8 +290,7 @@ void preload_threads_enable_probe(struct task_struct *task, unsigned long addr)
                __remove_from_disable_list(da);
 
 enable_probe_failed:
-
-       __unlock();
+       return; /* make gcc happy: cannot place label right before '}' */
 }
 
 int preload_threads_put_data(struct task_struct *task)
@@ -337,52 +298,22 @@ int preload_threads_put_data(struct task_struct *task)
        struct thread_slot *slot;
        int ret = 0;
 
-       __lock();
-
        slot = __get_task_slot(task);
        if (slot != NULL) {
                __reinit_slot(slot);
+               __clean_slot(slot); /* remove from list */
                goto put_data_done;
        }
 
 put_data_done:
-       __unlock();
-
        return ret;
 }
 
-/* Allocates slots */
 int preload_threads_init(void)
 {
-       int i, ret = 0;
-
-       __lock_init();
-
-       __lock();
-
-       for (i = 0; i < DEFAULT_SLOTS_CNT; i++) {
-               if (__grow_slot() == NULL) {
-                       ret = -ENOMEM;
-                       goto init_failed;
-               }
-       }
-
-       __unlock();
-
        return 0;
-
-init_failed:
-
-       __clean_all();
-       __unlock();
-
-       return ret;
 }
 
-/* Cleans slots */
 void preload_threads_exit(void)
 {
-       __lock();
-       __clean_all();
-       __unlock();
 }
index 64d9b59..9020fb8 100644 (file)
@@ -3,6 +3,10 @@
 
 struct task_struct;
 
+unsigned long get_preload_flags(struct task_struct *task);
+void set_preload_flags(struct task_struct *task,
+                      unsigned long flags);
+
 int preload_threads_set_data(struct task_struct *task, unsigned long caller,
                             unsigned char call_type,
                             unsigned long disable_addr, bool drop);