Example #1
0
        private List <ArrayPosition> TranslateSectors(long startSectorIndex, int sectorCount)
        {
            List <ArrayPosition> result = new List <ArrayPosition>();

            int numberOfDisks = m_extents.Count;

            int  sectorsLeft        = sectorCount;
            long currentSectorIndex = startSectorIndex;

            while (sectorsLeft > 0)
            {
                long extentStartSectorInColumn     = 0;
                long nextExtentStartSectorInColumn = 0;
                for (int index = 0; index < m_extents.Count; index++)
                {
                    DynamicDiskExtent extent = m_extents[index];
                    extentStartSectorInColumn      = nextExtentStartSectorInColumn;
                    nextExtentStartSectorInColumn += extent.TotalSectors;
                    if (currentSectorIndex >= extentStartSectorInColumn && currentSectorIndex < nextExtentStartSectorInColumn)
                    {
                        long          sectorIndexInExtent = currentSectorIndex - extentStartSectorInColumn;
                        int           sectorCountInExtent = (int)Math.Min(extent.TotalSectors - sectorIndexInExtent, sectorsLeft);
                        ArrayPosition position            = new ArrayPosition(index, sectorIndexInExtent, sectorCountInExtent);
                        result.Add(position);
                        currentSectorIndex += sectorCountInExtent;
                        sectorsLeft        -= sectorCountInExtent;
                    }
                }
            }

            return(result);
        }
        private static void MoveExtentLeft(List<DynamicDisk> disks, DynamicVolume volume, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            DynamicDiskExtent relocatedExtent = DynamicDiskExtentHelper.GetByExtentID(volume.DynamicExtents, resumeRecord.ExtentID);
            if (resumeRecord.OldStartSector == (ulong)relocatedExtent.FirstSector)
            { 
                // the database update was not completed (this must be a resume operation)
                relocatedExtent = new DynamicDiskExtent(relocatedExtent.Disk, (long)resumeRecord.NewStartSector, relocatedExtent.Size, resumeRecord.ExtentID);
                VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, relocatedExtent);
            }

            DiskExtent sourceExtent = new DiskExtent(relocatedExtent.Disk, (long)resumeRecord.OldStartSector, relocatedExtent.Size);

            MoveHelper.MoveExtentDataLeft(volume, sourceExtent, relocatedExtent, resumeRecord, ref bytesCopied);
            
            // if this is a resume, then volume is StripedVolume, otherwise it is a Raid5Volume
            if (resumeRecord.RestoreRAID5)
            {
                VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
                // get the updated volume (we just reconverted to RAID-5)
                volume = DynamicVolumeHelper.GetVolumeByGuid(disks, volume.VolumeGuid);
            }
            
            // restore the filesystem boot sector
            byte[] filesystemBootRecord = relocatedExtent.Disk.ReadSector((long)resumeRecord.BootRecordBackupSector);
            volume.WriteSectors(0, filesystemBootRecord);

            ClearBackupData(relocatedExtent.Disk, resumeRecord);
        }
Example #3
0
        public static void ExtendStripedVolume(DiskGroupDatabase database, StripedVolume volume, long additionalNumberOfExtentSectors)
        {
            if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0)
            {
                throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
            }

            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);

            volumeRecord          = (VolumeRecord)volumeRecord.Clone();
            volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * volume.NumberOfColumns);
            records.Add(volumeRecord);

            // we only want to extend the last extent in each column
            foreach (DynamicColumn column in volume.Columns)
            {
                DynamicDiskExtent lastExtent   = column.Extents[column.Extents.Count - 1];
                ExtentRecord      extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
                extentRecord          = (ExtentRecord)extentRecord.Clone();
                extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors;
                records.Add(extentRecord);

                DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
                diskRecord = (DiskRecord)diskRecord.Clone();
                records.Add(diskRecord);
            }

            database.UpdateDatabase(records);
        }
