patches and low-level development discussion
 help / color / mirror / code / Atom feed
From: Demi Marie Obenour <demiobenour@gmail.com>
To: Alyssa Ross <hi@alyssa.is>
Cc: Spectrum OS Development <devel@spectrum-os.org>
Subject: Re: [PATCH v4 02/20] tools: Add control group manager
Date: Wed, 29 Jul 2026 20:40:20 -0400	[thread overview]
Message-ID: <295f55ea-f5bd-49e0-8eb6-3a6aab9e981f@gmail.com> (raw)
In-Reply-To: <87cxw8ljkh.fsf@alyssa.is>


[-- Attachment #1.1: Type: text/plain, Size: 22042 bytes --]

On 7/27/26 08:10, Alyssa Ross wrote:
> Demi Marie Obenour <demiobenour@gmail.com> writes:
> 
>> On 7/22/26 12:01, Alyssa Ross wrote:
>>> Demi Marie Obenour <demiobenour@gmail.com> writes:
> 
>>>> +#[derive(Debug)]
>>>> +pub(crate) struct Cgroup {
>>>> +    path: PathBuf,
>>>> +    fd: Vec<(OwnedFd, bool)>,
>>>
>>> There's no point storing all these exclusivity bools, is there?  I think
>>> only the last one is ever checked, so we could make things tighter and
>>> clearer like this, where we only track the exclusivity of the last fd:
>>
>> Cgroup::enable_subtree_control() checks the exclusivity
>> of the caller-provided depth.  Line 232 of main.rs calls
>> enable_subtree_control(2).
>>
>> These bools are only used in assertions, so they could be removed.
>> I will leave that up to you.  The advantage of keeping them is that a
>> panic is vastly easier to debug than a race condition due to improper
>> locking.
> 
> Can you explain to me why we need to support enabling subtree control
> for different depths?  Intuitively, I'd expect this program to only ever
> operate on the cgroup associated with the service that's invoking it.

Init runs "cgroup-setup --init-subtree .", which moves PID 1 to
/$inner.service and enables all controllers in /.  I'll just use sed
to enable the controllers, which avoids special cases around paths
with no file name.

>> I'm very used to writing this kind of code in C, so I went with a C-like
>> style instead of using Rust stdlib APIs.  I don't like having extra
>> abstractions in this kind of code, as it obscures what is going on
>> under the hood.  That is less important here, but it's very important
>> in programs like mount-flatpak.
> 
> I did say myself that my suggestion here might be too clever, because it
> uses the standard library in a way that's unintuitive.  In general,
> though, we can expect readers of Rust code to be more familiar with the
> standard library than with byte-by-byte stringy patch processing.  Would
> still be nicer to take &Path here, I think, even if you then walk
> through it byte by byte, just to make it slightly clearer what this does.

Will fix in v5.

>>>> +    pub fn read_control_file(&self, fd: BorrowedFd, p: &Path) -> Result<Vec<u8>, String> {
>>>> +        let mut buf = Vec::new();
>>>> +        let err = |e: &dyn Display, p: &Path, msg: &str| {
>>>> +            let path = self.path.join(p);
>>>> +            format!("Cannot {msg} {path:?}: {e}")
>>>> +        };
>>>> +        File::from(open_subtree_raw(Path::new(p), fd.as_fd()).map_err(|e| err(&e, p, "open"))?)
>>>
>>> If we're using it for opening files, open_subtree_raw is probably misnamed.
>>
>> Yup!  Do you have a suggestion for improving it?  Maybe open_child()?
> 
> open_beneath?

Will use in v5.

>>>> +    pub fn open_sub_cgroup(
>>>> +        &mut self,
>>>> +        path: &std::path::Path,
>>>> +        exclusive: bool,
>>>> +        allow_missing: bool,
>>>> +    ) -> Result<bool, String> {
>>>> +        let mut iter = path.components().peekable();
>>>> +        while let Some(component) = iter.next() {
>>>
>>> Perhaps would be nicer:
>>>
>>> let mut components = path.components().peekable();
>>> for component in components {
>>
>> That results in a borrowcheck error.  The for loop takes ownership
>> of the iterator, but .peek() is called inside the loop.
> 
> Ah, okay.  This is fine then.

Need is obviated in v5 by a refactor.

>>>> +            let component = match component {
>>>> +                Component::Normal(component) => component,
>>>> +                _ => unreachable!(),
>>>> +            };
>>>> +            let sub_fd = match self
>>>> +                .open_sub_cgroup_raw(OFlags::DIRECTORY | OFlags::RDONLY, component.as_bytes())
>>>> +            {
>>>> +                Ok(sub_fd) => {
>>>> +                    self.path.push(component);
>>>
>>> I would really like to not try to store self.path.  It seems very
>>> complicated to track.  It's also very unclear to me from the name (and
>>> the code) what it is.  Is it the path to the cgroup itself, or to its
>>> parent?  It looks to me like it should be the cgroup itself, but then
>>> what's going on in purge?
>>
>> It's the path to the cgroup itself, relative to /sys/fs/cgroup.  Its only
>> purpose is for logging.
>>
>>> We could actually improve readability of this quite complicated function
>>> even further if you find it acceptable to just use Errno for the error
>>> type.  In that case, we'd just return Result<(), Errno>, and callers
>>> would check for Errno::NOENT if they wanted to allow missing.  Then we
>>> could just completely drop that argument.  In my opinion it would be
>>> worth it to move complexity out of here.
>>
>> I can do this, but it would result in much worse error messages: the
>> error would only reference the file name, not the full cgroup path.
>> Which would you prefer?
> 
> I would much prefer code I can easily understand.  There's always strace
> for getting the full paths when debugging.

This code is dropped in v5.

>>>> +    pub fn wait_for_empty(&self) -> std::io::Result<()> {
>>>> +        assert!(self.exclusive());
>>>> +        let wait_file = self.open_subtree(std::path::Path::new("cgroup.events"))?;
>>>> +        let poll_fd = wait_file.as_raw_fd();
>>>> +        let mut wait_fd = File::from(wait_file);
>>>> +        let mut fds = libc::pollfd {
>>>> +            fd: poll_fd,
>>>> +            events: libc::POLLPRI | libc::POLLERR,
>>>> +            revents: 0,
>>>> +        };
>>>> +        let mut v = vec![];
>>>> +        loop {
>>>> +            v.clear();
>>>> +            wait_fd
>>>> +                .seek(std::io::SeekFrom::Start(0))
>>>> +                .expect("Seek on control group file should succeed");
>>>> +            wait_fd
>>>> +                .read_to_end(&mut v)
>>>> +                .expect("reading from control group should work");
>>>> +            if v.split(|&c| c == b'\n').any(|line| line == b"populated 0") {
>>>> +                break;
>>>> +            }
>>>> +            // SAFETY: FFI call, valid arguments, fds contains 1 element
>>>> +            if unsafe { libc::poll(&raw mut fds, 1, -1) } != 1 {
>>>> +                panic!("poll failed");
>>>> +            }
>>>> +        }
>>>
>>> Are you 100% confident that this doesn't race?  I don't understand why
>>> poll would be triggered in this scenario:
>>>
>>>  1. "1" is written to cgroup.kill
>>>  2. Every process in the cgroup exits and is reaped.
>>>  3. cgroup.events is opened, with the cgroup already empty.
>>>
>>> Are you not relying on 2 happening after 3?  Presumably if you open
>>> cgroup.events for a cgroup that's already empty, you're not going to get
>>> a poll event to tell you it's empty.
>>
>> In that case, cgroup.events will include a "populated 0"
>> line, so poll will not be called.
> 
> You are correct. :)
> 
> Perhaps could be written more clearly with a while, something like this:
> 
>     while !v.split(|&c| c == b'\n').any(|line| line == b"populated 0") {
>         // SAFETY: FFI call, valid arguments, fds contains 1 element
>         if unsafe { libc::poll(&raw mut fds, 1, -1) } != 1 {
>             panic!("poll failed");
>         }
>         v.clear();
>         wait_fd
>             .seek(std::io::SeekFrom::Start(0))
>             .expect("Seek on control group file should succeed");
>         wait_fd
>             .read_to_end(&mut v)
>             .expect("reading from control group should work");
>     }
> 
> But if you prefer the current way (which avoids a redundant initial
> check of an empty Vec, although that should be cheap) that's also fine.

That version would call poll() before checking that the cgroup is empty,
and that *can* deadlock :).

>>>> +    pub(crate) fn make_child(&mut self, path: &Path) -> Result<(), Errno> {
>>>> +        assert!(self.exclusive());
>>>> +        let component = path.as_os_str().as_bytes();
>>>> +        assert_single_component(component);
>>>> +        match rustix::fs::mkdirat(
>>>> +            self.as_fd(),
>>>> +            path,
>>>> +            Mode::RUSR
>>>> +                | Mode::WUSR
>>>> +                | Mode::XUSR
>>>> +                | Mode::RGRP
>>>> +                | Mode::XGRP
>>>> +                | Mode::ROTH
>>>> +                | Mode::XOTH,
>>>> +        ) {
>>>> +            Ok(()) | Err(Errno::EXIST) => {}
>>>> +            bad => return bad,
>>>> +        }
>>>> +        let p = self.open_sub_cgroup_raw(OFlags::RDONLY | OFlags::DIRECTORY, component)?;
>>>> +        // exclusive lock on parent acts as exclusive lock on child
>>>> +        self.fd.push((p, true));
>>>> +        self.path.push(path);
>>>> +        Ok(())
>>>> +    }
>>>> +
>>>> +    pub(super) fn purge(&mut self, path: &Path) -> Result<(), String> {
>>>
>>> I guess we have to call purge on the parent, rather than on the cgroup
>>> itself, because of the unlink?  Maybe we could call it purge_child?  It
>>> confused me for a while.
>>
>> Correct.  Will rename in v5.
>>
>> The way to understand this code is that Cgroup has two stacks: one
>> for file descriptors and one for path components.  All operations
>> operate at a specified depth from the top of the stack.  1 refers to
>> the top of the stack, 2 to one level below that, and so on.
> 
> 1-indexing is a little unintuitive for Rust, no?
> 
>>
>> This function is really confusing because it performs multiple pushes
>> and pops on the internal file descriptor stack.  The specific algorithm is:
>>
>>  1. Start with an exclusive lock.
>>
>>  2. Try to delete the child directly.
>>
>>  3. If deletion succeeds, or if it fails with ENOENT, return success.
>>
>>  4. If deletion fails with anything other than EBUSY, return an error.
>>
>>  5. Open the child cgroup and take an exclusive lock on it.  This pushes
>>     the child cgroup's FD onto the stack.  The open_subtree() method
>>     also pushes the child path onto the stack.
>>
>>  6. Take a *shared* lock on the FD that is directly below the top
>>     of the stack.  This is the file descriptor that was initially
>>     on the top of the stack.
>>
>>     This releases the exclusive lock, allowing other operations on
>>     different children to proceed.  Different operations on the cgroup
>>     being purged will be blocked by the exclusive lock taken in step 5.
>>
>>  7. Kill all programs in the cgroup by writing 1 to cgroup.kill.
>>
>>  8. Open cgroup.events.
>>
>>  9. Read from the FD opened in step 8.  If the file contains the line
>>     "populated 0", go to step 11.
>>
>> 10. Call poll() on the FD opened in step 8 to wait for POLLERR or
>>     POLLPRI to happen.  Then go back to step 9.
>>
>>     This is race-free because the kernel will set the "this is ready"
>>     flag after every change that affects what would be read from
>>     the file.
>>
>> 11. Pop the file descriptor to the being-purged cgroup from the stack.
>>
>> 12. Use the just-popped file descriptor to remove all subdirectories
>>     recursively.  Files must not be deleted, as the kernel doesn't
>>     allow it.  Then close the file descriptor, releasing the exclusive
>>     lock held on it.
>>     
>> 13. Take an exclusive lock on the *parent* of the cgroup that was just purged.
>>
>>     This must be done after the file descriptor to the cgroup being
>>     purged has been closed.  Otherwise, there is the potential for
>>     an ABBA deadlock: another program might hold a shared lock on
>>     the parent, and be waiting to get an exclusive lock on the child.
>>
>> 14. Delete the being-purged cgroup.  Treat EBUSY and ENOENT as success:
>>     the first means that a concurrently-running program re-created the
>>     cgroup, while the second means that a concurrently-running program
>>     deleted it.  The name of the cgroup being purged is currently at
>>     the top of the path stack.
>>
>> 15. Pop the name of the cgroup being purged off of the stack.
>>     
>> At the end, self is in the same state it was before the operation.
>>
>> If you are complaining that this is about as readable as Forth,
>> then I agree with you :).
> 
> Could this be made clearer by not mutating self, and either just storing
> the child stuff in local variables or another Cgroup object?  I think a
> big part of the confusion here is that this function temporarily changes
> which cgroup the Cgroup object it's called on refers to while it's
> running.  That's extremely difficult to reason about.

That makes sense.  I'll try to move stuff into helper objects.  You're
correct that the 

>>>> +            Err(e) => return Err(format!("Cannot purge {:?}: {e}", self.joined_path(path))),
>>>> +        }
>>>> +        if !self.open_sub_cgroup(path, true, true)? {
>>>> +            return Ok(());
>>>> +        }
>>>> +
>>>> +        rustix::fs::flock(
>>>> +            self.fd[self.fd.len() - 2].0.as_fd(),
>>>> +            FlockOperation::LockShared,
>>>
>>> We already must have at least a shared lock on this at this point, no?
>>> I don't think we need another one.
>>
>> We actually have an exclusive lock.  If it succeeds,
>> Cgroup::open_sub_cgroup() pushes a file descriptor onto self.fd.
>> Therefore, the fd being locked here is the one that was initially on the
>> top of the stack.  We assert that an exclusive lock is held on that FD.
>>
>> Waiting for the control group to become empty is a blocking operation,
>> so this downgrades the lock to a shared one.  Otherwise, an in-progress
>> purge of /a/b would prevent /a/c from being created.
> 
> Ah, didn't realise it would downgrade.  Makes sense.



>>>> +fn remove_recursively(fd: OwnedFd, remaining_depth: usize) -> Result<(), Errno> {
>>>> +    if remaining_depth < 1 {
>>>> +        panic!("control groups too deeply nested");
>>>> +    }
>>>> +    let mut d = rustix::fs::Dir::new(fd).expect("cannot start iterating");
>>>> +    while let Some(element) = d.next() {
>>>> +        let element = element.expect("Iterating through a cgroup directory failed?");
>>>> +        if element.file_type() != rustix::fs::FileType::Directory {
>>>> +            continue;
>>>> +        }
>>>> +
>>>> +        let remaining_depth = remaining_depth - 1;
>>>> +        let d: &rustix::fs::Dir = &d;
>>>> +        let dirfd = d.fd().unwrap();
>>>> +        let path = element.file_name();
>>>> +        remove_all(remaining_depth, dirfd, path)?;
> 
> I'd inline these variables into the function call the extent possible.
> Otherwise I have to follow a lot of shuffling around.

Will fix in v5.

>>>> +    }
>>>> +    drop(d);
>>>> +    Ok(())
>>>> +}
>>>> +
>>>> +fn remove_all(
>>>> +    remaining_depth: usize,
>>>> +    dirfd: BorrowedFd<'_>,
>>>> +    path: &std::ffi::CStr,
>>>> +) -> Result<(), Errno> {
>>>> +    if path == c"." || path == c".." {
>>>> +        return Ok(());
>>>> +    }
> 
> It's a bit weird that calling remove_all on . or .. does not fail.
> Maybe would be clearer to move this check to the call site?

Will fix in v5, making the code simpler.

>>>> +    if rustix::fs::unlinkat(dirfd, path, AtFlags::REMOVEDIR).is_ok() {
>>>> +        return Ok(());
>>>> +    }
> 
> We could drop this, right?  A few extra syscalls, but less to wrap my
> head around.

Correct.

>>>> +    let fd = rustix::fs::openat2(
>>>> +        dirfd,
>>>> +        path,
>>>> +        OFlags::CLOEXEC | OFlags::NOFOLLOW | OFlags::RDONLY | OFlags::DIRECTORY,
>>>> +        Mode::empty(),
>>>> +        ResolveFlags::NO_SYMLINKS | ResolveFlags::BENEATH | ResolveFlags::NO_XDEV,
>>>> +    )?;
>>>> +    remove_recursively(fd, remaining_depth)?;
>>>> +    rustix::fs::unlinkat(dirfd, path, AtFlags::REMOVEDIR)?;
>>>> +    Ok(())
>>>> +}
>>>
>>> Could we save a lot of code by calling std::fs::remove_dir_all with a
>>> /proc/self/fd path?  It's already documented to ignore symlinks.
>>
>> I tried, but that tries to delete files too, and that isn't allowed.
> 
> Ah, alright.  That could use an explanatory comment.
> 
> How can we be confident we have enough stack for this?  Is there a way
> it could be done non-recursively, with state on the heap?

In Spectrum, the depth is currently limited to a small constant.

>>>> +    let status = parse_digit_string(&args.next().unwrap(), "exit status")?;
>>>> +    let signal = args.next().unwrap();
>>>> +    let signal = if status == 256 {
>>>> +        Some(parse_digit_string(&signal, "signal number")?)
>>>> +    } else {
>>>> +        None
>>>> +    };
>>>> +    let service = args.next().unwrap();
>>>> +
>>>> +    let (path, mut cgroup) = open_relative_cgroup(service)?;
>>>> +    let cgroup_target = Path::new(path.file_name().unwrap());
>>>> +    let exit_125 = if let Some(signal) = signal {
>>>> +        match signal as libc::c_int {
>>>> +            libc::SIGBUS
>>>> +            | libc::SIGFPE
>>>> +            | libc::SIGABRT
>>>> +            | libc::SIGTRAP
>>>> +            | libc::SIGSEGV
>>>> +            | libc::SIGILL => {
>>>> +                // Process *crashed*, indicating a *possible exploit attempt*.
>>>> +                // s6 should *not* restart it.  This is distinct from a Rust panic,
>>>> +                // which is much less likely to indicate memory corruption.
>>>> +                true
>>>> +            }
>>>
>>> This has absolutely nothing to do with cgroups.  If you want to have
>>> some common finish behaviour, a program called cgroup-setup is not the
>>> place for it.  I don't think there's any need for a separate
>>> cgroup-s6-finish mode (as opposed to cgroup-purge).
>>
>> This program is a multi-call binary, so the various things it can do
>> aren't necessarily super tightly related.  For instance, all of the
>> execline binaries can be built as one program, as can most if not
>> all busybox applets.  When invoked as cgroup-setup or cgroup-purge,
>> it indeed only does cgroup-related tasks.  cgroup-s6-finish not
>> only handles cgroups, but also other tasks related to being an s6
>> finish script.
>>
>> That said, using this changes behavior in a way that isn't related
>> to cgroups, so if it is to be used at all it should be in a separate
>> patch series.  I'll remove this from v5.
> 
> Thank you.  If we want to have a big multi-call binary that does lots of
> different things, only some of which are cgroup-specific, cgroup-setup
> is not the name for that program.

No argument there!

>>>> +fn local_cgroup() -> Result<PathBuf, String> {
>>>> +    let mut local_cgroup: Vec<u8> = std::fs::read("/proc/thread-self/cgroup")
>>>> +        .map_err(|e| format!("cannot read /proc/thread-self/cgroup: {e}"))?;
>>>> +    let local_cgroup_len = local_cgroup.len();
>>>> +    if local_cgroup_len < 5
>>>> +        || local_cgroup[..4] != *b"0::/"
>>>> +        || local_cgroup[local_cgroup_len - 1] != b'\n'
>>>> +        || local_cgroup[4..local_cgroup_len - 1].contains(&b'\n')
>>>
>>> Last time I suggested a clearer way of doing this, but it has instead
>>> got even less clear.
>>>
>>> (I'm not sure why we'd care if there's a newline specifically, as
>>> opposed to any other control character.)
>>
>> If cgroups v1 is in use, the file can contain multiple lines, one for
>> each cgroup the program is in.  I also am not sure if starting with
>> "0::/" is an invariant in that case.
>>
>> Using this program with cgroups v1 mounted is user error and will
>> never happen on Spectrum, but if this tool is used outside of Spectrum,
>> it could happen.
> 
> I see.  It seems like with cgroups v1, it _could_ start with 0::/, but
> probably wouldn't.  I think it may not be possible to tell from this
> file whether cgroups v1 is in use.
> 
> So I suppose it depends what you want to happen if cgroups v1 is in use.
> If it looks enough like cgroups v2, do you continue, or do you
> explicitly check for cgroups v1?  If the latter (sounds more sensible to
> me), you need to explicitly check for cgroups v1 somehow I think.  Can
> cgroups v1 and v2 be in use at the same time?  If so, checking might be
> complicated, but if not, you can just check what type of filesystem is
> mounted at /sys/fs/cgroup, or see if it has a
> /sys/fs/cgroup/cgroup.controllers file.

In the case of Spectrum, I think it's okay to just panic if cgroups v2
isn't mounted or isn't working properly.  It's a bug in either
cgroup-setup or the kernel, almost certainly the former.

>>>> +    {
>>>> +        // It's possible to get here if the cgroup path contains a newline,
>>>> +        // but that never happens in Spectrum.
>>>> +        return Err(format!(
>>>> +            "Invalid contents {local_cgroup:?} of /proc/thread-self/cgroup - \
>>>> +             do you have cgroups v1 mounted instead of cgroups v2?"
>>>> +        ));
>>>> +    }
>>>> +
>>>> +    local_cgroup.copy_within(4..local_cgroup_len - 1, 0);
>>>> +    local_cgroup.truncate(local_cgroup_len - 5);
>>>> +    let local_cgroup = OsString::from_vec(local_cgroup);
>>>> +    check_path(&local_cgroup).unwrap();
>>>
>>> Why do we need to do this?  You're worried the kernel is going to start
>>> including .. components in /proc/thread-self/cgroup?
>>
>> Originally, I was going to create a wrapper around `Path` that
>> guaranteed no `.` or `..` components were present.  Its constructor
>> would have checked this invariant.  However, this turned out to be
>> more work due to the amount of wrapper functions required.
> 
> Wise not to proceed with that, I think.  But I don't think we need this
> particular instance of the check, given it comes from the kernel.

Dropped in v5.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

  reply	other threads:[~2026-07-30  0:40 UTC|newest]

Thread overview: 132+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-20 14:23 [PATCH] Set up control groups for most services Demi Marie Obenour
2026-06-20 17:27 ` [PATCH v2] " Demi Marie Obenour
2026-06-24 12:13   ` Alyssa Ross
2026-06-24 12:36     ` Alyssa Ross
2026-06-25  2:03     ` Demi Marie Obenour
2026-06-25  3:03       ` Demi Marie Obenour
2026-06-25  9:55         ` Alyssa Ross
2026-06-25  9:49       ` Alyssa Ross
2026-07-11 20:12   ` [PATCH v3 00/22] Control group support Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 01/22] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-13  9:39       ` Alyssa Ross
2026-07-13 17:27         ` Demi Marie Obenour
2026-07-15 18:28           ` Alyssa Ross
2026-07-22  1:59       ` [PATCH v4 00/20] Control group support Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 01/20] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 02/20] tools: Add control group manager Demi Marie Obenour
2026-07-22 16:01           ` Alyssa Ross
2026-07-23 23:07             ` Demi Marie Obenour
2026-07-27 12:10               ` Alyssa Ross
2026-07-30  0:40                 ` Demi Marie Obenour [this message]
2026-07-30 14:53                   ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 03/20] Documentation: Mention control groups Demi Marie Obenour
2026-07-27 11:22           ` Alyssa Ross
2026-07-28 10:41           ` Valentin Gagarin
2026-07-22  1:59         ` [PATCH v4 04/20] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-27 11:23           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 05/20] host/rootfs: Add helper program for per-VM services Demi Marie Obenour
2026-07-27 11:27           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 06/20] host/rootfs: Enable controllers in sub-cgroups Demi Marie Obenour
2026-07-27 12:11           ` Alyssa Ross
2026-07-28  2:19             ` Demi Marie Obenour
2026-07-29 14:13               ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 07/20] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-27 12:12           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 08/20] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-27 12:14           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 09/20] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-27 12:16           ` Alyssa Ross
2026-07-28  3:01             ` Demi Marie Obenour
2026-07-29 14:29               ` Alyssa Ross
2026-07-29 20:20                 ` Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 10/20] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 11/20] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 12/20] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-27 12:18           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 13/20] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-27 12:19           ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 14/20] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 15/20] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 16/20] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 17/20] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 18/20] host/rootfs: systemd-udevd: " Demi Marie Obenour
2026-07-27 12:20           ` Alyssa Ross
2026-07-28  3:11             ` Demi Marie Obenour
2026-07-29 14:15               ` Alyssa Ross
2026-07-29 20:39                 ` Demi Marie Obenour
2026-07-30 14:55                   ` Alyssa Ross
2026-07-22  1:59         ` [PATCH v4 19/20] host/rootfs: weston: " Demi Marie Obenour
2026-07-27 12:23           ` Alyssa Ross
2026-07-28  3:14             ` Demi Marie Obenour
2026-07-22  1:59         ` [PATCH v4 20/20] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-07-31 21:54         ` [PATCH v5 00/19] Control group support Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 01/19] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 02/19] tools: Add control group manager Demi Marie Obenour
2026-08-03 12:47             ` Alyssa Ross
2026-08-05  1:36               ` Demi Marie Obenour
2026-08-05 16:39                 ` Alyssa Ross
2026-07-31 21:54           ` [PATCH v5 03/19] Documentation: Mention control groups Demi Marie Obenour
2026-08-03 13:22             ` Alyssa Ross
2026-07-31 21:54           ` [PATCH v5 04/19] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 05/19] host/rootfs: Enable controllers in non-root cgroups Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 06/19] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 07/19] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 08/19] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 09/19] host/rootfs: weston: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 10/19] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 11/19] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 12/19] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 13/19] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 14/19] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 15/19] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 16/19] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 17/19] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 18/19] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-31 21:54           ` [PATCH v5 19/19] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-08-06  1:16           ` [PATCH v6 00/19] Control group support Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 01/19] host/rootfs: Mount filesystems before s6-rc-init Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 02/19] tools: Add control group manager Demi Marie Obenour
2026-08-06  6:58               ` Demi Marie Obenour
2026-08-12 21:10               ` Alyssa Ross
2026-08-06  1:16             ` [PATCH v6 03/19] Documentation: Mention control groups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 04/19] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 05/19] host/rootfs: Enable controllers in non-root cgroups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 06/19] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 07/19] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 08/19] host/rootfs: systemd-udevd: Run in cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 09/19] host/rootfs: weston: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 10/19] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 11/19] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 12/19] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 13/19] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 14/19] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 15/19] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 16/19] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 17/19] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 18/19] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-08-06  1:16             ` [PATCH v6 19/19] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-08-12 21:06             ` [PATCH v6 00/19] Control group support Alyssa Ross
2026-07-11 20:12     ` [PATCH v3 02/22] scripts: Support symlinks in s6-rc-compile inputs Demi Marie Obenour
2026-07-13  9:42       ` Alyssa Ross
2026-07-13 14:19         ` Demi Marie Obenour
2026-07-15 18:30           ` Alyssa Ross
2026-07-11 20:12     ` [PATCH v3 03/22] tools: Add control group manager Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 04/22] Documentation: Mention control groups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 05/22] Mount cgroup2 filesystem at /sys/fs/cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 06/22] host/rootfs: Add helper program for per-VM services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 07/22] host/rootfs: Enable controllers in sub-cgroups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 08/22] host/rootfs: Add comments where cgroups are intentionally not used Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 09/22] host/rootfs: serial-getty-generator: Use cgroups Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 10/22] host/rootfs: Set up parent cgroup for all per-VM services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 11/22] host/rootfs: Create per-VM cgroup for all of the VM's services Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 12/22] host/rootfs: run-vmm: Create per-VM cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 13/22] host/rootfs: run-appimage: Purge the " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 14/22] host/rootfs: run-flatpak: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 15/22] host/rootfs: dbus: Run in cgroup Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 16/22] host/rootfs: vhost-user-fs: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 17/22] host/rootfs: vhost-user-gpu: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 18/22] host/rootfs: xdg-desktop-portal-spectrum-host: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 19/22] host/rootfs: systemd-udevd: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 20/22] host/rootfs: weston: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 21/22] host/rootfs: spectrum-router: " Demi Marie Obenour
2026-07-11 20:12     ` [PATCH v3 22/22] host/rootfs: vm-import: Use elglob -w Demi Marie Obenour

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=295f55ea-f5bd-49e0-8eb6-3a6aab9e981f@gmail.com \
    --to=demiobenour@gmail.com \
    --cc=devel@spectrum-os.org \
    --cc=hi@alyssa.is \
    /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.
Code repositories for project(s) associated with this public inbox

	https://spectrum-os.org/git/doc
	https://spectrum-os.org/git/mktuntap
	https://spectrum-os.org/git/spectrum
	https://spectrum-os.org/git/ucspi-vsock

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).