mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tejun Heo <tj@kernel.org>
To: Juri Lelli <juri.lelli@redhat.com>,
	Peter Zijlstra <peterz@infradead.org>
Cc: Ingo Molnar <mingo@redhat.com>,
	Vincent Guittot <vincent.guittot@linaro.org>,
	Dietmar Eggemann <dietmar.eggemann@arm.com>,
	Valentin Schneider <vschneid@redhat.com>,
	K Prateek Nayak <kprateek.nayak@amd.com>,
	Luca Abeni <luca.abeni@santannapisa.it>,
	Andrea Righi <arighi@nvidia.com>,
	sched-ext@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: sched/deadline: total_bw leak when a task leaves SCHED_DEADLINE while not queued
Date: Thu, 29 Oct 2026 11:55:10 -1000	[thread overview]
Message-ID: <2b5fb3f22bca6aa383be8f4781923392@kernel.org> (raw)

Hello,

A SCHED_DEADLINE task that is switched to another policy while it isn't
queued (sleeping, or stopped with SIGSTOP) leaves its bandwidth in the root
domain's total_bw. The bandwidth stays after the task exits. The leak
happens as follows.

1. sched_dl_overflow() doesn't subtract the bandwidth of a task leaving
   DEADLINE and leaves that to switched_from_dl().
2. switched_from_dl() calls task_non_contending() only for a queued task.
   task_non_contending() releases the bandwidth right away if the 0-lag time
   has passed and otherwise arms the inactive timer, and
   inactive_task_timer() releases it once the task is no longer dl_task().
3. For a task that isn't queued, the bandwidth is released only if its
   inactive timer is still pending from its dequeue. No timer is pending if
   the task never ran as DEADLINE, or if it already fired while the task
   slept. In the latter case the task was still DEADLINE when the timer
   fired, so it only dropped running_bw. Either way, nothing releases the
   bandwidth.

Repeating it fills total_bw up to the admission cap. After that,
sched_setattr(SCHED_DEADLINE) and CPU offline fail with -EBUSY, and so does
enabling a sched_ext scheduler, because attaching the ext dl_server
bandwidth fails:

  sched_ext: failed to attach ext_server on CPU 0 (-16)
  sched_ext: simple: scx_root_enable() failed (-16)

The reproducer below needs root and debugfs, and assumes x86-64 for the
syscall number 314.

  import ctypes, os, signal, time
  libc = ctypes.CDLL(None, use_errno=True)
  class SchedAttr(ctypes.Structure):
      _fields_ = [("size", ctypes.c_uint32), ("policy", ctypes.c_uint32),
                  ("flags", ctypes.c_uint64), ("nice", ctypes.c_int32),
                  ("prio", ctypes.c_uint32), ("runtime", ctypes.c_uint64),
                  ("deadline", ctypes.c_uint64), ("period", ctypes.c_uint64)]
  def setattr_(pid, policy, runtime=0, period=0):
      a = SchedAttr(ctypes.sizeof(SchedAttr), policy, 0, 0, 0, runtime, period, period)
      if libc.syscall(314, pid, ctypes.byref(a), 0):      # sched_setattr
          raise OSError(ctypes.get_errno(), "sched_setattr")
  def total_bw():
      s = open("/sys/kernel/debug/sched/debug").read().split("dl_rq[0]:")[1]
      return int(s.split("dl_bw->total_bw")[1].split(":")[1].split()[0])
  def trial(name, body, stop=False):
      pid = os.fork()
      if pid == 0:
          if body == "spin":
              while True:
                  pass
          while True:
              time.sleep(3600)
      time.sleep(0.05)
      bw0 = total_bw()
      setattr_(pid, 6, runtime=2_000_000, period=10_000_000)  # DEADLINE 2ms/10ms
      if stop:
          os.kill(pid, signal.SIGSTOP)
          time.sleep(0.02)
      setattr_(pid, 0)                                         # back to OTHER
      os.kill(pid, signal.SIGKILL)
      os.waitpid(pid, 0)
      time.sleep(0.05)
      print(f"{name:24s} leaked total_bw {total_bw() - bw0:7d} (0.2 CPU = 209715)")
  trial("sleeping dl>other", "sleep")
  trial("stopped dl>other", "spin", stop=True)
  trial("running dl>other", "spin")                            # control

On mainline 4dd16e31135e it prints:

  sleeping dl>other        leaked total_bw  209715 (0.2 CPU = 209715)
  stopped dl>other         leaked total_bw  209715 (0.2 CPU = 209715)
  running dl>other         leaked total_bw       0 (0.2 CPU = 209715)

Thanks.

--
tejun

             reply	other threads:[~2026-10-29 21:55 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 21:55 Tejun Heo [this message]
2026-10-30  9:22 ` Juri Lelli

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=2b5fb3f22bca6aa383be8f4781923392@kernel.org \
    --to=tj@kernel.org \
    --cc=arighi@nvidia.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=juri.lelli@redhat.com \
    --cc=kprateek.nayak@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luca.abeni@santannapisa.it \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=sched-ext@lists.linux.dev \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    /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

all inboxes | Powered by JetHome®