From 8c6587f27ca8aa0d9292c1d7d0bcce922be7b97b Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 12 Oct 2015 17:08:27 -0700 Subject: [PATCH] Shim'ing out the processor affinity function calls for the Linux kernel --- .../libc/Interop.sched_getsetaffinity.cs | 48 ------------ .../Interop.SchedGetSetAffinity.cs | 29 +++++++ src/Native/Common/pal_config.h.in | 2 + src/Native/System.Native/pal_process.cpp | 75 +++++++++++++++++++ src/Native/System.Native/pal_process.h | 38 ++++++++++ src/Native/configure.cmake | 8 ++ .../src/System.Diagnostics.Process.csproj | 4 +- .../src/System/Diagnostics/Process.Linux.cs | 12 +-- 8 files changed, 160 insertions(+), 56 deletions(-) delete mode 100644 src/Common/src/Interop/Linux/libc/Interop.sched_getsetaffinity.cs create mode 100644 src/Common/src/Interop/Unix/System.Native/Interop.SchedGetSetAffinity.cs diff --git a/src/Common/src/Interop/Linux/libc/Interop.sched_getsetaffinity.cs b/src/Common/src/Interop/Linux/libc/Interop.sched_getsetaffinity.cs deleted file mode 100644 index 7f8b982ad47d..000000000000 --- a/src/Common/src/Interop/Linux/libc/Interop.sched_getsetaffinity.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Runtime.InteropServices; - -using cpu_mask = System.Int64; -using pid_t = System.Int32; -using size_t = System.IntPtr; - -internal static partial class Interop -{ - internal static partial class libc - { - [DllImport(Libraries.Libc, SetLastError = true)] - internal static extern unsafe int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask); - - [DllImport(Libraries.Libc, SetLastError = true)] - internal static extern unsafe int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t* mask); - - internal unsafe struct cpu_set_t - { - internal fixed cpu_mask bits[CPU_SETSIZE / NCPUBITS]; - } - - internal static unsafe void CPU_SET(int cpu, cpu_set_t* set) - { - set->bits[CPUELT(cpu)] |= CPUMASK(cpu); - } - - internal static unsafe bool CPU_ISSET(int cpu, cpu_set_t* set) - { - return (set->bits[CPUELT(cpu)] & CPUMASK(cpu)) != 0; - } - - private const int CPU_SETSIZE = 1024; - private const int NCPUBITS = 64; // 8 * sizeof(cpu_set_t) - - private static int CPUELT(int cpu) - { - return cpu / NCPUBITS; - } - - private static cpu_mask CPUMASK(int cpu) - { - return (cpu_mask)1 << (cpu % NCPUBITS); - } - } -} diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.SchedGetSetAffinity.cs b/src/Common/src/Interop/Unix/System.Native/Interop.SchedGetSetAffinity.cs new file mode 100644 index 000000000000..07a1646dc5f0 --- /dev/null +++ b/src/Common/src/Interop/Unix/System.Native/Interop.SchedGetSetAffinity.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Sys + { + internal unsafe struct CpuSetBits + { + private fixed ulong Bits[16]; + } + + [DllImport(Libraries.SystemNative, SetLastError = true)] + internal static extern int SchedSetAffinity(int pid, ref CpuSetBits mask); + + [DllImport(Libraries.SystemNative, SetLastError = true)] + internal static extern int SchedGetAffinity(int pid, out CpuSetBits mask); + + [DllImport(Libraries.SystemNative)] + internal static extern void CpuSet(int cpu, ref CpuSetBits set); + + [DllImport(Libraries.SystemNative)] + internal static extern bool CpuIsSet(int cpu, ref CpuSetBits set); + + } +} diff --git a/src/Native/Common/pal_config.h.in b/src/Native/Common/pal_config.h.in index 925b9a39a8e3..b636fd25e92d 100644 --- a/src/Native/Common/pal_config.h.in +++ b/src/Native/Common/pal_config.h.in @@ -15,6 +15,8 @@ #cmakedefine01 HAVE_IN6_U #cmakedefine01 HAVE_IOCTL #cmakedefine01 HAVE_TIOCGWINSZ +#cmakedefine01 HAVE_SCHED_GETAFFINITY +#cmakedefine01 HAVE_SCHED_SETAFFINITY // Mac OS X has stat64, but it is deprecated since plain stat now // provides the same 64-bit aware struct when targeting OS X > 10.5 diff --git a/src/Native/System.Native/pal_process.cpp b/src/Native/System.Native/pal_process.cpp index 862857545f38..4147ff76e605 100644 --- a/src/Native/System.Native/pal_process.cpp +++ b/src/Native/System.Native/pal_process.cpp @@ -17,6 +17,10 @@ #include #include +#if HAVE_SCHED_SETAFFINITY || HAVE_SCHED_GETAFFINITY +#include +#endif + // Validate that our Signals enum values are correct for the platform static_assert(PAL_SIGKILL == SIGKILL, ""); @@ -59,6 +63,12 @@ static_assert(PAL_PRIO_PROCESS == static_cast(PRIO_PROCESS), ""); static_assert(PAL_PRIO_PGRP == static_cast(PRIO_PGRP), ""); static_assert(PAL_PRIO_USER == static_cast(PRIO_USER), ""); +#if HAVE_SCHED_SETAFFINITY || HAVE_SCHED_GETAFFINITY +// Validate that the number of bits in the PAL CPU affinity struct is +// greater than or equal to the native one so we can memcpy correctly +static_assert(sizeof(CpuSetBits) >= sizeof(cpu_set_t), ""); +#endif + enum { READ_END_OF_PIPE = 0, @@ -417,3 +427,68 @@ extern "C" char* GetCwd(char* buffer, int32_t bufferSize) return getcwd(buffer, UnsignedCast(bufferSize)); } + +#if HAVE_SCHED_SETAFFINITY +static bool ConvertPalToNativeCpuSet(const CpuSetBits& pal, cpu_set_t& native) +{ + if (sizeof(pal) > sizeof(native)) + { + // If the PAL struct is bigger than the native struct + // then make sure the user hasn't put data in the + // bits that would be inaccessible to the underlying OS + for (size_t i = sizeof(native); i < sizeof(pal); i++) + { + if (pal.Bits[i] != 0) + return false; + } + } + + memcpy(native.__bits, pal.Bits, ARRAY_SIZE(native.__bits)); + return true; +} + +extern "C" int32_t SchedSetAffinity(int32_t pid, CpuSetBits* mask) +{ + assert(mask != nullptr); + cpu_set_t set = {}; + if (!ConvertPalToNativeCpuSet(*mask, set)) + { + errno = EINVAL; + return -1; + } + return sched_setaffinity(pid, sizeof(cpu_set_t), &set); +} +#endif + +#if HAVE_SCHED_GETAFFINITY +static void ConvertNativeToPalCpuSet(const cpu_set_t& native, CpuSetBits& pal) +{ + pal = {}; + memcpy(pal.Bits, native.__bits, ARRAY_SIZE(native.__bits)); +} + +extern "C" int32_t SchedGetAffinity(int32_t pid, CpuSetBits* mask) +{ + assert(mask != nullptr); + cpu_set_t set; + int32_t result = sched_getaffinity(pid, sizeof(cpu_set_t), &set); + if (result == 0) + { + ConvertNativeToPalCpuSet(set, *mask); + } + + return result; +} +#endif + +#if HAVE_SCHED_GETAFFINITY || HAVE_SCHED_SETAFFINITY +extern "C" void CpuSet(int32_t cpu, CpuSetBits* set) +{ + set->Bits[__CPUELT(cpu)] |= __CPUMASK(cpu); +} + +extern "C" bool CpuIsSet(int32_t cpu, CpuSetBits* set) +{ + return (set->Bits[__CPUELT(cpu)] & __CPUMASK(cpu)) != 0; +} +#endif diff --git a/src/Native/System.Native/pal_process.h b/src/Native/System.Native/pal_process.h index 714296863bd2..417dfd831259 100644 --- a/src/Native/System.Native/pal_process.h +++ b/src/Native/System.Native/pal_process.h @@ -150,6 +150,16 @@ struct RLimit uint64_t MaximumLimit; }; +/** + * The native struct is dependent on the size of a numeric type + * so make it the largest possible value here and then we will + * copy to native as necessary + */ +struct CpuSetBits +{ + uint64_t Bits[16]; // __CPU_SETSIZE / (8 * sizeof(int64_t)) +}; + /** * Get the current limit for the specified resource of the current process. * Returns 0 on success; returns -1 on failure and errno is set to the error reason. @@ -246,3 +256,31 @@ extern "C" int32_t SetPriority(PriorityWhich which, int32_t who, int32_t nice); * Gets the current working directory of the currently executing process. */ extern "C" char* GetCwd(char* buffer, int32_t bufferSize); + +#if HAVE_SCHED_SETAFFINITY +/** + * Sets the CPU affinity mask for a specified thread (or the current thread if 0). + * + * Returns 0 on success; otherwise, -1 is returned and errno is set + */ +extern "C" int32_t SchedSetAffinity(int32_t pid, CpuSetBits* mask); +#endif + +#if HAVE_SCHED_GETAFFINITY +/** + * Gets the affinity mask of the specified thread (or the current thread if 0). + * + * Returns 0 on success; otherwise, -1 is returned and errno is set. + */ +extern "C" int32_t SchedGetAffinity(int32_t pid, CpuSetBits* mask); +#endif + +#if HAVE_SCHED_GETAFFINITY || HAVE_SCHED_SETAFFINITY + +/** + * Shim's for the macros of the same name. Sets a CPU bit or + * retrieves if the CPU bit is set + */ +extern "C" void CpuSet(int32_t cpu, CpuSetBits* set); +extern "C" bool CpuIsSet(int32_t cpu, CpuSetBits* set); +#endif diff --git a/src/Native/configure.cmake b/src/Native/configure.cmake index eec5337d5d52..945e3ba3982a 100644 --- a/src/Native/configure.cmake +++ b/src/Native/configure.cmake @@ -37,6 +37,14 @@ check_function_exists( ioctl HAVE_IOCTL) +check_function_exists( + sched_getaffinity + HAVE_SCHED_GETAFFINITY) + +check_function_exists( + sched_setaffinity + HAVE_SCHED_SETAFFINITY) + check_symbol_exists( TIOCGWINSZ "sys/ioctl.h" diff --git a/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 6a6f5568fa5e..9de003db7ee6 100644 --- a/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -268,8 +268,8 @@ Common\Interop\Linux\Interop.ProcFsStat.cs - - Common\Interop\Linux\Interop.sched_getsetaffinity.cs + + Common\Interop\Linux\Interop.SchedGetSetAffinity.cs Common\System\Collections\Generic\EnumerableHelpers.cs diff --git a/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs b/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs index b48bf099a3a7..64f9593c029d 100644 --- a/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs +++ b/src/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs @@ -91,8 +91,8 @@ private unsafe IntPtr ProcessorAffinityCore { EnsureState(State.HaveId); - Interop.libc.cpu_set_t set = default(Interop.libc.cpu_set_t); - if (Interop.libc.sched_getaffinity(_processId, (IntPtr)sizeof(Interop.libc.cpu_set_t), &set) != 0) + Interop.Sys.CpuSetBits set = default(Interop.Sys.CpuSetBits); + if (Interop.Sys.SchedGetAffinity(_processId, out set) != 0) { throw new Win32Exception(); // match Windows exception } @@ -101,7 +101,7 @@ private unsafe IntPtr ProcessorAffinityCore int maxCpu = IntPtr.Size == 4 ? 32 : 64; for (int cpu = 0; cpu < maxCpu; cpu++) { - if (Interop.libc.CPU_ISSET(cpu, &set)) + if (Interop.Sys.CpuIsSet(cpu, ref set)) bits |= (1u << cpu); } return (IntPtr)bits; @@ -110,17 +110,17 @@ private unsafe IntPtr ProcessorAffinityCore { EnsureState(State.HaveId); - Interop.libc.cpu_set_t set = default(Interop.libc.cpu_set_t); + Interop.Sys.CpuSetBits set = default(Interop.Sys.CpuSetBits); long bits = (long)value; int maxCpu = IntPtr.Size == 4 ? 32 : 64; for (int cpu = 0; cpu < maxCpu; cpu++) { if ((bits & (1u << cpu)) != 0) - Interop.libc.CPU_SET(cpu, &set); + Interop.Sys.CpuSet(cpu, ref set); } - if (Interop.libc.sched_setaffinity(_processId, (IntPtr)sizeof(Interop.libc.cpu_set_t), &set) != 0) + if (Interop.Sys.SchedSetAffinity(_processId, ref set) != 0) { throw new Win32Exception(); // match Windows exception }