Commit 4033714d authored by Michał Mirosław's avatar Michał Mirosław Committed by Greg Kroah-Hartman

staging: wfx: fix init/remove vs IRQ race

Current code races in init/exit with interrupt handlers. This is noticed
by the warning below. Fix it by using devres for ordering allocations and
IRQ de/registration.

WARNING: CPU: 0 PID: 827 at drivers/staging/wfx/bus_spi.c:142 wfx_spi_irq_handler+0x5c/0x64 [wfx]
race condition in driver init/deinit

Cc: stable@vger.kernel.org
Fixes: 0096214a ("staging: wfx: add support for I/O access")
Signed-off-by: default avatarMichał Mirosław <mirq-linux@rere.qmqm.pl>
Reviewed-by: default avatarJérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/f0c66cbb3110c2736cd4357c753fba8c14ee3aee.1581416843.git.mirq-linux@rere.qmqm.plSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 032b423b
...@@ -200,25 +200,23 @@ static int wfx_sdio_probe(struct sdio_func *func, ...@@ -200,25 +200,23 @@ static int wfx_sdio_probe(struct sdio_func *func,
if (ret) if (ret)
goto err0; goto err0;
ret = wfx_sdio_irq_subscribe(bus);
if (ret)
goto err1;
bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata,
&wfx_sdio_hwbus_ops, bus); &wfx_sdio_hwbus_ops, bus);
if (!bus->core) { if (!bus->core) {
ret = -EIO; ret = -EIO;
goto err2; goto err1;
} }
ret = wfx_sdio_irq_subscribe(bus);
if (ret)
goto err1;
ret = wfx_probe(bus->core); ret = wfx_probe(bus->core);
if (ret) if (ret)
goto err3; goto err2;
return 0; return 0;
err3:
wfx_free_common(bus->core);
err2: err2:
wfx_sdio_irq_unsubscribe(bus); wfx_sdio_irq_unsubscribe(bus);
err1: err1:
...@@ -234,7 +232,6 @@ static void wfx_sdio_remove(struct sdio_func *func) ...@@ -234,7 +232,6 @@ static void wfx_sdio_remove(struct sdio_func *func)
struct wfx_sdio_priv *bus = sdio_get_drvdata(func); struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
wfx_release(bus->core); wfx_release(bus->core);
wfx_free_common(bus->core);
wfx_sdio_irq_unsubscribe(bus); wfx_sdio_irq_unsubscribe(bus);
sdio_claim_host(func); sdio_claim_host(func);
sdio_disable_func(func); sdio_disable_func(func);
......
...@@ -154,6 +154,11 @@ static void wfx_spi_request_rx(struct work_struct *work) ...@@ -154,6 +154,11 @@ static void wfx_spi_request_rx(struct work_struct *work)
wfx_bh_request_rx(bus->core); wfx_bh_request_rx(bus->core);
} }
static void wfx_flush_irq_work(void *w)
{
flush_work(w);
}
static size_t wfx_spi_align_size(void *priv, size_t size) static size_t wfx_spi_align_size(void *priv, size_t size)
{ {
// Most of SPI controllers avoid DMA if buffer size is not 32bit aligned // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned
...@@ -207,22 +212,23 @@ static int wfx_spi_probe(struct spi_device *func) ...@@ -207,22 +212,23 @@ static int wfx_spi_probe(struct spi_device *func)
udelay(2000); udelay(2000);
} }
ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler,
IRQF_TRIGGER_RISING, "wfx", bus);
if (ret)
return ret;
INIT_WORK(&bus->request_rx, wfx_spi_request_rx); INIT_WORK(&bus->request_rx, wfx_spi_request_rx);
bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata,
&wfx_spi_hwbus_ops, bus); &wfx_spi_hwbus_ops, bus);
if (!bus->core) if (!bus->core)
return -EIO; return -EIO;
ret = wfx_probe(bus->core); ret = devm_add_action_or_reset(&func->dev, wfx_flush_irq_work,
&bus->request_rx);
if (ret) if (ret)
wfx_free_common(bus->core); return ret;
ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler,
IRQF_TRIGGER_RISING, "wfx", bus);
if (ret)
return ret; return ret;
return wfx_probe(bus->core);
} }
static int wfx_spi_remove(struct spi_device *func) static int wfx_spi_remove(struct spi_device *func)
...@@ -230,11 +236,6 @@ static int wfx_spi_remove(struct spi_device *func) ...@@ -230,11 +236,6 @@ static int wfx_spi_remove(struct spi_device *func)
struct wfx_spi_priv *bus = spi_get_drvdata(func); struct wfx_spi_priv *bus = spi_get_drvdata(func);
wfx_release(bus->core); wfx_release(bus->core);
wfx_free_common(bus->core);
// A few IRQ will be sent during device release. Hopefully, no IRQ
// should happen after wdev/wvif are released.
devm_free_irq(&func->dev, func->irq, bus);
flush_work(&bus->request_rx);
return 0; return 0;
} }
......
...@@ -262,6 +262,16 @@ static int wfx_send_pdata_pds(struct wfx_dev *wdev) ...@@ -262,6 +262,16 @@ static int wfx_send_pdata_pds(struct wfx_dev *wdev)
return ret; return ret;
} }
static void wfx_free_common(void *data)
{
struct wfx_dev *wdev = data;
mutex_destroy(&wdev->rx_stats_lock);
mutex_destroy(&wdev->conf_mutex);
wfx_tx_queues_deinit(wdev);
ieee80211_free_hw(wdev->hw);
}
struct wfx_dev *wfx_init_common(struct device *dev, struct wfx_dev *wfx_init_common(struct device *dev,
const struct wfx_platform_data *pdata, const struct wfx_platform_data *pdata,
const struct hwbus_ops *hwbus_ops, const struct hwbus_ops *hwbus_ops,
...@@ -332,15 +342,10 @@ struct wfx_dev *wfx_init_common(struct device *dev, ...@@ -332,15 +342,10 @@ struct wfx_dev *wfx_init_common(struct device *dev,
wfx_init_hif_cmd(&wdev->hif_cmd); wfx_init_hif_cmd(&wdev->hif_cmd);
wfx_tx_queues_init(wdev); wfx_tx_queues_init(wdev);
return wdev; if (devm_add_action_or_reset(dev, wfx_free_common, wdev))
} return NULL;
void wfx_free_common(struct wfx_dev *wdev) return wdev;
{
mutex_destroy(&wdev->rx_stats_lock);
mutex_destroy(&wdev->conf_mutex);
wfx_tx_queues_deinit(wdev);
ieee80211_free_hw(wdev->hw);
} }
int wfx_probe(struct wfx_dev *wdev) int wfx_probe(struct wfx_dev *wdev)
......
...@@ -34,7 +34,6 @@ struct wfx_dev *wfx_init_common(struct device *dev, ...@@ -34,7 +34,6 @@ struct wfx_dev *wfx_init_common(struct device *dev,
const struct wfx_platform_data *pdata, const struct wfx_platform_data *pdata,
const struct hwbus_ops *hwbus_ops, const struct hwbus_ops *hwbus_ops,
void *hwbus_priv); void *hwbus_priv);
void wfx_free_common(struct wfx_dev *wdev);
int wfx_probe(struct wfx_dev *wdev); int wfx_probe(struct wfx_dev *wdev);
void wfx_release(struct wfx_dev *wdev); void wfx_release(struct wfx_dev *wdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment