[PATCH 6/7] tuna: Change housekeeping cpuset default to partition=member

John Kacur <[email protected]> Thu, 16 Jul 2026 13:45:49 -0400
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
Change the default partition type for housekeeping cpusets from 'isolated'
to 'member', aligning with rteval's design. In rteval, housekeeping cpusets
use partition=member while only the measurement cpuset uses partition=isolated.

This change makes tuna's behavior consistent with rteval for users who are
familiar with both tools.

Add a new --housekeeping-isolated flag to allow users to override the default
and create housekeeping cpusets with partition=isolated if desired.

Updated tests to reflect the new behavior:
- Fixed two existing tests to expect partition=member for housekeeping
- Added test_isolate_housekeeping_isolated_flag to verify the new flag works
- Updated tests/README.md to document the changes (159 total tests now)

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <[email protected]>
---
 tests/README.md          | 27 +++++++++++++++------------
 tests/test_cpuset_cli.py | 30 ++++++++++++++++++++++++++++--
 tuna-cmd.py              |  8 ++++++--
 3 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/tests/README.md b/tests/README.md
index 1b96a54807f9..073f79837196 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -12,7 +12,7 @@ This directory contains the test suite for tuna, using Python's `unittest` frame
 # Run all non-root tests only (45 tests)
 make tests
 
-# Run ALL tests (all 158 tests, requires sudo for root-required tests)
+# Run ALL tests (all 159 tests, requires sudo for root-required tests)
 make test-all
 
 # Run specific test module
@@ -37,7 +37,7 @@ make tests
 # or
 make unit-tests
 
-# Using make (ALL tests including root-required tests, 158 tests)
+# Using make (ALL tests including root-required tests, 159 tests)
 make test-all
 
 # Using the test runner directly (non-root tests only)
@@ -186,7 +186,7 @@ Tests are organized using Python's `unittest` framework. Each test file contains
   - Note: TestCpusetsInit (5 tests) and TestNumaDetection (6 tests) run without root, see non-root section
   - Run: `sudo python3 -m unittest tests.test_cpuset -v` (runs all 43 tests with root)
 
-- **test_cpuset_cli.py** (50 tests) - CLI command tests
+- **test_cpuset_cli.py** (51 tests) - CLI command tests
   - TestGetNextTunaCpusetName: Auto-naming logic (tuna0, tuna1, gap-filling)
   - TestCpusetCreateCLI: Create command with auto/custom names, --isolated, and NUMA auto-detection/override
   - TestCpusetListCLI: List command with --pattern, --verbose, --skip-empty
@@ -196,9 +196,11 @@ Tests are organized using Python's `unittest` framework. Each test file contains
   - TestCpusetStatusCLI: Status command for system-wide cpuset overview
   - TestCpusetModifyCLI: Modify command with --add-cpus, --remove-cpus, --memory-nodes, --isolated/--no-isolated
   - TestTunaMoveCpusetCLI: Integration of --cpuset with tuna move command (5 tests)
-  - TestTunaIsolateCpusetCLI: Integration of --cpuset with tuna isolate command (8 tests)
+  - TestTunaIsolateCpusetCLI: Integration of --cpuset with tuna isolate command (9 tests)
     - Tests cpuset-based CPU isolation with isolated and housekeeping cpusets
-    - Verifies partition types, CPU assignments, and process migration
+    - Verifies partition types: isolated cpuset uses partition=isolated, housekeeping uses partition=member by default
+    - Tests --housekeeping-isolated flag to override default and make housekeeping partition=isolated
+    - Verifies CPU assignments and process migration
     - Tests error handling and backward compatibility
   - TestTunaRunCpusetCLI: Integration of --cpuset with tuna run command (5 tests)
     - test_run_with_cpuset_basic: Verifies running commands in cpusets
