m/n/c/devmgr: init
Add a minimal device manager based on kobject/uevents. Currently this
only loads kernel modules. Further functionality will be added in
subsequent CLs.
Change-Id: I444ecdaff3f8ddb9ec169b094ba03e169dd70c4e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1790
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/go.mod b/go.mod
index 19e6f52..0fd7bb9 100644
--- a/go.mod
+++ b/go.mod
@@ -294,6 +294,7 @@
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
+ github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d
github.com/mdlayher/packet v0.0.0-20220221164757-67998ac0ff93 // indirect
github.com/mdlayher/socket v0.2.1 // indirect
github.com/miekg/dns v1.1.48 // indirect
diff --git a/go.sum b/go.sum
index 4af2081..bbec387 100644
--- a/go.sum
+++ b/go.sum
@@ -1542,6 +1542,8 @@
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
+github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d h1:JmrZTpS0GAyMV4ZQVVH/AS0Y6r2PbnYNSRUuRX+HOLA=
+github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d/go.mod h1:+SexPO1ZvdbbWUdUnyXEWv3+4NwHZjKhxOmQqHY4Pqc=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v0.0.0-20190516121005-0087c778e469/go.mod h1:gOrA34zDL0K3RsACQe54bDYLF/CeFspQ9m5DOycycQ8=
github.com/mdlayher/netlink v0.0.0-20190828143259-340058475d09/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
diff --git a/metropolis/node/core/devmgr/BUILD.bazel b/metropolis/node/core/devmgr/BUILD.bazel
new file mode 100644
index 0000000..15b8065
--- /dev/null
+++ b/metropolis/node/core/devmgr/BUILD.bazel
@@ -0,0 +1,13 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+ name = "devmgr",
+ srcs = ["devmgr.go"],
+ importpath = "source.monogon.dev/metropolis/node/core/devmgr",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//metropolis/pkg/kmod",
+ "//metropolis/pkg/supervisor",
+ "@com_github_mdlayher_kobject//:kobject",
+ ],
+)
diff --git a/metropolis/node/core/devmgr/devmgr.go b/metropolis/node/core/devmgr/devmgr.go
new file mode 100644
index 0000000..fc626b0
--- /dev/null
+++ b/metropolis/node/core/devmgr/devmgr.go
@@ -0,0 +1,78 @@
+// Package devmgr is the userspace pendant to the kernel device management
+// system. It talks to the kernel and the performs any further userspace actions
+// related to device events. It corresponds very roughly to systemd-udevd on
+// more conventional Linux distros. It currently only handles dynamic module
+// loading, but will be extended to provide better device handling in other
+// parts of the system.
+package devmgr
+
+import (
+ "context"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+
+ "github.com/mdlayher/kobject"
+
+ "source.monogon.dev/metropolis/pkg/kmod"
+ "source.monogon.dev/metropolis/pkg/supervisor"
+)
+
+type Service struct{}
+
+func New() *Service {
+ return &Service{}
+}
+
+func (s *Service) Run(ctx context.Context) error {
+ c, err := kobject.New()
+ if err != nil {
+ return fmt.Errorf("unable to create kobject uevent socket: %w", err)
+ }
+ defer c.Close()
+
+ l := supervisor.Logger(ctx)
+
+ modMgr, err := kmod.NewManagerFromPath("/lib/modules")
+ if err != nil {
+ return fmt.Errorf("error creating module manager: %w", err)
+ }
+
+ // Start goroutine which instructs the kernel to generate "synthetic"
+ // uevents for all preexisting devices. This allows the kobject netlink
+ // listener below to "catch up" on devices added before it was created.
+ // This functionality is minimally documented in the Linux kernel, the
+ // closest we have is
+ // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent which
+ // contains documentation on how to trigger synthetic events.
+ go func() {
+ err = filepath.WalkDir("/sys/devices", func(path string, d fs.DirEntry, err error) error {
+ if !d.IsDir() && d.Name() == "uevent" {
+ if err := os.WriteFile(path, []byte("add"), 0); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ l.Errorf("failed to load initial device database: %v", err)
+ } else {
+ l.Info("Initial device loading done")
+ }
+ }()
+
+ for {
+ e, err := c.Receive()
+ if err != nil {
+ return fmt.Errorf("error receiving kobject uevent: %w", err)
+ }
+ if e.Action == kobject.Add {
+ if e.Values["MODALIAS"] != "" {
+ if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil {
+ l.Errorf("Error loading kernel modules: %w", err)
+ }
+ }
+ }
+ }
+}
diff --git a/third_party/go/repositories.bzl b/third_party/go/repositories.bzl
index 5540762..b16e523 100644
--- a/third_party/go/repositories.bzl
+++ b/third_party/go/repositories.bzl
@@ -3520,6 +3520,13 @@
version = "v1.2.0",
)
go_repository(
+ name = "com_github_mdlayher_kobject",
+ importpath = "github.com/mdlayher/kobject",
+ sum = "h1:JmrZTpS0GAyMV4ZQVVH/AS0Y6r2PbnYNSRUuRX+HOLA=",
+ version = "v0.0.0-20200520190114-19ca17470d7d",
+ )
+
+ go_repository(
name = "com_github_mdlayher_netlink",
importpath = "github.com/mdlayher/netlink",
sum = "h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=",