Skip to content

Commit

Permalink
Merge pull request dotnet/corefx#3798 from sokket/shim
Browse files Browse the repository at this point in the history
Shim'ing out the processor affinity function calls for the Linux kernel

Commit migrated from dotnet/corefx@7691049
  • Loading branch information
Jonathan Miller committed Oct 13, 2015
2 parents 62ff9b5 + 36b4da7 commit 56b4033
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 56 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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);

}
}
2 changes: 2 additions & 0 deletions src/libraries/Native/Common/pal_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 75 additions & 0 deletions src/libraries/Native/System.Native/pal_process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
#include <syslog.h>
#include <unistd.h>

#if HAVE_SCHED_SETAFFINITY || HAVE_SCHED_GETAFFINITY
#include <sched.h>
#endif

// Validate that our Signals enum values are correct for the platform
static_assert(PAL_SIGKILL == SIGKILL, "");

Expand Down Expand Up @@ -59,6 +63,12 @@ static_assert(PAL_PRIO_PROCESS == static_cast<int>(PRIO_PROCESS), "");
static_assert(PAL_PRIO_PGRP == static_cast<int>(PRIO_PGRP), "");
static_assert(PAL_PRIO_USER == static_cast<int>(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,
Expand Down Expand Up @@ -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
38 changes: 38 additions & 0 deletions src/libraries/Native/System.Native/pal_process.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
8 changes: 8 additions & 0 deletions src/libraries/Native/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@
<Compile Include="$(CommonPath)\Interop\Linux\procfs\Interop.ProcFsStat.cs">
<Link>Common\Interop\Linux\Interop.ProcFsStat.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Linux\libc\Interop.sched_getsetaffinity.cs">
<Link>Common\Interop\Linux\Interop.sched_getsetaffinity.cs</Link>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.SchedGetSetAffinity.cs">
<Link>Common\Interop\Linux\Interop.SchedGetSetAffinity.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Collections\Generic\EnumerableHelpers.cs">
<Link>Common\System\Collections\Generic\EnumerableHelpers.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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;
Expand All @@ -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
}
Expand Down

0 comments on commit 56b4033

Please sign in to comment.