1/*
2 * Copyright (c) 2002-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//-----------------------------------------------------------------------------
26//	Includes
27//-----------------------------------------------------------------------------
28
29#include <IOKit/IOTypes.h>
30#include "SCSIParallelTimer.h"
31
32
33//-----------------------------------------------------------------------------
34//	Macros
35//-----------------------------------------------------------------------------
36
37#define DEBUG 												0
38#define DEBUG_ASSERT_COMPONENT_NAME_STRING					"SPI TIMER"
39
40#if DEBUG
41#define SCSI_PARALLEL_TIMER_DEBUGGING_LEVEL					0
42#endif
43
44
45#include "IOSCSIParallelFamilyDebugging.h"
46
47
48#if ( SCSI_PARALLEL_TIMER_DEBUGGING_LEVEL >= 1 )
49#define PANIC_NOW(x)		panic x
50#else
51#define PANIC_NOW(x)
52#endif
53
54#if ( SCSI_PARALLEL_TIMER_DEBUGGING_LEVEL >= 2 )
55#define ERROR_LOG(x)		IOLog x
56#else
57#define ERROR_LOG(x)
58#endif
59
60#if ( SCSI_PARALLEL_TIMER_DEBUGGING_LEVEL >= 3 )
61#define STATUS_LOG(x)		IOLog x
62#else
63#define STATUS_LOG(x)
64#endif
65
66
67#define super IOTimerEventSource
68OSDefineMetaClassAndStructors ( SCSIParallelTimer, IOTimerEventSource );
69
70
71#if 0
72#pragma mark -
73#pragma mark IOKit Member Routines
74#pragma mark -
75#endif
76
77
78//-----------------------------------------------------------------------------
79//	CreateTimerEventSource									   [STATIC][PUBLIC]
80//-----------------------------------------------------------------------------
81
82SCSIParallelTimer *
83SCSIParallelTimer::CreateTimerEventSource ( OSObject * owner, Action action )
84{
85
86	SCSIParallelTimer *		timer = NULL;
87
88	timer = OSTypeAlloc ( SCSIParallelTimer );
89	require_nonzero ( timer, ErrorExit );
90
91	require ( timer->Init ( owner, action ), FreeTimer );
92
93	return timer;
94
95
96FreeTimer:
97
98
99	require_nonzero ( timer, ErrorExit );
100	timer->release ( );
101	timer = NULL;
102
103
104ErrorExit:
105
106
107	return timer;
108
109}
110
111
112//-----------------------------------------------------------------------------
113//	Enable - Enables timer.											   [PUBLIC]
114//-----------------------------------------------------------------------------
115
116bool
117SCSIParallelTimer::Init ( OSObject * owner, Action action )
118{
119
120	queue_init ( &fListHead );
121	return super::init ( owner, action );
122
123}
124
125
126//-----------------------------------------------------------------------------
127//	Enable - Enables timer.											   [PUBLIC]
128//-----------------------------------------------------------------------------
129
130void
131SCSIParallelTimer::Enable ( void )
132{
133	super::enable ( );
134}
135
136
137//-----------------------------------------------------------------------------
138//	Disable - Disables timer.										   [PUBLIC]
139//-----------------------------------------------------------------------------
140
141void
142SCSIParallelTimer::Disable ( void )
143{
144	super::disable ( );
145}
146
147
148//-----------------------------------------------------------------------------
149//	CancelTimeout - Cancels timeout.								   [PUBLIC]
150//-----------------------------------------------------------------------------
151
152void
153SCSIParallelTimer::CancelTimeout ( void )
154{
155	super::cancelTimeout ( );
156}
157
158
159//-----------------------------------------------------------------------------
160//	BeginTimeoutContext - Begins context.							   [PUBLIC]
161//-----------------------------------------------------------------------------
162
163void
164SCSIParallelTimer::BeginTimeoutContext ( void )
165{
166
167	closeGate ( );
168	fHandlingTimeout = true;
169	openGate ( );
170
171}
172
173
174//-----------------------------------------------------------------------------
175//	EndTimeoutContext - Ends context.								   [PUBLIC]
176//-----------------------------------------------------------------------------
177
178
179void
180SCSIParallelTimer::EndTimeoutContext ( void )
181{
182
183	closeGate ( );
184	fHandlingTimeout = false;
185	openGate ( );
186
187}
188
189
190//-----------------------------------------------------------------------------
191//	CompareDeadlines - Compares absolute times.						   [PUBLIC]
192//-----------------------------------------------------------------------------
193
194SInt32
195SCSIParallelTimer::CompareDeadlines ( AbsoluteTime time1, AbsoluteTime time2 )
196{
197
198	return CMP_ABSOLUTETIME ( &time1, &time2 );
199
200}
201
202
203//-----------------------------------------------------------------------------
204//	GetDeadline - Gets the deadline from the task.					   [PUBLIC]
205//-----------------------------------------------------------------------------
206
207AbsoluteTime
208SCSIParallelTimer::GetDeadline ( SCSIParallelTask * task )
209{
210
211	check ( task != NULL );
212	return task->GetTimeoutDeadline ( );
213
214}
215
216
217//-----------------------------------------------------------------------------
218//	GetTimeoutDuration - Gets the timeout from the task.			   [PUBLIC]
219//-----------------------------------------------------------------------------
220
221UInt32
222SCSIParallelTimer::GetTimeoutDuration ( SCSIParallelTask * task )
223{
224
225	check ( task != NULL );
226	return task->GetTimeoutDuration ( );
227
228}
229
230
231//-----------------------------------------------------------------------------
232//	GetExpiredTask - Gets the task which timed out.					   [PUBLIC]
233//-----------------------------------------------------------------------------
234
235SCSIParallelTaskIdentifier
236SCSIParallelTimer::GetExpiredTask ( void )
237{
238
239	SCSIParallelTask *	expiredTask = NULL;
240
241	closeGate ( );
242
243	if ( queue_empty ( &fListHead ) == false )
244	{
245
246		uint64_t			now;
247		AbsoluteTime		deadline1;
248		AbsoluteTime		deadline2;
249		SCSIParallelTask *	task;
250
251        task		= ( SCSIParallelTask * ) queue_first ( &fListHead );
252        now 		= mach_absolute_time ( );
253        deadline1	= *( AbsoluteTime * ) &now;
254		deadline2 	= GetDeadline ( task );
255
256		if ( CompareDeadlines ( deadline1, deadline2 ) == 1 )
257		{
258
259			queue_remove_first ( &fListHead, expiredTask, SCSIParallelTask *, fTimeoutChain );
260
261		}
262
263	}
264
265	openGate ( );
266
267	return ( SCSIParallelTaskIdentifier ) expiredTask;
268
269}
270
271
272//-----------------------------------------------------------------------------
273//	SetTimeout - Sets timeout.										   [PUBLIC]
274//-----------------------------------------------------------------------------
275
276IOReturn
277SCSIParallelTimer::SetTimeout ( SCSIParallelTaskIdentifier	taskIdentifier,
278								UInt32						inTimeoutMS )
279{
280
281	SCSIParallelTask *	task 		= ( SCSIParallelTask * ) taskIdentifier;
282	IOReturn			status		= kIOReturnBadArgument;
283	AbsoluteTime		deadline;
284
285	require_nonzero ( task, ErrorExit );
286
287	// Close the gate in order to ensure single-threaded access to list
288	closeGate ( );
289
290	// Did the HBA override the timeout value in the task?
291	if ( inTimeoutMS == kTimeoutValueNone )
292	{
293
294		// No, use the timeout value in the task (in milliseconds)
295		inTimeoutMS = GetTimeoutDuration ( task );
296
297		// Is the timeout set to infinite?
298		if ( inTimeoutMS == kTimeoutValueNone )
299		{
300
301			// Yes, set to longest possible timeout (ULONG_MAX)
302			inTimeoutMS = 0xFFFFFFFF;
303
304		}
305
306	}
307
308	clock_interval_to_deadline ( inTimeoutMS, kMillisecondScale, &deadline );
309	task->SetTimeoutDeadline ( deadline );
310
311	// 1) Check if we have a list head. If not, put this
312	// element at the beginning.
313	// 2) Check if the task has a shorter timeout than the list head
314	if ( ( queue_empty ( &fListHead ) == true ) ||
315		 ( CompareDeadlines ( GetDeadline ( ( SCSIParallelTask * ) queue_first ( &fListHead ) ), deadline ) == 1 ) )
316	{
317
318		queue_enter_first ( &fListHead, task, SCSIParallelTask *, fTimeoutChain );
319		Rearm ( );
320
321	}
322
323	// 3) In the normal case, I/Os are coming down with standard timeout intervals (30s). In this
324	// case, we try to check against the last I/O on the timeout list (to avoid walking the entire
325	// list in the normal case).
326	else if ( CompareDeadlines ( deadline, GetDeadline ( ( SCSIParallelTask * ) queue_last ( &fListHead ) ) ) == 1 )
327	{
328
329		queue_enter ( &fListHead, task, SCSIParallelTask *, fTimeoutChain );
330
331	}
332
333	// 4) Walk the entire list looking for the proper slot. <sigh>
334	else
335	{
336
337		SCSIParallelTask *	currentTask = NULL;
338		bool				slotFound	= false;
339
340		queue_iterate ( &fListHead, currentTask, SCSIParallelTask *, fTimeoutChain )
341		{
342
343			// Check if the next deadline is greater or not.
344			if ( CompareDeadlines ( GetDeadline ( currentTask ), deadline ) == 1 )
345			{
346
347				// Found the slot. This task should be ahead of currentTask.
348				queue_insert_before ( &fListHead, task, currentTask, SCSIParallelTask *, fTimeoutChain );
349				slotFound = true;
350
351				// We're done. Break out.
352				break;
353
354			}
355
356		}
357
358		if ( slotFound == false )
359		{
360
361			// Found the slot (end of the list).
362			queue_enter ( &fListHead, task, SCSIParallelTask *, fTimeoutChain );
363
364		}
365
366	}
367
368	openGate ( );
369	status = kIOReturnSuccess;
370
371
372ErrorExit:
373
374
375	return status;
376
377}
378
379
380//-----------------------------------------------------------------------------
381//	RemoveTask - Removes a task from the timeout list.				   [PUBLIC]
382//-----------------------------------------------------------------------------
383
384void
385SCSIParallelTimer::RemoveTask ( SCSIParallelTaskIdentifier parallelRequest )
386{
387
388	SCSIParallelTask *	task		= NULL;
389	bool				headOfList	= false;
390
391	task = OSDynamicCast ( SCSIParallelTask, parallelRequest );
392
393	require_nonzero ( task, Exit );
394	require_nonzero ( ( task->fTimeoutChain.next ), Exit );
395	require_nonzero ( ( task->fTimeoutChain.prev ), Exit );
396
397	closeGate ( );
398
399	require ( ( queue_empty ( &fListHead ) == false ), ExitGate );
400
401	if ( task == ( SCSIParallelTask * ) queue_first ( &fListHead ) )
402		headOfList = true;
403
404	queue_remove ( &fListHead, task, SCSIParallelTask *, fTimeoutChain );
405
406	// Special case for parallelRequest being the list head.
407	if ( headOfList == true )
408	{
409
410		Rearm ( );
411
412	}
413
414
415ExitGate:
416
417
418	openGate ( );
419
420
421Exit:
422
423
424	return;
425
426}
427
428
429//-----------------------------------------------------------------------------
430//	Rearm - Arms the timeout timer.									   [PUBLIC]
431//-----------------------------------------------------------------------------
432
433bool
434SCSIParallelTimer::Rearm ( void )
435{
436
437	bool	result = false;
438
439	closeGate ( );
440
441	if ( ( queue_empty ( &fListHead ) == false ) && ( fHandlingTimeout == false ) )
442	{
443
444		// Re-arm the timer with new timeout deadline
445		wakeAtTime ( GetDeadline ( ( SCSIParallelTask * ) queue_first ( &fListHead ) ) );
446		result = true;
447
448	}
449
450	else
451	{
452
453		// No list head, cancel the timer.
454		cancelTimeout ( );
455
456	}
457
458	openGate ( );
459
460	return result;
461
462}