Rewrote the log entry writing so that it should be BFS compatible - not yet tested for
compatibility, though! Writing is now combined into a few writev_pos() function calls to speed up log writing. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14415 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -11,23 +11,287 @@
|
|||||||
|
|
||||||
#include <Drivers.h>
|
#include <Drivers.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
#include <util/Stack.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
struct log_entry : public DoublyLinkedListLinkImpl<log_entry> {
|
struct run_array {
|
||||||
uint16 start;
|
int32 count;
|
||||||
uint16 length;
|
union {
|
||||||
uint32 cached_blocks;
|
int32 max_runs;
|
||||||
Journal *journal;
|
int32 block_count;
|
||||||
|
};
|
||||||
|
block_run runs[0];
|
||||||
|
|
||||||
|
int32 CountRuns() const { return BFS_ENDIAN_TO_HOST_INT32(count); }
|
||||||
|
int32 MaxRuns() const { return BFS_ENDIAN_TO_HOST_INT32(max_runs); }
|
||||||
|
const block_run &RunAt(int32 i) const { return runs[i]; }
|
||||||
|
|
||||||
|
static int32 MaxRuns(int32 blockSize)
|
||||||
|
{ return (blockSize - sizeof(run_array)) / sizeof(block_run); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RunArrays {
|
||||||
|
public:
|
||||||
|
RunArrays(Journal *journal);
|
||||||
|
~RunArrays();
|
||||||
|
|
||||||
|
uint32 Length() const { return fLength; }
|
||||||
|
|
||||||
|
status_t Insert(off_t blockNumber);
|
||||||
|
|
||||||
|
run_array *ArrayAt(int32 i) { return fArrays.Array()[i]; }
|
||||||
|
int32 CountArrays() const { return fArrays.CountItems(); }
|
||||||
|
|
||||||
|
int32 MaxArrayLength();
|
||||||
|
void PrepareForWriting();
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _AddArray();
|
||||||
|
bool _ContainsRun(block_run &run);
|
||||||
|
bool _AddRun(block_run &run);
|
||||||
|
|
||||||
|
Journal *fJournal;
|
||||||
|
uint32 fLength;
|
||||||
|
Stack<run_array *> fArrays;
|
||||||
|
run_array *fLastArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LogEntry : public DoublyLinkedListLinkImpl<LogEntry> {
|
||||||
|
public:
|
||||||
|
LogEntry(Journal *journal, uint32 logStart, uint32 length);
|
||||||
|
~LogEntry();
|
||||||
|
|
||||||
|
uint32 Start() const { return fStart; }
|
||||||
|
uint32 Length() const { return fLength; }
|
||||||
|
|
||||||
|
Journal *GetJournal() { return fJournal; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Journal *fJournal;
|
||||||
|
uint32 fStart;
|
||||||
|
uint32 fLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_to_iovec(iovec *vecs, int32 &index, int32 max, const void *address, size_t size)
|
||||||
|
{
|
||||||
|
if (index > 0
|
||||||
|
&& (addr_t)vecs[index - 1].iov_base + vecs[index - 1].iov_len == (addr_t)address) {
|
||||||
|
// the iovec can be combined with the previous one
|
||||||
|
vecs[index - 1].iov_len += size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == max)
|
||||||
|
panic("no more space for iovecs!");
|
||||||
|
|
||||||
|
// we need to start a new iovec
|
||||||
|
vecs[index].iov_base = const_cast<void *>(address);
|
||||||
|
vecs[index].iov_len = size;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
LogEntry::LogEntry(Journal *journal, uint32 start, uint32 length)
|
||||||
|
:
|
||||||
|
fJournal(journal),
|
||||||
|
fStart(start),
|
||||||
|
fLength(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogEntry::~LogEntry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
RunArrays::RunArrays(Journal *journal)
|
||||||
|
:
|
||||||
|
fJournal(journal),
|
||||||
|
fLength(0),
|
||||||
|
fArrays(),
|
||||||
|
fLastArray(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RunArrays::~RunArrays()
|
||||||
|
{
|
||||||
|
run_array *array;
|
||||||
|
while (fArrays.Pop(&array))
|
||||||
|
free(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
RunArrays::_ContainsRun(block_run &run)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < CountArrays(); i++) {
|
||||||
|
run_array *array = ArrayAt(i);
|
||||||
|
|
||||||
|
for (int32 j = 0; j < array->CountRuns(); j++) {
|
||||||
|
block_run &arrayRun = array->runs[j];
|
||||||
|
if (run.AllocationGroup() != arrayRun.AllocationGroup())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (run.Start() >= arrayRun.Start()
|
||||||
|
&& run.Start() + run.Length() <= arrayRun.Start() + arrayRun.Length())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Adds the specified block_run into the array.
|
||||||
|
* Note: it doesn't support overlapping - it must only be used
|
||||||
|
* with block_runs of length 1!
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
RunArrays::_AddRun(block_run &run)
|
||||||
|
{
|
||||||
|
ASSERT(run.length == 1);
|
||||||
|
|
||||||
|
// search for an existing adjacent block_run
|
||||||
|
// ToDo: this could be improved by sorting and a binary search
|
||||||
|
|
||||||
|
for (int32 i = 0; i < CountArrays(); i++) {
|
||||||
|
run_array *array = ArrayAt(i);
|
||||||
|
|
||||||
|
for (int32 j = 0; j < array->CountRuns(); j++) {
|
||||||
|
block_run &arrayRun = array->runs[j];
|
||||||
|
if (run.AllocationGroup() != arrayRun.AllocationGroup())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (run.Start() == arrayRun.Start() + arrayRun.Length()) {
|
||||||
|
// matches the end
|
||||||
|
arrayRun.length = HOST_ENDIAN_TO_BFS_INT16(arrayRun.Length() + 1);
|
||||||
|
array->block_count++;
|
||||||
|
fLength++;
|
||||||
|
return true;
|
||||||
|
} else if (run.start + 1 == arrayRun.start) {
|
||||||
|
// matches the start
|
||||||
|
arrayRun.start = run.start;
|
||||||
|
arrayRun.length = HOST_ENDIAN_TO_BFS_INT16(arrayRun.Length() + 1);
|
||||||
|
array->block_count++;
|
||||||
|
fLength++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no entry found, add new to the last array
|
||||||
|
|
||||||
|
if (fLastArray == NULL || fLastArray->CountRuns() == fLastArray->MaxRuns())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fLastArray->runs[fLastArray->CountRuns()] = run;
|
||||||
|
fLastArray->count = HOST_ENDIAN_TO_BFS_INT16(fLastArray->CountRuns() + 1);
|
||||||
|
fLastArray->block_count++;
|
||||||
|
fLength++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RunArrays::_AddArray()
|
||||||
|
{
|
||||||
|
int32 blockSize = fJournal->GetVolume()->BlockSize();
|
||||||
|
run_array *array = (run_array *)malloc(blockSize);
|
||||||
|
if (array == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
if (fArrays.Push(array) != B_OK) {
|
||||||
|
free(array);
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(array, 0, blockSize);
|
||||||
|
array->block_count = 1;
|
||||||
|
fLastArray = array;
|
||||||
|
fLength++;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
RunArrays::Insert(off_t blockNumber)
|
||||||
|
{
|
||||||
|
Volume *volume = fJournal->GetVolume();
|
||||||
|
block_run run = volume->ToBlockRun(blockNumber);
|
||||||
|
|
||||||
|
if (fLastArray != NULL) {
|
||||||
|
// check if the block is already in the array
|
||||||
|
if (_ContainsRun(run))
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert block into array
|
||||||
|
|
||||||
|
if (!_AddRun(run)) {
|
||||||
|
// array is full
|
||||||
|
if (_AddArray() != B_OK)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// insert entry manually, because _AddRun() would search the
|
||||||
|
// all arrays again for a free spot
|
||||||
|
fLastArray->runs[0] = run;
|
||||||
|
fLastArray->count = HOST_ENDIAN_TO_BFS_INT16(1);
|
||||||
|
fLastArray->block_count++;
|
||||||
|
fLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
RunArrays::MaxArrayLength()
|
||||||
|
{
|
||||||
|
int32 max = 0;
|
||||||
|
for (int32 i = 0; i < CountArrays(); i++) {
|
||||||
|
if (ArrayAt(i)->block_count > max)
|
||||||
|
max = ArrayAt(i)->block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
RunArrays::PrepareForWriting()
|
||||||
|
{
|
||||||
|
int32 blockSize = fJournal->GetVolume()->BlockSize();
|
||||||
|
|
||||||
|
for (int32 i = 0; i < CountArrays(); i++) {
|
||||||
|
ArrayAt(i)->max_runs = HOST_ENDIAN_TO_BFS_INT32(run_array::MaxRuns(blockSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
Journal::Journal(Volume *volume)
|
Journal::Journal(Volume *volume)
|
||||||
:
|
:
|
||||||
fVolume(volume),
|
fVolume(volume),
|
||||||
fLock("bfs journal"),
|
fLock("bfs journal"),
|
||||||
fOwner(NULL),
|
fOwner(NULL),
|
||||||
fArray(volume->BlockSize()),
|
|
||||||
fLogSize(volume->Log().length),
|
fLogSize(volume->Log().length),
|
||||||
fMaxTransactionSize(fLogSize / 4 - 5),
|
fMaxTransactionSize(fLogSize / 4 - 5),
|
||||||
fUsed(0),
|
fUsed(0),
|
||||||
@@ -59,10 +323,22 @@ Journal::InitCheck()
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Journal::CheckLogEntry(int32 count, const off_t *array)
|
Journal::_CheckRunArray(const run_array *array)
|
||||||
{
|
{
|
||||||
// ToDo: check log entry integrity (block numbers and entry size)
|
int32 maxRuns = run_array::MaxRuns(fVolume->BlockSize());
|
||||||
PRINT(("Log entry has %ld entries (%Ld)\n", count, array[0]));
|
if (array->MaxRuns() != maxRuns
|
||||||
|
|| array->CountRuns() > maxRuns
|
||||||
|
|| array->CountRuns() <= 0) {
|
||||||
|
FATAL(("Log entry has broken header!\n"));
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < array->CountRuns(); i++) {
|
||||||
|
if (fVolume->ValidateBlockRun(array->RunAt(i)) != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINT(("Log entry has %ld entries (%Ld)\n", array->CountRuns()));
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,65 +349,50 @@ Journal::CheckLogEntry(int32 count, const off_t *array)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Journal::ReplayLogEntry(int32 *_start)
|
Journal::_ReplayRunArray(int32 *_start)
|
||||||
{
|
{
|
||||||
PRINT(("ReplayLogEntry(start = %ld)\n", *_start));
|
PRINT(("ReplayRunArray(start = %ld)\n", *_start));
|
||||||
|
|
||||||
off_t logOffset = fVolume->ToBlock(fVolume->Log());
|
off_t logOffset = fVolume->ToBlock(fVolume->Log());
|
||||||
off_t arrayBlock = (*_start % fLogSize) + fVolume->ToBlock(fVolume->Log());
|
off_t blockNumber = *_start % fLogSize;
|
||||||
int32 blockSize = fVolume->BlockSize();
|
int32 blockSize = fVolume->BlockSize();
|
||||||
int32 count = 1, valuesInBlock = blockSize / sizeof(off_t);
|
int32 count = 1;
|
||||||
int32 numArrayBlocks;
|
|
||||||
off_t blockNumber = 0;
|
CachedBlock cachedArray(fVolume);
|
||||||
bool first = true;
|
|
||||||
|
const run_array *array = (const run_array *)cachedArray.SetTo(logOffset + blockNumber);
|
||||||
|
if (array == NULL)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
if (_CheckRunArray(array) < B_OK)
|
||||||
|
return B_BAD_DATA;
|
||||||
|
|
||||||
|
blockNumber = (blockNumber + 1) % fLogSize;
|
||||||
|
|
||||||
CachedBlock cached(fVolume);
|
CachedBlock cached(fVolume);
|
||||||
|
for (int32 index = 0; index < array->CountRuns(); index++) {
|
||||||
|
const block_run &run = array->RunAt(index);
|
||||||
|
PRINT(("replay block run %lu:%u:%u in log at %Ld!\n", run.AllocationGroup(),
|
||||||
|
run.Start(), run.Length(), blockNumber));
|
||||||
|
|
||||||
while (count > 0) {
|
off_t offset = fVolume->ToOffset(run);
|
||||||
const off_t *array = (const off_t *)cached.SetTo(arrayBlock);
|
for (int32 i = 0; i < run.Length(); i++) {
|
||||||
if (array == NULL)
|
const uint8 *data = cached.SetTo(logOffset + blockNumber);
|
||||||
return B_IO_ERROR;
|
if (data == NULL)
|
||||||
|
|
||||||
int32 index = 0;
|
|
||||||
if (first) {
|
|
||||||
if (array[0] < 1 || array[0] >= fLogSize)
|
|
||||||
return B_BAD_DATA;
|
|
||||||
|
|
||||||
count = array[0];
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
numArrayBlocks = ((count + 1) * sizeof(off_t) + blockSize - 1) / blockSize;
|
|
||||||
blockNumber = (*_start + numArrayBlocks) % fLogSize;
|
|
||||||
// first real block in this log entry
|
|
||||||
*_start += count;
|
|
||||||
index++;
|
|
||||||
// the first entry in the first block is the number
|
|
||||||
// of blocks in that log entry
|
|
||||||
}
|
|
||||||
(*_start)++;
|
|
||||||
|
|
||||||
if (CheckLogEntry(count, array + 1) < B_OK)
|
|
||||||
return B_BAD_DATA;
|
|
||||||
|
|
||||||
CachedBlock cachedCopy(fVolume);
|
|
||||||
for (; index < valuesInBlock && count-- > 0; index++) {
|
|
||||||
PRINT(("replay block %Ld in log at %Ld!\n", array[index], blockNumber));
|
|
||||||
|
|
||||||
const uint8 *copy = cachedCopy.SetTo(logOffset + blockNumber);
|
|
||||||
if (copy == NULL)
|
|
||||||
RETURN_ERROR(B_IO_ERROR);
|
RETURN_ERROR(B_IO_ERROR);
|
||||||
|
|
||||||
|
dprintf("replay block: %Ld\n", fVolume->ToBlock(run) + i);
|
||||||
ssize_t written = write_pos(fVolume->Device(),
|
ssize_t written = write_pos(fVolume->Device(),
|
||||||
array[index] << fVolume->BlockShift(), copy, blockSize);
|
offset + (i * blockSize), data, blockSize);
|
||||||
if (written != blockSize)
|
if (written != blockSize)
|
||||||
RETURN_ERROR(B_IO_ERROR);
|
RETURN_ERROR(B_IO_ERROR);
|
||||||
|
|
||||||
blockNumber = (blockNumber + 1) % fLogSize;
|
blockNumber = (blockNumber + 1) % fLogSize;
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
arrayBlock++;
|
|
||||||
if (arrayBlock > fVolume->ToBlock(fVolume->Log()) + fLogSize)
|
|
||||||
arrayBlock = fVolume->ToBlock(fVolume->Log());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*_start += count;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +422,7 @@ Journal::ReplayLog()
|
|||||||
}
|
}
|
||||||
lastStart = start;
|
lastStart = start;
|
||||||
|
|
||||||
status_t status = ReplayLogEntry(&start);
|
status_t status = _ReplayRunArray(&start);
|
||||||
if (status < B_OK) {
|
if (status < B_OK) {
|
||||||
FATAL(("replaying log entry from %ld failed: %s\n", start, strerror(status)));
|
FATAL(("replaying log entry from %ld failed: %s\n", start, strerror(status)));
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -185,22 +446,26 @@ Journal::ReplayLog()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
Journal::blockNotify(int32 transactionID, void *arg)
|
Journal::_blockNotify(int32 transactionID, void *arg)
|
||||||
{
|
{
|
||||||
log_entry *logEntry = (log_entry *)arg;
|
LogEntry *logEntry = (LogEntry *)arg;
|
||||||
|
|
||||||
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
|
PRINT(("Log entry %p has been finished, transaction ID = %ld\n", logEntry, transactionID));
|
||||||
|
|
||||||
Journal *journal = logEntry->journal;
|
Journal *journal = logEntry->GetJournal();
|
||||||
disk_super_block &superBlock = journal->fVolume->SuperBlock();
|
disk_super_block &superBlock = journal->fVolume->SuperBlock();
|
||||||
bool update = false;
|
bool update = false;
|
||||||
|
|
||||||
// Set log_start pointer if possible...
|
// Set log_start pointer if possible...
|
||||||
|
|
||||||
|
journal->fEntriesLock.Lock();
|
||||||
|
|
||||||
if (logEntry == journal->fEntries.First()) {
|
if (logEntry == journal->fEntries.First()) {
|
||||||
log_entry *next = journal->fEntries.GetNext(logEntry);
|
LogEntry *next = journal->fEntries.GetNext(logEntry);
|
||||||
if (next != NULL) {
|
if (next != NULL) {
|
||||||
int32 length = next->start - logEntry->start;
|
int32 length = next->Start() - logEntry->Start();
|
||||||
|
// log entries inbetween could have been already released, so
|
||||||
|
// we can't just use LogEntry::Length() here
|
||||||
superBlock.log_start = (superBlock.log_start + length) % journal->fLogSize;
|
superBlock.log_start = (superBlock.log_start + length) % journal->fLogSize;
|
||||||
} else
|
} else
|
||||||
superBlock.log_start = journal->fVolume->LogEnd();
|
superBlock.log_start = journal->fVolume->LogEnd();
|
||||||
@@ -208,12 +473,11 @@ Journal::blockNotify(int32 transactionID, void *arg)
|
|||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
journal->fEntriesLock.Lock();
|
journal->fUsed -= logEntry->Length();
|
||||||
journal->fUsed -= logEntry->length;
|
|
||||||
journal->fEntries.Remove(logEntry);
|
journal->fEntries.Remove(logEntry);
|
||||||
journal->fEntriesLock.Unlock();
|
journal->fEntriesLock.Unlock();
|
||||||
|
|
||||||
free(logEntry);
|
delete logEntry;
|
||||||
|
|
||||||
// update the super block, and change the disk's state, if necessary
|
// update the super block, and change the disk's state, if necessary
|
||||||
|
|
||||||
@@ -235,21 +499,34 @@ Journal::blockNotify(int32 transactionID, void *arg)
|
|||||||
status_t
|
status_t
|
||||||
Journal::WriteLogEntry()
|
Journal::WriteLogEntry()
|
||||||
{
|
{
|
||||||
|
// ToDo: in case of a failure, we need a backup plan like writing all
|
||||||
|
// changed blocks back to disk immediately
|
||||||
|
|
||||||
fTransactionsInEntry = 0;
|
fTransactionsInEntry = 0;
|
||||||
fHasChangedBlocks = false;
|
fHasChangedBlocks = false;
|
||||||
|
|
||||||
// insert all changed blocks into the log array
|
int32 blockShift = fVolume->BlockShift();
|
||||||
|
off_t logOffset = fVolume->ToBlock(fVolume->Log()) << blockShift;
|
||||||
|
off_t logStart = fVolume->LogEnd();
|
||||||
|
off_t logPosition = logStart % fLogSize;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
// create run_array structures for all changed blocks
|
||||||
|
|
||||||
|
RunArrays runArrays(this);
|
||||||
|
|
||||||
uint32 cookie = 0;
|
uint32 cookie = 0;
|
||||||
{
|
off_t blockNumber;
|
||||||
off_t blockNumber;
|
while (cache_next_block_in_transaction(fVolume->BlockCache(), fTransactionID,
|
||||||
while (cache_next_block_in_transaction(fVolume->BlockCache(), fTransactionID, &cookie,
|
&cookie, &blockNumber, NULL, NULL) == B_OK) {
|
||||||
&blockNumber, NULL, NULL) == B_OK) {
|
status = runArrays.Insert(blockNumber);
|
||||||
fArray.Insert(blockNumber);
|
if (status < B_OK) {
|
||||||
|
FATAL(("filling log entry failed!"));
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorted_array *array = fArray.Array();
|
if (runArrays.Length() == 0) {
|
||||||
if (array == NULL || array->count == 0) {
|
|
||||||
// nothing has changed during this transaction
|
// nothing has changed during this transaction
|
||||||
cache_end_transaction(fVolume->BlockCache(), fTransactionID, NULL, NULL);
|
cache_end_transaction(fVolume->BlockCache(), fTransactionID, NULL, NULL);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -268,57 +545,85 @@ Journal::WriteLogEntry()
|
|||||||
return B_BAD_DATA;
|
return B_BAD_DATA;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
int32 blockShift = fVolume->BlockShift();
|
|
||||||
off_t logOffset = fVolume->ToBlock(fVolume->Log()) << blockShift;
|
|
||||||
off_t logStart = fVolume->LogEnd();
|
|
||||||
off_t logPosition = logStart % fLogSize;
|
|
||||||
|
|
||||||
// Write disk block array
|
// Write log entries to disk
|
||||||
|
|
||||||
uint8 *arrayBlock = (uint8 *)array;
|
int32 maxVecs = runArrays.MaxArrayLength();
|
||||||
|
|
||||||
// ToDo: the single writes should be combined!
|
iovec *vecs = (iovec *)malloc(sizeof(iovec) * maxVecs);
|
||||||
for (int32 size = fArray.BlocksUsed(); size-- > 0;) {
|
if (vecs == NULL) {
|
||||||
write_pos(fVolume->Device(), logOffset + (logPosition << blockShift),
|
// ToDo: write back log entries directly?
|
||||||
arrayBlock, fVolume->BlockSize());
|
|
||||||
|
|
||||||
logPosition = (logPosition + 1) % fLogSize;
|
|
||||||
arrayBlock += fVolume->BlockSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write logged blocks into the log
|
|
||||||
|
|
||||||
for (int32 i = 0; i < array->count; i++) {
|
|
||||||
const uint8 *block = (const uint8 *)block_cache_get(fVolume->BlockCache(), array->values[i]);
|
|
||||||
if (block == NULL) {
|
|
||||||
FATAL(("Could not get block %Ld\n", array->values[i]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDo: combine blocks whenever possible (using iovecs)!
|
|
||||||
write_pos(fVolume->Device(), logOffset + (logPosition << blockShift),
|
|
||||||
block, fVolume->BlockSize());
|
|
||||||
|
|
||||||
block_cache_put(fVolume->BlockCache(), array->values[i]);
|
|
||||||
logPosition = (logPosition + 1) % fLogSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and add log entry
|
|
||||||
|
|
||||||
log_entry *logEntry = (log_entry *)malloc(sizeof(log_entry));
|
|
||||||
if (logEntry == NULL) {
|
|
||||||
DIE(("Could not create next log entry (out of memory)\n"));
|
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
logEntry->start = logStart;
|
runArrays.PrepareForWriting();
|
||||||
logEntry->length = TransactionSize();
|
|
||||||
logEntry->journal = this;
|
|
||||||
|
|
||||||
fEntriesLock.Lock();
|
for (int32 k = 0; k < runArrays.CountArrays(); k++) {
|
||||||
fEntries.Add(logEntry);
|
run_array *array = runArrays.ArrayAt(k);
|
||||||
fUsed += logEntry->length;
|
int32 index = 0, count = 1;
|
||||||
fEntriesLock.Unlock();
|
int32 wrap = fLogSize - logStart;
|
||||||
|
add_to_iovec(vecs, index, maxVecs, (void *)array, fVolume->BlockSize());
|
||||||
|
|
||||||
|
// add block runs
|
||||||
|
|
||||||
|
for (int32 i = 0; i < array->CountRuns(); i++) {
|
||||||
|
const block_run &run = array->RunAt(i);
|
||||||
|
off_t blockNumber = fVolume->ToBlock(run);
|
||||||
|
|
||||||
|
for (int32 j = 0; j < run.Length(); j++) {
|
||||||
|
if (count >= wrap) {
|
||||||
|
// we need to write back the first half of the entry directly
|
||||||
|
logPosition = logStart + count;
|
||||||
|
if (writev_pos(fVolume->Device(), logOffset
|
||||||
|
+ (logStart << blockShift), vecs, index) < 0)
|
||||||
|
FATAL(("could not write log area!\n"));
|
||||||
|
|
||||||
|
logStart = 0;
|
||||||
|
wrap = fLogSize;
|
||||||
|
count = 0;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make blocks available in the cache
|
||||||
|
const void *data;
|
||||||
|
if (j == 0) {
|
||||||
|
data = block_cache_get_etc(fVolume->BlockCache(), blockNumber,
|
||||||
|
blockNumber, run.Length());
|
||||||
|
} else
|
||||||
|
data = block_cache_get(fVolume->BlockCache(), blockNumber + j);
|
||||||
|
|
||||||
|
if (data == NULL)
|
||||||
|
return B_IO_ERROR;
|
||||||
|
|
||||||
|
add_to_iovec(vecs, index, maxVecs, data, fVolume->BlockSize());
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write back log entry
|
||||||
|
if (count > 0) {
|
||||||
|
logPosition = logStart + count;
|
||||||
|
if (writev_pos(fVolume->Device(), logOffset + (logStart << blockShift),
|
||||||
|
vecs, index) < 0)
|
||||||
|
FATAL(("could not write log area: %s!\n", strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// release blocks again
|
||||||
|
for (int32 i = 0; i < array->CountRuns(); i++) {
|
||||||
|
const block_run &run = array->RunAt(i);
|
||||||
|
off_t blockNumber = fVolume->ToBlock(run);
|
||||||
|
|
||||||
|
for (int32 j = 0; j < run.Length(); j++) {
|
||||||
|
block_cache_put(fVolume->BlockCache(), blockNumber + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogEntry *logEntry = new LogEntry(this, fVolume->LogEnd(), runArrays.Length());
|
||||||
|
if (logEntry == NULL) {
|
||||||
|
FATAL(("no memory to allocate log entries!"));
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the log end pointer in the super block
|
// Update the log end pointer in the super block
|
||||||
|
|
||||||
@@ -326,7 +631,7 @@ Journal::WriteLogEntry()
|
|||||||
fVolume->SuperBlock().log_end = logPosition;
|
fVolume->SuperBlock().log_end = logPosition;
|
||||||
fVolume->LogEnd() = logPosition;
|
fVolume->LogEnd() = logPosition;
|
||||||
|
|
||||||
status_t status = fVolume->WriteSuperBlock();
|
status = fVolume->WriteSuperBlock();
|
||||||
|
|
||||||
// We need to flush the drives own cache here to ensure
|
// We need to flush the drives own cache here to ensure
|
||||||
// disk consistency.
|
// disk consistency.
|
||||||
@@ -335,8 +640,13 @@ Journal::WriteLogEntry()
|
|||||||
|
|
||||||
// at this point, we can finally end the transaction - we're in
|
// at this point, we can finally end the transaction - we're in
|
||||||
// a guaranteed valid state
|
// a guaranteed valid state
|
||||||
cache_end_transaction(fVolume->BlockCache(), fTransactionID, blockNotify, logEntry);
|
|
||||||
fArray.MakeEmpty();
|
fEntriesLock.Lock();
|
||||||
|
fEntries.Add(logEntry);
|
||||||
|
fUsed += logEntry->Length();
|
||||||
|
fEntriesLock.Unlock();
|
||||||
|
|
||||||
|
cache_end_transaction(fVolume->BlockCache(), fTransactionID, _blockNotify, logEntry);
|
||||||
|
|
||||||
// If the log goes to the next round (the log is written as a
|
// If the log goes to the next round (the log is written as a
|
||||||
// circular buffer), all blocks will be flushed out which is
|
// circular buffer), all blocks will be flushed out which is
|
||||||
@@ -364,7 +674,7 @@ Journal::FlushLogAndBlocks()
|
|||||||
|
|
||||||
// write the current log entry to disk
|
// write the current log entry to disk
|
||||||
|
|
||||||
if (fTransactionID != -1 && TransactionSize() != 0) {
|
if (fTransactionID != -1 /*&& TransactionSize() != 0*/) {
|
||||||
status = WriteLogEntry();
|
status = WriteLogEntry();
|
||||||
if (status < B_OK)
|
if (status < B_OK)
|
||||||
FATAL(("writing current log entry failed: %s\n", strerror(status)));
|
FATAL(("writing current log entry failed: %s\n", strerror(status)));
|
||||||
@@ -413,7 +723,7 @@ Journal::Unlock(Transaction *owner, bool success)
|
|||||||
if (fLock.OwnerCount() == 1) {
|
if (fLock.OwnerCount() == 1) {
|
||||||
// we only end the transaction if we would really unlock it
|
// we only end the transaction if we would really unlock it
|
||||||
// ToDo: what about failing transactions that do not unlock?
|
// ToDo: what about failing transactions that do not unlock?
|
||||||
TransactionDone(success);
|
_TransactionDone(success);
|
||||||
|
|
||||||
fTransactionID = -1;
|
fTransactionID = -1;
|
||||||
fTimestamp = system_time();
|
fTimestamp = system_time();
|
||||||
@@ -425,10 +735,9 @@ Journal::Unlock(Transaction *owner, bool success)
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Journal::TransactionDone(bool success)
|
Journal::_TransactionDone(bool success)
|
||||||
{
|
{
|
||||||
if (!success) {
|
if (!success) {
|
||||||
fArray.MakeEmpty();
|
|
||||||
cache_abort_transaction(fVolume->BlockCache(), fTransactionID);
|
cache_abort_transaction(fVolume->BlockCache(), fTransactionID);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -466,7 +775,7 @@ status_t
|
|||||||
Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks)
|
Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks)
|
||||||
{
|
{
|
||||||
panic("LogBlocks() called!\n");
|
panic("LogBlocks() called!\n");
|
||||||
|
#if 0
|
||||||
// ToDo: that's for now - we should change the log file size here
|
// ToDo: that's for now - we should change the log file size here
|
||||||
if (TransactionSize() + numBlocks + 1 > fLogSize)
|
if (TransactionSize() + numBlocks + 1 > fLogSize)
|
||||||
return B_DEVICE_FULL;
|
return B_DEVICE_FULL;
|
||||||
@@ -503,6 +812,7 @@ Journal::LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks)
|
|||||||
/* if (TransactionSize() > FreeLogBlocks())
|
/* if (TransactionSize() > FreeLogBlocks())
|
||||||
force_cache_flush(fVolume->Device(), true);
|
force_cache_flush(fVolume->Device(), true);
|
||||||
*/
|
*/
|
||||||
|
#endif
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
#include "Utility.h"
|
#include "Utility.h"
|
||||||
|
|
||||||
|
|
||||||
struct log_entry;
|
struct run_array;
|
||||||
typedef DoublyLinkedList<log_entry> LogEntryList;
|
class LogEntry;
|
||||||
|
typedef DoublyLinkedList<LogEntry> LogEntryList;
|
||||||
|
|
||||||
|
|
||||||
// Locking policy in BFS: if you need both, the volume lock and the
|
// Locking policy in BFS: if you need both, the volume lock and the
|
||||||
@@ -46,15 +47,13 @@ class Journal {
|
|||||||
status_t Lock(Transaction *owner);
|
status_t Lock(Transaction *owner);
|
||||||
void Unlock(Transaction *owner, bool success);
|
void Unlock(Transaction *owner, bool success);
|
||||||
|
|
||||||
status_t CheckLogEntry(int32 count, const off_t *array);
|
|
||||||
status_t ReplayLogEntry(int32 *start);
|
|
||||||
status_t ReplayLog();
|
status_t ReplayLog();
|
||||||
|
|
||||||
status_t WriteLogEntry();
|
status_t WriteLogEntry();
|
||||||
status_t LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks);
|
status_t LogBlocks(off_t blockNumber, const uint8 *buffer, size_t numBlocks);
|
||||||
|
|
||||||
Transaction *CurrentTransaction() const { return fOwner; }
|
Transaction *CurrentTransaction() const { return fOwner; }
|
||||||
uint32 TransactionSize() const { return fArray.CountItems() + fArray.BlocksUsed(); }
|
// uint32 TransactionSize() const { return fArray.CountItems() + fArray.BlocksUsed(); }
|
||||||
|
|
||||||
status_t FlushLogAndBlocks();
|
status_t FlushLogAndBlocks();
|
||||||
Volume *GetVolume() const { return fVolume; }
|
Volume *GetVolume() const { return fVolume; }
|
||||||
@@ -63,15 +62,16 @@ class Journal {
|
|||||||
inline uint32 FreeLogBlocks() const;
|
inline uint32 FreeLogBlocks() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct log_entry;
|
friend class LogEntry;
|
||||||
|
|
||||||
static void blockNotify(int32 transactionID, void *arg);
|
status_t _CheckRunArray(const run_array *array);
|
||||||
status_t TransactionDone(bool success);
|
status_t _ReplayRunArray(int32 *start);
|
||||||
|
status_t _TransactionDone(bool success);
|
||||||
|
static void _blockNotify(int32 transactionID, void *arg);
|
||||||
|
|
||||||
Volume *fVolume;
|
Volume *fVolume;
|
||||||
RecursiveLock fLock;
|
RecursiveLock fLock;
|
||||||
Transaction *fOwner;
|
Transaction *fOwner;
|
||||||
BlockArray fArray;
|
|
||||||
uint32 fLogSize, fMaxTransactionSize, fUsed;
|
uint32 fLogSize, fMaxTransactionSize, fUsed;
|
||||||
int32 fTransactionsInEntry;
|
int32 fTransactionsInEntry;
|
||||||
SimpleLock fEntriesLock;
|
SimpleLock fEntriesLock;
|
||||||
|
|||||||
Reference in New Issue
Block a user