Example #4
0
        public static void ExtendRAID5Volume(DiskGroupDatabase database, Raid5Volume volume, long numberOfAdditionalExtentSectors)
        {
            if (numberOfAdditionalExtentSectors % volume.SectorsPerStripe > 0)
            {
                throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector");
            }

            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);

            volumeRecord          = (VolumeRecord)volumeRecord.Clone();
            volumeRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalExtentSectors * (volume.NumberOfColumns - 1), volume.BytesPerSector);
            records.Add(volumeRecord);

            foreach (DynamicColumn column in volume.Columns)
            {
                DynamicDiskExtent lastExtent   = column.Extents[column.Extents.Count - 1];
                ExtentRecord      extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID);
                extentRecord          = (ExtentRecord)extentRecord.Clone();
                extentRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalExtentSectors, volume.BytesPerSector);
                records.Add(extentRecord);

                DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs
                diskRecord = (DiskRecord)diskRecord.Clone();
                records.Add(diskRecord);
            }

            database.UpdateDatabase(records);
        }
        public const int BackupBufferSizeLBA = 128; // there are about 180 contiguous free sectors in a private region

        /// <summary>
        /// Move extent to another disk
        /// </summary>
        public static void MoveExtentToAnotherDisk(List<DynamicDisk> disks, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            // copy the data
            long transferSizeLBA = Settings.MaximumTransferSizeLBA;
            for (long sectorIndex = 0; sectorIndex < relocatedExtent.TotalSectors; sectorIndex += transferSizeLBA)
            {
                long sectorsLeft = relocatedExtent.TotalSectors - sectorIndex;
                int sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft);

                byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead);
                
                relocatedExtent.WriteSectors(sectorIndex, data);

                bytesCopied += sectorsToRead * sourceExtent.BytesPerSector;
            }

            // Update the database to point to the relocated extent
            DynamicDisk targetDisk = DynamicDisk.ReadFromDisk(relocatedExtent.Disk);
            DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);
            dynamicRelocatedExtent.Name = sourceExtent.Name;
            dynamicRelocatedExtent.DiskGuid = targetDisk.DiskGuid;
            VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);
        }
Example #6
0
        public static void ResumeAddDiskToRaid5Volume(List <DynamicDisk> disks, StripedVolume stripedVolume, AddDiskOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            List <DynamicColumn> columns   = stripedVolume.Columns;
            DynamicDiskExtent    newExtent = columns[columns.Count - 1].Extents[0];

            columns.RemoveAt(columns.Count - 1);

            Raid5Volume volume = new Raid5Volume(columns, stripedVolume.SectorsPerStripe, stripedVolume.VolumeGuid, stripedVolume.DiskGroupGuid);

            volume.VolumeID      = stripedVolume.VolumeID;
            volume.Name          = stripedVolume.Name;
            volume.DiskGroupName = stripedVolume.DiskGroupName;
            ResumeAddDiskToRaid5Volume(disks, volume, newExtent, resumeRecord, ref bytesCopied);
        }
        /// <param name="targetOffset">in bytes</param>
        public static bool IsMoveLocationValid(DynamicDiskExtent sourceExtent, DynamicDisk targetDisk, long targetOffset)
        {
            bool isSameDisk = (sourceExtent.Disk == targetDisk.Disk);
            List <DynamicDiskExtent> extents = GetDiskExtents(targetDisk);

            // extents are sorted by first sector
            if (extents == null)
            {
                return(false);
            }

            PrivateHeader privateHeader = targetDisk.PrivateHeader;

            if (sourceExtent.BytesPerSector != targetDisk.BytesPerSector)
            {
                // We must not move an extent to another disk that has different sector size
                return(false);
            }
            if (targetOffset % privateHeader.BytesPerSector > 0)
            {
                return(false);
            }
            long       targetSector = targetOffset / targetDisk.BytesPerSector;
            DiskExtent targetExtent = new DiskExtent(targetDisk.Disk, targetSector, sourceExtent.Size);

            List <DiskExtent> usedExtents = new List <DiskExtent>();

            foreach (DynamicDiskExtent extent in extents)
            {
                if (!isSameDisk || extent.FirstSector != sourceExtent.FirstSector)
                {
                    usedExtents.Add(extent);
                }
            }

            long publicRegionStartSector         = (long)privateHeader.PublicRegionStartLBA;
            long publicRegionSize                = (long)privateHeader.PublicRegionSizeLBA * targetDisk.BytesPerSector;
            List <DiskExtent> unallocatedExtents = DiskExtentsHelper.GetUnallocatedExtents(targetDisk.Disk, publicRegionStartSector, publicRegionSize, usedExtents);

            foreach (DiskExtent extent in unallocatedExtents)
            {
                if (extent.FirstSector <= targetExtent.FirstSector && targetExtent.LastSector <= extent.LastSector)
                {
                    return(true);
                }
            }
            return(false);
        }
