diff --git a/include/linux/ipipe_percpu.h b/include/linux/ipipe_percpu.h index 4b4d1f5..5d1b88d 100644 --- a/include/linux/ipipe_percpu.h +++ b/include/linux/ipipe_percpu.h @@ -34,6 +34,7 @@ struct ipipe_percpu_domain_data { unsigned long irqheld_mask[IPIPE_IRQ_IWORDS]; unsigned long irqall[IPIPE_NR_IRQS]; u64 evsync; + int evstamp; }; #ifdef CONFIG_SMP diff --git a/kernel/ipipe/core.c b/kernel/ipipe/core.c index 3a7b4d9..3baa443 100644 --- a/kernel/ipipe/core.c +++ b/kernel/ipipe/core.c @@ -262,6 +262,7 @@ void __ipipe_init_stage(struct ipipe_domain *ipd) ipipe_percpudom(ipd, irqall, cpu)[n] = 0; ipipe_percpudom(ipd, evsync, cpu) = 0; + ipipe_percpudom(ipd, evstamp, cpu) = 0; } for (n = 0; n < IPIPE_NR_IRQS; n++) { @@ -827,6 +828,7 @@ int fastcall __ipipe_dispatch_event (unsigned event, void *data) propagate = !evhand(event, start_domain, data); local_irq_save_hw(flags); ipipe_cpudom_var(next_domain, evsync) &= ~(1LL << event); + ipipe_cpudom_var(next_domain, evstamp)++; if (ipipe_current_domain != next_domain) this_domain = ipipe_current_domain; } @@ -1251,8 +1253,8 @@ ipipe_event_handler_t ipipe_catch_event(struct ipipe_domain *ipd, ipipe_event_handler_t handler) { ipipe_event_handler_t old_handler; + int self = 0, cpu, evstamp; unsigned long flags; - int self = 0, cpu; if (event & IPIPE_EVENT_SELF) { event &= ~IPIPE_EVENT_SELF; @@ -1305,8 +1307,18 @@ ipipe_event_handler_t ipipe_catch_event(struct ipipe_domain *ipd, */ for_each_online_cpu(cpu) { - while (ipipe_percpudom(ipd, evsync, cpu) & (1LL << event)) + /* + * We allow a grace period of 1s for the + * handler to complete, before assuming that + * it will never do so on the current CPU + * because of a migration. + */ + evstamp = ipipe_percpudom(ipd, evstamp, cpu); + do { + if ((ipipe_percpudom(ipd, evsync, cpu) & (1LL << event)) == 0) + break; schedule_timeout_interruptible(HZ / 50); + } while(++evstamp - ipipe_percpudom(ipd, evstamp, cpu) < 50); } }