UefiCpuPkg/MpInitLib: wait no longer than necessary for initial AP startup

Sometimes a platform knows exactly how many CPUs it has at boot. It should
be able to
- set PcdCpuMaxLogicalProcessorNumber dynamically to this number,
- set PcdCpuApInitTimeOutInMicroSeconds to a very long time (for example
  MAX_UINT32, approx. 71 minutes),
- and expect that MpInitLib wait exactly as long as necessary for all APs
  to report in.

Other platforms should be able to continue setting a reasonably large
upper bound on supported CPUs, and waiting for a reasonable, fixed amount
of time for all APs to report in.

Add this functionality. The TimedWaitForApFinish() function will return
when all APs have reported in, or the timeout has expired -- whichever
happens first.

(Accessing these PCDs dynamically is safe. The PEI and DXE phase instances
of this library are restricted to PEIM and DXE_DRIVER client modules, thus
the PCD accesses cannot be linked into runtime code.)

Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Jeff Fan <jeff.fan@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Michael Kinney <michael.d.kinney@intel.com>
Bugzilla: https://bugzilla.tianocore.org/show_bug.cgi?id=116
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Reviewed-by: Jeff Fan <jeff.fan@intel.com>
This commit is contained in:
Laszlo Ersek 2016-11-24 12:19:54 +01:00
parent 2b2efe33ea
commit 6e1987f19a
1 changed files with 72 additions and 1 deletions

View File

@ -683,6 +683,21 @@ FillExchangeInfoData (
AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
} }
/**
Helper function that waits until the finished AP count reaches the specified
limit, or the specified timeout elapses (whichever comes first).
@param[in] CpuMpData Pointer to CPU MP Data.
@param[in] FinishedApLimit The number of finished APs to wait for.
@param[in] TimeLimit The number of microseconds to wait for.
**/
VOID
TimedWaitForApFinish (
IN CPU_MP_DATA *CpuMpData,
IN UINT32 FinishedApLimit,
IN UINT32 TimeLimit
);
/** /**
This function will be called by BSP to wakeup AP. This function will be called by BSP to wakeup AP.
@ -748,7 +763,11 @@ WakeUpAP (
// //
// Wait for all potential APs waken up in one specified period // Wait for all potential APs waken up in one specified period
// //
MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds)); TimedWaitForApFinish (
CpuMpData,
PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,
PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)
);
} else { } else {
// //
// Wait all APs waken up if this is not the 1st broadcast of SIPI // Wait all APs waken up if this is not the 1st broadcast of SIPI
@ -894,6 +913,58 @@ CheckTimeout (
return FALSE; return FALSE;
} }
/**
Helper function that waits until the finished AP count reaches the specified
limit, or the specified timeout elapses (whichever comes first).
@param[in] CpuMpData Pointer to CPU MP Data.
@param[in] FinishedApLimit The number of finished APs to wait for.
@param[in] TimeLimit The number of microseconds to wait for.
**/
VOID
TimedWaitForApFinish (
IN CPU_MP_DATA *CpuMpData,
IN UINT32 FinishedApLimit,
IN UINT32 TimeLimit
)
{
//
// CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0
// "infinity", so check for (TimeLimit == 0) explicitly.
//
if (TimeLimit == 0) {
return;
}
CpuMpData->TotalTime = 0;
CpuMpData->ExpectedTime = CalculateTimeout (
TimeLimit,
&CpuMpData->CurrentTime
);
while (CpuMpData->FinishedCount < FinishedApLimit &&
!CheckTimeout (
&CpuMpData->CurrentTime,
&CpuMpData->TotalTime,
CpuMpData->ExpectedTime
)) {
CpuPause ();
}
if (CpuMpData->FinishedCount >= FinishedApLimit) {
DEBUG ((
DEBUG_VERBOSE,
"%a: reached FinishedApLimit=%u in %Lu microseconds\n",
__FUNCTION__,
FinishedApLimit,
DivU64x64Remainder (
MultU64x32 (CpuMpData->TotalTime, 1000000),
GetPerformanceCounterProperties (NULL, NULL),
NULL
)
));
}
}
/** /**
Reset an AP to Idle state. Reset an AP to Idle state.