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}