diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 97d9ba26915e..a3b5edb26bc5 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -579,12 +579,15 @@ enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ TASKLET_STATE_RUN, /* Tasklet is running (SMP only) */ - TASKLET_STATE_PENDING /* Tasklet is pending */ + TASKLET_STATE_PENDING, /* Tasklet is pending */ + TASKLET_STATE_CHAINED /* Tasklet is chained */ }; #define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED) #define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN) #define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING) +#define TASKLET_STATEF_CHAINED (1 << TASKLET_STATE_CHAINED) +#define TASKLET_STATEF_RC (TASKLET_STATEF_RUN | TASKLET_STATEF_CHAINED) #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) static inline int tasklet_trylock(struct tasklet_struct *t) diff --git a/kernel/softirq.c b/kernel/softirq.c index 25bcf2f2714b..73dae64bfc9c 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -947,6 +947,10 @@ static void __tasklet_schedule_common(struct tasklet_struct *t, * is locked before adding it to the list. */ if (test_bit(TASKLET_STATE_SCHED, &t->state)) { + if (test_and_set_bit(TASKLET_STATE_CHAINED, &t->state)) { + tasklet_unlock(t); + return; + } t->next = NULL; *head->tail = t; head->tail = &(t->next); @@ -1040,7 +1044,7 @@ static void tasklet_action_common(struct softirq_action *a, again: t->func(t->data); - while (!tasklet_tryunlock(t)) { + while (cmpxchg(&t->state, TASKLET_STATEF_RC, 0) != TASKLET_STATEF_RC) { /* * If it got disabled meanwhile, bail out: */ diff --git a/localversion-rt b/localversion-rt index 24707986c321..4b7dca68a5b4 100644 --- a/localversion-rt +++ b/localversion-rt @@ -1 +1 @@ --rt48 +-rt49