+#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 {
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)
{
static inline void __init_slot(struct thread_slot *slot)
{
- slot->task = NULL;
slot->caller = 0;
slot->call_type = 0;
slot->drop = false;
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;
INIT_LIST_HEAD(&tmp->list);
__init_slot(tmp);
- list_add_tail(&tmp->list, &thread_slot_list);
return tmp;
}
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);
}
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;
}
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;
}
struct thread_slot *slot;
int ret = 0;
- __lock();
-
slot = __get_task_slot(task);
if (slot != NULL) {
*call_type = slot->call_type;
ret = -EINVAL;
get_call_type_done:
- __unlock();
-
return ret;
}
struct thread_slot *slot;
int ret = 0;
- __lock();
-
slot = __get_task_slot(task);
if (slot != NULL) {
*drop = slot->drop;
ret = -EINVAL;
get_drop_done:
- __unlock();
-
return ret;
}
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;
}
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;
}
__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)
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();
}