trim task_work: get rid of hlist
Al Viro [Wed, 27 Jun 2012 05:24:13 +0000 (09:24 +0400)]
layout based on Oleg's suggestion; single-linked list,
task->task_works points to the last element, forward pointer
from said last element points to head.  I'd still prefer
much more regular scheme with two pointers in task_work,
but...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

include/linux/sched.h
include/linux/task_work.h
include/linux/tracehook.h
kernel/fork.c
kernel/task_work.c

index 4059c0f..b9216eb 100644 (file)
@@ -1405,7 +1405,7 @@ struct task_struct {
        int (*notifier)(void *priv);
        void *notifier_data;
        sigset_t *notifier_mask;
-       struct hlist_head task_works;
+       void *task_works;
 
        struct audit_context *audit_context;
 #ifdef CONFIG_AUDITSYSCALL
index 627421c..3b3e2c8 100644 (file)
@@ -8,7 +8,7 @@ struct task_work;
 typedef void (*task_work_func_t)(struct task_work *);
 
 struct task_work {
-       struct hlist_node hlist;
+       struct task_work *next;
        task_work_func_t func;
 };
 
@@ -24,7 +24,7 @@ void task_work_run(void);
 
 static inline void exit_task_work(struct task_struct *task)
 {
-       if (unlikely(!hlist_empty(&task->task_works)))
+       if (unlikely(task->task_works))
                task_work_run();
 }
 
index 6a4d82b..1e98b55 100644 (file)
@@ -192,7 +192,7 @@ static inline void tracehook_notify_resume(struct pt_regs *regs)
         * hlist_add_head(task->task_works);
         */
        smp_mb__after_clear_bit();
-       if (unlikely(!hlist_empty(&current->task_works)))
+       if (unlikely(current->task_works))
                task_work_run();
 }
 
index ab5211b..bebabad 100644 (file)
@@ -1415,7 +1415,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
         */
        p->group_leader = p;
        INIT_LIST_HEAD(&p->thread_group);
-       INIT_HLIST_HEAD(&p->task_works);
+       p->task_works = NULL;
 
        /* Now that the task is set up, run cgroup callbacks if
         * necessary. We need to run them before the task is visible
index 82d1c79..9b8948d 100644 (file)
@@ -19,7 +19,12 @@ task_work_add(struct task_struct *task, struct task_work *twork, bool notify)
         */
        raw_spin_lock_irqsave(&task->pi_lock, flags);
        if (likely(!(task->flags & PF_EXITING))) {
-               hlist_add_head(&twork->hlist, &task->task_works);
+               struct task_work *last = task->task_works;
+               struct task_work *first = last ? last->next : twork;
+               twork->next = first;
+               if (last)
+                       last->next = twork;
+               task->task_works = twork;
                err = 0;
        }
        raw_spin_unlock_irqrestore(&task->pi_lock, flags);
@@ -34,51 +39,48 @@ struct task_work *
 task_work_cancel(struct task_struct *task, task_work_func_t func)
 {
        unsigned long flags;
-       struct task_work *twork;
-       struct hlist_node *pos;
+       struct task_work *last, *res = NULL;
 
        raw_spin_lock_irqsave(&task->pi_lock, flags);
-       hlist_for_each_entry(twork, pos, &task->task_works, hlist) {
-               if (twork->func == func) {
-                       hlist_del(&twork->hlist);
-                       goto found;
+       last = task->task_works;
+       if (last) {
+               struct task_work *q = last, *p = q->next;
+               while (1) {
+                       if (p->func == func) {
+                               q->next = p->next;
+                               if (p == last)
+                                       task->task_works = q == p ? NULL : q;
+                               res = p;
+                               break;
+                       }
+                       if (p == last)
+                               break;
+                       q = p;
+                       p = q->next;
                }
        }
-       twork = NULL;
- found:
        raw_spin_unlock_irqrestore(&task->pi_lock, flags);
-
-       return twork;
+       return res;
 }
 
 void task_work_run(void)
 {
        struct task_struct *task = current;
-       struct hlist_head task_works;
-       struct hlist_node *pos;
+       struct task_work *p, *q;
 
        raw_spin_lock_irq(&task->pi_lock);
-       hlist_move_list(&task->task_works, &task_works);
+       p = task->task_works;
+       task->task_works = NULL;
        raw_spin_unlock_irq(&task->pi_lock);
 
-       if (unlikely(hlist_empty(&task_works)))
+       if (unlikely(!p))
                return;
-       /*
-        * We use hlist to save the space in task_struct, but we want fifo.
-        * Find the last entry, the list should be short, then process them
-        * in reverse order.
-        */
-       for (pos = task_works.first; pos->next; pos = pos->next)
-               ;
-
-       for (;;) {
-               struct hlist_node **pprev = pos->pprev;
-               struct task_work *twork = container_of(pos, struct task_work,
-                                                       hlist);
-               twork->func(twork);
 
-               if (pprev == &task_works.first)
-                       break;
-               pos = container_of(pprev, struct hlist_node, next);
+       q = p->next; /* head */
+       p->next = NULL; /* cut it */
+       while (q) {
+               p = q->next;
+               q->func(q);
+               q = p;
        }
 }