diff --git a/images/virtualization-dra/internal/plugin/driver.go b/images/virtualization-dra/internal/plugin/driver.go index 0179b10180..ffff51afe0 100644 --- a/images/virtualization-dra/internal/plugin/driver.go +++ b/images/virtualization-dra/internal/plugin/driver.go @@ -205,6 +205,8 @@ func (d *Driver) UnprepareResourceClaims(ctx context.Context, claims []kubeletpl func (d *Driver) unprepareResourceClaim(ctx context.Context, claim kubeletplugin.NamespacedObject) error { if err := d.allocator.Unprepare(ctx, claim.UID); err != nil { + d.log.Error("error unpreparing devices for claim", slog.Any("error", err), slog.String("uid", string(claim.UID))) + return fmt.Errorf("error unpreparing devices for claim %v: %w", claim.UID, err) } diff --git a/images/virtualization-dra/internal/usb-gateway/attach_record.go b/images/virtualization-dra/internal/usb-gateway/attach_record.go index fd7ec836d0..07b4900079 100644 --- a/images/virtualization-dra/internal/usb-gateway/attach_record.go +++ b/images/virtualization-dra/internal/usb-gateway/attach_record.go @@ -119,17 +119,29 @@ func (r *attachRecordManager) Refresh() error { } // keep only real entries - var newEntries []AttachEntry + newEntries := make([]AttachEntry, 0, len(record.Entries)) for _, e := range record.Entries { if _, ok := ports[e.Rhport]; ok { newEntries = append(newEntries, e) } } - record.Entries = newEntries + newRecord := attachRecord{Entries: newEntries} + if slices.Equal(record.Entries, newRecord.Entries) { + r.record = newRecord + return nil + } - r.record = record + b, err = json.Marshal(newRecord) + if err != nil { + return err + } + + if err = os.WriteFile(r.recordFile, b, 0o600); err != nil { + return err + } + r.record = newRecord return nil } diff --git a/images/virtualization-dra/internal/usb-gateway/usbgateway.go b/images/virtualization-dra/internal/usb-gateway/usbgateway.go index 2bb329f5ed..f85f6706f0 100644 --- a/images/virtualization-dra/internal/usb-gateway/usbgateway.go +++ b/images/virtualization-dra/internal/usb-gateway/usbgateway.go @@ -101,7 +101,9 @@ func (c *USBGatewayController) Detach(deviceName string) error { } entry := c.findEntry(deviceName) - if entry != nil { + if entry == nil { + log.Info("Device is already detached") + } else { log.Info("Detaching USB device") err = c.usbIP.Detach(entry.Rhport) if err != nil { @@ -115,6 +117,10 @@ func (c *USBGatewayController) Detach(deviceName string) error { return fmt.Errorf("failed to unexport device %s: %w", deviceName, err) } + if err := c.attachRecordManager.Refresh(); err != nil { + return fmt.Errorf("failed to Refresh attach record after detach for device %s: %w", deviceName, err) + } + return nil } diff --git a/images/virtualization-dra/internal/usb/store.go b/images/virtualization-dra/internal/usb/store.go index 58eaaf6fb2..bf33e4d395 100644 --- a/images/virtualization-dra/internal/usb/store.go +++ b/images/virtualization-dra/internal/usb/store.go @@ -436,29 +436,34 @@ func (s *AllocationStore) Unprepare(_ context.Context, claimUID types.UID) error return fmt.Errorf("unprepare called before synchronize NRI Hook") } - usbGatewayEnabled := featuregates.Default().USBGatewayEnabled() - - allocatedDevices := s.resourceClaimAllocations[claimUID] - s.log.Info("Unpreparing devices", slog.Any("devices", allocatedDevices), slog.String("claimUID", string(claimUID))) - - for _, device := range allocatedDevices { - if usbGatewayEnabled { - count := s.usbipAllocatedDevicesCount[device] - s.log.Info("Device attached by USBGateway", slog.String("device", device), slog.Int("count", count)) - if count <= 1 { - s.log.Info("Detaching device because has only one consumer", slog.String("device", device)) - if err := s.usbGateway.Detach(device); err != nil { - return fmt.Errorf("failed to detach device %s: %w", device, err) + allocatedDevices, exists := s.resourceClaimAllocations[claimUID] + if !exists || len(allocatedDevices) == 0 { + s.log.Info("Claim has no tracked allocations, skipping device cleanup", slog.String("claimUID", string(claimUID))) + } else { + usbGatewayEnabled := featuregates.Default().USBGatewayEnabled() + + s.log.Info("Unpreparing devices", slog.Any("devices", allocatedDevices), slog.String("claimUID", string(claimUID))) + + for _, device := range allocatedDevices { + if usbGatewayEnabled { + count, hasCount := s.usbipAllocatedDevicesCount[device] + s.log.Info("Device attached by USBGateway", slog.String("device", device), slog.Int("count", count)) + switch { + case !hasCount || count <= 1: + s.log.Info("Device has no tracked consumers, attempting detach cleanup", slog.String("device", device), slog.Int("count", count)) + if err := s.usbGateway.Detach(device); err != nil { + return fmt.Errorf("failed to detach device %s: %w", device, err) + } + delete(s.usbipAllocatedDevicesCount, device) + default: + s.log.Info("Decrementing device consumer count", slog.String("device", device), slog.Int("newCount", count-1)) + s.usbipAllocatedDevicesCount[device]-- } - delete(s.usbipAllocatedDevicesCount, device) - } else { - s.log.Info("Decrementing device consumer count", slog.String("device", device), slog.Int("newCount", count-1)) - s.usbipAllocatedDevicesCount[device]-- } - } - s.log.Info("Deleting device from allocated devices", slog.String("device", device)) - s.allocatedDevices.Delete(device) + s.log.Info("Deleting device from allocated devices", slog.String("device", device)) + s.allocatedDevices.Delete(device) + } } s.log.Info("Deleting CDI claim spec file", slog.String("claimUID", string(claimUID))) diff --git a/images/virtualization-dra/pkg/usbip/exporter.go b/images/virtualization-dra/pkg/usbip/exporter.go index 9956a0efff..f70d576cb0 100644 --- a/images/virtualization-dra/pkg/usbip/exporter.go +++ b/images/virtualization-dra/pkg/usbip/exporter.go @@ -98,11 +98,12 @@ func (e *usbExporter) Unexport(host, busID string, port int) error { return fmt.Errorf("unsupported USBIP version: %d", unExportReply.Version) } - if unExportReply.Status != protocol.OpStatusOk { + switch unExportReply.Status { + case protocol.OpStatusOk, protocol.OpStatusNoDev: + return nil + default: return fmt.Errorf("reply failed: %s", unExportReply.Status.String()) } - - return nil } func (e *usbExporter) usbipNetTCPConnect(host string, port int) (*net.TCPConn, error) {