This repository has been archived by the owner on May 12, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
grpc: Handle hotplugged devices by updating spec info
When adding a host device to the container by using things like "docker run --device /dev/sda1:/dev/blk1", the runtime will not be able to anticipate the major/minor numbers for the device being hotplugged into the VM. This means the agent has to update the list of devices provided through the spec, by relying on the list of Storages. This commit allows the agent to analyze a list of mounts identified as devices by using the field Driver of the Storage structure. This way, it knows when it should try to find major/minor pair related to the devices listed in the spec. Then, it updates this list in the spec structure so that proper bind mounts between the VM and the container are performed. Fixes #132 Signed-off-by: Sebastien Boeuf <[email protected]>
- Loading branch information
Sebastien Boeuf
committed
Feb 9, 2018
1 parent
ca67bc9
commit 15e72eb
Showing
4 changed files
with
172 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// | ||
// Copyright (c) 2018 Intel Corporation | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/kata-containers/agent/pkg/uevent" | ||
pb "github.com/kata-containers/agent/protocols/grpc" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type deviceDriversHandler func(device pb.Storage, spec *pb.Spec) error | ||
|
||
var deviceDriversHandlerList = map[string]deviceDriversHandler{ | ||
"blk": blockDeviceHandler, | ||
} | ||
|
||
func blockDeviceHandler(device pb.Storage, spec *pb.Spec) error { | ||
if spec.Linux == nil || len(spec.Linux.Devices) == 0 { | ||
return fmt.Errorf("No devices found from the spec, cannot update") | ||
} | ||
|
||
// First need to make sure the expected device shows up properly, | ||
// and then we need to retrieve its device info (such as major and | ||
// minor numbers), useful to update the device provided | ||
// through the OCI specification. | ||
if err := waitForDevice(device.Source); err != nil { | ||
return err | ||
} | ||
|
||
stat := syscall.Stat_t{} | ||
if err := syscall.Stat(device.Source, &stat); err != nil { | ||
return err | ||
} | ||
|
||
major := int64(stat.Rdev / 256) | ||
minor := int64(stat.Rdev % 256) | ||
|
||
agentLog.Infof("Device: %s, Major: %d, Minor: %d", device.Source, major, minor) | ||
|
||
// Update the spec | ||
updated := false | ||
for idx, d := range spec.Linux.Devices { | ||
if d.Path == device.MountPoint { | ||
spec.Linux.Devices[idx].Major = major | ||
spec.Linux.Devices[idx].Minor = minor | ||
updated = true | ||
break | ||
} | ||
} | ||
|
||
if !updated { | ||
return fmt.Errorf("Should have found a matching device in the spec") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func waitForDevice(devicePath string) error { | ||
deviceName := strings.TrimPrefix(devicePath, devPrefix) | ||
|
||
if _, err := os.Stat(devicePath); err == nil { | ||
return nil | ||
} | ||
|
||
uEvHandler, err := uevent.NewHandler() | ||
if err != nil { | ||
return err | ||
} | ||
defer uEvHandler.Close() | ||
|
||
fieldLogger := agentLog.WithField("device", deviceName) | ||
|
||
// Check if the device already exists. | ||
if _, err := os.Stat(devicePath); err == nil { | ||
fieldLogger.Info("Device already hotplugged, quit listening") | ||
return nil | ||
} | ||
|
||
fieldLogger.Info("Started listening for uevents for device hotplug") | ||
|
||
// Channel to signal when desired uevent has been received. | ||
done := make(chan bool) | ||
|
||
go func() { | ||
// This loop will be either ended if the hotplugged device is | ||
// found by listening to the netlink socket, or it will end | ||
// after the function returns and the uevent handler is closed. | ||
for { | ||
uEv, err := uEvHandler.Read() | ||
if err != nil { | ||
fieldLogger.Error(err) | ||
continue | ||
} | ||
|
||
fieldLogger = fieldLogger.WithFields(logrus.Fields{ | ||
"uevent-action": uEv.Action, | ||
"uevent-devpath": uEv.DevPath, | ||
"uevent-subsystem": uEv.SubSystem, | ||
"uevent-seqnum": uEv.SeqNum, | ||
}) | ||
|
||
fieldLogger.Info("Got uevent") | ||
|
||
if uEv.Action == "add" && | ||
filepath.Base(uEv.DevPath) == deviceName { | ||
fieldLogger.Info("Hotplug event received") | ||
break | ||
} | ||
} | ||
|
||
close(done) | ||
}() | ||
|
||
select { | ||
case <-done: | ||
case <-time.After(time.Duration(timeoutHotplug) * time.Second): | ||
return fmt.Errorf("Timeout reached after %ds waiting for device %s", | ||
timeoutHotplug, deviceName) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters