/* Copyright (C) 2014 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. */ using System; using System.Collections.Generic; using System.Text; using DiskAccessLibrary.LogicalDiskManager; using Utilities; namespace DiskAccessLibrary { public class MoveHelper { /// /// When a user want to move an extent one sector to the left/right (e.g. for alignment purposes), /// the regular operation method will mandate reading and writing one sector at a time, /// this can be extremely slow, and to avoid this, we use free space on the disk to "buffer" the data read. /// (this will allow us to recover from a power failure) /// public const int BufferedModeThresholdLBA = 64; 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 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; } } } }