public static void AddDiskToRaid5Volume(List <DynamicDisk> disks, Raid5Volume volume, DiskExtent newExtent, ref long bytesCopied) { DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid); if (database == null) { throw new DatabaseNotFoundException(); } // If there will be a power failure during the conversion, our RAID volume will resync during boot, // To prevent destruction of the data, we temporarily convert the array to striped volume VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid); ulong newExtentID = VolumeManagerDatabaseHelper.AddNewExtentToVolume(database, volume, newExtent); // Backup the first sector of the first extent to the last sector of the new extent // (We replace the filesystem boot record with our own sector for recovery purposes) byte[] filesystemBootRecord = volume.Extents[0].ReadSector(0); newExtent.WriteSectors(newExtent.TotalSectors - 1, filesystemBootRecord); AddDiskOperationBootRecord resumeRecord = new AddDiskOperationBootRecord(); resumeRecord.VolumeGuid = volume.VolumeGuid; PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk); // privateHeader cannot be null at this point resumeRecord.NumberOfCommittedSectors = 0; // we use volume.WriteSectors so that the parity information will be update // this way, we could recover the first sector of each extent if a disk will fail volume.WriteSectors(0, resumeRecord.GetBytes()); ResumeAddDiskToRaid5Volume(disks, volume, new DynamicDiskExtent(newExtent, newExtentID), resumeRecord, ref bytesCopied); }
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); }
public static void MoveExtentDataLeft(Volume volume, DiskExtent sourceExtent, DiskExtent relocatedExtent, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied) { // we make sure no data will be overwritten too soon: long distanceLBA = (long)(resumeRecord.OldStartSector - resumeRecord.NewStartSector); bool bufferedMode = false; if (distanceLBA < BufferedModeThresholdLBA) { bufferedMode = true; } int transferSizeLBA; if (bufferedMode) { transferSizeLBA = (int)resumeRecord.BackupBufferSizeLBA;; } else { transferSizeLBA = (int)Math.Min(Settings.MaximumTransferSizeLBA, distanceLBA); } // move the data for (long sectorIndex = (long)resumeRecord.NumberOfCommittedSectors; sectorIndex < relocatedExtent.TotalSectors; sectorIndex += transferSizeLBA) { long sectorsLeft = relocatedExtent.TotalSectors - sectorIndex; int sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft); byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead); if (bufferedMode) { // we write the data to the buffer for recovery purposes relocatedExtent.Disk.WriteSectors((long)resumeRecord.BackupBufferStartSector, data); resumeRecord.RestoreFromBuffer = true; relocatedExtent.WriteSectors(0, resumeRecord.GetBytes()); } relocatedExtent.WriteSectors(sectorIndex, data); // update the resume record resumeRecord.RestoreFromBuffer = false; resumeRecord.NumberOfCommittedSectors += (ulong)sectorsToRead; volume.WriteSectors(0, resumeRecord.GetBytes()); bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * sourceExtent.BytesPerSector; } }
public static void MoveExtentDataRight(Volume volume, DiskExtent sourceExtent, DiskExtent relocatedExtent, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied) { // we make sure no data will be overwritten too soon: long distanceLBA = (long)(resumeRecord.NewStartSector - resumeRecord.OldStartSector); bool bufferedMode = false; if (distanceLBA < BufferedModeThresholdLBA) { bufferedMode = true; } int transferSizeLBA; if (bufferedMode) { transferSizeLBA = (int)resumeRecord.BackupBufferSizeLBA; } else { transferSizeLBA = (int)Math.Min(Settings.MaximumTransferSizeLBA, distanceLBA); } // move the data for (long readCount = (long)resumeRecord.NumberOfCommittedSectors; readCount < relocatedExtent.TotalSectors; readCount += transferSizeLBA) { // we read (and write) from the end of the extent and progress to the left long sectorsLeft = relocatedExtent.TotalSectors - readCount; int sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft); long sectorIndex = relocatedExtent.TotalSectors - readCount - sectorsToRead; byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead); if (bufferedMode) { // we write the data to the buffer for recovery purposes relocatedExtent.Disk.WriteSectors((long)resumeRecord.BackupBufferStartSector, data); resumeRecord.RestoreFromBuffer = true; // Note: if the extent we move is the first in the volume, we will write the resume record to // the source extent, which is the one that the database is still referring to volume.WriteSectors(0, resumeRecord.GetBytes()); } relocatedExtent.WriteSectors(sectorIndex, data); // update the resume record resumeRecord.RestoreFromBuffer = false; resumeRecord.NumberOfCommittedSectors += (ulong)sectorsToRead; volume.WriteSectors(0, resumeRecord.GetBytes()); bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * sourceExtent.BytesPerSector; } }
public override void WriteSectors(long sectorIndex, byte[] data) { m_extent.WriteSectors(sectorIndex, data); }
/// <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); } }