Skip to content

Commit 95f0a49

Browse files
committed
devmapper: rollback thin devices on error
Signed-off-by: Maksym Pavlenko <[email protected]>
1 parent adf5c64 commit 95f0a49

2 files changed

Lines changed: 111 additions & 36 deletions

File tree

snapshots/devmapper/pool_device.go

Lines changed: 108 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func NewPoolDevice(ctx context.Context, config *Config) (*PoolDevice, error) {
5858
if _, err := dmsetup.Info(poolPath); err != nil {
5959
return nil, errors.Wrapf(err, "failed to query pool %q", poolPath)
6060
}
61+
6162
return &PoolDevice{
6263
poolName: config.PoolName,
6364
metadata: poolMetaStore,
@@ -108,7 +109,7 @@ func (p *PoolDevice) transition(ctx context.Context, deviceName string, tryingSt
108109
// CreateThinDevice creates new devmapper thin-device with given name and size.
109110
// Device ID for thin-device will be allocated from metadata store.
110111
// If allocation successful, device will be activated with /dev/mapper/<deviceName>
111-
func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, virtualSizeBytes uint64) error {
112+
func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, virtualSizeBytes uint64) (retErr error) {
112113
info := &DeviceInfo{
113114
Name: deviceName,
114115
Size: virtualSizeBytes,
@@ -120,15 +121,46 @@ func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, vi
120121
return errors.Wrapf(err, "failed to save initial metadata for new thin device %q", deviceName)
121122
}
122123

124+
defer func() {
125+
if retErr == nil {
126+
return
127+
}
128+
129+
// Rollback metadata
130+
retErr = multierror.Append(retErr, p.metadata.RemoveDevice(ctx, info.Name))
131+
}()
132+
123133
// Create thin device
124-
if err := p.transition(ctx, deviceName, Creating, Created, func() error {
134+
if err := p.createDevice(ctx, info); err != nil {
135+
return err
136+
}
137+
138+
defer func() {
139+
if retErr == nil {
140+
return
141+
}
142+
143+
// Rollback creation
144+
retErr = multierror.Append(retErr, p.deleteDevice(ctx, info))
145+
}()
146+
147+
return p.activateDevice(ctx, info)
148+
}
149+
150+
// createDevice creates thin device
151+
func (p *PoolDevice) createDevice(ctx context.Context, info *DeviceInfo) error {
152+
if err := p.transition(ctx, info.Name, Creating, Created, func() error {
125153
return dmsetup.CreateDevice(p.poolName, info.DeviceID)
126154
}); err != nil {
127155
return errors.Wrapf(err, "failed to create new thin device %q (dev: %d)", info.Name, info.DeviceID)
128156
}
129157

130-
// Activate thin device
131-
if err := p.transition(ctx, deviceName, Activating, Activated, func() error {
158+
return nil
159+
}
160+
161+
// activateDevice activates thin device
162+
func (p *PoolDevice) activateDevice(ctx context.Context, info *DeviceInfo) error {
163+
if err := p.transition(ctx, info.Name, Activating, Activated, func() error {
132164
return dmsetup.ActivateDevice(p.poolName, info.Name, info.DeviceID, info.Size, "")
133165
}); err != nil {
134166
return errors.Wrapf(err, "failed to activate new thin device %q (dev: %d)", info.Name, info.DeviceID)
@@ -138,20 +170,23 @@ func (p *PoolDevice) CreateThinDevice(ctx context.Context, deviceName string, vi
138170
}
139171

140172
// CreateSnapshotDevice creates and activates new thin-device from parent thin-device (makes snapshot)
141-
func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string, snapshotName string, virtualSizeBytes uint64) error {
173+
func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string, snapshotName string, virtualSizeBytes uint64) (retErr error) {
142174
baseInfo, err := p.metadata.GetDevice(ctx, deviceName)
143175
if err != nil {
144176
return errors.Wrapf(err, "failed to query device metadata for %q", deviceName)
145177
}
146178

147-
// Suspend thin device if it was activated previously
179+
// Suspend thin device if it was activated previously to avoid corruptions
148180
isActivated := p.IsActivated(baseInfo.Name)
149181
if isActivated {
150-
if err := p.transition(ctx, baseInfo.Name, Suspending, Suspended, func() error {
151-
return dmsetup.SuspendDevice(baseInfo.Name)
152-
}); err != nil {
153-
return errors.Wrapf(err, "failed to suspend device %q", baseInfo.Name)
182+
if err := p.suspendDevice(ctx, baseInfo); err != nil {
183+
return err
154184
}
185+
186+
// Resume back base thin device on exit
187+
defer func() {
188+
retErr = multierror.Append(retErr, p.resumeDevice(ctx, baseInfo)).ErrorOrNil()
189+
}()
155190
}
156191

157192
snapInfo := &DeviceInfo{
@@ -166,33 +201,63 @@ func (p *PoolDevice) CreateSnapshotDevice(ctx context.Context, deviceName string
166201
return errors.Wrapf(err, "failed to save initial metadata for snapshot %q", snapshotName)
167202
}
168203

204+
defer func() {
205+
if retErr == nil {
206+
return
207+
}
208+
209+
// Rollback metadata
210+
retErr = multierror.Append(retErr, p.metadata.RemoveDevice(ctx, snapInfo.Name))
211+
}()
212+
169213
// Create thin device snapshot
214+
if err := p.createSnapshot(ctx, baseInfo, snapInfo); err != nil {
215+
return err
216+
}
217+
218+
defer func() {
219+
if retErr == nil {
220+
return
221+
}
222+
223+
// Rollback snapshot creation
224+
retErr = multierror.Append(retErr, p.deleteDevice(ctx, snapInfo))
225+
}()
226+
227+
// Activate snapshot device
228+
return p.activateDevice(ctx, snapInfo)
229+
}
230+
231+
func (p *PoolDevice) suspendDevice(ctx context.Context, info *DeviceInfo) error {
232+
if err := p.transition(ctx, info.Name, Suspending, Suspended, func() error {
233+
return dmsetup.SuspendDevice(info.Name)
234+
}); err != nil {
235+
return errors.Wrapf(err, "failed to suspend device %q", info.Name)
236+
}
237+
238+
return nil
239+
}
240+
241+
func (p *PoolDevice) resumeDevice(ctx context.Context, info *DeviceInfo) error {
242+
if err := p.transition(ctx, info.Name, Resuming, Resumed, func() error {
243+
return dmsetup.ResumeDevice(info.Name)
244+
}); err != nil {
245+
return errors.Wrapf(err, "failed to resume device %q", info.Name)
246+
}
247+
248+
return nil
249+
}
250+
251+
func (p *PoolDevice) createSnapshot(ctx context.Context, baseInfo, snapInfo *DeviceInfo) error {
170252
if err := p.transition(ctx, snapInfo.Name, Creating, Created, func() error {
171253
return dmsetup.CreateSnapshot(p.poolName, snapInfo.DeviceID, baseInfo.DeviceID)
172254
}); err != nil {
173255
return errors.Wrapf(err,
174-
"failed to create snapshot %q (dev: %d) from %q (dev: %d, activated: %t)",
256+
"failed to create snapshot %q (dev: %d) from %q (dev: %d)",
175257
snapInfo.Name,
176258
snapInfo.DeviceID,
177259
baseInfo.Name,
178-
baseInfo.DeviceID,
179-
isActivated)
180-
}
181-
182-
if isActivated {
183-
// Resume base thin-device
184-
if err := p.transition(ctx, baseInfo.Name, Resuming, Resumed, func() error {
185-
return dmsetup.ResumeDevice(baseInfo.Name)
186-
}); err != nil {
187-
return errors.Wrapf(err, "failed to resume device %q", deviceName)
188-
}
189-
}
190-
191-
// Activate snapshot
192-
if err := p.transition(ctx, snapInfo.Name, Activating, Activated, func() error {
193-
return dmsetup.ActivateDevice(p.poolName, snapInfo.Name, snapInfo.DeviceID, snapInfo.Size, "")
194-
}); err != nil {
195-
return errors.Wrapf(err, "failed to activate snapshot device %q (dev: %d)", snapInfo.Name, snapInfo.DeviceID)
260+
baseInfo.DeviceID)
196261
}
197262

198263
return nil
@@ -221,7 +286,7 @@ func (p *PoolDevice) DeactivateDevice(ctx context.Context, deviceName string, de
221286
// IsActivated returns true if thin-device is activated and not suspended
222287
func (p *PoolDevice) IsActivated(deviceName string) bool {
223288
infos, err := dmsetup.Info(deviceName)
224-
if err != nil || len(infos) == 0 {
289+
if err != nil || len(infos) != 1 {
225290
// Couldn't query device info, device not active
226291
return false
227292
}
@@ -244,11 +309,8 @@ func (p *PoolDevice) RemoveDevice(ctx context.Context, deviceName string) error
244309
return err
245310
}
246311

247-
if err := p.transition(ctx, deviceName, Removing, Removed, func() error {
248-
// Send 'delete' message to thin-pool
249-
return dmsetup.DeleteDevice(p.poolName, info.DeviceID)
250-
}); err != nil {
251-
return errors.Wrapf(err, "failed to delete device %q (dev id: %d)", info.Name, info.DeviceID)
312+
if err := p.deleteDevice(ctx, info); err != nil {
313+
return err
252314
}
253315

254316
// Remove record from meta store and free device ID
@@ -259,6 +321,17 @@ func (p *PoolDevice) RemoveDevice(ctx context.Context, deviceName string) error
259321
return nil
260322
}
261323

324+
func (p *PoolDevice) deleteDevice(ctx context.Context, info *DeviceInfo) error {
325+
if err := p.transition(ctx, info.Name, Removing, Removed, func() error {
326+
// Send 'delete' message to thin-pool
327+
return dmsetup.DeleteDevice(p.poolName, info.DeviceID)
328+
}); err != nil {
329+
return errors.Wrapf(err, "failed to delete device %q (dev id: %d)", info.Name, info.DeviceID)
330+
}
331+
332+
return nil
333+
}
334+
262335
// RemovePool deactivates all child thin-devices and removes thin-pool device
263336
func (p *PoolDevice) RemovePool(ctx context.Context) error {
264337
deviceNames, err := p.metadata.GetDeviceNames(ctx)

snapshots/devmapper/snapshotter.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,9 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
310310
}
311311

312312
if err := s.mkfs(ctx, deviceName); err != nil {
313-
return nil, err
313+
// Rollback thin device creation if mkfs failed
314+
return nil, multierror.Append(err,
315+
s.pool.RemoveDevice(ctx, deviceName))
314316
}
315317
} else {
316318
parentDeviceName := s.getDeviceName(snap.ParentIDs[0])

0 commit comments

Comments
 (0)