@@ -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.
126135func (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.
169201func (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
285356func (p * StoragePool ) locateUnusedStorage () []* nvme.Storage {
286357 log := p .storageService .log .WithValues (storagePoolIdKey , p .id )
@@ -356,6 +427,9 @@ func (p *StoragePool) deallocateVolumes() error {
356427
357428const storagePoolRegistryPrefix = "SP"
358429
430+ // Invalid namespace ID used to mark volumes as unusable
431+ const invalidNamespaceID = nvme2 .NamespaceIdentifier (0xFFFFFFFF )
432+
359433const (
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