Instead of frobbing around with scheduler internals, use the shiny new task_call_func() interface.
Showing the changes to kernel/livepatch (1 of 1 file). Show all
Unified · Split
| @@ -13,7 +13,6 @@ · 12 unmodified lines | |||
| 13 | #include "core.h" | 13 | #include "core.h" |
| 14 | #include "patch.h" | 14 | #include "patch.h" |
| 15 | #include "transition.h" | 15 | #include "transition.h" |
| 16 | #include "../sched/sched.h" | ||
| 17 | 16 | ||
| 18 | #define MAX_STACK_ENTRIES 100 | 17 | #define MAX_STACK_ENTRIES 100 |
| 19 | #define STACK_ERR_BUF_SIZE 128 | 18 | #define STACK_ERR_BUF_SIZE 128 |
| @@ -240,7 +239,7 @@ static int klp_check_stack_func(struct klp_func *func, unsigned long *entries, · 220 unmodified lines | |||
| 240 | * Determine whether it's safe to transition the task to the target patch state | 239 | * Determine whether it's safe to transition the task to the target patch state |
| 241 | * by looking for any to-be-patched or to-be-unpatched functions on its stack. | 240 | * by looking for any to-be-patched or to-be-unpatched functions on its stack. |
| 242 | */ | 241 | */ |
| 243 | static int klp_check_stack(struct task_struct *task, char *err_buf) | 242 | static int klp_check_stack(struct task_struct *task, const char **oldname) |
| 244 | { | 243 | { |
| 245 | static unsigned long entries[MAX_STACK_ENTRIES]; | 244 | static unsigned long entries[MAX_STACK_ENTRIES]; |
| 246 | struct klp_object *obj; | 245 | struct klp_object *obj; |
| @@ -248,12 +247,8 @@ static int klp_check_stack(struct task_struct *task, char *err_buf) · 1 unmodified line | |||
| 248 | int ret, nr_entries; | 247 | int ret, nr_entries; |
| 249 | 248 | ||
| 250 | ret = stack_trace_save_tsk_reliable(task, entries, ARRAY_SIZE(entries)); | 249 | ret = stack_trace_save_tsk_reliable(task, entries, ARRAY_SIZE(entries)); |
| 251 | if (ret < 0) { | 250 | if (ret < 0) |
| 252 | snprintf(err_buf, STACK_ERR_BUF_SIZE, | 251 | return -EINVAL; |
| 253 | "%s: %s:%d has an unreliable stack\n", | ||
| 254 | __func__, task->comm, task->pid); | ||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | nr_entries = ret; | 252 | nr_entries = ret; |
| 258 | 253 | ||
| 259 | klp_for_each_object(klp_transition_patch, obj) { | 254 | klp_for_each_object(klp_transition_patch, obj) { |
| @@ -262,11 +257,8 @@ static int klp_check_stack(struct task_struct *task, char *err_buf) · 2 unmodified lines | |||
| 262 | klp_for_each_func(obj, func) { | 257 | klp_for_each_func(obj, func) { |
| 263 | ret = klp_check_stack_func(func, entries, nr_entries); | 258 | ret = klp_check_stack_func(func, entries, nr_entries); |
| 264 | if (ret) { | 259 | if (ret) { |
| 265 | snprintf(err_buf, STACK_ERR_BUF_SIZE, | 260 | *oldname = func->old_name; |
| 266 | "%s: %s:%d is sleeping on function %s\n", | 261 | return -EADDRINUSE; |
| 267 | __func__, task->comm, task->pid, | ||
| 268 | func->old_name); | ||
| 269 | return ret; | ||
| 270 | } | 262 | } |
| 271 | } | 263 | } |
| 272 | } | 264 | } |
| @@ -274,6 +266,22 @@ static int klp_check_stack(struct task_struct *task, char *err_buf) · 1 unmodified line | |||
| 274 | return 0; | 266 | return 0; |
| 275 | } | 267 | } |
| 276 | 268 | ||
| 269 | static int klp_check_and_switch_task(struct task_struct *task, void *arg) | ||
| 270 | { | ||
| 271 | int ret; | ||
| 272 | |||
| 273 | if (task_curr(task) && task != current) | ||
| 274 | return -EBUSY; | ||
| 275 | |||
| 276 | ret = klp_check_stack(task, arg); | ||
| 277 | if (ret) | ||
| 278 | return ret; | ||
| 279 | |||
| 280 | clear_tsk_thread_flag(task, TIF_PATCH_PENDING); | ||
| 281 | task->patch_state = klp_target_state; | ||
| 282 | return 0; | ||
| 283 | } | ||
| 284 | |||
| 277 | /* | 285 | /* |
| 278 | * Try to safely switch a task to the target patch state. If it's currently | 286 | * Try to safely switch a task to the target patch state. If it's currently |
| 279 | * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or | 287 | * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or |
| @@ -281,13 +289,8 @@ static int klp_check_stack(struct task_struct *task, char *err_buf) · 1 unmodified line | |||
| 281 | */ | 289 | */ |
| 282 | static bool klp_try_switch_task(struct task_struct *task) | 290 | static bool klp_try_switch_task(struct task_struct *task) |
| 283 | { | 291 | { |
| 284 | static char err_buf[STACK_ERR_BUF_SIZE]; | 292 | const char *old_name; |
| 285 | struct rq *rq; | ||
| 286 | struct rq_flags flags; | ||
| 287 | int ret; | 293 | int ret; |
| 288 | bool success = false; | ||
| 289 | |||
| 290 | err_buf[0] = '\0'; | ||
| 291 | 294 | ||
| 292 | /* check if this task has already switched over */ | 295 | /* check if this task has already switched over */ |
| 293 | if (task->patch_state == klp_target_state) | 296 | if (task->patch_state == klp_target_state) |
| @@ -305,36 +308,31 @@ static bool klp_try_switch_task(struct task_struct *task) · 11 unmodified lines | |||
| 305 | * functions. If all goes well, switch the task to the target patch | 308 | * functions. If all goes well, switch the task to the target patch |
| 306 | * state. | 309 | * state. |
| 307 | */ | 310 | */ |
| 308 | rq = task_rq_lock(task, &flags); | 311 | ret = task_call_func(task, klp_check_and_switch_task, &old_name); |
| 312 | switch (ret) { | ||
| 313 | case 0: /* success */ | ||
| 314 | break; | ||
| 309 | 315 | ||
| 310 | if (task_running(rq, task) && task != current) { | 316 | case -EBUSY: /* klp_check_and_switch_task() */ |
| 311 | snprintf(err_buf, STACK_ERR_BUF_SIZE, | 317 | pr_debug("%s: %s:%d is running\n", |
| 312 | "%s: %s:%d is running\n", __func__, task->comm, | 318 | __func__, task->comm, task->pid); |
| 313 | task->pid); | 319 | break; |
| 314 | goto done; | 320 | case -EINVAL: /* klp_check_and_switch_task() */ |
| 321 | pr_debug("%s: %s:%d has an unreliable stack\n", | ||
| 322 | __func__, task->comm, task->pid); | ||
| 323 | break; | ||
| 324 | case -EADDRINUSE: /* klp_check_and_switch_task() */ | ||
| 325 | pr_debug("%s: %s:%d is sleeping on function %s\n", | ||
| 326 | __func__, task->comm, task->pid, old_name); | ||
| 327 | break; | ||
| 328 | |||
| 329 | default: | ||
| 330 | pr_debug("%s: Unknown error code (%d) when trying to switch %s:%d\n", | ||
| 331 | __func__, ret, task->comm, task->pid); | ||
| 332 | break; | ||
| 315 | } | 333 | } |
| 316 | 334 | ||
| 317 | ret = klp_check_stack(task, err_buf); | 335 | return !ret; |
| 318 | if (ret) | ||
| 319 | goto done; | ||
| 320 | |||
| 321 | success = true; | ||
| 322 | |||
| 323 | clear_tsk_thread_flag(task, TIF_PATCH_PENDING); | ||
| 324 | task->patch_state = klp_target_state; | ||
| 325 | |||
| 326 | done: | ||
| 327 | task_rq_unlock(rq, task, &flags); | ||
| 328 | |||
| 329 | /* | ||
| 330 | * Due to console deadlock issues, pr_debug() can't be used while | ||
| 331 | * holding the task rq lock. Instead we have to use a temporary buffer | ||
| 332 | * and print the debug message after releasing the lock. | ||
| 333 | */ | ||
| 334 | if (err_buf[0] != '\0') | ||
| 335 | pr_debug("%s", err_buf); | ||
| 336 | |||
| 337 | return success; | ||
| 338 | } | 336 | } |
| 339 | 337 | ||
| 340 | /* | 338 | /* |