summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
authorJames Morse <james.morse@arm.com>2020-02-21 19:35:08 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-10-01 14:14:35 +0300
commitf674193b8e9a76762aadf0183172cf087c923d62 (patch)
treec5006db40ac4881ec18eaf67af07813d8d61d814 /drivers/firmware
parent5fe40ed2a8262ab7bb6ea38c30527396e65b640b (diff)
downloadlinux-f674193b8e9a76762aadf0183172cf087c923d62.tar.xz
firmware: arm_sdei: Use cpus_read_lock() to avoid races with cpuhp
[ Upstream commit 54f529a6806c9710947a4f2cdc15d6ea54121ccd ] SDEI has private events that need registering and enabling on each CPU. CPUs can come and go while we are trying to do this. SDEI tries to avoid these problems by setting the reregister flag before the register call, so any CPUs that come online register the event too. Sticking plaster like this doesn't work, as if the register call fails, a CPU that subsequently comes online will register the event before reregister is cleared. Take cpus_read_lock() around the register and enable calls. We don't want surprise CPUs to do the wrong thing if they race with these calls failing. Signed-off-by: James Morse <james.morse@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/arm_sdei.c26
1 files changed, 14 insertions, 12 deletions
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 05b528c7ed8f..e809f4d9a9e9 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -410,14 +410,19 @@ int sdei_event_enable(u32 event_num)
return -ENOENT;
}
- spin_lock(&sdei_list_lock);
- event->reenable = true;
- spin_unlock(&sdei_list_lock);
+ cpus_read_lock();
if (event->type == SDEI_EVENT_TYPE_SHARED)
err = sdei_api_event_enable(event->event_num);
else
err = sdei_do_cross_call(_local_event_enable, event);
+
+ if (!err) {
+ spin_lock(&sdei_list_lock);
+ event->reenable = true;
+ spin_unlock(&sdei_list_lock);
+ }
+ cpus_read_unlock();
mutex_unlock(&sdei_events_lock);
return err;
@@ -619,21 +624,18 @@ int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
break;
}
- spin_lock(&sdei_list_lock);
- event->reregister = true;
- spin_unlock(&sdei_list_lock);
-
+ cpus_read_lock();
err = _sdei_event_register(event);
if (err) {
- spin_lock(&sdei_list_lock);
- event->reregister = false;
- event->reenable = false;
- spin_unlock(&sdei_list_lock);
-
sdei_event_destroy(event);
pr_warn("Failed to register event %u: %d\n", event_num,
err);
+ } else {
+ spin_lock(&sdei_list_lock);
+ event->reregister = true;
+ spin_unlock(&sdei_list_lock);
}
+ cpus_read_unlock();
} while (0);
mutex_unlock(&sdei_events_lock);