Example #8
0
        public void WriteSectors(long sectorIndex, byte[] data)
        {
            int sectorCount = data.Length / BytesPerSector;
            List <ArrayPosition> writePositions = TranslateSectors(sectorIndex, sectorCount);

            int bytesWritten = 0;

            foreach (ArrayPosition writePosition in writePositions)
            {
                DynamicDiskExtent extent      = m_extents[writePosition.DiskIndex];
                byte[]            extentBytes = new byte[writePosition.SectorCount * BytesPerSector];
                Array.Copy(data, bytesWritten, extentBytes, 0, extentBytes.Length);
                extent.WriteSectors(writePosition.SectorIndex, extentBytes);

                bytesWritten += extentBytes.Length;
            }
        }
Example #9
0
        /// <summary>
        /// Segment - sequence of stripes that is a multiple of (NumberOfColumns - 1),
        /// and every (NumberOfColumns - 1) stripes in the sequence have the same stripeIndexInColumn
        /// (Such sequence can be written to disk without reading the parity information first)
        /// </summary>
        public static void WriteSegment(Raid5Volume volume, DynamicDiskExtent newExtent, long firstStripeIndexInColumn, byte[] data)
        {
            List <DynamicColumn> newArray = new List <DynamicColumn>();

            newArray.AddRange(volume.Columns);
            newArray.Add(new DynamicColumn(newExtent));

            int bytesPerStripe = volume.BytesPerStripe;

            int stripesToWritePerColumn = (data.Length / bytesPerStripe) / (newArray.Count - 1);

            int dataLengthPerColumn = stripesToWritePerColumn * bytesPerStripe;

            byte[][] columnData = new byte[newArray.Count][];
            for (int index = 0; index < columnData.Length; index++)
            {
                columnData[index] = new byte[dataLengthPerColumn];
            }

            Parallel.For(0, stripesToWritePerColumn, delegate(int stripeOffsetInColumn)
            {
                long stripeIndexInColumn = firstStripeIndexInColumn + stripeOffsetInColumn;
                int parityColumnIndex    = (newArray.Count - 1) - (int)(stripeIndexInColumn % newArray.Count);

                byte[] parityData = new byte[bytesPerStripe];
                for (int stripeVerticalIndex = 0; stripeVerticalIndex < newArray.Count - 1; stripeVerticalIndex++)
                {
                    int columnIndex = (parityColumnIndex + 1 + stripeVerticalIndex) % newArray.Count;

                    long stripeOffsetInData = (stripeOffsetInColumn * (newArray.Count - 1) + stripeVerticalIndex) * bytesPerStripe;
#warning long array index
                    Array.Copy(data, (int)stripeOffsetInData, columnData[columnIndex], stripeOffsetInColumn * bytesPerStripe, bytesPerStripe);

                    parityData = Raid5Volume.XOR(parityData, columnData[columnIndex], stripeOffsetInColumn * bytesPerStripe, bytesPerStripe);
                }
                Array.Copy(parityData, 0, columnData[parityColumnIndex], stripeOffsetInColumn * bytesPerStripe, bytesPerStripe);
            });

            // write the data
            long firstSectorIndexInColumn = firstStripeIndexInColumn * volume.SectorsPerStripe;
            for (int columnIndex = 0; columnIndex < newArray.Count; columnIndex++)
            {
                newArray[columnIndex].WriteSectors(firstSectorIndexInColumn, columnData[columnIndex]);
            }
        }
