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:
Mark Geisert
2019-06-23 14:51:06 -07:00
committed by Corinna Vinschen
parent d54edfdf81
commit 641ecb0753
11 changed files with 389 additions and 5 deletions

View File

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