diff options
author | Tejun Heo <tj@kernel.org> | 2015-09-11 15:00:21 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2015-09-22 12:46:53 -0400 |
commit | adaae5dcf8920375a2fdc6268f762a0b7b331c55 (patch) | |
tree | a09f5065616798908cb15822081d7fe79d2fc50f | |
parent | 9af2ec45c2d323d5ce35b431bb150c0eb567f19a (diff) |
cgroup: separate out taskset operations from cgroup_migrate()
Currently, cgroup_migreate() implements large part of the migration
logic inline including building the target taskset and actually
migrating them. This patch separates out the following taskset
operations.
CGROUP_TASKSET_INIT() : taskset initializer
cgroup_taskset_add() : add a task to a taskset
cgroup_taskset_migrate() : migrate a taskset to the destination cgroup
This will be used to implement atomic multi-process migration in
cgroup_update_dfl_csses(). This is pure reorganization which doesn't
introduce any functional changes.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Zefan Li <lizefan@huawei.com>
-rw-r--r-- | kernel/cgroup.c | 211 |
1 files changed, 125 insertions, 86 deletions
diff --git a/kernel/cgroup.c b/kernel/cgroup.c index dc079560dd53..f24d3cedd809 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c | |||
@@ -2144,6 +2144,49 @@ struct cgroup_taskset { | |||
2144 | struct task_struct *cur_task; | 2144 | struct task_struct *cur_task; |
2145 | }; | 2145 | }; |
2146 | 2146 | ||
2147 | #define CGROUP_TASKSET_INIT(tset) (struct cgroup_taskset){ \ | ||
2148 | .src_csets = LIST_HEAD_INIT(tset.src_csets), \ | ||
2149 | .dst_csets = LIST_HEAD_INIT(tset.dst_csets), \ | ||
2150 | .csets = &tset.src_csets, \ | ||
2151 | } | ||
2152 | |||
2153 | /** | ||
2154 | * cgroup_taskset_add - try to add a migration target task to a taskset | ||
2155 | * @task: target task | ||
2156 | * @tset: target taskset | ||
2157 | * | ||
2158 | * Add @task, which is a migration target, to @tset. This function becomes | ||
2159 | * noop if @task doesn't need to be migrated. @task's css_set should have | ||
2160 | * been added as a migration source and @task->cg_list will be moved from | ||
2161 | * the css_set's tasks list to mg_tasks one. | ||
2162 | */ | ||
2163 | static void cgroup_taskset_add(struct task_struct *task, | ||
2164 | struct cgroup_taskset *tset) | ||
2165 | { | ||
2166 | struct css_set *cset; | ||
2167 | |||
2168 | lockdep_assert_held(&css_set_rwsem); | ||
2169 | |||
2170 | /* @task either already exited or can't exit until the end */ | ||
2171 | if (task->flags & PF_EXITING) | ||
2172 | return; | ||
2173 | |||
2174 | /* leave @task alone if post_fork() hasn't linked it yet */ | ||
2175 | if (list_empty(&task->cg_list)) | ||
2176 | return; | ||
2177 | |||
2178 | cset = task_css_set(task); | ||
2179 | if (!cset->mg_src_cgrp) | ||
2180 | return; | ||
2181 | |||
2182 | list_move_tail(&task->cg_list, &cset->mg_tasks); | ||
2183 | if (list_empty(&cset->mg_node)) | ||
2184 | list_add_tail(&cset->mg_node, &tset->src_csets); | ||
2185 | if (list_empty(&cset->mg_dst_cset->mg_node)) | ||
2186 | list_move_tail(&cset->mg_dst_cset->mg_node, | ||
2187 | &tset->dst_csets); | ||
2188 | } | ||
2189 | |||
2147 | /** | 2190 | /** |
2148 | * cgroup_taskset_first - reset taskset and return the first task | 2191 | * cgroup_taskset_first - reset taskset and return the first task |
2149 | * @tset: taskset of interest | 2192 | * @tset: taskset of interest |
@@ -2228,6 +2271,84 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp, | |||
2228 | } | 2271 | } |
2229 | 2272 | ||
2230 | /** | 2273 | /** |
2274 | * cgroup_taskset_migrate - migrate a taskset to a cgroup | ||
2275 | * @tset: taget taskset | ||
2276 | * @dst_cgrp: destination cgroup | ||
2277 | * | ||
2278 | * Migrate tasks in @tset to @dst_cgrp. This function fails iff one of the | ||
2279 | * ->can_attach callbacks fails and guarantees that either all or none of | ||
2280 | * the tasks in @tset are migrated. @tset is consumed regardless of | ||
2281 | * success. | ||
2282 | */ | ||
2283 | static int cgroup_taskset_migrate(struct cgroup_taskset *tset, | ||
2284 | struct cgroup *dst_cgrp) | ||
2285 | { | ||
2286 | struct cgroup_subsys_state *css, *failed_css = NULL; | ||
2287 | struct task_struct *task, *tmp_task; | ||
2288 | struct css_set *cset, *tmp_cset; | ||
2289 | int i, ret; | ||
2290 | |||
2291 | /* methods shouldn't be called if no task is actually migrating */ | ||
2292 | if (list_empty(&tset->src_csets)) | ||
2293 | return 0; | ||
2294 | |||
2295 | /* check that we can legitimately attach to the cgroup */ | ||
2296 | for_each_e_css(css, i, dst_cgrp) { | ||
2297 | if (css->ss->can_attach) { | ||
2298 | ret = css->ss->can_attach(css, tset); | ||
2299 | if (ret) { | ||
2300 | failed_css = css; | ||
2301 | goto out_cancel_attach; | ||
2302 | } | ||
2303 | } | ||
2304 | } | ||
2305 | |||
2306 | /* | ||
2307 | * Now that we're guaranteed success, proceed to move all tasks to | ||
2308 | * the new cgroup. There are no failure cases after here, so this | ||
2309 | * is the commit point. | ||
2310 | */ | ||
2311 | down_write(&css_set_rwsem); | ||
2312 | list_for_each_entry(cset, &tset->src_csets, mg_node) { | ||
2313 | list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) | ||
2314 | cgroup_task_migrate(cset->mg_src_cgrp, task, | ||
2315 | cset->mg_dst_cset); | ||
2316 | } | ||
2317 | up_write(&css_set_rwsem); | ||
2318 | |||
2319 | /* | ||
2320 | * Migration is committed, all target tasks are now on dst_csets. | ||
2321 | * Nothing is sensitive to fork() after this point. Notify | ||
2322 | * controllers that migration is complete. | ||
2323 | */ | ||
2324 | tset->csets = &tset->dst_csets; | ||
2325 | |||
2326 | for_each_e_css(css, i, dst_cgrp) | ||
2327 | if (css->ss->attach) | ||
2328 | css->ss->attach(css, tset); | ||
2329 | |||
2330 | ret = 0; | ||
2331 | goto out_release_tset; | ||
2332 | |||
2333 | out_cancel_attach: | ||
2334 | for_each_e_css(css, i, dst_cgrp) { | ||
2335 | if (css == failed_css) | ||
2336 | break; | ||
2337 | if (css->ss->cancel_attach) | ||
2338 | css->ss->cancel_attach(css, tset); | ||
2339 | } | ||
2340 | out_release_tset: | ||
2341 | down_write(&css_set_rwsem); | ||
2342 | list_splice_init(&tset->dst_csets, &tset->src_csets); | ||
2343 | list_for_each_entry_safe(cset, tmp_cset, &tset->src_csets, mg_node) { | ||
2344 | list_splice_tail_init(&cset->mg_tasks, &cset->tasks); | ||
2345 | list_del_init(&cset->mg_node); | ||
2346 | } | ||
2347 | up_write(&css_set_rwsem); | ||
2348 | return ret; | ||
2349 | } | ||
2350 | |||
2351 | /** | ||
2231 | * cgroup_migrate_finish - cleanup after attach | 2352 | * cgroup_migrate_finish - cleanup after attach |
2232 | * @preloaded_csets: list of preloaded css_sets | 2353 | * @preloaded_csets: list of preloaded css_sets |
2233 | * | 2354 | * |
@@ -2381,15 +2502,8 @@ err: | |||
2381 | static int cgroup_migrate(struct task_struct *leader, bool threadgroup, | 2502 | static int cgroup_migrate(struct task_struct *leader, bool threadgroup, |
2382 | struct cgroup *cgrp) | 2503 | struct cgroup *cgrp) |
2383 | { | 2504 | { |
2384 | struct cgroup_taskset tset = { | 2505 | struct cgroup_taskset tset = CGROUP_TASKSET_INIT(tset); |
2385 | .src_csets = LIST_HEAD_INIT(tset.src_csets), | 2506 | struct task_struct *task; |
2386 | .dst_csets = LIST_HEAD_INIT(tset.dst_csets), | ||
2387 | .csets = &tset.src_csets, | ||
2388 | }; | ||
2389 | struct cgroup_subsys_state *css, *failed_css = NULL; | ||
2390 | struct css_set *cset, *tmp_cset; | ||
2391 | struct task_struct *task, *tmp_task; | ||
2392 | int i, ret; | ||
2393 | 2507 | ||
2394 | /* | 2508 | /* |
2395 | * Prevent freeing of tasks while we take a snapshot. Tasks that are | 2509 | * Prevent freeing of tasks while we take a snapshot. Tasks that are |
@@ -2400,89 +2514,14 @@ static int cgroup_migrate(struct task_struct *leader, bool threadgroup, | |||
2400 | rcu_read_lock(); | 2514 | rcu_read_lock(); |
2401 | task = leader; | 2515 | task = leader; |
2402 | do { | 2516 | do { |
2403 | /* @task either already exited or can't exit until the end */ | 2517 | cgroup_taskset_add(task, &tset); |
2404 | if (task->flags & PF_EXITING) | ||
2405 | goto next; | ||
2406 | |||
2407 | /* leave @task alone if post_fork() hasn't linked it yet */ | ||
2408 | if (list_empty(&task->cg_list)) | ||
2409 | goto next; | ||
2410 | |||
2411 | cset = task_css_set(task); | ||
2412 | if (!cset->mg_src_cgrp) | ||
2413 | goto next; | ||
2414 | |||
2415 | list_move_tail(&task->cg_list, &cset->mg_tasks); | ||
2416 | if (list_empty(&cset->mg_node)) | ||
2417 | list_add_tail(&cset->mg_node, &tset.src_csets); | ||
2418 | if (list_empty(&cset->mg_dst_cset->mg_node)) | ||
2419 | list_move_tail(&cset->mg_dst_cset->mg_node, | ||
2420 | &tset.dst_csets); | ||
2421 | next: | ||
2422 | if (!threadgroup) | 2518 | if (!threadgroup) |
2423 | break; | 2519 | break; |
2424 | } while_each_thread(leader, task); | 2520 | } while_each_thread(leader, task); |
2425 | rcu_read_unlock(); | 2521 | rcu_read_unlock(); |
2426 | up_write(&css_set_rwsem); | 2522 | up_write(&css_set_rwsem); |
2427 | 2523 | ||
2428 | /* methods shouldn't be called if no task is actually migrating */ | 2524 | return cgroup_taskset_migrate(&tset, cgrp); |
2429 | if (list_empty(&tset.src_csets)) | ||
2430 | return 0; | ||
2431 | |||
2432 | /* check that we can legitimately attach to the cgroup */ | ||
2433 | for_each_e_css(css, i, cgrp) { | ||
2434 | if (css->ss->can_attach) { | ||
2435 | ret = css->ss->can_attach(css, &tset); | ||
2436 | if (ret) { | ||
2437 | failed_css = css; | ||
2438 | goto out_cancel_attach; | ||
2439 | } | ||
2440 | } | ||
2441 | } | ||
2442 | |||
2443 | /* | ||
2444 | * Now that we're guaranteed success, proceed to move all tasks to | ||
2445 | * the new cgroup. There are no failure cases after here, so this | ||
2446 | * is the commit point. | ||
2447 | */ | ||
2448 | down_write(&css_set_rwsem); | ||
2449 | list_for_each_entry(cset, &tset.src_csets, mg_node) { | ||
2450 | list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) | ||
2451 | cgroup_task_migrate(cset->mg_src_cgrp, task, | ||
2452 | cset->mg_dst_cset); | ||
2453 | } | ||
2454 | up_write(&css_set_rwsem); | ||
2455 | |||
2456 | /* | ||
2457 | * Migration is committed, all target tasks are now on dst_csets. | ||
2458 | * Nothing is sensitive to fork() after this point. Notify | ||
2459 | * controllers that migration is complete. | ||
2460 | */ | ||
2461 | tset.csets = &tset.dst_csets; | ||
2462 | |||
2463 | for_each_e_css(css, i, cgrp) | ||
2464 | if (css->ss->attach) | ||
2465 | css->ss->attach(css, &tset); | ||
2466 | |||
2467 | ret = 0; | ||
2468 | goto out_release_tset; | ||
2469 | |||
2470 | out_cancel_attach: | ||
2471 | for_each_e_css(css, i, cgrp) { | ||
2472 | if (css == failed_css) | ||
2473 | break; | ||
2474 | if (css->ss->cancel_attach) | ||
2475 | css->ss->cancel_attach(css, &tset); | ||
2476 | } | ||
2477 | out_release_tset: | ||
2478 | down_write(&css_set_rwsem); | ||
2479 | list_splice_init(&tset.dst_csets, &tset.src_csets); | ||
2480 | list_for_each_entry_safe(cset, tmp_cset, &tset.src_csets, mg_node) { | ||
2481 | list_splice_tail_init(&cset->mg_tasks, &cset->tasks); | ||
2482 | list_del_init(&cset->mg_node); | ||
2483 | } | ||
2484 | up_write(&css_set_rwsem); | ||
2485 | return ret; | ||
2486 | } | 2525 | } |
2487 | 2526 | ||
2488 | /** | 2527 | /** |