On Thu, 15 Feb 2018 18:20:41 +0100 Sebastian Andrzej Siewior <bige...@linutronix.de> wrote:
> -void __tasklet_schedule(struct tasklet_struct *t) > +static void __tasklet_schedule_common(struct tasklet_struct *t, > + struct tasklet_head *head, > + unsigned int softirq_nr) > { > unsigned long flags; > > local_irq_save(flags); If you look at the original patch, it did not move local_irq_save() into the common function. > t->next = NULL; > - *__this_cpu_read(tasklet_vec.tail) = t; > - __this_cpu_write(tasklet_vec.tail, &(t->next)); > - raise_softirq_irqoff(TASKLET_SOFTIRQ); > + *head->tail = t; > + head->tail = &(t->next); > + raise_softirq_irqoff(softirq_nr); > local_irq_restore(flags); > } > + > +void __tasklet_schedule(struct tasklet_struct *t) > +{ > + __tasklet_schedule_common(t, this_cpu_ptr(&tasklet_vec), What can happen is, we reference (tasklet_vec) on one CPU, get preempted (running in ksoftirqd), scheduled on another CPU, then when inside the common code, we are executing on a different CPU than the tasklet is for. The rasise_softirq() is happening on the wrong CPU. The local_irq_save() can't be moved to the common function. It must be done by each individual function. -- Steve > + TASKLET_SOFTIRQ); > +} > EXPORT_SYMBOL(__tasklet_schedule);