Example #10
0
        /// <summary>
        /// Support null disks
        /// </summary>
        public static DynamicDiskExtent GetDiskExtent(DynamicDisk dynamicDisk, ExtentRecord extentRecord)
        {
            long extentStartSector = GetExtentStartSector(dynamicDisk, extentRecord);
            long extentSize        = (long)extentRecord.SizeLBA * PublicRegionHelper.BytesPerPublicRegionSector;
            Disk disk     = null;
            Guid diskGuid = Guid.Empty;

            if (dynamicDisk != null)
            {
                disk     = dynamicDisk.Disk;
                diskGuid = dynamicDisk.DiskGuid;
            }
            DynamicDiskExtent extent = new DynamicDiskExtent(disk, extentStartSector, extentSize, extentRecord.ExtentId);

            extent.Name     = extentRecord.Name;
            extent.DiskGuid = diskGuid;
            return(extent);
        }
Example #11
0
        public byte[] ReadSectors(long sectorIndex, int sectorCount)
        {
            List <ArrayPosition> readPositions = TranslateSectors(sectorIndex, sectorCount);

            byte[] result    = new byte[sectorCount * BytesPerSector];
            int    bytesRead = 0;

            foreach (ArrayPosition readPosition in readPositions)
            {
                DynamicDiskExtent extent      = m_extents[readPosition.DiskIndex];
                byte[]            extentBytes = extent.ReadSectors(readPosition.SectorIndex, readPosition.SectorCount);

                Array.Copy(extentBytes, 0, result, bytesRead, extentBytes.Length);
                bytesRead += extentBytes.Length;
            }

            return(result);
        }
        public static void ResumeMoveExtent(List<DynamicDisk> disks, DynamicVolume volume, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            if (resumeRecord.OldStartSector == resumeRecord.NewStartSector)
            {
                throw new InvalidDataException("Invalid move record");
            }

            if (resumeRecord.RestoreFromBuffer)
            {
                // we need to use the backup buffer to restore the data that may have been overwritten
                int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, resumeRecord.ExtentID);
                DynamicDiskExtent sourceExtent = volume.DynamicExtents[extentIndex];

                byte[] backupBuffer = sourceExtent.Disk.ReadSectors((long)resumeRecord.BackupBufferStartSector, BackupBufferSizeLBA);
                if (resumeRecord.OldStartSector < resumeRecord.NewStartSector)
                {
                    // move right
                    long readCount = (long)resumeRecord.NumberOfCommittedSectors;
                    int sectorsToRead = BackupBufferSizeLBA;
                    long sectorIndex = sourceExtent.TotalSectors - readCount - sectorsToRead;
                    sourceExtent.WriteSectors(sectorIndex, backupBuffer);

                    System.Diagnostics.Debug.WriteLine("Restored to " + sectorIndex);
                }
                else
                {
                    // move left
                    long sectorIndex = (long)resumeRecord.NumberOfCommittedSectors;
                    sourceExtent.WriteSectors(sectorIndex, backupBuffer);

                    System.Diagnostics.Debug.WriteLine("Restored to " + sectorIndex);
                }
            }

            if (resumeRecord.OldStartSector < resumeRecord.NewStartSector)
            {
                MoveExtentRight(disks, volume, resumeRecord, ref bytesCopied);
            }
            else
            {
                MoveExtentLeft(disks, volume, resumeRecord, ref bytesCopied);
            }
        }
