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

Configure Feed

Select the types of activity you want to include in your feed.

ASoC/soundwire/qdsp6/wcd: fix leaks and probe deferral

Merge series from Johan Hovold <johan+linaro@kernel.org>:

I've been hitting a race during boot which breaks probe of the sound
card on the Lenovo ThinkPad X13s as I've previously reported here:

https://lore.kernel.org/all/ZIHMMFtuDtvdpFAZ@hovoldconsulting.com/

The immediate issue appeared to be a probe deferral that was turned into
a hard failure, but addressing that in itself only made things worse as
it exposed further bugs.

I was hoping someone more familiar with the code in question would look
into this, but as this affects users of the X13s and breaks audio on my
machine every fifth boot or so, I decided to investigate it myself.

As expected, the Qualcomm codec drivers are broken and specifically leak
resources on component remove, which in turn breaks sound card probe
deferrals.

The source of the deferral itself appears to be legitimate and was
simply due to some audio component not yet having been registered due to
random changes in timing during boot.

These issues can most easily be reproduced by simply blacklisting the
q6apm_dai module and loading it manually after boot.

Included are also two patches that suppresses error messages on
component probe deferral to avoid spamming the logs during boot.

+118 -30
+41 -16
sound/soc/codecs/wcd-mbhc-v2.c
··· 1454 1454 return ERR_PTR(-EINVAL); 1455 1455 } 1456 1456 1457 - mbhc = devm_kzalloc(dev, sizeof(*mbhc), GFP_KERNEL); 1457 + mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL); 1458 1458 if (!mbhc) 1459 1459 return ERR_PTR(-ENOMEM); 1460 1460 ··· 1474 1474 1475 1475 INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); 1476 1476 1477 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_sw_intr, NULL, 1477 + ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL, 1478 1478 wcd_mbhc_mech_plug_detect_irq, 1479 1479 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1480 1480 "mbhc sw intr", mbhc); 1481 1481 if (ret) 1482 - goto err; 1482 + goto err_free_mbhc; 1483 1483 1484 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_press_intr, NULL, 1484 + ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL, 1485 1485 wcd_mbhc_btn_press_handler, 1486 1486 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1487 1487 "Button Press detect", mbhc); 1488 1488 if (ret) 1489 - goto err; 1489 + goto err_free_sw_intr; 1490 1490 1491 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_btn_release_intr, NULL, 1491 + ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL, 1492 1492 wcd_mbhc_btn_release_handler, 1493 1493 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1494 1494 "Button Release detect", mbhc); 1495 1495 if (ret) 1496 - goto err; 1496 + goto err_free_btn_press_intr; 1497 1497 1498 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_ins_intr, NULL, 1498 + ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL, 1499 1499 wcd_mbhc_adc_hs_ins_irq, 1500 1500 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1501 1501 "Elect Insert", mbhc); 1502 1502 if (ret) 1503 - goto err; 1503 + goto err_free_btn_release_intr; 1504 1504 1505 1505 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr); 1506 1506 1507 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->mbhc_hs_rem_intr, NULL, 1507 + ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL, 1508 1508 wcd_mbhc_adc_hs_rem_irq, 1509 1509 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1510 1510 "Elect Remove", mbhc); 1511 1511 if (ret) 1512 - goto err; 1512 + goto err_free_hs_ins_intr; 1513 1513 1514 1514 disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr); 1515 1515 1516 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_left_ocp, NULL, 1516 + ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL, 1517 1517 wcd_mbhc_hphl_ocp_irq, 1518 1518 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1519 1519 "HPH_L OCP detect", mbhc); 1520 1520 if (ret) 1521 - goto err; 1521 + goto err_free_hs_rem_intr; 1522 1522 1523 - ret = devm_request_threaded_irq(dev, mbhc->intr_ids->hph_right_ocp, NULL, 1523 + ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL, 1524 1524 wcd_mbhc_hphr_ocp_irq, 1525 1525 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 1526 1526 "HPH_R OCP detect", mbhc); 1527 1527 if (ret) 1528 - goto err; 1528 + goto err_free_hph_left_ocp; 1529 1529 1530 1530 return mbhc; 1531 - err: 1531 + 1532 + err_free_hph_left_ocp: 1533 + free_irq(mbhc->intr_ids->hph_left_ocp, mbhc); 1534 + err_free_hs_rem_intr: 1535 + free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); 1536 + err_free_hs_ins_intr: 1537 + free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); 1538 + err_free_btn_release_intr: 1539 + free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc); 1540 + err_free_btn_press_intr: 1541 + free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc); 1542 + err_free_sw_intr: 1543 + free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc); 1544 + err_free_mbhc: 1545 + kfree(mbhc); 1546 + 1532 1547 dev_err(dev, "Failed to request mbhc interrupts %d\n", ret); 1533 1548 1534 1549 return ERR_PTR(ret); ··· 1552 1537 1553 1538 void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) 1554 1539 { 1540 + free_irq(mbhc->intr_ids->hph_right_ocp, mbhc); 1541 + free_irq(mbhc->intr_ids->hph_left_ocp, mbhc); 1542 + free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); 1543 + free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); 1544 + free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc); 1545 + free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc); 1546 + free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc); 1547 + 1555 1548 mutex_lock(&mbhc->lock); 1556 1549 wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); 1557 1550 mutex_unlock(&mbhc->lock); 1551 + 1552 + kfree(mbhc); 1558 1553 } 1559 1554 EXPORT_SYMBOL(wcd_mbhc_deinit); 1560 1555
+12
sound/soc/codecs/wcd934x.c
··· 3044 3044 3045 3045 return 0; 3046 3046 } 3047 + 3048 + static void wcd934x_mbhc_deinit(struct snd_soc_component *component) 3049 + { 3050 + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component); 3051 + 3052 + if (!wcd->mbhc) 3053 + return; 3054 + 3055 + wcd_mbhc_deinit(wcd->mbhc); 3056 + } 3057 + 3047 3058 static int wcd934x_comp_probe(struct snd_soc_component *component) 3048 3059 { 3049 3060 struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); ··· 3088 3077 { 3089 3078 struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); 3090 3079 3080 + wcd934x_mbhc_deinit(comp); 3091 3081 wcd_clsh_ctrl_free(wcd->clsh_ctrl); 3092 3082 } 3093 3083
+52 -7
sound/soc/codecs/wcd938x.c
··· 2636 2636 2637 2637 return 0; 2638 2638 } 2639 + 2640 + static void wcd938x_mbhc_deinit(struct snd_soc_component *component) 2641 + { 2642 + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); 2643 + 2644 + wcd_mbhc_deinit(wcd938x->wcd_mbhc); 2645 + } 2646 + 2639 2647 /* END MBHC */ 2640 2648 2641 2649 static const struct snd_kcontrol_new wcd938x_snd_controls[] = { ··· 3114 3106 WCD938X_ID_MASK); 3115 3107 3116 3108 wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X); 3109 + if (IS_ERR(wcd938x->clsh_info)) { 3110 + pm_runtime_put(dev); 3111 + return PTR_ERR(wcd938x->clsh_info); 3112 + } 3117 3113 3118 3114 wcd938x_io_init(wcd938x); 3119 3115 /* Set all interrupts as edge triggered */ ··· 3139 3127 ret = request_threaded_irq(wcd938x->hphr_pdm_wd_int, NULL, wcd938x_wd_handle_irq, 3140 3128 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 3141 3129 "HPHR PDM WD INT", wcd938x); 3142 - if (ret) 3130 + if (ret) { 3143 3131 dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret); 3132 + goto err_free_clsh_ctrl; 3133 + } 3144 3134 3145 3135 ret = request_threaded_irq(wcd938x->hphl_pdm_wd_int, NULL, wcd938x_wd_handle_irq, 3146 3136 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 3147 3137 "HPHL PDM WD INT", wcd938x); 3148 - if (ret) 3138 + if (ret) { 3149 3139 dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret); 3140 + goto err_free_hphr_pdm_wd_int; 3141 + } 3150 3142 3151 3143 ret = request_threaded_irq(wcd938x->aux_pdm_wd_int, NULL, wcd938x_wd_handle_irq, 3152 3144 IRQF_ONESHOT | IRQF_TRIGGER_RISING, 3153 3145 "AUX PDM WD INT", wcd938x); 3154 - if (ret) 3146 + if (ret) { 3155 3147 dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret); 3148 + goto err_free_hphl_pdm_wd_int; 3149 + } 3156 3150 3157 3151 /* Disable watchdog interrupt for HPH and AUX */ 3158 3152 disable_irq_nosync(wcd938x->hphr_pdm_wd_int); ··· 3173 3155 dev_err(component->dev, 3174 3156 "%s: Failed to add snd ctrls for variant: %d\n", 3175 3157 __func__, wcd938x->variant); 3176 - goto err; 3158 + goto err_free_aux_pdm_wd_int; 3177 3159 } 3178 3160 break; 3179 3161 case WCD9385: ··· 3183 3165 dev_err(component->dev, 3184 3166 "%s: Failed to add snd ctrls for variant: %d\n", 3185 3167 __func__, wcd938x->variant); 3186 - goto err; 3168 + goto err_free_aux_pdm_wd_int; 3187 3169 } 3188 3170 break; 3189 3171 default: ··· 3191 3173 } 3192 3174 3193 3175 ret = wcd938x_mbhc_init(component); 3194 - if (ret) 3176 + if (ret) { 3195 3177 dev_err(component->dev, "mbhc initialization failed\n"); 3196 - err: 3178 + goto err_free_aux_pdm_wd_int; 3179 + } 3180 + 3181 + return 0; 3182 + 3183 + err_free_aux_pdm_wd_int: 3184 + free_irq(wcd938x->aux_pdm_wd_int, wcd938x); 3185 + err_free_hphl_pdm_wd_int: 3186 + free_irq(wcd938x->hphl_pdm_wd_int, wcd938x); 3187 + err_free_hphr_pdm_wd_int: 3188 + free_irq(wcd938x->hphr_pdm_wd_int, wcd938x); 3189 + err_free_clsh_ctrl: 3190 + wcd_clsh_ctrl_free(wcd938x->clsh_info); 3191 + 3197 3192 return ret; 3193 + } 3194 + 3195 + static void wcd938x_soc_codec_remove(struct snd_soc_component *component) 3196 + { 3197 + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); 3198 + 3199 + wcd938x_mbhc_deinit(component); 3200 + 3201 + free_irq(wcd938x->aux_pdm_wd_int, wcd938x); 3202 + free_irq(wcd938x->hphl_pdm_wd_int, wcd938x); 3203 + free_irq(wcd938x->hphr_pdm_wd_int, wcd938x); 3204 + 3205 + wcd_clsh_ctrl_free(wcd938x->clsh_info); 3198 3206 } 3199 3207 3200 3208 static int wcd938x_codec_set_jack(struct snd_soc_component *comp, ··· 3239 3195 static const struct snd_soc_component_driver soc_codec_dev_wcd938x = { 3240 3196 .name = "wcd938x_codec", 3241 3197 .probe = wcd938x_soc_codec_probe, 3198 + .remove = wcd938x_soc_codec_remove, 3242 3199 .controls = wcd938x_snd_controls, 3243 3200 .num_controls = ARRAY_SIZE(wcd938x_snd_controls), 3244 3201 .dapm_widgets = wcd938x_dapm_widgets,
+2 -2
sound/soc/qcom/qdsp6/topology.c
··· 1277 1277 1278 1278 ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw); 1279 1279 if (ret < 0) { 1280 - dev_err(dev, "tplg component load failed%d\n", ret); 1281 - ret = -EINVAL; 1280 + if (ret != -EPROBE_DEFER) 1281 + dev_err(dev, "tplg component load failed: %d\n", ret); 1282 1282 } 1283 1283 1284 1284 release_firmware(fw);
+4 -2
sound/soc/soc-core.c
··· 1988 1988 /* probe all components used by DAI links on this card */ 1989 1989 ret = soc_probe_link_components(card); 1990 1990 if (ret < 0) { 1991 - dev_err(card->dev, 1992 - "ASoC: failed to instantiate card %d\n", ret); 1991 + if (ret != -EPROBE_DEFER) { 1992 + dev_err(card->dev, 1993 + "ASoC: failed to instantiate card %d\n", ret); 1994 + } 1993 1995 goto probe_end; 1994 1996 } 1995 1997
+7 -3
sound/soc/soc-topology.c
··· 1732 1732 1733 1733 ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1); 1734 1734 if (ret < 0) { 1735 - dev_err(tplg->dev, "ASoC: adding FE link failed\n"); 1735 + if (ret != -EPROBE_DEFER) 1736 + dev_err(tplg->dev, "ASoC: adding FE link failed\n"); 1736 1737 goto err; 1737 1738 } 1738 1739 ··· 2493 2492 /* load the header object */ 2494 2493 ret = soc_tplg_load_header(tplg, hdr); 2495 2494 if (ret < 0) { 2496 - dev_err(tplg->dev, 2497 - "ASoC: topology: could not load header: %d\n", ret); 2495 + if (ret != -EPROBE_DEFER) { 2496 + dev_err(tplg->dev, 2497 + "ASoC: topology: could not load header: %d\n", 2498 + ret); 2499 + } 2498 2500 return ret; 2499 2501 } 2500 2502