Finally implemented growing in the double indirect range.
Tested, and works - we are now feature complete (and now for the bugs... :-)). git-svn-id: file:///srv/svn/repos/haiku/trunk/current@974 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -985,6 +985,7 @@ Inode::FillGapWithZeros(off_t pos,off_t newSize)
|
|||||||
|
|
||||||
/** Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
|
/** Allocates NUM_ARRAY_BLOCKS blocks, and clears their contents. Growing
|
||||||
* the indirect and double indirect range uses this method.
|
* the indirect and double indirect range uses this method.
|
||||||
|
* The allocated block_run is saved in "run"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
@@ -1001,7 +1002,7 @@ Inode::AllocateBlockArray(Transaction *transaction, block_run &run)
|
|||||||
CachedBlock cached(fVolume);
|
CachedBlock cached(fVolume);
|
||||||
off_t block = fVolume->ToBlock(run);
|
off_t block = fVolume->ToBlock(run);
|
||||||
|
|
||||||
for (int32 i = 1;i < run.length;i++) {
|
for (int32 i = 0;i < run.length;i++) {
|
||||||
block_run *runs = (block_run *)cached.SetTo(block + i, true);
|
block_run *runs = (block_run *)cached.SetTo(block + i, true);
|
||||||
if (runs == NULL)
|
if (runs == NULL)
|
||||||
return B_IO_ERROR;
|
return B_IO_ERROR;
|
||||||
@@ -1115,7 +1116,7 @@ Inode::GrowStream(Transaction *transaction, off_t size)
|
|||||||
|
|
||||||
data->max_indirect_range = data->max_direct_range;
|
data->max_indirect_range = data->max_direct_range;
|
||||||
// insert the block_run in the first block
|
// insert the block_run in the first block
|
||||||
runs = (block_run *)cached.SetTo(data->indirect, true);
|
runs = (block_run *)cached.SetTo(data->indirect);
|
||||||
} else {
|
} else {
|
||||||
uint32 numberOfRuns = fVolume->BlockSize() / sizeof(block_run);
|
uint32 numberOfRuns = fVolume->BlockSize() / sizeof(block_run);
|
||||||
block = fVolume->ToBlock(data->indirect);
|
block = fVolume->ToBlock(data->indirect);
|
||||||
@@ -1148,7 +1149,7 @@ Inode::GrowStream(Transaction *transaction, off_t size)
|
|||||||
} else {
|
} else {
|
||||||
runs[free] = run;
|
runs[free] = run;
|
||||||
}
|
}
|
||||||
data->max_indirect_range += run.length * fVolume->BlockSize();
|
data->max_indirect_range += run.length << fVolume->BlockShift();
|
||||||
data->size = blocksNeeded > 0 ? data->max_indirect_range : size;
|
data->size = blocksNeeded > 0 ? data->max_indirect_range : size;
|
||||||
|
|
||||||
cached.WriteBack(transaction);
|
cached.WriteBack(transaction);
|
||||||
@@ -1159,9 +1160,6 @@ Inode::GrowStream(Transaction *transaction, off_t size)
|
|||||||
// Double indirect block range
|
// Double indirect block range
|
||||||
|
|
||||||
if (data->size <= data->max_double_indirect_range || !data->max_double_indirect_range) {
|
if (data->size <= data->max_double_indirect_range || !data->max_double_indirect_range) {
|
||||||
FATAL(("growing in the double indirect range is not yet implemented!\n"));
|
|
||||||
// ToDo: implement growing into the double indirect range, please!
|
|
||||||
|
|
||||||
while ((run.length % NUM_ARRAY_BLOCKS) != 0) {
|
while ((run.length % NUM_ARRAY_BLOCKS) != 0) {
|
||||||
// The number of allocated blocks isn't a multiple of NUM_ARRAY_BLOCKS,
|
// The number of allocated blocks isn't a multiple of NUM_ARRAY_BLOCKS,
|
||||||
// so we have to change this. This can happen the first time the stream
|
// so we have to change this. This can happen the first time the stream
|
||||||
@@ -1176,20 +1174,16 @@ Inode::GrowStream(Transaction *transaction, off_t size)
|
|||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// Are there any blocks left in the run? If not, allocate a new one
|
blocksNeeded += rest;
|
||||||
if (run.length == 0) {
|
blocks = (blocksNeeded + NUM_ARRAY_BLOCKS - 1) & ~(NUM_ARRAY_BLOCKS - 1);
|
||||||
int32 needed = (blocksNeeded + NUM_ARRAY_BLOCKS - 1) & ~(NUM_ARRAY_BLOCKS - 1);
|
minimum = NUM_ARRAY_BLOCKS;
|
||||||
// we make sure here that we have at minimum NUM_ARRAY_BLOCKS allocated,
|
// we make sure here that we have at minimum NUM_ARRAY_BLOCKS allocated,
|
||||||
// if this call succeeds, so we don't run into an endless loop
|
// so if the allocation succeeds, we don't run into an endless loop
|
||||||
status = fVolume->Allocate(transaction, this, needed, run, NUM_ARRAY_BLOCKS);
|
|
||||||
if (status < B_OK)
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedBlock cached(fVolume);
|
// Are there any blocks left in the run? If not, allocate a new one
|
||||||
block_run *runs = NULL;
|
if (run.length == 0)
|
||||||
int32 needed,index;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// if there is no double indirect block yet, create one
|
// if there is no double indirect block yet, create one
|
||||||
if (data->double_indirect.IsZero()) {
|
if (data->double_indirect.IsZero()) {
|
||||||
@@ -1198,26 +1192,107 @@ Inode::GrowStream(Transaction *transaction, off_t size)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
data->max_double_indirect_range = data->max_indirect_range;
|
data->max_double_indirect_range = data->max_indirect_range;
|
||||||
needed = run.length / NUM_ARRAY_BLOCKS;
|
|
||||||
index = 0;
|
|
||||||
} else {
|
|
||||||
// calculate array position where to insert the new blocks into
|
|
||||||
|
|
||||||
//index =
|
|
||||||
//data->max_double_indirect_range = ;
|
|
||||||
needed = run.length / NUM_ARRAY_BLOCKS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate new block arrays
|
// calculate the index where to insert the new blocks
|
||||||
block_run *array;
|
|
||||||
|
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
|
||||||
|
int32 indirectSize = ((1L << INDIRECT_BLOCKS_SHIFT) << fVolume->BlockShift())
|
||||||
|
* runsPerBlock;
|
||||||
|
int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift();
|
||||||
|
int32 runsPerArray = runsPerBlock << ARRAY_BLOCKS_SHIFT;
|
||||||
|
|
||||||
|
off_t start = data->max_double_indirect_range - data->max_indirect_range;
|
||||||
|
int32 indirectIndex = start / indirectSize;
|
||||||
|
int32 index = start / directSize;
|
||||||
|
|
||||||
|
// distribute the blocks to the array and allocate
|
||||||
|
// new array blocks when needed
|
||||||
|
|
||||||
|
CachedBlock cached(fVolume);
|
||||||
|
CachedBlock cachedDirect(fVolume);
|
||||||
|
block_run *array = NULL;
|
||||||
|
uint32 runLength = run.length;
|
||||||
|
|
||||||
|
// ToDo: the following code is commented - it could be used to
|
||||||
|
// preallocate all needed block arrays to see in advance if the
|
||||||
|
// allocation will succeed.
|
||||||
|
// I will probably remove it later, because it's no perfect solution
|
||||||
|
// either: if the allocation was broken up before (blocksNeeded != 0),
|
||||||
|
// it doesn't guarantee anything.
|
||||||
|
// And since failing in this case is not that common, it doesn't have
|
||||||
|
// to be optimized in that way.
|
||||||
|
// Anyway, I wanted to have it in CVS - all those lines, and they will
|
||||||
|
// be removed soon :-)
|
||||||
|
/*
|
||||||
|
// allocate new block arrays if needed
|
||||||
|
|
||||||
|
off_t block = -1;
|
||||||
|
|
||||||
for (int32 i = 0;i < needed;i++) {
|
for (int32 i = 0;i < needed;i++) {
|
||||||
status = AllocateBlockArray(transaction, array[i]);
|
// get the block to insert the run into
|
||||||
|
block = fVolume->ToBlock(data->double_indirect) + i + indirectIndex / runsPerBlock;
|
||||||
|
if (cached.BlockNumber() != block)
|
||||||
|
array = (block_run *)cached.SetTo(block);
|
||||||
|
|
||||||
|
if (array == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
status = AllocateBlockArray(transaction, array[i + indirectIndex % runsPerBlock]);
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
// place new array entry somewhere...
|
|
||||||
}
|
}
|
||||||
//continue;
|
*/
|
||||||
|
|
||||||
|
while (run.length) {
|
||||||
|
// get the indirect array block
|
||||||
|
if (array == NULL) {
|
||||||
|
if (cached.Block() != NULL
|
||||||
|
&& cached.WriteBack(transaction) < B_OK)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
array = (block_run *)cached.SetTo(fVolume->ToBlock(data->double_indirect) +
|
||||||
|
indirectIndex / runsPerBlock);
|
||||||
|
if (array == NULL)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
// do we need a new array block?
|
||||||
|
if (array[indirectIndex % runsPerBlock].IsZero()) {
|
||||||
|
status = AllocateBlockArray(transaction, array[indirectIndex % runsPerBlock]);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_run *runs = (block_run *)cachedDirect.SetTo(
|
||||||
|
fVolume->ToBlock(array[indirectIndex % runsPerBlock])
|
||||||
|
+ index / runsPerBlock);
|
||||||
|
if (runs == NULL)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
do {
|
||||||
|
// insert the block_run into the array
|
||||||
|
runs[index % runsPerBlock] = run;
|
||||||
|
runs[index % runsPerBlock].length = NUM_ARRAY_BLOCKS;
|
||||||
|
|
||||||
|
// alter the remaining block_run
|
||||||
|
run.start += NUM_ARRAY_BLOCKS;
|
||||||
|
run.length -= NUM_ARRAY_BLOCKS;
|
||||||
|
} while ((++index % runsPerBlock) != 0 && run.length);
|
||||||
|
|
||||||
|
if (cachedDirect.WriteBack(transaction) < B_OK)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
} while ((index % runsPerArray) != 0 && run.length);
|
||||||
|
|
||||||
|
if (++indirectIndex % runsPerBlock == 0)
|
||||||
|
array = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->max_double_indirect_range += runLength << fVolume->BlockShift();
|
||||||
|
data->size = blocksNeeded > 0 ? data->max_double_indirect_range : size;
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_ERROR(EFBIG);
|
RETURN_ERROR(EFBIG);
|
||||||
@@ -1234,7 +1309,8 @@ Inode::FreeStaticStreamArray(Transaction *transaction,int32 level,block_run run,
|
|||||||
{
|
{
|
||||||
int32 indirectSize;
|
int32 indirectSize;
|
||||||
if (level == 0)
|
if (level == 0)
|
||||||
indirectSize = (16 << fVolume->BlockShift()) * (fVolume->BlockSize() / sizeof(block_run));
|
indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + fVolume->BlockShift()))
|
||||||
|
* (fVolume->BlockSize() / sizeof(block_run));
|
||||||
else if (level == 1)
|
else if (level == 1)
|
||||||
indirectSize = 4 << fVolume->BlockShift();
|
indirectSize = 4 << fVolume->BlockShift();
|
||||||
|
|
||||||
|
|||||||
@@ -276,7 +276,8 @@ Stream<Cache>::FindBlockRun(off_t pos,block_run &run,off_t &offset)
|
|||||||
Cache cached(fVolume);
|
Cache cached(fVolume);
|
||||||
|
|
||||||
off_t start = pos - data->max_indirect_range;
|
off_t start = pos - data->max_indirect_range;
|
||||||
int32 indirectSize = (16 << fVolume->BlockShift()) * (fVolume->BlockSize() / sizeof(block_run));
|
int32 indirectSize = (1L << (INDIRECT_BLOCKS_SHIFT + fVolume->BlockShift()))
|
||||||
|
* (fVolume->BlockSize() / sizeof(block_run));
|
||||||
int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift();
|
int32 directSize = NUM_ARRAY_BLOCKS << fVolume->BlockShift();
|
||||||
int32 index = start / indirectSize;
|
int32 index = start / indirectSize;
|
||||||
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
|
int32 runsPerBlock = fVolume->BlockSize() / sizeof(block_run);
|
||||||
|
|||||||
@@ -88,9 +88,10 @@ struct data_stream
|
|||||||
|
|
||||||
// This defines the size of the indirect and double indirect
|
// This defines the size of the indirect and double indirect
|
||||||
// blocks. Note: the code may not work correctly at some places
|
// blocks. Note: the code may not work correctly at some places
|
||||||
// if this value is changed (most notably Inode::FindBlockRun()).
|
// if this value is changed (it's not tested).
|
||||||
// In any way, the value must be a power of 2.
|
#define NUM_ARRAY_BLOCKS 4
|
||||||
#define NUM_ARRAY_BLOCKS 4
|
#define ARRAY_BLOCKS_SHIFT 2
|
||||||
|
#define INDIRECT_BLOCKS_SHIFT (ARRAY_BLOCKS_SHIFT + ARRAY_BLOCKS_SHIFT)
|
||||||
|
|
||||||
//**************************************
|
//**************************************
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user