Example #13
0
        private static List <DynamicColumn> GetDynamicVolumeColumns(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord)
        {
            // extentRecords are sorted by offset in column
            List <ExtentRecord> extentRecords = database.FindExtentsByComponentID(componentRecord.ComponentId);

            if (componentRecord.NumberOfExtents != extentRecords.Count || extentRecords.Count == 0)
            {
                // database record is invalid
                throw new InvalidDataException("Number of extents in component record does not match actual number of extent records");
            }

            SortedList <uint, List <DynamicDiskExtent> > columns = new SortedList <uint, List <DynamicDiskExtent> >();

            foreach (ExtentRecord extentRecord in extentRecords)
            {
                DiskRecord        diskRecord = database.FindDiskByDiskID(extentRecord.DiskId);
                DynamicDisk       disk       = DynamicDiskHelper.FindDisk(database.Disks, diskRecord.DiskGuid); // we add nulls as well
                DynamicDiskExtent extent     = DynamicDiskExtentHelper.GetDiskExtent(disk, extentRecord);

                if (columns.ContainsKey(extentRecord.ColumnIndex))
                {
                    columns[extentRecord.ColumnIndex].Add(extent);
                }
                else
                {
                    List <DynamicDiskExtent> list = new List <DynamicDiskExtent>();
                    list.Add(extent);
                    columns.Add(extentRecord.ColumnIndex, list);
                }
            }

            List <DynamicColumn> result = new List <DynamicColumn>();

            foreach (List <DynamicDiskExtent> extents in columns.Values)
            {
                result.Add(new DynamicColumn(extents));
            }
            return(result);
        }
Example #14
0
        private static void MoveExtentRight(List <DynamicDisk> disks, DynamicVolume volume, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, resumeRecord.ExtentID);
            DynamicDiskExtent sourceExtent    = volume.DynamicExtents[extentIndex];
            DiskExtent        relocatedExtent = new DiskExtent(sourceExtent.Disk, (long)resumeRecord.NewStartSector, sourceExtent.Size);

            MoveHelper.MoveExtentDataRight(volume, sourceExtent, relocatedExtent, resumeRecord, ref bytesCopied);

            // even if the database update won't complete, the resume record was copied

            // update the database
            DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);

            dynamicRelocatedExtent.Name     = sourceExtent.Name;
            dynamicRelocatedExtent.DiskGuid = sourceExtent.DiskGuid;
            VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);

            // if this is a resume, then volume is StripedVolume, otherwise it is a Raid5Volume
            if (resumeRecord.RestoreRAID5)
            {
                VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
            }
            // get the updated volume (we moved an extent and possibly reconverted to RAID-5)
            volume = DynamicVolumeHelper.GetVolumeByGuid(disks, volume.VolumeGuid);

            // restore the filesystem boot sector
            byte[] filesystemBootRecord = relocatedExtent.Disk.ReadSector((long)resumeRecord.BootRecordBackupSector);
            volume.WriteSectors(0, filesystemBootRecord);

            ClearBackupData(relocatedExtent.Disk, resumeRecord);
        }
        /// <summary>
        /// Sorted by first sector
        /// </summary>
        /// <returns>null if there was a problem reading extent information from disk</returns>
        public static List <DynamicDiskExtent> GetDiskExtents(DynamicDisk disk)
        {
            List <DynamicDiskExtent> result        = new List <DynamicDiskExtent>();
            PrivateHeader            privateHeader = disk.PrivateHeader;

            if (privateHeader != null)
            {
                VolumeManagerDatabase database = VolumeManagerDatabase.ReadFromDisk(disk);
                if (database != null)
                {
                    DiskRecord          diskRecord    = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
                    List <ExtentRecord> extentRecords = database.FindExtentsByDiskID(diskRecord.DiskId);
                    foreach (ExtentRecord extentRecord in extentRecords)
                    {
                        DynamicDiskExtent extent = DynamicDiskExtentHelper.GetDiskExtent(disk, extentRecord);
                        result.Add(extent);
                    }
                    DynamicDiskExtentsHelper.SortExtentsByFirstSector(result);
                    return(result);
                }
            }
            return(null);
        }