@@ -242,7 +244,7 @@ Tests are organized using Python's `unittest` framework. Each test file contains
     - test_save_skips_system_cpusets: Verify system cpuset filtering
   - Run: `sudo python3 -m unittest tests.test_save_apply -v`
 
-**Total: 158 tests** (45 non-root + 113 root-required)
+**Total: 159 tests** (45 non-root + 114 root-required)
 
 ### Test Requirements: Root vs Non-Root
 
@@ -301,7 +303,7 @@ These tests require root to create/manipulate cgroups:
    - Test task migration between cpusets
    - All tests clean up created cpusets in tearDown()
 
-2. **Cpuset CLI tests** (50 tests) test command-line interface functions
+2. **Cpuset CLI tests** (51 tests) test command-line interface functions
    - Test cpuset create/list/destroy/move/show/status/modify commands
    - Verify auto-naming logic (tuna0, tuna1, etc.)
    - Test NUMA-aware memory node auto-detection and manual override
@@ -309,7 +311,8 @@ These tests require root to create/manipulate cgroups:
    - Test process migration with --pids and --threads
    - Test cpuset modification with --add-cpus, --remove-cpus, --memory-nodes, --isolated
    - Test tuna move --cpuset integration (5 tests)
-   - Test tuna isolate --cpuset integration (8 tests) - cpuset-based CPU isolation
+   - Test tuna isolate --cpuset integration (9 tests) - cpuset-based CPU isolation
+   - Test --housekeeping-isolated flag to override default partition=member for housekeeping
    - Test tuna run --cpuset integration (5 tests) - running commands in cpusets
    - All tests clean up created cpusets afterwards
 
@@ -462,9 +465,9 @@ Ran 45 tests in 0.XXXs
 OK (skipped=102)
 ```
 
-### All Tests Including Root-Required (158 tests)
+### All Tests Including Root-Required (159 tests)
 
-Running with sudo will run all 158 tests:
+Running with sudo will run all 159 tests:
 
 ```bash
 $ sudo python3 -m unittest discover -s tests -p "test_*.py" -v
@@ -474,12 +477,12 @@ test_cpuset_path (test_cpuset.TestCpusetsInit) ... ok
 ... (32 cpuset module tests)
 test_first_name_is_tuna0 (test_cpuset_cli.TestGetNextTunaCpusetName) ... ok
 test_sequential_naming (test_cpuset_cli.TestGetNextTunaCpusetName) ... ok
-... (50 cpuset CLI tests)
+... (51 cpuset CLI tests)
 ... (6 show_threads --cpuset tests)
 ... (4 show_threads -G display tests)
 
 ----------------------------------------------------------------------
