1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
|
////////////////////////////////////////////////////////////////////////////////////
// w32chan.c Fish's new i/o scheduling logic
////////////////////////////////////////////////////////////////////////////////////
// (c) Copyright "Fish" (David B. Trout), 2001-2009. Released under the Q Public License
// (http://www.hercules-390.org/herclic.html) as modifications to Hercules.
////////////////////////////////////////////////////////////////////////////////////
// $Id: w32chan.c 5127 2009-01-23 13:25:01Z bernard $
//
// $Log$
// Revision 1.28 2007/11/30 14:54:33 jmaynard
// Changed conmicro.cx to hercules-390.org or conmicro.com, as needed.
//
// Revision 1.27 2007/06/23 00:04:19 ivan
// Update copyright notices to include current year (2007)
//
// Revision 1.26 2006/12/08 09:43:31 jj
// Add CVS message log
//
#include "hstdinc.h"
#define _W32CHAN_C_
#define _HENGINE_DLL_
#include "hercules.h"
#include "w32chan.h"
#if defined(OPTION_FISHIO)
/////////////////////////////////////////////////////////////////////////////
// Internal scheduler helper macros...
#if defined(FISH_HANG)
#define LockScheduler() (FishHang_EnterCriticalSection(__FILE__,__LINE__,&IOSchedulerLock))
#define LockThreadParms(pThreadParms) (FishHang_EnterCriticalSection(__FILE__,__LINE__,&pThreadParms->IORequestListLock))
#define UnlockScheduler() (FishHang_LeaveCriticalSection(__FILE__,__LINE__,&IOSchedulerLock))
#define UnlockThreadParms(pThreadParms) (FishHang_LeaveCriticalSection(__FILE__,__LINE__,&pThreadParms->IORequestListLock))
#else
#define LockScheduler() (EnterCriticalSection(&IOSchedulerLock))
#define LockThreadParms(pThreadParms) (EnterCriticalSection(&pThreadParms->IORequestListLock))
#define UnlockScheduler() (LeaveCriticalSection(&IOSchedulerLock))
#define UnlockThreadParms(pThreadParms) (LeaveCriticalSection(&pThreadParms->IORequestListLock))
#endif
/////////////////////////////////////////////////////////////////////////////
// i/o scheduler variables... (some private, some externally visible)
int ios_arch_mode = 0; // current architecture mode
int ios_devthread_timeout = 30; // max device thread wait time
int* ios_devthread_prio = NULL; // pointer to sysblk.devprio
LIST_ENTRY ThreadListHeadListEntry; // anchor for DEVTHREADPARMS linked list
CRITICAL_SECTION IOSchedulerLock; // lock for accessing above list
// and below variables
long ios_devtwait = 0; // #of threads currently idle
int ios_devtnbr = 0; // #of threads currently active
int ios_devthwm = 0; // max #of threads that WERE active
int ios_devtmax = 0; // max #of threads there can be
int ios_devtunavail = 0; // #of times 'idle' thread unavailable
/////////////////////////////////////////////////////////////////////////////
// initialize i/o scheduler variables and work areas...
void InitIOScheduler
(
int arch_mode, // (for calling execute_ccw_chain)
int* devt_prio, // (ptr to device thread priority)
int devt_timeout, // (MAX_DEVICE_THREAD_IDLE_SECS)
long devt_max // (maximum #of device threads allowed)
)
{
ios_arch_mode = arch_mode;
ios_devthread_prio = devt_prio;
ios_devthread_timeout = devt_timeout;
ios_devtmax = devt_max;
InitializeListHead(&ThreadListHeadListEntry);
MyInitializeCriticalSection(&IOSchedulerLock);
}
/////////////////////////////////////////////////////////////////////////////
// device_thread parms block...
typedef struct _DevThreadParms
{
DWORD dwThreadID; // device_thread thread id
LIST_ENTRY ThreadListLinkingListEntry; // (just a link in the chain)
HANDLE hShutdownEvent; // tells device thread to exit
HANDLE hRequestQueuedEvent; // tells device thread it has work
LIST_ENTRY IORequestListHeadListEntry; // anchor for DEVIOREQUEST list
CRITICAL_SECTION IORequestListLock; // (for accessing above list)
BOOL bThreadIsDead; // TRUE = thread has exited
}
DEVTHREADPARMS;
/////////////////////////////////////////////////////////////////////////////
// i/o request block...
typedef struct _DevIORequest
{
LIST_ENTRY IORequestListLinkingListEntry; // (just a link in the chain)
void* pDevBlk; // (ptr to device block)
int* pnDevPrio; // (ptr to device i/o priority)
unsigned short wDevNum; // (device number for debugging)
}
DEVIOREQUEST;
/////////////////////////////////////////////////////////////////////////////
// (forward references for some of our functions...)
DEVTHREADPARMS* SelectDeviceThread();
DEVTHREADPARMS* CreateDeviceThread(unsigned short wDevNum);
void AdjustThreadPriority(int* pCurPrio, int* pNewPrio);
void* DeviceThread(void* pThreadParms);
void RemoveDeadThreadsFromList();
void RemoveThisThreadFromOurList(DEVTHREADPARMS* pThreadParms);
/////////////////////////////////////////////////////////////////////////////
// Schedule a DeviceThread for this i/o request... (called by the 'startio'
// function whenever the startio or start subchannel instruction is executed)
int ScheduleIORequest(void* pDevBlk, unsigned short wDevNum, int* pnDevPrio)
{
/////////////////////////////////////////////////////////////////
// PROGRAMMING NOTE: The various errors that can occur in this
// function should probably be reported by returning cc1 with
// 'channel control check' set (or something similar), but until
// I can work out the details, I'm going to be lazy and just
// return cc2 (subchannel busy) for now to allow the system to
// retry the i/o request if it so desires. Hopefully the problem
// was only intermittent and will work the second time around.
/////////////////////////////////////////////////////////////////
DEVIOREQUEST* pIORequest; // ptr to i/o request
DEVTHREADPARMS* pThreadParms; // ptr to device_thread parameters
// Create an i/o request queue entry for this i/o request
pIORequest = (DEVIOREQUEST*) malloc(sizeof(DEVIOREQUEST));
if (!pIORequest)
{
logmsg(_("HHCCP084E malloc(DEVIOREQUEST) failed; device=%4.4X, strerror=\"%s\"\n"),
wDevNum,strerror(errno));
return 2;
}
InitializeListLink(&pIORequest->IORequestListLinkingListEntry);
pIORequest->pDevBlk = pDevBlk;
pIORequest->wDevNum = wDevNum;
pIORequest->pnDevPrio = pnDevPrio;
// Schedule a device_thread to process this i/o request
LockScheduler(); // (lock scheduler vars)
if (ios_devtmax < 0)
{
// Create new "one time only" thread each time...
// (Note: the device thread's parms will automatically
// be locked upon return if creation was successful.)
RemoveDeadThreadsFromList(); // (prevent runaway list)
if (!(pThreadParms = CreateDeviceThread(wDevNum)))
{
UnlockScheduler(); // (unlock scheduler vars)
free(pIORequest); // (discard i/o request)
return 2; // (return device busy)
}
}
else
{
// Select a non-busy device thread to handle this i/o request...
// (Note: the device thread's parms will automatically
// be locked upon return if selection was successful.)
if (!(pThreadParms = SelectDeviceThread()))
{
// All threads are currently busy or no threads exist yet.
// Since the possibility of a deadlock[1] can easily occur if we schedule
// an i/o request to a currently busy device thread[2], we have no choice
// but to create another device thread.
// [1] Not an actual programmatic deadlock of course, but nonetheless
// a deadlock from the guest operating system's point of view.
// [2] A curently busy device thread could easily have its channel program
// suspended and thus would never see the queued request until such time
// its suspended channel program was resumed and allowed to complete, and
// if the request we queued (that would end up waiting to be processed
// by the currently suspended device thread) just so happens to be one
// that the operating system needs to have completed before it can resume
// the currently suspended channel program, then the operating system will
// obviously hang. (It would end up waiting for an i/o to complete that
// would never complete until the currently suspended channel program was
// resumed, which would never be resumed until the queued i/o completes,
// which would never even be processed until the currently suspended
// channel program is resumed ..... etc.)
if (ios_devtmax && ios_devtnbr >= ios_devtmax) // max threads already created?
{
logmsg(_("HHCCP085W *WARNING* max device threads exceeded.\n"));
ios_devtunavail++; // (count occurrences)
}
// Create a new device thread for this i/o request...
// (Note: the device thread's parms will automatically
// be locked upon return if creation was successful.)
if (!(pThreadParms = CreateDeviceThread(wDevNum)))
{
UnlockScheduler(); // (unlock scheduler vars)
free(pIORequest); // (discard i/o request)
return 2; // (return device busy)
}
}
}
// (Note: the thread parms lock should still be held at this point)
// Queue the i/o request to the selected device_thread's i/o request queue...
InsertListTail(&pThreadParms->IORequestListHeadListEntry,&pIORequest->IORequestListLinkingListEntry);
// Tell device_thread it has work (must do this while its request list is still
// locked to prevent it from prematurely exiting in case it's just about to die)
MySetEvent(pThreadParms->hRequestQueuedEvent);
// Now unlock its request queue so it can process the request we just gave it
UnlockThreadParms(pThreadParms); // (let it proceed)
// We're done, so unlock the scheduler vars so another cpu thread
// in the configuration can schedule and i/o request and then exit
// back the the startio function...
UnlockScheduler(); // (unlock vars and exit; we're done)
return 0; // (success)
}
/////////////////////////////////////////////////////////////////////////////
// Select a non-busy DeviceThread...
// NOTE! the IOSchedulerLock must be acquired before calling this function!
// NOTE! the selected thread's parms will be locked upon return!
DEVTHREADPARMS* SelectDeviceThread()
{
DEVTHREADPARMS* pThreadParms; // ptr to selected device thread
LIST_ENTRY* pListEntry; // (work)
pListEntry = ThreadListHeadListEntry.Flink;
while (pListEntry != &ThreadListHeadListEntry)
{
pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
LockThreadParms(pThreadParms); // (freeze moving target)
if (pThreadParms->bThreadIsDead)
{
UnlockThreadParms(pThreadParms);
RemoveThisThreadFromOurList(pThreadParms);
pListEntry = ThreadListHeadListEntry.Flink;
continue;
}
if (!IsEventSet(pThreadParms->hRequestQueuedEvent))
{
RemoveListEntry(pListEntry);
InsertListTail(&ThreadListHeadListEntry,pListEntry);
return pThreadParms;
}
UnlockThreadParms(pThreadParms); // (can't use this one)
pListEntry = pListEntry->Flink;
}
return NULL; // (all of them are busy)
}
/////////////////////////////////////////////////////////////////////////////
// create a new device thread and add it to the list...
// NOTE! the IOSchedulerLock must be acquired before calling this function!
// NOTE! the created thread's parms will be locked upon return!
DEVTHREADPARMS* CreateDeviceThread(unsigned short wDevNum)
{
DEVTHREADPARMS* pThreadParms; // ptr to returned device_thread parameters
DWORD dwThreadID; // (work)
pThreadParms = malloc(sizeof(DEVTHREADPARMS)); // (allocate structure)
if (!pThreadParms)
{
logmsg(_("HHCCP086E malloc(DEVTHREADPARMS) failed; device=%4.4X, strerror=\"%s\"\n"),
wDevNum,strerror(errno));
return NULL; // (error)
}
pThreadParms->hShutdownEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL);
if (!pThreadParms->hShutdownEvent)
{
logmsg(_("HHCCP087E CreateEvent(hShutdownEvent) failed; device=%4.4X, strerror=\"%s\"\n"),
wDevNum,strerror(errno));
free(pThreadParms);
return NULL; // (error)
}
pThreadParms->hRequestQueuedEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL);
if (!pThreadParms->hRequestQueuedEvent)
{
logmsg(_("HHCCP088E CreateEvent(hRequestQueuedEvent) failed; device=%4.4X, strerror=\"%s\"\n"),
wDevNum,strerror(errno));
MyCloseHandle(pThreadParms->hShutdownEvent);
free(pThreadParms);
return NULL; // (error)
}
MyInitializeCriticalSection(&pThreadParms->IORequestListLock);
InitializeListLink(&pThreadParms->ThreadListLinkingListEntry);
InitializeListHead(&pThreadParms->IORequestListHeadListEntry);
pThreadParms->bThreadIsDead = FALSE;
pThreadParms->dwThreadID = 0;
#ifdef FISH_HANG
if (fthread_create(__FILE__,__LINE__,&dwThreadID,NULL,DeviceThread,pThreadParms,"DeviceThread") != 0)
#else
if (fthread_create(&dwThreadID,NULL,DeviceThread,pThreadParms,"DeviceThread") != 0)
#endif
{
logmsg(_("HHCCP089E fthread_create(DeviceThread) failed; device=%4.4X, strerror=\"%s\"\n"),
wDevNum,strerror(errno));
MyCloseHandle(pThreadParms->hShutdownEvent);
MyCloseHandle(pThreadParms->hRequestQueuedEvent);
MyDeleteCriticalSection(&pThreadParms->IORequestListLock);
free(pThreadParms);
return NULL; // (error)
}
// Add the newly created device_thread to the end of our list of managed threads.
InsertListTail(&ThreadListHeadListEntry,&pThreadParms->ThreadListLinkingListEntry);
if (++ios_devtnbr > ios_devthwm) ios_devthwm = ios_devtnbr;
LockThreadParms(pThreadParms); // (lock thread parms before using)
return pThreadParms; // (success)
}
/////////////////////////////////////////////////////////////////////////////
// helper function to set a thread's priority to its proper value
void AdjustThreadPriority(int* pCurPrio, int* pNewPrio)
{
if (*pCurPrio != *pNewPrio)
{
setpriority(PRIO_PROCESS, 0, *pNewPrio);
*pCurPrio = *pNewPrio;
}
}
/////////////////////////////////////////////////////////////////////////////
// the device_thread itself... (processes queued i/o request
// by calling "execute_ccw_chain" function in channel.c...)
extern void call_execute_ccw_chain(int arch_mode, void* pDevBlk);
void* DeviceThread (void* pArg)
{
DEVTHREADPARMS* pThreadParms; // ptr to thread parms
LIST_ENTRY* pListEntry; // (work)
DEVIOREQUEST* pIORequest; // ptr to i/o request
void* pDevBlk; // ptr to device block
int* pnDevPrio; // ptr to device i/o priority
int nCurPrio; // current thread priority
pThreadParms = (DEVTHREADPARMS*) pArg;
pThreadParms->dwThreadID = GetCurrentThreadId();
nCurPrio = getpriority(PRIO_PROCESS, 0);
AdjustThreadPriority(&nCurPrio,ios_devthread_prio);
for (;;)
{
// Wait for an i/o request to be queued...
InterlockedIncrement(&ios_devtwait);
MyWaitForSingleObject(pThreadParms->hRequestQueuedEvent,ios_devthread_timeout * 1000);
InterlockedDecrement(&ios_devtwait);
if (IsEventSet(pThreadParms->hShutdownEvent)) break;
// Lock our queue so it doesn't change while we take a look at it...
LockThreadParms(pThreadParms); // (freeze moving target)
// Check to see if we have any work...
if (IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
{
// We've waited long enough...
pThreadParms->bThreadIsDead = TRUE; // (keep scheduler informed)
UnlockThreadParms(pThreadParms); // (let go of our parms block)
return NULL; // (return, NOT break!)
}
// Remove the i/o request from our queue...
// It's important that we remove the request from our queue but
// NOT reset our flag (if the list is now empty) until AFTER we've
// processed the request (see further below for details as to why).
pListEntry = RemoveListHead(&pThreadParms->IORequestListHeadListEntry);
UnlockThreadParms(pThreadParms); // (done with thread parms for now)
pIORequest = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
pDevBlk = pIORequest->pDevBlk; // (need ptr to devblk)
pnDevPrio = pIORequest->pnDevPrio; // (need ptr to devprio)
free(pIORequest); // (not needed anymore)
// Process the i/o request by calling the proper 'execute_ccw_chain'
// function (based on architectural mode) in source module channel.c
// Set thread priority to requested device level
AdjustThreadPriority(&nCurPrio,pnDevPrio);
call_execute_ccw_chain(ios_arch_mode, pDevBlk); // (process i/o request)
// Reset thread priority, if necessary
if (nCurPrio > *ios_devthread_prio)
AdjustThreadPriority(&nCurPrio,ios_devthread_prio);
////////////////////////////////////////////////////////////////////////////
//
// * * * I M P O R T A N T * * *
//
// It's important that we reset our flag AFTER we're done with our request
// in order to prevent the scheduler from trying to give us more work while
// we're still busy processing the current i/o request (in case the channel
// program we're processing gets suspended). If the channel program we're
// processing gets suspended and the scheduler places another request in our
// queue, it won't ever get processed until our suspended channel program is
// resumed, and if the request that was placed in our queue was a request
// for an i/o to another device that the operating system needs to complete
// in order to obtain the information needed to resume our suspended channel
// program, a deadlock will occur! (i.e. if the operating system needs to
// read data from another device before it can resume our channel program,
// then the i/o request to read from that device better not be given to us
// because we're suspended and will never get around to executing it until
// we're resumed, which will never happen until the i/o request waiting in
// our queue is completed, which will never happend until we're resumed,
// etc...)
//
////////////////////////////////////////////////////////////////////////////
// Now that we're done this i/o, reset our flag.
LockThreadParms(pThreadParms); // (freeze moving target)
if (IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
MyResetEvent(pThreadParms->hRequestQueuedEvent);
UnlockThreadParms(pThreadParms); // (thaw moving target)
if (IsEventSet(pThreadParms->hShutdownEvent)) break;
// If we're a "one time only" thread, then we're done.
if (ios_devtmax < 0)
{
// Note: no need to lock our thread parms before setting 'dead' flag
// since the scheduler should never queue us another request anyway
// because we're a "one-time-only" thread.
pThreadParms->bThreadIsDead = TRUE; // (let garbage collector discard us)
return NULL; // (return, NOT break!)
}
}
// The only time we reach here is if the shutdown event was signalled (i.e. we
// were manually "cancelled" or asked to stop processing; i.e. the i/o subsystem
// was reset). Discard all i/o requests that may still be remaining in our queue.
TRACE("** DeviceThread %8.8X: shutdown detected\n",
(unsigned int)pThreadParms->dwThreadID);
LockThreadParms(pThreadParms); // (freeze moving target)
// Discard all queued i/o requests...
while (!IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
{
pListEntry = RemoveListHead(&pThreadParms->IORequestListHeadListEntry);
pIORequest = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
TRACE("** DeviceThread %8.8X: discarding i/o request for device %4.4X\n",
(unsigned int)pThreadParms->dwThreadID,pIORequest->wDevNum);
free(pIORequest);
}
pThreadParms->bThreadIsDead = TRUE; // (tell scheduler we've died)
TRACE("** DeviceThread %8.8X: shutdown complete\n",
(unsigned int)pThreadParms->dwThreadID);
UnlockThreadParms(pThreadParms); // (thaw moving target)
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
// shutdown idle threads (if possible) until number drops below threshold...
void TrimDeviceThreads()
{
DEVTHREADPARMS* pThreadParms;
BOOL bThreadIsBusy;
LIST_ENTRY* pListEntry;
LockScheduler(); // (lock scheduler vars)
pListEntry = ThreadListHeadListEntry.Flink;
while (pListEntry != &ThreadListHeadListEntry && ios_devtnbr > ios_devtmax)
{
pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
pListEntry = pListEntry->Flink;
LockThreadParms(pThreadParms);
bThreadIsBusy = IsEventSet(pThreadParms->hRequestQueuedEvent);
UnlockThreadParms(pThreadParms);
if (bThreadIsBusy) continue;
// Thread is currently idle and awaiting work. Tell it to exit.
// (Note: we need to signal the hRequestQueuedEvent event too so that
// it can wake up and notice that the hShutdownEvent event is signaled)
MySetEvent(pThreadParms->hShutdownEvent);
MySetEvent(pThreadParms->hRequestQueuedEvent); // (wakeup thread)
Sleep(10); // (give it time to die)
RemoveDeadThreadsFromList();
}
UnlockScheduler(); // (unlock shceduler vars)
}
#if 0 // (the following is not needed yet)
/////////////////////////////////////////////////////////////////////////////
// ask all device_threads to exit... (i.e. i/o subsystem reset)
void KillAllDeviceThreads()
{
DEVTHREADPARMS* pThreadParms;
LIST_ENTRY* pListEntry;
TRACE("** ENTRY KillAllDeviceThreads\n");
LockScheduler(); // (lock scheduler vars)
RemoveDeadThreadsFromList(); // (discard dead threads)
while (!IsListEmpty(&ThreadListHeadListEntry))
{
pListEntry = ThreadListHeadListEntry.Flink; // (get starting link in chain)
while (pListEntry != &ThreadListHeadListEntry) // (while not end of chain reached...)
{
pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
// (Note: we need to signal the hRequestQueuedEvent event too so that
// it can wake up and notice that the hShutdownEvent event is signaled)
TRACE("** KillAllDeviceThreads: setting shutdown event for thread %8.8X\n",
pThreadParms->dwThreadID);
LockThreadParms(pThreadParms); // (lock thread parms)
MySetEvent(pThreadParms->hShutdownEvent); // (request shutdown)
MySetEvent(pThreadParms->hRequestQueuedEvent); // (wakeup thread)
UnlockThreadParms(pThreadParms); // (unlock thread parms)
pListEntry = pListEntry->Flink; // (go on to next link in chain)
}
UnlockScheduler(); // (unlock scheduler vars)
TRACE("** KillAllDeviceThreads: waiting for thread(s) to shutdown\n");
Sleep(10); // (give them time to die)
LockScheduler(); // (re-lock scheduler vars)
RemoveDeadThreadsFromList(); // (discard dead threads)
}
UnlockScheduler(); // (unlock scheduler vars)
TRACE("** EXIT KillAllDeviceThreads\n");
}
#endif // (the preceding is not needed yet)
/////////////////////////////////////////////////////////////////////////////
// remove all dead threads from our list of threads...
// NOTE! the IOSchedulerLock must be acquired before calling this function!
void RemoveDeadThreadsFromList()
{
DEVTHREADPARMS* pThreadParms;
BOOL bThreadIsDead;
LIST_ENTRY* pListEntry = ThreadListHeadListEntry.Flink;
while (pListEntry != &ThreadListHeadListEntry)
{
pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
pListEntry = pListEntry->Flink;
LockThreadParms(pThreadParms);
bThreadIsDead = pThreadParms->bThreadIsDead;
UnlockThreadParms(pThreadParms);
if (bThreadIsDead) RemoveThisThreadFromOurList(pThreadParms);
}
}
/////////////////////////////////////////////////////////////////////////////
// private function to remove an entry from our list and discard it...
// NOTE! the IOSchedulerLock must be acquired before calling this function!
void RemoveThisThreadFromOurList(DEVTHREADPARMS* pThreadParms)
{
RemoveListEntry(&pThreadParms->ThreadListLinkingListEntry);
MyCloseHandle(pThreadParms->hShutdownEvent);
MyCloseHandle(pThreadParms->hRequestQueuedEvent);
MyDeleteCriticalSection(&pThreadParms->IORequestListLock);
free(pThreadParms);
ios_devtnbr--; // (track number of active device_thread)
}
/////////////////////////////////////////////////////////////////////////////
// Debugging...
#if defined(FISH_HANG)
char PrintDEVIOREQUESTBuffer[2048];
char* PrintDEVIOREQUEST(DEVIOREQUEST* pIORequest, DEVTHREADPARMS* pDEVTHREADPARMS)
{
LIST_ENTRY* pListEntry;
DEVIOREQUEST* pNextDEVIOREQUEST;
pListEntry = pIORequest->IORequestListLinkingListEntry.Flink;
if (pListEntry != &pDEVTHREADPARMS->IORequestListHeadListEntry)
{
pNextDEVIOREQUEST = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
}
else pNextDEVIOREQUEST = (DEVIOREQUEST*) &pDEVTHREADPARMS->IORequestListHeadListEntry;
snprintf(PrintDEVIOREQUESTBuffer,sizeof(PrintDEVIOREQUESTBuffer),
"DEVIOREQUEST @ %8.8X\n"
" pDevBlk = %8.8X\n"
" wDevNum = %4.4X\n"
" IORequestListLinkingListEntry = %8.8X\n",
(int)pIORequest,
(int)pIORequest->pDevBlk,
pIORequest->wDevNum,
(int)pNextDEVIOREQUEST
);
PrintDEVIOREQUESTBuffer[ sizeof(PrintDEVIOREQUESTBuffer) - 1 ] = 0;
return PrintDEVIOREQUESTBuffer;
}
/////////////////////////////////////////////////////////////////////////////
void PrintAllDEVIOREQUESTs(DEVTHREADPARMS* pDEVTHREADPARMS)
{
DEVIOREQUEST* pDEVIOREQUEST;
LIST_ENTRY* pListEntry;
pListEntry = pDEVTHREADPARMS->IORequestListHeadListEntry.Flink;
while (pListEntry != &pDEVTHREADPARMS->IORequestListHeadListEntry)
{
pDEVIOREQUEST = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
pListEntry = pListEntry->Flink;
FishHang_Printf("%s\n",PrintDEVIOREQUEST(pDEVIOREQUEST,pDEVTHREADPARMS));
}
}
/////////////////////////////////////////////////////////////////////////////
char PrintDEVTHREADPARMSBuffer[4096];
char* PrintDEVTHREADPARMS(DEVTHREADPARMS* pDEVTHREADPARMS)
{
LIST_ENTRY* pListEntry;
DEVIOREQUEST* pDEVIOREQUEST;
DEVTHREADPARMS* pNextDEVTHREADPARMS;
pListEntry = pDEVTHREADPARMS->IORequestListHeadListEntry.Flink;
if (pListEntry != &pDEVTHREADPARMS->IORequestListHeadListEntry)
{
pDEVIOREQUEST = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
}
else pDEVIOREQUEST = NULL;
pListEntry = pDEVTHREADPARMS->ThreadListLinkingListEntry.Flink;
if (pListEntry != &ThreadListHeadListEntry)
{
pNextDEVTHREADPARMS = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
}
else pNextDEVTHREADPARMS = (DEVTHREADPARMS*) &ThreadListHeadListEntry;
snprintf(PrintDEVTHREADPARMSBuffer,sizeof(PrintDEVTHREADPARMSBuffer),
"DEVTHREADPARMS @ %8.8X\n"
" dwThreadID = %8.8X\n"
" bThreadIsDead = %s\n"
" hShutdownEvent = %8.8X %s\n"
" hRequestQueuedEvent = %8.8X %s\n"
" IORequestListHeadListEntry = %8.8X\n"
" ThreadListLinkingListEntry = %8.8X\n"
,(int)pDEVTHREADPARMS
,(int)pDEVTHREADPARMS->dwThreadID
,pDEVTHREADPARMS->bThreadIsDead ? "TRUE" : "false"
,(unsigned int)pDEVTHREADPARMS->hShutdownEvent
, IsEventSet(pDEVTHREADPARMS->hShutdownEvent) ? "** SIGNALED **" : "(not signaled)"
,(unsigned int)pDEVTHREADPARMS->hRequestQueuedEvent
, IsEventSet(pDEVTHREADPARMS->hRequestQueuedEvent) ? "** SIGNALED **" : "(not signaled)"
,(int)pDEVIOREQUEST
,(int)pNextDEVTHREADPARMS
);
PrintDEVTHREADPARMSBuffer[ sizeof(PrintDEVTHREADPARMSBuffer) - 1 ] = 0;
return PrintDEVTHREADPARMSBuffer;
}
/////////////////////////////////////////////////////////////////////////////
// (called by panel.c "FishHangReport" command...)
void PrintAllDEVTHREADPARMSs()
{
LIST_ENTRY* pListEntry;
DEVTHREADPARMS* pDEVTHREADPARMS;
LockScheduler();
pListEntry = ThreadListHeadListEntry.Flink;
if (pListEntry != &ThreadListHeadListEntry)
{
pDEVTHREADPARMS = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
}
else pDEVTHREADPARMS = (DEVTHREADPARMS*) &ThreadListHeadListEntry;
FishHang_Printf("\nDEVTHREADPARMS LIST ANCHOR @ %8.8X --> %8.8X\n\n",
(int)&ThreadListHeadListEntry,(int)pDEVTHREADPARMS);
while (pListEntry != &ThreadListHeadListEntry)
{
pDEVTHREADPARMS = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
LockThreadParms(pDEVTHREADPARMS);
FishHang_Printf("%s\n",PrintDEVTHREADPARMS(pDEVTHREADPARMS));
PrintAllDEVIOREQUESTs(pDEVTHREADPARMS);
UnlockThreadParms(pDEVTHREADPARMS);
pListEntry = pListEntry->Flink;
}
UnlockScheduler();
}
/////////////////////////////////////////////////////////////////////////////
#endif // defined(FISH_HANG)
/////////////////////////////////////////////////////////////////////////////
#endif // !defined(OPTION_FISHIO)
|