Example #16
0
        private static SimpleVolume GetSimpleVolume(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord)
        {
            List <ExtentRecord> extentRecords = database.FindExtentsByComponentID(componentRecord.ComponentId);

            if (extentRecords.Count == 1)
            {
                ExtentRecord extentRecord = extentRecords[0];

                DiskRecord        diskRecord = database.FindDiskByDiskID(extentRecord.DiskId);
                DynamicDisk       disk       = DynamicDiskHelper.FindDisk(database.Disks, diskRecord.DiskGuid); // we add nulls as well
                DynamicDiskExtent extent     = DynamicDiskExtentHelper.GetDiskExtent(disk, extentRecord);

                SimpleVolume volume = new SimpleVolume(extent, volumeRecord.VolumeGuid, database.DiskGroupGuid);
                volume.VolumeID      = volumeRecord.VolumeId;
                volume.Name          = volumeRecord.Name;
                volume.DiskGroupName = database.DiskGroupName;
                return(volume);
            }
            else
            {
                // component / extent records are invalid
                throw new InvalidDataException("Number of extents in component record does not match actual number of extent records");
            }
        }
Example #17
0
        private static void ResumeAddDiskToRaid5Volume(List <DynamicDisk> disks, Raid5Volume volume, DynamicDiskExtent newExtent, AddDiskOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            // When reading from the volume, we must use the old volume (without the new disk)
            // However, when writing the boot sector to the volume, we must use the new volume or otherwise parity information will be invalid
            List <DynamicColumn> newVolumeColumns = new List <DynamicColumn>();

            newVolumeColumns.AddRange(volume.Columns);
            newVolumeColumns.Add(new DynamicColumn(newExtent));
            Raid5Volume newVolume = new Raid5Volume(newVolumeColumns, volume.SectorsPerStripe, volume.VolumeGuid, volume.DiskGroupGuid);

            int oldColumnCount = volume.Columns.Count;
            int newColumnCount = oldColumnCount + 1;

            long resumeFromStripe = (long)resumeRecord.NumberOfCommittedSectors / volume.SectorsPerStripe;
            // it would be prudent to write the new extent before committing to the operation, however, it would take much longer.

            // The number of sectors in extent / column is always a multiple of SectorsPerStripe.

            // We read enough stripes to write a vertical stripe segment in the new array,
            // We will read MaximumTransferSizeLBA and make sure maximumStripesToTransfer is multiple of (NumberOfColumns - 1).
            int  maximumStripesToTransfer = (Settings.MaximumTransferSizeLBA / volume.SectorsPerStripe) / (newColumnCount - 1) * (newColumnCount - 1);
            long totalStripesInVolume     = volume.TotalStripes;

            long stripeIndexInVolume = resumeFromStripe;

            while (stripeIndexInVolume < totalStripesInVolume)
            {
                // When we add a column, the distance between the stripes we read (later in the column) to thes one we write (earlier),
                // Is growing constantly (because we can stack more stripes in each vertical stripe), so we increment the number of stripes we
                // can safely transfer as we go.
                // (We assume that the segment we write will be corrupted if there will be a power failure)

                long stripeToReadIndexInColumn     = stripeIndexInVolume / (oldColumnCount - 1);
                long stripeToWriteIndexInColumn    = stripeIndexInVolume / (newColumnCount - 1);
                long numberOfStripesSafeToTransfer = (stripeToReadIndexInColumn - stripeToWriteIndexInColumn) * (newColumnCount - 1);
                bool verticalStripeAtRisk          = (numberOfStripesSafeToTransfer == 0);
                if (numberOfStripesSafeToTransfer == 0)
                {
                    // The first few stripes in each column are 'at rist', meaning that we may overwrite crucial data (that is only stored in memory)
                    // when writing the segment that will be lost forever if a power failure will occur during the write operation.
                    // Note: The number of 'at risk' vertical stripes is equal to the number of columns in the old array - 1
                    numberOfStripesSafeToTransfer = (newColumnCount - 1);
                }
                int numberOfStripesToTransfer = (int)Math.Min(numberOfStripesSafeToTransfer, maximumStripesToTransfer);

                long stripesLeft = totalStripesInVolume - stripeIndexInVolume;
                numberOfStripesToTransfer = (int)Math.Min(numberOfStripesToTransfer, stripesLeft);
                byte[] segmentData = volume.ReadStripes(stripeIndexInVolume, numberOfStripesToTransfer);

                if (numberOfStripesToTransfer % (newColumnCount - 1) > 0)
                {
                    // this is the last segment and we need to zero-fill it for the write:
                    int    numberOfStripesToWrite = (int)Math.Ceiling((decimal)numberOfStripesToTransfer / (newColumnCount - 1)) * (newColumnCount - 1);
                    byte[] temp = new byte[numberOfStripesToWrite * volume.BytesPerStripe];
                    Array.Copy(segmentData, temp, segmentData.Length);
                    segmentData = temp;
                }

                long firstStripeIndexInColumn = stripeIndexInVolume / (newColumnCount - 1);
                if (verticalStripeAtRisk)
                {
                    // we write 'at risk' stripes one at a time to the new volume, this will make sure they will not overwrite crucial data
                    // (because they will be written in an orderly fashion, and not in bulk from the first column to the last)
                    newVolume.WriteStripes(stripeIndexInVolume, segmentData);
                }
                else
                {
                    WriteSegment(volume, newExtent, firstStripeIndexInColumn, segmentData);
                }

                // update resume record
                resumeRecord.NumberOfCommittedSectors += (ulong)(numberOfStripesToTransfer * volume.SectorsPerStripe);
                bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * volume.BytesPerSector;
                newVolume.WriteSectors(0, resumeRecord.GetBytes());

                stripeIndexInVolume += numberOfStripesToTransfer;
            }

            // we're done, let's restore the filesystem boot record
            byte[] filesystemBootRecord = newExtent.ReadSector(newExtent.TotalSectors - 1);
            newVolume.WriteSectors(0, filesystemBootRecord);
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

            VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
        }
