diff --git a/client/client_linux.go b/client/client_linux.go index 54ac5fd..1853e8c 100644 --- a/client/client_linux.go +++ b/client/client_linux.go @@ -19,17 +19,24 @@ package client import ( "fmt" + "time" labi "github.com/google/go-sev-guest/client/linuxabi" "golang.org/x/sys/unix" ) -// defaultSevGuestDevicePath is the platform's usual device path to the SEV guest. -const defaultSevGuestDevicePath = "/dev/sev-guest" +const ( + // defaultSevGuestDevicePath is the platform's usual device path to the SEV guest. + defaultSevGuestDevicePath = "/dev/sev-guest" + throttleDuration = 2 * time.Second + burstMax = 2 +) // LinuxDevice implements the Device interface with Linux ioctls. type LinuxDevice struct { - fd int + fd int + lastCmd time.Time + burst int } // Open opens the SEV-SNP guest device from a given path @@ -71,11 +78,24 @@ func (d *LinuxDevice) Close() error { // Ioctl sends a command with its wrapped request and response values to the Linux device. func (d *LinuxDevice) Ioctl(command uintptr, req any) (uintptr, error) { + // TODO(Issue #40): Remove the workaround to the ENOTTY lockout when throttled + // in Linux 6.1 by throttling ourselves first. + if d.burst == 0 { + sinceLast := time.Since(d.lastCmd) + // Self-throttle for tests without guest OS throttle detection + if sinceLast < throttleDuration { + time.Sleep(throttleDuration - sinceLast) + } + } switch sreq := req.(type) { case *labi.SnpUserGuestRequest: abi := sreq.ABI() result, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(d.fd), command, uintptr(abi.Pointer())) abi.Finish(sreq) + d.burst = (d.burst + 1) % burstMax + if d.burst == 0 { + d.lastCmd = time.Now() + } // TODO(Issue #5): remove the work around for the kernel bug that writes // uninitialized memory back on non-EIO.