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:
Augustin Cavalier
2026-08-13 18:57:26 +00:00
committed by waddlesplash
parent a08764fd75
commit c12c2d4488
2 changed files with 59 additions and 24 deletions
+14
View File
@@ -58,10 +58,12 @@ public:
inline bool Matches(const CPUSet& mask) const;
inline CPUSet And(const CPUSet& mask) const;
inline bool operator==(const CPUSet& other) const;
inline bool IsEmpty() const;
inline uint32 Bits(uint32 index) const { return fBitmap[index];}
private:
static const int kArrayBits = 32;
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
CPUSet::IsEmpty() const
{
+45 -24
View File
@@ -4026,28 +4026,43 @@ _user_get_cpu()
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;
if (!IS_USER_ADDRESS(userMask))
return B_BAD_ADDRESS;
CPUSet mask;
if (id == 0)
id = thread_get_current_thread_id();
// get the thread
Thread* thread = Thread::GetAndLock(id);
if (thread == NULL)
return B_BAD_THREAD_ID;
BReference<Thread> threadReference(thread, true);
ThreadLocker threadLocker(thread, true);
memcpy(&mask, &thread->cpumask, sizeof(mask));
{
if (id == 0)
id = thread_get_current_thread_id();
Thread* thread = Thread::GetAndLock(id);
if (thread == NULL)
return B_BAD_THREAD_ID;
BReference<Thread> threadReference(thread, true);
ThreadLocker threadLocker(thread, true);
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;
if (userMaskSize > maskSize) {
if (user_memset((uint8*)userMask + maskSize, 0, userMaskSize - maskSize) != B_OK)
return B_BAD_ADDRESS;
}
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))
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userMask))
return B_BAD_ADDRESS;
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;
CPUSet cpus;
cpus.SetAll();
for (int i = 0; i < smp_get_num_cpus(); i++)
cpus.ClearBit(i);
if (mask.Matches(cpus))
CPUSet andEnabled = mask.And(gCPUEnabled);
if (andEnabled.IsEmpty())
return B_BAD_VALUE;
// if setting to all enabled CPUs, just use no mask instead
if (andEnabled == gCPUEnabled)
mask.ClearAll();
if (id == 0)
id = thread_get_current_thread_id();
// get the thread
Thread* thread = Thread::GetAndLock(id);
if (thread == NULL)
return B_BAD_THREAD_ID;
BReference<Thread> threadReference(thread, true);
ThreadLocker threadLocker(thread, true);
memcpy(&thread->cpumask, &mask, sizeof(mask));
// check if running on masked cpu
if (!thread->cpumask.GetBit(thread->cpu->cpu_num))
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;
thread->cpumask = mask;
threadLocker.Unlock();
// check if running on masked CPU
if (!mask.IsEmpty() && !mask.GetBit(thread->cpu->cpu_num))
thread_yield();
return B_OK;