xref: /haiku/src/system/kernel/device_manager/IOSchedulerSimple.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2  * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "IOSchedulerSimple.h"
9 
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <algorithm>
16 
17 #include <khash.h>
18 #include <lock.h>
19 #include <thread_types.h>
20 #include <thread.h>
21 #include <util/AutoLock.h>
22 
23 #include "IOSchedulerRoster.h"
24 
25 
26 //#define TRACE_IO_SCHEDULER
27 #ifdef TRACE_IO_SCHEDULER
28 #	define TRACE(x...) dprintf(x)
29 #else
30 #	define TRACE(x...) ;
31 #endif
32 
33 
34 // #pragma mark -
35 
36 
37 void
38 IORequestOwner::Dump() const
39 {
40 	kprintf("IORequestOwner at %p\n", this);
41 	kprintf("  team:     %" B_PRId32 "\n", team);
42 	kprintf("  thread:   %" B_PRId32 "\n", thread);
43 	kprintf("  priority: %" B_PRId32 "\n", priority);
44 
45 	kprintf("  requests:");
46 	for (IORequestList::ConstIterator it = requests.GetIterator();
47 			IORequest* request = it.Next();) {
48 		kprintf(" %p", request);
49 	}
50 	kprintf("\n");
51 
52 	kprintf("  completed requests:");
53 	for (IORequestList::ConstIterator it = completed_requests.GetIterator();
54 			IORequest* request = it.Next();) {
55 		kprintf(" %p", request);
56 	}
57 	kprintf("\n");
58 
59 	kprintf("  operations:");
60 	for (IOOperationList::ConstIterator it = operations.GetIterator();
61 			IOOperation* operation = it.Next();) {
62 		kprintf(" %p", operation);
63 	}
64 	kprintf("\n");
65 }
66 
67 
68 // #pragma mark -
69 
70 
71 struct IOSchedulerSimple::RequestOwnerHashDefinition {
72 	typedef thread_id		KeyType;
73 	typedef IORequestOwner	ValueType;
74 
75 	size_t HashKey(thread_id key) const				{ return key; }
76 	size_t Hash(const IORequestOwner* value) const	{ return value->thread; }
77 	bool Compare(thread_id key, const IORequestOwner* value) const
78 		{ return value->thread == key; }
79 	IORequestOwner*& GetLink(IORequestOwner* value) const
80 		{ return value->hash_link; }
81 };
82 
83 struct IOSchedulerSimple::RequestOwnerHashTable
84 		: BOpenHashTable<RequestOwnerHashDefinition, false> {
85 };
86 
87 
88 IOSchedulerSimple::IOSchedulerSimple(DMAResource* resource)
89 	:
90 	IOScheduler(resource),
91 	fSchedulerThread(-1),
92 	fRequestNotifierThread(-1),
93 	fOperationArray(NULL),
94 	fAllocatedRequestOwners(NULL),
95 	fRequestOwners(NULL),
96 	fBlockSize(0),
97 	fPendingOperations(0),
98 	fTerminating(false)
99 {
100 	mutex_init(&fLock, "I/O scheduler");
101 	B_INITIALIZE_SPINLOCK(&fFinisherLock);
102 
103 	fNewRequestCondition.Init(this, "I/O new request");
104 	fFinishedOperationCondition.Init(this, "I/O finished operation");
105 	fFinishedRequestCondition.Init(this, "I/O finished request");
106 
107 }
108 
109 
110 IOSchedulerSimple::~IOSchedulerSimple()
111 {
112 	// shutdown threads
113 	MutexLocker locker(fLock);
114 	InterruptsSpinLocker finisherLocker(fFinisherLock);
115 	fTerminating = true;
116 
117 	fNewRequestCondition.NotifyAll();
118 	fFinishedOperationCondition.NotifyAll();
119 	fFinishedRequestCondition.NotifyAll();
120 
121 	finisherLocker.Unlock();
122 	locker.Unlock();
123 
124 	if (fSchedulerThread >= 0)
125 		wait_for_thread(fSchedulerThread, NULL);
126 
127 	if (fRequestNotifierThread >= 0)
128 		wait_for_thread(fRequestNotifierThread, NULL);
129 
130 	// destroy our belongings
131 	mutex_lock(&fLock);
132 	mutex_destroy(&fLock);
133 
134 	while (IOOperation* operation = fUnusedOperations.RemoveHead())
135 		delete operation;
136 
137 	delete[] fOperationArray;
138 
139 	delete fRequestOwners;
140 	delete[] fAllocatedRequestOwners;
141 }
142 
143 
144 status_t
145 IOSchedulerSimple::Init(const char* name)
146 {
147 	status_t error = IOScheduler::Init(name);
148 	if (error != B_OK)
149 		return error;
150 
151 	size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16;
152 	for (size_t i = 0; i < count; i++) {
153 		IOOperation* operation = new(std::nothrow) IOOperation;
154 		if (operation == NULL)
155 			return B_NO_MEMORY;
156 
157 		fUnusedOperations.Add(operation);
158 	}
159 
160 	fOperationArray = new(std::nothrow) IOOperation*[count];
161 
162 	if (fDMAResource != NULL)
163 		fBlockSize = fDMAResource->BlockSize();
164 	if (fBlockSize == 0)
165 		fBlockSize = 512;
166 
167 	fAllocatedRequestOwnerCount = thread_max_threads();
168 	fAllocatedRequestOwners
169 		= new(std::nothrow) IORequestOwner[fAllocatedRequestOwnerCount];
170 	if (fAllocatedRequestOwners == NULL)
171 		return B_NO_MEMORY;
172 
173 	for (int32 i = 0; i < fAllocatedRequestOwnerCount; i++) {
174 		IORequestOwner& owner = fAllocatedRequestOwners[i];
175 		owner.team = -1;
176 		owner.thread = -1;
177 		owner.priority = B_IDLE_PRIORITY;
178 		fUnusedRequestOwners.Add(&owner);
179 	}
180 
181 	fRequestOwners = new(std::nothrow) RequestOwnerHashTable;
182 	if (fRequestOwners == NULL)
183 		return B_NO_MEMORY;
184 
185 	error = fRequestOwners->Init(fAllocatedRequestOwnerCount);
186 	if (error != B_OK)
187 		return error;
188 
189 	// TODO: Use a device speed dependent bandwidths!
190 	fIterationBandwidth = fBlockSize * 8192;
191 	fMinOwnerBandwidth = fBlockSize * 1024;
192 	fMaxOwnerBandwidth = fBlockSize * 4096;
193 
194 	// start threads
195 	char buffer[B_OS_NAME_LENGTH];
196 	strlcpy(buffer, name, sizeof(buffer));
197 	strlcat(buffer, " scheduler ", sizeof(buffer));
198 	size_t nameLength = strlen(buffer);
199 	snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32,
200 		fID);
201 	fSchedulerThread = spawn_kernel_thread(&_SchedulerThread, buffer,
202 		B_NORMAL_PRIORITY + 2, (void *)this);
203 	if (fSchedulerThread < B_OK)
204 		return fSchedulerThread;
205 
206 	strlcpy(buffer, name, sizeof(buffer));
207 	strlcat(buffer, " notifier ", sizeof(buffer));
208 	nameLength = strlen(buffer);
209 	snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32,
210 		fID);
211 	fRequestNotifierThread = spawn_kernel_thread(&_RequestNotifierThread,
212 		buffer, B_NORMAL_PRIORITY + 2, (void *)this);
213 	if (fRequestNotifierThread < B_OK)
214 		return fRequestNotifierThread;
215 
216 	resume_thread(fSchedulerThread);
217 	resume_thread(fRequestNotifierThread);
218 
219 	return B_OK;
220 }
221 
222 
223 status_t
224 IOSchedulerSimple::ScheduleRequest(IORequest* request)
225 {
226 	TRACE("%p->IOSchedulerSimple::ScheduleRequest(%p)\n", this, request);
227 
228 	IOBuffer* buffer = request->Buffer();
229 
230 	// TODO: it would be nice to be able to lock the memory later, but we can't
231 	// easily do it in the I/O scheduler without being able to asynchronously
232 	// lock memory (via another thread or a dedicated call).
233 
234 	if (buffer->IsVirtual()) {
235 		status_t status = buffer->LockMemory(request->TeamID(),
236 			request->IsWrite());
237 		if (status != B_OK) {
238 			request->SetStatusAndNotify(status);
239 			return status;
240 		}
241 	}
242 
243 	MutexLocker locker(fLock);
244 
245 	IORequestOwner* owner = _GetRequestOwner(request->TeamID(),
246 		request->ThreadID(), true);
247 	if (owner == NULL) {
248 		panic("IOSchedulerSimple: Out of request owners!\n");
249 		locker.Unlock();
250 		if (buffer->IsVirtual())
251 			buffer->UnlockMemory(request->TeamID(), request->IsWrite());
252 		request->SetStatusAndNotify(B_NO_MEMORY);
253 		return B_NO_MEMORY;
254 	}
255 
256 	bool wasActive = owner->IsActive();
257 	request->SetOwner(owner);
258 	owner->requests.Add(request);
259 
260 	int32 priority = thread_get_io_priority(request->ThreadID());
261 	if (priority >= 0)
262 		owner->priority = priority;
263 //dprintf("  request %p -> owner %p (thread %ld, active %d)\n", request, owner, owner->thread, wasActive);
264 
265 	if (!wasActive)
266 		fActiveRequestOwners.Add(owner);
267 
268 	IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_SCHEDULED, this,
269 		request);
270 
271 	fNewRequestCondition.NotifyAll();
272 
273 	return B_OK;
274 }
275 
276 
277 void
278 IOSchedulerSimple::AbortRequest(IORequest* request, status_t status)
279 {
280 	// TODO:...
281 //B_CANCELED
282 }
283 
284 
285 void
286 IOSchedulerSimple::OperationCompleted(IOOperation* operation, status_t status,
287 	generic_size_t transferredBytes)
288 {
289 	InterruptsSpinLocker _(fFinisherLock);
290 
291 	// finish operation only once
292 	if (operation->Status() <= 0)
293 		return;
294 
295 	operation->SetStatus(status);
296 
297 	// set the bytes transferred (of the net data)
298 	generic_size_t partialBegin
299 		= operation->OriginalOffset() - operation->Offset();
300 	operation->SetTransferredBytes(
301 		transferredBytes > partialBegin ? transferredBytes - partialBegin : 0);
302 
303 	fCompletedOperations.Add(operation);
304 	fFinishedOperationCondition.NotifyAll();
305 }
306 
307 
308 void
309 IOSchedulerSimple::Dump() const
310 {
311 	kprintf("IOSchedulerSimple at %p\n", this);
312 	kprintf("  DMA resource:   %p\n", fDMAResource);
313 
314 	kprintf("  active request owners:");
315 	for (RequestOwnerList::ConstIterator it
316 				= fActiveRequestOwners.GetIterator();
317 			IORequestOwner* owner = it.Next();) {
318 		kprintf(" %p", owner);
319 	}
320 	kprintf("\n");
321 }
322 
323 
324 /*!	Must not be called with the fLock held. */
325 void
326 IOSchedulerSimple::_Finisher()
327 {
328 	while (true) {
329 		InterruptsSpinLocker locker(fFinisherLock);
330 		IOOperation* operation = fCompletedOperations.RemoveHead();
331 		if (operation == NULL)
332 			return;
333 
334 		locker.Unlock();
335 
336 		TRACE("IOSchedulerSimple::_Finisher(): operation: %p\n", operation);
337 
338 		bool operationFinished = operation->Finish();
339 
340 		IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_FINISHED,
341 			this, operation->Parent(), operation);
342 			// Notify for every time the operation is passed to the I/O hook,
343 			// not only when it is fully finished.
344 
345 		if (!operationFinished) {
346 			TRACE("  operation: %p not finished yet\n", operation);
347 			MutexLocker _(fLock);
348 			operation->SetTransferredBytes(0);
349 			operation->Parent()->Owner()->operations.Add(operation);
350 			fPendingOperations--;
351 			continue;
352 		}
353 
354 		// notify request and remove operation
355 		IORequest* request = operation->Parent();
356 
357 		generic_size_t operationOffset
358 			= operation->OriginalOffset() - request->Offset();
359 		request->OperationFinished(operation, operation->Status(),
360 			operation->TransferredBytes() < operation->OriginalLength(),
361 			operation->Status() == B_OK
362 				? operationOffset + operation->OriginalLength()
363 				: operationOffset);
364 
365 		// recycle the operation
366 		MutexLocker _(fLock);
367 		if (fDMAResource != NULL)
368 			fDMAResource->RecycleBuffer(operation->Buffer());
369 
370 		fPendingOperations--;
371 		fUnusedOperations.Add(operation);
372 
373 		// If the request is done, we need to perform its notifications.
374 		if (request->IsFinished()) {
375 			if (request->Status() == B_OK && request->RemainingBytes() > 0) {
376 				// The request has been processed OK so far, but it isn't really
377 				// finished yet.
378 				request->SetUnfinished();
379 			} else {
380 				// Remove the request from the request owner.
381 				IORequestOwner* owner = request->Owner();
382 				owner->requests.MoveFrom(&owner->completed_requests);
383 				owner->requests.Remove(request);
384 				request->SetOwner(NULL);
385 
386 				if (!owner->IsActive()) {
387 					fActiveRequestOwners.Remove(owner);
388 					fUnusedRequestOwners.Add(owner);
389 				}
390 
391 				if (request->HasCallbacks()) {
392 					// The request has callbacks that may take some time to
393 					// perform, so we hand it over to the request notifier.
394 					fFinishedRequests.Add(request);
395 					fFinishedRequestCondition.NotifyAll();
396 				} else {
397 					// No callbacks -- finish the request right now.
398 					IOSchedulerRoster::Default()->Notify(
399 						IO_SCHEDULER_REQUEST_FINISHED, this, request);
400 					request->NotifyFinished();
401 				}
402 			}
403 		}
404 	}
405 }
406 
407 
408 /*!	Called with \c fFinisherLock held.
409 */
410 bool
411 IOSchedulerSimple::_FinisherWorkPending()
412 {
413 	return !fCompletedOperations.IsEmpty();
414 }
415 
416 
417 bool
418 IOSchedulerSimple::_PrepareRequestOperations(IORequest* request,
419 	IOOperationList& operations, int32& operationsPrepared, off_t quantum,
420 	off_t& usedBandwidth)
421 {
422 //dprintf("IOSchedulerSimple::_PrepareRequestOperations(%p)\n", request);
423 	usedBandwidth = 0;
424 
425 	if (fDMAResource != NULL) {
426 		while (quantum >= (off_t)fBlockSize && request->RemainingBytes() > 0) {
427 			IOOperation* operation = fUnusedOperations.RemoveHead();
428 			if (operation == NULL)
429 				return false;
430 
431 			status_t status = fDMAResource->TranslateNext(request, operation,
432 				quantum);
433 			if (status != B_OK) {
434 				operation->SetParent(NULL);
435 				fUnusedOperations.Add(operation);
436 
437 				// B_BUSY means some resource (DMABuffers or
438 				// DMABounceBuffers) was temporarily unavailable. That's OK,
439 				// we'll retry later.
440 				if (status == B_BUSY)
441 					return false;
442 
443 				AbortRequest(request, status);
444 				return true;
445 			}
446 //dprintf("  prepared operation %p\n", operation);
447 
448 			off_t bandwidth = operation->Length();
449 			quantum -= bandwidth;
450 			usedBandwidth += bandwidth;
451 
452 			operations.Add(operation);
453 			operationsPrepared++;
454 		}
455 	} else {
456 		// TODO: If the device has block size restrictions, we might need to use
457 		// a bounce buffer.
458 		IOOperation* operation = fUnusedOperations.RemoveHead();
459 		if (operation == NULL)
460 			return false;
461 
462 		status_t status = operation->Prepare(request);
463 		if (status != B_OK) {
464 			operation->SetParent(NULL);
465 			fUnusedOperations.Add(operation);
466 			AbortRequest(request, status);
467 			return true;
468 		}
469 
470 		operation->SetOriginalRange(request->Offset(), request->Length());
471 		request->Advance(request->Length());
472 
473 		off_t bandwidth = operation->Length();
474 		quantum -= bandwidth;
475 		usedBandwidth += bandwidth;
476 
477 		operations.Add(operation);
478 		operationsPrepared++;
479 	}
480 
481 	return true;
482 }
483 
484 
485 off_t
486 IOSchedulerSimple::_ComputeRequestOwnerBandwidth(int32 priority) const
487 {
488 // TODO: Use a priority dependent quantum!
489 	return fMinOwnerBandwidth;
490 }
491 
492 
493 bool
494 IOSchedulerSimple::_NextActiveRequestOwner(IORequestOwner*& owner,
495 	off_t& quantum)
496 {
497 	while (true) {
498 		if (fTerminating)
499 			return false;
500 
501 		if (owner != NULL)
502 			owner = fActiveRequestOwners.GetNext(owner);
503 		if (owner == NULL)
504 			owner = fActiveRequestOwners.Head();
505 
506 		if (owner != NULL) {
507 			quantum = _ComputeRequestOwnerBandwidth(owner->priority);
508 			return true;
509 		}
510 
511 		// Wait for new requests owners. First check whether any finisher work
512 		// has to be done.
513 		InterruptsSpinLocker finisherLocker(fFinisherLock);
514 		if (_FinisherWorkPending()) {
515 			finisherLocker.Unlock();
516 			mutex_unlock(&fLock);
517 			_Finisher();
518 			mutex_lock(&fLock);
519 			continue;
520 		}
521 
522 		// Wait for new requests.
523 		ConditionVariableEntry entry;
524 		fNewRequestCondition.Add(&entry);
525 
526 		finisherLocker.Unlock();
527 		mutex_unlock(&fLock);
528 
529 		entry.Wait(B_CAN_INTERRUPT);
530 		_Finisher();
531 		mutex_lock(&fLock);
532 	}
533 }
534 
535 
536 struct OperationComparator {
537 	inline bool operator()(const IOOperation* a, const IOOperation* b)
538 	{
539 		off_t offsetA = a->Offset();
540 		off_t offsetB = b->Offset();
541 		return offsetA < offsetB
542 			|| (offsetA == offsetB && a->Length() > b->Length());
543 	}
544 };
545 
546 
547 void
548 IOSchedulerSimple::_SortOperations(IOOperationList& operations,
549 	off_t& lastOffset)
550 {
551 // TODO: _Scheduler() could directly add the operations to the array.
552 	// move operations to an array and sort it
553 	int32 count = 0;
554 	while (IOOperation* operation = operations.RemoveHead())
555 		fOperationArray[count++] = operation;
556 
557 	std::sort(fOperationArray, fOperationArray + count, OperationComparator());
558 
559 	// move the sorted operations to a temporary list we can work with
560 //dprintf("operations after sorting:\n");
561 	IOOperationList sortedOperations;
562 	for (int32 i = 0; i < count; i++)
563 //{
564 //dprintf("  %3ld: %p: offset: %lld, length: %lu\n", i, fOperationArray[i], fOperationArray[i]->Offset(), fOperationArray[i]->Length());
565 		sortedOperations.Add(fOperationArray[i]);
566 //}
567 
568 	// Sort the operations so that no two adjacent operations overlap. This
569 	// might result in several elevator runs.
570 	while (!sortedOperations.IsEmpty()) {
571 		IOOperation* operation = sortedOperations.Head();
572 		while (operation != NULL) {
573 			IOOperation* nextOperation = sortedOperations.GetNext(operation);
574 			if (operation->Offset() >= lastOffset) {
575 				sortedOperations.Remove(operation);
576 //dprintf("  adding operation %p\n", operation);
577 				operations.Add(operation);
578 				lastOffset = operation->Offset() + operation->Length();
579 			}
580 
581 			operation = nextOperation;
582 		}
583 
584 		if (!sortedOperations.IsEmpty())
585 			lastOffset = 0;
586 	}
587 }
588 
589 
590 status_t
591 IOSchedulerSimple::_Scheduler()
592 {
593 	IORequestOwner marker;
594 	marker.thread = -1;
595 	{
596 		MutexLocker locker(fLock);
597 		fActiveRequestOwners.Add(&marker, false);
598 	}
599 
600 	off_t lastOffset = 0;
601 
602 	IORequestOwner* owner = NULL;
603 	off_t quantum = 0;
604 
605 	while (!fTerminating) {
606 //dprintf("IOSchedulerSimple::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum);
607 		MutexLocker locker(fLock);
608 
609 		IOOperationList operations;
610 		int32 operationCount = 0;
611 		bool resourcesAvailable = true;
612 		off_t iterationBandwidth = fIterationBandwidth;
613 
614 		if (owner == NULL) {
615 			owner = fActiveRequestOwners.GetPrevious(&marker);
616 			quantum = 0;
617 			fActiveRequestOwners.Remove(&marker);
618 		}
619 
620 		if (owner == NULL || quantum < (off_t)fBlockSize) {
621 			if (!_NextActiveRequestOwner(owner, quantum)) {
622 				// we've been asked to terminate
623 				return B_OK;
624 			}
625 		}
626 
627 		while (resourcesAvailable && iterationBandwidth >= (off_t)fBlockSize) {
628 //dprintf("IOSchedulerSimple::_Scheduler(): request owner: %p (thread %ld)\n",
629 //owner, owner->thread);
630 			// Prepare operations for the owner.
631 
632 			// There might still be unfinished ones.
633 			while (IOOperation* operation = owner->operations.RemoveHead()) {
634 				// TODO: We might actually grant the owner more bandwidth than
635 				// it deserves.
636 				// TODO: We should make sure that after the first read operation
637 				// of a partial write, no other write operation to the same
638 				// location is scheduled!
639 				operations.Add(operation);
640 				operationCount++;
641 				off_t bandwidth = operation->Length();
642 				quantum -= bandwidth;
643 				iterationBandwidth -= bandwidth;
644 
645 				if (quantum < (off_t)fBlockSize
646 					|| iterationBandwidth < (off_t)fBlockSize) {
647 					break;
648 				}
649 			}
650 
651 			while (resourcesAvailable && quantum >= (off_t)fBlockSize
652 					&& iterationBandwidth >= (off_t)fBlockSize) {
653 				IORequest* request = owner->requests.Head();
654 				if (request == NULL) {
655 					resourcesAvailable = false;
656 if (operationCount == 0)
657 panic("no more requests for owner %p (thread %" B_PRId32 ")", owner, owner->thread);
658 					break;
659 				}
660 
661 				off_t bandwidth = 0;
662 				resourcesAvailable = _PrepareRequestOperations(request,
663 					operations, operationCount, quantum, bandwidth);
664 				quantum -= bandwidth;
665 				iterationBandwidth -= bandwidth;
666 				if (request->RemainingBytes() == 0 || request->Status() <= 0) {
667 					// If the request has been completed, move it to the
668 					// completed list, so we don't pick it up again.
669 					owner->requests.Remove(request);
670 					owner->completed_requests.Add(request);
671 				}
672 			}
673 
674 			// Get the next owner.
675 			if (resourcesAvailable)
676 				_NextActiveRequestOwner(owner, quantum);
677 		}
678 
679 		// If the current owner doesn't have anymore requests, we have to
680 		// insert our marker, since the owner will be gone in the next
681 		// iteration.
682 		if (owner->requests.IsEmpty()) {
683 			fActiveRequestOwners.Insert(owner, &marker);
684 			owner = NULL;
685 		}
686 
687 		if (operations.IsEmpty())
688 			continue;
689 
690 		fPendingOperations = operationCount;
691 
692 		locker.Unlock();
693 
694 		// sort the operations
695 		_SortOperations(operations, lastOffset);
696 
697 		// execute the operations
698 #ifdef TRACE_IO_SCHEDULER
699 		int32 i = 0;
700 #endif
701 		while (IOOperation* operation = operations.RemoveHead()) {
702 			TRACE("IOSchedulerSimple::_Scheduler(): calling callback for "
703 				"operation %ld: %p\n", i++, operation);
704 
705 			IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_STARTED,
706 				this, operation->Parent(), operation);
707 
708 			fIOCallback(fIOCallbackData, operation);
709 
710 			_Finisher();
711 		}
712 
713 		// wait for all operations to finish
714 		while (!fTerminating) {
715 			locker.Lock();
716 
717 			if (fPendingOperations == 0)
718 				break;
719 
720 			// Before waiting first check whether any finisher work has to be
721 			// done.
722 			InterruptsSpinLocker finisherLocker(fFinisherLock);
723 			if (_FinisherWorkPending()) {
724 				finisherLocker.Unlock();
725 				locker.Unlock();
726 				_Finisher();
727 				continue;
728 			}
729 
730 			// wait for finished operations
731 			ConditionVariableEntry entry;
732 			fFinishedOperationCondition.Add(&entry);
733 
734 			finisherLocker.Unlock();
735 			locker.Unlock();
736 
737 			entry.Wait(B_CAN_INTERRUPT);
738 			_Finisher();
739 		}
740 	}
741 
742 	return B_OK;
743 }
744 
745 
746 /*static*/ status_t
747 IOSchedulerSimple::_SchedulerThread(void *_self)
748 {
749 	IOSchedulerSimple *self = (IOSchedulerSimple *)_self;
750 	return self->_Scheduler();
751 }
752 
753 
754 status_t
755 IOSchedulerSimple::_RequestNotifier()
756 {
757 	while (true) {
758 		MutexLocker locker(fLock);
759 
760 		// get a request
761 		IORequest* request = fFinishedRequests.RemoveHead();
762 
763 		if (request == NULL) {
764 			if (fTerminating)
765 				return B_OK;
766 
767 			ConditionVariableEntry entry;
768 			fFinishedRequestCondition.Add(&entry);
769 
770 			locker.Unlock();
771 
772 			entry.Wait();
773 			continue;
774 		}
775 
776 		locker.Unlock();
777 
778 		IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_FINISHED,
779 			this, request);
780 
781 		// notify the request
782 		request->NotifyFinished();
783 	}
784 
785 	// never can get here
786 	return B_OK;
787 }
788 
789 
790 /*static*/ status_t
791 IOSchedulerSimple::_RequestNotifierThread(void *_self)
792 {
793 	IOSchedulerSimple *self = (IOSchedulerSimple*)_self;
794 	return self->_RequestNotifier();
795 }
796 
797 
798 IORequestOwner*
799 IOSchedulerSimple::_GetRequestOwner(team_id team, thread_id thread,
800 	bool allocate)
801 {
802 	// lookup in table
803 	IORequestOwner* owner = fRequestOwners->Lookup(thread);
804 	if (owner != NULL && !owner->IsActive())
805 		fUnusedRequestOwners.Remove(owner);
806 	if (owner != NULL || !allocate)
807 		return owner;
808 
809 	// not in table -- allocate an unused one
810 	RequestOwnerList existingOwners;
811 
812 	while ((owner = fUnusedRequestOwners.RemoveHead()) != NULL) {
813 		if (owner->thread < 0 || !Thread::IsAlive(owner->thread)) {
814 			if (owner->thread >= 0)
815 				fRequestOwners->RemoveUnchecked(owner);
816 			owner->team = team;
817 			owner->thread = thread;
818 			owner->priority = B_IDLE_PRIORITY;
819 			fRequestOwners->InsertUnchecked(owner);
820 			break;
821 		}
822 
823 		existingOwners.Add(owner);
824 	}
825 
826 	fUnusedRequestOwners.MoveFrom(&existingOwners);
827 	return owner;
828 }
829