/
cpu.go
241 lines (204 loc) · 5.74 KB
/
cpu.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package power
import (
"fmt"
"path/filepath"
"strings"
"sync"
)
// Cpu represents a compute unit/thread as seen by the OS
// it is either a physical core ot virtual thread if hyperthreading/SMT is enabled
type Cpu interface {
GetID() uint
SetPool(pool Pool) error
getPool() Pool
doSetPool(pool Pool) error
consolidate() error
consolidate_unsafe() error
// C-States stuff
SetCStates(cStates CStates) error
// used only to set initial pool when creating core instance
_setPoolProperty(pool Pool)
}
type cpuImpl struct {
id uint
mutex sync.Locker
pool Pool
core *cpuCore
// C-States properties
cStates *CStates
}
func newCpu(coreID uint, core *cpuCore) (Cpu, error) {
cpu := &cpuImpl{
id: coreID,
mutex: &sync.Mutex{},
core: core,
}
return cpu, nil
}
func (cpu *cpuImpl) consolidate() error {
cpu.mutex.Lock()
defer cpu.mutex.Unlock()
return cpu.consolidate_unsafe()
}
func (cpu *cpuImpl) consolidate_unsafe() error {
if err := cpu.updateFrequencies(); err != nil {
return err
}
if err := cpu.updateCStates(); err != nil {
return err
}
return nil
}
// SetPool moves current core to a specified target pool
// allowed movements are reservedPoolType <-> sharedPoolType and sharedPoolType <-> any exclusive pool
func (cpu *cpuImpl) SetPool(targetPool Pool) error {
/*
case 0: current and target pool are the same -> do nothing
case 1: target = reserved, current = reserved -> case 0
case 2: target = reserved, current = shared -> do it
case 3: target = reserved, current = exclusive -> error
case 4: target = shared, current = exclusive -> do it
case 5: target = shared, current = shared -> case 0
case 6: target = shared, current = reserved -> do it
case 7: target = exclusive, current = other exclusive -> error
case 8: target = exclusive, current = shared -> do it
case 9: target = exclusive, current = reserved -> error
*/
if targetPool == nil {
return fmt.Errorf("target pool cannot be nil")
}
log.Info("Set pool", "cpu", cpu.id, "source pool", cpu.pool.Name(), "target pool", targetPool.Name())
cpu.mutex.Lock()
defer cpu.mutex.Unlock()
if cpu.pool == targetPool { // case 0,1,5
return nil
}
reservedPool := cpu.pool.getHost().GetReservedPool()
sharedPool := cpu.pool.getHost().GetSharedPool()
if cpu.pool == reservedPool && targetPool.isExclusive() { // case 3
return fmt.Errorf("cannot move from reserved to exclusive pool")
}
if cpu.pool.isExclusive() && targetPool.isExclusive() { // case 7
return fmt.Errorf("cannot move exclusive to different exclusive pool")
}
if cpu.pool.isExclusive() && targetPool == reservedPool { // case 9
return fmt.Errorf("cannot move from exclusive to reserved")
}
// cases 2,4,5,6,8
if targetPool == sharedPool || cpu.pool == sharedPool {
return cpu.doSetPool(targetPool)
}
panic("we should never get here")
}
func (cpu *cpuImpl) doSetPool(pool Pool) error {
cpu.pool.poolMutex().Lock()
pool.poolMutex().Lock()
log.V(4).Info("acquired mutexes", "source", cpu.pool.Name(), "target", pool.Name(), "cpu")
origPool := cpu.pool
cpu.pool = pool
defer func() {
log.V(4).Info("releasing mutexes", "source", origPool.Name(), "target", pool.Name())
origPool.poolMutex().Unlock()
pool.poolMutex().Unlock()
}()
origPoolCpus := origPool.Cpus()
log.V(4).Info("removing cpu from pool", "pool", origPool.Name(), "coreID", cpu.id)
if err := origPoolCpus.remove(cpu); err != nil {
cpu.pool = origPool
return err
}
log.V(4).Info("starting consolidation of cpu", "coreID", cpu.id)
if err := cpu.consolidate_unsafe(); err != nil {
cpu.pool = origPool
origPoolCpus.add(cpu)
return err
}
newPoolCpus := cpu.pool.Cpus()
newPoolCpus.add(cpu)
return nil
}
func (cpu *cpuImpl) getPool() Pool {
return cpu.pool
}
func (cpu *cpuImpl) GetID() uint {
return cpu.id
}
func (cpu *cpuImpl) _setPoolProperty(pool Pool) {
cpu.pool = pool
}
// read property of specific CPU as an int, takes CPUid and path to specific file within cpu subdirectory in sysfs
func readCpuUintProperty(cpuID uint, file string) (uint, error) {
path := filepath.Join(basePath, fmt.Sprint("cpu", cpuID), file)
return readUintFromFile(path)
}
// reads content of a file and returns it as a string
func readCpuStringProperty(cpuID uint, file string) (string, error) {
path := filepath.Join(basePath, fmt.Sprint("cpu", cpuID), file)
value, err := readStringFromFile(path)
if err != nil {
return "", fmt.Errorf("failed to read cpuCore %d string property: %w", cpuID, err)
}
value = strings.TrimSuffix(value, "\n")
return value, nil
}
type CpuList []Cpu
func (cpus *CpuList) IndexOf(cpu Cpu) int {
for i, c := range *cpus {
if c == cpu {
return i
}
}
return -1
}
func (cpus *CpuList) Contains(cpu Cpu) bool {
if cpus.IndexOf(cpu) < 0 {
return false
} else {
return true
}
}
func (cpus *CpuList) add(cpu Cpu) {
*cpus = append(*cpus, cpu)
}
func (cpus *CpuList) remove(cpu Cpu) error {
index := cpus.IndexOf(cpu)
if index < 0 {
return fmt.Errorf("cpu %d is not in pool", cpu.GetID())
}
size := len(*cpus) - 1
(*cpus)[index] = (*cpus)[size]
*cpus = (*cpus)[:size]
return nil
}
func (cpus *CpuList) IDs() []uint {
ids := make([]uint, len(*cpus))
for i, cpu := range *cpus {
ids[i] = cpu.GetID()
}
return ids
}
func (cpus *CpuList) ByID(id uint) Cpu {
index := int(id)
// first we try index == cpuId
if len(*cpus) > index && (*cpus)[index].GetID() == id {
return (*cpus)[index]
}
// if that doesn't work we fall back to looping
for _, cpu := range *cpus {
if cpu.GetID() == id {
return cpu
}
}
return nil
}
func (cpus *CpuList) ManyByIDs(ids []uint) (CpuList, error) {
targets := make(CpuList, len(ids))
for i, id := range ids {
cpu := cpus.ByID(id)
if cpu == nil {
return nil, fmt.Errorf("cpu with id %d, not in list", id)
}
targets[i] = cpu
}
return targets, nil
}