[ Upstream commit 27e850c88df0e25474a8caeb2903e2e90b62c1dc ]
The notifier callback node allocation is currently done while holding the notify_lock mutex. While this is safe even if memory allocation may sleep, we need to move the allocation outside the locked region in preparation to move from using muxtes to rwlocks.
Move the memory allocation to avoid potential sleeping in atomic context once the locks are moved from mutex to rwlocks.
Fixes: e0573444edbf ("firmware: arm_ffa: Add interfaces to request notification callbacks") Message-Id: 20250528-ffa_notif_fix-v1-2-5ed7bc7f8437@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Signed-off-by: Sudeep Holla sudeep.holla@arm.com --- drivers/firmware/arm_ffa/driver.c | 40 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-)
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index c0f3b7cdb6ed..b0d92f411334 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1141,12 +1141,11 @@ notifier_hash_node_get(u16 notify_id, enum notify_type type) return NULL; }
-static int -update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb, - void *cb_data, bool is_registration) +static int update_notifier_cb(int notify_id, enum notify_type type, + struct notifier_cb_info *cb) { struct notifier_cb_info *cb_info = NULL; - bool cb_found; + bool cb_found, is_registration = !!cb;
cb_info = notifier_hash_node_get(notify_id, type); cb_found = !!cb_info; @@ -1155,15 +1154,7 @@ update_notifier_cb(int notify_id, enum notify_type type, ffa_notifier_cb cb, return -EINVAL;
if (is_registration) { - cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL); - if (!cb_info) - return -ENOMEM; - - cb_info->type = type; - cb_info->cb = cb; - cb_info->cb_data = cb_data; - - hash_add(drv_info->notifier_hash, &cb_info->hnode, notify_id); + hash_add(drv_info->notifier_hash, &cb->hnode, notify_id); } else { hash_del(&cb_info->hnode); kfree(cb_info); @@ -1193,7 +1184,7 @@ static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id)
mutex_lock(&drv_info->notify_lock);
- rc = update_notifier_cb(notify_id, type, NULL, NULL, false); + rc = update_notifier_cb(notify_id, type, NULL); if (rc) { pr_err("Could not unregister notification callback\n"); mutex_unlock(&drv_info->notify_lock); @@ -1212,6 +1203,7 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, { int rc; u32 flags = 0; + struct notifier_cb_info *cb_info = NULL; enum notify_type type = ffa_notify_type_get(dev->vm_id);
if (ffa_notifications_disabled()) @@ -1220,24 +1212,34 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, if (notify_id >= FFA_MAX_NOTIFICATIONS) return -EINVAL;
+ cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL); + if (!cb_info) + return -ENOMEM; + + cb_info->type = type; + cb_info->cb_data = cb_data; + cb_info->cb = cb; + mutex_lock(&drv_info->notify_lock);
if (is_per_vcpu) flags = PER_VCPU_NOTIFICATION_FLAG;
rc = ffa_notification_bind(dev->vm_id, BIT(notify_id), flags); - if (rc) { - mutex_unlock(&drv_info->notify_lock); - return rc; - } + if (rc) + goto out_unlock_free;
- rc = update_notifier_cb(notify_id, type, cb, cb_data, true); + rc = update_notifier_cb(notify_id, type, cb_info); if (rc) { pr_err("Failed to register callback for %d - %d\n", notify_id, rc); ffa_notification_unbind(dev->vm_id, BIT(notify_id)); } + +out_unlock_free: mutex_unlock(&drv_info->notify_lock); + if (rc) + kfree(cb_info);
return rc; }
[ Upstream commit 9ca7a421229bbdfbe2e1e628cff5cfa782720a10 ]
The current use of a mutex to protect the notifier hashtable accesses can lead to issues in the atomic context. It results in the below kernel warnings:
| BUG: sleeping function called from invalid context at kernel/locking/mutex.c:258 | in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 9, name: kworker/0:0 | preempt_count: 1, expected: 0 | RCU nest depth: 0, expected: 0 | CPU: 0 UID: 0 PID: 9 Comm: kworker/0:0 Not tainted 6.14.0 #4 | Workqueue: ffa_pcpu_irq_notification notif_pcpu_irq_work_fn | Call trace: | show_stack+0x18/0x24 (C) | dump_stack_lvl+0x78/0x90 | dump_stack+0x18/0x24 | __might_resched+0x114/0x170 | __might_sleep+0x48/0x98 | mutex_lock+0x24/0x80 | handle_notif_callbacks+0x54/0xe0 | notif_get_and_handle+0x40/0x88 | generic_exec_single+0x80/0xc0 | smp_call_function_single+0xfc/0x1a0 | notif_pcpu_irq_work_fn+0x2c/0x38 | process_one_work+0x14c/0x2b4 | worker_thread+0x2e4/0x3e0 | kthread+0x13c/0x210 | ret_from_fork+0x10/0x20
To address this, replace the mutex with an rwlock to protect the notifier hashtable accesses. This ensures that read-side locking does not sleep and multiple readers can acquire the lock concurrently, avoiding unnecessary contention and potential deadlocks. Writer access remains exclusive, preserving correctness.
This change resolves warnings from lockdep about potential sleep in atomic context.
Cc: Jens Wiklander jens.wiklander@linaro.org Reported-by: Jérôme Forissier jerome.forissier@linaro.org Closes: https://github.com/OP-TEE/optee_os/issues/7394 Fixes: e0573444edbf ("firmware: arm_ffa: Add interfaces to request notification callbacks") Message-Id: 20250528-ffa_notif_fix-v1-3-5ed7bc7f8437@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Tested-by: Jens Wiklander jens.wiklander@linaro.org Signed-off-by: Sudeep Holla sudeep.holla@arm.com --- drivers/firmware/arm_ffa/driver.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index b0d92f411334..83dad9c2da06 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -110,7 +110,7 @@ struct ffa_drv_info { struct work_struct sched_recv_irq_work; struct xarray partition_info; DECLARE_HASHTABLE(notifier_hash, ilog2(FFA_MAX_NOTIFICATIONS)); - struct mutex notify_lock; /* lock to protect notifier hashtable */ + rwlock_t notify_lock; /* lock to protect notifier hashtable */ };
static struct ffa_drv_info *drv_info; @@ -1182,18 +1182,18 @@ static int ffa_notify_relinquish(struct ffa_device *dev, int notify_id) if (notify_id >= FFA_MAX_NOTIFICATIONS) return -EINVAL;
- mutex_lock(&drv_info->notify_lock); + write_lock(&drv_info->notify_lock);
rc = update_notifier_cb(notify_id, type, NULL); if (rc) { pr_err("Could not unregister notification callback\n"); - mutex_unlock(&drv_info->notify_lock); + write_unlock(&drv_info->notify_lock); return rc; }
rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));
- mutex_unlock(&drv_info->notify_lock); + write_unlock(&drv_info->notify_lock);
return rc; } @@ -1220,7 +1220,7 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, cb_info->cb_data = cb_data; cb_info->cb = cb;
- mutex_lock(&drv_info->notify_lock); + write_lock(&drv_info->notify_lock);
if (is_per_vcpu) flags = PER_VCPU_NOTIFICATION_FLAG; @@ -1237,7 +1237,7 @@ static int ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu, }
out_unlock_free: - mutex_unlock(&drv_info->notify_lock); + write_unlock(&drv_info->notify_lock); if (rc) kfree(cb_info);
@@ -1269,9 +1269,9 @@ static void handle_notif_callbacks(u64 bitmap, enum notify_type type) if (!(bitmap & 1)) continue;
- mutex_lock(&drv_info->notify_lock); + read_lock(&drv_info->notify_lock); cb_info = notifier_hash_node_get(notify_id, type); - mutex_unlock(&drv_info->notify_lock); + read_unlock(&drv_info->notify_lock);
if (cb_info && cb_info->cb) cb_info->cb(notify_id, cb_info->cb_data); @@ -1721,7 +1721,7 @@ static void ffa_notifications_setup(void) goto cleanup;
hash_init(drv_info->notifier_hash); - mutex_init(&drv_info->notify_lock); + rwlock_init(&drv_info->notify_lock);
drv_info->notif_enabled = true; return;
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 9ca7a421229bbdfbe2e1e628cff5cfa782720a10
Status in newer kernel trees: 6.15.y | Present (different SHA1: 6dd05bec4179)
Note: The patch differs from the upstream commit: --- 1: 9ca7a421229bb < -: ------------- firmware: arm_ffa: Replace mutex with rwlock to avoid sleep in atomic context -: ------------- > 1: df64e51d4ab83 Linux 6.12.36 ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.15.y | Success | Success |
[ Sasha's backport helper bot ]
Hi,
✅ All tests passed successfully. No issues detected. No action required from the submitter.
The upstream commit SHA1 provided is correct: 27e850c88df0e25474a8caeb2903e2e90b62c1dc
Status in newer kernel trees: 6.15.y | Present (different SHA1: 59bc31bc48c5)
Note: The patch differs from the upstream commit: --- 1: 27e850c88df0e < -: ------------- firmware: arm_ffa: Move memory allocation outside the mutex locking -: ------------- > 1: df64e51d4ab83 Linux 6.12.36 ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.12.y | Success | Success |
linux-stable-mirror@lists.linaro.org