Example #18
0
 public DynamicColumn(DynamicDiskExtent extent)
 {
     m_extents.Add(extent);
 }
Example #19
0
        /// <summary>
        /// Update the database to point to the new extent location (same or different disk)
        /// </summary>
        public static void UpdateExtentLocation(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent relocatedExtent)
        {
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(relocatedExtent.Disk);

            DiskRecord   targetDiskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
            VolumeRecord volumeRecord     = database.FindVolumeByVolumeGuid(volume.VolumeGuid);

            List <DatabaseRecord> records               = new List <DatabaseRecord>();
            ExtentRecord          sourceExtentRecord    = database.FindExtentByExtentID(relocatedExtent.ExtentID);
            ExtentRecord          relocatedExtentRecord = (ExtentRecord)sourceExtentRecord.Clone();

            relocatedExtentRecord.DiskId        = targetDiskRecord.DiskId;
            relocatedExtentRecord.DiskOffsetLBA = (ulong)PublicRegionHelper.TranslateToPublicRegionLBA(relocatedExtent.FirstSector, privateHeader);
            records.Add(relocatedExtentRecord);

            // we should update the disk records
            foreach (DynamicDiskExtent extent in volume.Extents)
            {
                DiskRecord diskRecord = database.FindDiskByDiskID(relocatedExtentRecord.DiskId);
                // there could be multiple extents on the same disk, make sure we only add each disk once
                if (!records.Contains(diskRecord))
                {
                    diskRecord = (DiskRecord)diskRecord.Clone();
                    records.Add(diskRecord);
                }
            }

            // when moving to a new disk, we should update the new disk record as well
            if (!records.Contains(targetDiskRecord))
            {
                records.Add(targetDiskRecord.Clone());
            }

            database.UpdateDatabase(records);
        }
Example #20
0
 public DynamicColumn(DynamicDiskExtent extent)
 {
     m_extents.Add(extent);
     m_bytesPerSector = GetBytesPerSector(m_extents, DefaultBytesPerSector);
 }
