cgroup: rstat: force flush on css exit
Subsystems (2)
PATCH 1/1 cgroup: rstat: force flush on css exit
Cuurently the rstat update side is lockless and transfers the css of cgroup whose stats has been updated through lockless list (llist). There is an expected race where rstat updater skips adding css to the llist because it was already in the list but the flusher might not see those updates done by the skipped updater.
Usually the subsequent updater will take care of such situation but what if the skipped updater was the last updater before the cgroup is removed by the user. In that case stat updates by the skipped updater will be lost. To avoid that let's always flush the stats of the offlined cgroup.
--- kernel/cgroup/rstat.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
kernel/cgroup/rstat.c +10 -0
@@ -283,6 +283,16 @@ static struct cgroup_subsys_state *css_rstat_updated_list(css_process_update_tree (root->ss, cpu); +/* +* We allow race between rstat updater and flusher which can cause a +* scenario where the updater skips adding the css to the list but the +* flusher might not see updater's updates. Usually the subsequent +* updater would take care of that but what if that was the last updater +* on that CPU before getting removed. Handle that scenario here. +*/ +if (!css_is_online (root)) +__css_process_update_tree (root, cpu); +/* Return NULL if this subtree is not on-list */ if (!rstatc->updated_next)return NULL ;
Signature · 1 line
2.47.3
Michal Koutný
maintainerHi Shakeel.
Quoted 5 lines · Shakeel Butt
Cuurently the rstat update side is lockless and transfers the css of cgroup whose stats has been updated through lockless list (llist). There is an expected race where rstat updater skips adding css to the llist because it was already in the list but the flusher might not see those updates done by the skipped updater.
Notice that there's css_rstat_flush() in
css_free_rwork_fn()/css_rstat_exit().
Quoted 4 lines
Usually the subsequent updater will take care of such situation but what if the skipped updater was the last updater before the cgroup is removed by the user. In that case stat updates by the skipped updater will be lost. To avoid that let's always flush the stats of the offlined cgroup.
Are you sure here that this is the different cause of the loss than the other with unlocked cmpxchg you posted later?
Quoted 4 lines
kernel/cgroup/rstat.c +0
-0@@ -283,6 +283,16 @@ static struct cgroup_subsys_state *css_rstat_updated_list(css_process_update_tree (root->ss, cpu);
kernel/cgroup/rstat.c static struct cgroup_subsys_state *css_rstat_updated_list( · line 295
Quoted 10 lines
kernel/cgroup/rstat.c +10
-0+/* +* We allow race between rstat updater and flusher which can cause a +* scenario where the updater skips adding the css to the list but the +* flusher might not see updater's updates. Usually the subsequent +* updater would take care of that but what if that was the last updater +* on that CPU before getting removed. Handle that scenario here. +*/ +if (!css_is_online (root)) +__css_process_update_tree (root, cpu); +
I'm thinking about this approach:
@@ -482,6 +484,15 @@ void css_rstat_exit(struct cgroup_subsys_state *css)
if (!css->rstat_cpu)
return;+ /*
+ * We allow race between rstat updater and flusher which can cause a
+ * scenario where the updater skips adding the css to the list but the
+ * flusher might not see updater's updates. Usually the subsequent
+ * updater would take care of that but what if that was the last updater
+ * on that CPU before getting removed. Handle that scenario here.
+ */
+ for_each_possible_cpu(cpu)
+ css_rstat_updated(css, cpu);
css_rstat_flush(css);
/* sanity check */because that moves the special treating from relatively commonn css_rstat_updated_list() to only cgroup_exit().
(I didn't check this wouldn't break anything.)
Thanks,
Michal
Quoted 12 lines · Michal Koutný
Hi Shakeel.
Quoted 5 lines · Shakeel Butt
Cuurently the rstat update side is lockless and transfers the css of cgroup whose stats has been updated through lockless list (llist). There is an expected race where rstat updater skips adding css to the llist because it was already in the list but the flusher might not see those updates done by the skipped updater.
Notice that there's
css_rstat_flush()in
css_free_rwork_fn()/css_rstat_exit().
Quoted 8 lines
Quoted 4 lines
Usually the subsequent updater will take care of such situation but what if the skipped updater was the last updater before the cgroup is removed by the user. In that case stat updates by the skipped updater will be lost. To avoid that let's always flush the stats of the offlined cgroup.
Are you sure here that this is the different cause of the loss than the other with unlocked cmpxchg you posted later?
I didn't see any stats loss due to this specific case but I found this on code inspection while debugging the other issue.
Quoted 34 lines
Quoted 4 lines
kernel/cgroup/rstat.c +0
-0@@ -283,6 +283,16 @@ static struct cgroup_subsys_state *css_rstat_updated_list(css_process_update_tree (root->ss, cpu);kernel/cgroup/rstat.c static struct cgroup_subsys_state *css_rstat_updated_list( · line 295
Quoted 10 lines
kernel/cgroup/rstat.c +10
-0+/* +* We allow race between rstat updater and flusher which can cause a +* scenario where the updater skips adding the css to the list but the +* flusher might not see updater's updates. Usually the subsequent +* updater would take care of that but what if that was the last updater +* on that CPU before getting removed. Handle that scenario here. +*/ +if (!css_is_online (root)) +__css_process_update_tree (root, cpu); +I'm thinking about this approach:
kernel/cgroup/rstat.c +9
-0@@ -482,6 +484,15 @@ void css_rstat_exit(struct cgroup_subsys_state *css)if (!css->rstat_cpu)return ; +/* +* We allow race between rstat updater and flusher which can cause a +* scenario where the updater skips adding the css to the list but the +* flusher might not see updater's updates. Usually the subsequent +* updater would take care of that but what if that was the last updater +* on that CPU before getting removed. Handle that scenario here. +*/ +for_each_possible_cpu (cpu) +css_rstat_updated (css, cpu);css_rstat_flush (css);/* sanity check */
Quoted 4 lines
because that moves the special treating from relatively commonn
css_rstat_updated_list()to onlycgroup_exit().(I didn't check this wouldn't break anything.)
Yes I think this is much better. We just need to disable preemption for the assert within css_rstat_updated().