Cygwin: Implement sched_[gs]etaffinity()
This patch set implements the Linux syscalls sched_getaffinity, sched_setaffinity, pthread_getaffinity_np, and pthread_setaffinity_np. Linux has a straightforward view of the cpu sets used in affinity masks. They are simply long (1024-bit) bit masks. This code emulates that view while internally dealing with Windows' distribution of available CPUs among processor groups.
This commit is contained in:
parent
d54edfdf81
commit
641ecb0753
@ -92,6 +92,29 @@ int sched_yield( void );
|
||||
|
||||
#if __GNU_VISIBLE
|
||||
int sched_getcpu(void);
|
||||
|
||||
/* Affinity-related definitions, here until numerous enough to separate out */
|
||||
#ifdef __x86_64__
|
||||
typedef uint64_t __cpu_mask;
|
||||
#else
|
||||
typedef uint32_t __cpu_mask;
|
||||
#endif
|
||||
#define __CPU_SETSIZE 1024 // maximum number of logical processors tracked
|
||||
#define __NCPUBITS (8 * sizeof (__cpu_mask)) // max size of processor group
|
||||
#define __CPU_GROUPMAX (__CPU_SETSIZE / __NCPUBITS) // maximum group number
|
||||
|
||||
#define __CPUELT(cpu) ((cpu) / __NCPUBITS)
|
||||
#define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
__cpu_mask __bits[__CPU_GROUPMAX];
|
||||
} cpu_set_t;
|
||||
|
||||
int sched_getaffinity (pid_t, size_t, cpu_set_t *);
|
||||
int sched_get_thread_affinity (void *, size_t, cpu_set_t *);
|
||||
int sched_setaffinity (pid_t, size_t, const cpu_set_t *);
|
||||
int sched_set_thread_affinity (void *, size_t, const cpu_set_t *);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1084,6 +1084,7 @@ pthread_create SIGFE
|
||||
pthread_detach SIGFE
|
||||
pthread_equal SIGFE
|
||||
pthread_exit SIGFE
|
||||
pthread_getaffinity_np SIGFE
|
||||
pthread_getattr_np SIGFE
|
||||
pthread_getconcurrency SIGFE
|
||||
pthread_getcpuclockid SIGFE
|
||||
@ -1128,6 +1129,7 @@ pthread_rwlockattr_getpshared SIGFE
|
||||
pthread_rwlockattr_init SIGFE
|
||||
pthread_rwlockattr_setpshared SIGFE
|
||||
pthread_self SIGFE
|
||||
pthread_setaffinity_np SIGFE
|
||||
pthread_setcancelstate SIGFE
|
||||
pthread_setcanceltype SIGFE
|
||||
pthread_setconcurrency SIGFE
|
||||
@ -1248,10 +1250,12 @@ scandirat SIGFE
|
||||
scanf SIGFE
|
||||
sched_get_priority_max SIGFE
|
||||
sched_get_priority_min SIGFE
|
||||
sched_getaffinity SIGFE
|
||||
sched_getcpu SIGFE
|
||||
sched_getparam SIGFE
|
||||
sched_getscheduler NOSIGFE
|
||||
sched_rr_get_interval SIGFE
|
||||
sched_setaffinity SIGFE
|
||||
sched_setparam SIGFE
|
||||
sched_setscheduler SIGFE
|
||||
sched_yield SIGFE
|
||||
|
@ -509,12 +509,14 @@ details. */
|
||||
336: New Cygwin PID algorithm (yeah, not really an API change)
|
||||
337: MOUNT_BINARY -> MOUNT_TEXT
|
||||
338: Export secure_getenv.
|
||||
339: Export sched_getaffinity, sched_setaffinity, pthread_getaffinity_np,
|
||||
pthread_setaffinity_np.
|
||||
|
||||
Note that we forgot to bump the api for ualarm, strtoll, strtoull,
|
||||
sigaltstack, sethostname. */
|
||||
|
||||
#define CYGWIN_VERSION_API_MAJOR 0
|
||||
#define CYGWIN_VERSION_API_MINOR 338
|
||||
#define CYGWIN_VERSION_API_MINOR 339
|
||||
|
||||
/* There is also a compatibity version number associated with the shared memory
|
||||
regions. It is incremented when incompatible changes are made to the shared
|
||||
|
@ -226,8 +226,10 @@ void pthread_testcancel (void);
|
||||
/* Non posix calls */
|
||||
|
||||
#if __GNU_VISIBLE
|
||||
int pthread_getaffinity_np (pthread_t, size_t, cpu_set_t *);
|
||||
int pthread_getattr_np (pthread_t, pthread_attr_t *);
|
||||
int pthread_getname_np (pthread_t, char *, size_t) __attribute__((__nonnull__(2)));
|
||||
int pthread_setaffinity_np (pthread_t, size_t, const cpu_set_t *);
|
||||
int pthread_setname_np (pthread_t, const char *) __attribute__((__nonnull__(2)));
|
||||
int pthread_sigqueue (pthread_t *, int, const union sigval);
|
||||
int pthread_timedjoin_np (pthread_t, void **, const struct timespec *);
|
||||
|
@ -963,17 +963,19 @@ SetThreadName(DWORD dwThreadID, const char* threadName)
|
||||
|
||||
#define add_size(p,s) ((p) = ((__typeof__(p))((PBYTE)(p)+(s))))
|
||||
|
||||
static WORD num_cpu_per_group = 0;
|
||||
static WORD group_count = 0;
|
||||
|
||||
WORD
|
||||
__get_cpus_per_group (void)
|
||||
{
|
||||
static WORD num_cpu_per_group = 0;
|
||||
|
||||
tmp_pathbuf tp;
|
||||
|
||||
if (num_cpu_per_group)
|
||||
return num_cpu_per_group;
|
||||
|
||||
num_cpu_per_group = 64;
|
||||
group_count = 1;
|
||||
|
||||
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX lpi =
|
||||
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) tp.c_get ();
|
||||
@ -1005,10 +1007,20 @@ __get_cpus_per_group (void)
|
||||
actually available CPUs. The ActiveProcessorCount is correct
|
||||
though. So we just use ActiveProcessorCount for now, hoping for
|
||||
the best. */
|
||||
num_cpu_per_group
|
||||
= plpi->Group.GroupInfo[0].ActiveProcessorCount;
|
||||
num_cpu_per_group = plpi->Group.GroupInfo[0].ActiveProcessorCount;
|
||||
|
||||
/* Follow that lead to get the group count. */
|
||||
group_count = plpi->Group.ActiveGroupCount;
|
||||
break;
|
||||
}
|
||||
|
||||
return num_cpu_per_group;
|
||||
}
|
||||
|
||||
WORD
|
||||
__get_group_count (void)
|
||||
{
|
||||
if (group_count == 0)
|
||||
(void) __get_cpus_per_group (); // caller should have called this first
|
||||
return group_count;
|
||||
}
|
||||
|
@ -120,5 +120,6 @@ extern "C" HANDLE WINAPI CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func,
|
||||
void SetThreadName (DWORD dwThreadID, const char* threadName);
|
||||
|
||||
WORD __get_cpus_per_group (void);
|
||||
WORD __get_group_count (void);
|
||||
|
||||
#endif /*_MISCFUNCS_H*/
|
||||
|
@ -5,6 +5,9 @@ What's new:
|
||||
1703 or later. Add fake 24 bit color support for legacy console,
|
||||
which uses the nearest color from 16 system colors.
|
||||
|
||||
- New APIs: sched_getaffinity, sched_setaffinity, pthread_getaffinity_np,
|
||||
pthread_setaffinity_np.
|
||||
|
||||
|
||||
What changed:
|
||||
-------------
|
||||
|
@ -424,4 +424,312 @@ sched_getcpu ()
|
||||
return pnum.Group * __get_cpus_per_group () + pnum.Number;
|
||||
}
|
||||
|
||||
/* construct an affinity mask with just the 'count' lower-order bits set */
|
||||
static __cpu_mask
|
||||
groupmask (int count)
|
||||
{
|
||||
if (count >= (int) (NBBY * sizeof (__cpu_mask)))
|
||||
return ~(__cpu_mask) 0;
|
||||
else
|
||||
return ((__cpu_mask) 1 << count) - 1;
|
||||
}
|
||||
|
||||
/* return the affinity mask of the indicated group from the given cpu set */
|
||||
static __cpu_mask
|
||||
getgroup (size_t sizeof_set, const cpu_set_t *set, int groupnum)
|
||||
{
|
||||
int groupsize = __get_cpus_per_group ();
|
||||
int bitindex = groupnum * groupsize;
|
||||
|
||||
int setsize = NBBY * sizeof_set; // bit size of whole cpu set
|
||||
if (bitindex + groupsize > setsize)
|
||||
return (__cpu_mask) 0;
|
||||
|
||||
int wordsize = NBBY * sizeof (cpu_set_t);
|
||||
int wordindex = bitindex / wordsize;
|
||||
|
||||
__cpu_mask result = set->__bits[wordindex];
|
||||
int offset = bitindex % wordsize;
|
||||
if (offset)
|
||||
{
|
||||
result >>= offset;
|
||||
offset = wordsize - offset;
|
||||
}
|
||||
else
|
||||
offset = wordsize;
|
||||
|
||||
if (offset < groupsize)
|
||||
result |= (set->__bits[wordindex + 1] << offset);
|
||||
if (groupsize < wordsize)
|
||||
result &= groupmask (groupsize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* set the given affinity mask for indicated group within the given cpu set */
|
||||
static __cpu_mask
|
||||
setgroup (size_t sizeof_set, cpu_set_t *set, int groupnum, __cpu_mask aff)
|
||||
{
|
||||
int groupsize = __get_cpus_per_group ();
|
||||
int bitindex = groupnum * groupsize;
|
||||
|
||||
int setsize = NBBY * sizeof_set; // bit size of whole cpu set
|
||||
if (bitindex + groupsize > setsize)
|
||||
return (__cpu_mask) 0;
|
||||
|
||||
int wordsize = NBBY * sizeof (cpu_set_t);
|
||||
int wordindex = bitindex / wordsize;
|
||||
int offset = bitindex % wordsize;
|
||||
__cpu_mask mask = groupmask (groupsize);
|
||||
aff &= mask;
|
||||
|
||||
set->__bits[wordindex] &= ~(mask << offset);
|
||||
set->__bits[wordindex] |= aff << offset;
|
||||
|
||||
if ((bitindex + groupsize - 1) / wordsize != wordindex)
|
||||
{
|
||||
offset = wordsize - offset;
|
||||
set->__bits[wordindex + 1] &= ~(mask >> offset);
|
||||
set->__bits[wordindex + 1] |= aff >> offset;
|
||||
}
|
||||
|
||||
return aff;
|
||||
}
|
||||
|
||||
/* figure out which processor group the set bits indicate; can only be one */
|
||||
static int
|
||||
whichgroup (size_t sizeof_set, const cpu_set_t *set)
|
||||
{
|
||||
int res = -1;
|
||||
int maxgroup = min (__get_group_count (),
|
||||
(NBBY * sizeof_set) / __get_cpus_per_group ());
|
||||
|
||||
for (int i = 0; i < maxgroup; ++i)
|
||||
if (getgroup (sizeof_set, set, i))
|
||||
{
|
||||
if (res >= 0)
|
||||
return -1; // error return if more than one group indicated
|
||||
else
|
||||
res = i; // remember first group found
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
sched_get_thread_affinity (HANDLE thread, size_t sizeof_set, cpu_set_t *set)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (thread)
|
||||
{
|
||||
memset (set, 0, sizeof_set);
|
||||
if (wincap.has_processor_groups () && __get_group_count () > 1)
|
||||
{
|
||||
GROUP_AFFINITY ga;
|
||||
|
||||
if (!GetThreadGroupAffinity (thread, &ga))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
setgroup (sizeof_set, set, ga.Group, ga.Mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
THREAD_BASIC_INFORMATION tbi;
|
||||
|
||||
status = NtQueryInformationThread (thread, ThreadBasicInformation,
|
||||
&tbi, sizeof (tbi), NULL);
|
||||
if (NT_SUCCESS (status))
|
||||
setgroup (sizeof_set, set, 0, tbi.AffinityMask);
|
||||
else
|
||||
status = geterrno_from_nt_status (status);
|
||||
}
|
||||
}
|
||||
else
|
||||
status = ESRCH;
|
||||
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
sched_getaffinity (pid_t pid, size_t sizeof_set, cpu_set_t *set)
|
||||
{
|
||||
HANDLE process = 0;
|
||||
int status = 0;
|
||||
|
||||
pinfo p (pid ? pid : getpid ());
|
||||
if (p)
|
||||
{
|
||||
process = pid && pid != myself->pid ?
|
||||
OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
||||
p->dwProcessId) : GetCurrentProcess ();
|
||||
KAFFINITY procmask;
|
||||
KAFFINITY sysmask;
|
||||
|
||||
if (!GetProcessAffinityMask (process, &procmask, &sysmask))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
memset (set, 0, sizeof_set);
|
||||
if (wincap.has_processor_groups () && __get_group_count () > 1)
|
||||
{
|
||||
USHORT groupcount = __CPU_GROUPMAX;
|
||||
USHORT grouparray[__CPU_GROUPMAX];
|
||||
|
||||
if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
|
||||
KAFFINITY miscmask = groupmask (__get_cpus_per_group ());
|
||||
for (int i = 0; i < groupcount; i++)
|
||||
setgroup (sizeof_set, set, grouparray[i], miscmask);
|
||||
}
|
||||
else
|
||||
setgroup (sizeof_set, set, 0, procmask);
|
||||
}
|
||||
else
|
||||
status = ESRCH;
|
||||
|
||||
done:
|
||||
if (process && process != GetCurrentProcess ())
|
||||
CloseHandle (process);
|
||||
|
||||
if (status)
|
||||
{
|
||||
set_errno (status);
|
||||
status = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Emulate documented Linux kernel behavior on successful return */
|
||||
status = wincap.cpu_count ();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
sched_set_thread_affinity (HANDLE thread, size_t sizeof_set, const cpu_set_t *set)
|
||||
{
|
||||
int group = whichgroup (sizeof_set, set);
|
||||
int status = 0;
|
||||
|
||||
if (thread)
|
||||
{
|
||||
if (wincap.has_processor_groups () && __get_group_count () > 1)
|
||||
{
|
||||
GROUP_AFFINITY ga;
|
||||
|
||||
if (group < 0)
|
||||
{
|
||||
status = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
memset (&ga, 0, sizeof (ga));
|
||||
ga.Mask = getgroup (sizeof_set, set, group);
|
||||
ga.Group = group;
|
||||
if (!SetThreadGroupAffinity (thread, &ga, NULL))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (group != 0)
|
||||
{
|
||||
status = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (!SetThreadAffinityMask (thread, getgroup (sizeof_set, set, 0)))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
status = ESRCH;
|
||||
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
sched_setaffinity (pid_t pid, size_t sizeof_set, const cpu_set_t *set)
|
||||
{
|
||||
int group = whichgroup (sizeof_set, set);
|
||||
HANDLE process = 0;
|
||||
int status = 0;
|
||||
|
||||
pinfo p (pid ? pid : getpid ());
|
||||
if (p)
|
||||
{
|
||||
process = pid && pid != myself->pid ?
|
||||
OpenProcess (PROCESS_SET_INFORMATION, FALSE,
|
||||
p->dwProcessId) : GetCurrentProcess ();
|
||||
if (wincap.has_processor_groups () && __get_group_count () > 1)
|
||||
{
|
||||
USHORT groupcount = __CPU_GROUPMAX;
|
||||
USHORT grouparray[__CPU_GROUPMAX];
|
||||
|
||||
if (!GetProcessGroupAffinity (process, &groupcount, grouparray))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
if (group < 0)
|
||||
{
|
||||
status = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (groupcount == 1 && grouparray[0] == group)
|
||||
{
|
||||
if (!SetProcessAffinityMask (process, getgroup (sizeof_set, set, group)))
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* If we get here, the user is trying to add the process to another
|
||||
group or move it from current group to another group. These ops
|
||||
are not allowed by Windows. One has to move one or more of the
|
||||
process' threads to the new group(s) one by one. Here, we bail.
|
||||
*/
|
||||
status = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (group != 0)
|
||||
{
|
||||
status = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (!SetProcessAffinityMask (process, getgroup (sizeof_set, set, 0)))
|
||||
{
|
||||
status = geterrno_from_win_error (GetLastError (), EPERM);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
status = ESRCH;
|
||||
|
||||
done:
|
||||
if (process && process != GetCurrentProcess ())
|
||||
CloseHandle (process);
|
||||
|
||||
if (status)
|
||||
{
|
||||
set_errno (status);
|
||||
status = -1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
} /* extern C */
|
||||
|
@ -23,6 +23,7 @@ details. */
|
||||
#include "winsup.h"
|
||||
#include "miscfuncs.h"
|
||||
#include "path.h"
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
#include "sigproc.h"
|
||||
#include "fhandler.h"
|
||||
@ -2606,6 +2607,24 @@ pthread_timedjoin_np (pthread_t thread, void **return_val,
|
||||
return pthread::join (&thread, (void **) return_val, &timeout);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
pthread_getaffinity_np (pthread_t thread, size_t sizeof_set, cpu_set_t *set)
|
||||
{
|
||||
if (!pthread::is_good_object (&thread))
|
||||
return ESRCH;
|
||||
|
||||
return sched_get_thread_affinity (thread->win32_obj_id, sizeof_set, set);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
pthread_setaffinity_np (pthread_t thread, size_t sizeof_set, const cpu_set_t *set)
|
||||
{
|
||||
if (!pthread::is_good_object (&thread))
|
||||
return ESRCH;
|
||||
|
||||
return sched_set_thread_affinity (thread->win32_obj_id, sizeof_set, set);
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
pthread_getattr_np (pthread_t thread, pthread_attr_t *attr)
|
||||
{
|
||||
|
@ -29,6 +29,12 @@ If a SA_SIGINFO signal handler changes the ucontext_t pointed to by the
|
||||
third parameter, follow it after returning from the handler.
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
Support for getting and setting process and thread affinities. New APIs:
|
||||
sched_getaffinity, sched_setaffinity, pthread_getaffinity_np,
|
||||
pthread_setaffinity_np.
|
||||
</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</sect2>
|
||||
|
@ -1359,8 +1359,10 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
|
||||
pow10f
|
||||
pow10l
|
||||
ppoll
|
||||
pthread_getaffinity_np
|
||||
pthread_getattr_np
|
||||
pthread_getname_np
|
||||
pthread_setaffinity_np
|
||||
pthread_setname_np
|
||||
pthread_sigqueue
|
||||
pthread_timedjoin_np
|
||||
@ -1374,7 +1376,9 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
|
||||
rawmemchr
|
||||
removexattr
|
||||
scandirat
|
||||
sched_getaffinity
|
||||
sched_getcpu
|
||||
sched_setaffinity
|
||||
secure_getenv
|
||||
setxattr
|
||||
signalfd
|
||||
|
Loading…
Reference in New Issue
Block a user