|
@@ -29,6 +29,14 @@
|
|
|
|
|
|
#define MAX_KEY_LEN 100
|
|
|
|
|
|
+/*
|
|
|
+ * blkcg_pol_mutex protects blkcg_policy[] and policy [de]activation.
|
|
|
+ * blkcg_pol_register_mutex nests outside of it and synchronizes entire
|
|
|
+ * policy [un]register operations including cgroup file additions /
|
|
|
+ * removals. Putting cgroup file registration outside blkcg_pol_mutex
|
|
|
+ * allows grabbing it from cgroup callbacks.
|
|
|
+ */
|
|
|
+static DEFINE_MUTEX(blkcg_pol_register_mutex);
|
|
|
static DEFINE_MUTEX(blkcg_pol_mutex);
|
|
|
|
|
|
struct blkcg blkcg_root;
|
|
@@ -38,6 +46,8 @@ struct cgroup_subsys_state * const blkcg_root_css = &blkcg_root.css;
|
|
|
|
|
|
static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
|
|
|
|
|
|
+static LIST_HEAD(all_blkcgs); /* protected by blkcg_pol_mutex */
|
|
|
+
|
|
|
static bool blkcg_policy_enabled(struct request_queue *q,
|
|
|
const struct blkcg_policy *pol)
|
|
|
{
|
|
@@ -453,20 +463,7 @@ static int blkcg_reset_stats(struct cgroup_subsys_state *css,
|
|
|
struct blkcg_gq *blkg;
|
|
|
int i;
|
|
|
|
|
|
- /*
|
|
|
- * XXX: We invoke cgroup_add/rm_cftypes() under blkcg_pol_mutex
|
|
|
- * which ends up putting cgroup's internal cgroup_tree_mutex under
|
|
|
- * it; however, cgroup_tree_mutex is nested above cgroup file
|
|
|
- * active protection and grabbing blkcg_pol_mutex from a cgroup
|
|
|
- * file operation creates a possible circular dependency. cgroup
|
|
|
- * internal locking is planned to go through further simplification
|
|
|
- * and this issue should go away soon. For now, let's trylock
|
|
|
- * blkcg_pol_mutex and restart the write on failure.
|
|
|
- *
|
|
|
- * http://lkml.kernel.org/g/5363C04B.4010400@oracle.com
|
|
|
- */
|
|
|
- if (!mutex_trylock(&blkcg_pol_mutex))
|
|
|
- return restart_syscall();
|
|
|
+ mutex_lock(&blkcg_pol_mutex);
|
|
|
spin_lock_irq(&blkcg->lock);
|
|
|
|
|
|
/*
|
|
@@ -822,8 +819,17 @@ static void blkcg_css_free(struct cgroup_subsys_state *css)
|
|
|
{
|
|
|
struct blkcg *blkcg = css_to_blkcg(css);
|
|
|
|
|
|
- if (blkcg != &blkcg_root)
|
|
|
+ mutex_lock(&blkcg_pol_mutex);
|
|
|
+ list_del(&blkcg->all_blkcgs_node);
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
+
|
|
|
+ if (blkcg != &blkcg_root) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < BLKCG_MAX_POLS; i++)
|
|
|
+ kfree(blkcg->pd[i]);
|
|
|
kfree(blkcg);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static struct cgroup_subsys_state *
|
|
@@ -833,6 +839,8 @@ blkcg_css_alloc(struct cgroup_subsys_state *parent_css)
|
|
|
struct cgroup_subsys_state *ret;
|
|
|
int i;
|
|
|
|
|
|
+ mutex_lock(&blkcg_pol_mutex);
|
|
|
+
|
|
|
if (!parent_css) {
|
|
|
blkcg = &blkcg_root;
|
|
|
goto done;
|
|
@@ -875,14 +883,17 @@ done:
|
|
|
#ifdef CONFIG_CGROUP_WRITEBACK
|
|
|
INIT_LIST_HEAD(&blkcg->cgwb_list);
|
|
|
#endif
|
|
|
+ list_add_tail(&blkcg->all_blkcgs_node, &all_blkcgs);
|
|
|
+
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
return &blkcg->css;
|
|
|
|
|
|
free_pd_blkcg:
|
|
|
for (i--; i >= 0; i--)
|
|
|
kfree(blkcg->pd[i]);
|
|
|
-
|
|
|
free_blkcg:
|
|
|
kfree(blkcg);
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -1037,10 +1048,8 @@ int blkcg_activate_policy(struct request_queue *q,
|
|
|
const struct blkcg_policy *pol)
|
|
|
{
|
|
|
LIST_HEAD(pds);
|
|
|
- LIST_HEAD(cpds);
|
|
|
struct blkcg_gq *blkg;
|
|
|
struct blkg_policy_data *pd, *nd;
|
|
|
- struct blkcg_policy_data *cpd, *cnd;
|
|
|
int cnt = 0, ret;
|
|
|
|
|
|
if (blkcg_policy_enabled(q, pol))
|
|
@@ -1053,10 +1062,7 @@ int blkcg_activate_policy(struct request_queue *q,
|
|
|
cnt++;
|
|
|
spin_unlock_irq(q->queue_lock);
|
|
|
|
|
|
- /*
|
|
|
- * Allocate per-blkg and per-blkcg policy data
|
|
|
- * for all existing blkgs.
|
|
|
- */
|
|
|
+ /* allocate per-blkg policy data for all existing blkgs */
|
|
|
while (cnt--) {
|
|
|
pd = kzalloc_node(pol->pd_size, GFP_KERNEL, q->node);
|
|
|
if (!pd) {
|
|
@@ -1064,15 +1070,6 @@ int blkcg_activate_policy(struct request_queue *q,
|
|
|
goto out_free;
|
|
|
}
|
|
|
list_add_tail(&pd->alloc_node, &pds);
|
|
|
-
|
|
|
- if (!pol->cpd_size)
|
|
|
- continue;
|
|
|
- cpd = kzalloc_node(pol->cpd_size, GFP_KERNEL, q->node);
|
|
|
- if (!cpd) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto out_free;
|
|
|
- }
|
|
|
- list_add_tail(&cpd->alloc_node, &cpds);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1082,32 +1079,17 @@ int blkcg_activate_policy(struct request_queue *q,
|
|
|
spin_lock_irq(q->queue_lock);
|
|
|
|
|
|
list_for_each_entry(blkg, &q->blkg_list, q_node) {
|
|
|
- if (WARN_ON(list_empty(&pds)) ||
|
|
|
- WARN_ON(pol->cpd_size && list_empty(&cpds))) {
|
|
|
+ if (WARN_ON(list_empty(&pds))) {
|
|
|
/* umm... this shouldn't happen, just abort */
|
|
|
ret = -ENOMEM;
|
|
|
goto out_unlock;
|
|
|
}
|
|
|
- cpd = list_first_entry(&cpds, struct blkcg_policy_data,
|
|
|
- alloc_node);
|
|
|
- list_del_init(&cpd->alloc_node);
|
|
|
pd = list_first_entry(&pds, struct blkg_policy_data, alloc_node);
|
|
|
list_del_init(&pd->alloc_node);
|
|
|
|
|
|
/* grab blkcg lock too while installing @pd on @blkg */
|
|
|
spin_lock(&blkg->blkcg->lock);
|
|
|
|
|
|
- if (!pol->cpd_size)
|
|
|
- goto no_cpd;
|
|
|
- if (!blkg->blkcg->pd[pol->plid]) {
|
|
|
- /* Per-policy per-blkcg data */
|
|
|
- blkg->blkcg->pd[pol->plid] = cpd;
|
|
|
- cpd->plid = pol->plid;
|
|
|
- pol->cpd_init_fn(blkg->blkcg);
|
|
|
- } else { /* must free it as it has already been extracted */
|
|
|
- kfree(cpd);
|
|
|
- }
|
|
|
-no_cpd:
|
|
|
blkg->pd[pol->plid] = pd;
|
|
|
pd->blkg = blkg;
|
|
|
pd->plid = pol->plid;
|
|
@@ -1124,8 +1106,6 @@ out_free:
|
|
|
blk_queue_bypass_end(q);
|
|
|
list_for_each_entry_safe(pd, nd, &pds, alloc_node)
|
|
|
kfree(pd);
|
|
|
- list_for_each_entry_safe(cpd, cnd, &cpds, alloc_node)
|
|
|
- kfree(cpd);
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(blkcg_activate_policy);
|
|
@@ -1162,8 +1142,6 @@ void blkcg_deactivate_policy(struct request_queue *q,
|
|
|
|
|
|
kfree(blkg->pd[pol->plid]);
|
|
|
blkg->pd[pol->plid] = NULL;
|
|
|
- kfree(blkg->blkcg->pd[pol->plid]);
|
|
|
- blkg->blkcg->pd[pol->plid] = NULL;
|
|
|
|
|
|
spin_unlock(&blkg->blkcg->lock);
|
|
|
}
|
|
@@ -1182,11 +1160,13 @@ EXPORT_SYMBOL_GPL(blkcg_deactivate_policy);
|
|
|
*/
|
|
|
int blkcg_policy_register(struct blkcg_policy *pol)
|
|
|
{
|
|
|
+ struct blkcg *blkcg;
|
|
|
int i, ret;
|
|
|
|
|
|
if (WARN_ON(pol->pd_size < sizeof(struct blkg_policy_data)))
|
|
|
return -EINVAL;
|
|
|
|
|
|
+ mutex_lock(&blkcg_pol_register_mutex);
|
|
|
mutex_lock(&blkcg_pol_mutex);
|
|
|
|
|
|
/* find an empty slot */
|
|
@@ -1195,19 +1175,49 @@ int blkcg_policy_register(struct blkcg_policy *pol)
|
|
|
if (!blkcg_policy[i])
|
|
|
break;
|
|
|
if (i >= BLKCG_MAX_POLS)
|
|
|
- goto out_unlock;
|
|
|
+ goto err_unlock;
|
|
|
|
|
|
- /* register and update blkgs */
|
|
|
+ /* register @pol */
|
|
|
pol->plid = i;
|
|
|
- blkcg_policy[i] = pol;
|
|
|
+ blkcg_policy[pol->plid] = pol;
|
|
|
+
|
|
|
+ /* allocate and install cpd's */
|
|
|
+ if (pol->cpd_size) {
|
|
|
+ list_for_each_entry(blkcg, &all_blkcgs, all_blkcgs_node) {
|
|
|
+ struct blkcg_policy_data *cpd;
|
|
|
+
|
|
|
+ cpd = kzalloc(pol->cpd_size, GFP_KERNEL);
|
|
|
+ if (!cpd) {
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
+ goto err_free_cpds;
|
|
|
+ }
|
|
|
+
|
|
|
+ blkcg->pd[pol->plid] = cpd;
|
|
|
+ cpd->plid = pol->plid;
|
|
|
+ pol->cpd_init_fn(blkcg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&blkcg_pol_mutex);
|
|
|
|
|
|
/* everything is in place, add intf files for the new policy */
|
|
|
if (pol->cftypes)
|
|
|
WARN_ON(cgroup_add_legacy_cftypes(&blkio_cgrp_subsys,
|
|
|
pol->cftypes));
|
|
|
- ret = 0;
|
|
|
-out_unlock:
|
|
|
+ mutex_unlock(&blkcg_pol_register_mutex);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_free_cpds:
|
|
|
+ if (pol->cpd_size) {
|
|
|
+ list_for_each_entry(blkcg, &all_blkcgs, all_blkcgs_node) {
|
|
|
+ kfree(blkcg->pd[pol->plid]);
|
|
|
+ blkcg->pd[pol->plid] = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ blkcg_policy[pol->plid] = NULL;
|
|
|
+err_unlock:
|
|
|
mutex_unlock(&blkcg_pol_mutex);
|
|
|
+ mutex_unlock(&blkcg_pol_register_mutex);
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(blkcg_policy_register);
|
|
@@ -1220,7 +1230,9 @@ EXPORT_SYMBOL_GPL(blkcg_policy_register);
|
|
|
*/
|
|
|
void blkcg_policy_unregister(struct blkcg_policy *pol)
|
|
|
{
|
|
|
- mutex_lock(&blkcg_pol_mutex);
|
|
|
+ struct blkcg *blkcg;
|
|
|
+
|
|
|
+ mutex_lock(&blkcg_pol_register_mutex);
|
|
|
|
|
|
if (WARN_ON(blkcg_policy[pol->plid] != pol))
|
|
|
goto out_unlock;
|
|
@@ -1229,9 +1241,19 @@ void blkcg_policy_unregister(struct blkcg_policy *pol)
|
|
|
if (pol->cftypes)
|
|
|
cgroup_rm_cftypes(pol->cftypes);
|
|
|
|
|
|
- /* unregister and update blkgs */
|
|
|
+ /* remove cpds and unregister */
|
|
|
+ mutex_lock(&blkcg_pol_mutex);
|
|
|
+
|
|
|
+ if (pol->cpd_size) {
|
|
|
+ list_for_each_entry(blkcg, &all_blkcgs, all_blkcgs_node) {
|
|
|
+ kfree(blkcg->pd[pol->plid]);
|
|
|
+ blkcg->pd[pol->plid] = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
blkcg_policy[pol->plid] = NULL;
|
|
|
-out_unlock:
|
|
|
+
|
|
|
mutex_unlock(&blkcg_pol_mutex);
|
|
|
+out_unlock:
|
|
|
+ mutex_unlock(&blkcg_pol_register_mutex);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(blkcg_policy_unregister);
|