[PATCH 21/36] tuna: Change cpuset commands to skip empty by default
John Kacur <[email protected]> Fri, 10 Jul 2026 10:14:59 -0400
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
Change the default behavior for cpuset list, destroy, and status commands to skip empty cpusets (cpusets with no CPUs assigned). Previously, these commands required --skip-empty to filter out empty cpusets. This was counterintuitive since empty cpusets are rarely useful in practice. New flags: - cpuset list/status: Use --show-empty to include empty cpusets - cpuset destroy: Use --include-empty to destroy empty cpusets The default behavior (without flags) now skips empty cpusets for all three commands, providing a cleaner and more useful user experience. Tests updated to reflect the new flag names and default behavior. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: John Kacur <[email protected]> --- tests/test_cpuset_cli.py | 22 +++++++++++----------- tuna-cmd.py | 36 ++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/test_cpuset_cli.py b/tests/test_cpuset_cli.py index 51c9b5d193c0..f8bf723d5c72 100644 --- a/tests/test_cpuset_cli.py +++ b/tests/test_cpuset_cli.py @@ -248,12 +248,12 @@ class TestCpusetListCLI(unittest.TestCase): self.assertIn('test_list_2', cpusets) self.assertIn('test_list_empty', cpusets) - def test_list_skip_empty(self): - """Test cpuset list with --skip-empty""" + def test_list_default_skips_empty(self): + """Test cpuset list default behavior (skips empty cpusets)""" # Get all cpusets matching pattern all_cpusets = cpuset.list_cpusets(pattern='test_list_*', recursive=False) - # Filter out empty ones manually (simulating --skip-empty) + # Filter out empty ones manually (simulating default behavior) non_empty = [] for cs_name in all_cpusets: cs_path = os.path.join('/sys/fs/cgroup', cs_name) @@ -300,7 +300,7 @@ class TestCpusetDestroyCLI(unittest.TestCase): # Destroy it (without force, no tasks) cpuset_destroy(name='test_destroy_single', pattern=None, force=False, - skip_empty=False, recursive=True) + include_empty=True, recursive=True) # Should be gone self.assertFalse(os.path.exists(path)) @@ -317,15 +317,15 @@ class TestCpusetDestroyCLI(unittest.TestCase): # Destroy by pattern cpuset_destroy(name=None, pattern='test_destroy_bulk_*', force=True, - skip_empty=False, recursive=False) + include_empty=True, recursive=False) # Should be gone self.assertFalse(os.path.exists(cs1.cpuset_path)) self.assertFalse(os.path.exists(cs2.cpuset_path)) self.created_cpusets = [] - def test_destroy_with_skip_empty(self): - """Test destroying with --skip-empty (should skip empty cpusets)""" + def test_destroy_default_skips_empty(self): + """Test destroying with default behavior (skips empty cpusets)""" # Create cpuset with CPUs cs1 = cpuset.Cpuset('test_destroy_nonempty') cs1.write_memnode('0') @@ -337,9 +337,9 @@ class TestCpusetDestroyCLI(unittest.TestCase): cs2.write_memnode('0') self.created_cpusets.append('test_destroy_empty') - # Destroy with skip_empty + # Destroy with default (include_empty=False, which skips empty) cpuset_destroy(name=None, pattern='test_destroy_*', force=True, - skip_empty=True, recursive=False) + include_empty=False, recursive=False) # Non-empty should be destroyed self.assertFalse(os.path.exists(cs1.cpuset_path)) @@ -617,8 +617,8 @@ class TestCpusetStatusCLI(unittest.TestCase): except: pass - def test_status_skip_empty(self): - """Test status with skip_empty option""" + def test_status_empty_filtering(self): + """Test status filtering of empty cpusets""" # Create cpuset with CPUs cs1 = cpuset.Cpuset('test_status_filled') cs1.write_memnode('0') diff --git a/tuna-cmd.py b/tuna-cmd.py index b560ec805b7a..62553bf51869 100755 --- a/tuna-cmd.py +++ b/tuna-cmd.py @@ -283,7 +283,7 @@ def gen_parser(): cpuset_list = cpuset_subparser.add_parser('list', description='List cpusets', help='List cpusets') cpuset_list.add_argument('-p', '--pattern', type=str, metavar='PATTERN', help='Filter by glob pattern (e.g., tuna*)') cpuset_list.add_argument('-v', '--verbose', action='store_true', help='Show detailed information (CPUs, tasks, partition type)') - cpuset_list.add_argument('--skip-empty', action='store_true', help='Skip cpusets with no CPUs assigned') + cpuset_list.add_argument('--show-empty', action='store_true', help='Include cpusets with no CPUs assigned (default: skip empty)') cpuset_list.add_argument('--no-recursive', dest='recursive', action='store_false', default=True, help='Only search top-level cpusets') # cpuset destroy @@ -291,7 +291,7 @@ def gen_parser(): cpuset_destroy.add_argument('name', nargs='?', type=str, metavar='NAME', help='Cpuset name to destroy (use this OR --pattern)') cpuset_destroy.add_argument('-p', '--pattern', type=str, metavar='PATTERN', help='Glob pattern to destroy multiple cpusets (e.g., tuna*)') cpuset_destroy.add_argument('-f', '--force', action='store_true', help='Migrate tasks to root cgroup before destroying') - cpuset_destroy.add_argument('--skip-empty', action='store_true', help='When using --pattern, skip cpusets with no CPUs assigned') + cpuset_destroy.add_argument('--include-empty', action='store_true', help='When using --pattern, include cpusets with no CPUs assigned (default: skip empty)') cpuset_destroy.add_argument('--no-recursive', dest='recursive', action='store_false', default=True, help='Only destroy top-level cpusets when using --pattern') # cpuset move @@ -308,7 +308,7 @@ def gen_parser(): # cpuset status cpuset_status = cpuset_subparser.add_parser('status', description='Show system-wide cpuset overview', help='Show cpuset status') cpuset_status.add_argument('-p', '--pattern', type=str, metavar='PATTERN', help='Filter by glob pattern (e.g., tuna*)') - cpuset_status.add_argument('--skip-empty', action='store_true', help='Skip cpusets with no CPUs assigned') + cpuset_status.add_argument('--show-empty', action='store_true', help='Include cpusets with no CPUs assigned (default: skip empty)') cpuset_status.add_argument('--no-recursive', dest='recursive', action='store_false', default=True, help='Only show top-level cpusets') return parser @@ -983,13 +983,13 @@ def cpuset_create(cpu_list, name, isolated, memory_nodes=None): sys.exit(1) -def cpuset_list(pattern, verbose, skip_empty, recursive): +def cpuset_list(pattern, verbose, show_empty, recursive): """Handler for 'tuna cpuset list' command. Args: pattern: Glob pattern to filter cpusets (None for all) verbose: If True, show detailed info (CPUs, memory nodes, tasks, partition) - skip_empty: If True, skip cpusets with no CPUs assigned + show_empty: If True, show cpusets with no CPUs assigned (default: skip empty) recursive: If True, search nested cpusets """ # Check cgroup v2 support @@ -1001,8 +1001,8 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): # List cpusets cpusets = cpuset.list_cpusets(pattern=pattern, recursive=recursive) - # Filter out empty cpusets if requested - if skip_empty: + # Filter out empty cpusets unless --show-empty was specified + if not show_empty: filtered_cpusets = [] for cs_name in cpusets: cs_path = os.path.join('/sys/fs/cgroup', cs_name) @@ -1096,14 +1096,14 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): print("This may indicate a problem with blocklist protection.", file=sys.stderr) -def cpuset_destroy(name, pattern, force, skip_empty, recursive): +def cpuset_destroy(name, pattern, force, include_empty, recursive): """Handler for 'tuna cpuset destroy' command. Args: name: Cpuset name to destroy (single cpuset, mutually exclusive with pattern) pattern: Glob pattern for cpusets to destroy (mutually exclusive with name) force: If True, migrate tasks to root before destroying - skip_empty: If True, skip cpusets with no CPUs assigned (only used with pattern) + include_empty: If True, include cpusets with no CPUs assigned (default: skip empty, only used with pattern) recursive: If True, find and clean nested cpusets (only used with pattern) """ # Check cgroup v2 support @@ -1122,12 +1122,12 @@ def cpuset_destroy(name, pattern, force, skip_empty, recursive): try: if name: - # Single cpuset destroy (skip_empty not applicable) + # Single cpuset destroy (include_empty not applicable) cpuset.destroy_cpuset(name, force=force) print(f"Destroyed cpuset '{name}'") else: # Pattern-based cleanup - if skip_empty: + if not include_empty: # Filter out empty cpusets before destroying all_cpusets = cpuset.list_cpusets(pattern=pattern, recursive=recursive) cpusets_to_destroy = [] @@ -1312,12 +1312,12 @@ def cpuset_show(name, show_tasks): print(" These critical system processes should not be in custom cpusets!", file=sys.stderr) -def cpuset_status(pattern, skip_empty, recursive): +def cpuset_status(pattern, show_empty, recursive): """Handler for 'tuna cpuset status' command. Args: pattern: Glob pattern to filter cpusets (None for all) - skip_empty: If True, skip cpusets with no CPUs assigned + show_empty: If True, show cpusets with no CPUs assigned (default: skip empty) recursive: If True, search nested cpusets """ # Check cgroup v2 support @@ -1326,8 +1326,8 @@ def cpuset_status(pattern, skip_empty, recursive): print("Error: cgroup v2 cpusets not supported on this system", file=sys.stderr) sys.exit(1) - # Get all cpusets info - infos = get_all_cpusets_info(pattern=pattern, recursive=recursive, skip_empty=skip_empty) + # Get all cpusets info (skip_empty is inverse of show_empty) + infos = get_all_cpusets_info(pattern=pattern, recursive=recursive, skip_empty=not show_empty) if not infos: if pattern: @@ -1625,10 +1625,10 @@ def main(): cpuset_create(args.cpu_list, args.name, args.isolated, args.memory_nodes) elif args.cpuset_command == 'list': - cpuset_list(args.pattern, args.verbose, args.skip_empty, args.recursive) + cpuset_list(args.pattern, args.verbose, args.show_empty, args.recursive) elif args.cpuset_command == 'destroy': - cpuset_destroy(args.name, args.pattern, args.force, args.skip_empty, args.recursive) + cpuset_destroy(args.name, args.pattern, args.force, args.include_empty, args.recursive) elif args.cpuset_command == 'move': cpuset_move(args.name, args.thread_list, args.pid_list) @@ -1637,7 +1637,7 @@ def main(): cpuset_show(args.name, args.show_tasks) elif args.cpuset_command == 'status': - cpuset_status(args.pattern, args.skip_empty, args.recursive) + cpuset_status(args.pattern, args.show_empty, args.recursive) if __name__ == '__main__': -- 2.54.0