From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0459835DA41; Sun, 6 Sep 2026 10:07:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788689245; cv=none; b=U6M8tY8vWYAHTHY2FEIGNaGnHqpmLk8BG6x8qftU2sijM6BkUaA5P2qsZweeuqigPcU3QjN2+OySkQi7DlJIECMyEcrZDFUALCOqCZ4+LYzMVaS7RyQHGe8CzVyPtOwDPokRtlmN4J4yZ+uR4cxAx6FVkKiswT+SBPkl1rqY9XQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788689245; c=relaxed/simple; bh=xz2xQvb+MHwrPFRPFij56gjDEEAe9ulHJeEaAI1Yt+4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XuCMFzuhF0dOTqI3IDj6+ywzPqE+DPO4IX/8gKgp+q8UKuqa29vd2c6wk2EcbB+w7n2K7McEEAtcc+Awfds11s+fgiImZAejUInbzny0KKyun9GHYuA11Q97z1ulweSZkt6AegP9i2yRFnXDYWz2AcvieP7Di9eCIyC5gmM4ZYA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=rXFw9hch; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="rXFw9hch" Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B4A411A25; Sun, 6 Sep 2026 03:07:18 -0700 (PDT) Received: from pluto (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 29B143F7D8; Sun, 6 Sep 2026 03:07:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1788689242; bh=xz2xQvb+MHwrPFRPFij56gjDEEAe9ulHJeEaAI1Yt+4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rXFw9hchp86FN5SsvaVrY9wcTXh2qlTIpWsW4b6MZodxIbLQXNHsxBmEZqaWFzr2w tAPCHlirCATKjkxcSK+axjBae8PKWf4kfzV1YvnNFz5nHhAADA2h6SC7hQhIKUNwkj T9lw85C/V2olJBpr3Qw7pb3n6QnpacnZU3flzXNA= From: Cristian Marussi To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, arm-scmi@vger.kernel.org, linux-doc@vger.kernel.org Cc: sudeep.holla@kernel.org, james.quinlan@broadcom.com, f.fainelli@gmail.com, vincent.guittot@linaro.org, etienne.carriere@st.com, peng.fan@oss.nxp.com, michal.simek@amd.com, d-gole@ti.com, jic23@kernel.org, elif.topuz@arm.com, lukasz.luba@arm.com, philip.radford@arm.com, david@kernel.org, souvik.chakravarty@arm.com, leitao@kernel.org, kas@kernel.org, puranjay@kernel.org, usama.arif@linux.dev, kernel-team@meta.com, Cristian Marussi , Jonathan Cameron Subject: [PATCH v11 03/25] firmware: arm_scmi: Introduce protocol instance notifiers Date: Sun, 6 Sep 2026 11:06:01 +0100 Message-ID: <20260906100623.3488327-4-cristian.marussi@arm.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260906100623.3488327-1-cristian.marussi@arm.com> References: <20260906100623.3488327-1-cristian.marussi@arm.com> Precedence: bulk X-Mailing-List: linux-doc@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit SCMI Protocol notifications are typically used by SCMI drivers to detect and react to particular conditions: this was the assumption and the classic usage scenario upon which the SCMI notification framework was built. Some protocols like Telemetry, though, could be interested in enabling and registering for such notifications: in such a scenario each initialized protocol instance could want to register a common protocol notifier. In order to reuse the existing SCMI Notifications machinery, allow protocols themselves to register for their own notifications and provide their own notifier callbacks. Each protocol, at the end of its initialization phase, can now provide one unique per-protocol-instance notifier block, which will be registered, by the core SCMI stack, on the related notification chain: such notifier will be automatically removed during the protocol de-initialization phase. Reviewed-by: Jonathan Cameron Signed-off-by: Cristian Marussi --- v10 -->v11 - added a few dev_dbg v8 --> v9 - Added membarrier comment v7 --> v8 - Reworked logic around protocol notifier registration to address possible races as pointed out by Sashiko - Reviewed commit message v5 -->v6 - move protocol notifier registration out of proto_init v2-->v3 - split out unrelated changes on event sizing v1-->v2 - Fixed multiline comment format --- drivers/firmware/arm_scmi/common.h | 22 +++++++ drivers/firmware/arm_scmi/driver.c | 89 +++++++++++++++++++++------ drivers/firmware/arm_scmi/notify.c | 40 ++++++++++++ drivers/firmware/arm_scmi/protocols.h | 10 +++ 4 files changed, 142 insertions(+), 19 deletions(-) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 6f89ba96493d..1d9e6af09672 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -687,8 +688,29 @@ static struct platform_driver __drv = { \ .probe = __tag##_probe, \ } +struct scmi_protocol_notifier { + u8 proto_id; + u8 evt_id; + const u32 *src_id; + struct notifier_block *nb; + bool registered; +}; + void scmi_notification_instance_data_set(const struct scmi_handle *handle, void *priv); void *scmi_notification_instance_data_get(const struct scmi_handle *handle); + +int scmi_protocol_notifier_register(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno); +int scmi_protocol_notifier_unregister(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno); + +static inline bool +scmi_protocol_notifier_registered(struct scmi_protocol_notifier *pno) +{ + /* Ensure registered is visible */ + return smp_load_acquire(&pno->registered); +} + int scmi_inflight_count(const struct scmi_handle *handle); #endif /* _SCMI_COMMON_H */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ef29fd223287..faf87fb9c423 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -91,6 +91,8 @@ struct scmi_xfers_info { * @gid: A reference for per-protocol devres management. * @users: A refcount to track effective users of this protocol. * @priv: Reference for optional protocol private data. + * @pno: A protocol instance notifier descriptor: active only when the + * included notifier block @nb is non-NULL. * @version: Protocol version supported by the platform as detected at runtime. * @negotiated_version: When the platform supports a newer protocol version, * the agent will try to negotiate with the platform the @@ -110,6 +112,7 @@ struct scmi_protocol_instance { void *gid; refcount_t users; void *priv; + struct scmi_protocol_notifier pno; unsigned int version; unsigned int negotiated_version; struct scmi_protocol_handle ph; @@ -1656,6 +1659,24 @@ static void *scmi_get_protocol_priv(const struct scmi_protocol_handle *ph) return pi->priv; } +static int +scmi_register_instance_notifier(const struct scmi_protocol_handle *ph, u8 evt_id, + const u32 *src_id, struct notifier_block *nb) +{ + struct scmi_protocol_instance *pi = ph_to_pi(ph); + + pi->pno.proto_id = pi->proto->id; + pi->pno.evt_id = evt_id; + pi->pno.src_id = src_id; + /* + * Ensure the descriptor fields are visibile when the notifier block is + * made available + */ + smp_store_release(&pi->pno.nb, nb); + + return 0; +} + static const struct scmi_xfer_ops xfer_ops = { .xfer_get_init = xfer_get_init, .reset_rx_to_maxsz = reset_rx_to_maxsz, @@ -2256,6 +2277,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, pi->ph.hops = &helpers_ops; pi->ph.set_priv = scmi_set_protocol_priv; pi->ph.get_priv = scmi_get_protocol_priv; + pi->ph.instance_notifier_register = scmi_register_instance_notifier; refcount_set(&pi->users, 1); /* @@ -2318,24 +2340,34 @@ static struct scmi_protocol_instance * __must_check scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id) { struct scmi_protocol_instance *pi; + struct notifier_block *proto_notifier_nb = NULL; struct scmi_info *info = handle_to_scmi_info(handle); - mutex_lock(&info->protocols_mtx); - pi = idr_find(&info->protocols, protocol_id); + scoped_guard(mutex, &info->protocols_mtx) { + pi = idr_find(&info->protocols, protocol_id); + if (pi) { + refcount_inc(&pi->users); + } else { + const struct scmi_protocol *proto; - if (pi) { - refcount_inc(&pi->users); - } else { - const struct scmi_protocol *proto; + /* Fails if protocol not registered on bus */ + proto = scmi_protocol_get(protocol_id, &info->version); + if (!proto) + return ERR_PTR(-EPROBE_DEFER); - /* Fails if protocol not registered on bus */ - proto = scmi_protocol_get(protocol_id, &info->version); - if (proto) pi = scmi_alloc_init_protocol_instance(info, proto); - else - pi = ERR_PTR(-EPROBE_DEFER); + if (IS_ERR(pi)) + return pi; + + proto_notifier_nb = READ_ONCE(pi->pno.nb); + } + } + + if (proto_notifier_nb) { + if (scmi_protocol_notifier_register(pi->handle, &pi->pno)) + dev_warn(handle->dev, + "Failed to register protocol notifier\n"); } - mutex_unlock(&info->protocols_mtx); return pi; } @@ -2366,13 +2398,35 @@ int scmi_protocol_acquire(const struct scmi_handle *handle, u8 protocol_id) void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id) { struct scmi_info *info = handle_to_scmi_info(handle); + struct notifier_block *proto_notifier_nb = NULL; struct scmi_protocol_instance *pi; - mutex_lock(&info->protocols_mtx); - pi = idr_find(&info->protocols, protocol_id); - if (WARN_ON(!pi)) - goto out; + scoped_guard(mutex, &info->protocols_mtx) { + pi = idr_find(&info->protocols, protocol_id); + if (WARN_ON(!pi)) + return; + /* + * If a protocol notifier was registered and this is the + * last istance releasing the protocol, mark the notifier + * for un-registration: note that the notifier itself counts + * as one user, as for any other regular notification, so if a + * protocol notifier is registered and there are only 2 users + * active we can derive that this is the last protocol + * instance de-registering. + */ + if (scmi_protocol_notifier_registered(&pi->pno) && + refcount_read(&pi->users) == 2) + proto_notifier_nb = READ_ONCE(pi->pno.nb); + } + + if (proto_notifier_nb) { + if (scmi_protocol_notifier_unregister(pi->handle, &pi->pno)) + dev_err(handle->dev, + "Failed to release protocol notifier\n"); + } + + guard(mutex)(&info->protocols_mtx); if (refcount_dec_and_test(&pi->users)) { void *gid = pi->gid; @@ -2390,9 +2444,6 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id) dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n", protocol_id); } - -out: - mutex_unlock(&info->protocols_mtx); } void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 3264e1062700..188282d9adc0 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1417,6 +1417,25 @@ static int scmi_notifier_register(const struct scmi_handle *handle, return ret; } +int scmi_protocol_notifier_register(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno) +{ + int ret; + + ret = scmi_notifier_register(handle, pno->proto_id, pno->evt_id, + pno->src_id, pno->nb); + if (ret) + return ret; + + /* Make sure registration is visible */ + smp_store_release(&pno->registered, true); + + dev_dbg(handle->dev, + "Registered SCMI protocol [0x%X] notifier\n", pno->proto_id); + + return 0; +} + /** * scmi_notifier_unregister() - Unregister a notifier_block for an event * @handle: The handle identifying the platform instance against which the @@ -1474,6 +1493,27 @@ static int scmi_notifier_unregister(const struct scmi_handle *handle, return 0; } +int scmi_protocol_notifier_unregister(const struct scmi_handle *handle, + struct scmi_protocol_notifier *pno) +{ + int ret; + + /* Make sure de-registration is visible BEFORE calling unregister */ + smp_store_release(&pno->registered, false); + ret = scmi_notifier_unregister(handle, pno->proto_id, pno->evt_id, + pno->src_id, pno->nb); + if (WARN_ON(ret)) { + /* Make sure rollback is visible */ + smp_store_release(&pno->registered, true); + return ret; + } + + dev_dbg(handle->dev, + "Unregistered SCMI protocol [0x%X] notifier\n", pno->proto_id); + + return 0; +} + struct scmi_notifier_devres { const struct scmi_handle *handle; u8 proto_id; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index 15ad5162e37a..b7f29003a7dc 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -166,6 +166,13 @@ struct scmi_proto_helpers_ops; * can be used by the protocol implementation to generate SCMI messages. * @set_priv: A method to set protocol private data for this instance. * @get_priv: A method to get protocol private data previously set. + * @instance_notifier_register: A method to register interest for specific + * notifications from within a protocol + * implementation unit: ONLY one instance notifier + * can be registered per-protocol. + * The related notifier block will be registered + * right after the protocol is initialized and + * automatically removed on protocol release. * * This structure represents a protocol initialized against specific SCMI * instance and it will be used as follows: @@ -185,6 +192,9 @@ struct scmi_protocol_handle { const struct scmi_proto_helpers_ops *hops; int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv); void *(*get_priv)(const struct scmi_protocol_handle *ph); + int (*instance_notifier_register)(const struct scmi_protocol_handle *ph, + u8 evt_id, const u32 *src_id, + struct notifier_block *nb); }; /** -- 2.54.0