Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 0096214a59a7 ("staging: wfx: add support for I/O access")
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Reviewed-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://lore.kernel.org/r/f0c66cbb3110c2736cd4357c753fba8c14ee3aee.1581416843.git.mirq-linux@rere.qmqm.pl
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Michał Mirosław and committed by
Greg Kroah-Hartman
4033714d 032b423b

+33 -31
+6 -9
drivers/staging/wfx/bus_sdio.c
··· 200 200 if (ret) 201 201 goto err0; 202 202 203 - ret = wfx_sdio_irq_subscribe(bus); 204 - if (ret) 205 - goto err1; 206 - 207 203 bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata, 208 204 &wfx_sdio_hwbus_ops, bus); 209 205 if (!bus->core) { 210 206 ret = -EIO; 211 - goto err2; 207 + goto err1; 212 208 } 209 + 210 + ret = wfx_sdio_irq_subscribe(bus); 211 + if (ret) 212 + goto err1; 213 213 214 214 ret = wfx_probe(bus->core); 215 215 if (ret) 216 - goto err3; 216 + goto err2; 217 217 218 218 return 0; 219 219 220 - err3: 221 - wfx_free_common(bus->core); 222 220 err2: 223 221 wfx_sdio_irq_unsubscribe(bus); 224 222 err1: ··· 232 234 struct wfx_sdio_priv *bus = sdio_get_drvdata(func); 233 235 234 236 wfx_release(bus->core); 235 - wfx_free_common(bus->core); 236 237 wfx_sdio_irq_unsubscribe(bus); 237 238 sdio_claim_host(func); 238 239 sdio_disable_func(func);
+14 -13
drivers/staging/wfx/bus_spi.c
··· 154 154 wfx_bh_request_rx(bus->core); 155 155 } 156 156 157 + static void wfx_flush_irq_work(void *w) 158 + { 159 + flush_work(w); 160 + } 161 + 157 162 static size_t wfx_spi_align_size(void *priv, size_t size) 158 163 { 159 164 // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned ··· 212 207 udelay(2000); 213 208 } 214 209 215 - ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, 216 - IRQF_TRIGGER_RISING, "wfx", bus); 217 - if (ret) 218 - return ret; 219 - 220 210 INIT_WORK(&bus->request_rx, wfx_spi_request_rx); 221 211 bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata, 222 212 &wfx_spi_hwbus_ops, bus); 223 213 if (!bus->core) 224 214 return -EIO; 225 215 226 - ret = wfx_probe(bus->core); 216 + ret = devm_add_action_or_reset(&func->dev, wfx_flush_irq_work, 217 + &bus->request_rx); 227 218 if (ret) 228 - wfx_free_common(bus->core); 219 + return ret; 229 220 230 - return ret; 221 + ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler, 222 + IRQF_TRIGGER_RISING, "wfx", bus); 223 + if (ret) 224 + return ret; 225 + 226 + return wfx_probe(bus->core); 231 227 } 232 228 233 229 static int wfx_spi_remove(struct spi_device *func) ··· 236 230 struct wfx_spi_priv *bus = spi_get_drvdata(func); 237 231 238 232 wfx_release(bus->core); 239 - wfx_free_common(bus->core); 240 - // A few IRQ will be sent during device release. Hopefully, no IRQ 241 - // should happen after wdev/wvif are released. 242 - devm_free_irq(&func->dev, func->irq, bus); 243 - flush_work(&bus->request_rx); 244 233 return 0; 245 234 } 246 235
+13 -8
drivers/staging/wfx/main.c
··· 262 262 return ret; 263 263 } 264 264 265 + static void wfx_free_common(void *data) 266 + { 267 + struct wfx_dev *wdev = data; 268 + 269 + mutex_destroy(&wdev->rx_stats_lock); 270 + mutex_destroy(&wdev->conf_mutex); 271 + wfx_tx_queues_deinit(wdev); 272 + ieee80211_free_hw(wdev->hw); 273 + } 274 + 265 275 struct wfx_dev *wfx_init_common(struct device *dev, 266 276 const struct wfx_platform_data *pdata, 267 277 const struct hwbus_ops *hwbus_ops, ··· 342 332 wfx_init_hif_cmd(&wdev->hif_cmd); 343 333 wfx_tx_queues_init(wdev); 344 334 345 - return wdev; 346 - } 335 + if (devm_add_action_or_reset(dev, wfx_free_common, wdev)) 336 + return NULL; 347 337 348 - void wfx_free_common(struct wfx_dev *wdev) 349 - { 350 - mutex_destroy(&wdev->rx_stats_lock); 351 - mutex_destroy(&wdev->conf_mutex); 352 - wfx_tx_queues_deinit(wdev); 353 - ieee80211_free_hw(wdev->hw); 338 + return wdev; 354 339 } 355 340 356 341 int wfx_probe(struct wfx_dev *wdev)
-1
drivers/staging/wfx/main.h
··· 34 34 const struct wfx_platform_data *pdata, 35 35 const struct hwbus_ops *hwbus_ops, 36 36 void *hwbus_priv); 37 - void wfx_free_common(struct wfx_dev *wdev); 38 37 39 38 int wfx_probe(struct wfx_dev *wdev); 40 39 void wfx_release(struct wfx_dev *wdev);