From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A82ED265623 for ; Fri, 10 Jul 2026 13:51:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783691520; cv=none; b=UNxvDB1d9aHty+5AYblkrmCFfyr/MJ7O8hxA3fMrHkaEKgZY0yPFGR/Q00bEIrq6HnLAE6E7BwzGL5hxp3EraWf2Gkyy2kVyWJUawLRaMw7XhQFtjSpnWOmDJ6kRrGtKdTVEBA8AH/P9CAGDMEiaKG4NAnM0EPTsC22nG4TEc9s= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783691520; c=relaxed/simple; bh=mUI2lurZX8MTNaE/xcDA/AsULsylDaS4tgl3GGNcEgI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=koDZW7LgS8jp0ez7yOiTxwvBxIiDVfnOcl1YsydtPtpfwYcrgZFQL+UXgRIe/eyNbAsH4QakFWEiOTilzyW4WUjuP/KiHCMJrEixxxJVT2bh9ga6ckJFt6/K3E3ktnOE2iv1DBv9tQ6iz/3D91ssA/rc5G5hR69G1Ww+Dv+xtMA= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=liiwYQAm; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=VVhIvgNG; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="liiwYQAm"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="VVhIvgNG" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66AD6xPh731379 for ; Fri, 10 Jul 2026 13:51:56 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 02SiYWyXnbqCdoLpis1OtyNs+Gni3uKHhVvKdNPCT5g=; b=liiwYQAmw5YJSXp2 KX6yn2xpJTsRLenRYPWIQcIGfNPf9rR+mXC+p5w0K5aUyYU10NSOPftblXNgv7Ix 9WxmRqHc7WarG3G3wcf4pqoK01BLgwQpUTJTGT8iwuaaR2/jdf/kkJGH3irf95z8 5d5Y27NsDBePAUqC4zimY64h67YlDvnVrTKHz7uAQotgKM5d+ntIxRLzdicxG7D/ NUpmZpYKfoT8/Uh7XXt5a3r1HEfA8+d+jy/BVI6ZoBVLoK5dUirf+p0B+trOZ9n8 eApkHbYUjmOFzHacUaKud7Ta5pQjaT2PdoaXeImiGfgw3X95N1v1C2q5DBlgPOWx A7Xv7A== Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4faf24vguk-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 10 Jul 2026 13:51:56 +0000 (GMT) Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-51c26012cd0so10438161cf.0 for ; Fri, 10 Jul 2026 06:51:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1783691516; x=1784296316; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=02SiYWyXnbqCdoLpis1OtyNs+Gni3uKHhVvKdNPCT5g=; b=VVhIvgNG+W9SwTdSJ50dj6g9aquZaOv2tiyvxOpgeMYeDmYkRsnuGy/EoUV+aOWE4I LI5HXsYjpjUBr8VdgFRtD6B7tC9R6CwYMfbbeR6h5MLOolp8jIoqsTEgkeUZ/NxC+z4q L9Gla5GFQ2j/ZVlgT9jzhZy0IM/uO2u3m5S4Cr6TOknfwIQgI1bB0glvqUFvn7j9vDIH xIJdAWmRSi7AAUqctr9KbV9QQsJgWf5SN/7cqKWal1/QYIYCCTRvwc01eRjHKSe5ULHu 7VQL3sy5vZxEh8IBrKb1MeVYVcMrUn4M1B00bTXVeUpUEIR+QXLTSxrRmHC4ARxXI/h4 9VLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783691516; x=1784296316; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=02SiYWyXnbqCdoLpis1OtyNs+Gni3uKHhVvKdNPCT5g=; b=svZFnCXizdIJf3N5Vzh3tlSmvEi5O620+5T53/8TBQ4kAjQqYhElSH32m+Qt06IrWn D3Y5K6VPcqZqB7XpAr1DLzu5CoO/E08XWtOaSJ4HkAXAk2zVFQNA4qu06IB1uQXRWnRK Uk5kVV9ag4aNkGuuIHhot5d/rx6ONPe1FTj+naWIkwtOM8+y24L8Z/OCK8Q2cDQj/APX C4QqpbmdVabFKJ4jfNZ223KrJU3DYJdjcclut+iHs78GGoja6OhzHjBpk43dFuSDiyWH NSw/ZE9KC+0+kkdDLWZBnXE4VXXr06x3lH/T3ayn4V1erFfCCswB8JCwc4oghIuFyzzY jkVg== X-Forwarded-Encrypted: i=1; AHgh+RpRMlPIUvxL/RPS7TENOWahDifdr2Z57mFrlv93NSrGltWALcgELuxeWlmg2i3GNoj07zOrRQWJFECr@vger.kernel.org X-Gm-Message-State: AOJu0Yw4qTYtBT1clhuDvEy1ak/VGiHorYL0jwuihH4RfCv8aZVtCUCc dO0AqQ4+pDTmfXS8RgZV+B6t1e/MUfqKeY8vRwR79yFD6Nm5d5VM8LzJRhfi2M0P6ZcSd5e+UsZ 93j1nb+1m7jpw7iXqcQntnpeUGGpCcGkFRUPfvNFz0ZmHVX1F2h8422KwfsaSMcez X-Gm-Gg: AfdE7clCbM5eTksiDCRt6deM6EAzLKk1Vyr5fj7rwjml04ueVDkJbgIIR/FLuuqWBiy n98LsafpG3/rXBDzOk9EWfeu3myQ0EOoKk+PBjsNlxo4pjF7zr8Ei6aXXDi0RPN8x6okIlnvY9i OtEAl9zllsQISC32CueI3GeB//0hmZCw8OfDKhPYVvo58KW+GjbXhXgLVo7uuUwzTTB6cjQiTT8 f19D8rKMBbklbCutA9ZkIiCnpfCtu20hRdKe9ZYRw5s13k/hkoAwjkyeABgfFER/pNJrsvIwMt9 miGpB9dZmHgp8qFojsyZzsFgR0RQdXPSfFXtbTrvrKTtA8WCHgefk2cJaAsSZSGBqZYgBuuXHT0 vADIUjLDgPF9lHIQs6eOk23WkfwHvLnUlaHiPbSRs X-Received: by 2002:a05:622a:1342:b0:51c:f3:34de with SMTP id d75a77b69052e-51c8b4307abmr117053151cf.20.1783691515893; Fri, 10 Jul 2026 06:51:55 -0700 (PDT) X-Received: by 2002:a05:622a:1342:b0:51c:f3:34de with SMTP id d75a77b69052e-51c8b4307abmr117052571cf.20.1783691515316; Fri, 10 Jul 2026 06:51:55 -0700 (PDT) Received: from brgl-qcom.local ([2a01:cb1d:dc:7e00:adb6:d740:6427:4097]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-493eb670a41sm135669295e9.0.2026.07.10.06.51.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Jul 2026 06:51:53 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 10 Jul 2026 15:51:22 +0200 Subject: [PATCH v3 3/5] software node: add kunit tests for fw_devlink support Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260710-swnode-fw-devlink-v3-3-993f31874e40@oss.qualcomm.com> References: <20260710-swnode-fw-devlink-v3-0-993f31874e40@oss.qualcomm.com> In-Reply-To: <20260710-swnode-fw-devlink-v3-0-993f31874e40@oss.qualcomm.com> To: Brendan Higgins , David Gow , Rae Moar , Andy Shevchenko , Daniel Scally , Heikki Krogerus , Sakari Ailus , Bartosz Golaszewski , Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Linus Walleij , Dmitry Torokhov Cc: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-acpi@vger.kernel.org, driver-core@lists.linux.dev, linux-gpio@vger.kernel.org, Bartosz Golaszewski X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=13711; i=bartosz.golaszewski@oss.qualcomm.com; h=from:subject:message-id; bh=mUI2lurZX8MTNaE/xcDA/AsULsylDaS4tgl3GGNcEgI=; b=owEBbQKS/ZANAwAKAQWdLsv/NoTDAcsmYgBqUPjsqX1crAXT/cEB/5LnnOYhspDaxDHDB+n93 LBODAUFxluJAjMEAAEKAB0WIQSR5RMt5bVGHXuiZfwFnS7L/zaEwwUCalD47AAKCRAFnS7L/zaE wzpjD/wKH2Disj3iDjy7kho9Is20f1CNM36OXuHms48OicTzK979qjSEcOUqSG1qw9sCPLm/2sr zz5uYoaKsB9kFJij+DoOue7oSA1cmuQiUyWakXUoQGSC4G/VeIaRYMUGRSiqsuFJyYOZxUcWja1 GIxJ3QVG/Y9AudJ5QkE6OUkXlfyb4icDmun4DGk1KjRYisnLYyforFF9Glkvno+HuZ8dlIOH5DK pj8IKDDRVq58/IbRK0xMahJw2hrvNDZerwSUb3/kxH6ojCM8tmIbWUx2juWk/nliq8uNHZo+lFW 0uuHtgIcN4u69Ar9Mxo+uVxH25TThN55WlMGzfafzOf94teQdYsHJQRiZLsN3SKtIGEFtssjTXs IxdIOps0neHxFZfCngYXyhruwoIHCODjxwp0BwnimPLPZ5NZNZagRmVWLMMfO+AmUcul046PtPk ANl3LN2K1/2ol11rnm7AXbIyhSovrbV7n1t/Q5kVrs95RgL0zMk5NJMy2zGidlAcwG0XwbIvXTR GcvoP1kgB63WLpcR76DVjv3KheTk0MyiwXqo8p5gH9JmqEFXOS2JC8dMdtlAWTF/QhTUDGfxMt4 scc5u8DXfaj2HpsIBeUjXpMahkATsGUtOsXhChTkHUJVVBzSDvuVZsy8gWUdkNVBFpK0rvvxoeE BCBjiZ9PYrlLDXQ== X-Developer-Key: i=bartosz.golaszewski@oss.qualcomm.com; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 X-Proofpoint-GUID: Wfpwn4C6yDfH0TELP7NYHuAXp_xBN4cF X-Proofpoint-ORIG-GUID: Wfpwn4C6yDfH0TELP7NYHuAXp_xBN4cF X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDEzNiBTYWx0ZWRfX5cEAMJLgzuWI 2Mm4seF1Va49cADA3NdN+f46SjS+g6M6aNjJi6gZcn4tZF4o82Qy4reR7nTIBgZkR4t7FQ9j33L 9GDSd9zfX29qX9nEE0c0tN/psX7haGQ= X-Authority-Analysis: v=2.4 cv=daKwG3Xe c=1 sm=1 tr=0 ts=6a50f8fc cx=c_pps a=WeENfcodrlLV9YRTxbY/uA==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=QyXUC8HyAAAA:8 a=EUspDBNiAAAA:8 a=VwQbUJbxAAAA:8 a=9zVEpjMGfH8rPmTIezIA:9 a=QEXdDO2ut3YA:10 a=kacYvNCVWA4VmyqE58fU:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDEzNiBTYWx0ZWRfX1UauyE49m2ov Yb8uAp69g9V+GVp8LWsokmABvoAm6NxFqPM9poIcIDYhB+XfHj/QRCMKIscxsf7vQGx5B4qTimJ kUJqZ6ceoQtoeJgbVGZfEbAPhdu8MdAxoNGKUYa1tP1nqYw6bejciBYeUC6Sw/D5f3ArcAX8tRV 9SzYWBPDaoJm6KlF99LRaB5a84Dp4khN8wnI8x85AoslJGPZFxUiIM+VpDldTeE5crAkzIU6rJl fppSnX6O1pGNuQt0RyqWhmOv0p+xGMIvVMzJd+TvgmE0apne9rveCoruY+mxGMCOYgjPx8oTmKF UrZxWjBRKPCY2uMQfn5fJu51rJuX8p1GXkERNdpso39raRPS7VyADjAmALZS2oH2tiqGrNu1I77 Vr8Ei9bc/fbD+boMqByr1LKXi9CLwlw0GYDR3mvbvdQAOb+VKNbHhoWdewzxoEt5R34nigq73C+ ttfrkbur5doGu4Wx1Pg== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-10_03,2026-07-10_01,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 phishscore=0 adultscore=0 suspectscore=0 lowpriorityscore=0 clxscore=1015 malwarescore=0 priorityscore=1501 bulkscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607100136 Add a kunit test suite for fw_devlink support for software nodes. Most cases call add_links() directly and inspect the resulting fwnode supplier/consumer lists: a single reference, multiple references, a reference to an unregistered node, a "remote-endpoint" reference and a reference array. The last case is end-to-end - it registers real consumer and supplier platform devices together with their drivers, adds the consumer first and checks that fw_devlink defers its probe until the supplier has been bound. Acked-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- MAINTAINERS | 1 + drivers/base/test/Kconfig | 5 + drivers/base/test/Makefile | 2 + drivers/base/test/swnode-devlink-test.c | 339 ++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce2447c3e89784b142d998f7384b95..d7741614c941ceed9e46e9caea72bad77cfb7618 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25105,6 +25105,7 @@ L: linux-acpi@vger.kernel.org S: Maintained F: drivers/base/property.c F: drivers/base/swnode.c +F: drivers/base/test/swnode-devlink-test.c F: include/linux/fwnode.h F: include/linux/property.h diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 2756870615ccab67ec26d8671c1e4dba69342134..1ecf0791241a1b2eee7e1e787772217724abacb9 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -18,3 +18,8 @@ config DRIVER_PE_KUNIT_TEST tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT default KUNIT_ALL_TESTS + +config DRIVER_SWNODE_KUNIT_TEST + tristate "KUnit Tests for software node fw_devlink links" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index e321dfc7e92266d2073d442f652cadb6e911dba5..9ced7bbd569fc49ba2719aa0cab57c7d8245dde1 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN) + +obj-$(CONFIG_DRIVER_SWNODE_KUNIT_TEST) += swnode-devlink-test.o diff --git a/drivers/base/test/swnode-devlink-test.c b/drivers/base/test/swnode-devlink-test.c new file mode 100644 index 0000000000000000000000000000000000000000..6f59f13214fcf39cebe02244bd0029470d3d104a --- /dev/null +++ b/drivers/base/test/swnode-devlink-test.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) Qualcomm Technologies, Inc. and/or its subsidiaries + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int swnode_count_suppliers(struct fwnode_handle *fwnode) +{ + struct fwnode_link *link; + unsigned int count = 0; + + /* + * The suppliers and consumers lists should typically only be accessed + * with the fwnode_link_lock taken but it's private to the driver core. + * + * These are tests and at this point nobody should be modifying them so + * let's just access the list. + */ + list_for_each_entry(link, &fwnode->suppliers, c_hook) + count++; + + return count; +} + +/* True if a supplier link con->sup exists, checked from both list ends. */ +static bool swnode_has_link(struct fwnode_handle *consumer, + struct fwnode_handle *supplier) +{ + bool from_con = false, from_sup = false; + struct fwnode_link *link; + + list_for_each_entry(link, &consumer->suppliers, c_hook) { + if (link->supplier == supplier && link->consumer == consumer) + from_con = true; + } + + list_for_each_entry(link, &supplier->consumers, s_hook) { + if (link->supplier == supplier && link->consumer == consumer) + from_sup = true; + } + + return from_con && from_sup; +} + +/* A single reference creates exactly one supplier link, on both list ends. */ +static void swnode_devlink_test_single_ref(struct kunit *test) +{ + static const struct software_node supp_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + struct fwnode_handle *cons_fwnode, *supp_fwnode; + int ret; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("supplier", &supp_swnode), + { } + }; + + supp_fwnode = kunit_software_node_register(test, &supp_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode); + + cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode); + + ret = fwnode_call_int_op(cons_fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 1); + KUNIT_EXPECT_TRUE(test, swnode_has_link(cons_fwnode, supp_fwnode)); +} + +/* Multiple distinct references create multiple supplier links. */ +static void swnode_devlink_test_multiple_refs(struct kunit *test) +{ + static const struct software_node supp1_swnode = { + .name = "swnode-devlink-test-supplier-1", + }; + static const struct software_node supp2_swnode = { + .name = "swnode-devlink-test-supplier-2", + }; + static const struct software_node *supp_nodes[] = { + &supp1_swnode, &supp2_swnode, NULL + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("foo", &supp1_swnode), + PROPERTY_ENTRY_REF("bar", &supp2_swnode), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + ret = kunit_software_node_register_node_group(test, supp_nodes); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode))); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode))); +} + +/* A reference to an unregistered node creates no link (graceful skip). */ +static void swnode_devlink_test_unregistered_ref(struct kunit *test) +{ + static const struct software_node supp_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("supplier", &supp_swnode), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 0); +} + +/* Graph "remote-endpoint" references are excluded. */ +static void swnode_devlink_test_remote_endpoint_excluded(struct kunit *test) +{ + static const struct software_node ep_swnode = { + .name = "swnode-devlink-test-end-point" + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF("remote-endpoint", &ep_swnode), + { } + }; + + struct fwnode_handle *cons_fwnode, *supp_fwnode; + int ret; + + supp_fwnode = kunit_software_node_register(test, &ep_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supp_fwnode); + + cons_fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cons_fwnode); + + ret = fwnode_call_int_op(cons_fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(cons_fwnode), 0); +} + +/* A reference array creates one link per registered element. */ +static void swnode_devlink_test_ref_array(struct kunit *test) +{ + static const struct software_node supp1_swnode = { + .name = "swnode-devlink-test-supplier-1", + }; + static const struct software_node supp2_swnode = { + .name = "swnode-devlink-test-supplier-2", + }; + static const struct software_node *supp_nodes[] = { + &supp1_swnode, &supp2_swnode, NULL + }; + static const struct software_node_ref_args refs[] = { + SOFTWARE_NODE_REFERENCE(&supp1_swnode), + SOFTWARE_NODE_REFERENCE(&supp2_swnode, 4, 2), + }; + + const struct property_entry props[] = { + PROPERTY_ENTRY_REF_ARRAY("suppliers", refs), + { } + }; + + struct fwnode_handle *fwnode; + int ret; + + ret = kunit_software_node_register_node_group(test, supp_nodes); + KUNIT_ASSERT_EQ(test, ret, 0); + + fwnode = kunit_fwnode_create_software_node(test, props, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = fwnode_call_int_op(fwnode, add_links); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, swnode_count_suppliers(fwnode), 2); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp1_swnode))); + KUNIT_EXPECT_TRUE(test, swnode_has_link(fwnode, software_node_fwnode(&supp2_swnode))); +} + +/* + * End-to-end test: fw_devlink must defer a consumer's probe until its + * supplier has probed. + * + * The reference created by software_node_add_links() is only useful if the + * driver core promotes it to a real device_link and uses it to order probing. + * This test drives actual probing through the platform bus and asserts the + * supplier binds before the consumer. + */ + +#define SWNODE_DEVLINK_TEST_SUPPLIER "swnode-link-supplier" +#define SWNODE_DEVLINK_TEST_CONSUMER "swnode-link-consumer" +#define SWNODE_DEVLINK_TEST_TIMEOUT_MS (2 * MSEC_PER_SEC) + +struct swnode_test_probe_order { + /* Names in the order their drivers' .probe ran. */ + const char *probed[2]; + unsigned int count; + wait_queue_head_t wq; +}; + +static int swnode_test_record_probe(struct platform_device *pdev) +{ + struct swnode_test_probe_order *order = platform_get_drvdata(pdev); + + if (order && order->count < ARRAY_SIZE(order->probed)) { + order->probed[order->count++] = dev_name(&pdev->dev); + wake_up_interruptible(&order->wq); + } + + return 0; +} + +static struct platform_driver swnode_test_supplier_driver = { + .probe = swnode_test_record_probe, + .driver = { + .name = SWNODE_DEVLINK_TEST_SUPPLIER, + }, +}; + +static struct platform_driver swnode_test_consumer_driver = { + .probe = swnode_test_record_probe, + .driver = { + .name = SWNODE_DEVLINK_TEST_CONSUMER, + }, +}; + +static void swnode_devlink_test_probe_order(struct kunit *test) +{ + static const struct software_node supplier_swnode = { + .name = "swnode-devlink-test-supplier", + }; + + const struct property_entry consumer_props[] = { + PROPERTY_ENTRY_REF("supplier-ref", &supplier_swnode), + { } + }; + + struct platform_device *supplier, *consumer; + struct swnode_test_probe_order *order; + struct fwnode_handle *fwnode; + int ret; + + order = kunit_kzalloc(test, sizeof(*order), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, order); + init_waitqueue_head(&order->wq); + + fwnode = kunit_software_node_register(test, &supplier_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fwnode); + + ret = kunit_platform_driver_register(test, &swnode_test_supplier_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_platform_driver_register(test, &swnode_test_consumer_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + supplier = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_SUPPLIER, + PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, supplier); + consumer = kunit_platform_device_alloc(test, SWNODE_DEVLINK_TEST_CONSUMER, + PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, consumer); + + platform_set_drvdata(supplier, order); + platform_set_drvdata(consumer, order); + + ret = device_add_software_node(&supplier->dev, &supplier_swnode); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = device_create_managed_software_node(&consumer->dev, + consumer_props, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = kunit_platform_device_add(test, consumer); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_platform_device_add(test, supplier); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = wait_event_interruptible_timeout(order->wq, + order->count == 2, + msecs_to_jiffies(SWNODE_DEVLINK_TEST_TIMEOUT_MS)); + KUNIT_ASSERT_GT(test, ret, 0); + + KUNIT_EXPECT_STREQ(test, order->probed[0], SWNODE_DEVLINK_TEST_SUPPLIER); + KUNIT_EXPECT_STREQ(test, order->probed[1], SWNODE_DEVLINK_TEST_CONSUMER); + + /* Tear down the consumer (and its device link) before the supplier. */ + kunit_platform_device_unregister(test, consumer); + + device_remove_software_node(&supplier->dev); +} + +static struct kunit_case swnode_test_cases[] = { + KUNIT_CASE(swnode_devlink_test_single_ref), + KUNIT_CASE(swnode_devlink_test_multiple_refs), + KUNIT_CASE(swnode_devlink_test_unregistered_ref), + KUNIT_CASE(swnode_devlink_test_remote_endpoint_excluded), + KUNIT_CASE(swnode_devlink_test_ref_array), + KUNIT_CASE(swnode_devlink_test_probe_order), + { } +}; + +static struct kunit_suite swnode_test_suite = { + .name = "software-node-links", + .test_cases = swnode_test_cases, +}; + +kunit_test_suite(swnode_test_suite); + +MODULE_DESCRIPTION("Test module for software node fw_devlink support"); +MODULE_AUTHOR("Bartosz Golaszewski "); +MODULE_LICENSE("GPL"); -- 2.47.3