/* Take the mutex for any API call or modification. Take the mutex first. */ static DEFINE_MUTEX(sdei_events_lock);
/* and then hold this when modifying the list */ static DEFINE_SPINLOCK(sdei_list_lock); static LIST_HEAD(sdei_list);
/* Private events are registered/enabled via IPI passing one of these */ struct sdei_crosscall_args { struct sdei_event *event;
atomic_t errors; int first_error;
};
if (sdei_firmware_call) {
sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4,
&res);
err = sdei_to_linux_errno(res.a0);
} else { /* * !sdei_firmware_call means we failed to probe or called * sdei_mark_interface_broken(). -EIO is not an error returned * by sdei_to_linux_errno() and is used to suppress messages * from this driver.
*/
err = -EIO;
res.a0 = SDEI_NOT_SUPPORTED;
}
/* * unregister events, but don't destroy them as they are re-registered by * sdei_reregister_shared().
*/ staticint sdei_unregister_shared(void)
{ int err = 0; struct sdei_event *event;
if (event->reregister) {
err = sdei_do_local_call(_local_event_register, event); if (err) {
pr_err("Failed to re-register event %u: %d\n",
event->event_num, err);
}
}
if (event->reenable) {
err = sdei_do_local_call(_local_event_enable, event); if (err) {
pr_err("Failed to re-enable event %u: %d\n",
event->event_num, err);
}
}
}
spin_unlock(&sdei_list_lock);
return sdei_unmask_local_cpu();
}
/* When entering idle, mask/unmask events for this cpu */ staticint sdei_pm_notifier(struct notifier_block *nb, unsignedlong action, void *data)
{ int rv;
WARN_ON_ONCE(preemptible());
switch (action) { case CPU_PM_ENTER:
rv = sdei_mask_local_cpu(); break; case CPU_PM_EXIT: case CPU_PM_ENTER_FAILED:
rv = sdei_unmask_local_cpu(); break; default: return NOTIFY_DONE;
}
/* * We need all events to be reregistered when we resume from hibernate. * * The sequence is freeze->thaw. Reboot. freeze->restore. We unregister * events during freeze, then re-register and re-enable them during thaw * and restore.
*/ staticint sdei_device_freeze(struct device *dev)
{ int err;
/* * Mask all CPUs and unregister all events on panic, reboot or kexec.
*/ staticint sdei_reboot_notifier(struct notifier_block *nb, unsignedlong action, void *data)
{ /* * We are going to reset the interface, after this there is no point * doing work when we take CPUs offline.
*/
cpuhp_remove_state(sdei_hp_state);
int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
sdei_event_callback *critical_cb)
{ int err;
u64 result;
u32 event_num;
sdei_event_callback *cb;
if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) return -EOPNOTSUPP;
event_num = ghes->generic->notify.vector; if (event_num == 0) { /* * Event 0 is reserved by the specification for * SDEI_EVENT_SIGNAL.
*/ return -EINVAL;
}
err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
&result); if (err) return err;
err = sdei_event_register(event_num, cb, ghes); if (!err)
err = sdei_event_enable(event_num);
return err;
}
int sdei_unregister_ghes(struct ghes *ghes)
{ int i; int err;
u32 event_num = ghes->generic->notify.vector;
might_sleep();
if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) return -EOPNOTSUPP;
/* * The event may be running on another CPU. Disable it * to stop new events, then try to unregister a few times.
*/
err = sdei_event_disable(event_num); if (err) return err;
for (i = 0; i < 3; i++) {
err = sdei_event_unregister(event_num); if (err != -EINPROGRESS) break;
staticint sdei_probe(struct platform_device *pdev)
{ int err;
u64 ver = 0; int conduit;
conduit = sdei_get_conduit(pdev); if (!sdei_firmware_call) return 0;
err = sdei_api_get_version(&ver); if (err) {
pr_err("Failed to get SDEI version: %d\n", err);
sdei_mark_interface_broken(); return err;
}
pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
(int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
(int)SDEI_VERSION_VENDOR(ver));
if (SDEI_VERSION_MAJOR(ver) != 1) {
pr_warn("Conflicting SDEI version detected.\n");
sdei_mark_interface_broken(); return -EINVAL;
}
err = sdei_platform_reset(); if (err) return err;
sdei_entry_point = sdei_arch_get_entry_point(conduit); if (!sdei_entry_point) { /* Not supported due to hardware or boot configuration */
sdei_mark_interface_broken(); return 0;
}
err = cpu_pm_register_notifier(&sdei_pm_nb); if (err) {
pr_warn("Failed to register CPU PM notifier...\n"); goto error;
}
err = register_reboot_notifier(&sdei_reboot_nb); if (err) {
pr_warn("Failed to register reboot notifier...\n"); goto remove_cpupm;
}
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "SDEI",
&sdei_cpuhp_up, &sdei_cpuhp_down); if (err < 0) {
pr_warn("Failed to register CPU hotplug notifier...\n"); goto remove_reboot;
}
int sdei_event_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
{ int err;
u32 event_num = arg->event_num;
err = arg->callback(event_num, regs, arg->callback_arg); if (err)
pr_err_ratelimited("event %u on CPU %u failed with error: %d\n",
event_num, smp_processor_id(), err);
void sdei_handler_abort(void)
{ /* * If the crash happened in an SDEI event handler then we need to * finish the handler with the firmware so that we can have working * interrupts in the crash kernel.
*/ if (__this_cpu_read(sdei_active_critical_event)) {
pr_warn("still in SDEI critical event context, attempting to finish handler.\n");
__sdei_handler_abort();
__this_cpu_write(sdei_active_critical_event, NULL);
} if (__this_cpu_read(sdei_active_normal_event)) {
pr_warn("still in SDEI normal event context, attempting to finish handler.\n");
__sdei_handler_abort();
__this_cpu_write(sdei_active_normal_event, NULL);
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
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.