kernel/thread: Fix behavior of {get|set}_thread_affinity.
* Handle threads without masks: this means they can run on any CPU. * Return only enabled CPUs from get_thread_affinity. * Clear the user mask's extra bits. * Allow masks with more CPUs set than are active, same as Linux, but disallow masks with no intersection with the active CPUs. * If setting to all enabled CPUs, clear the mask, so that if more CPUs are enabled in the future, this can run on them too. * Add permissions checks. Fixes #20251. (cherry picked from commit f46b3483057b149179dda7abdcc2dde38994a8cc) Change-Id: Ib8649cb0a252fc8d9cfca691c2e03748561db741 Reviewed-on: https://review.haiku-os.org/c/haiku/+/11529 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
a08764fd75
commit
c12c2d4488
@@ -58,10 +58,12 @@ public:
|
|||||||
|
|
||||||
inline bool Matches(const CPUSet& mask) const;
|
inline bool Matches(const CPUSet& mask) const;
|
||||||
inline CPUSet And(const CPUSet& mask) const;
|
inline CPUSet And(const CPUSet& mask) const;
|
||||||
|
inline bool operator==(const CPUSet& other) const;
|
||||||
|
|
||||||
inline bool IsEmpty() const;
|
inline bool IsEmpty() const;
|
||||||
|
|
||||||
inline uint32 Bits(uint32 index) const { return fBitmap[index];}
|
inline uint32 Bits(uint32 index) const { return fBitmap[index];}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kArrayBits = 32;
|
static const int kArrayBits = 32;
|
||||||
static const int kArraySize = ROUNDUP(SMP_MAX_CPUS, kArrayBits) / kArrayBits;
|
static const int kArraySize = ROUNDUP(SMP_MAX_CPUS, kArrayBits) / kArrayBits;
|
||||||
@@ -199,6 +201,18 @@ CPUSet::Matches(const CPUSet& mask) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
CPUSet::operator==(const CPUSet& other) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kArraySize; i++) {
|
||||||
|
if (fBitmap[i] != other.fBitmap[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
CPUSet::IsEmpty() const
|
CPUSet::IsEmpty() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4026,28 +4026,43 @@ _user_get_cpu()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
_user_get_thread_affinity(thread_id id, void* userMask, size_t size)
|
_user_get_thread_affinity(thread_id id, void* userMask, size_t userMaskSize)
|
||||||
{
|
{
|
||||||
if (userMask == NULL || id < B_OK)
|
const size_t maskSize = sizeof(CPUSet);
|
||||||
|
if (userMask == NULL || id < 0 || userMaskSize < maskSize)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
if (!IS_USER_ADDRESS(userMask))
|
if (!IS_USER_ADDRESS(userMask))
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
CPUSet mask;
|
CPUSet mask;
|
||||||
|
|
||||||
if (id == 0)
|
{
|
||||||
id = thread_get_current_thread_id();
|
if (id == 0)
|
||||||
// get the thread
|
id = thread_get_current_thread_id();
|
||||||
Thread* thread = Thread::GetAndLock(id);
|
Thread* thread = Thread::GetAndLock(id);
|
||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return B_BAD_THREAD_ID;
|
return B_BAD_THREAD_ID;
|
||||||
BReference<Thread> threadReference(thread, true);
|
BReference<Thread> threadReference(thread, true);
|
||||||
ThreadLocker threadLocker(thread, true);
|
ThreadLocker threadLocker(thread, true);
|
||||||
memcpy(&mask, &thread->cpumask, sizeof(mask));
|
|
||||||
|
|
||||||
if (user_memcpy(userMask, &mask, min_c(sizeof(mask), size)) < B_OK)
|
if (thread->team == team_get_kernel_team())
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
if (thread->team != thread_get_current_thread()->team && geteuid() != 0)
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
|
mask = thread->cpumask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask.IsEmpty())
|
||||||
|
mask.SetAll();
|
||||||
|
mask = mask.And(gCPUEnabled);
|
||||||
|
|
||||||
|
if (user_memcpy(userMask, &mask, maskSize) < B_OK)
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
if (userMaskSize > maskSize) {
|
||||||
|
if (user_memset((uint8*)userMask + maskSize, 0, userMaskSize - maskSize) != B_OK)
|
||||||
|
return B_BAD_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -4057,34 +4072,40 @@ _user_set_thread_affinity(thread_id id, const void* userMask, size_t size)
|
|||||||
{
|
{
|
||||||
if (userMask == NULL || id < B_OK || size < sizeof(CPUSet))
|
if (userMask == NULL || id < B_OK || size < sizeof(CPUSet))
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
if (!IS_USER_ADDRESS(userMask))
|
if (!IS_USER_ADDRESS(userMask))
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
CPUSet mask;
|
CPUSet mask;
|
||||||
if (user_memcpy(&mask, userMask, min_c(sizeof(CPUSet), size)) < B_OK)
|
if (user_memcpy(&mask, userMask, sizeof(CPUSet)) < B_OK)
|
||||||
return B_BAD_ADDRESS;
|
return B_BAD_ADDRESS;
|
||||||
|
|
||||||
CPUSet cpus;
|
CPUSet andEnabled = mask.And(gCPUEnabled);
|
||||||
cpus.SetAll();
|
if (andEnabled.IsEmpty())
|
||||||
for (int i = 0; i < smp_get_num_cpus(); i++)
|
|
||||||
cpus.ClearBit(i);
|
|
||||||
if (mask.Matches(cpus))
|
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// if setting to all enabled CPUs, just use no mask instead
|
||||||
|
if (andEnabled == gCPUEnabled)
|
||||||
|
mask.ClearAll();
|
||||||
|
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
id = thread_get_current_thread_id();
|
id = thread_get_current_thread_id();
|
||||||
|
|
||||||
// get the thread
|
|
||||||
Thread* thread = Thread::GetAndLock(id);
|
Thread* thread = Thread::GetAndLock(id);
|
||||||
if (thread == NULL)
|
if (thread == NULL)
|
||||||
return B_BAD_THREAD_ID;
|
return B_BAD_THREAD_ID;
|
||||||
BReference<Thread> threadReference(thread, true);
|
BReference<Thread> threadReference(thread, true);
|
||||||
ThreadLocker threadLocker(thread, true);
|
ThreadLocker threadLocker(thread, true);
|
||||||
memcpy(&thread->cpumask, &mask, sizeof(mask));
|
|
||||||
|
|
||||||
// check if running on masked cpu
|
if (thread->team == team_get_kernel_team())
|
||||||
if (!thread->cpumask.GetBit(thread->cpu->cpu_num))
|
return B_NOT_ALLOWED;
|
||||||
|
if (thread->team != thread_get_current_thread()->team && geteuid() != 0)
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
|
thread->cpumask = mask;
|
||||||
|
threadLocker.Unlock();
|
||||||
|
|
||||||
|
// check if running on masked CPU
|
||||||
|
if (!mask.IsEmpty() && !mask.GetBit(thread->cpu->cpu_num))
|
||||||
thread_yield();
|
thread_yield();
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|||||||
Reference in New Issue
Block a user