summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/device/device.go
diff options
context:
space:
mode:
authorRahat Mahmood <rahat@google.com>2019-04-01 15:38:08 -0700
committerShentubot <shentubot@google.com>2019-04-01 15:39:16 -0700
commit7cff746ef2bbe5351e5985bebc88efc9e0881c78 (patch)
treec33298a64853f1f33e4b7dbc6fdb5c5f7f6bb612 /pkg/sentry/device/device.go
parent1a02ba3e6e1ee01e878efcff6a20ab7ff3083303 (diff)
Save/restore simple devices.
We weren't saving simple devices' last allocated inode numbers, which caused inode number reuse across S/R. PiperOrigin-RevId: 241414245 Change-Id: I964289978841ef0a57d2fa48daf8eab7633c1284
Diffstat (limited to 'pkg/sentry/device/device.go')
-rw-r--r--pkg/sentry/device/device.go111
1 files changed, 88 insertions, 23 deletions
diff --git a/pkg/sentry/device/device.go b/pkg/sentry/device/device.go
index 27e4eb258..ae4fa1d93 100644
--- a/pkg/sentry/device/device.go
+++ b/pkg/sentry/device/device.go
@@ -14,11 +14,6 @@
// Package device defines reserved virtual kernel devices and structures
// for managing them.
-//
-// Saving and restoring devices is not necessary if the devices are initialized
-// as package global variables. Package initialization happens in a single goroutine
-// and in a deterministic order, so minor device numbers will be assigned in the
-// same order as packages are loaded.
package device
import (
@@ -30,7 +25,83 @@ import (
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
)
+// Registry tracks all simple devices and related state on the system for
+// save/restore.
+//
+// The set of devices across save/restore must remain consistent. That is, no
+// devices may be created or removed on restore relative to the saved
+// system. Practically, this means do not create new devices specifically as
+// part of restore.
+//
+// +stateify savable
+type Registry struct {
+ // lastAnonDeviceMinor is the last minor device number used for an anonymous
+ // device. Must be accessed atomically.
+ lastAnonDeviceMinor uint64
+
+ // mu protects the fields below.
+ mu sync.Mutex `state:"nosave"`
+
+ devices map[ID]*Device
+}
+
+// SimpleDevices is the system-wide simple device registry. This is
+// saved/restored by kernel.Kernel, but defined here to allow access without
+// depending on the kernel package. See kernel.Kernel.deviceRegistry.
+var SimpleDevices = newRegistry()
+
+func newRegistry() *Registry {
+ return &Registry{
+ devices: make(map[ID]*Device),
+ }
+}
+
+// newAnonID assigns a major and minor number to an anonymous device ID.
+func (r *Registry) newAnonID() ID {
+ return ID{
+ // Anon devices always have a major number of 0.
+ Major: 0,
+ // Use the next minor number.
+ Minor: atomic.AddUint64(&r.lastAnonDeviceMinor, 1),
+ }
+}
+
+// newAnonDevice allocates a new anonymous device with a unique minor device
+// number, and registers it with r.
+func (r *Registry) newAnonDevice() *Device {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ d := &Device{
+ ID: r.newAnonID(),
+ }
+ r.devices[d.ID] = d
+ return d
+}
+
+// LoadFrom initializes the internal state of all devices in r from other. The
+// set of devices in both registries must match. Devices may not be created or
+// destroyed across save/restore.
+func (r *Registry) LoadFrom(other *Registry) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ other.mu.Lock()
+ defer other.mu.Unlock()
+ if len(r.devices) != len(other.devices) {
+ panic(fmt.Sprintf("Devices were added or removed when restoring the registry:\nnew:\n%+v\nold:\n%+v", r.devices, other.devices))
+ }
+ for id, otherD := range other.devices {
+ ourD, ok := r.devices[id]
+ if !ok {
+ panic(fmt.Sprintf("Device %+v could not be restored as it wasn't defined in the new registry", otherD))
+ }
+ ourD.loadFrom(otherD)
+ }
+ atomic.StoreUint64(&r.lastAnonDeviceMinor, atomic.LoadUint64(&other.lastAnonDeviceMinor))
+}
+
// ID identifies a device.
+//
+// +stateify savable
type ID struct {
Major uint64
Minor uint64
@@ -41,18 +112,12 @@ func (i *ID) DeviceID() uint64 {
return uint64(linux.MakeDeviceID(uint16(i.Major), uint32(i.Minor)))
}
-// nextAnonDeviceMinor is the next minor number for a new anonymous device.
-// Must be accessed atomically.
-var nextAnonDeviceMinor uint64
-
// NewAnonDevice creates a new anonymous device. Packages that require an anonymous
// device should initialize the device in a global variable in a file called device.go:
//
// var myDevice = device.NewAnonDevice()
func NewAnonDevice() *Device {
- return &Device{
- ID: newAnonID(),
- }
+ return SimpleDevices.newAnonDevice()
}
// NewAnonMultiDevice creates a new multi-keyed anonymous device. Packages that require
@@ -62,21 +127,13 @@ func NewAnonDevice() *Device {
// var myDevice = device.NewAnonMultiDevice()
func NewAnonMultiDevice() *MultiDevice {
return &MultiDevice{
- ID: newAnonID(),
- }
-}
-
-// newAnonID assigns a major and minor number to an anonymous device ID.
-func newAnonID() ID {
- return ID{
- // Anon devices always have a major number of 0.
- Major: 0,
- // Use the next minor number.
- Minor: atomic.AddUint64(&nextAnonDeviceMinor, 1),
+ ID: SimpleDevices.newAnonID(),
}
}
// Device is a simple virtual kernel device.
+//
+// +stateify savable
type Device struct {
ID
@@ -84,6 +141,14 @@ type Device struct {
last uint64
}
+// loadFrom initializes d from other. The IDs of both devices must match.
+func (d *Device) loadFrom(other *Device) {
+ if d.ID != other.ID {
+ panic(fmt.Sprintf("Attempting to initialize a device %+v from %+v, but device IDs don't match", d, other))
+ }
+ atomic.StoreUint64(&d.last, atomic.LoadUint64(&other.last))
+}
+
// NextIno generates a new inode number
func (d *Device) NextIno() uint64 {
return atomic.AddUint64(&d.last, 1)