@@ -163,13 +163,62 @@ type setnsProcess struct {
163
163
initProcessPid int
164
164
}
165
165
166
+ // Starts setns process with specified initial CPU affinity.
167
+ func (p * setnsProcess ) startWithCPUAffinity () error {
168
+ aff := p .config .CPUAffinity
169
+ if aff == nil || aff .Initial == "" {
170
+ return p .cmd .Start ()
171
+ }
172
+ cpus , err := configs .ToCPUSet (aff .Initial )
173
+ if err != nil {
174
+ return fmt .Errorf ("invalid CPUAffinity.initial: %w" , err )
175
+ }
176
+
177
+ errCh := make (chan error )
178
+ defer close (errCh )
179
+
180
+ // Use a goroutine to dedicate an OS thread.
181
+ go func () {
182
+ runtime .LockOSThread ()
183
+ // Command inherits the CPU affinity.
184
+ if err := unix .SchedSetaffinity (unix .Gettid (), cpus ); err != nil {
185
+ runtime .UnlockOSThread ()
186
+ errCh <- fmt .Errorf ("setting initial CPU affinity: %w" , err )
187
+ return
188
+ }
189
+
190
+ errCh <- p .cmd .Start ()
191
+ // Deliberately omit runtime.UnlockOSThread here.
192
+ // https://pkg.go.dev/runtime#LockOSThread says:
193
+ // "If the calling goroutine exits without unlocking the
194
+ // thread, the thread will be terminated".
195
+ }()
196
+
197
+ return <- errCh
198
+ }
199
+
200
+ func (p * setnsProcess ) setFinalCPUAffinity () error {
201
+ aff := p .config .CPUAffinity
202
+ if aff == nil || aff .Final == "" {
203
+ return nil
204
+ }
205
+ cpus , err := configs .ToCPUSet (aff .Final )
206
+ if err != nil {
207
+ return fmt .Errorf ("invalid CPUAffinity.final: %w" , err )
208
+ }
209
+ if err := unix .SchedSetaffinity (p .pid (), cpus ); err != nil {
210
+ return fmt .Errorf ("setting final CPU affinity: %w" , err )
211
+ }
212
+ return nil
213
+ }
214
+
166
215
func (p * setnsProcess ) start () (retErr error ) {
167
216
defer p .comm .closeParent ()
168
217
169
- // get the "before" value of oom kill count
218
+ // Get the "before" value of oom kill count.
170
219
oom , _ := p .manager .OOMKillCount ()
171
- err := p .cmd . Start ()
172
- // close the child-side of the pipes (controlled by child)
220
+ err := p .startWithCPUAffinity ()
221
+ // Close the child-side of the pipes (controlled by child).
173
222
p .comm .closeChild ()
174
223
if err != nil {
175
224
return fmt .Errorf ("error starting setns process: %w" , err )
@@ -228,6 +277,9 @@ func (p *setnsProcess) start() (retErr error) {
228
277
}
229
278
}
230
279
}
280
+ if err := p .setFinalCPUAffinity (); err != nil {
281
+ return err
282
+ }
231
283
232
284
if err := utils .WriteJSON (p .comm .initSockParent , p .config ); err != nil {
233
285
return fmt .Errorf ("error writing config to pipe: %w" , err )
0 commit comments