Example #21
0
 public SimpleVolume(DynamicDiskExtent extent, Guid volumeGuid, Guid diskGroupGuid) : base(volumeGuid, diskGroupGuid)
 {
     m_extent = extent;
 }
        /// <summary>
        /// Move extent to a new location on the same disk
        /// </summary>
        public static void MoveExtentWithinSameDisk(List<DynamicDisk> disks, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            MoveExtentOperationBootRecord resumeRecord = new MoveExtentOperationBootRecord();
            // If there will be a power failure during the move, a RAID volume will resync during boot,
            // To prevent destruction of the data, we temporarily convert the array to striped volume
            if (volume is Raid5Volume)
            {
                VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid);
                resumeRecord.RestoreRAID5 = true;
            }

            // We want to write our own volume boot sector for recovery purposes, so we must find where to backup the old boot sector.
            // We don't want to store the backup in the range of the existing or relocated extent, because then we would have to move
            // the backup around during the move operation, other options include:
            // 1. Store it between sectors 1-62 (cons: Could be in use, Windows occasionally start a volume from sector 1)
            // 2. Find an easily compressible sector (e.g. zero-filled) within the existing extent, overwrite it with the backup, and restore it when the operation is done.
            // 3. use the LDM private region to store the sector.

            DynamicDisk dynamicDisk = DynamicDisk.ReadFromDisk(relocatedExtent.Disk);
            // Note: backupSectorIndex will be from the beginning of the private region while backupBufferStartSector will be from the end
            // so there is no need to allocate them
            long backupSectorIndex = DynamicDiskHelper.FindUnusedSectorInPrivateRegion(dynamicDisk);

            resumeRecord.VolumeGuid = volume.VolumeGuid;
            resumeRecord.NumberOfCommittedSectors = 0;
            resumeRecord.ExtentID = sourceExtent.ExtentID;
            resumeRecord.OldStartSector = (ulong)sourceExtent.FirstSector;
            resumeRecord.NewStartSector = (ulong)relocatedExtent.FirstSector;
            resumeRecord.BootRecordBackupSector = (ulong)backupSectorIndex;

            long distanceLBA = Math.Abs((long)resumeRecord.NewStartSector - (long)resumeRecord.OldStartSector);
            if (distanceLBA < MoveHelper.BufferedModeThresholdLBA)
            {
                long backupBufferStartSector = DynamicDiskHelper.FindUnusedRegionInPrivateRegion(dynamicDisk, BackupBufferSizeLBA);
                if (backupBufferStartSector == -1)
                {
                    throw new Exception("Private region is full");
                }

                if (backupBufferStartSector <= backupSectorIndex)
                {
                    throw new Exception("Private region structure is unknown");
                }
                resumeRecord.BackupBufferStartSector = (ulong)backupBufferStartSector;
                resumeRecord.BackupBufferSizeLBA = BackupBufferSizeLBA;
            }

            // Backup the first sector of the first extent
            // (We replace the filesystem boot record with our own sector for recovery purposes)
            byte[] filesystemBootRecord = volume.ReadSector(0);
            relocatedExtent.Disk.WriteSectors(backupSectorIndex, filesystemBootRecord);

            // we write the resume record instead of the boot record
            volume.WriteSectors(0, resumeRecord.GetBytes());

            if (sourceExtent.FirstSector < relocatedExtent.FirstSector)
            {
                // move right
                MoveExtentRight(disks, volume, resumeRecord, ref bytesCopied);
            }
            else
            { 
                // move left

                // we write the resume record at the new location as well (to be able to resume if a power failure will occur immediately after updating the database)
                relocatedExtent.WriteSectors(0, resumeRecord.GetBytes());
                DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);
                dynamicRelocatedExtent.Name = sourceExtent.Name;
                dynamicRelocatedExtent.DiskGuid = sourceExtent.DiskGuid;
                VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);
                int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, sourceExtent.ExtentID);
                // get the updated volume (we just moved an extent)
                volume = DynamicVolumeHelper.GetVolumeByGuid(disks, volume.VolumeGuid);
                MoveExtentLeft(disks, volume, resumeRecord, ref bytesCopied);
            }
        }