Unverified Commit 9a5d769c authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'ffa-fixes-6.16' of...

Merge tag 'ffa-fixes-6.16' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/fixes

Arm FF-A fixes for v6.16

Couple of fixes to address:

1. The safety and memory issues in the FF-A notification callback handler:

   The fixes replaces a mutex with an rwlock to prevent sleeping in atomic
   context, resolving kernel warnings. Memory allocation is moved outside
   the lock to support this transition safely. Additionally, a memory leak
   in the notifier unregistration path is fixed by properly freeing the
   callback node.

2. The missing entry in struct ffa_indirect_msg_hdr:

   The fix adds the missing 32 bit reserved entry in the structure as
   required by the FF-A specification.

* tag 'ffa-fixes-6.16' of https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_ffa: Fix the missing entry in struct ffa_indirect_msg_hdr
  firmware: arm_ffa: Replace mutex with rwlock to avoid sleep in atomic context
  firmware: arm_ffa: Move memory allocation outside the mutex locking
  firmware: arm_ffa: Fix memory leak by freeing notifier callback node

Link: https://lore.kernel.org/r/20250609105207.1185570-1-sudeep.holla@arm.com


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 86731a2a 4c46a471
Loading
Loading
Loading
Loading
+36 −35
Original line number Diff line number Diff line
@@ -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;
@@ -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,20 +1269,10 @@ 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);
	}

	return 0;
@@ -1300,20 +1289,19 @@ 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(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);
		write_unlock(&drv_info->notify_lock);
		return rc;
	}

	if (!is_framework)
		rc = ffa_notification_unbind(dev->vm_id, BIT(notify_id));

	mutex_unlock(&drv_info->notify_lock);
	write_unlock(&drv_info->notify_lock);

	return rc;
}
@@ -1334,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;
@@ -1341,28 +1330,40 @@ static int __ffa_notify_request(struct ffa_device *dev, bool is_per_vcpu,
	if (notify_id >= FFA_MAX_NOTIFICATIONS)
		return -EINVAL;

	mutex_lock(&drv_info->notify_lock);
	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;

	write_lock(&drv_info->notify_lock);

	if (!is_framework) {
		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(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));
	}
	mutex_unlock(&drv_info->notify_lock);

out_unlock_free:
	write_unlock(&drv_info->notify_lock);
	if (rc)
		kfree(cb_info);

	return rc;
}
@@ -1406,9 +1407,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_hnode_get_by_type(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);
@@ -1446,9 +1447,9 @@ static void handle_fwk_notif_callbacks(u32 bitmap)

	ffa_rx_release();

	mutex_lock(&drv_info->notify_lock);
	read_lock(&drv_info->notify_lock);
	cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid);
	mutex_unlock(&drv_info->notify_lock);
	read_unlock(&drv_info->notify_lock);

	if (cb_info && cb_info->fwk_cb)
		cb_info->fwk_cb(notify_id, cb_info->cb_data, buf);
@@ -1973,7 +1974,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;
+1 −0
Original line number Diff line number Diff line
@@ -283,6 +283,7 @@ struct ffa_indirect_msg_hdr {
	u32 offset;
	u32 send_recv_id;
	u32 size;
	u32 res1;
	uuid_t uuid;
};