Commit c66d52b1 authored by Liguang Zhang's avatar Liguang Zhang Committed by Catalin Marinas
Browse files

firmware: arm_sdei: fix possible double-lock on hibernate error path



We call sdei_reregister_event() with sdei_list_lock held, if the register
fails we call sdei_event_destroy() which also acquires sdei_list_lock
thus creating A-A deadlock.

Add '_llocked' to sdei_reregister_event(), to indicate the list lock
is held, and add a _llocked variant of sdei_event_destroy().

Fixes: da351827 ("firmware: arm_sdei: Add support for CPU and system power states")
Signed-off-by: default avatarLiguang Zhang <zhangliguang@linux.alibaba.com>
[expanded subject, added wrappers instead of duplicating contents]
Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 6ded0b61
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -280,13 +280,12 @@ static struct sdei_event *sdei_event_create(u32 event_num,
	return event;
}

static void sdei_event_destroy(struct sdei_event *event)
static void sdei_event_destroy_llocked(struct sdei_event *event)
{
	lockdep_assert_held(&sdei_events_lock);
	lockdep_assert_held(&sdei_list_lock);

	spin_lock(&sdei_list_lock);
	list_del(&event->list);
	spin_unlock(&sdei_list_lock);

	if (event->type == SDEI_EVENT_TYPE_SHARED)
		kfree(event->registered);
@@ -296,6 +295,13 @@ static void sdei_event_destroy(struct sdei_event *event)
	kfree(event);
}

static void sdei_event_destroy(struct sdei_event *event)
{
	spin_lock(&sdei_list_lock);
	sdei_event_destroy_llocked(event);
	spin_unlock(&sdei_list_lock);
}

static int sdei_api_get_version(u64 *version)
{
	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_VERSION, 0, 0, 0, 0, 0, version);
@@ -643,16 +649,17 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
}
EXPORT_SYMBOL(sdei_event_register);

static int sdei_reregister_event(struct sdei_event *event)
static int sdei_reregister_event_llocked(struct sdei_event *event)
{
	int err;

	lockdep_assert_held(&sdei_events_lock);
	lockdep_assert_held(&sdei_list_lock);

	err = _sdei_event_register(event);
	if (err) {
		pr_err("Failed to re-register event %u\n", event->event_num);
		sdei_event_destroy(event);
		sdei_event_destroy_llocked(event);
		return err;
	}

@@ -681,7 +688,7 @@ static int sdei_reregister_shared(void)
			continue;

		if (event->reregister) {
			err = sdei_reregister_event(event);
			err = sdei_reregister_event_llocked(event);
			if (err)
				break;
		}