diff --git a/machine/cmdline.go b/machine/cmdline.go index e2e7b71..27d8094 100644 --- a/machine/cmdline.go +++ b/machine/cmdline.go @@ -134,8 +134,13 @@ func (c Cmdline) String() string { // Timing shortcuts a KVM guest can take: // no_timer_check skip the boot-time timer IRQ delivery probe, // which exists for hardware that misroutes it. + // tsc=reliable trust the TSC and skip the clocksource watchdog. + // The watchdog exists for silicon whose TSC drifts + // or stops; this machine's TSC is the host's, which + // KVM advertises as invariant, and the guest is + // told so through CPUID. // rcupdate.rcu_expedited=1 expedite RCU grace periods during boot. - parts = append(parts, "no_timer_check", "rcupdate.rcu_expedited=1") + parts = append(parts, "no_timer_check", "tsc=reliable", "rcupdate.rcu_expedited=1") parts = append(parts, c.Extra...) diff --git a/machine/machine.go b/machine/machine.go index 89fa3c7..4ae1b94 100644 --- a/machine/machine.go +++ b/machine/machine.go @@ -642,8 +642,6 @@ func (s Spec) Args() ([]string, error) { // every VM restored from one have their memory in a file whatever the spec being // asked was configured with. func (s Spec) Fingerprint() (string, error) { - shape := s.TemplateShape() - h := sha256.New() // Length-prefixed, so that no two different machines can produce the same @@ -675,6 +673,32 @@ func (s Spec) Fingerprint() (string, error) { } write(f.name, sum) } + ident, err := s.Identity() + if err != nil { + return "", err + } + write("identity", ident) + + return hex.EncodeToString(h.Sum(nil)), nil +} + +// Identity is everything the fingerprint hashes except the contents of those +// three files: the machine's shape, its device topology, and the host's own CPU +// when the guest is being shown it. +// +// It is separate because reading the three files is the expensive half — 76 MB +// of SHA-256, 29 ms on a machine measured — and a caller that memoises that half +// needs the other half whole to key the memo on. Under-keying it is the failure +// that has no symptom: a stale fingerprint is a template that matches a machine +// it does not describe, and a restore into it is undefined rather than an error. +// So there is no list here for a caller to keep in step; there is this. +func (s Spec) Identity() (string, error) { + shape := s.TemplateShape() + + var b strings.Builder + write := func(key, value string) { + _, _ = fmt.Fprintf(&b, "%s=%d:%s\n", key, len(value), value) + } write("machine", shape.Machine) write("cpu", shape.CPU) write("smp", shape.SMP) @@ -703,7 +727,7 @@ func (s Spec) Fingerprint() (string, error) { write("host-cpu", cpu) } - return hex.EncodeToString(h.Sum(nil)), nil + return b.String(), nil } // topology is the machine's device list — which models, at which slots — with