* Implemented missing cleanup when an IOScheduler is destroyed. The threads

weren't terminated orderly.
* IOScheduler now stores its name and gets a unique ID.
* Added IOSchedulerRoster singleton which registers all IOSchedulers. It also
  provides a notification service. We generate interesting events for
  IOSchedulers, IORequests, and IOOperations.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34702 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-12-19 14:32:14 +00:00
parent e001389fc7
commit ca77afe75f
4 changed files with 221 additions and 18 deletions
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_cd ; SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_cd ;
UsePrivateHeaders drivers kernel ; UsePrivateKernelHeaders ;
UsePrivateHeaders drivers ;
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
KernelAddon scsi_cd : KernelAddon scsi_cd :
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_disk ; SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_disk ;
UsePrivateHeaders drivers kernel ; UsePrivateKernelHeaders ;
UsePrivateHeaders drivers ;
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ; SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
KernelAddon scsi_disk : KernelAddon scsi_disk :
+155 -13
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2008, Ingo Weinhold, [email protected]. * Copyright 2008-2009, Ingo Weinhold, [email protected].
* Copyright 2004-2009, Axel Dörfler, [email protected]. * Copyright 2004-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -30,6 +30,9 @@
#endif #endif
// #pragma mark - IOCallback
IOCallback::~IOCallback() IOCallback::~IOCallback()
{ {
} }
@@ -99,21 +102,51 @@ struct IOScheduler::RequestOwnerHashTable
IOScheduler::IOScheduler(DMAResource* resource) IOScheduler::IOScheduler(DMAResource* resource)
: :
fDMAResource(resource), fDMAResource(resource),
fName(NULL),
fID(-1),
fSchedulerThread(-1),
fRequestNotifierThread(-1),
fOperationArray(NULL), fOperationArray(NULL),
fAllocatedRequestOwners(NULL), fAllocatedRequestOwners(NULL),
fRequestOwners(NULL), fRequestOwners(NULL),
fBlockSize(0), fBlockSize(0),
fPendingOperations(0) fPendingOperations(0),
fTerminating(false)
{ {
mutex_init(&fLock, "I/O scheduler"); mutex_init(&fLock, "I/O scheduler");
B_INITIALIZE_SPINLOCK(&fFinisherLock); B_INITIALIZE_SPINLOCK(&fFinisherLock);
fNewRequestCondition.Init(this, "I/O new request");
fFinishedOperationCondition.Init(this, "I/O finished operation");
fFinishedRequestCondition.Init(this, "I/O finished request");
} }
IOScheduler::~IOScheduler() IOScheduler::~IOScheduler()
{ {
// TODO: Shutdown threads. if (InitCheck() == B_OK)
IOSchedulerRoster::Default()->RemoveScheduler(this);
// shutdown threads
MutexLocker locker(fLock);
InterruptsSpinLocker finisherLocker(fFinisherLock);
fTerminating = true;
fNewRequestCondition.NotifyAll();
fFinishedOperationCondition.NotifyAll();
fFinishedRequestCondition.NotifyAll();
finisherLocker.Unlock();
locker.Unlock();
if (fSchedulerThread >= 0)
wait_for_thread(fSchedulerThread, NULL);
if (fRequestNotifierThread >= 0)
wait_for_thread(fRequestNotifierThread, NULL);
// destroy our belongings
mutex_lock(&fLock); mutex_lock(&fLock);
mutex_destroy(&fLock); mutex_destroy(&fLock);
@@ -124,15 +157,17 @@ IOScheduler::~IOScheduler()
delete fRequestOwners; delete fRequestOwners;
delete[] fAllocatedRequestOwners; delete[] fAllocatedRequestOwners;
free(fName);
} }
status_t status_t
IOScheduler::Init(const char* name) IOScheduler::Init(const char* name)
{ {
fNewRequestCondition.Init(this, "I/O new request"); fName = strdup(name);
fFinishedOperationCondition.Init(this, "I/O finished operation"); if (fName == NULL)
fFinishedRequestCondition.Init(this, "I/O finished request"); return B_NO_MEMORY;
size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16; size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
@@ -195,10 +230,20 @@ IOScheduler::Init(const char* name)
resume_thread(fSchedulerThread); resume_thread(fSchedulerThread);
resume_thread(fRequestNotifierThread); resume_thread(fRequestNotifierThread);
IOSchedulerRoster::Default()->AddScheduler(this);
return B_OK; return B_OK;
} }
status_t
IOScheduler::InitCheck() const
{
return fRequestNotifierThread >= 0 ? B_OK : B_NO_INIT;
}
void void
IOScheduler::SetCallback(IOCallback& callback) IOScheduler::SetCallback(IOCallback& callback)
{ {
@@ -259,6 +304,9 @@ IOScheduler::ScheduleRequest(IORequest* request)
if (!wasActive) if (!wasActive)
fActiveRequestOwners.Add(owner); fActiveRequestOwners.Add(owner);
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_SCHEDULED, this,
request);
fNewRequestCondition.NotifyAll(); fNewRequestCondition.NotifyAll();
return B_OK; return B_OK;
@@ -325,7 +373,14 @@ IOScheduler::_Finisher()
TRACE("IOScheduler::_Finisher(): operation: %p\n", operation); TRACE("IOScheduler::_Finisher(): operation: %p\n", operation);
if (!operation->Finish()) { bool operationFinished = operation->Finish();
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_FINISHED,
this, operation->Parent(), operation);
// Notify for every time the operation is passed to the I/O hook,
// not only when it is fully finished.
if (!operationFinished) {
TRACE(" operation: %p not finished yet\n", operation); TRACE(" operation: %p not finished yet\n", operation);
MutexLocker _(fLock); MutexLocker _(fLock);
operation->SetTransferredBytes(0); operation->SetTransferredBytes(0);
@@ -379,6 +434,8 @@ IOScheduler::_Finisher()
fFinishedRequestCondition.NotifyAll(); fFinishedRequestCondition.NotifyAll();
} else { } else {
// No callbacks -- finish the request right now. // No callbacks -- finish the request right now.
IOSchedulerRoster::Default()->Notify(
IO_SCHEDULER_REQUEST_FINISHED, this, request);
request->NotifyFinished(); request->NotifyFinished();
} }
} }
@@ -472,10 +529,13 @@ IOScheduler::_ComputeRequestOwnerBandwidth(int32 priority) const
} }
void bool
IOScheduler::_NextActiveRequestOwner(IORequestOwner*& owner, off_t& quantum) IOScheduler::_NextActiveRequestOwner(IORequestOwner*& owner, off_t& quantum)
{ {
while (true) { while (true) {
if (fTerminating)
return false;
if (owner != NULL) if (owner != NULL)
owner = fActiveRequestOwners.GetNext(owner); owner = fActiveRequestOwners.GetNext(owner);
if (owner == NULL) if (owner == NULL)
@@ -483,7 +543,7 @@ IOScheduler::_NextActiveRequestOwner(IORequestOwner*& owner, off_t& quantum)
if (owner != NULL) { if (owner != NULL) {
quantum = _ComputeRequestOwnerBandwidth(owner->priority); quantum = _ComputeRequestOwnerBandwidth(owner->priority);
return; return true;
} }
// Wait for new requests owners. First check whether any finisher work // Wait for new requests owners. First check whether any finisher work
@@ -579,7 +639,7 @@ IOScheduler::_Scheduler()
IORequestOwner* owner = NULL; IORequestOwner* owner = NULL;
off_t quantum = 0; off_t quantum = 0;
while (true) { while (!fTerminating) {
//dprintf("IOScheduler::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum); //dprintf("IOScheduler::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum);
MutexLocker locker(fLock); MutexLocker locker(fLock);
@@ -594,8 +654,12 @@ IOScheduler::_Scheduler()
fActiveRequestOwners.Remove(&marker); fActiveRequestOwners.Remove(&marker);
} }
if (owner == NULL || quantum < fBlockSize) if (owner == NULL || quantum < fBlockSize) {
_NextActiveRequestOwner(owner, quantum); if (!_NextActiveRequestOwner(owner, quantum)) {
// we've been asked to terminate
return B_OK;
}
}
while (resourcesAvailable && iterationBandwidth >= fBlockSize) { while (resourcesAvailable && iterationBandwidth >= fBlockSize) {
//dprintf("IOScheduler::_Scheduler(): request owner: %p (thread %ld)\n", //dprintf("IOScheduler::_Scheduler(): request owner: %p (thread %ld)\n",
@@ -673,13 +737,16 @@ panic("no more requests for owner %p (thread %ld)", owner, owner->thread);
TRACE("IOScheduler::_Scheduler(): calling callback for " TRACE("IOScheduler::_Scheduler(): calling callback for "
"operation %ld: %p\n", i++, operation); "operation %ld: %p\n", i++, operation);
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_STARTED,
this, operation->Parent(), operation);
fIOCallback(fIOCallbackData, operation); fIOCallback(fIOCallbackData, operation);
_Finisher(); _Finisher();
} }
// wait for all operations to finish // wait for all operations to finish
while (true) { while (!fTerminating) {
locker.Lock(); locker.Lock();
if (fPendingOperations == 0) if (fPendingOperations == 0)
@@ -729,6 +796,9 @@ IOScheduler::_RequestNotifier()
IORequest* request = fFinishedRequests.RemoveHead(); IORequest* request = fFinishedRequests.RemoveHead();
if (request == NULL) { if (request == NULL) {
if (fTerminating)
return B_OK;
ConditionVariableEntry entry; ConditionVariableEntry entry;
fFinishedRequestCondition.Add(&entry); fFinishedRequestCondition.Add(&entry);
@@ -740,6 +810,9 @@ IOScheduler::_RequestNotifier()
locker.Unlock(); locker.Unlock();
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_FINISHED,
this, request);
// notify the request // notify the request
request->NotifyFinished(); request->NotifyFinished();
} }
@@ -796,3 +869,72 @@ IOScheduler::_IOCallbackWrapper(void* data, io_operation* operation)
return ((IOCallback*)data)->DoIO(operation); return ((IOCallback*)data)->DoIO(operation);
} }
// #pragma mark - IOSchedulerNotificationService
/*static*/ IOSchedulerRoster IOSchedulerRoster::sDefaultInstance;
/*static*/ void
IOSchedulerRoster::Init()
{
new(&sDefaultInstance) IOSchedulerRoster;
}
void
IOSchedulerRoster::AddScheduler(IOScheduler* scheduler)
{
AutoLocker<IOSchedulerRoster> locker(this);
scheduler->SetID(fNextID++);
fSchedulers.Add(scheduler);
locker.Unlock();
Notify(IO_SCHEDULER_ADDED, scheduler);
}
void
IOSchedulerRoster::RemoveScheduler(IOScheduler* scheduler)
{
AutoLocker<IOSchedulerRoster> locker(this);
fSchedulers.Remove(scheduler);
locker.Unlock();
Notify(IO_SCHEDULER_REMOVED, scheduler);
}
void
IOSchedulerRoster::Notify(uint32 eventCode, const IOScheduler* scheduler,
IORequest* request, IOOperation* operation)
{
char eventBuffer[128];
KMessage event;
event.SetTo(eventBuffer, sizeof(eventBuffer), IO_SCHEDULER_MONITOR);
event.AddInt32("event", eventCode);
event.AddPointer("scheduler", scheduler);
if (request != NULL) {
event.AddPointer("request", request);
if (operation != NULL)
event.AddPointer("operation", operation);
}
fNotificationService.Notify(event, eventCode);
}
IOSchedulerRoster::IOSchedulerRoster()
:
fNextID(1),
fNotificationService("I/O")
{
mutex_init(&fLock, "IOSchedulerRoster");
}
IOSchedulerRoster::~IOSchedulerRoster()
{
mutex_destroy(&fLock);
}
+62 -3
View File
@@ -1,15 +1,17 @@
/* /*
* Copyright 2008, Ingo Weinhold, [email protected]. * Copyright 2008-2009, Ingo Weinhold, [email protected].
* Copyright 2004-2008, Axel Dörfler, [email protected]. * Copyright 2004-2008, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef IO_SCHEDULER_H #ifndef IO_SCHEDULER_H
#define IO_SCHEDULER_H #define IO_SCHEDULER_H
#include <KernelExport.h> #include <KernelExport.h>
#include <condition_variable.h> #include <condition_variable.h>
#include <lock.h> #include <lock.h>
#include <Notifications.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h> #include <util/OpenHashTable.h>
@@ -17,6 +19,16 @@
#include "IORequest.h" #include "IORequest.h"
// I/O scheduler notifications
#define IO_SCHEDULER_MONITOR '_io_'
#define IO_SCHEDULER_ADDED 0x01
#define IO_SCHEDULER_REMOVED 0x02
#define IO_SCHEDULER_REQUEST_SCHEDULED 0x04
#define IO_SCHEDULER_REQUEST_FINISHED 0x08
#define IO_SCHEDULER_OPERATION_STARTED 0x10
#define IO_SCHEDULER_OPERATION_FINISHED 0x20
class IOCallback { class IOCallback {
public: public:
virtual ~IOCallback(); virtual ~IOCallback();
@@ -45,12 +57,13 @@ struct IORequestOwner : DoublyLinkedListLinkImpl<IORequestOwner> {
}; };
class IOScheduler { class IOScheduler : public DoublyLinkedListLinkImpl<IOScheduler> {
public: public:
IOScheduler(DMAResource* resource); IOScheduler(DMAResource* resource);
~IOScheduler(); ~IOScheduler();
status_t Init(const char* name); status_t Init(const char* name);
status_t InitCheck() const;
void SetCallback(IOCallback& callback); void SetCallback(IOCallback& callback);
void SetCallback(io_callback callback, void* data); void SetCallback(io_callback callback, void* data);
@@ -65,6 +78,11 @@ public:
// has been completed successfully or failed // has been completed successfully or failed
// for some reason // for some reason
const char* Name() const { return fName; }
int32 ID() const { return fID; }
void SetID(int32 id) { fID = id; }
void Dump() const; void Dump() const;
private: private:
@@ -77,7 +95,7 @@ private:
bool _FinisherWorkPending(); bool _FinisherWorkPending();
off_t _ComputeRequestOwnerBandwidth( off_t _ComputeRequestOwnerBandwidth(
int32 priority) const; int32 priority) const;
void _NextActiveRequestOwner(IORequestOwner*& owner, bool _NextActiveRequestOwner(IORequestOwner*& owner,
off_t& quantum); off_t& quantum);
bool _PrepareRequestOperations(IORequest* request, bool _PrepareRequestOperations(IORequest* request,
IOOperationList& operations, IOOperationList& operations,
@@ -102,6 +120,8 @@ private:
private: private:
DMAResource* fDMAResource; DMAResource* fDMAResource;
char* fName;
int32 fID;
spinlock fFinisherLock; spinlock fFinisherLock;
mutex fLock; mutex fLock;
thread_id fSchedulerThread; thread_id fSchedulerThread;
@@ -126,6 +146,45 @@ private:
off_t fIterationBandwidth; off_t fIterationBandwidth;
off_t fMinOwnerBandwidth; off_t fMinOwnerBandwidth;
off_t fMaxOwnerBandwidth; off_t fMaxOwnerBandwidth;
volatile bool fTerminating;
}; };
typedef DoublyLinkedList<IOScheduler> IOSchedulerList;
class IOSchedulerRoster {
public:
static void Init();
static IOSchedulerRoster* Default() { return &sDefaultInstance; }
bool Lock() { return mutex_lock(&fLock) == B_OK; }
void Unlock() { mutex_unlock(&fLock); }
const IOSchedulerList& SchedulerList() const
{ return fSchedulers; }
// caller must keep the roster locked,
// while accessing the list
void AddScheduler(IOScheduler* scheduler);
void RemoveScheduler(IOScheduler* scheduler);
void Notify(uint32 eventCode,
const IOScheduler* scheduler,
IORequest* request = NULL,
IOOperation* operation = NULL);
private:
IOSchedulerRoster();
~IOSchedulerRoster();
private:
mutex fLock;
int32 fNextID;
IOSchedulerList fSchedulers;
DefaultNotificationService fNotificationService;
static IOSchedulerRoster sDefaultInstance;
};
#endif // IO_SCHEDULER_H #endif // IO_SCHEDULER_H