-Ran 158 tests in X.XXXs
+Ran 159 tests in X.XXXs
 
 OK
 ```
diff --git a/tests/test_cpuset_cli.py b/tests/test_cpuset_cli.py
index c95cea29b4a3..619c719094ac 100644
--- a/tests/test_cpuset_cli.py
+++ b/tests/test_cpuset_cli.py
@@ -1018,6 +1018,7 @@ class TestTunaIsolateCpusetCLI(unittest.TestCase):
         try:
             cpuset.cleanup_cpusets('tuna_*', force=True, recursive=False)
             cpuset.cleanup_cpusets('my_*', force=True, recursive=False)
+            cpuset.cleanup_cpusets('test_*', force=True, recursive=False)
         except:
             pass
 
@@ -1027,6 +1028,7 @@ class TestTunaIsolateCpusetCLI(unittest.TestCase):
         try:
             cpuset.cleanup_cpusets('tuna_*', force=True, recursive=False)
             cpuset.cleanup_cpusets('my_*', force=True, recursive=False)
+            cpuset.cleanup_cpusets('test_*', force=True, recursive=False)
         except:
             pass
 
@@ -1114,7 +1116,7 @@ class TestTunaIsolateCpusetCLI(unittest.TestCase):
 
         with open(f'{hk_path}/cpuset.cpus.partition') as f:
             partition = f.read().strip()
-            self.assertEqual(partition, 'isolated')
+            self.assertEqual(partition, 'member')
 
         # Verify processes were migrated to housekeeping
         with open(f'{hk_path}/cgroup.procs') as f:
@@ -1152,11 +1154,35 @@ class TestTunaIsolateCpusetCLI(unittest.TestCase):
             self.assertIn('2', cpus)
             self.assertIn('3', cpus)
 
-        # Both should be isolated partitions
+        # Verify partition types: isolated cpuset should be 'isolated', housekeeping should be 'member'
         with open('/sys/fs/cgroup/my_rt/cpuset.cpus.partition') as f:
             self.assertEqual(f.read().strip(), 'isolated')
 
         with open('/sys/fs/cgroup/my_hk/cpuset.cpus.partition') as f:
+            self.assertEqual(f.read().strip(), 'member')
+
+    def test_isolate_housekeeping_isolated_flag(self):
+        """Test --housekeeping-isolated flag makes housekeeping partition=isolated"""
+        result = subprocess.run([
+            sys.executable, './tuna-cmd.py', 'isolate',
+            '-c', '0-1', '--cpuset', 'test_iso',
+            '--cpuset-housekeeping', 'test_hk_iso', '2-3',
+            '--housekeeping-isolated'
+        ], capture_output=True, text=True)
+
+        self.assertEqual(result.returncode, 0, f"Command failed: {result.stderr}")
+        self.assertIn('test_iso', result.stdout)
+        self.assertIn('test_hk_iso', result.stdout)
+
+        # Verify both cpusets exist
+        self.assertTrue(os.path.exists('/sys/fs/cgroup/test_iso'))
+        self.assertTrue(os.path.exists('/sys/fs/cgroup/test_hk_iso'))
+
+        # Both should have partition=isolated when --housekeeping-isolated is used
+        with open('/sys/fs/cgroup/test_iso/cpuset.cpus.partition') as f:
+            self.assertEqual(f.read().strip(), 'isolated')
+
+        with open('/sys/fs/cgroup/test_hk_iso/cpuset.cpus.partition') as f:
             self.assertEqual(f.read().strip(), 'isolated')
 
     def test_isolate_housekeeping_without_cpuset_error(self):
diff --git a/tuna-cmd.py b/tuna-cmd.py
index 5e88ed252575..3d4414d93d0e 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -181,7 +181,9 @@ def gen_parser():
                         metavar='NAME', default=None,
                         help='Use cpusets for isolation with optional name (default: tuna_isolated, partition=isolated)')
     isolate.add_argument('--cpuset-housekeeping', nargs='+', metavar='...',
-                        help='Create housekeeping cpuset: [NAME] CPU-LIST (default name: tuna_housekeeping, partition=isolated)')
+                        help='Create housekeeping cpuset: [NAME] CPU-LIST (default name: tuna_housekeeping, partition=member)')
+    isolate.add_argument('--housekeeping-isolated', action='store_true',
+                        help='Make housekeeping cpuset use partition=isolated instead of member (requires --cpuset-housekeeping)')
 
     include_group = include.add_mutually_exclusive_group(required=True)
     include_group.add_argument('-c', '--cpus', **MODS['cpus'])
@@ -1694,7 +1696,9 @@ def main():
                     hk_cpuset = cpuset.Cpuset(housekeeping_name)
                     hk_cpuset.write_memnode(hk_numa)
                     hk_cpuset.assign_cpus(housekeeping_cpus)
-                    hk_cpuset.write_cpu_exclusive(True)  # partition=isolated
+                    # Default to partition=member (False) unless --housekeeping-isolated specified
+                    hk_isolated = getattr(args, 'housekeeping_isolated', False)
+                    hk_cpuset.write_cpu_exclusive(hk_isolated)
 
                     # Migrate processes from root to housekeeping
                     print(f"Migrating processes to housekeeping cpuset...")
-- 
2.55.0