From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 021A93CC7EA for ; Wed, 24 Jun 2026 15:51:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782316285; cv=none; b=m49ZBXyq1gQX9GtzXZ68ovqzDlya1oT/y4J+YCeevFjX9dIQjZVEeHGImNboWOJ06jWJG4SldAh/7SJc25HEwBsUVu2JaAJ5BwOIrB9LDDOWJUjciKwRxEBb5Iy+mRxhgyJmlxuamQBBKo0CX8EgWlOMjlRaexCdi0kTChrax5c= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782316285; c=relaxed/simple; bh=SPSyKGFNqUlE00c799wH7mnKP2DkeNW0J3KraDUXkq8=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=ga0sn3bnhwAvCcQjsalSxqZFrGt+oByNymSvvSVAGNOBgJ3nKOSKHdtIdCwnJTM3QMcqgutIhzTx7tQGEaiey+3Vctft0uQ/ItKiydPMopHzLnWEtXnpqAWwybZh1yWe17n3zDktMmT6aI4ce5/2Mxvl/E0WWZy7cCAuakFjcTU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com; spf=pass smtp.mailfrom=suse.com; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b=LiyBVKWL; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=suse.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=suse.com header.i=@suse.com header.b="LiyBVKWL" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-490b64c8311so15150165e9.3 for ; Wed, 24 Jun 2026 08:51:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=google; t=1782316282; x=1782921082; darn=vger.kernel.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=lDMWkskmjjy58meVllaXju4HrZ/TNfRubZ9JnOoUZ2A=; b=LiyBVKWLBI8pBAwACVffiHCkIZApUG7dh9d9D1GmybNJ6WaVll8qrREvPsXoPOVYkz vRxH+VuugeqE7znLmvocTBr4p0T//AEFxaQqb0FPKC5k320ixIZAO3iKqjWZuVFm4af4 dDbbFEi5Mr0CCI3ei/EYJyv0glo56/PnowOZpDS9buHW+96ZLzooVFVUxXQA7QFB0f9t 98xM4irEqyjNkV1CHYNAhL/3sXQXmo1EwVZ2xh/mqBA9WTNjo0g+3PWN/gbw+co4Ff7d tssa4FcldnN+jJ/LmVlTMPWnYEn9yx0UWMgxFu658ZsmWTIvDS9RXzeI1KDB90MWgtfK s0uQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782316282; x=1782921082; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lDMWkskmjjy58meVllaXju4HrZ/TNfRubZ9JnOoUZ2A=; b=sYALzLS1yU3gcvKxWgY3m6WkhFh6x2Mwebq+UW4AoSSO8D7Hj5PT0nL8hikjhsbkjq DKmKYB8MGyETQmDKVrxRGWecFkVAWvdVagUev4aPMTOxbVy7zuwVzIasylEinln/LsH8 AVjeTbRDfwmvnesFoM5HEy1tUm6Cvh055c/DUdeGBl8OkKAEQwvuwu6S3NxD9LObDMLA +8VzG1wKE8bkOENmjo54sn/tM6vdpKkgVp9rm17uYN60On1VmrLpLa65gp4NiLTC8ZPx sI5cZbOqfw9lC3wkAOrGmPpS9PjX2Pcfncs8ZbjScWc86uf9BXNeiywriH5brAz1udM3 kvcA== X-Forwarded-Encrypted: i=1; AFNElJ9N5HlTBktsIuXJKTmIK0mIZa7dovGz9foJkox8Sqz8nCIOK/WmQxNVIxKw8ENgWltnMqXV+jpNBMBrDds=@vger.kernel.org X-Gm-Message-State: AOJu0Yw0AIVNbZIPMSJhkl/DjtFiiet6XZtxnaRjeoP9W8+XA4cJngqz fmzT/QKUmOsItpa0gc6T4ofVk9TNA/ffN3lhHQ248A5F9gKiGhxLGIFq3btGHMEiwSY= X-Gm-Gg: AfdE7cl1GtlATUBjwEJkljbLKzX3t2OyqOUh5M50tA1xkibjl9o30Ftvf9ExOYFqtIV 4y4Mm9Ny+AZCPeHjlyXKsbqqMRkEn0j4Fu8Mf1Kpr4CnzmnfAQKHhrNljwfGSd2ZWCImXWAn8FJ +fiFe98fgay4QcavwDtRkiusHzJm9wn0eASgzC90vjxwvYDARchsGWOjPctC2P5PiguIgIkUlRW yZr0kS5GRL2RHEW8A9MeQtq6riKT9mlz13iAqSt9Ld4+XCvQjU8vSEa+T3rTA+Re8HaPpbQxCqW qTtPqCye5OoMbOCRt7v2d2lxajX40Jpzc8pmXTdJwbsaq94jPHS99rohIONYR63EHZ+VG42pCYH EGY0mBUzE2erR6zwcbWDEcHav5IbzT0V+m7nWLIGG9dMRo8URdYv6s0eUQl2Na/5QS4ooIYXaLH UU39OjjhlnRNwv7J3B3D+bHbudQrLe X-Received: by 2002:a05:600c:e547:20b0:490:e19b:bd99 with SMTP id 5b1f17b1804b1-49260875a6emr44031825e9.30.1782316282320; Wed, 24 Jun 2026 08:51:22 -0700 (PDT) Received: from localhost.localdomain ([62.77.90.70]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49264011f6fsm1890835e9.2.2026.06.24.08.51.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jun 2026 08:51:21 -0700 (PDT) Date: Wed, 24 Jun 2026 17:51:20 +0200 From: Michal =?utf-8?Q?Koutn=C3=BD?= To: Waiman Long Cc: Chen Ridong , Tejun Heo , Johannes Weiner , Peter Zijlstra , cgroups@vger.kernel.org, linux-kernel@vger.kernel.org, Aaron Tomlin , Guopeng Zhang Subject: Re: [PATCH-next v5 0/6] cgroup/cpuset: Support multiple source/destination cpusets for cpuset_*attach() Message-ID: References: <20260602023203.248077-1-longman@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="u2eod7infvpbk4wg" Content-Disposition: inline In-Reply-To: <20260602023203.248077-1-longman@redhat.com> --u2eod7infvpbk4wg Content-Type: text/plain; protected-headers=v1; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Subject: Re: [PATCH-next v5 0/6] cgroup/cpuset: Support multiple source/destination cpusets for cpuset_*attach() MIME-Version: 1.0 On Mon, Jun 01, 2026 at 10:31:57PM -0400, Waiman Long = wrote: > Patch 6 makes the necessary changes to enable the support of multiple > source and destination cpusets by keeping all the source and destination > cpusets found during task iterations in two singly linked lists for > source and destination cpusets respectively. Thanks for looking into this! I've played with a coding assistant and produced the following selftest (it (expectedly) fails on my machine), feel free to include in the series (if it validates the fix). -- 8< -- =46rom ed4e6cf91413bb4b64befb1c15412c8cfd205d73 Mon Sep 17 00:00:00 2001 =46rom: =3D?UTF-8?q?Michal=3D20Koutn=3DC3=3DBD?=3D Date: Wed, 24 Jun 2026 16:39:30 +0200 Subject: [PATCH] selftests/cgroup: Add test for cpuset affinity on controll= er disable MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit Add a new selftest that exposes a bug in cpuset_attach() where thread CPU affinity is not properly updated when the cpuset controller is disabled in a threaded cgroup hierarchy. The test creates a threaded cgroup hierarchy with two child cgroups (A and B) having different cpuset.cpus constraints: - Parent: cpuset.cpus=3D0-1 - Child A: cpuset.cpus=3D0-1 - Child B: cpuset.cpus=3D1 (restricted to CPU 1 only) A multithreaded process is created with threads placed in different cgroups. When the cpuset controller is disabled on the parent, thread affinities should be updated to match the parent's cpuset. Expected behavior: - thread_a affinity: {0-1} before and after (unchanged) - thread_b affinity: {1} before, {0-1} after (expanded) Current buggy behavior: - thread_b affinity remains {1} after controller disable Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Michal Koutn=FD --- tools/testing/selftests/cgroup/test_cpuset.c | 243 +++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/tools/testing/selftests/cgroup/test_cpuset.c b/tools/testing/s= elftests/cgroup/test_cpuset.c index c5cf8b56ceb8f..1d72a199ca552 100644 --- a/tools/testing/selftests/cgroup/test_cpuset.c +++ b/tools/testing/selftests/cgroup/test_cpuset.c @@ -1,7 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 =20 +#define _GNU_SOURCE +#include #include +#include +#include #include +#include +#include =20 #include "kselftest.h" #include "cgroup_util.h" @@ -232,6 +238,242 @@ static int test_cpuset_perms_subtree(const char *root) return ret; } =20 +static int get_cpu_affinity(cpu_set_t *mask) +{ + CPU_ZERO(mask); + return sched_getaffinity(0, sizeof(*mask), mask); +} + +static int cpu_set_equal(cpu_set_t *dst, unsigned long mask) +{ + cpu_set_t expected; + + CPU_ZERO(&expected); + assert(sizeof(mask) < CPU_SETSIZE); + + for (int cpu =3D 0; cpu < sizeof(mask); ++cpu) + if ((1UL << cpu) & mask) + CPU_SET(cpu, &expected); +=09 + return CPU_EQUAL(&expected, dst); +} + +enum test_phase { + AFFINITY_SETUP, + AFFINITY_THREAD_A_READY, + AFFINITY_THREADS_READY, + AFFINITY_CONTROLLER_DISABLED, + AFFINITY_COMPLETE, + AFFINITY_ERROR +}; + +struct thread_args { + const char *cgroup; + cpu_set_t *affinity_before; + cpu_set_t *affinity_after; + enum test_phase ready_phase; +}; + +static pthread_mutex_t test_mutex =3D PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t test_cond =3D PTHREAD_COND_INITIALIZER; +static enum test_phase test_phase; + +static void *affinity_thread_fn(void *arg) +{ + struct thread_args *args =3D (struct thread_args *)arg; + + if (cg_enter_current_thread(args->cgroup)) + goto fail; + + if (get_cpu_affinity(args->affinity_before) !=3D 0) + goto fail; + + pthread_mutex_lock(&test_mutex); + if (test_phase < args->ready_phase) + test_phase =3D args->ready_phase; + pthread_cond_broadcast(&test_cond); + + while (test_phase < AFFINITY_CONTROLLER_DISABLED) + pthread_cond_wait(&test_cond, &test_mutex); + pthread_mutex_unlock(&test_mutex); + + if (get_cpu_affinity(args->affinity_after) !=3D 0) + goto fail; + + + return NULL; + +fail: + pthread_mutex_lock(&test_mutex); + test_phase =3D AFFINITY_ERROR; + pthread_cond_broadcast(&test_cond); + pthread_mutex_unlock(&test_mutex); + return NULL; +} + +/* + * Test that disabling cpuset controller properly updates thread affinity. + * + * This test exposes a bug in cpuset_attach() where threads in child cgrou= ps + * don't get their affinity updated when the cpuset controller is disabled. + * + * Setup: + * - Create parent cgroup with cpuset.cpus=3D0-1 + * - Create child A with cpuset.cpus=3D0-1 + * - Create child B with cpuset.cpus=3D1 + * - Place multithreaded process: group leader + thread_a in A, thread_b i= n B + * - Disable cpuset controller on parent + * + * Expected: thread_b's affinity should expand from {1} to {0-1} + * Buggy: thread_b's affinity remains {1} + */ +static int test_cpuset_affinity_on_controller_disable(const char *root) +{ + char *parent =3D NULL, *child_a =3D NULL, *child_b =3D NULL; + pthread_t thread_a, thread_b; + int thread_a_created =3D 0, thread_b_created =3D 0; + cpu_set_t affinity_a_before, affinity_a_after; + cpu_set_t affinity_b_before, affinity_b_after; + int ret =3D KSFT_FAIL; + + parent =3D cg_name(root, "cpuset_affinity_test"); + if (!parent) + goto cleanup; + if (cg_create(parent)) + goto cleanup; + if (cg_write(parent, "cgroup.type", "threaded")) + goto cleanup; + + child_a =3D cg_name(parent, "A"); + if (!child_a) + goto cleanup; + if (cg_create(child_a)) + goto cleanup; + if (cg_write(child_a, "cgroup.type", "threaded")) + goto cleanup; + + child_b =3D cg_name(parent, "B"); + if (!child_b) + goto cleanup; + if (cg_create(child_b)) + goto cleanup; + if (cg_write(child_b, "cgroup.type", "threaded")) + goto cleanup; + + /* Now enable cpuset controller in parent */ + if (cg_write(parent, "cgroup.subtree_control", "+cpuset")) { + ret =3D KSFT_SKIP; + goto cleanup; + } + + /* Set CPU affinity constraints */ + if (cg_write(parent, "cpuset.cpus", "0-1")) + goto cleanup; + if (cg_write(child_a, "cpuset.cpus", "0-1")) + goto cleanup; + if (cg_write(child_b, "cpuset.cpus", "1")) + goto cleanup; + + /* Move group leader (main thread) to child A */ + if (cg_enter_current(child_a)) + goto cleanup; + + /* Create threads - they will move themselves to their respective cgroups= */ + test_phase =3D AFFINITY_SETUP; + + struct thread_args args_a =3D { + .cgroup =3D child_a, + .affinity_before =3D &affinity_a_before, + .affinity_after =3D &affinity_a_after, + .ready_phase =3D AFFINITY_THREAD_A_READY, + }; + if (pthread_create(&thread_a, NULL, affinity_thread_fn, &args_a)) + goto cleanup; + thread_a_created =3D 1; + + struct thread_args args_b =3D { + .cgroup =3D child_b, + .affinity_before =3D &affinity_b_before, + .affinity_after =3D &affinity_b_after, + .ready_phase =3D AFFINITY_THREADS_READY, + }; + if (pthread_create(&thread_b, NULL, affinity_thread_fn, &args_b)) + goto cleanup_threads; + thread_b_created =3D 1; + + pthread_mutex_lock(&test_mutex); + while (test_phase < AFFINITY_THREADS_READY) + pthread_cond_wait(&test_cond, &test_mutex); + + /* If a thread failed during setup, bail out */ + if (test_phase =3D=3D AFFINITY_ERROR) { + pthread_mutex_unlock(&test_mutex); + goto cleanup_threads; + } + pthread_mutex_unlock(&test_mutex); + + if (!cpu_set_equal(&affinity_a_before, 0x3)) { + ksft_print_msg("FAIL: thread_a initial affinity incorrect\n"); + goto cleanup_threads; + } + + if (!cpu_set_equal(&affinity_b_before, 0x2)) { + ksft_print_msg("FAIL: thread_b initial affinity incorrect\n"); + goto cleanup_threads; + } + + /* Disable cpuset controller - this should trigger affinity update */ + if (cg_write(parent, "cgroup.subtree_control", "-cpuset")) + goto cleanup_threads; + + /* Signal threads to save their final affinity and exit */ + pthread_mutex_lock(&test_mutex); + test_phase =3D AFFINITY_CONTROLLER_DISABLED; + pthread_cond_broadcast(&test_cond); + pthread_mutex_unlock(&test_mutex); + + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + + /* Verify thread affinities AFTER disabling controller */ + if (!cpu_set_equal(&affinity_a_after, 0x3)) { + ksft_print_msg("FAIL: thread_a final affinity incorrect\n"); + goto cleanup; + } + + if (!cpu_set_equal(&affinity_b_after, 0x3)) { + ksft_print_msg("FAIL: thread_b affinity did not expand to {0-1}\n"); + goto cleanup; + } + + ret =3D KSFT_PASS; + goto cleanup; + +cleanup_threads: + pthread_mutex_lock(&test_mutex); + test_phase =3D AFFINITY_COMPLETE; + pthread_cond_broadcast(&test_cond); + pthread_mutex_unlock(&test_mutex); + + if (thread_a_created) + pthread_join(thread_a, NULL); + if (thread_b_created) + pthread_join(thread_b, NULL); + +cleanup: + /* Move back to root before cleanup */ + cg_enter_current(root); + + cg_destroy(child_b); + free(child_b); + cg_destroy(child_a); + free(child_a); + cg_destroy(parent); + free(parent); + + return ret; +} + =20 #define T(x) { x, #x } struct cpuset_test { @@ -241,6 +483,7 @@ struct cpuset_test { T(test_cpuset_perms_object_allow), T(test_cpuset_perms_object_deny), T(test_cpuset_perms_subtree), + T(test_cpuset_affinity_on_controller_disable), }; #undef T =20 --=20 2.54.0 --u2eod7infvpbk4wg Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iJEEABYKADkWIQRCE24Fn/AcRjnLivR+PQLnlNv4CAUCajv88BsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIACgkQfj0C55Tb+AjIfgD/RX8bciHelzyg++c57ZdH S1tSujKe4zgEddyO3XI03mQBAJGdmjU/EMaF0iq8OhaCD1gDbvlqILuYUxdEAUQn JGAA =964Z -----END PGP SIGNATURE----- --u2eod7infvpbk4wg--