diff options
-rw-r--r-- | security/device_cgroup.c | 56 |
1 files changed, 18 insertions, 38 deletions
diff --git a/security/device_cgroup.c b/security/device_cgroup.c index dd0dc574d78d..e8aad69f0d69 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c | |||
@@ -49,8 +49,6 @@ struct dev_cgroup { | |||
49 | struct cgroup_subsys_state css; | 49 | struct cgroup_subsys_state css; |
50 | struct list_head exceptions; | 50 | struct list_head exceptions; |
51 | enum devcg_behavior behavior; | 51 | enum devcg_behavior behavior; |
52 | /* temporary list for pending propagation operations */ | ||
53 | struct list_head propagate_pending; | ||
54 | }; | 52 | }; |
55 | 53 | ||
56 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) | 54 | static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) |
@@ -241,7 +239,6 @@ static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) | |||
241 | if (!dev_cgroup) | 239 | if (!dev_cgroup) |
242 | return ERR_PTR(-ENOMEM); | 240 | return ERR_PTR(-ENOMEM); |
243 | INIT_LIST_HEAD(&dev_cgroup->exceptions); | 241 | INIT_LIST_HEAD(&dev_cgroup->exceptions); |
244 | INIT_LIST_HEAD(&dev_cgroup->propagate_pending); | ||
245 | dev_cgroup->behavior = DEVCG_DEFAULT_NONE; | 242 | dev_cgroup->behavior = DEVCG_DEFAULT_NONE; |
246 | 243 | ||
247 | return &dev_cgroup->css; | 244 | return &dev_cgroup->css; |
@@ -445,34 +442,6 @@ static void revalidate_active_exceptions(struct dev_cgroup *devcg) | |||
445 | } | 442 | } |
446 | 443 | ||
447 | /** | 444 | /** |
448 | * get_online_devcg - walks the cgroup tree and fills a list with the online | ||
449 | * groups | ||
450 | * @root: cgroup used as starting point | ||
451 | * @online: list that will be filled with online groups | ||
452 | * | ||
453 | * Must be called with devcgroup_mutex held. Grabs RCU lock. | ||
454 | * Because devcgroup_mutex is held, no devcg will become online or offline | ||
455 | * during the tree walk (see devcgroup_online, devcgroup_offline) | ||
456 | * A separated list is needed because propagate_behavior() and | ||
457 | * propagate_exception() need to allocate memory and can block. | ||
458 | */ | ||
459 | static void get_online_devcg(struct cgroup *root, struct list_head *online) | ||
460 | { | ||
461 | struct cgroup *pos; | ||
462 | struct dev_cgroup *devcg; | ||
463 | |||
464 | lockdep_assert_held(&devcgroup_mutex); | ||
465 | |||
466 | rcu_read_lock(); | ||
467 | cgroup_for_each_descendant_pre(pos, root) { | ||
468 | devcg = cgroup_to_devcgroup(pos); | ||
469 | if (is_devcg_online(devcg)) | ||
470 | list_add_tail(&devcg->propagate_pending, online); | ||
471 | } | ||
472 | rcu_read_unlock(); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * propagate_exception - propagates a new exception to the children | 445 | * propagate_exception - propagates a new exception to the children |
477 | * @devcg_root: device cgroup that added a new exception | 446 | * @devcg_root: device cgroup that added a new exception |
478 | * @ex: new exception to be propagated | 447 | * @ex: new exception to be propagated |
@@ -482,15 +451,24 @@ static void get_online_devcg(struct cgroup *root, struct list_head *online) | |||
482 | static int propagate_exception(struct dev_cgroup *devcg_root, | 451 | static int propagate_exception(struct dev_cgroup *devcg_root, |
483 | struct dev_exception_item *ex) | 452 | struct dev_exception_item *ex) |
484 | { | 453 | { |
485 | struct cgroup *root = devcg_root->css.cgroup; | 454 | struct cgroup *root = devcg_root->css.cgroup, *pos; |
486 | struct dev_cgroup *devcg, *parent, *tmp; | ||
487 | int rc = 0; | 455 | int rc = 0; |
488 | LIST_HEAD(pending); | ||
489 | 456 | ||
490 | get_online_devcg(root, &pending); | 457 | rcu_read_lock(); |
491 | 458 | ||
492 | list_for_each_entry_safe(devcg, tmp, &pending, propagate_pending) { | 459 | cgroup_for_each_descendant_pre(pos, root) { |
493 | parent = cgroup_to_devcgroup(devcg->css.cgroup->parent); | 460 | struct dev_cgroup *devcg = cgroup_to_devcgroup(pos); |
461 | |||
462 | /* | ||
463 | * Because devcgroup_mutex is held, no devcg will become | ||
464 | * online or offline during the tree walk (see on/offline | ||
465 | * methods), and online ones are safe to access outside RCU | ||
466 | * read lock without bumping refcnt. | ||
467 | */ | ||
468 | if (!is_devcg_online(devcg)) | ||
469 | continue; | ||
470 | |||
471 | rcu_read_unlock(); | ||
494 | 472 | ||
495 | /* | 473 | /* |
496 | * in case both root's behavior and devcg is allow, a new | 474 | * in case both root's behavior and devcg is allow, a new |
@@ -512,8 +490,10 @@ static int propagate_exception(struct dev_cgroup *devcg_root, | |||
512 | } | 490 | } |
513 | revalidate_active_exceptions(devcg); | 491 | revalidate_active_exceptions(devcg); |
514 | 492 | ||
515 | list_del_init(&devcg->propagate_pending); | 493 | rcu_read_lock(); |
516 | } | 494 | } |
495 | |||
496 | rcu_read_unlock(); | ||
517 | return rc; | 497 | return rc; |
518 | } | 498 | } |
519 | 499 | ||