From nobody Mon Apr 6 09:11:32 2026 Received: from mail-pf1-f180.google.com (mail-pf1-f180.google.com [209.85.210.180]) (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 D7A692BDC2A for ; Fri, 20 Mar 2026 05:24:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773984259; cv=none; b=CFE6UKsvgokWh6Kyl+NS5nLQtqQXMfFgKcBP9WmUE//FuChfIY/4kDe9h3IKF8eVS0B98NOVnXcNR4Ih74Ah4uOXbaCTb2WENDC7Lpxiv1w9zB1um3KJaIXQBkA35w7HwkHFVw38vSyoG2j3k2U0bFxWdY13nAmFYP7uX2nIGpo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773984259; c=relaxed/simple; bh=B6D09KPg8bU807ng53r6CYvmc+YI3/Mx3nGb3Q0iQyE=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=tn3S4P02CJ76T7kGVkecobeKPRi2tvLKIyox8dP4clsRXigMIE8RSiin+k++nbd2NLRL+xucHhrdf8RwoAVfwnFpEtP8CYixJksJzbg3Ff1VcsJ2N99Jqp/QVKSKmDuDU1APvdO6NOvAP3bPnehgFaBR6XrL6/3TWVwGTg5m83s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CxGAM+hI; arc=none smtp.client-ip=209.85.210.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CxGAM+hI" Received: by mail-pf1-f180.google.com with SMTP id d2e1a72fcca58-82a655cfab5so277978b3a.1 for ; Thu, 19 Mar 2026 22:24:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773984257; x=1774589057; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=wLeUwMCfEp8eEcDYTzjt0sXlpqSHQtTVQ6yFHJWh/lI=; b=CxGAM+hIqZEwxgs0QyiWDqX85DRauGn4tUUFveUIWWbOTh6lnR8CK7WLQLlMg1DVrJ HCQ8rPJb9n6stvumzsGHYM4pdF6Qqx8xBHtzwAVvMdd61HjCgi6BucpsWeHSMArYInXj 9iQj6KBZVOFwrR/ORJELc+5z8sx5M362YhuKEG2lwTLoo7S6U9iwICXx0TSv/qf/tJM/ y64Cl/7hHZ36pCjqIwEf3O5a/C/1V0+arVM550+SJXe1kfWPQTclbF0hcJSHmIzIm6Wq GNRnht1UPoOuHcgzoshrh5ZU4p16n0TcszvR4JoF9iTpBr4WMO6mGRIggNYUp3v1x1qu q9rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773984257; x=1774589057; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=wLeUwMCfEp8eEcDYTzjt0sXlpqSHQtTVQ6yFHJWh/lI=; b=kam4DyZsg/+JWxTlPPoJV6htWdHIMSJjcxGN0Y5YHIydScQ1VmyYN8btBdWPhlF7U+ sWTut9f1I88Y3arZMdNrHXGT+OskoYmQ1Vf/APH38YPkSBecVpI7P7uXXmDJaTv3Kzj5 9bgjIYaiMv799ynOfjF9MoiD/Ixe/r5Qqfq7bXqoQJi1GshK0WVel6SdWBS9xQY7Nh3q T1nhqCorZvVmQGgs1PHn+XLtdQqnkGSijRfyWJD6kDaPNnDmYFji3WuThsVXxcKbuGVB 4bLreRf+wxn3khZE6g+/0BZ+565rrlPw198RcqRYYgguKqUtuQGQ+u5HHXY6WAK2ZqlY VUEw== X-Forwarded-Encrypted: i=1; AJvYcCWevOa7Mtx1fl/peOAHPg0NniqS1hOLQmU4jGnDydtpam/xFBsN4bdO5Nbf4H6QV/76b1HVRkEChWDvLH8=@vger.kernel.org X-Gm-Message-State: AOJu0YzWzygX9I/5imqZ/fUAV0J4+SfiLljRS/l3dwEfChADPrOBnu74 2MdEc76ukTvDaTVq68EtEuK1vv1qUUYO16CK4TXk5NUOUEvrIFa2AA/hZzHMAw== X-Gm-Gg: ATEYQzyy7Lv4h43JhKXIFZNybLx32sF69huTuB86/vH3jf1tZt5Sc14fN+QKz7Pt7nj QjfD13F0LO8utrFRpQbRMN90cXf/FRklcZ6Mq82o5FuDw7PfZc+GU2XWumYm4/r9DgcntaUtt8c ZqRO9wEmpl3ZV9e8CVKRYhaw291wsyqAg0ELkWTpZ/7Kfp7LphoqJ9bChKS45B5Hyz7c9EsN/ly AhQaI8ppvAHR0lSM8DD8biYDYSUSoQagRiz0RoSGFcdYayeH/XrCtUW9EdWxVPYnqMmL0sIJc2F LXlD4lYN11pa6gJduPKEp1ivu2mOHs1qAXz1Vl2QcW+Jz2hLTykoCv1/Rz6fJzJLl9a5lyP+vWO pDEIgsK0ZooBM/iZIA4egPwXuL1IDe4RsJY8YizF7MQqOsiAPOK7DPlWWPnftvqQmjFFQW0ZL0j TN9eGhLpyJqcUtEKL3Vim1F+T2iTLWTnM/gUDAwgiB2d0IuKGnBS67UIrrYVKp5g4SC6oHt4w= X-Received: by 2002:a05:6a00:328c:b0:81f:3fa0:8c38 with SMTP id d2e1a72fcca58-82a8c285868mr1577867b3a.20.1773984256943; Thu, 19 Mar 2026 22:24:16 -0700 (PDT) Received: from li-1a3e774c-28e4-11b2-a85c-acc9f2883e29.ibm.com ([49.207.193.112]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82b0409c11asm802316b3a.30.2026.03.19.22.24.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Mar 2026 22:24:16 -0700 (PDT) From: "Mukesh Kumar Chaurasiya (IBM)" To: maddy@linux.ibm.com, mpe@ellerman.id.au, npiggin@gmail.com, chleroy@kernel.org, mkchauras@gmail.com, linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org Subject: [PATCH] powerpc/xive: KUnit tests for xive interrupt controller Date: Fri, 20 Mar 2026 10:54:00 +0530 Message-ID: <20260320052400.3230999-1-mkchauras@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Kunit tests for xive interrupt controller. Signed-off-by: Mukesh Kumar Chaurasiya (IBM) --- arch/powerpc/sysdev/xive/.kunitconfig | 4 + arch/powerpc/sysdev/xive/Kconfig | 9 + arch/powerpc/sysdev/xive/Makefile | 7 +- arch/powerpc/sysdev/xive/xive-test.c | 294 ++++++++++++++++++++++++++ 4 files changed, 311 insertions(+), 3 deletions(-) create mode 100644 arch/powerpc/sysdev/xive/.kunitconfig create mode 100644 arch/powerpc/sysdev/xive/xive-test.c diff --git a/arch/powerpc/sysdev/xive/.kunitconfig b/arch/powerpc/sysdev/xi= ve/.kunitconfig new file mode 100644 index 000000000000..ea6634bb0718 --- /dev/null +++ b/arch/powerpc/sysdev/xive/.kunitconfig @@ -0,0 +1,4 @@ +CONFIG_KUNIT=3Dy +CONFIG_PPC_PSERIES=3Dy +CONFIG_PPC_XIVE_SPAPR=3Dy +CONFIG_PPC_XIVE_KUNIT_TEST=3Dy \ No newline at end of file diff --git a/arch/powerpc/sysdev/xive/Kconfig b/arch/powerpc/sysdev/xive/Kc= onfig index 785c292d104b..81727b9c22b7 100644 --- a/arch/powerpc/sysdev/xive/Kconfig +++ b/arch/powerpc/sysdev/xive/Kconfig @@ -12,3 +12,12 @@ config PPC_XIVE_NATIVE config PPC_XIVE_SPAPR bool select PPC_XIVE + +config PPC_XIVE_KUNIT_TEST + tristate "KUnit tests for XIVE interrupt controller" if !KUNIT_ALL_TESTS + depends on KUNIT && PPC_XIVE + default KUNIT_ALL_TESTS + help + This builds unit tests for the XIVE interrupt controller. + + If unsure, say N. diff --git a/arch/powerpc/sysdev/xive/Makefile b/arch/powerpc/sysdev/xive/M= akefile index e5108883894a..e1f9f513af09 100644 --- a/arch/powerpc/sysdev/xive/Makefile +++ b/arch/powerpc/sysdev/xive/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only =20 -obj-y +=3D common.o -obj-$(CONFIG_PPC_XIVE_NATIVE) +=3D native.o -obj-$(CONFIG_PPC_XIVE_SPAPR) +=3D spapr.o +obj-y +=3D common.o +obj-$(CONFIG_PPC_XIVE_NATIVE) +=3D native.o +obj-$(CONFIG_PPC_XIVE_SPAPR) +=3D spapr.o +obj-$(CONFIG_PPC_XIVE_KUNIT_TEST) +=3D xive-test.o diff --git a/arch/powerpc/sysdev/xive/xive-test.c b/arch/powerpc/sysdev/xiv= e/xive-test.c new file mode 100644 index 000000000000..ee08f18af864 --- /dev/null +++ b/arch/powerpc/sysdev/xive/xive-test.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * KUnit tests for XIVE interrupt controller + * + * Copyright 2026 IBM Corporation. + */ + +#include +#include +#include +#include +#include + +/* + * Mock xive_try_pick_target for testing + * The real function checks queue capacity, we simplify for testing + */ +static bool xive_try_pick_target(int cpu) +{ + /* For testing, accept any online CPU */ + return cpu_online(cpu); +} + +/* + * Copy of xive_find_target_in_mask from common.c for testing + * This allows us to test the static function without modifying source + */ +static int xive_find_target_in_mask(const struct cpumask *mask, + unsigned int fuzz) +{ + int cpu, first; + + /* Pick up a starting point CPU in the mask based on fuzz */ + fuzz %=3D cpumask_weight(mask); + first =3D cpumask_nth(fuzz, mask); + WARN_ON(first >=3D nr_cpu_ids); + + /* + * Now go through the entire mask until we find a valid + * target. + */ + for_each_cpu_wrap(cpu, mask, first) { + if (cpu_online(cpu) && xive_try_pick_target(cpu)) + return cpu; + } + + return -1; +} + +/* + * Test: Empty CPU mask + * Expected: Should return -1 when the mask contains no CPUs + */ +static void xive_test_find_target_empty_mask(struct kunit *test) +{ + struct cpumask empty_mask; + int result; + + cpumask_clear(&empty_mask); + + result =3D xive_find_target_in_mask(&empty_mask, 0); + + KUNIT_EXPECT_EQ(test, result, -1); +} + +/* + * Test: Single CPU in mask + * Expected: Should return that CPU if it's online + */ +static void xive_test_find_target_single_cpu(struct kunit *test) +{ + struct cpumask single_mask; + int cpu =3D 0; + int result; + + /* Skip test if CPU 0 is not online */ + if (!cpu_online(0)) + kunit_skip(test, "CPU 0 is not online"); + + cpumask_clear(&single_mask); + cpumask_set_cpu(cpu, &single_mask); + + result =3D xive_find_target_in_mask(&single_mask, 0); + + KUNIT_EXPECT_EQ(test, result, cpu); +} + +/* + * Test: Multiple CPUs in mask with fuzz=3D0 + * Expected: Should return a valid CPU from the mask + */ +static void xive_test_find_target_multiple_cpus(struct kunit *test) +{ + struct cpumask multi_mask; + int result; + int cpu; + int count =3D 0; + + cpumask_clear(&multi_mask); + + /* Add first 4 online CPUs to the mask */ + for_each_online_cpu(cpu) { + cpumask_set_cpu(cpu, &multi_mask); + count++; + if (count >=3D 4) + break; + } + + if (count =3D=3D 0) + kunit_skip(test, "No online CPUs available"); + + result =3D xive_find_target_in_mask(&multi_mask, 0); + + /* Result should be a valid CPU in the mask */ + KUNIT_EXPECT_NE(test, result, -1); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result, &multi_mask)); + KUNIT_EXPECT_TRUE(test, cpu_online(result)); +} + +/* + * Test: Fuzz parameter affects starting point + * Expected: Different fuzz values may select different CPUs + */ +static void xive_test_find_target_fuzz_variation(struct kunit *test) +{ + struct cpumask multi_mask; + int result1, result2; + int cpu; + int count =3D 0; + + cpumask_clear(&multi_mask); + + /* Add multiple online CPUs to the mask */ + for_each_online_cpu(cpu) { + cpumask_set_cpu(cpu, &multi_mask); + count++; + if (count >=3D 4) + break; + } + + if (count < 2) + kunit_skip(test, "Need at least 2 online CPUs for this test"); + + result1 =3D xive_find_target_in_mask(&multi_mask, 0); + result2 =3D xive_find_target_in_mask(&multi_mask, 1); + + /* Both results should be valid CPUs in the mask */ + KUNIT_EXPECT_NE(test, result1, -1); + KUNIT_EXPECT_NE(test, result2, -1); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result1, &multi_mask)); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result2, &multi_mask)); +} + +/* + * Test: Large fuzz value (modulo behavior) + * Expected: Should handle fuzz values larger than mask weight correctly + */ +static void xive_test_find_target_large_fuzz(struct kunit *test) +{ + struct cpumask multi_mask; + int result; + int cpu; + int count =3D 0; + unsigned int large_fuzz =3D 1000; + + cpumask_clear(&multi_mask); + + /* Add online CPUs to the mask */ + for_each_online_cpu(cpu) { + cpumask_set_cpu(cpu, &multi_mask); + count++; + if (count >=3D 3) + break; + } + + if (count =3D=3D 0) + kunit_skip(test, "No online CPUs available"); + + result =3D xive_find_target_in_mask(&multi_mask, large_fuzz); + + /* Result should be a valid CPU in the mask */ + KUNIT_EXPECT_NE(test, result, -1); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result, &multi_mask)); + KUNIT_EXPECT_TRUE(test, cpu_online(result)); +} + +/* + * Test: Wrap-around behavior at mask boundary + * Expected: Should correctly wrap around when starting near the end + */ +static void xive_test_find_target_wrap_around(struct kunit *test) +{ + struct cpumask wrap_mask; + int result; + int cpu; + int count =3D 0; + unsigned int weight; + + cpumask_clear(&wrap_mask); + + /* Add online CPUs to the mask */ + for_each_online_cpu(cpu) { + cpumask_set_cpu(cpu, &wrap_mask); + count++; + if (count >=3D 4) + break; + } + + if (count < 2) + kunit_skip(test, "Need at least 2 online CPUs for this test"); + + weight =3D cpumask_weight(&wrap_mask); + + /* Test with fuzz at the boundary */ + result =3D xive_find_target_in_mask(&wrap_mask, weight - 1); + + KUNIT_EXPECT_NE(test, result, -1); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result, &wrap_mask)); + KUNIT_EXPECT_TRUE(test, cpu_online(result)); +} + +/* + * Test: Using cpu_online_mask + * Expected: Should handle the system's online CPU mask correctly + */ +static void xive_test_find_target_online_mask(struct kunit *test) +{ + int result; + + if (cpumask_empty(cpu_online_mask)) + kunit_skip(test, "No online CPUs in system"); + + result =3D xive_find_target_in_mask(cpu_online_mask, 0); + + KUNIT_EXPECT_NE(test, result, -1); + KUNIT_EXPECT_TRUE(test, cpu_online(result)); +} + +/* + * Test: Fuzz value equal to mask weight + * Expected: Should wrap to first CPU (fuzz % weight =3D=3D 0) + */ +static void xive_test_find_target_fuzz_equals_weight(struct kunit *test) +{ + struct cpumask test_mask; + int result; + int cpu; + int count =3D 0; + unsigned int weight; + + cpumask_clear(&test_mask); + + /* Add online CPUs to the mask */ + for_each_online_cpu(cpu) { + cpumask_set_cpu(cpu, &test_mask); + count++; + if (count >=3D 3) + break; + } + + if (count =3D=3D 0) + kunit_skip(test, "No online CPUs available"); + + weight =3D cpumask_weight(&test_mask); + + /* Fuzz equal to weight should wrap to start */ + result =3D xive_find_target_in_mask(&test_mask, weight); + + KUNIT_EXPECT_NE(test, result, -1); + KUNIT_EXPECT_TRUE(test, cpumask_test_cpu(result, &test_mask)); +} + +static struct kunit_case xive_test_cases[] =3D { + KUNIT_CASE(xive_test_find_target_empty_mask), + KUNIT_CASE(xive_test_find_target_single_cpu), + KUNIT_CASE(xive_test_find_target_multiple_cpus), + KUNIT_CASE(xive_test_find_target_fuzz_variation), + KUNIT_CASE(xive_test_find_target_large_fuzz), + KUNIT_CASE(xive_test_find_target_wrap_around), + KUNIT_CASE(xive_test_find_target_online_mask), + KUNIT_CASE(xive_test_find_target_fuzz_equals_weight), + {} +}; + +static struct kunit_suite xive_test_suite =3D { + .name =3D "xive_find_target_in_mask", + .test_cases =3D xive_test_cases, +}; + +kunit_test_suites(&xive_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for XIVE interrupt controller"); +MODULE_AUTHOR("Mukesh Kumar Chaurasiya (IBM) "); --=20 2.53.0