SuspendDevice flushes the outstanding IO and blocks the further IO
(ctx context.Context, deviceName string)
| 392 | |
| 393 | // SuspendDevice flushes the outstanding IO and blocks the further IO |
| 394 | func (p *PoolDevice) SuspendDevice(ctx context.Context, deviceName string) error { |
| 395 | // Retry logic for suspend operations to handle resource contention |
| 396 | var lastErr error |
| 397 | for attempt := range 3 { |
| 398 | if attempt > 0 { |
| 399 | // Add exponential backoff between retry attempts |
| 400 | time.Sleep(time.Duration(100*attempt) * time.Millisecond) |
| 401 | } |
| 402 | |
| 403 | err := p.transition(ctx, deviceName, Suspending, Suspended, func() error { |
| 404 | return dmsetup.SuspendDevice(deviceName) |
| 405 | }) |
| 406 | |
| 407 | if err == nil { |
| 408 | return nil |
| 409 | } |
| 410 | |
| 411 | lastErr = err |
| 412 | |
| 413 | // Check if this is a recoverable error |
| 414 | if strings.Contains(err.Error(), "invalid argument") || strings.Contains(err.Error(), "device busy") { |
| 415 | log.G(ctx).WithError(err).Warnf("suspend device %q failed (attempt %d/3), retrying", deviceName, attempt+1) |
| 416 | continue |
| 417 | } |
| 418 | |
| 419 | // Non-recoverable error, fail immediately |
| 420 | return fmt.Errorf("failed to suspend device %q: %w", deviceName, err) |
| 421 | } |
| 422 | |
| 423 | return fmt.Errorf("failed to suspend device %q after 3 attempts: %w", deviceName, lastErr) |
| 424 | } |
| 425 | |
| 426 | // ResumeDevice resumes IO for the given device |
| 427 | func (p *PoolDevice) ResumeDevice(ctx context.Context, deviceName string) error { |
no test coverage detected