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 A83BF26738D 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 66AD7i2P732754 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-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4faf24vgug-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-f200.google.com with SMTP id d75a77b69052e-51c33f48ee4so10237901cf.1 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=bYPQz19j0jZIhaLeqFDBIIs3tTTInBMVH8iTcp2dZIKKPwm/LY+oNCbAYpRE8ykUCt 1QVXB4yxOOa6w9PYMfSQMtlTxrMFW3sT/JZspMETJkcICXuRkIKR1Ao86EKc4NlIwJoa n9UWfh7ERXbfZpmy9tfgt8asBeUJ2LiRDKK2iW+iMwzmUue873zTQzD+zj1elN7iosKG vE4g9VKCE6wZj10o78clcS3a/iSQGwMXvjK0sML/zoXIgJ0+/zv4S8mMOOZuh9TRzGrl sN5zQ1ZVUO6L63OrX1YgYRTBnoe+lbETK1rZY/k7l9/m8wDO15TidCNNW09hhKqpiJCB 6dBw== X-Forwarded-Encrypted: i=1; AHgh+Rpltx8rgaZHJ1y2lLPo9dGvsO8ejPSGJ0YEOfoMc/9s8sWWQByD9VOKOCS3+jp0LgU4twqiYGFl1owL@vger.kernel.org X-Gm-Message-State: AOJu0Yy1BZVhI0Uxf7bfo5FZbIDh8m4Ms+sEvm2bcmsPVgqsP2KiVswo /bJgFCfUZ+Dc+8Aw4qgS/+qjXzRI1g2fH6fhygrysQ53zWZRExySc8s4dbXHYzNvNP9RxVXJrF8 IXGi/9Jw3ZXzH7kRx8b8L5Je3SINCSL0UcchCe+gsBNFzkxIHzg3RGR/3bcKLfumH X-Gm-Gg: AfdE7ckpVBlUZhjGj52qnZstbEn56S821CTQvjBgoI0oPDlcZ3h/b7SFxruY3GQXTT2 6hyUdsbaw+t51JdPuOlwnmLJzPv1MZRY4Dx7YaJfFgVVxiq2nyGWlpS6L0WCf2/+KRRXytCLRt6 HdMpCQ0UlxqtyNFMwq+8jGKMI8Mk10JcTHyM9ckHXNEIkR1hpqHyyA1qIr5lgs8UjT9jpqp+1L/ o7OU8TjaOJWWA5t9NlVhz0H9GhKM7KkViD1bnvDMQyU6fFYSogIJyr7VCeY3gizNGlJPqy2Iz1P t8F1JmeOLCKpZWJeSunK582lLqQBXFYZgO551SLJz7bFX224oPHFsS0UPCba162eKEUl38LvW4p 0j0j7t4VSU7G6yVwbYyjyk5nLtQ+hndm/rzObaUsH X-Received: by 2002:a05:622a:1342:b0:51c:f3:34de with SMTP id d75a77b69052e-51c8b4307abmr117053161cf.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-acpi@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: OIsNbLLTzLkuUw1i90aGFODTtRfWQpJJ X-Proofpoint-ORIG-GUID: OIsNbLLTzLkuUw1i90aGFODTtRfWQpJJ X-Proofpoint-Spam-Info: AW1haW4tMjYwNzEwMDEzNiBTYWx0ZWRfX6NXaCkl0jIsv AnVwSnSoKX/WTFIHOFOspdUYFCoRcuHTsDWwkeHPz8KPD66zDF2RfAecOuD437yb3dIVGwf6MCt pgVzWoSOZuOlN5a6ffpQnM5eGmk+bjg= X-Authority-Analysis: v=2.4 cv=daKwG3Xe c=1 sm=1 tr=0 ts=6a50f8fc cx=c_pps a=JbAStetqSzwMeJznSMzCyw==: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=uxP6HrT_eTzRwkO_Te1X:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzEwMDEzNiBTYWx0ZWRfX28f6nRoBGmk9 NHAu5Zf6OuPprFbw9W1TFI00LoGPMKUn6wt6EWAjlIx8MTjsTr8S5J8/djQqjs2+JBvqNYEOn3v VjmtzjemuaAhaZ4kF1V4hZaM8bHZGYaKgIudlWqFO9mdR3TBiThY2LxeXjr2yBYq6E3ZyCZuyxL AT6DexLc19tpIrfXUL+W1HoP4nujsM0xWbjWo3MXequKnPodQ8rKUWHHT/ShVeVvWWlgP/ym9nB l6lDwK7tot43zh7s2CnP/mM93wEQ8Xn6Wv0l04JCZ5KekSrASwpSdlYb8hI5aB7giB9TPzUy9eU qoaV406ktv3qs4TRfuA/St6jGMzwBVVer0pAYhEOX0+3cl2YnwNuExQl7Qc6nI8UQcWrCU/TCYW vmpVYhTsgaQj8zuRsZHT46dndc72Zf3Yd9VbgHATJDiZxy+P51ColgFIFN79B4e4f/I64clJ19Y qcpk2jjsXII56dYAsXQ== 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