@@ -20,8 +20,6 @@ import (
2020
2121const socketWaitTimeout = 5 * time .Second
2222
23- var savedNetns netns.NsHandle //nolint:gochecknoglobals
24-
2523// Start launches the Cloud Hypervisor process for each VM ref.
2624// Returns the IDs that were successfully started.
2725func (ch * CloudHypervisor ) Start (ctx context.Context , refs []string ) ([]string , error ) {
@@ -133,11 +131,11 @@ func (ch *CloudHypervisor) launchProcess(ctx context.Context, vmID, socketPath s
133131 // If the VM has network, CH must be launched inside the VM's netns
134132 // so it can access the tap device. We setns before fork and restore after.
135133 if withNetwork {
136- if enterErr := enterNetns (ch .conf .CNINetnsPath (vmID )); enterErr != nil {
134+ restore , enterErr := enterNetns (ch .conf .CNINetnsPath (vmID ))
135+ if enterErr != nil {
137136 return 0 , fmt .Errorf ("enter netns: %w" , enterErr )
138137 }
139- // Deferred restore happens after cmd.Start() forks the child.
140- defer exitNetns ()
138+ defer restore ()
141139 }
142140
143141 if startErr := cmd .Start (); startErr != nil {
@@ -179,37 +177,34 @@ func waitForSocket(ctx context.Context, socketPath string, pid int) error {
179177
180178// enterNetns locks the OS thread, saves the current netns, and switches
181179// to the target netns. The forked child process inherits the new netns.
182- // Call exitNetns (via defer) after cmd.Start() to restore.
183- func enterNetns (nsPath string ) error {
180+ // Returns a restore function that must be deferred by the caller.
181+ // No global state — safe for concurrent use.
182+ func enterNetns (nsPath string ) (restore func (), err error ) {
184183 runtime .LockOSThread ()
185184
186185 orig , err := netns .Get ()
187186 if err != nil {
188187 runtime .UnlockOSThread ()
189- return fmt .Errorf ("get current netns: %w" , err )
188+ return nil , fmt .Errorf ("get current netns: %w" , err )
190189 }
191- savedNetns = orig
192190
193191 target , err := netns .GetFromPath (nsPath )
194192 if err != nil {
195193 _ = orig .Close ()
196194 runtime .UnlockOSThread ()
197- return fmt .Errorf ("open netns %s: %w" , nsPath , err )
195+ return nil , fmt .Errorf ("open netns %s: %w" , nsPath , err )
198196 }
199197 defer target .Close () //nolint:errcheck
200198
201199 if err := netns .Set (target ); err != nil {
202200 _ = orig .Close ()
203201 runtime .UnlockOSThread ()
204- return fmt .Errorf ("setns %s: %w" , nsPath , err )
202+ return nil , fmt .Errorf ("setns %s: %w" , nsPath , err )
205203 }
206- return nil
207- }
208204
209- // exitNetns restores the original netns and unlocks the OS thread.
210- func exitNetns () {
211- _ = netns .Set (savedNetns )
212- _ = savedNetns .Close ()
213- savedNetns = - 1
214- runtime .UnlockOSThread ()
205+ return func () {
206+ _ = netns .Set (orig )
207+ _ = orig .Close ()
208+ runtime .UnlockOSThread ()
209+ }, nil
215210}
0 commit comments