* 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:
@@ -1,6 +1,7 @@
|
||||
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 ;
|
||||
|
||||
KernelAddon scsi_cd :
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 ;
|
||||
|
||||
KernelAddon scsi_disk :
|
||||
|
||||
@@ -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].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@@ -30,6 +30,9 @@
|
||||
#endif
|
||||
|
||||
|
||||
// #pragma mark - IOCallback
|
||||
|
||||
|
||||
IOCallback::~IOCallback()
|
||||
{
|
||||
}
|
||||
@@ -99,21 +102,51 @@ struct IOScheduler::RequestOwnerHashTable
|
||||
IOScheduler::IOScheduler(DMAResource* resource)
|
||||
:
|
||||
fDMAResource(resource),
|
||||
fName(NULL),
|
||||
fID(-1),
|
||||
fSchedulerThread(-1),
|
||||
fRequestNotifierThread(-1),
|
||||
fOperationArray(NULL),
|
||||
fAllocatedRequestOwners(NULL),
|
||||
fRequestOwners(NULL),
|
||||
fBlockSize(0),
|
||||
fPendingOperations(0)
|
||||
fPendingOperations(0),
|
||||
fTerminating(false)
|
||||
{
|
||||
mutex_init(&fLock, "I/O scheduler");
|
||||
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()
|
||||
{
|
||||
// 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_destroy(&fLock);
|
||||
|
||||
@@ -124,15 +157,17 @@ IOScheduler::~IOScheduler()
|
||||
|
||||
delete fRequestOwners;
|
||||
delete[] fAllocatedRequestOwners;
|
||||
|
||||
free(fName);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
IOScheduler::Init(const char* name)
|
||||
{
|
||||
fNewRequestCondition.Init(this, "I/O new request");
|
||||
fFinishedOperationCondition.Init(this, "I/O finished operation");
|
||||
fFinishedRequestCondition.Init(this, "I/O finished request");
|
||||
fName = strdup(name);
|
||||
if (fName == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
@@ -195,10 +230,20 @@ IOScheduler::Init(const char* name)
|
||||
|
||||
resume_thread(fSchedulerThread);
|
||||
resume_thread(fRequestNotifierThread);
|
||||
|
||||
IOSchedulerRoster::Default()->AddScheduler(this);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
IOScheduler::InitCheck() const
|
||||
{
|
||||
return fRequestNotifierThread >= 0 ? B_OK : B_NO_INIT;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IOScheduler::SetCallback(IOCallback& callback)
|
||||
{
|
||||
@@ -259,6 +304,9 @@ IOScheduler::ScheduleRequest(IORequest* request)
|
||||
if (!wasActive)
|
||||
fActiveRequestOwners.Add(owner);
|
||||
|
||||
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_SCHEDULED, this,
|
||||
request);
|
||||
|
||||
fNewRequestCondition.NotifyAll();
|
||||
|
||||
return B_OK;
|
||||
@@ -325,7 +373,14 @@ IOScheduler::_Finisher()
|
||||
|
||||
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);
|
||||
MutexLocker _(fLock);
|
||||
operation->SetTransferredBytes(0);
|
||||
@@ -379,6 +434,8 @@ IOScheduler::_Finisher()
|
||||
fFinishedRequestCondition.NotifyAll();
|
||||
} else {
|
||||
// No callbacks -- finish the request right now.
|
||||
IOSchedulerRoster::Default()->Notify(
|
||||
IO_SCHEDULER_REQUEST_FINISHED, this, request);
|
||||
request->NotifyFinished();
|
||||
}
|
||||
}
|
||||
@@ -472,10 +529,13 @@ IOScheduler::_ComputeRequestOwnerBandwidth(int32 priority) const
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
bool
|
||||
IOScheduler::_NextActiveRequestOwner(IORequestOwner*& owner, off_t& quantum)
|
||||
{
|
||||
while (true) {
|
||||
if (fTerminating)
|
||||
return false;
|
||||
|
||||
if (owner != NULL)
|
||||
owner = fActiveRequestOwners.GetNext(owner);
|
||||
if (owner == NULL)
|
||||
@@ -483,7 +543,7 @@ IOScheduler::_NextActiveRequestOwner(IORequestOwner*& owner, off_t& quantum)
|
||||
|
||||
if (owner != NULL) {
|
||||
quantum = _ComputeRequestOwnerBandwidth(owner->priority);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait for new requests owners. First check whether any finisher work
|
||||
@@ -579,7 +639,7 @@ IOScheduler::_Scheduler()
|
||||
IORequestOwner* owner = NULL;
|
||||
off_t quantum = 0;
|
||||
|
||||
while (true) {
|
||||
while (!fTerminating) {
|
||||
//dprintf("IOScheduler::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum);
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
@@ -594,8 +654,12 @@ IOScheduler::_Scheduler()
|
||||
fActiveRequestOwners.Remove(&marker);
|
||||
}
|
||||
|
||||
if (owner == NULL || quantum < fBlockSize)
|
||||
_NextActiveRequestOwner(owner, quantum);
|
||||
if (owner == NULL || quantum < fBlockSize) {
|
||||
if (!_NextActiveRequestOwner(owner, quantum)) {
|
||||
// we've been asked to terminate
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
while (resourcesAvailable && iterationBandwidth >= fBlockSize) {
|
||||
//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 "
|
||||
"operation %ld: %p\n", i++, operation);
|
||||
|
||||
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_STARTED,
|
||||
this, operation->Parent(), operation);
|
||||
|
||||
fIOCallback(fIOCallbackData, operation);
|
||||
|
||||
_Finisher();
|
||||
}
|
||||
|
||||
// wait for all operations to finish
|
||||
while (true) {
|
||||
while (!fTerminating) {
|
||||
locker.Lock();
|
||||
|
||||
if (fPendingOperations == 0)
|
||||
@@ -729,6 +796,9 @@ IOScheduler::_RequestNotifier()
|
||||
IORequest* request = fFinishedRequests.RemoveHead();
|
||||
|
||||
if (request == NULL) {
|
||||
if (fTerminating)
|
||||
return B_OK;
|
||||
|
||||
ConditionVariableEntry entry;
|
||||
fFinishedRequestCondition.Add(&entry);
|
||||
|
||||
@@ -740,6 +810,9 @@ IOScheduler::_RequestNotifier()
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_FINISHED,
|
||||
this, request);
|
||||
|
||||
// notify the request
|
||||
request->NotifyFinished();
|
||||
}
|
||||
@@ -796,3 +869,72 @@ IOScheduler::_IOCallbackWrapper(void* data, io_operation* 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);
|
||||
}
|
||||
|
||||
@@ -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].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef IO_SCHEDULER_H
|
||||
#define IO_SCHEDULER_H
|
||||
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <condition_variable.h>
|
||||
#include <lock.h>
|
||||
#include <Notifications.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
@@ -17,6 +19,16 @@
|
||||
#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 {
|
||||
public:
|
||||
virtual ~IOCallback();
|
||||
@@ -45,12 +57,13 @@ struct IORequestOwner : DoublyLinkedListLinkImpl<IORequestOwner> {
|
||||
};
|
||||
|
||||
|
||||
class IOScheduler {
|
||||
class IOScheduler : public DoublyLinkedListLinkImpl<IOScheduler> {
|
||||
public:
|
||||
IOScheduler(DMAResource* resource);
|
||||
~IOScheduler();
|
||||
|
||||
status_t Init(const char* name);
|
||||
status_t InitCheck() const;
|
||||
|
||||
void SetCallback(IOCallback& callback);
|
||||
void SetCallback(io_callback callback, void* data);
|
||||
@@ -65,6 +78,11 @@ public:
|
||||
// has been completed successfully or failed
|
||||
// for some reason
|
||||
|
||||
const char* Name() const { return fName; }
|
||||
|
||||
int32 ID() const { return fID; }
|
||||
void SetID(int32 id) { fID = id; }
|
||||
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
@@ -77,7 +95,7 @@ private:
|
||||
bool _FinisherWorkPending();
|
||||
off_t _ComputeRequestOwnerBandwidth(
|
||||
int32 priority) const;
|
||||
void _NextActiveRequestOwner(IORequestOwner*& owner,
|
||||
bool _NextActiveRequestOwner(IORequestOwner*& owner,
|
||||
off_t& quantum);
|
||||
bool _PrepareRequestOperations(IORequest* request,
|
||||
IOOperationList& operations,
|
||||
@@ -102,6 +120,8 @@ private:
|
||||
|
||||
private:
|
||||
DMAResource* fDMAResource;
|
||||
char* fName;
|
||||
int32 fID;
|
||||
spinlock fFinisherLock;
|
||||
mutex fLock;
|
||||
thread_id fSchedulerThread;
|
||||
@@ -126,6 +146,45 @@ private:
|
||||
off_t fIterationBandwidth;
|
||||
off_t fMinOwnerBandwidth;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user