[ 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; }