diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
| -rw-r--r-- | arch/powerpc/kernel/rtas.c | 104 | 
1 files changed, 91 insertions, 13 deletions
| diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index ff80bbad22a5..733e6ef36758 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -492,8 +492,25 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)  }  EXPORT_SYMBOL(rtas_call); -/* For RTAS_BUSY (-2), delay for 1 millisecond.  For an extended busy status - * code of 990n, perform the hinted delay of 10^n (last digit) milliseconds. +/** + * rtas_busy_delay_time() - From an RTAS status value, calculate the + *                          suggested delay time in milliseconds. + * + * @status: a value returned from rtas_call() or similar APIs which return + *          the status of a RTAS function call. + * + * Context: Any context. + * + * Return: + * * 100000 - If @status is 9905. + * * 10000  - If @status is 9904. + * * 1000   - If @status is 9903. + * * 100    - If @status is 9902. + * * 10     - If @status is 9901. + * * 1      - If @status is either 9900 or -2. This is "wrong" for -2, but + *            some callers depend on this behavior, and the worst outcome + *            is that they will delay for longer than necessary. + * * 0      - If @status is not a busy or extended delay value.   */  unsigned int rtas_busy_delay_time(int status)  { @@ -513,17 +530,77 @@ unsigned int rtas_busy_delay_time(int status)  }  EXPORT_SYMBOL(rtas_busy_delay_time); -/* For an RTAS busy status code, perform the hinted delay. */ -unsigned int rtas_busy_delay(int status) +/** + * rtas_busy_delay() - helper for RTAS busy and extended delay statuses + * + * @status: a value returned from rtas_call() or similar APIs which return + *          the status of a RTAS function call. + * + * Context: Process context. May sleep or schedule. + * + * Return: + * * true  - @status is RTAS_BUSY or an extended delay hint. The + *           caller may assume that the CPU has been yielded if necessary, + *           and that an appropriate delay for @status has elapsed. + *           Generally the caller should reattempt the RTAS call which + *           yielded @status. + * + * * false - @status is not @RTAS_BUSY nor an extended delay hint. The + *           caller is responsible for handling @status. + */ +bool rtas_busy_delay(int status)  {  	unsigned int ms; +	bool ret; -	might_sleep(); -	ms = rtas_busy_delay_time(status); -	if (ms && need_resched()) -		msleep(ms); +	switch (status) { +	case RTAS_EXTENDED_DELAY_MIN...RTAS_EXTENDED_DELAY_MAX: +		ret = true; +		ms = rtas_busy_delay_time(status); +		/* +		 * The extended delay hint can be as high as 100 seconds. +		 * Surely any function returning such a status is either +		 * buggy or isn't going to be significantly slowed by us +		 * polling at 1HZ. Clamp the sleep time to one second. +		 */ +		ms = clamp(ms, 1U, 1000U); +		/* +		 * The delay hint is an order-of-magnitude suggestion, not +		 * a minimum. It is fine, possibly even advantageous, for +		 * us to pause for less time than hinted. For small values, +		 * use usleep_range() to ensure we don't sleep much longer +		 * than actually needed. +		 * +		 * See Documentation/timers/timers-howto.rst for +		 * explanation of the threshold used here. In effect we use +		 * usleep_range() for 9900 and 9901, msleep() for +		 * 9902-9905. +		 */ +		if (ms <= 20) +			usleep_range(ms * 100, ms * 1000); +		else +			msleep(ms); +		break; +	case RTAS_BUSY: +		ret = true; +		/* +		 * We should call again immediately if there's no other +		 * work to do. +		 */ +		cond_resched(); +		break; +	default: +		ret = false; +		/* +		 * Not a busy or extended delay status; the caller should +		 * handle @status itself. Ensure we warn on misuses in +		 * atomic context regardless. +		 */ +		might_sleep(); +		break; +	} -	return ms; +	return ret;  }  EXPORT_SYMBOL(rtas_busy_delay); @@ -809,13 +886,13 @@ void rtas_os_term(char *str)  /**   * rtas_activate_firmware() - Activate a new version of firmware.   * + * Context: This function may sleep. + *   * Activate a new version of partition firmware. The OS must call this   * after resuming from a partition hibernation or migration in order   * to maintain the ability to perform live firmware updates. It's not   * catastrophic for this method to be absent or to fail; just log the   * condition in that case. - * - * Context: This function may sleep.   */  void rtas_activate_firmware(void)  { @@ -890,11 +967,12 @@ int rtas_call_reentrant(int token, int nargs, int nret, int *outputs, ...)  #endif /* CONFIG_PPC_PSERIES */  /** - * Find a specific pseries error log in an RTAS extended event log. + * get_pseries_errorlog() - Find a specific pseries error log in an RTAS + *                          extended event log.   * @log: RTAS error/event log   * @section_id: two character section identifier   * - * Returns a pointer to the specified errorlog or NULL if not found. + * Return: A pointer to the specified errorlog or NULL if not found.   */  struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log,  					      uint16_t section_id) |