Commit 27e850c8 authored by Sudeep Holla's avatar Sudeep Holla
Browse files

firmware: arm_ffa: Move memory allocation outside the mutex locking



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: e0573444 ("firmware: arm_ffa: Add interfaces to request notification callbacks")
Message-Id: <20250528-ffa_notif_fix-v1-2-5ed7bc7f8437@arm.com>
Reviewed-by: default avatarJens Wiklander <jens.wiklander@linaro.org>
Signed-off-by: default avatarSudeep Holla <sudeep.holla@arm.com>
parent a833d31a
Loading
Loading
Loading
Loading
+24 −24
Original line number Diff line number Diff line
@@ -1250,13 +1250,12 @@ notifier_hnode_get_by_type(u16 notify_id, enum notify_type type)
	return NULL;
}

static int
update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb,
		   void *cb_data, bool is_registration, bool is_framework)
static int update_notifier_cb(struct ffa_device *dev, int notify_id,
			      struct notifier_cb_info *cb, bool is_framework)
{
	struct notifier_cb_info *cb_info = NULL;
	enum notify_type type = ffa_notify_type_get(dev->vm_id);
	bool cb_found;
	bool cb_found, is_registration = !!cb;

	if (is_framework)
		cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, dev->vm_id,
@@ -1270,18 +1269,7 @@ update_notifier_cb(struct ffa_device *dev, int notify_id, void *cb,
		return -EINVAL;

	if (is_registration) {
		cb_info = kzalloc(sizeof(*cb_info), GFP_KERNEL);
		if (!cb_info)
			return -ENOMEM;

		cb_info->dev = dev;
		cb_info->cb_data = cb_data;
		if (is_framework)
			cb_info->fwk_cb = cb;
		else
			cb_info->cb = cb;

		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);
@@ -1303,8 +1291,7 @@ static int __ffa_notify_relinquish(struct ffa_device *dev, int notify_id,

	mutex_lock(&drv_info->notify_lock);

	rc = update_notifier_cb(dev, notify_id, NULL, NULL, false,
				is_framework);
	rc = update_notifier_cb(dev, notify_id, NULL, is_framework);
	if (rc) {
		pr_err("Could not unregister notification callback\n");
		mutex_unlock(&drv_info->notify_lock);
@@ -1335,6 +1322,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;

	if (ffa_notifications_disabled())
		return -EOPNOTSUPP;
@@ -1342,6 +1330,17 @@ 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->dev = dev;
	cb_info->cb_data = cb_data;
	if (is_framework)
		cb_info->fwk_cb = cb;
	else
		cb_info->cb = cb;

	mutex_lock(&drv_info->notify_lock);

	if (!is_framework) {
@@ -1349,21 +1348,22 @@ static int __ffa_notify_request(struct ffa_device *dev, bool 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(dev, notify_id, cb, cb_data, true,
				is_framework);
	rc = update_notifier_cb(dev, notify_id, cb_info, is_framework);
	if (rc) {
		pr_err("Failed to register callback for %d - %d\n",
		       notify_id, rc);
		if (!is_framework)
			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;
}