/* When epoll triggers we do not know why it did so * we can also have different IRQs for read and write. * This is why we keep a small irq_reg array for each fd - * one entry per IRQ type
*/ struct irq_reg { void *id; int irq; /* it's cheaper to store this than to query it */ int events; bool active; bool pending; bool wakeup; #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT bool pending_event; void (*timetravel_handler)(int, int, void *, struct time_travel_event *); struct time_travel_event event; #endif
};
staticvoid irq_io_loop(struct irq_reg *irq, struct uml_pt_regs *regs)
{ /* * irq->active guards against reentry * irq->pending accumulates pending requests * if pending is raised the irq_handler is re-run * until pending is cleared
*/ if (irq->active) {
irq->active = false;
do {
irq->pending = false;
do_IRQ(irq->irq, regs);
} while (irq->pending);
/* * 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, ®->event);
if (os_epoll_triggered(idx, reg->events) <= 0) return;
if (irq_do_timetravel_handler(entry, t)) return;
/* * 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
reg->pending_event = true;
irqs_pending = true;
mark_sigio_pending(); #endif return;
}
irq_io_loop(reg, regs);
}
staticvoid _sigio_handler(struct uml_pt_regs *regs, bool timetravel_handlers_only)
{ struct irq_entry *irq_entry; int n, i;
if (timetravel_handlers_only && !um_irq_timetravel_handler_used()) return;
/* Flush out pending events that were ignored due to time-travel. */ if (!irqs_suspended)
irq_do_pending_events(timetravel_handlers_only);
while (1) { /* This is now lockless - epoll keeps back-referencesto the irqs * which have trigger it so there is no need to walk the irq * list and lock it every time. We avoid locking by turning off * IO for a specific fd by executing os_del_epoll_fd(fd) before * we do any changes to the actual data structures
*/
n = os_waiting_for_events_epoll();
if (n <= 0) { if (n == -EINTR) continue; else break;
}
for (i = 0; i < n ; i++) { enum um_irq_type t;
irq_entry = os_epoll_get_data_pointer(i);
for (t = 0; t < NUM_IRQ_TYPES; t++)
sigio_reg_handler(i, irq_entry, t, regs,
timetravel_handlers_only);
}
}
raw_spin_lock_irqsave(&irq_lock, flags);
irq_entry = get_irq_entry_by_fd(fd); if (irq_entry) {
already: /* cannot register the same FD twice with the same type */ if (WARN_ON(irq_entry->reg[type].events)) {
err = -EALREADY; goto out_unlock;
}
/* * Remove the entry or entries for a specific FD, if you * don't want to remove all the possible entries then use * um_free_irq() or deactivate_fd() instead.
*/ void free_irq_by_fd(int fd)
{ struct irq_entry *to_free; unsignedlong flags;
/* * Called just before shutdown in order to provide a clean exec * environment in case the system is rebooting. No locking because * that would cause a pointless shutdown hang if something hadn't * released the lock.
*/ int deactivate_all_fds(void)
{ struct irq_entry *entry;
/* Stop IO. The IRQ loop has no lock so this is our * only way of making sure we are safe to dispose * of all IRQ handlers
*/
os_set_ioignore();
/* we can no longer call kfree() here so just deactivate */
list_for_each_entry(entry, &active_fds, list)
os_del_epoll_fd(entry->fd);
os_close_epoll_fd(); return 0;
}
/* * do_IRQ handles all normal device IRQs (the special * SMP cross-CPU interrupts have their own specific * handlers).
*/ unsignedint do_IRQ(int irq, struct uml_pt_regs *regs)
{ struct pt_regs *old_regs = set_irq_regs((struct pt_regs *)regs);
irq_enter();
generic_handle_irq(irq);
irq_exit();
set_irq_regs(old_regs); return 1;
}
for (t = 0; t < NUM_IRQ_TYPES; t++) { if (!entry->reg[t].events) continue;
/* * For the SIGIO_WRITE_IRQ, which is used to handle the * SIGIO workaround thread, we need special handling: * enable wake for it itself, but below we tell it about * any FDs that should be suspended.
*/ if (entry->reg[t].wakeup ||
entry->reg[t].irq == SIGIO_WRITE_IRQ #ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
|| entry->reg[t].timetravel_handler #endif
) {
clear = false; break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.