Add dev_warn_probe() and improve error handling in

Merge series from Dragan Simic <dsimic@manjaro.org>:

This is a small series that introduces dev_warn_probe() function, which
produces warnings on failed resource acquisitions, and improves error
handling in the probe paths of Rockchip SPI drivers, by using functions
dev_err_probe() and dev_warn_probe() properly in multiple places.

This series also performs a bunch of small, rather trivial code cleanups,
to make the code neater and a bit easier to read.
This commit is contained in:
Mark Brown 2024-10-10 10:49:17 +01:00
commit eaa59db7e9
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 116 additions and 44 deletions

View File

@ -4980,6 +4980,49 @@ define_dev_printk_level(_dev_info, KERN_INFO);
#endif
static void __dev_probe_failed(const struct device *dev, int err, bool fatal,
const char *fmt, va_list vargsp)
{
struct va_format vaf;
va_list vargs;
/*
* On x86_64 and possibly on other architectures, va_list is actually a
* size-1 array containing a structure. As a result, function parameter
* vargsp decays from T[1] to T*, and &vargsp has type T** rather than
* T(*)[1], which is expected by its assignment to vaf.va below.
*
* One standard way to solve this mess is by creating a copy in a local
* variable of type va_list and then using a pointer to that local copy
* instead, which is the approach employed here.
*/
va_copy(vargs, vargsp);
vaf.fmt = fmt;
vaf.va = &vargs;
switch (err) {
case -EPROBE_DEFER:
device_set_deferred_probe_reason(dev, &vaf);
dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
break;
case -ENOMEM:
/* Don't print anything on -ENOMEM, there's already enough output */
break;
default:
/* Log fatal final failures as errors, otherwise produce warnings */
if (fatal)
dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
else
dev_warn(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
break;
}
va_end(vargs);
}
/**
* dev_err_probe - probe error check and log helper
* @dev: the pointer to the struct device
@ -4992,7 +5035,7 @@ define_dev_printk_level(_dev_info, KERN_INFO);
* -EPROBE_DEFER and propagate error upwards.
* In case of -EPROBE_DEFER it sets also defer probe reason, which can be
* checked later by reading devices_deferred debugfs attribute.
* It replaces code sequence::
* It replaces the following code sequence::
*
* if (err != -EPROBE_DEFER)
* dev_err(dev, ...);
@ -5004,48 +5047,78 @@ define_dev_printk_level(_dev_info, KERN_INFO);
*
* return dev_err_probe(dev, err, ...);
*
* Using this helper in your probe function is totally fine even if @err is
* known to never be -EPROBE_DEFER.
* Using this helper in your probe function is totally fine even if @err
* is known to never be -EPROBE_DEFER.
* The benefit compared to a normal dev_err() is the standardized format
* of the error code, it being emitted symbolically (i.e. you get "EAGAIN"
* instead of "-35") and the fact that the error code is returned which allows
* more compact error paths.
* of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
* instead of "-35"), and having the error code returned allows more
* compact error paths.
*
* Returns @err.
*/
int dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_list vargs;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
va_start(vargs, fmt);
switch (err) {
case -EPROBE_DEFER:
device_set_deferred_probe_reason(dev, &vaf);
dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
break;
/* Use dev_err() for logging when err doesn't equal -EPROBE_DEFER */
__dev_probe_failed(dev, err, true, fmt, vargs);
case -ENOMEM:
/*
* We don't print anything on -ENOMEM, there is already enough
* output.
*/
break;
default:
dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
break;
}
va_end(args);
va_end(vargs);
return err;
}
EXPORT_SYMBOL_GPL(dev_err_probe);
/**
* dev_warn_probe - probe error check and log helper
* @dev: the pointer to the struct device
* @err: error value to test
* @fmt: printf-style format string
* @...: arguments as specified in the format string
*
* This helper implements common pattern present in probe functions for error
* checking: print debug or warning message depending if the error value is
* -EPROBE_DEFER and propagate error upwards.
* In case of -EPROBE_DEFER it sets also defer probe reason, which can be
* checked later by reading devices_deferred debugfs attribute.
* It replaces the following code sequence::
*
* if (err != -EPROBE_DEFER)
* dev_warn(dev, ...);
* else
* dev_dbg(dev, ...);
* return err;
*
* with::
*
* return dev_warn_probe(dev, err, ...);
*
* Using this helper in your probe function is totally fine even if @err
* is known to never be -EPROBE_DEFER.
* The benefit compared to a normal dev_warn() is the standardized format
* of the error code, which is emitted symbolically (i.e. you get "EAGAIN"
* instead of "-35"), and having the error code returned allows more
* compact error paths.
*
* Returns @err.
*/
int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...)
{
va_list vargs;
va_start(vargs, fmt);
/* Use dev_warn() for logging when err doesn't equal -EPROBE_DEFER */
__dev_probe_failed(dev, err, false, fmt, vargs);
va_end(vargs);
return err;
}
EXPORT_SYMBOL_GPL(dev_warn_probe);
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
{
return fwnode && !IS_ERR(fwnode->secondary);

View File

@ -773,15 +773,15 @@ static int rockchip_spi_probe(struct platform_device *pdev)
rs->apb_pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
if (IS_ERR(rs->apb_pclk)) {
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
ret = PTR_ERR(rs->apb_pclk);
ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->apb_pclk),
"Failed to get apb_pclk\n");
goto err_put_ctlr;
}
rs->spiclk = devm_clk_get_enabled(&pdev->dev, "spiclk");
if (IS_ERR(rs->spiclk)) {
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
ret = PTR_ERR(rs->spiclk);
ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->spiclk),
"Failed to get spi_pclk\n");
goto err_put_ctlr;
}
@ -817,8 +817,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
rs->fifo_len = get_fifo_len(rs);
if (!rs->fifo_len) {
dev_err(&pdev->dev, "Failed to get fifo length\n");
ret = -EINVAL;
ret = dev_err_probe(&pdev->dev, -EINVAL, "Failed to get fifo length\n");
goto err_put_ctlr;
}
@ -858,22 +857,21 @@ static int rockchip_spi_probe(struct platform_device *pdev)
ctlr->dma_tx = dma_request_chan(rs->dev, "tx");
if (IS_ERR(ctlr->dma_tx)) {
/* Check tx to see if we need defer probing driver */
if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
/* Check tx to see if we need to defer driver probing */
ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_tx),
"Failed to request optional TX DMA channel\n");
if (ret == -EPROBE_DEFER)
goto err_disable_pm_runtime;
}
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
ctlr->dma_tx = NULL;
}
ctlr->dma_rx = dma_request_chan(rs->dev, "rx");
if (IS_ERR(ctlr->dma_rx)) {
if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
/* Check rx to see if we need to defer driver probing */
ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_rx),
"Failed to request optional RX DMA channel\n");
if (ret == -EPROBE_DEFER)
goto err_free_dma_tx;
}
dev_warn(rs->dev, "Failed to request RX DMA channel\n");
ctlr->dma_rx = NULL;
}

View File

@ -276,6 +276,7 @@ do { \
dev_driver_string(dev), dev_name(dev), ## arg)
__printf(3, 4) int dev_err_probe(const struct device *dev, int err, const char *fmt, ...);
__printf(3, 4) int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...);
/* Simple helper for dev_err_probe() when ERR_PTR() is to be returned. */
#define dev_err_ptr_probe(dev, ___err, fmt, ...) \