Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Shim'ing out the processor affinity function calls for the Linux kernel #3798

Merged
merged 1 commit into from
Oct 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 0 additions & 48 deletions src/Common/src/Interop/Linux/libc/Interop.sched_getsetaffinity.cs

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more thought. Since the Bits field is never used and is here purely to impact the size of the struct, you could just add a StructLayout attribute to the type to set its size to 128. Then the type wouldn't need to be unsafe. But if you'd prefer to stick with what you have, that's fine, though you might chaynge the visibility of Bits to be private.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change the field to be private so that it's still visible what the struct contains and how it fits with the native implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

{
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/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/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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Earlier we statically asserted that sizeof(CpuSetBits) >= sizeof(cpu_set_t). So while unlikely, it's possible that CpuSetBits is larger than cpu_set_t, which means it's possible that this call could be trying to set affinity to a value that's out of range for the native side, and because we're only coping ARRAY_SIZE(native.__bits), we'll end up trimming those off and losing the information. Are we ok with that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a check when sizeof(pal) > sizeof(native) that will look at the bits from the end of pal until the end of native and make sure they are all 0 so that we won't truncate data. If we would truncate, I'll set errno to EINVAL so it will bubble up to the managed layer

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/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/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