Commit 12d9d5b2 authored by Tobias Klauser's avatar Tobias Klauser Committed by Tobias Klauser

unix: add SchedGetaffinity and SchedSetaffinity on Linux

SchedGetaffinity and SchedSetaffinity are used to get and set a thread's
CPU affinity mask.

See http://man7.org/linux/man-pages/man2/sched_setaffinity.2.html for
details.

Also add a manual implementation of CpuSet_t and the corresponding
accessor functions (mimicking the CPU_* macros from sched.h).

Fixes golang/go#11243

Change-Id: Ia5abc0053cd06810b3b09ab65c27434f5323c1ad
Reviewed-on: https://go-review.googlesource.com/85915
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent a3f2cbd5
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CPU affinity functions
package unix
import (
"math/bits"
"unsafe"
)
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
// CPUSet represents a CPU affinity mask.
type CPUSet [cpuSetSize]cpuMask
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(set)), uintptr(unsafe.Pointer(set)))
if e != 0 {
return errnoErr(e)
}
return nil
}
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
// If pid is 0 the calling thread is used.
func SchedGetaffinity(pid int, set *CPUSet) error {
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
}
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
// If pid is 0 the calling thread is used.
func SchedSetaffinity(pid int, set *CPUSet) error {
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
}
// Zero clears the set s, so that it contains no CPUs.
func (s *CPUSet) Zero() {
for i := range s {
s[i] = 0
}
}
func cpuBitsIndex(cpu int) int {
return cpu / _NCPUBITS
}
func cpuBitsMask(cpu int) cpuMask {
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
}
// Set adds cpu to the set s.
func (s *CPUSet) Set(cpu int) {
i := cpuBitsIndex(cpu)
if i < len(s) {
s[i] |= cpuBitsMask(cpu)
}
}
// Clear removes cpu from the set s.
func (s *CPUSet) Clear(cpu int) {
i := cpuBitsIndex(cpu)
if i < len(s) {
s[i] &^= cpuBitsMask(cpu)
}
}
// IsSet reports whether cpu is in the set s.
func (s *CPUSet) IsSet(cpu int) bool {
i := cpuBitsIndex(cpu)
if i < len(s) {
return s[i]&cpuBitsMask(cpu) != 0
}
return false
}
// Count returns the number of CPUs in the set s.
func (s *CPUSet) Count() int {
c := 0
for _, b := range s {
c += bits.OnesCount64(uint64(b))
}
return c
}
......@@ -24,6 +24,7 @@ package unix
#include <netinet/tcp.h>
#include <netpacket/packet.h>
#include <poll.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <sys/epoll.h>
......@@ -594,3 +595,12 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = C.CTRL_ATTR_MCAST_GRP_NAME
CTRL_ATTR_MCAST_GRP_ID = C.CTRL_ATTR_MCAST_GRP_ID
)
// CPU affinity
type cpuMask C.__cpu_mask
const (
_CPU_SETSIZE = C.__CPU_SETSIZE
_NCPUBITS = C.__NCPUBITS
)
......@@ -1455,11 +1455,9 @@ func Vmsplice(fd int, iovs []Iovec, flags int) (int, error) {
// RtSigtimedwait
// SchedGetPriorityMax
// SchedGetPriorityMin
// SchedGetaffinity
// SchedGetparam
// SchedGetscheduler
// SchedRrGetInterval
// SchedSetaffinity
// SchedSetparam
// SchedYield
// Security
......
......@@ -9,6 +9,7 @@ package unix_test
import (
"io/ioutil"
"os"
"runtime"
"testing"
"time"
......@@ -255,6 +256,58 @@ func TestFstatat(t *testing.T) {
}
}
func TestSchedSetaffinity(t *testing.T) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
var oldMask unix.CPUSet
err := unix.SchedGetaffinity(0, &oldMask)
if err != nil {
t.Fatalf("SchedGetaffinity: %v", err)
}
var newMask unix.CPUSet
newMask.Zero()
if newMask.Count() != 0 {
t.Errorf("CpuZero: didn't zero CPU set: %v", newMask)
}
cpu := 1
newMask.Set(cpu)
if newMask.Count() != 1 || !newMask.IsSet(cpu) {
t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask)
}
cpu = 5
newMask.Set(cpu)
if newMask.Count() != 2 || !newMask.IsSet(cpu) {
t.Errorf("CpuSet: didn't set CPU %d in set: %v", cpu, newMask)
}
newMask.Clear(cpu)
if newMask.Count() != 1 || newMask.IsSet(cpu) {
t.Errorf("CpuClr: didn't clear CPU %d in set: %v", cpu, newMask)
}
err = unix.SchedSetaffinity(0, &newMask)
if err != nil {
t.Fatalf("SchedSetaffinity: %v", err)
}
var gotMask unix.CPUSet
err = unix.SchedGetaffinity(0, &gotMask)
if err != nil {
t.Fatalf("SchedGetaffinity: %v", err)
}
if gotMask != newMask {
t.Errorf("SchedSetaffinity: returned affinity mask does not match set affinity mask")
}
// Restore old mask so it doesn't affect successive tests
err = unix.SchedSetaffinity(0, &oldMask)
if err != nil {
t.Fatalf("SchedSetaffinity: %v", err)
}
}
// utilities taken from os/os_test.go
func touch(t *testing.T, name string) {
......
......@@ -792,3 +792,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint32
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x20
)
......@@ -810,3 +810,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -781,3 +781,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint32
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x20
)
......@@ -789,3 +789,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -786,3 +786,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint32
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x20
)
......@@ -791,3 +791,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -791,3 +791,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -786,3 +786,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint32
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x20
)
......@@ -799,3 +799,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -799,3 +799,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
......@@ -816,3 +816,10 @@ const (
CTRL_ATTR_MCAST_GRP_NAME = 0x1
CTRL_ATTR_MCAST_GRP_ID = 0x2
)
type cpuMask uint64
const (
_CPU_SETSIZE = 0x400
_NCPUBITS = 0x40
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment