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

ASoC: SDCA: Correct FDL locking in sdca_fdl_process()

The current locking in sdca_fdl_process() locks over
sdca_ump_cancel_timeout() and the timeout work function takes the same
lock, this can lead to a deadlock if the work runs as part of the
cancel. To fix this use scoped_guard and move the cancel timeout to be
outside the lock.

Fixes: e92e25f77748 ("ASoC: SDCA: Add UMP timeout handling for FDL")
Tested-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Maciej Strozek <mstrozek@opensource.cirrus.com>
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Tested-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20251120153023.2105663-5-ckeepax@opensource.cirrus.com
Reviewed-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Charles Keepax and committed by
Mark Brown
cc58055b 5fe65824

+51 -50
+51 -50
sound/soc/sdca/sdca_fdl.c
··· 402 402 unsigned int reg, status; 403 403 int response, ret; 404 404 405 - guard(mutex)(&fdl_state->lock); 406 - 407 405 ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, 408 406 interrupt->function, interrupt->entity, 409 407 interrupt->control); ··· 410 412 411 413 sdca_ump_cancel_timeout(&fdl_state->timeout); 412 414 413 - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 414 - SDCA_CTL_XU_FDL_STATUS, 0); 415 - ret = regmap_read(interrupt->function_regmap, reg, &status); 416 - if (ret < 0) { 417 - dev_err(dev, "failed to read FDL status: %d\n", ret); 418 - return ret; 419 - } 420 - 421 - dev_dbg(dev, "FDL status: %#x\n", status); 422 - 423 - ret = fdl_status_process(interrupt, status); 424 - if (ret < 0) 425 - goto reset_function; 426 - 427 - response = ret; 428 - 429 - dev_dbg(dev, "FDL response: %#x\n", response); 430 - 431 - ret = regmap_write(interrupt->function_regmap, reg, 432 - response | (status & ~SDCA_CTL_XU_FDLH_MASK)); 433 - if (ret < 0) { 434 - dev_err(dev, "failed to set FDL status signal: %d\n", ret); 435 - return ret; 436 - } 437 - 438 - ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap, 439 - interrupt->function, interrupt->entity, 440 - interrupt->control); 441 - if (ret) 442 - return ret; 443 - 444 - switch (response) { 445 - case SDCA_CTL_XU_FDLH_RESET_ACK: 446 - dev_dbg(dev, "FDL request reset\n"); 447 - 448 - switch (xu->reset_mechanism) { 449 - default: 450 - dev_warn(dev, "Requested reset mechanism not implemented\n"); 451 - fallthrough; 452 - case SDCA_XU_RESET_FUNCTION: 453 - goto reset_function; 415 + scoped_guard(mutex, &fdl_state->lock) { 416 + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, 417 + interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0); 418 + ret = regmap_read(interrupt->function_regmap, reg, &status); 419 + if (ret < 0) { 420 + dev_err(dev, "failed to read FDL status: %d\n", ret); 421 + return ret; 454 422 } 455 - case SDCA_CTL_XU_FDLH_COMPLETE: 456 - if (status & SDCA_CTL_XU_FDLD_REQ_ABORT || 457 - status == SDCA_CTL_XU_FDLD_COMPLETE) 423 + 424 + dev_dbg(dev, "FDL status: %#x\n", status); 425 + 426 + ret = fdl_status_process(interrupt, status); 427 + if (ret < 0) 428 + goto reset_function; 429 + 430 + response = ret; 431 + 432 + dev_dbg(dev, "FDL response: %#x\n", response); 433 + 434 + ret = regmap_write(interrupt->function_regmap, reg, 435 + response | (status & ~SDCA_CTL_XU_FDLH_MASK)); 436 + if (ret < 0) { 437 + dev_err(dev, "failed to set FDL status signal: %d\n", ret); 438 + return ret; 439 + } 440 + 441 + ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap, 442 + interrupt->function, 443 + interrupt->entity, 444 + interrupt->control); 445 + if (ret) 446 + return ret; 447 + 448 + switch (response) { 449 + case SDCA_CTL_XU_FDLH_RESET_ACK: 450 + dev_dbg(dev, "FDL request reset\n"); 451 + 452 + switch (xu->reset_mechanism) { 453 + default: 454 + dev_warn(dev, "Requested reset mechanism not implemented\n"); 455 + fallthrough; 456 + case SDCA_XU_RESET_FUNCTION: 457 + goto reset_function; 458 + } 459 + case SDCA_CTL_XU_FDLH_COMPLETE: 460 + if (status & SDCA_CTL_XU_FDLD_REQ_ABORT || 461 + status == SDCA_CTL_XU_FDLD_COMPLETE) 462 + return 0; 463 + fallthrough; 464 + default: 465 + sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay); 458 466 return 0; 459 - fallthrough; 460 - default: 461 - sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay); 462 - return 0; 467 + } 463 468 } 464 469 465 470 reset_function: