summaryrefslogtreecommitdiffstats
path: root/kernel/dynamic_rpc_dispatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/dynamic_rpc_dispatcher.c')
-rw-r--r--kernel/dynamic_rpc_dispatcher.c42
1 files changed, 25 insertions, 17 deletions
diff --git a/kernel/dynamic_rpc_dispatcher.c b/kernel/dynamic_rpc_dispatcher.c
index ca4268d..3853604 100644
--- a/kernel/dynamic_rpc_dispatcher.c
+++ b/kernel/dynamic_rpc_dispatcher.c
@@ -221,41 +221,49 @@ static struct rpc_interface interface_emu = {
int dynamic_dispatcher_init(const char *fw_path)
{
struct fw_analysis_result result;
+ struct rpc_interface *iface;
+ bool use_native = false;
int ret;
spin_lock(&g_dispatcher.lock);
-
if (g_dispatcher.initialized) {
spin_unlock(&g_dispatcher.lock);
return 0;
}
+ spin_unlock(&g_dispatcher.lock);
- /* Analyze firmware */
+ /*
+ * Firmware analysis must run outside the lock: request_firmware()
+ * waits for userspace and sleeps, so holding g_dispatcher.lock
+ * (a spinlock) across it is a scheduling-while-atomic bug.
+ */
ret = fw_analyzer_load_and_analyze(fw_path, &result);
if (ret) {
pr_warn("DYN_DISPATCH: Firmware analysis failed, using emulation: %d\n", ret);
- g_dispatcher.current_interface = &interface_emu;
- g_dispatcher.initialized = true;
- spin_unlock(&g_dispatcher.lock);
- return 0;
- }
-
- g_dispatcher.fw_analysis = result;
-
- /* Select interface based on firmware version */
- if (result.matched && strcmp(result.version, "570.144W") == 0) {
- interface_570.fw_addrs = result.addresses;
- g_dispatcher.current_interface = &interface_570;
+ iface = &interface_emu;
+ } else if (result.matched && strcmp(result.version, "570.144W") == 0) {
+ iface = &interface_570;
+ use_native = true;
pr_info("DYN_DISPATCH: Using native 570.144W interface\n");
} else {
- g_dispatcher.current_interface = &interface_emu;
+ iface = &interface_emu;
pr_info("DYN_DISPATCH: Using emulation interface (version: %s)\n", result.version);
}
- g_dispatcher.initialized = true;
+ spin_lock(&g_dispatcher.lock);
+ if (!g_dispatcher.initialized) {
+ if (!ret) {
+ g_dispatcher.fw_analysis = result;
+ if (use_native)
+ interface_570.fw_addrs = result.addresses;
+ }
+ g_dispatcher.current_interface = iface;
+ g_dispatcher.initialized = true;
+ }
spin_unlock(&g_dispatcher.lock);
- fw_analyzer_print_result(&result);
+ if (!ret)
+ fw_analyzer_print_result(&result);
return 0;
}