Fixed a bad bug in the journaling code: set_blocks_info() was called before

the super block was written back. This had a big race condition when all
blocks in the transaction were written back before the super block was
written. In that case, the log info part of the super block got out of
sync and was plain wrong (eventually causing the disk to be unmountable).
Also moved the list code over to the kernel's DoublyLinkedList.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13366 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-06-30 16:23:56 +00:00
parent b986196626
commit 2921880701
3 changed files with 72 additions and 101 deletions
@@ -1,8 +1,8 @@
/* Journal - transaction and logging
**
** Initial version by Axel Dörfler, [email protected]
** This file may be used under the terms of the OpenBeOS License.
*/
*
* Copyright 2001-2005, Axel Dörfler, [email protected]
* This file may be used under the terms of the MIT License.
*/
#include "Journal.h"
@@ -13,6 +13,14 @@
#include <util/kernel_cpp.h>
struct log_entry : public DoublyLinkedListLinkImpl<log_entry> {
uint16 start;
uint16 length;
uint32 cached_blocks;
Journal *journal;
};
Journal::Journal(Volume *volume)
:
fVolume(volume),
@@ -186,9 +194,10 @@ Journal::blockNotify(off_t blockNumber, size_t numBlocks, void *arg)
// Set log_start pointer if possible...
if (logEntry == journal->fEntries.head) {
if (logEntry->Next() != NULL) {
int32 length = logEntry->next->start - logEntry->start;
if (logEntry == journal->fEntries.First()) {
log_entry *next = journal->fEntries.GetNext(logEntry);
if (next != NULL) {
int32 length = next->start - logEntry->start;
superBlock.log_start = (superBlock.log_start + length) % journal->fLogSize;
} else
superBlock.log_start = journal->fVolume->LogEnd();
@@ -198,7 +207,7 @@ Journal::blockNotify(off_t blockNumber, size_t numBlocks, void *arg)
journal->fUsed -= logEntry->length;
journal->fEntriesLock.Lock();
logEntry->Remove();
journal->fEntries.Remove(logEntry);
journal->fEntriesLock.Unlock();
free(logEntry);
@@ -211,7 +220,9 @@ Journal::blockNotify(off_t blockNumber, size_t numBlocks, void *arg)
if (superBlock.log_start == superBlock.log_end)
superBlock.flags = SUPER_BLOCK_DISK_CLEAN;
journal->fVolume->WriteSuperBlock();
status_t status = journal->fVolume->WriteSuperBlock();
if (status != B_OK)
FATAL(("blockNotify: could not write back super block: %s\n", strerror(status)));
}
}
@@ -270,23 +281,42 @@ Journal::WriteLogEntry()
logPosition = (logPosition + 1) % fLogSize;
}
// create and add log entry
log_entry *logEntry = (log_entry *)malloc(sizeof(log_entry));
if (logEntry != NULL) {
logEntry->start = logStart;
logEntry->length = TransactionSize();
logEntry->cached_blocks = array->count;
logEntry->journal = this;
fEntriesLock.Lock();
fEntries.Add(logEntry);
fEntriesLock.Unlock();
fCurrent = logEntry;
fUsed += logEntry->length;
set_blocks_info(fVolume->Device(), &array->values[0], array->count, blockNotify, logEntry);
if (logEntry == NULL) {
DIE(("Could not create next log entry (out of memory)\n"));
return B_NO_MEMORY;
}
logEntry->start = logStart;
logEntry->length = TransactionSize();
logEntry->cached_blocks = array->count;
logEntry->journal = this;
fEntriesLock.Lock();
fEntries.Add(logEntry);
fEntriesLock.Unlock();
fCurrent = logEntry;
fUsed += logEntry->length;
// Update the log end pointer in the super block
fVolume->SuperBlock().flags = SUPER_BLOCK_DISK_DIRTY;
fVolume->SuperBlock().log_end = logPosition;
fVolume->LogEnd() = logPosition;
status_t status = fVolume->WriteSuperBlock();
// We need to flush the drives own cache here to ensure
// disk consistency.
// If that call fails, we can't do anything about it anyway
ioctl(fVolume->Device(), B_FLUSH_DRIVE_CACHE);
set_blocks_info(fVolume->Device(), &array->values[0],
array->count, blockNotify, logEntry);
fArray.MakeEmpty();
// If the log goes to the next round (the log is written as a
// circular buffer), all blocks will be flushed out which is
// possible because we don't have any locked blocks at this
@@ -294,19 +324,7 @@ Journal::WriteLogEntry()
if (logPosition < logStart)
fVolume->FlushDevice();
// We need to flush the drives own cache here to ensure
// disk consistency.
// If that call fails, we can't do anything about it anyway
ioctl(fVolume->Device(), B_FLUSH_DRIVE_CACHE);
fArray.MakeEmpty();
// Update the log end pointer in the super block
fVolume->SuperBlock().flags = SUPER_BLOCK_DISK_DIRTY;
fVolume->SuperBlock().log_end = logPosition;
fVolume->LogEnd() = logPosition;
return fVolume->WriteSuperBlock();
return status;
}
@@ -8,6 +8,7 @@
#include <KernelExport.h>
#include <util/DoublyLinkedList.h>
#ifdef USER
# include <stdio.h>
@@ -20,13 +21,8 @@
#include "Utility.h"
struct log_entry : node<log_entry> {
uint16 start;
uint16 length;
uint32 cached_blocks;
Journal *journal;
};
struct log_entry;
typedef DoublyLinkedList<log_entry> LogEntryList;
// Locking policy in BFS: if you need both, the volume lock and the
// journal lock, you must lock the volume first - or else you will
@@ -67,17 +63,17 @@ class Journal {
static void blockNotify(off_t blockNumber, size_t numBlocks, void *arg);
status_t TransactionDone(bool success);
Volume *fVolume;
Volume *fVolume;
RecursiveLock fLock;
Transaction *fOwner;
BlockArray fArray;
uint32 fLogSize, fMaxTransactionSize, fUsed;
int32 fTransactionsInEntry;
SimpleLock fEntriesLock;
list<log_entry> fEntries;
log_entry *fCurrent;
bool fHasChangedBlocks;
bigtime_t fTimestamp;
Transaction *fOwner;
BlockArray fArray;
uint32 fLogSize, fMaxTransactionSize, fUsed;
int32 fTransactionsInEntry;
SimpleLock fEntriesLock;
LogEntryList fEntries;
log_entry *fCurrent;
bool fHasChangedBlocks;
bigtime_t fTimestamp;
};
@@ -1,10 +1,10 @@
/* Utility - some helper classes
*
* Copyright 2001-2005, Axel Dörfler, [email protected]
* This file may be used under the terms of the MIT License.
*/
#ifndef UTILITY_H
#define UTILITY_H
/* Utility - some helper classes
**
** Initial version by Axel Dörfler, [email protected]
** This file may be used under the terms of the OpenBeOS License.
*/
#include <SupportDefs.h>
@@ -69,49 +69,6 @@ class BlockArray {
};
// Doubly linked list
template<class Node> struct node {
Node *next,*prev;
void
Remove()
{
prev->next = next;
next->prev = prev;
}
Node *
Next()
{
if (next && next->next != NULL)
return next;
return NULL;
}
};
template<class Node> struct list {
Node *head,*tail,*last;
list()
{
head = (Node *)&tail;
tail = NULL;
last = (Node *)&head;
}
void
Add(Node *entry)
{
entry->next = (Node *)&tail;
entry->prev = last;
last->next = entry;
last = entry;
}
};
// Some atomic operations that are somehow missing in BeOS:
//
// _atomic_test_and_set(value, newValue, testAgainst)