um: irqs: allow invoking time-travel handler multiple times
[linux-2.6-microblaze.git] / arch / um / kernel / irq.c
index 82af519..f3f19da 100644 (file)
@@ -101,10 +101,12 @@ static bool irq_do_timetravel_handler(struct irq_entry *entry,
        if (!reg->timetravel_handler)
                return false;
 
-       /* prevent nesting - we'll get it again later when we SIGIO ourselves */
-       if (reg->pending_on_resume)
-               return true;
-
+       /*
+        * Handle all messages - we might get multiple even while
+        * interrupts are already suspended, due to suspend order
+        * etc. Note that time_travel_add_irq_event() will not add
+        * an event twice, if it's pending already "first wins".
+        */
        reg->timetravel_handler(reg->irq, entry->fd, reg->id, &reg->event);
 
        if (!reg->event.pending)
@@ -123,7 +125,8 @@ static bool irq_do_timetravel_handler(struct irq_entry *entry,
 #endif
 
 static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t,
-                             struct uml_pt_regs *regs)
+                             struct uml_pt_regs *regs,
+                             bool timetravel_handlers_only)
 {
        struct irq_reg *reg = &entry->reg[t];
 
@@ -136,18 +139,29 @@ static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type
        if (irq_do_timetravel_handler(entry, t))
                return;
 
-       if (irqs_suspended)
+       /*
+        * If we're called to only run time-travel handlers then don't
+        * actually proceed but mark sigio as pending (if applicable).
+        * For suspend/resume, timetravel_handlers_only may be true
+        * despite time-travel not being configured and used.
+        */
+       if (timetravel_handlers_only) {
+#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
+               mark_sigio_pending();
+#endif
                return;
+       }
 
        irq_io_loop(reg, regs);
 }
 
-void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
+static void _sigio_handler(struct uml_pt_regs *regs,
+                          bool timetravel_handlers_only)
 {
        struct irq_entry *irq_entry;
        int n, i;
 
-       if (irqs_suspended && !um_irq_timetravel_handler_used())
+       if (timetravel_handlers_only && !um_irq_timetravel_handler_used())
                return;
 
        while (1) {
@@ -172,14 +186,20 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
                        irq_entry = os_epoll_get_data_pointer(i);
 
                        for (t = 0; t < NUM_IRQ_TYPES; t++)
-                               sigio_reg_handler(i, irq_entry, t, regs);
+                               sigio_reg_handler(i, irq_entry, t, regs,
+                                                 timetravel_handlers_only);
                }
        }
 
-       if (!irqs_suspended)
+       if (!timetravel_handlers_only)
                free_irqs();
 }
 
+void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
+{
+       _sigio_handler(regs, irqs_suspended);
+}
+
 static struct irq_entry *get_irq_entry_by_fd(int fd)
 {
        struct irq_entry *walk;
@@ -467,6 +487,11 @@ int um_request_irq_tt(int irq, int fd, enum um_irq_type type,
                               devname, dev_id, timetravel_handler);
 }
 EXPORT_SYMBOL(um_request_irq_tt);
+
+void sigio_run_timetravel_handlers(void)
+{
+       _sigio_handler(NULL, true);
+}
 #endif
 
 #ifdef CONFIG_PM_SLEEP