Linux cgroups development
 help / color / mirror / Atom feed
From: Junnan Zhang <zhangjn_dev@163.com>
To: mkoutny@suse.com
Cc: cgroups@vger.kernel.org, hannes@cmpxchg.org,
	linux-kernel@vger.kernel.org, sunshx@chinatelecom.cn,
	tj@kernel.org, zhangjn11@chinatelecom.cn, zhangjn_dev@163.com
Subject: Re: [PATCH v3] cgroup: avoid flushing global workqueue in cgroup1_pidlist_destroy_all
Date: Tue,  1 Sep 2026 11:39:23 +0800	[thread overview]
Message-ID: <20260901033923.40420-1-zhangjn_dev@163.com> (raw)
In-Reply-To: <FIXME-fill-in-Michals-message-id>

Hi Michal,

Thanks for the review.

> a) a single reader taking more than hung_task_timeout_secs (that'd be
>    a softlockup earlier),
> b) starvation of cgroup1_pidlist_destroy_all() by many (queued)
>    cgroup_pidlist_start() callers which goes against the second-long
>    caching of pidlists,
> c) there is large number of nr_cgroups * nr_pidnses which makes the
>    caching ineffective

An honest caveat first: this was reported by a customer on a production
Kubernetes node. A guest memory dump was taken at the time, but it
couldn't be analyzed with crash, so I can't give you the exact
nr_cgroups/nr_pidnses.

That said, I believe c) alone is sufficient, and neither a) nor b) is
needed to explain the 120s stall, because the flush latency is
backlog x per-work latency rather than the latency of any single work:

- backlog: flush_workqueue() waits for all works already queued on the
  shared wq, which drains serially (WQ_PERCPU, max_active=1). Container
  churn constantly runs cgroup destruction, and each destroyed cgroup
  queues one work per cached (type, ns) pidlist, so the queue ahead of
  the flusher grows with churn rate x nr_cgroups.

- per-work latency: each destroy work must acquire the owner's
  pidlist_mutex, contending with readers (kubelet/cadvisor/runtime
  scraping cgroup.procs). pidlist_array_load() runs entirely under
  that mutex (css_set walk, kvmalloc, sort), so on a node with frequent
  scraping each work can sit tens of ms behind a reader.

A few thousand queued works each delayed by tens of ms already exceed
hung_task_timeout_secs -- no single reader needs to hold the mutex for
that long (so a) doesn't apply), and each individual work does get the
mutex eventually, so it's not sustained starvation either (so b)
doesn't apply).

More on b): the second-long caching only helps readers that re-read
within that 1s window (e.g. a single `cat` doing several seq_file
iterations). Periodic scrapers like kubelet/cadvisor, with typical
intervals of 10-30s, never hit the cache at all: every scrape round
rebuilds the pidlist under the mutex and queues an expiry work 1s
later. So heavy reader traffic and the existence of the cache are not
in conflict -- the cache is simply ineffective for this access
pattern. It also means the wq steadily carries ~nr_cgroups expiry
works per scrape round, on top of the works queued by churn, which is
what the flusher ends up waiting behind.

On nr_pidnses specifically: containers typically share the pod/host
pid namespace, so in the common case the multiplier is really
nr_cgroups x scraping frequency rather than nr_pidnses. I can't confirm
the customer's pidns usage for the same reason as above.

> OTOH, I'm surprised this v1-issue popped up only now

cgroup v1 is legacy but still the default on widely deployed enterprise
distros, and per-node cgroup density plus metrics scraping frequency
have grown a lot in recent years, so the backlog needed to trip this
only became common recently. Triggering it also requires all three
conditions at once -- many cgroups, frequent full-sweep scraping, and
sustained create/destroy churn -- which is presumably why it isn't
seen more often: missing any one of them, the queue never builds up.
That's my best explanation -- admittedly not provable without an
analyzable dump.

> What's the point of the workqueue after this change? (Mainly the
> expiration + having process context for the handler.)

It still serves the normal path: the deferred expiry that makes the
pidlist cache work, and process context for freeing. The patch only
removes the flush from the cgroup-removal path where, as you noted,
the orphan list guarantees the pidlists head won't be used after cgrp
removal.

If the lack of exact field numbers is a blocker, I can put together a
synthetic reproducer (N cgroups with concurrent cgroup.procs readers
plus destroy churn) and report measured flush_workqueue() latency with
and without the patch. Would that address your concern?

Thanks,
Junnan


      parent reply	other threads:[~2026-09-01  3:39 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14  8:40 [PATCH] cgroup: avoid flushing global workqueue in cgroup1_pidlist_destroy_all Junnan Zhang
2026-08-14  9:45 ` [PATCH v2] " Junnan Zhang
2026-08-14 10:20 ` [PATCH v3] " Junnan Zhang
2026-08-31  7:39   ` Junnan Zhang
2026-08-31  8:16   ` Michal Koutný
2026-09-01  1:53     ` Ridong Chen
2026-09-01  3:48       ` Junnan Zhang
2026-09-01  3:51       ` Junnan Zhang
     [not found]     ` <FIXME-fill-in-Michals-message-id>
2026-09-01  3:38       ` Junnan Zhang
2026-09-01  3:39       ` Junnan Zhang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260901033923.40420-1-zhangjn_dev@163.com \
    --to=zhangjn_dev@163.com \
    --cc=cgroups@vger.kernel.org \
    --cc=hannes@cmpxchg.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mkoutny@suse.com \
    --cc=sunshx@chinatelecom.cn \
    --cc=tj@kernel.org \
    --cc=zhangjn11@chinatelecom.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox