From 461d68d43d69a3d96750448ea5c3e0a68fc8d4c6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 4 Apr 2025 12:48:42 +0200 Subject: pwm: Add actual hardware state to pwm debugfs file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Traditionally /sys/kernel/debug/pwm only contained info from pwm->state. Most of the time this data represents the last requested setting which might differ considerably from the actually configured in hardware setting. Expand the information in the debugfs file with the actual values. Signed-off-by: Uwe Kleine-König Reviewed-by: Trevor Gamblin Link: https://lore.kernel.org/r/20250404104844.543479-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0387bd838487..02d98e4c75a5 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -2221,25 +2221,28 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) for (i = 0; i < chip->npwm; i++) { struct pwm_device *pwm = &chip->pwms[i]; - struct pwm_state state; + struct pwm_state state, hwstate; pwm_get_state(pwm, &state); + pwm_get_state_hw(pwm, &hwstate); seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); if (test_bit(PWMF_REQUESTED, &pwm->flags)) seq_puts(s, " requested"); - if (state.enabled) - seq_puts(s, " enabled"); + seq_puts(s, "\n"); - seq_printf(s, " period: %llu ns", state.period); - seq_printf(s, " duty: %llu ns", state.duty_cycle); - seq_printf(s, " polarity: %s", + seq_printf(s, " requested configuration: %3sabled, %llu/%llu ns, %s polarity", + state.enabled ? "en" : "dis", state.duty_cycle, state.period, state.polarity ? "inverse" : "normal"); - if (state.usage_power) - seq_puts(s, " usage_power"); + seq_puts(s, ", usage_power"); + seq_puts(s, "\n"); + + seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity", + hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period, + hwstate.polarity ? "inverse" : "normal"); seq_puts(s, "\n"); } -- cgit v1.2.3 From 96d20cfd16e779923153f7347b0bef6b3c7606ce Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 5 Apr 2025 11:27:17 +0200 Subject: pwm: Do stricter return value checking for .round_waveform_tohw() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .round_waveform_tohw() is supposed to return 0 if the request could be rounded down to match the hardware capabilities and return 1 if rounding down wasn't possible. Expand the PWM_DEBUG check to not only assert proper downrounding if 0 was returned but also check that it was actually rounded up when the callback signalled uprounding. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/dfb824ae37f99df068c752d48cbd163c044a74fb.1743844730.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 02d98e4c75a5..59cc8792e312 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -270,10 +270,10 @@ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform * wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw); if (IS_ENABLED(CONFIG_PWM_DEBUG) && - ret_tohw == 0 && !pwm_check_rounding(&wf_req, wf)) - dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + (ret_tohw == 0) != pwm_check_rounding(&wf_req, wf)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, - wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns); + wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, ret_tohw); return ret_tohw; } @@ -341,10 +341,10 @@ static int __pwm_set_waveform(struct pwm_device *pwm, if (err) return err; - if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw == 0 && !pwm_check_rounding(wf, &wf_rounded)) - dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n", + if (IS_ENABLED(CONFIG_PWM_DEBUG) && (ret_tohw == 0) != pwm_check_rounding(wf, &wf_rounded)) + dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu], ret: %d\n", wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, - wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns); + wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns, ret_tohw); if (exact && pwmwfcmp(wf, &wf_rounded)) { dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n", -- cgit v1.2.3 From e463b05d10da12b13d03f41a407e2ad043af158f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 8 Apr 2025 16:23:54 +0200 Subject: pwm: Better document return value of pwm_round_waveform_might_sleep() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better explain how pwm_round_waveform_might_sleep() (and so the respective lowlevel driver callback) is supposed to round and the meaning of the return value. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/db84abf1e82e4498fc0e7c318d2673771d0039fe.1744120697.git.ukleinek@kernel.org [ukleinek: Fix a rst formatting issue reported by Stephen Rothwell] Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 59cc8792e312..079964961bd8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -229,8 +229,12 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c * these two calls and the waveform determined by * pwm_round_waveform_might_sleep() cannot be implemented any more. * - * Returns 0 on success, 1 if there is no valid hardware configuration matching - * the input waveform under the PWM rounding rules or a negative errno. + * Usually all values passed in @wf are rounded down to the nearest possible + * value (in the order period_length_ns, duty_length_ns and then + * duty_offset_ns). Only if this isn't possible, a value might grow. + * + * Returns 0 on success, 1 if at least one value had to be rounded up or a + * negative errno. */ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { -- cgit v1.2.3 From 7f8ce4d88b42fcbd3350370ec4d02e00979fc5a9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 17 Apr 2025 20:16:11 +0200 Subject: pwm: Fix various formatting issues in kernel-doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Return and (where interesting) Context sections, fix some formatting and drop documenting the internal function __pwm_apply(). Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20250417181611.2693599-2-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 42 +++++++++++++++++++++++++++--------------- include/linux/pwm.h | 8 +++++--- 2 files changed, 32 insertions(+), 18 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 079964961bd8..e0a90c4cd723 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -216,14 +216,14 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c * * Typically a given waveform cannot be implemented exactly by hardware, e.g. * because hardware only supports coarse period resolution or no duty_offset. - * This function returns the actually implemented waveform if you pass wf to - * pwm_set_waveform_might_sleep now. + * This function returns the actually implemented waveform if you pass @wf to + * pwm_set_waveform_might_sleep() now. * * Note however that the world doesn't stop turning when you call it, so when - * doing + * doing:: * - * pwm_round_waveform_might_sleep(mypwm, &wf); - * pwm_set_waveform_might_sleep(mypwm, &wf, true); + * pwm_round_waveform_might_sleep(mypwm, &wf); + * pwm_set_waveform_might_sleep(mypwm, &wf, true); * * the latter might fail, e.g. because an input clock changed its rate between * these two calls and the waveform determined by @@ -233,8 +233,9 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c * value (in the order period_length_ns, duty_length_ns and then * duty_offset_ns). Only if this isn't possible, a value might grow. * - * Returns 0 on success, 1 if at least one value had to be rounded up or a + * Returns: 0 on success, 1 if at least one value had to be rounded up or a * negative errno. + * Context: May sleep. */ int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { @@ -291,6 +292,9 @@ EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep); * * Stores the current configuration of the PWM in @wf. Note this is the * equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform. + * + * Returns: 0 on success or a negative errno + * Context: May sleep. */ int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf) { @@ -399,13 +403,17 @@ static int __pwm_set_waveform(struct pwm_device *pwm, * * Typically a requested waveform cannot be implemented exactly, e.g. because * you requested .period_length_ns = 100 ns, but the hardware can only set - * periods that are a multiple of 8.5 ns. With that hardware passing exact = + * periods that are a multiple of 8.5 ns. With that hardware passing @exact = * true results in pwm_set_waveform_might_sleep() failing and returning 1. If - * exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger + * @exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger * than the requested value). - * Note that even with exact = true, some rounding by less than 1 is + * Note that even with @exact = true, some rounding by less than 1 ns is * possible/needed. In the above example requesting .period_length_ns = 94 and - * exact = true, you get the hardware configured with period = 93.5 ns. + * @exact = true, you get the hardware configured with period = 93.5 ns. + * + * Returns: 0 on success, 1 if was rounded up (if !@exact) or no perfect match was + * possible (if @exact), or a negative errno + * Context: May sleep. */ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact) @@ -565,11 +573,6 @@ static bool pwm_state_valid(const struct pwm_state *state) return true; } -/** - * __pwm_apply() - atomically apply a new state to a PWM device - * @pwm: PWM device - * @state: new state to apply - */ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) { struct pwm_chip *chip; @@ -678,6 +681,9 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state) * Cannot be used in atomic context. * @pwm: PWM device * @state: new state to apply + * + * Returns: 0 on success, or a negative errno + * Context: May sleep. */ int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state) { @@ -719,6 +725,9 @@ EXPORT_SYMBOL_GPL(pwm_apply_might_sleep); * Not all PWM devices support this function, check with pwm_might_sleep(). * @pwm: PWM device * @state: new state to apply + * + * Returns: 0 on success, or a negative errno + * Context: Any */ int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state) { @@ -792,6 +801,9 @@ EXPORT_SYMBOL_GPL(pwm_get_state_hw); * This function will adjust the PWM config to the PWM arguments provided * by the DT or PWM lookup table. This is particularly useful to adapt * the bootloader config to the Linux one. + * + * Returns: 0 on success or a negative error code on failure. + * Context: May sleep. */ int pwm_adjust_config(struct pwm_device *pwm) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index bf0469b2201d..63a17d2b4ec8 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -218,6 +218,8 @@ static inline void pwm_init_state(const struct pwm_device *pwm, * * pwm_get_state(pwm, &state); * duty = pwm_get_relative_duty_cycle(&state, 100); + * + * Returns: rounded relative duty cycle multiplied by @scale */ static inline unsigned int pwm_get_relative_duty_cycle(const struct pwm_state *state, unsigned int scale) @@ -244,8 +246,8 @@ pwm_get_relative_duty_cycle(const struct pwm_state *state, unsigned int scale) * pwm_set_relative_duty_cycle(&state, 50, 100); * pwm_apply_might_sleep(pwm, &state); * - * This functions returns -EINVAL if @duty_cycle and/or @scale are - * inconsistent (@scale == 0 or @duty_cycle > @scale). + * Returns: 0 on success or ``-EINVAL`` if @duty_cycle and/or @scale are + * inconsistent (@scale == 0 or @duty_cycle > @scale) */ static inline int pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, @@ -351,7 +353,7 @@ struct pwm_chip { * pwmchip_supports_waveform() - checks if the given chip supports waveform callbacks * @chip: The pwm_chip to test * - * Returns true iff the pwm chip support the waveform functions like + * Returns: true iff the pwm chip support the waveform functions like * pwm_set_waveform_might_sleep() and pwm_round_waveform_might_sleep() */ static inline bool pwmchip_supports_waveform(struct pwm_chip *chip) -- cgit v1.2.3 From e866834c8baabc33b431902beeeb0c94dfbc1024 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 30 Apr 2025 13:55:58 +0200 Subject: pwm: Let pwm_set_waveform_might_sleep() fail for exact but impossible requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now pwm_set_waveform_might_sleep() returned 1 for exact requests that couldn't be served exactly. In contrast to pwm_round_waveform_might_sleep() and pwm_set_waveform_might_sleep() with exact = false this is an error condition. So simplify handling for callers of pwm_set_waveform_might_sleep() by returning -EDOM instead of 1 in this case. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20538a46719584dafd8a1395c886780a97dcdf79.1746010245.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index e0a90c4cd723..28cb6ab0f62d 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -404,15 +404,16 @@ static int __pwm_set_waveform(struct pwm_device *pwm, * Typically a requested waveform cannot be implemented exactly, e.g. because * you requested .period_length_ns = 100 ns, but the hardware can only set * periods that are a multiple of 8.5 ns. With that hardware passing @exact = - * true results in pwm_set_waveform_might_sleep() failing and returning 1. If - * @exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger - * than the requested value). + * true results in pwm_set_waveform_might_sleep() failing and returning -EDOM. + * If @exact = false you get a period of 93.5 ns (i.e. the biggest period not + * bigger than the requested value). * Note that even with @exact = true, some rounding by less than 1 ns is * possible/needed. In the above example requesting .period_length_ns = 94 and * @exact = true, you get the hardware configured with period = 93.5 ns. * - * Returns: 0 on success, 1 if was rounded up (if !@exact) or no perfect match was - * possible (if @exact), or a negative errno + * Returns: 0 on success, 1 if was rounded up (if !@exact), -EDOM if setting + * failed due to the exact waveform not being possible (if @exact), or a + * different negative errno on failure. * Context: May sleep. */ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, @@ -440,6 +441,16 @@ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, err = __pwm_set_waveform(pwm, wf, exact); } + /* + * map err == 1 to -EDOM for exact requests. Also make sure that -EDOM is + * only returned in exactly that case. Note that __pwm_set_waveform() + * should never return -EDOM which justifies the unlikely(). + */ + if (unlikely(err == -EDOM)) + err = -EINVAL; + else if (exact && err == 1) + err = -EDOM; + return err; } EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep); -- cgit v1.2.3 From 164c4ac754abaf9643815d09001cc7d81042d624 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 30 Apr 2025 13:55:59 +0200 Subject: pwm: Let pwm_set_waveform_might_sleep() return 0 instead of 1 after rounding up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While telling the caller of pwm_set_waveform_might_sleep() if the request was completed by rounding down only or (some) rounding up gives additional information, it makes usage this function needlessly hard and the additional information is not used. A prove for that is that currently both users of this function just pass the returned value up to their caller even though a positive value isn't intended there. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/528cc3bbd9e35dea8646b1bcc0fbfe6c498bb4ed.1746010245.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 28cb6ab0f62d..5cf64b3a4cdf 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -411,9 +411,8 @@ static int __pwm_set_waveform(struct pwm_device *pwm, * possible/needed. In the above example requesting .period_length_ns = 94 and * @exact = true, you get the hardware configured with period = 93.5 ns. * - * Returns: 0 on success, 1 if was rounded up (if !@exact), -EDOM if setting - * failed due to the exact waveform not being possible (if @exact), or a - * different negative errno on failure. + * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not + * being possible (if @exact), or a different negative errno on failure. * Context: May sleep. */ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, @@ -442,14 +441,17 @@ int pwm_set_waveform_might_sleep(struct pwm_device *pwm, } /* - * map err == 1 to -EDOM for exact requests. Also make sure that -EDOM is - * only returned in exactly that case. Note that __pwm_set_waveform() - * should never return -EDOM which justifies the unlikely(). + * map err == 1 to -EDOM for exact requests and 0 for !exact ones. Also + * make sure that -EDOM is only returned in exactly that case. Note that + * __pwm_set_waveform() should never return -EDOM which justifies the + * unlikely(). */ if (unlikely(err == -EDOM)) err = -EINVAL; else if (exact && err == 1) err = -EDOM; + else if (err == 1) + err = 0; return err; } -- cgit v1.2.3 From d041b76ac9fb9e60e7cdb0265ed9d8b6058a88bf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 30 Apr 2025 13:56:00 +0200 Subject: pwm: Formally describe the procedure used to pick a hardware waveform setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This serves as specification for both, PWM consumers and the respective callback for lowlevel drivers. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/d2916bfa70274961ded26b07ab6998c36b90e69a.1746010245.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/core.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers/pwm/core.c') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 5cf64b3a4cdf..4d842c692194 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -231,7 +231,9 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c * * Usually all values passed in @wf are rounded down to the nearest possible * value (in the order period_length_ns, duty_length_ns and then - * duty_offset_ns). Only if this isn't possible, a value might grow. + * duty_offset_ns). Only if this isn't possible, a value might grow. See the + * documentation for pwm_set_waveform_might_sleep() for a more formal + * description. * * Returns: 0 on success, 1 if at least one value had to be rounded up or a * negative errno. @@ -411,6 +413,26 @@ static int __pwm_set_waveform(struct pwm_device *pwm, * possible/needed. In the above example requesting .period_length_ns = 94 and * @exact = true, you get the hardware configured with period = 93.5 ns. * + * Let C be the set of possible hardware configurations for a given PWM device, + * consisting of tuples (p, d, o) where p is the period length, d is the duty + * length and o the duty offset. + * + * The following algorithm is implemented to pick the hardware setting + * (p, d, o) ∈ C for a given request (p', d', o') with @exact = false:: + * + * p = max( { ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C ∧ ṗ ≤ p' } ∪ { min({ ṗ | ∃ ḋ, ȯ : (ṗ, ḋ, ȯ) ∈ C }) }) + * d = max( { ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C ∧ ḋ ≤ d' } ∪ { min({ ḋ | ∃ ȯ : (p, ḋ, ȯ) ∈ C }) }) + * o = max( { ȯ | (p, d, ȯ) ∈ C ∧ ȯ ≤ o' } ∪ { min({ ȯ | (p, d, ȯ) ∈ C }) }) + * + * In words: The chosen period length is the maximal possible period length not + * bigger than the requested period length and if that doesn't exist, the + * minimal period length. The chosen duty length is the maximal possible duty + * length that is compatible with the chosen period length and isn't bigger than + * the requested duty length. Again if such a value doesn't exist, the minimal + * duty length compatible with the chosen period is picked. After that the duty + * offset compatible with the chosen period and duty length is chosen in the + * same way. + * * Returns: 0 on success, -EDOM if setting failed due to the exact waveform not * being possible (if @exact), or a different negative errno on failure. * Context: May sleep. -- cgit v1.2.3