Commit fbeae90d authored by Zheng Yejian's avatar Zheng Yejian Committed by Jialin Zhang
Browse files

livepatch/core: Fix hungtask against cpu hotplug on x86

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I6NVPT



--------------------------------

Concurrently enable/disable livepatch and online/offline cpu cause
deadlock:
  __klp_{enable,disable}_patch()      _cpu_{up,down}()
  --------                            --------
  mutex_lock(&text_mutex);
                                      cpus_write_lock();
  cpus_read_lock();
                                      mutex_lock(&text_mutex);

__klp_{enable,disable}_patch() hold text_mutex then wait cpu_hotplug_lock,
while _cpu_{up,down}() hold cpu_hotplug_lock but wait text_mutex,
finally result in the deadlock.

Cpu hotplug locking is a "percpu" rw semaphore, however write lock and read
lock on it are globally mutual exclusive, that is cpus_write_lock() on one
cpu can block all cpus_read_lock() on other cpus, vice versa.

Similar lock issue was solved in commit 2d1e38f5 ("kprobes: Cure
hotplug lock ordering issues") which change lock order to be:
  kprobe_mutex -> cpus_rwsem -> jump_label_mutex -> text_mutex

Therefore take cpus_read_lock() before text_mutex to avoid deadlock.

Fixes: f5a67467 ("livepatch/x86: support livepatch without ftrace")
Signed-off-by: default avatarZheng Yejian <zhengyejian1@huawei.com>
Reviewed-by: default avatarXu Kuohai <xukuohai@huawei.com>
Signed-off-by: default avatarJialin Zhang <zhangjialin11@huawei.com>
parent 189e7f35
Loading
Loading
Loading
Loading
+25 −11
Original line number Diff line number Diff line
@@ -1669,6 +1669,27 @@ static void klp_breakpoint_post_process(struct klp_patch *patch, bool restore)
	module_put(patch->mod);
}

static int klp_stop_machine(cpu_stop_fn_t fn, void *data, const struct cpumask *cpus)
{
	int ret;

	/*
	 * Cpu hotplug locking is a "percpu" rw semaphore, however write
	 * lock and read lock on it are globally mutual exclusive, that is
	 * cpus_write_lock() on one cpu can block all cpus_read_lock()
	 * on other cpus, vice versa.
	 *
	 * Since cpu hotplug take the cpus_write_lock() before text_mutex,
	 * here take cpus_read_lock() before text_mutex to avoid deadlock.
	 */
	cpus_read_lock();
	arch_klp_code_modify_prepare();
	ret = stop_machine_cpuslocked(fn, data, cpus);
	arch_klp_code_modify_post_process();
	cpus_read_unlock();
	return ret;
}

static int __klp_disable_patch(struct klp_patch *patch)
{
	int ret;
@@ -1689,9 +1710,7 @@ static int __klp_disable_patch(struct klp_patch *patch)
	}
#endif

	arch_klp_code_modify_prepare();
	ret = stop_machine(klp_try_disable_patch, &patch_data, cpu_online_mask);
	arch_klp_code_modify_post_process();
	ret = klp_stop_machine(klp_try_disable_patch, &patch_data, cpu_online_mask);
	if (ret)
		return ret;

@@ -1960,10 +1979,8 @@ static int klp_breakpoint_optimize(struct klp_patch *patch)

		cnt++;

		arch_klp_code_modify_prepare();
		ret = stop_machine(klp_try_enable_patch, &patch_data,
		ret = klp_stop_machine(klp_try_enable_patch, &patch_data,
				       cpu_online_mask);
		arch_klp_code_modify_post_process();
		if (!ret || ret != -EAGAIN)
			break;

@@ -2010,10 +2027,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
	if (ret)
		return ret;

	arch_klp_code_modify_prepare();
	ret = stop_machine(klp_try_enable_patch, &patch_data,
			   cpu_online_mask);
	arch_klp_code_modify_post_process();
	ret = klp_stop_machine(klp_try_enable_patch, &patch_data, cpu_online_mask);
	if (!ret)
		goto move_patch_to_tail;
	if (ret != -EAGAIN)