Received: by 2002:a05:7412:b995:b0:f9:9502:5bb8 with SMTP id it21csp2207423rdb; Mon, 25 Dec 2023 00:23:49 -0800 (PST) X-Google-Smtp-Source: AGHT+IFHxFWhXq7BlhuOKXJ8CxswZQ+basaj/Grwr831TvDY3nfotOH694g7MajseDN1ubqaMHpT X-Received: by 2002:a05:6808:1a06:b0:3b8:b063:6ba8 with SMTP id bk6-20020a0568081a0600b003b8b0636ba8mr7559510oib.87.1703492629010; Mon, 25 Dec 2023 00:23:49 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1703492628; cv=none; d=google.com; s=arc-20160816; b=QZ4MamGqVPOhaXWLB5DnO0ovPFdx6yNdGnev7ljNVSx1ZStRgn4M+DSX6oW4Jg8Vsw C+ci1cA3+A5MGEUwuvhLYxkZ3rrmOdbI8lputPBlAEsZ/Se/+9kcBvFyuvC0NJAJM+Qr iLVRCXGrdS8AoHWOM3TQFRrp6G9qT2gj9S6fGvlo58GoQyek4YnQaA9IPhPoJSB/YbOC f5hgBe6gKwXJDc2IsghZRuPfU4mh3K7BjKMTHi+ZXoAmvoJoGNjlnIeK8xqeyRnLGWS2 xO2WO9lTXASWUQgApifot7uWVhYck76i5roGK3nprhki6vEIoSN5DNJXqhXVq2Tjw2MO WVZg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=mime-version:list-unsubscribe:list-subscribe:list-id:precedence :message-id:date:subject:cc:to:from:dkim-signature; bh=PQyHzORnG83yHLUkK9mRKYUuUoNyLGS/PDd58jgbxD4=; fh=kl4ZFCPXou1z4z3bDW4EqIXeJhuNtAVI6YG/leZ8hrk=; b=tHMYgycMXHxgh/zUN1x9wZAQbQ4Dtk6EzyrZJvHRSBq0qVvxavbmCY+ezr0n4Q3JJQ pKvcERgH3c63AHzD/810JbruzGwU7ZaQ5ktH56KeYAmN+SRemAcAoJgSASO2j9frpPd4 DugzKitah4U7p7IIa9ou2oPW2Tp06sipbEbbxA6DBXeU/OUaf2tP+wGTml3xpraZ9O3/ GS26wgIrOmv2wo4tnwRSudEFDtiRSP1TyT9KP0fcYd0Ax0QJCS1YmbqSWg5dCnslaOxD POnCAWKRS8oz+GyIm+du+peBiF9cj5yBKa43cMBqOUmVApr6izcFsC31OjpW7I8smV82 aceA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=BZX8En16; spf=pass (google.com: domain of linux-kernel+bounces-10948-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-10948-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Return-Path: Received: from sy.mirrors.kernel.org (sy.mirrors.kernel.org. [147.75.48.161]) by mx.google.com with ESMTPS id t5-20020a654085000000b005cda184c281si7175741pgp.199.2023.12.25.00.23.48 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 Dec 2023 00:23:48 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-10948-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) client-ip=147.75.48.161; Authentication-Results: mx.google.com; dkim=pass header.i=@quicinc.com header.s=qcppdkim1 header.b=BZX8En16; spf=pass (google.com: domain of linux-kernel+bounces-10948-linux.lists.archive=gmail.com@vger.kernel.org designates 147.75.48.161 as permitted sender) smtp.mailfrom="linux-kernel+bounces-10948-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=quicinc.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sy.mirrors.kernel.org (Postfix) with ESMTPS id 0BB85B20E77 for ; Mon, 25 Dec 2023 08:23:45 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3D3421C32; Mon, 25 Dec 2023 08:23:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="BZX8En16" X-Original-To: linux-kernel@vger.kernel.org Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F28A523B0; Mon, 25 Dec 2023 08:23:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Received: from pps.filterd (m0279863.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 3BP7JeJV021626; Mon, 25 Dec 2023 08:23:31 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= from:to:cc:subject:date:message-id:mime-version:content-type; s= qcppdkim1; bh=PQyHzORnG83yHLUkK9mRKYUuUoNyLGS/PDd58jgbxD4=; b=BZ X8En16mJpxrg0+WjhFtwytpagLaWZJv51115DH7xnN8ccHLa0Ckcgq3oj9zx6n2p 3Ze68BQxEJfc5rwp0C3Cjz0WpHCA/rd/cLdlSx6I/exYip3gjeqda9Waigi2/S6c rHF6aGgPY7DjlIcWn9KMvoLgip/0lo5WKBkASKt4sHQMZuFb7kYcu5z2+OAGJo+J VHZhybtDOkjFbGtu/u3KYo0vdz1iPcyjhEXxjywJHEf3l8rymxjyg1IrsPp8pc1p fs8ogg4hsJJVAKdMeYQX9TTHub1uxKGRWKO/9z02ouUeQcTAlf0G8wC0LFE4wC3C pyR9xdiNwfraof458Jbw== Received: from nasanppmta04.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3v5qs5k8fn-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 25 Dec 2023 08:23:31 +0000 (GMT) Received: from nasanex01a.na.qualcomm.com (nasanex01a.na.qualcomm.com [10.52.223.231]) by NASANPPMTA04.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 3BP8NUrI002894 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Mon, 25 Dec 2023 08:23:30 GMT Received: from aiquny2-gv.qualcomm.com (10.80.80.8) by nasanex01a.na.qualcomm.com (10.52.223.231) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Mon, 25 Dec 2023 00:23:25 -0800 From: Maria Yu To: , CC: Maria Yu , , , , Subject: [PATCH v3] pinctrl: Add lock to ensure the state atomization Date: Mon, 25 Dec 2023 16:23:05 +0800 Message-ID: <20231225082305.12343-1-quic_aiquny@quicinc.com> X-Mailer: git-send-email 2.17.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nasanex01a.na.qualcomm.com (10.52.223.231) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: hH_aHujIMxFYogMR7psukANS5hx7O3qw X-Proofpoint-GUID: hH_aHujIMxFYogMR7psukANS5hx7O3qw X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.997,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-12-09_01,2023-12-07_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 clxscore=1015 adultscore=0 mlxlogscore=891 mlxscore=0 priorityscore=1501 bulkscore=0 impostorscore=0 suspectscore=0 malwarescore=0 phishscore=0 spamscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2311290000 definitions=main-2312250063 Currently pinctrl_select_state is an export symbol and don't have effective re-entrance protect design. During async probing of devices it's possible to end up in pinctrl_select_state() from multiple contexts simultaneously, so make it thread safe. More over, when the real racy happened, the system frequently have printk message like: "not freeing pin xx (xxx) as part of deactivating group xxx - it is already used for some other setting". Finally the system crashed after the flood log. Add per pinctrl lock to ensure the old state and new state transition atomization. Also move dev error print message outside the region with interrupts disabled. Use scoped guard to simplify the lock protection needed code. Fixes: 4198a9b57106 ("pinctrl: avoid reload of p state in list iteration") Signed-off-by: Maria Yu --- drivers/pinctrl/core.c | 144 +++++++++++++++++++++-------------------- drivers/pinctrl/core.h | 2 + 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index f2977eb65522..ab158b745486 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "pinctrl core: " fmt #include +#include #include #include #include @@ -1066,6 +1067,7 @@ static struct pinctrl *create_pinctrl(struct device *dev, p->dev = dev; INIT_LIST_HEAD(&p->states); INIT_LIST_HEAD(&p->dt_maps); + spin_lock_init(&p->lock); ret = pinctrl_dt_to_map(p, pctldev); if (ret < 0) { @@ -1262,93 +1264,95 @@ static void pinctrl_link_add(struct pinctrl_dev *pctldev, static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) { struct pinctrl_setting *setting, *setting2; - struct pinctrl_state *old_state = READ_ONCE(p->state); + struct pinctrl_state *old_state; int ret; - if (old_state) { - /* - * For each pinmux setting in the old state, forget SW's record - * of mux owner for that pingroup. Any pingroups which are - * still owned by the new state will be re-acquired by the call - * to pinmux_enable_setting() in the loop below. - */ - list_for_each_entry(setting, &old_state->settings, node) { - if (setting->type != PIN_MAP_TYPE_MUX_GROUP) - continue; - pinmux_disable_setting(setting); + scoped_guard(spinlock_irqsave, &p->lock) { + old_state = p->state; + if (old_state) { + /* + * For each pinmux setting in the old state, forget SW's record + * of mux owner for that pingroup. Any pingroups which are + * still owned by the new state will be re-acquired by the call + * to pinmux_enable_setting() in the loop below. + */ + list_for_each_entry(setting, &old_state->settings, node) { + if (setting->type != PIN_MAP_TYPE_MUX_GROUP) + continue; + pinmux_disable_setting(setting); + } } - } - - p->state = NULL; - /* Apply all the settings for the new state - pinmux first */ - list_for_each_entry(setting, &state->settings, node) { - switch (setting->type) { - case PIN_MAP_TYPE_MUX_GROUP: - ret = pinmux_enable_setting(setting); - break; - case PIN_MAP_TYPE_CONFIGS_PIN: - case PIN_MAP_TYPE_CONFIGS_GROUP: - ret = 0; - break; - default: - ret = -EINVAL; - break; - } + p->state = NULL; - if (ret < 0) - goto unapply_new_state; + /* Apply all the settings for the new state - pinmux first */ + list_for_each_entry(setting, &state->settings, node) { + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = pinmux_enable_setting(setting); + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } - /* Do not link hogs (circular dependency) */ - if (p != setting->pctldev->p) - pinctrl_link_add(setting->pctldev, p->dev); - } + if (ret < 0) + goto unapply_new_state; - /* Apply all the settings for the new state - pinconf after */ - list_for_each_entry(setting, &state->settings, node) { - switch (setting->type) { - case PIN_MAP_TYPE_MUX_GROUP: - ret = 0; - break; - case PIN_MAP_TYPE_CONFIGS_PIN: - case PIN_MAP_TYPE_CONFIGS_GROUP: - ret = pinconf_apply_setting(setting); - break; - default: - ret = -EINVAL; - break; + /* Do not link hogs (circular dependency) */ + if (p != setting->pctldev->p) + pinctrl_link_add(setting->pctldev, p->dev); } - if (ret < 0) { - goto unapply_new_state; - } + /* Apply all the settings for the new state - pinconf after */ + list_for_each_entry(setting, &state->settings, node) { + switch (setting->type) { + case PIN_MAP_TYPE_MUX_GROUP: + ret = 0; + break; + case PIN_MAP_TYPE_CONFIGS_PIN: + case PIN_MAP_TYPE_CONFIGS_GROUP: + ret = pinconf_apply_setting(setting); + break; + default: + ret = -EINVAL; + break; + } - /* Do not link hogs (circular dependency) */ - if (p != setting->pctldev->p) - pinctrl_link_add(setting->pctldev, p->dev); - } + if (ret < 0) + goto unapply_new_state; - p->state = state; + /* Do not link hogs (circular dependency) */ + if (p != setting->pctldev->p) + pinctrl_link_add(setting->pctldev, p->dev); + } - return 0; + p->state = state; + + return 0; unapply_new_state: - dev_err(p->dev, "Error applying setting, reverse things back\n"); - list_for_each_entry(setting2, &state->settings, node) { - if (&setting2->node == &setting->node) - break; - /* - * All we can do here is pinmux_disable_setting. - * That means that some pins are muxed differently now - * than they were before applying the setting (We can't - * "unmux a pin"!), but it's not a big deal since the pins - * are free to be muxed by another apply_setting. - */ - if (setting2->type == PIN_MAP_TYPE_MUX_GROUP) - pinmux_disable_setting(setting2); + list_for_each_entry(setting2, &state->settings, node) { + if (&setting2->node == &setting->node) + break; + /* + * All we can do here is pinmux_disable_setting. + * That means that some pins are muxed differently now + * than they were before applying the setting (We can't + * "unmux a pin"!), but it's not a big deal since the pins + * are free to be muxed by another apply_setting. + */ + if (setting2->type == PIN_MAP_TYPE_MUX_GROUP) + pinmux_disable_setting(setting2); + } } + dev_err(p->dev, "Error applying setting, reverse things back\n"); /* There's no infinite recursive loop here because p->state is NULL */ if (old_state) pinctrl_select_state(p, old_state); diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 530370443c19..86fc41393f7b 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,7 @@ struct pinctrl { struct pinctrl_state *state; struct list_head dt_maps; struct kref users; + spinlock_t lock; }; /** base-commit: 861deac3b092f37b2c5e6871732f3e11486f7082 -- 2.17.1