diff options
Diffstat (limited to 'kernel/dynamic_rpc_dispatcher.c')
| -rw-r--r-- | kernel/dynamic_rpc_dispatcher.c | 42 |
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; } |
