Skip to content

Commit ca42294

Browse files
authored
Merge pull request #143 from NearNodeFlash/replace-ns
When a replacement Volume is added to a pool, ensure the volume is no…
2 parents 313dccc + adc2326 commit ca42294

1 file changed

Lines changed: 90 additions & 16 deletions

File tree

pkg/manager-nnf/storage_pool.go

Lines changed: 90 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ type StoragePool struct {
4949
missingVolumes []storagePoolPersistentVolumeInfo
5050

5151
// Original persistent volume information from KV store
52-
persistentVolumes []storagePoolPersistentVolumeInfo
52+
persistedVolumes []storagePoolPersistentVolumeInfo
5353

5454
storageGroupIds []string
5555
fileSystemId string
@@ -123,17 +123,34 @@ func (p *StoragePool) findStorageGroupByEndpoint(endpoint *Endpoint) *StorageGro
123123
return nil
124124
}
125125

126+
// recoverVolumes attempts to restore the state of volumes in the storage pool based on
127+
// the provided slice of persisted volume information. It updates the pool's internal
128+
// lists of persisted, missing, and providing volumes by:
129+
// - Skipping and marking as missing any volumes with an invalid namespace ID.
130+
// - Attempting to locate the corresponding storage device and namespace for each volume.
131+
// - Marking volumes as missing if the storage device or namespace cannot be found.
132+
// - Adding successfully located volumes to the providingVolumes list.
133+
//
134+
// Finally, it updates the allocatedVolume field to reflect the current pool capacity.
126135
func (p *StoragePool) recoverVolumes(volumes []storagePoolPersistentVolumeInfo) error {
127136
log := p.storageService.log.WithValues(storagePoolIdKey, p.id)
128137
log.Info("recover volumes")
129138

130-
// Store the persistent volumes information for later use
131-
p.persistentVolumes = make([]storagePoolPersistentVolumeInfo, len(volumes))
132-
copy(p.persistentVolumes, volumes)
139+
// Store the persisted volumes information for later use
140+
p.persistedVolumes = make([]storagePoolPersistentVolumeInfo, len(volumes))
141+
copy(p.persistedVolumes, volumes)
133142

134143
for _, volumeInfo := range volumes {
135144
log := log.WithValues("serialNumber", volumeInfo.SerialNumber, "namespaceId", volumeInfo.NamespaceID)
136145

146+
// Consider volumes that have been invalidated (marked with invalid namespace ID) as missing.
147+
// They need a replacement.
148+
if volumeInfo.NamespaceID == invalidNamespaceID {
149+
log.V(2).Info("skipping invalidated volume during recovery")
150+
p.missingVolumes = append(p.missingVolumes, volumeInfo)
151+
continue
152+
}
153+
137154
// Locate the NVMe Storage device by Serial Number
138155
storage := p.storageService.findStorage(volumeInfo.SerialNumber)
139156
if storage == nil {
@@ -165,16 +182,31 @@ func (p *StoragePool) recoverVolumes(volumes []storagePoolPersistentVolumeInfo)
165182
return nil
166183
}
167184

168-
// Rescan namespaces associated to set the missing volumes list
185+
// checkVolumes validates the current state of volumes in the storage pool by rescanning
186+
// associated storage devices and updating the pool's volume tracking lists.
187+
//
188+
// This method performs the following operations:
189+
// 1. Rescans all storage devices that should contain volumes for this pool to refresh
190+
// their namespace information and detect any changes in device state
191+
// 2. Clears and rebuilds both the missingVolumes and providingVolumes lists to ensure
192+
// they accurately reflect the current state
193+
// 3. Attempts to recover volumes from persistent storage information, identifying
194+
// which volumes are still available and which are missing or invalid
195+
// 4. Updates the pool's internal state to reflect any volumes that may have become
196+
// unavailable due to device failures, disconnections, or other issues
197+
//
198+
// The method is typically called during pool initialization, recovery operations,
199+
// or when storage device states may have changed. It ensures the pool maintains
200+
// an accurate view of its available storage resources.
169201
func (p *StoragePool) checkVolumes() error {
170202
log := p.storageService.log.WithValues(storagePoolIdKey, p.id)
171203
log.Info("check volumes")
172204

173205
// Rescan the storages to ensure our namespace information is up to date
174-
volumes := make([]storagePoolPersistentVolumeInfo, len(p.persistentVolumes))
175-
copy(volumes, p.persistentVolumes)
206+
volumesInPool := make([]storagePoolPersistentVolumeInfo, len(p.persistedVolumes))
207+
copy(volumesInPool, p.persistedVolumes)
176208

177-
for _, pv := range volumes {
209+
for _, pv := range volumesInPool {
178210
log := log.WithValues("serialNumber", pv.SerialNumber, "namespaceId", pv.NamespaceID)
179211
storage := p.storageService.findStorage(pv.SerialNumber)
180212
if storage == nil {
@@ -187,7 +219,7 @@ func (p *StoragePool) checkVolumes() error {
187219
p.missingVolumes = p.missingVolumes[:0] // Clear the missing volumes list
188220
p.providingVolumes = p.providingVolumes[:0] // Clear the providing volumes list
189221

190-
if err := p.recoverVolumes(volumes); err != nil {
222+
if err := p.recoverVolumes(volumesInPool); err != nil {
191223
log.Error(err, "Failed to recover volumes")
192224
return err
193225
}
@@ -268,11 +300,11 @@ func (p *StoragePool) replaceMissingVolumes() error {
268300
pv.Storage.SerialNumber(),
269301
pv.VolumeId), p)
270302

271-
// TODO: Find the serialnumber/volumeid in any other storage pools and
272-
// invalidate that volumeid to prevent reuse. Should probably store
273-
// that new value some how too.
274-
// OR
275-
// Patch all the storage pools and don't allow a particular storeage pool to be patched in isolation
303+
// Invalidate this volume in any other storage pools to prevent reuse
304+
if err := p.invalidateVolumeInOtherPools(pv.Storage.SerialNumber(), pv.Storage.FindVolume(pv.VolumeId).GetNamespaceId()); err != nil {
305+
log.Error(err, "Failed to invalidate volume in other storage pools", "serialNumber", pv.Storage.SerialNumber(), "namespaceId", pv.Storage.FindVolume(pv.VolumeId).GetNamespaceId())
306+
// This is not a fatal error, continue with the replacement
307+
}
276308
}
277309

278310
// We've replaced all the missing volumes, so clear the list
@@ -281,6 +313,45 @@ func (p *StoragePool) replaceMissingVolumes() error {
281313
return nil
282314
}
283315

316+
// invalidateVolumeInOtherPools finds and invalidates the specified volume in any other storage pools
317+
// to prevent reuse of the same volume in multiple pools. It replaces the namespace ID with a
318+
// nonsense value (0xFFFFFFFF) in the persistent volume information of other pools.
319+
func (p *StoragePool) invalidateVolumeInOtherPools(serialNumber string, namespaceID nvme2.NamespaceIdentifier) error {
320+
log := p.storageService.log.WithValues(storagePoolIdKey, p.id, "targetSerialNumber", serialNumber, "targetNamespaceId", namespaceID)
321+
log.V(2).Info("invalidating volume in other storage pools")
322+
323+
invalidatedPools := 0
324+
325+
// Check all other storage pools in the service
326+
for i := range p.storageService.pools {
327+
otherPool := &p.storageService.pools[i]
328+
if otherPool.id == p.id {
329+
continue // Skip self
330+
}
331+
332+
poolModified := false
333+
334+
// Check persistent volumes
335+
for j := range otherPool.persistedVolumes {
336+
persistentVol := &otherPool.persistedVolumes[j]
337+
if persistentVol.SerialNumber == serialNumber && persistentVol.NamespaceID == namespaceID {
338+
log.Info("invalidating persistent volume in storage pool", "otherPoolId", otherPool.id, "originalNamespaceId", persistentVol.NamespaceID)
339+
340+
// Replace with a nonsense namespace ID to prevent reuse
341+
persistentVol.NamespaceID = invalidNamespaceID
342+
poolModified = true
343+
}
344+
}
345+
346+
if poolModified {
347+
invalidatedPools++
348+
}
349+
}
350+
351+
log.V(2).Info("volume invalidation complete", "invalidatedPools", invalidatedPools)
352+
return nil
353+
}
354+
284355
// Locate Storages not providing a volume for this pool
285356
func (p *StoragePool) locateUnusedStorage() []*nvme.Storage {
286357
log := p.storageService.log.WithValues(storagePoolIdKey, p.id)
@@ -356,6 +427,9 @@ func (p *StoragePool) deallocateVolumes() error {
356427

357428
const storagePoolRegistryPrefix = "SP"
358429

430+
// Invalid namespace ID used to mark volumes as unusable
431+
const invalidNamespaceID = nvme2.NamespaceIdentifier(0xFFFFFFFF)
432+
359433
const (
360434
// Allocation Log Entry is recorded after the storage pool successfully allocates the backing storage resources (i.e. NVMe Namespaces)
361435
storagePoolStorageCreateStartLogEntryType uint32 = iota
@@ -430,8 +504,8 @@ func (p *StoragePool) GenerateStateData(state uint32) ([]byte, error) {
430504
}
431505

432506
// Store the persistent volumes information for later use
433-
p.persistentVolumes = make([]storagePoolPersistentVolumeInfo, len(entry.Volumes))
434-
copy(p.persistentVolumes, entry.Volumes)
507+
p.persistedVolumes = make([]storagePoolPersistentVolumeInfo, len(entry.Volumes))
508+
copy(p.persistedVolumes, entry.Volumes)
435509

436510
return json.Marshal(entry)
437511
}

0 commit comments

Comments
 (0)