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