diff options
Diffstat (limited to 'src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c')
-rw-r--r-- | src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c new file mode 100644 index 00000000..e12d7665 --- /dev/null +++ b/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c @@ -0,0 +1,258 @@ +/* $Id: mpnotification-r0drv-linux.c $ */ +/** @file + * IPRT - Multiprocessor Event Notifications, Ring-0 Driver, Linux. + */ + +/* + * Copyright (C) 2008-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-linux-kernel.h" +#include "internal/iprt.h" + +#include <iprt/asm-amd64-x86.h> +#include <iprt/errcore.h> +#include <iprt/cpuset.h> +#include <iprt/thread.h> +#include "r0drv/mp-r0drv.h" + +#if RTLNX_VER_MIN(4,10,0) + +static enum cpuhp_state g_rtR0MpOnline; + +/* + * Linux 4.10 completely removed CPU notifiers. So let's switch to CPU hotplug + * notification. + */ + +static int rtR0MpNotificationLinuxOnline(unsigned int cpu) +{ + RTCPUID idCpu = RTMpCpuIdFromSetIndex(cpu); + rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, idCpu); + return 0; +} + +static int rtR0MpNotificationLinuxOffline(unsigned int cpu) +{ + RTCPUID idCpu = RTMpCpuIdFromSetIndex(cpu); + rtMpNotificationDoCallbacks(RTMPEVENT_OFFLINE, idCpu); + return 0; +} + +DECLHIDDEN(int) rtR0MpNotificationNativeInit(void) +{ + int rc; + IPRT_LINUX_SAVE_EFL_AC(); + rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "vboxdrv:online", + rtR0MpNotificationLinuxOnline, rtR0MpNotificationLinuxOffline); + IPRT_LINUX_RESTORE_EFL_AC(); + /* + * cpuhp_setup_state_nocalls() returns a positive state number for + * CPUHP_AP_ONLINE_DYN or -ENOSPC if there is no free slot available + * (see cpuhp_reserve_state / definition of CPUHP_AP_ONLINE_DYN). + */ + AssertMsgReturn(rc > 0, ("%d\n", rc), RTErrConvertFromErrno(rc)); + g_rtR0MpOnline = rc; + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void) +{ + IPRT_LINUX_SAVE_EFL_AC(); + cpuhp_remove_state_nocalls(g_rtR0MpOnline); + IPRT_LINUX_RESTORE_EFL_AC(); +} + +#elif RTLNX_VER_MIN(2,5,71) && defined(CONFIG_SMP) + +static int rtMpNotificationLinuxCallback(struct notifier_block *pNotifierBlock, unsigned long ulNativeEvent, void *pvCpu); + +/** + * The notifier block we use for registering the callback. + */ +static struct notifier_block g_NotifierBlock = +{ + .notifier_call = rtMpNotificationLinuxCallback, + .next = NULL, + .priority = 0 +}; + +# ifdef CPU_DOWN_FAILED +/** + * The set of CPUs we've seen going offline recently. + */ +static RTCPUSET g_MpPendingOfflineSet; +# endif + + +/** + * The native callback. + * + * @returns NOTIFY_DONE. + * @param pNotifierBlock Pointer to g_NotifierBlock. + * @param ulNativeEvent The native event. + * @param pvCpu The cpu id cast into a pointer value. + * + * @remarks This can fire with preemption enabled and on any CPU. + */ +static int rtMpNotificationLinuxCallback(struct notifier_block *pNotifierBlock, unsigned long ulNativeEvent, void *pvCpu) +{ + bool fProcessEvent = false; + RTCPUID idCpu = (uintptr_t)pvCpu; + NOREF(pNotifierBlock); + + /* + * Note that redhat/CentOS ported _some_ of the FROZEN macros + * back to their 2.6.18-92.1.10.el5 kernel but actually don't + * use them. Thus we have to test for both CPU_TASKS_FROZEN and + * the individual event variants. + */ + switch (ulNativeEvent) + { + /* + * Pick up online events or failures to go offline. + * Ignore failure events for CPUs we didn't see go offline. + */ +# ifdef CPU_DOWN_FAILED + case CPU_DOWN_FAILED: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_FAILED_FROZEN) + case CPU_DOWN_FAILED_FROZEN: +# endif + if (!RTCpuSetIsMember(&g_MpPendingOfflineSet, idCpu)) + break; /* fProcessEvents = false */ + /* fall thru */ +# endif + case CPU_ONLINE: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_ONLINE_FROZEN) + case CPU_ONLINE_FROZEN: +# endif +# ifdef CPU_DOWN_FAILED + RTCpuSetDel(&g_MpPendingOfflineSet, idCpu); +# endif + fProcessEvent = true; + break; + + /* + * Pick the earliest possible offline event. + * The only important thing here is that we get the event and that + * it's exactly one. + */ +# ifdef CPU_DOWN_PREPARE + case CPU_DOWN_PREPARE: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_PREPARE_FROZEN) + case CPU_DOWN_PREPARE_FROZEN: +# endif + fProcessEvent = true; +# else + case CPU_DEAD: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_DEAD_FROZEN) + case CPU_DEAD_FROZEN: +# endif + /* Don't process CPU_DEAD notifications. */ +# endif +# ifdef CPU_DOWN_FAILED + RTCpuSetAdd(&g_MpPendingOfflineSet, idCpu); +# endif + break; + } + + if (!fProcessEvent) + return NOTIFY_DONE; + + switch (ulNativeEvent) + { +# ifdef CPU_DOWN_FAILED + case CPU_DOWN_FAILED: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_FAILED_FROZEN) + case CPU_DOWN_FAILED_FROZEN: +# endif +# endif + case CPU_ONLINE: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_ONLINE_FROZEN) + case CPU_ONLINE_FROZEN: +# endif + rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, idCpu); + break; + +# ifdef CPU_DOWN_PREPARE + case CPU_DOWN_PREPARE: +# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_PREPARE_FROZEN) + case CPU_DOWN_PREPARE_FROZEN: +# endif + rtMpNotificationDoCallbacks(RTMPEVENT_OFFLINE, idCpu); + break; +# endif + } + + return NOTIFY_DONE; +} + + +DECLHIDDEN(int) rtR0MpNotificationNativeInit(void) +{ + int rc; + IPRT_LINUX_SAVE_EFL_AC(); + +# ifdef CPU_DOWN_FAILED + RTCpuSetEmpty(&g_MpPendingOfflineSet); +# endif + + rc = register_cpu_notifier(&g_NotifierBlock); + IPRT_LINUX_RESTORE_EFL_AC(); + AssertMsgReturn(!rc, ("%d\n", rc), RTErrConvertFromErrno(rc)); + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void) +{ + IPRT_LINUX_SAVE_EFL_AC(); + unregister_cpu_notifier(&g_NotifierBlock); + IPRT_LINUX_RESTORE_EFL_AC(); +} + +#else /* Not supported / Not needed */ + +DECLHIDDEN(int) rtR0MpNotificationNativeInit(void) +{ + return VINF_SUCCESS; +} + +DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void) +{ +} + +#endif /* Not supported / Not needed */ + |