ResumeDevice resumes IO for the given device
(ctx context.Context, deviceName string)
| 425 | |
| 426 | // ResumeDevice resumes IO for the given device |
| 427 | func (p *PoolDevice) ResumeDevice(ctx context.Context, deviceName string) error { |
| 428 | // Retry logic for resume operations to handle resource contention |
| 429 | var lastErr error |
| 430 | for attempt := range 3 { |
| 431 | if attempt > 0 { |
| 432 | // Add exponential backoff between retry attempts |
| 433 | time.Sleep(time.Duration(100*attempt) * time.Millisecond) |
| 434 | } |
| 435 | |
| 436 | err := p.transition(ctx, deviceName, Resuming, Resumed, func() error { |
| 437 | return dmsetup.ResumeDevice(deviceName) |
| 438 | }) |
| 439 | |
| 440 | if err == nil { |
| 441 | return nil |
| 442 | } |
| 443 | |
| 444 | lastErr = err |
| 445 | |
| 446 | // Check if this is a recoverable error |
| 447 | if strings.Contains(err.Error(), "invalid argument") || strings.Contains(err.Error(), "device busy") { |
| 448 | log.G(ctx).WithError(err).Warnf("resume device %q failed (attempt %d/3), retrying", deviceName, attempt+1) |
| 449 | continue |
| 450 | } |
| 451 | |
| 452 | // Non-recoverable error, fail immediately |
| 453 | return fmt.Errorf("failed to resume device %q: %w", deviceName, err) |
| 454 | } |
| 455 | |
| 456 | return fmt.Errorf("failed to resume device %q after 3 attempts: %w", deviceName, lastErr) |
| 457 | } |
| 458 | |
| 459 | // DeactivateDevice deactivates thin device |
| 460 | func (p *PoolDevice) DeactivateDevice(ctx context.Context, deviceName string, deferred, withForce bool) error { |
no test coverage detected