linux-fpga.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem
@ 2023-05-11 14:19 Marco Pagani
  2023-05-11 14:19 ` [RFC PATCH v5 1/4] fpga: add fake FPGA manager Marco Pagani
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Marco Pagani @ 2023-05-11 14:19 UTC (permalink / raw)
  To: Moritz Fischer, Wu Hao, Xu Yilun, Tom Rix
  Cc: Marco Pagani, linux-kernel, linux-fpga

This patch set introduces initial KUnit test suites for the FPGA subsystem.

Tests can be run using:
[user@localhost linux]$ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/fpga/tests

v5:
- Removed most of the exported functions from fake components
- Moved all KUnit expectations/assertions to the main test module
- Removed standalone use case to simplify the code
- Removed instances counters from fake components (using device.id instead)
- Set header size in the .parse_header op
- improved bridge get_put_list test case

v4:
- Fix build error

v3:
- Calling fpga_bridges_put() between reconfigurations
- Functions for registering fake modules allocate and return context structs

v2:
- Restructured code into multiple suites to test components in isolation
- Reduced code duplication using init and exit methods
- Using a get_bridges() method to build the list of bridges just before programming
- Regions and Bridges are organized topologically
- Changed bitstream/bit to images
- Allocate images dynamically
- Renamed fpga-tests to fpga-test
- Simplified Kconfig
- Add license info to the fpga-test module

Marco Pagani (4):
  fpga: add fake FPGA manager
  fpga: add fake FPGA bridge
  fpga: add fake FPGA region
  fpga: add initial KUnit test suites

 drivers/fpga/Kconfig                  |   2 +
 drivers/fpga/Makefile                 |   3 +
 drivers/fpga/tests/.kunitconfig       |   5 +
 drivers/fpga/tests/Kconfig            |  11 +
 drivers/fpga/tests/Makefile           |   6 +
 drivers/fpga/tests/fake-fpga-bridge.c | 203 ++++++++++
 drivers/fpga/tests/fake-fpga-bridge.h |  40 ++
 drivers/fpga/tests/fake-fpga-mgr.c    | 271 +++++++++++++
 drivers/fpga/tests/fake-fpga-mgr.h    |  53 +++
 drivers/fpga/tests/fake-fpga-region.c | 245 ++++++++++++
 drivers/fpga/tests/fake-fpga-region.h |  40 ++
 drivers/fpga/tests/fpga-test.c        | 551 ++++++++++++++++++++++++++
 12 files changed, 1430 insertions(+)
 create mode 100644 drivers/fpga/tests/.kunitconfig
 create mode 100644 drivers/fpga/tests/Kconfig
 create mode 100644 drivers/fpga/tests/Makefile
 create mode 100644 drivers/fpga/tests/fake-fpga-bridge.c
 create mode 100644 drivers/fpga/tests/fake-fpga-bridge.h
 create mode 100644 drivers/fpga/tests/fake-fpga-mgr.c
 create mode 100644 drivers/fpga/tests/fake-fpga-mgr.h
 create mode 100644 drivers/fpga/tests/fake-fpga-region.c
 create mode 100644 drivers/fpga/tests/fake-fpga-region.h
 create mode 100644 drivers/fpga/tests/fpga-test.c


base-commit: ac9a78681b921877518763ba0e89202254349d1b
-- 
2.40.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH v5 1/4] fpga: add fake FPGA manager
  2023-05-11 14:19 [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem Marco Pagani
@ 2023-05-11 14:19 ` Marco Pagani
  2023-05-13 15:44   ` Xu Yilun
  2023-05-11 14:19 ` [RFC PATCH v5 2/4] fpga: add fake FPGA bridge Marco Pagani
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Marco Pagani @ 2023-05-11 14:19 UTC (permalink / raw)
  To: Moritz Fischer, Wu Hao, Xu Yilun, Tom Rix
  Cc: Marco Pagani, linux-kernel, linux-fpga

Add fake FPGA manager platform driver with support functions. This module
is part of the KUnit tests for the FPGA subsystem.

Signed-off-by: Marco Pagani <marpagan@redhat.com>
---
 drivers/fpga/tests/fake-fpga-mgr.c | 271 +++++++++++++++++++++++++++++
 drivers/fpga/tests/fake-fpga-mgr.h |  53 ++++++
 2 files changed, 324 insertions(+)
 create mode 100644 drivers/fpga/tests/fake-fpga-mgr.c
 create mode 100644 drivers/fpga/tests/fake-fpga-mgr.h

diff --git a/drivers/fpga/tests/fake-fpga-mgr.c b/drivers/fpga/tests/fake-fpga-mgr.c
new file mode 100644
index 000000000000..1e994db10159
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-mgr.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the fake FPGA manager
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#include "fake-fpga-mgr.h"
+
+#define FAKE_FPGA_MGR_DEV_NAME	"fake_fpga_mgr"
+
+struct fake_mgr_pdata {
+	struct kunit *test;
+	struct fake_fpga_mgr_stats *stats;
+};
+
+struct fake_mgr_priv {
+	int seq_num;
+	struct fake_mgr_pdata *pdata;
+};
+
+static bool check_header(struct kunit *test, const char *buf, size_t count)
+{
+	size_t i;
+
+	for (i = 0; i < count; i++)
+		if (buf[i] != TEST_HEADER_MAGIC)
+			return false;
+
+	return true;
+}
+
+static enum fpga_mgr_states op_state(struct fpga_manager *mgr)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: state\n");
+
+	return mgr->state;
+}
+
+static u64 op_status(struct fpga_manager *mgr)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: status\n");
+
+	return 0;
+}
+
+static int op_parse_header(struct fpga_manager *mgr, struct fpga_image_info *info,
+			   const char *buf, size_t count)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	/* Set header_size and data_size as expected */
+	info->header_size = TEST_HEADER_SIZE;
+	info->data_size = info->count - TEST_HEADER_SIZE;
+
+	priv->pdata->stats->header_done = check_header(priv->pdata->test, buf,
+						       info->header_size);
+	priv->pdata->stats->op_parse_header_state = mgr->state;
+	priv->pdata->stats->op_parse_header_seq = priv->seq_num++;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: parse_header\n");
+
+	return 0;
+}
+
+static int op_write_init(struct fpga_manager *mgr, struct fpga_image_info *info,
+			 const char *buf, size_t count)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	priv->pdata->stats->op_write_init_state = mgr->state;
+	priv->pdata->stats->op_write_init_seq = priv->seq_num++;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: write_init\n");
+
+	return 0;
+}
+
+static int op_write(struct fpga_manager *mgr, const char *buf, size_t count)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	priv->pdata->stats->op_write_state = mgr->state;
+	priv->pdata->stats->op_write_seq = priv->seq_num++;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: write\n");
+
+	return 0;
+}
+
+static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	priv->pdata->stats->op_write_state = mgr->state;
+	priv->pdata->stats->op_write_seq = priv->seq_num++;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: write_sg\n");
+
+	return 0;
+}
+
+static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	priv->pdata->stats->op_write_complete_state = mgr->state;
+	priv->pdata->stats->op_write_complete_seq = priv->seq_num++;
+	priv->pdata->stats->prog_count++;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: write_complete\n");
+
+	return 0;
+}
+
+static void op_fpga_remove(struct fpga_manager *mgr)
+{
+	struct fake_mgr_priv *priv;
+
+	priv = mgr->priv;
+
+	kunit_info(priv->pdata->test, "Fake FPGA manager: remove\n");
+}
+
+static const struct fpga_manager_ops fake_fpga_mgr_ops = {
+	.skip_header = false,
+	.state = op_state,
+	.status = op_status,
+	.parse_header = op_parse_header,
+	.write_init = op_write_init,
+	.write = op_write,
+	.write_sg = op_write_sg,
+	.write_complete = op_write_complete,
+	.fpga_remove = op_fpga_remove,
+};
+
+/**
+ * fake_fpga_mgr_register() - register a fake FPGA manager.
+ * @test: KUnit test context object.
+ * @mgr_ctx: fake FPGA manager context data structure.
+ *
+ * Return: pointer to a new fake FPGA manager on success, an ERR_PTR()
+ * encoded error code on failure.
+ */
+struct fake_fpga_mgr *
+fake_fpga_mgr_register(struct kunit *test, struct device *parent)
+{
+	struct fake_fpga_mgr *mgr_ctx;
+	struct fake_mgr_pdata pdata;
+	int ret;
+
+	mgr_ctx = kunit_kzalloc(test, sizeof(*mgr_ctx), GFP_KERNEL);
+	if (!mgr_ctx) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	mgr_ctx->pdev = platform_device_alloc(FAKE_FPGA_MGR_DEV_NAME,
+					      PLATFORM_DEVID_AUTO);
+	if (!mgr_ctx->pdev) {
+		kunit_err(test, "Fake FPGA manager device allocation failed\n");
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	pdata.test = test;
+	pdata.stats = &mgr_ctx->stats;
+	platform_device_add_data(mgr_ctx->pdev, &pdata, sizeof(pdata));
+
+	mgr_ctx->pdev->dev.parent = parent;
+	ret = platform_device_add(mgr_ctx->pdev);
+	if (ret) {
+		kunit_err(test, "Fake FPGA manager device add failed\n");
+		goto err_pdev;
+	}
+
+	mgr_ctx->mgr = platform_get_drvdata(mgr_ctx->pdev);
+
+	kunit_info(test, "Fake FPGA manager registered\n");
+
+	return mgr_ctx;
+
+err_pdev:
+	platform_device_put(mgr_ctx->pdev);
+err_mem:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fake_fpga_mgr_register);
+
+/**
+ * fake_fpga_mgr_unregister() - unregister a fake FPGA manager.
+ * @mgr_ctx: fake FPGA manager context data structure.
+ */
+void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx)
+{
+	struct fake_mgr_priv *priv;
+	struct kunit *test;
+
+	if (!mgr_ctx)
+		return;
+
+	priv = mgr_ctx->mgr->priv;
+	test = priv->pdata->test;
+
+	platform_device_unregister(mgr_ctx->pdev);
+
+	kunit_info(test, "Fake FPGA manager unregistered\n");
+}
+EXPORT_SYMBOL_GPL(fake_fpga_mgr_unregister);
+
+static int fake_fpga_mgr_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	struct fake_mgr_pdata *pdata;
+	struct fpga_manager *mgr;
+	struct fake_mgr_priv *priv;
+
+	dev = &pdev->dev;
+	pdata = dev_get_platdata(dev);
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdata = pdata;
+
+	mgr = devm_fpga_mgr_register(dev, "Fake FPGA Manager",
+				     &fake_fpga_mgr_ops, priv);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
+
+	platform_set_drvdata(pdev, mgr);
+
+	return 0;
+}
+
+static struct platform_driver fake_fpga_mgr_drv = {
+	.driver = {
+		.name = FAKE_FPGA_MGR_DEV_NAME
+	},
+	.probe = fake_fpga_mgr_probe,
+};
+
+module_platform_driver(fake_fpga_mgr_drv);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/fpga/tests/fake-fpga-mgr.h b/drivers/fpga/tests/fake-fpga-mgr.h
new file mode 100644
index 000000000000..282c20d8e40c
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-mgr.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for the fake FPGA manager
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#ifndef __FPGA_FAKE_MGR_H
+#define __FPGA_FAKE_MGR_H
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <kunit/test.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#define FPGA_IMG_BLOCK		1024
+#define TEST_HEADER_MAGIC	0x3f
+#define TEST_HEADER_SIZE	FPGA_IMG_BLOCK
+
+struct fake_fpga_mgr_stats {
+	u32 prog_count;
+	bool header_done;
+	u32 op_parse_header_seq;
+	u32 op_write_init_seq;
+	u32 op_write_seq;
+	u32 op_write_complete_seq;
+	enum fpga_mgr_states op_parse_header_state;
+	enum fpga_mgr_states op_write_init_state;
+	enum fpga_mgr_states op_write_state;
+	enum fpga_mgr_states op_write_complete_state;
+};
+
+/**
+ * struct fake_fpga_mgr - fake FPGA manager context data structure
+ *
+ * @mgr: FPGA manager.
+ * @pdev: platform device of the FPGA manager.
+ * @stats: info from the low level driver.
+ */
+struct fake_fpga_mgr {
+	struct fpga_manager *mgr;
+	struct platform_device *pdev;
+	struct fake_fpga_mgr_stats stats;
+};
+
+struct fake_fpga_mgr *
+fake_fpga_mgr_register(struct kunit *test, struct device *parent);
+
+void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx);
+
+#endif /* __FPGA_FAKE_MGR_H */
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH v5 2/4] fpga: add fake FPGA bridge
  2023-05-11 14:19 [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem Marco Pagani
  2023-05-11 14:19 ` [RFC PATCH v5 1/4] fpga: add fake FPGA manager Marco Pagani
@ 2023-05-11 14:19 ` Marco Pagani
  2023-05-13 15:46   ` Xu Yilun
  2023-05-11 14:19 ` [RFC PATCH v5 3/4] fpga: add fake FPGA region Marco Pagani
  2023-05-11 14:19 ` [RFC PATCH v5 4/4] fpga: add initial KUnit test suites Marco Pagani
  3 siblings, 1 reply; 12+ messages in thread
From: Marco Pagani @ 2023-05-11 14:19 UTC (permalink / raw)
  To: Moritz Fischer, Wu Hao, Xu Yilun, Tom Rix
  Cc: Marco Pagani, linux-kernel, linux-fpga

Add fake FPGA bridge platform driver with support functions. This module
is part of the KUnit tests for the FPGA subsystem.

Signed-off-by: Marco Pagani <marpagan@redhat.com>
---
 drivers/fpga/tests/fake-fpga-bridge.c | 203 ++++++++++++++++++++++++++
 drivers/fpga/tests/fake-fpga-bridge.h |  40 +++++
 2 files changed, 243 insertions(+)
 create mode 100644 drivers/fpga/tests/fake-fpga-bridge.c
 create mode 100644 drivers/fpga/tests/fake-fpga-bridge.h

diff --git a/drivers/fpga/tests/fake-fpga-bridge.c b/drivers/fpga/tests/fake-fpga-bridge.c
new file mode 100644
index 000000000000..e5db0a809ee3
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-bridge.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the fake FPGA bridge
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fpga/fpga-bridge.h>
+#include <kunit/test.h>
+
+#include "fake-fpga-bridge.h"
+
+#define FAKE_FPGA_BRIDGE_DEV_NAME	"fake_fpga_bridge"
+
+struct fake_bridge_pdata {
+	struct kunit *test;
+	struct fake_fpga_bridge_stats *stats;
+};
+
+struct fake_bridge_priv {
+	bool state;
+	struct fake_bridge_pdata *pdata;
+};
+
+static int op_enable_show(struct fpga_bridge *bridge)
+{
+	struct fake_bridge_priv *priv;
+
+	priv = bridge->priv;
+
+	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: enable_show\n",
+		   bridge->dev.id);
+
+	return priv->state;
+}
+
+static int op_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+	struct fake_bridge_priv *priv;
+
+	priv = bridge->priv;
+
+	if (enable && !priv->state)
+		priv->pdata->stats->cycles_count++;
+
+	priv->state = enable;
+	priv->pdata->stats->enable = priv->state;
+
+	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: enable_set: %d\n",
+		   bridge->dev.id, enable);
+
+	return 0;
+}
+
+static void op_remove(struct fpga_bridge *bridge)
+{
+	struct fake_bridge_priv *priv;
+
+	priv = bridge->priv;
+
+	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: remove\n",
+		   bridge->dev.id);
+}
+
+static const struct fpga_bridge_ops fake_fpga_bridge_ops = {
+	.enable_show = op_enable_show,
+	.enable_set = op_enable_set,
+	.fpga_bridge_remove = op_remove,
+};
+
+/**
+ * fake_fpga_bridge_register() - register a fake FPGA bridge.
+ * @test: KUnit test context object.
+ * @parent: parent device.
+ *
+ * Return: pointer to a new fake FPGA bridge on success, an ERR_PTR()
+ * encoded error code on failure.
+ */
+struct fake_fpga_bridge *
+fake_fpga_bridge_register(struct kunit *test, struct device *parent)
+{
+	struct fake_fpga_bridge *bridge_ctx;
+	struct fake_bridge_pdata pdata;
+	struct fake_bridge_priv *priv;
+	int ret;
+
+	bridge_ctx = kunit_kzalloc(test, sizeof(*bridge_ctx), GFP_KERNEL);
+	if (!bridge_ctx) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	bridge_ctx->pdev = platform_device_alloc(FAKE_FPGA_BRIDGE_DEV_NAME,
+						 PLATFORM_DEVID_AUTO);
+	if (!bridge_ctx->pdev) {
+		pr_err("Fake FPGA bridge device allocation failed\n");
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	pdata.test = test;
+	pdata.stats = &bridge_ctx->stats;
+	platform_device_add_data(bridge_ctx->pdev, &pdata, sizeof(pdata));
+
+	bridge_ctx->pdev->dev.parent = parent;
+	ret = platform_device_add(bridge_ctx->pdev);
+	if (ret) {
+		pr_err("Fake FPGA bridge device add failed\n");
+		goto err_pdev;
+	}
+
+	bridge_ctx->bridge = platform_get_drvdata(bridge_ctx->pdev);
+
+	priv = bridge_ctx->bridge->priv;
+	bridge_ctx->stats.enable = priv->state;
+
+	kunit_info(test, "Fake FPGA bridge %u: registered\n",
+		   bridge_ctx->bridge->dev.id);
+
+	return bridge_ctx;
+
+err_pdev:
+	platform_device_put(bridge_ctx->pdev);
+err_mem:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fake_fpga_bridge_register);
+
+/**
+ * fake_fpga_bridge_unregister() - unregister a fake FPGA bridge.
+ * @bridge_ctx: fake FPGA bridge context data structure.
+ */
+void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx)
+{
+	struct fake_bridge_priv *priv;
+	struct kunit *test;
+	u32 id;
+
+	if (!bridge_ctx)
+		return;
+
+	id = bridge_ctx->bridge->dev.id;
+	priv = bridge_ctx->bridge->priv;
+	test = priv->pdata->test;
+
+	platform_device_unregister(bridge_ctx->pdev);
+
+	kunit_info(test, "Fake FPGA bridge %u: unregistered\n", id);
+}
+EXPORT_SYMBOL_GPL(fake_fpga_bridge_unregister);
+
+static int fake_fpga_bridge_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	struct fake_bridge_pdata *pdata;
+	struct fpga_bridge *bridge;
+	struct fake_bridge_priv *priv;
+
+	dev = &pdev->dev;
+	pdata = dev_get_platdata(dev);
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdata = pdata;
+	priv->state = true;
+
+	bridge = fpga_bridge_register(dev, "Fake FPGA Bridge",
+				      &fake_fpga_bridge_ops, priv);
+	if (IS_ERR(bridge))
+		return PTR_ERR(bridge);
+
+	platform_set_drvdata(pdev, bridge);
+
+	return 0;
+}
+
+static int fake_fpga_bridge_remove(struct platform_device *pdev)
+{
+	struct fpga_bridge *bridge = platform_get_drvdata(pdev);
+
+	fpga_bridge_unregister(bridge);
+
+	return 0;
+}
+
+static struct platform_driver fake_fpga_bridge_drv = {
+	.driver = {
+		.name = FAKE_FPGA_BRIDGE_DEV_NAME
+	},
+	.probe = fake_fpga_bridge_probe,
+	.remove = fake_fpga_bridge_remove,
+};
+
+module_platform_driver(fake_fpga_bridge_drv);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/fpga/tests/fake-fpga-bridge.h b/drivers/fpga/tests/fake-fpga-bridge.h
new file mode 100644
index 000000000000..3ecfab7e2890
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-bridge.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for the fake FPGA bridge
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#ifndef __FPGA_FAKE_BRIDGE_H
+#define __FPGA_FAKE_BRIDGE_H
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <kunit/test.h>
+
+struct fake_fpga_bridge_stats {
+	bool enable;
+	u32 cycles_count;
+};
+
+/**
+ * struct fake_fpga_bridge - fake FPGA bridge context data structure
+ *
+ * @bridge: FPGA bridge.
+ * @pdev: platform device of the FPGA bridge.
+ * @stats: info from the low level driver.
+ */
+struct fake_fpga_bridge {
+	struct fpga_bridge *bridge;
+	struct platform_device *pdev;
+	struct fake_fpga_bridge_stats stats;
+};
+
+struct fake_fpga_bridge *
+fake_fpga_bridge_register(struct kunit *test, struct device *parent);
+
+void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx);
+
+#endif /* __FPGA_FAKE_BRIDGE_H */
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH v5 3/4] fpga: add fake FPGA region
  2023-05-11 14:19 [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem Marco Pagani
  2023-05-11 14:19 ` [RFC PATCH v5 1/4] fpga: add fake FPGA manager Marco Pagani
  2023-05-11 14:19 ` [RFC PATCH v5 2/4] fpga: add fake FPGA bridge Marco Pagani
@ 2023-05-11 14:19 ` Marco Pagani
  2023-05-13 16:05   ` Xu Yilun
  2023-05-11 14:19 ` [RFC PATCH v5 4/4] fpga: add initial KUnit test suites Marco Pagani
  3 siblings, 1 reply; 12+ messages in thread
From: Marco Pagani @ 2023-05-11 14:19 UTC (permalink / raw)
  To: Moritz Fischer, Wu Hao, Xu Yilun, Tom Rix
  Cc: Marco Pagani, linux-kernel, linux-fpga

Add fake FPGA region platform driver with support functions. This module
is part of the KUnit tests for the FPGA subsystem.

Signed-off-by: Marco Pagani <marpagan@redhat.com>
---
 drivers/fpga/tests/fake-fpga-region.c | 245 ++++++++++++++++++++++++++
 drivers/fpga/tests/fake-fpga-region.h |  40 +++++
 2 files changed, 285 insertions(+)
 create mode 100644 drivers/fpga/tests/fake-fpga-region.c
 create mode 100644 drivers/fpga/tests/fake-fpga-region.h

diff --git a/drivers/fpga/tests/fake-fpga-region.c b/drivers/fpga/tests/fake-fpga-region.c
new file mode 100644
index 000000000000..020c3ad13812
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-region.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the fake FPGA region
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/fpga/fpga-region.h>
+#include <linux/fpga/fpga-bridge.h>
+#include <kunit/test.h>
+
+#include "fake-fpga-region.h"
+
+#define FAKE_FPGA_REGION_DEV_NAME	"fake_fpga_region"
+
+struct fake_region_pdata {
+	struct kunit *test;
+	struct fpga_manager *mgr;
+};
+
+struct bridge_elem {
+	struct fpga_bridge *bridge;
+	struct list_head node;
+};
+
+struct fake_region_priv {
+	struct list_head bridge_list;
+	struct fake_region_pdata *pdata;
+};
+
+/**
+ * fake_fpga_region_register() - register a fake FPGA region.
+ * @mgr: associated FPGA manager.
+ * @parent: parent device.
+ * @test: KUnit test context object.
+ *
+ * Return: pointer to a new fake FPGA region on success, an ERR_PTR() encoded
+ * error code on failure.
+ */
+struct fake_fpga_region *
+fake_fpga_region_register(struct kunit *test, struct fpga_manager *mgr,
+			  struct device *parent)
+{
+	struct fake_fpga_region *region_ctx;
+	struct fake_region_pdata pdata;
+	int ret;
+
+	region_ctx = kunit_kzalloc(test, sizeof(*region_ctx), GFP_KERNEL);
+	if (!region_ctx) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	region_ctx->pdev = platform_device_alloc(FAKE_FPGA_REGION_DEV_NAME,
+						 PLATFORM_DEVID_AUTO);
+	if (!region_ctx->pdev) {
+		pr_err("Fake FPGA region device allocation failed\n");
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	pdata.mgr = mgr;
+	pdata.test = test;
+	platform_device_add_data(region_ctx->pdev, &pdata, sizeof(pdata));
+
+	region_ctx->pdev->dev.parent = parent;
+	ret = platform_device_add(region_ctx->pdev);
+	if (ret) {
+		pr_err("Fake FPGA region device add failed\n");
+		goto err_pdev;
+	}
+
+	region_ctx->region = platform_get_drvdata(region_ctx->pdev);
+
+	kunit_info(test, "Fake FPGA region %u: registered\n",
+		   region_ctx->region->dev.id);
+
+	return region_ctx;
+
+err_pdev:
+	platform_device_put(region_ctx->pdev);
+err_mem:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fake_fpga_region_register);
+
+/**
+ * fake_fpga_region_unregister() - unregister a fake FPGA region.
+ * @region_ctx: fake FPGA region context data structure.
+ */
+void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx)
+{
+	struct fake_region_priv *priv;
+	struct kunit *test;
+	u32 id;
+
+	if (!region_ctx)
+		return;
+
+	id = region_ctx->region->dev.id;
+	priv = region_ctx->region->priv;
+	test = priv->pdata->test;
+
+	platform_device_unregister(region_ctx->pdev);
+
+	kunit_info(test, "Fake FPGA region %u: unregistered\n", id);
+}
+EXPORT_SYMBOL_GPL(fake_fpga_region_unregister);
+
+/**
+ * fake_fpga_region_add_bridge() - add a bridge to a fake FPGA region.
+ * @region_ctx: fake FPGA region context data structure.
+ * @bridge: FPGA bridge.
+ *
+ * Return: 0 if registration succeeded, an error code otherwise.
+ */
+int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx,
+				struct fpga_bridge *bridge)
+{
+	struct fake_region_priv *priv;
+	struct bridge_elem *elem;
+
+	priv = region_ctx->region->priv;
+
+	elem = devm_kzalloc(&region_ctx->pdev->dev, sizeof(*elem), GFP_KERNEL);
+	if (!elem)
+		return -ENOMEM;
+
+	/* Add bridge to the list of bridges in the private context */
+	elem->bridge = bridge;
+	list_add(&elem->node, &priv->bridge_list);
+
+	kunit_info(priv->pdata->test, "Bridge: %u added to fake FPGA region: %u\n",
+		   bridge->dev.id, region_ctx->region->dev.id);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fake_fpga_region_add_bridge);
+
+int fake_fpga_region_program(struct fake_fpga_region *region_ctx)
+{
+	int ret;
+
+	ret = fpga_region_program_fpga(region_ctx->region);
+
+	/* fpga_region_program_fpga() already puts the bridges in case of errors */
+	if (!ret)
+		fpga_bridges_put(&region_ctx->region->bridge_list);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fake_fpga_region_program);
+
+static int fake_region_get_bridges(struct fpga_region *region)
+{
+	struct fake_region_priv *priv;
+	struct bridge_elem *elem;
+	int ret;
+
+	priv = region->priv;
+
+	/* Copy the list of bridges from the private context to the region */
+	list_for_each_entry(elem, &priv->bridge_list, node) {
+		ret = fpga_bridge_get_to_list(elem->bridge->dev.parent,
+					      region->info,
+					      &region->bridge_list);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int fake_fpga_region_probe(struct platform_device *pdev)
+{
+	struct device *dev;
+	struct fpga_region *region;
+	struct fpga_manager *mgr;
+	struct fake_region_pdata *pdata;
+	struct fake_region_priv *priv;
+	struct fpga_region_info info;
+
+	dev = &pdev->dev;
+	pdata = dev_get_platdata(dev);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Fake FPGA region probe: missing platform data\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mgr = fpga_mgr_get(pdata->mgr->dev.parent);
+	if (IS_ERR(mgr))
+		return PTR_ERR(mgr);
+
+	priv->pdata = pdata;
+	INIT_LIST_HEAD(&priv->bridge_list);
+
+	memset(&info, 0, sizeof(info));
+	info.priv = priv;
+	info.mgr = mgr;
+	info.get_bridges = fake_region_get_bridges;
+
+	region = fpga_region_register_full(dev, &info);
+	if (IS_ERR(region)) {
+		fpga_mgr_put(mgr);
+		return PTR_ERR(region);
+	}
+
+	platform_set_drvdata(pdev, region);
+
+	return 0;
+}
+
+static int fake_fpga_region_remove(struct platform_device *pdev)
+{
+	struct fpga_region *region = platform_get_drvdata(pdev);
+	struct fpga_manager *mgr = region->mgr;
+
+	fpga_mgr_put(mgr);
+	fpga_region_unregister(region);
+
+	return 0;
+}
+
+static struct platform_driver fake_fpga_region_drv = {
+	.driver = {
+		.name = FAKE_FPGA_REGION_DEV_NAME
+	},
+	.probe = fake_fpga_region_probe,
+	.remove = fake_fpga_region_remove,
+};
+
+module_platform_driver(fake_fpga_region_drv);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/fpga/tests/fake-fpga-region.h b/drivers/fpga/tests/fake-fpga-region.h
new file mode 100644
index 000000000000..c72992cbb218
--- /dev/null
+++ b/drivers/fpga/tests/fake-fpga-region.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for the fake FPGA region
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#ifndef __FPGA_FAKE_RGN_H
+#define __FPGA_FAKE_RGN_H
+
+#include <linux/platform_device.h>
+#include <kunit/test.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/fpga/fpga-bridge.h>
+
+/**
+ * struct fake_fpga_region - fake FPGA region context data structure
+ *
+ * @region: FPGA region.
+ * @pdev: platform device of the FPGA region.
+ */
+struct fake_fpga_region {
+	struct fpga_region *region;
+	struct platform_device *pdev;
+};
+
+struct fake_fpga_region *
+fake_fpga_region_register(struct kunit *test, struct fpga_manager *mgr,
+			  struct device *parent);
+
+int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx,
+				struct fpga_bridge *bridge);
+
+int fake_fpga_region_program(struct fake_fpga_region *region_ctx);
+
+void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx);
+
+#endif /* __FPGA_FAKE_RGN_H */
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH v5 4/4] fpga: add initial KUnit test suites
  2023-05-11 14:19 [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem Marco Pagani
                   ` (2 preceding siblings ...)
  2023-05-11 14:19 ` [RFC PATCH v5 3/4] fpga: add fake FPGA region Marco Pagani
@ 2023-05-11 14:19 ` Marco Pagani
  2023-05-13 17:40   ` Xu Yilun
  3 siblings, 1 reply; 12+ messages in thread
From: Marco Pagani @ 2023-05-11 14:19 UTC (permalink / raw)
  To: Moritz Fischer, Wu Hao, Xu Yilun, Tom Rix
  Cc: Marco Pagani, linux-kernel, linux-fpga

Introduce initial KUnit tests for the FPGA subsystem. Tests are organized
into three test suites. The first suite tests the FPGA Manager.
The second suite tests the FPGA Bridge. Finally, the last test suite
models a complete FPGA platform and tests static and partial reconfiguration.

Signed-off-by: Marco Pagani <marpagan@redhat.com>
---
 drivers/fpga/Kconfig            |   2 +
 drivers/fpga/Makefile           |   3 +
 drivers/fpga/tests/.kunitconfig |   5 +
 drivers/fpga/tests/Kconfig      |  11 +
 drivers/fpga/tests/Makefile     |   6 +
 drivers/fpga/tests/fpga-test.c  | 551 ++++++++++++++++++++++++++++++++
 6 files changed, 578 insertions(+)
 create mode 100644 drivers/fpga/tests/.kunitconfig
 create mode 100644 drivers/fpga/tests/Kconfig
 create mode 100644 drivers/fpga/tests/Makefile
 create mode 100644 drivers/fpga/tests/fpga-test.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 0a00763b9f28..2f689ac4ba3a 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -276,4 +276,6 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI
 	  FPGA manager driver support for Lattice FPGAs programming over slave
 	  SPI sysCONFIG interface.
 
+source "drivers/fpga/tests/Kconfig"
+
 endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 72e554b4d2f7..352a2612623e 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -55,3 +55,6 @@ obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000)	+= dfl-n3000-nios.o
 
 # Drivers for FPGAs which implement DFL
 obj-$(CONFIG_FPGA_DFL_PCI)		+= dfl-pci.o
+
+# KUnit tests
+obj-$(CONFIG_FPGA_KUNIT_TESTS)		+= tests/
diff --git a/drivers/fpga/tests/.kunitconfig b/drivers/fpga/tests/.kunitconfig
new file mode 100644
index 000000000000..a1c2a2974c39
--- /dev/null
+++ b/drivers/fpga/tests/.kunitconfig
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_FPGA=y
+CONFIG_FPGA_REGION=y
+CONFIG_FPGA_BRIDGE=y
+CONFIG_FPGA_KUNIT_TESTS=y
diff --git a/drivers/fpga/tests/Kconfig b/drivers/fpga/tests/Kconfig
new file mode 100644
index 000000000000..1cbea75dd29b
--- /dev/null
+++ b/drivers/fpga/tests/Kconfig
@@ -0,0 +1,11 @@
+config FPGA_KUNIT_TESTS
+	tristate "KUnit test for the FPGA subsystem" if !KUNIT_ALL_TESTS
+	depends on FPGA && FPGA_REGION && FPGA_BRIDGE && KUNIT
+	default KUNIT_ALL_TESTS
+        help
+          This builds unit tests for the FPGA subsystem
+
+          For more information on KUnit and unit tests in general,
+          please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+          If unsure, say N.
diff --git a/drivers/fpga/tests/Makefile b/drivers/fpga/tests/Makefile
new file mode 100644
index 000000000000..0b052570659b
--- /dev/null
+++ b/drivers/fpga/tests/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-mgr.o
+obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-region.o
+obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-bridge.o
+obj-$(CONFIG_FPGA_KUNIT_TESTS) += fpga-test.o
diff --git a/drivers/fpga/tests/fpga-test.c b/drivers/fpga/tests/fpga-test.c
new file mode 100644
index 000000000000..929684c1337e
--- /dev/null
+++ b/drivers/fpga/tests/fpga-test.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the FPGA subsystem
+ *
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Marco Pagani <marpagan@redhat.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/fpga/fpga-region.h>
+#include <linux/fpga/fpga-bridge.h>
+
+#include "fake-fpga-region.h"
+#include "fake-fpga-bridge.h"
+#include "fake-fpga-mgr.h"
+
+#define STATIC_IMG_BLOCKS	16
+#define STATIC_IMG_SIZE		(FPGA_IMG_BLOCK * STATIC_IMG_BLOCKS)
+
+#define PARTIAL_IMG_BLOCKS	4
+#define PARTIAL_IMG_SIZE	(FPGA_IMG_BLOCK * PARTIAL_IMG_BLOCKS)
+
+/**
+ * fill_test_header() - fill a buffer with the test header data.
+ * @buf: image buffer.
+ * @count: length of the header.
+ */
+static void fill_test_header(u8 *buf, size_t count)
+{
+	size_t i;
+
+	for (i = 0; i < count; i++)
+		buf[i] = TEST_HEADER_MAGIC;
+}
+
+/**
+ * buf_img_alloc() - Allocate a test FPGA image using a buffer.
+ * @test: KUnit test context object.
+ * @dev: owning device.
+ * @size: image size.
+ *
+ * Return: pointer to a struct fpga_image_info or NULL on failure.
+ */
+static struct fpga_image_info *buf_img_alloc(struct kunit *test, struct device *dev,
+					     size_t size)
+{
+	struct fpga_image_info *img_info;
+	char *img_buf;
+
+	img_buf = kunit_kzalloc(test, size, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf);
+	fill_test_header(img_buf, TEST_HEADER_SIZE);
+
+	img_info = fpga_image_info_alloc(dev);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
+
+	img_info->count = size;
+	img_info->buf = img_buf;
+	img_info->header_size = TEST_HEADER_SIZE;
+
+	kunit_info(test, "FPGA image allocated in a buffer, size: %zu\n", size);
+
+	return img_info;
+}
+
+/**
+ * sgt_img_alloc() - Allocate a test FPGA image using a scatter gather table.
+ * @test: KUnit test context object.
+ * @dev: owning device.
+ * @size: image size.
+ *
+ * Return: pointer to a struct fpga_image_info or NULL on failure.
+ */
+static struct fpga_image_info *sgt_img_alloc(struct kunit *test, struct device *dev,
+					     size_t size)
+{
+	struct fpga_image_info *img_info;
+	char *img_buf;
+	struct sg_table *sgt;
+	int ret;
+
+	img_buf = kunit_kzalloc(test, size, GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf);
+	fill_test_header(img_buf, TEST_HEADER_SIZE);
+
+	sgt = kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL);
+	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+	sg_init_one(sgt->sgl, img_buf, size);
+
+	img_info = fpga_image_info_alloc(dev);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
+
+	img_info->sgt = sgt;
+	img_info->header_size = TEST_HEADER_SIZE;
+
+	kunit_info(test, "FPGA image allocated in a scatter gather table, size: %zu\n",
+		   size);
+
+	return img_info;
+}
+
+/**
+ * img_free() - Free a test FPGA image
+ * @img_info: fpga image information struct.
+ *
+ */
+static void img_free(struct fpga_image_info *img_info)
+{
+	if (!img_info)
+		return;
+
+	if (img_info->sgt)
+		sg_free_table(img_info->sgt);
+
+	fpga_image_info_free(img_info);
+}
+
+/**
+ * fake_fpga_mgr_check_write() - check if the programming sequence is correct.
+ * @test: KUnit test context object.
+ * @mgr_ctx: fake FPGA manager context data structure.
+ */
+static void fake_fpga_mgr_check_write(struct kunit *test,
+				      const struct fake_fpga_mgr *mgr_ctx)
+{
+	KUNIT_EXPECT_TRUE(test, mgr_ctx->stats.header_done);
+
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_parse_header_state,
+			FPGA_MGR_STATE_PARSE_HEADER);
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_init_state,
+			FPGA_MGR_STATE_WRITE_INIT);
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_state,
+			FPGA_MGR_STATE_WRITE);
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_complete_state,
+			FPGA_MGR_STATE_WRITE_COMPLETE);
+
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_init_seq,
+			mgr_ctx->stats.op_parse_header_seq + 1);
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_seq,
+			mgr_ctx->stats.op_parse_header_seq + 2);
+	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_complete_seq,
+			mgr_ctx->stats.op_parse_header_seq + 3);
+}
+
+static int fpga_mgr_test_init(struct kunit *test)
+{
+	struct fake_fpga_mgr *mgr_ctx;
+
+	mgr_ctx = fake_fpga_mgr_register(test, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(mgr_ctx));
+
+	test->priv = mgr_ctx;
+
+	return 0;
+}
+
+static void fpga_mgr_test_img_load_buf(struct kunit *test)
+{
+	struct fake_fpga_mgr *mgr_ctx;
+	struct fpga_image_info *img_info;
+	int ret;
+
+	mgr_ctx = test->priv;
+
+	/* Allocate an FPGA image using a buffer */
+	img_info = buf_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
+
+	KUNIT_EXPECT_EQ(test, 0, mgr_ctx->stats.prog_count);
+
+	ret = fpga_mgr_load(mgr_ctx->mgr, img_info);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, mgr_ctx);
+
+	KUNIT_EXPECT_EQ(test, 1, mgr_ctx->stats.prog_count);
+
+	img_free(img_info);
+}
+
+static void fpga_mgr_test_img_load_sgt(struct kunit *test)
+{
+	struct fake_fpga_mgr *mgr_ctx;
+	struct fpga_image_info *img_info;
+	int ret;
+
+	mgr_ctx = test->priv;
+
+	/* Allocate an FPGA image using a scatter gather table */
+	img_info = sgt_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
+
+	KUNIT_EXPECT_EQ(test, 0, mgr_ctx->stats.prog_count);
+
+	ret = fpga_mgr_load(mgr_ctx->mgr, img_info);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, mgr_ctx);
+
+	KUNIT_EXPECT_EQ(test, 1, mgr_ctx->stats.prog_count);
+
+	img_free(img_info);
+}
+
+static void fpga_mgr_test_exit(struct kunit *test)
+{
+	struct fake_fpga_mgr *mgr_ctx;
+
+	mgr_ctx = test->priv;
+
+	if (mgr_ctx)
+		fake_fpga_mgr_unregister(mgr_ctx);
+}
+
+static struct kunit_case fpga_mgr_test_cases[] = {
+	KUNIT_CASE(fpga_mgr_test_img_load_buf),
+	KUNIT_CASE(fpga_mgr_test_img_load_sgt),
+	{}
+};
+
+static struct kunit_suite fpga_mgr_suite = {
+	.name = "fpga_mgr",
+	.init = fpga_mgr_test_init,
+	.exit = fpga_mgr_test_exit,
+	.test_cases = fpga_mgr_test_cases,
+};
+
+static int fpga_bridge_test_init(struct kunit *test)
+{
+	struct fake_fpga_bridge *bridge_ctx;
+
+	bridge_ctx = fake_fpga_bridge_register(test, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_ctx));
+
+	test->priv = bridge_ctx;
+
+	return 0;
+}
+
+static void fpga_bridge_test_exit(struct kunit *test)
+{
+	struct fake_fpga_bridge *bridge_ctx;
+
+	bridge_ctx = test->priv;
+
+	if (bridge_ctx)
+		fake_fpga_bridge_unregister(bridge_ctx);
+}
+
+static void fpga_bridge_test_toggle(struct kunit *test)
+{
+	struct fake_fpga_bridge *bridge_ctx;
+	int ret;
+
+	bridge_ctx = test->priv;
+
+	KUNIT_EXPECT_TRUE(test, bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, bridge_ctx->stats.cycles_count);
+
+	ret = fpga_bridge_disable(bridge_ctx->bridge);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_FALSE(test, bridge_ctx->stats.enable);
+
+	ret = fpga_bridge_enable(bridge_ctx->bridge);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	KUNIT_EXPECT_TRUE(test, bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, bridge_ctx->stats.cycles_count);
+}
+
+static void fpga_bridge_test_get_put_list(struct kunit *test)
+{
+	struct list_head bridge_list;
+	struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx;
+	int ret;
+
+	bridge_0_ctx = test->priv;
+
+	/* Register another bridge for this test */
+	bridge_1_ctx = fake_fpga_bridge_register(test, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx));
+
+	INIT_LIST_HEAD(&bridge_list);
+
+	/* Get bridge_0 and add it to the list */
+	ret = fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL,
+				      &bridge_list);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge,
+			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
+
+	/* Get bridge_1 and add it to the list */
+	ret = fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL,
+				      &bridge_list);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge,
+			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
+
+	/* Disable an then enable both bridges from the list */
+	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, bridge_0_ctx->stats.cycles_count);
+
+	KUNIT_EXPECT_TRUE(test, bridge_1_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, bridge_1_ctx->stats.cycles_count);
+
+	ret = fpga_bridges_disable(&bridge_list);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_FALSE(test, bridge_0_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, bridge_0_ctx->stats.cycles_count);
+
+	KUNIT_EXPECT_FALSE(test, bridge_1_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, bridge_1_ctx->stats.cycles_count);
+
+	ret = fpga_bridges_enable(&bridge_list);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, bridge_0_ctx->stats.cycles_count);
+
+	KUNIT_EXPECT_TRUE(test, bridge_1_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, bridge_1_ctx->stats.cycles_count);
+
+	/* Put and remove both bridges from the list */
+	fpga_bridges_put(&bridge_list);
+
+	KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
+
+	fake_fpga_bridge_unregister(bridge_1_ctx);
+}
+
+static struct kunit_case fpga_bridge_test_cases[] = {
+	KUNIT_CASE(fpga_bridge_test_toggle),
+	KUNIT_CASE(fpga_bridge_test_get_put_list),
+	{}
+};
+
+static struct kunit_suite fpga_bridge_suite = {
+	.name = "fpga_bridge",
+	.init = fpga_bridge_test_init,
+	.exit = fpga_bridge_test_exit,
+	.test_cases = fpga_bridge_test_cases,
+};
+
+struct fpga_base_ctx {
+	/*
+	 * Base FPGA layout consisting of a single region
+	 * controlled by a bridge and the FPGA manager
+	 */
+	struct fake_fpga_mgr *mgr_ctx;
+	struct fake_fpga_bridge *bridge_ctx;
+	struct fake_fpga_region *region_ctx;
+};
+
+static int fpga_test_init(struct kunit *test)
+{
+	struct fpga_base_ctx *base_ctx;
+	int ret;
+
+	base_ctx = kunit_kzalloc(test, sizeof(*base_ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, base_ctx);
+	test->priv = base_ctx;
+
+	/* Build the base FPGA layout */
+	base_ctx->mgr_ctx = fake_fpga_mgr_register(test, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->mgr_ctx));
+
+	base_ctx->bridge_ctx = fake_fpga_bridge_register(test, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->bridge_ctx));
+
+	/* The base region a child of the base bridge */
+	base_ctx->region_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
+							 &base_ctx->bridge_ctx->bridge->dev);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->region_ctx));
+
+	ret = fake_fpga_region_add_bridge(base_ctx->region_ctx, base_ctx->bridge_ctx->bridge);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	kunit_info(test, "FPGA base system built\n");
+
+	KUNIT_EXPECT_EQ(test, 0, base_ctx->mgr_ctx->stats.prog_count);
+	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, base_ctx->bridge_ctx->stats.cycles_count);
+
+	return 0;
+}
+
+static void fpga_test_exit(struct kunit *test)
+{
+	struct fpga_base_ctx *base_ctx;
+
+	base_ctx = test->priv;
+
+	if (!base_ctx)
+		return;
+
+	if (base_ctx->region_ctx)
+		fake_fpga_region_unregister(base_ctx->region_ctx);
+
+	if (base_ctx->bridge_ctx)
+		fake_fpga_bridge_unregister(base_ctx->bridge_ctx);
+
+	if (base_ctx->mgr_ctx)
+		fake_fpga_mgr_unregister(base_ctx->mgr_ctx);
+}
+
+static void fpga_test_static_cfg(struct kunit *test)
+{
+	struct fpga_base_ctx *base_ctx;
+	struct fpga_image_info *buf_img_info;
+	struct fpga_image_info *sgt_img_info;
+	int ret;
+
+	base_ctx = test->priv;
+
+	buf_img_info = buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf_img_info);
+
+	/* Configure the FPGA using the image in a buffer */
+	base_ctx->region_ctx->region->info = buf_img_info;
+	ret = fake_fpga_region_program(base_ctx->region_ctx);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
+
+	KUNIT_EXPECT_EQ(test, 1, base_ctx->mgr_ctx->stats.prog_count);
+	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, base_ctx->bridge_ctx->stats.cycles_count);
+
+	kunit_info(test, "FPGA configuration completed using a buffer image\n");
+
+	sgt_img_info = sgt_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt_img_info);
+
+	/* Re-configure the FPGA using the image in a scatter list */
+	base_ctx->region_ctx->region->info = sgt_img_info;
+	ret = fake_fpga_region_program(base_ctx->region_ctx);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
+
+	KUNIT_EXPECT_EQ(test, 2, base_ctx->mgr_ctx->stats.prog_count);
+	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 2, base_ctx->bridge_ctx->stats.cycles_count);
+
+	kunit_info(test, "FPGA configuration completed using scatter gather table image\n");
+
+	img_free(sgt_img_info);
+}
+
+static void fpga_test_partial_rcfg(struct kunit *test)
+{
+	struct fpga_base_ctx *base_ctx;
+	struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx;
+	struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx;
+	struct fpga_image_info *partial_img_info;
+	int ret;
+
+	base_ctx = test->priv;
+
+	/*
+	 * Add two reconfigurable sub-regions, each controlled by a bridge. The
+	 * reconfigurable sub-region are children of their bridges which are,
+	 * in turn, children of the base region. For simplicity, the same image
+	 * is used to configure reconfigurable regions
+	 */
+	sub_bridge_0_ctx = fake_fpga_bridge_register(test,
+						     &base_ctx->region_ctx->region->dev);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx));
+
+	sub_region_0_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
+						     &sub_bridge_0_ctx->bridge->dev);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx));
+
+	ret = fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->bridge);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	sub_bridge_1_ctx = fake_fpga_bridge_register(test,
+						     &base_ctx->region_ctx->region->dev);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx));
+
+	sub_region_1_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
+						     &sub_bridge_1_ctx->bridge->dev);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx));
+
+	ret = fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->bridge);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	/* Allocate a partial image using a buffer */
+	partial_img_info = buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev,
+					 PARTIAL_IMG_SIZE);
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, partial_img_info);
+	partial_img_info->flags = FPGA_MGR_PARTIAL_RECONFIG;
+
+	/* Re-configure sub-region 0 with the partial image */
+	sub_region_0_ctx->region->info = partial_img_info;
+	ret = fake_fpga_region_program(sub_region_0_ctx);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
+	KUNIT_EXPECT_EQ(test, 1, base_ctx->mgr_ctx->stats.prog_count);
+
+	KUNIT_EXPECT_TRUE(test, sub_bridge_0_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, sub_bridge_0_ctx->stats.cycles_count);
+
+	/* Re-configure sub-region 1 with the partial image */
+	sub_region_1_ctx->region->info = partial_img_info;
+	ret = fake_fpga_region_program(sub_region_1_ctx);
+	KUNIT_ASSERT_EQ(test, ret, 0);
+
+	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
+	KUNIT_EXPECT_EQ(test, 2, base_ctx->mgr_ctx->stats.prog_count);
+
+	KUNIT_EXPECT_TRUE(test, sub_bridge_1_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 1, sub_bridge_1_ctx->stats.cycles_count);
+
+	/* Check that the base bridge has not been disabled during reconfiguration */
+	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
+	KUNIT_EXPECT_EQ(test, 0, base_ctx->bridge_ctx->stats.cycles_count);
+
+	img_free(partial_img_info);
+	fake_fpga_region_unregister(sub_region_0_ctx);
+	fake_fpga_bridge_unregister(sub_bridge_0_ctx);
+	fake_fpga_region_unregister(sub_region_1_ctx);
+	fake_fpga_bridge_unregister(sub_bridge_1_ctx);
+}
+
+static struct kunit_case fpga_test_cases[] = {
+	KUNIT_CASE(fpga_test_static_cfg),
+	KUNIT_CASE(fpga_test_partial_rcfg),
+	{}
+};
+
+static struct kunit_suite fpga_suite = {
+	.name = "fpga",
+	.init = fpga_test_init,
+	.exit = fpga_test_exit,
+	.test_cases = fpga_test_cases,
+};
+
+kunit_test_suites(&fpga_mgr_suite, &fpga_bridge_suite, &fpga_suite);
+
+MODULE_LICENSE("GPL");
-- 
2.40.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 1/4] fpga: add fake FPGA manager
  2023-05-11 14:19 ` [RFC PATCH v5 1/4] fpga: add fake FPGA manager Marco Pagani
@ 2023-05-13 15:44   ` Xu Yilun
  0 siblings, 0 replies; 12+ messages in thread
From: Xu Yilun @ 2023-05-13 15:44 UTC (permalink / raw)
  To: Marco Pagani; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga

On 2023-05-11 at 16:19:19 +0200, Marco Pagani wrote:
> Add fake FPGA manager platform driver with support functions. This module
> is part of the KUnit tests for the FPGA subsystem.
> 
> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> ---
>  drivers/fpga/tests/fake-fpga-mgr.c | 271 +++++++++++++++++++++++++++++
>  drivers/fpga/tests/fake-fpga-mgr.h |  53 ++++++
>  2 files changed, 324 insertions(+)
>  create mode 100644 drivers/fpga/tests/fake-fpga-mgr.c
>  create mode 100644 drivers/fpga/tests/fake-fpga-mgr.h
> 
> diff --git a/drivers/fpga/tests/fake-fpga-mgr.c b/drivers/fpga/tests/fake-fpga-mgr.c
> new file mode 100644
> index 000000000000..1e994db10159
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-mgr.c
> @@ -0,0 +1,271 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for the fake FPGA manager
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/fpga/fpga-mgr.h>
> +
> +#include "fake-fpga-mgr.h"
> +
> +#define FAKE_FPGA_MGR_DEV_NAME	"fake_fpga_mgr"
> +
> +struct fake_mgr_pdata {
> +	struct kunit *test;
> +	struct fake_fpga_mgr_stats *stats;
> +};
> +
> +struct fake_mgr_priv {
> +	int seq_num;
> +	struct fake_mgr_pdata *pdata;
> +};
> +
> +static bool check_header(struct kunit *test, const char *buf, size_t count)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < count; i++)
> +		if (buf[i] != TEST_HEADER_MAGIC)
> +			return false;
> +
> +	return true;
> +}
> +
> +static enum fpga_mgr_states op_state(struct fpga_manager *mgr)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: state\n");
> +
> +	return mgr->state;
> +}
> +
> +static u64 op_status(struct fpga_manager *mgr)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: status\n");
> +
> +	return 0;
> +}
> +
> +static int op_parse_header(struct fpga_manager *mgr, struct fpga_image_info *info,
> +			   const char *buf, size_t count)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	/* Set header_size and data_size as expected */
> +	info->header_size = TEST_HEADER_SIZE;
> +	info->data_size = info->count - TEST_HEADER_SIZE;
> +
> +	priv->pdata->stats->header_done = check_header(priv->pdata->test, buf,
> +						       info->header_size);
> +	priv->pdata->stats->op_parse_header_state = mgr->state;
> +	priv->pdata->stats->op_parse_header_seq = priv->seq_num++;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: parse_header\n");
> +
> +	return 0;
> +}
> +
> +static int op_write_init(struct fpga_manager *mgr, struct fpga_image_info *info,
> +			 const char *buf, size_t count)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	priv->pdata->stats->op_write_init_state = mgr->state;
> +	priv->pdata->stats->op_write_init_seq = priv->seq_num++;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: write_init\n");
> +
> +	return 0;
> +}
> +
> +static int op_write(struct fpga_manager *mgr, const char *buf, size_t count)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	priv->pdata->stats->op_write_state = mgr->state;
> +	priv->pdata->stats->op_write_seq = priv->seq_num++;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: write\n");
> +
> +	return 0;
> +}
> +
> +static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	priv->pdata->stats->op_write_state = mgr->state;
> +	priv->pdata->stats->op_write_seq = priv->seq_num++;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: write_sg\n");
> +
> +	return 0;
> +}
> +
> +static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	priv->pdata->stats->op_write_complete_state = mgr->state;
> +	priv->pdata->stats->op_write_complete_seq = priv->seq_num++;
> +	priv->pdata->stats->prog_count++;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: write_complete\n");
> +
> +	return 0;
> +}
> +
> +static void op_fpga_remove(struct fpga_manager *mgr)
> +{
> +	struct fake_mgr_priv *priv;
> +
> +	priv = mgr->priv;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA manager: remove\n");
> +}
> +
> +static const struct fpga_manager_ops fake_fpga_mgr_ops = {
> +	.skip_header = false,
> +	.state = op_state,
> +	.status = op_status,
> +	.parse_header = op_parse_header,
> +	.write_init = op_write_init,
> +	.write = op_write,
> +	.write_sg = op_write_sg,
> +	.write_complete = op_write_complete,
> +	.fpga_remove = op_fpga_remove,
> +};
> +
> +/**
> + * fake_fpga_mgr_register() - register a fake FPGA manager.
> + * @test: KUnit test context object.
> + * @mgr_ctx: fake FPGA manager context data structure.
> + *
> + * Return: pointer to a new fake FPGA manager on success, an ERR_PTR()
> + * encoded error code on failure.
> + */
> +struct fake_fpga_mgr *
> +fake_fpga_mgr_register(struct kunit *test, struct device *parent)
> +{
> +	struct fake_fpga_mgr *mgr_ctx;
> +	struct fake_mgr_pdata pdata;
> +	int ret;
> +
> +	mgr_ctx = kunit_kzalloc(test, sizeof(*mgr_ctx), GFP_KERNEL);
> +	if (!mgr_ctx) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	mgr_ctx->pdev = platform_device_alloc(FAKE_FPGA_MGR_DEV_NAME,
> +					      PLATFORM_DEVID_AUTO);
> +	if (!mgr_ctx->pdev) {
> +		kunit_err(test, "Fake FPGA manager device allocation failed\n");
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	pdata.test = test;
> +	pdata.stats = &mgr_ctx->stats;
> +	platform_device_add_data(mgr_ctx->pdev, &pdata, sizeof(pdata));
> +
> +	mgr_ctx->pdev->dev.parent = parent;
> +	ret = platform_device_add(mgr_ctx->pdev);
> +	if (ret) {
> +		kunit_err(test, "Fake FPGA manager device add failed\n");
> +		goto err_pdev;
> +	}

Is it possible also move this function in fpga-test?

> +
> +	mgr_ctx->mgr = platform_get_drvdata(mgr_ctx->pdev);

If moved out, platform_get_drvdata() from outside the driver is not a
good idea, maybe use class_find_device() to find the mgr out.

> +
> +	kunit_info(test, "Fake FPGA manager registered\n");
> +
> +	return mgr_ctx;
> +
> +err_pdev:
> +	platform_device_put(mgr_ctx->pdev);
> +err_mem:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_register);
> +
> +/**
> + * fake_fpga_mgr_unregister() - unregister a fake FPGA manager.
> + * @mgr_ctx: fake FPGA manager context data structure.
> + */
> +void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx)
> +{
> +	struct fake_mgr_priv *priv;
> +	struct kunit *test;
> +
> +	if (!mgr_ctx)
> +		return;
> +
> +	priv = mgr_ctx->mgr->priv;
> +	test = priv->pdata->test;
> +
> +	platform_device_unregister(mgr_ctx->pdev);
> +
> +	kunit_info(test, "Fake FPGA manager unregistered\n");
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_unregister);
> +
> +static int fake_fpga_mgr_probe(struct platform_device *pdev)
> +{
> +	struct device *dev;
> +	struct fake_mgr_pdata *pdata;
> +	struct fpga_manager *mgr;
> +	struct fake_mgr_priv *priv;
> +
> +	dev = &pdev->dev;
> +	pdata = dev_get_platdata(dev);
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdata = pdata;
> +
> +	mgr = devm_fpga_mgr_register(dev, "Fake FPGA Manager",
> +				     &fake_fpga_mgr_ops, priv);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
> +
> +	platform_set_drvdata(pdev, mgr);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver fake_fpga_mgr_drv = {
> +	.driver = {
> +		.name = FAKE_FPGA_MGR_DEV_NAME
> +	},
> +	.probe = fake_fpga_mgr_probe,
> +};
> +
> +module_platform_driver(fake_fpga_mgr_drv);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/fpga/tests/fake-fpga-mgr.h b/drivers/fpga/tests/fake-fpga-mgr.h
> new file mode 100644
> index 000000000000..282c20d8e40c
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-mgr.h
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for the fake FPGA manager
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#ifndef __FPGA_FAKE_MGR_H
> +#define __FPGA_FAKE_MGR_H
> +
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <kunit/test.h>
> +#include <linux/fpga/fpga-mgr.h>
> +
> +#define FPGA_IMG_BLOCK		1024
> +#define TEST_HEADER_MAGIC	0x3f
> +#define TEST_HEADER_SIZE	FPGA_IMG_BLOCK
> +
> +struct fake_fpga_mgr_stats {
> +	u32 prog_count;
> +	bool header_done;
> +	u32 op_parse_header_seq;
> +	u32 op_write_init_seq;
> +	u32 op_write_seq;
> +	u32 op_write_complete_seq;
> +	enum fpga_mgr_states op_parse_header_state;
> +	enum fpga_mgr_states op_write_init_state;
> +	enum fpga_mgr_states op_write_state;
> +	enum fpga_mgr_states op_write_complete_state;
> +};
> +
> +/**
> + * struct fake_fpga_mgr - fake FPGA manager context data structure
> + *
> + * @mgr: FPGA manager.
> + * @pdev: platform device of the FPGA manager.
> + * @stats: info from the low level driver.
> + */
> +struct fake_fpga_mgr {
> +	struct fpga_manager *mgr;
> +	struct platform_device *pdev;
> +	struct fake_fpga_mgr_stats stats;
> +};
> +
> +struct fake_fpga_mgr *
> +fake_fpga_mgr_register(struct kunit *test, struct device *parent);
> +
> +void fake_fpga_mgr_unregister(struct fake_fpga_mgr *mgr_ctx);
> +
> +#endif /* __FPGA_FAKE_MGR_H */
> -- 
> 2.40.1
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 2/4] fpga: add fake FPGA bridge
  2023-05-11 14:19 ` [RFC PATCH v5 2/4] fpga: add fake FPGA bridge Marco Pagani
@ 2023-05-13 15:46   ` Xu Yilun
  0 siblings, 0 replies; 12+ messages in thread
From: Xu Yilun @ 2023-05-13 15:46 UTC (permalink / raw)
  To: Marco Pagani; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga

On 2023-05-11 at 16:19:20 +0200, Marco Pagani wrote:
> Add fake FPGA bridge platform driver with support functions. This module
> is part of the KUnit tests for the FPGA subsystem.
> 
> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> ---
>  drivers/fpga/tests/fake-fpga-bridge.c | 203 ++++++++++++++++++++++++++
>  drivers/fpga/tests/fake-fpga-bridge.h |  40 +++++
>  2 files changed, 243 insertions(+)
>  create mode 100644 drivers/fpga/tests/fake-fpga-bridge.c
>  create mode 100644 drivers/fpga/tests/fake-fpga-bridge.h
> 
> diff --git a/drivers/fpga/tests/fake-fpga-bridge.c b/drivers/fpga/tests/fake-fpga-bridge.c
> new file mode 100644
> index 000000000000..e5db0a809ee3
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-bridge.c
> @@ -0,0 +1,203 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for the fake FPGA bridge
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/fpga/fpga-bridge.h>
> +#include <kunit/test.h>
> +
> +#include "fake-fpga-bridge.h"
> +
> +#define FAKE_FPGA_BRIDGE_DEV_NAME	"fake_fpga_bridge"
> +
> +struct fake_bridge_pdata {
> +	struct kunit *test;
> +	struct fake_fpga_bridge_stats *stats;
> +};
> +
> +struct fake_bridge_priv {
> +	bool state;
> +	struct fake_bridge_pdata *pdata;
> +};
> +
> +static int op_enable_show(struct fpga_bridge *bridge)
> +{
> +	struct fake_bridge_priv *priv;
> +
> +	priv = bridge->priv;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: enable_show\n",
> +		   bridge->dev.id);
> +
> +	return priv->state;
> +}
> +
> +static int op_enable_set(struct fpga_bridge *bridge, bool enable)
> +{
> +	struct fake_bridge_priv *priv;
> +
> +	priv = bridge->priv;
> +
> +	if (enable && !priv->state)
> +		priv->pdata->stats->cycles_count++;
> +
> +	priv->state = enable;
> +	priv->pdata->stats->enable = priv->state;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: enable_set: %d\n",
> +		   bridge->dev.id, enable);
> +
> +	return 0;
> +}
> +
> +static void op_remove(struct fpga_bridge *bridge)
> +{
> +	struct fake_bridge_priv *priv;
> +
> +	priv = bridge->priv;
> +
> +	kunit_info(priv->pdata->test, "Fake FPGA bridge %u: remove\n",
> +		   bridge->dev.id);
> +}
> +
> +static const struct fpga_bridge_ops fake_fpga_bridge_ops = {
> +	.enable_show = op_enable_show,
> +	.enable_set = op_enable_set,
> +	.fpga_bridge_remove = op_remove,
> +};
> +
> +/**
> + * fake_fpga_bridge_register() - register a fake FPGA bridge.
> + * @test: KUnit test context object.
> + * @parent: parent device.
> + *
> + * Return: pointer to a new fake FPGA bridge on success, an ERR_PTR()
> + * encoded error code on failure.
> + */
> +struct fake_fpga_bridge *
> +fake_fpga_bridge_register(struct kunit *test, struct device *parent)
> +{
> +	struct fake_fpga_bridge *bridge_ctx;
> +	struct fake_bridge_pdata pdata;
> +	struct fake_bridge_priv *priv;
> +	int ret;
> +
> +	bridge_ctx = kunit_kzalloc(test, sizeof(*bridge_ctx), GFP_KERNEL);
> +	if (!bridge_ctx) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	bridge_ctx->pdev = platform_device_alloc(FAKE_FPGA_BRIDGE_DEV_NAME,
> +						 PLATFORM_DEVID_AUTO);
> +	if (!bridge_ctx->pdev) {
> +		pr_err("Fake FPGA bridge device allocation failed\n");
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	pdata.test = test;
> +	pdata.stats = &bridge_ctx->stats;
> +	platform_device_add_data(bridge_ctx->pdev, &pdata, sizeof(pdata));
> +
> +	bridge_ctx->pdev->dev.parent = parent;
> +	ret = platform_device_add(bridge_ctx->pdev);
> +	if (ret) {
> +		pr_err("Fake FPGA bridge device add failed\n");
> +		goto err_pdev;
> +	}
> +
> +	bridge_ctx->bridge = platform_get_drvdata(bridge_ctx->pdev);
> +
> +	priv = bridge_ctx->bridge->priv;
> +	bridge_ctx->stats.enable = priv->state;
> +
> +	kunit_info(test, "Fake FPGA bridge %u: registered\n",
> +		   bridge_ctx->bridge->dev.id);
> +
> +	return bridge_ctx;
> +
> +err_pdev:
> +	platform_device_put(bridge_ctx->pdev);
> +err_mem:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_bridge_register);

Same as fake_fpga_mgr.

Thanks,
Yilun

> +
> +/**
> + * fake_fpga_bridge_unregister() - unregister a fake FPGA bridge.
> + * @bridge_ctx: fake FPGA bridge context data structure.
> + */
> +void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx)
> +{
> +	struct fake_bridge_priv *priv;
> +	struct kunit *test;
> +	u32 id;
> +
> +	if (!bridge_ctx)
> +		return;
> +
> +	id = bridge_ctx->bridge->dev.id;
> +	priv = bridge_ctx->bridge->priv;
> +	test = priv->pdata->test;
> +
> +	platform_device_unregister(bridge_ctx->pdev);
> +
> +	kunit_info(test, "Fake FPGA bridge %u: unregistered\n", id);
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_bridge_unregister);
> +
> +static int fake_fpga_bridge_probe(struct platform_device *pdev)
> +{
> +	struct device *dev;
> +	struct fake_bridge_pdata *pdata;
> +	struct fpga_bridge *bridge;
> +	struct fake_bridge_priv *priv;
> +
> +	dev = &pdev->dev;
> +	pdata = dev_get_platdata(dev);
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdata = pdata;
> +	priv->state = true;
> +
> +	bridge = fpga_bridge_register(dev, "Fake FPGA Bridge",
> +				      &fake_fpga_bridge_ops, priv);
> +	if (IS_ERR(bridge))
> +		return PTR_ERR(bridge);
> +
> +	platform_set_drvdata(pdev, bridge);
> +
> +	return 0;
> +}
> +
> +static int fake_fpga_bridge_remove(struct platform_device *pdev)
> +{
> +	struct fpga_bridge *bridge = platform_get_drvdata(pdev);
> +
> +	fpga_bridge_unregister(bridge);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver fake_fpga_bridge_drv = {
> +	.driver = {
> +		.name = FAKE_FPGA_BRIDGE_DEV_NAME
> +	},
> +	.probe = fake_fpga_bridge_probe,
> +	.remove = fake_fpga_bridge_remove,
> +};
> +
> +module_platform_driver(fake_fpga_bridge_drv);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/fpga/tests/fake-fpga-bridge.h b/drivers/fpga/tests/fake-fpga-bridge.h
> new file mode 100644
> index 000000000000..3ecfab7e2890
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-bridge.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for the fake FPGA bridge
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#ifndef __FPGA_FAKE_BRIDGE_H
> +#define __FPGA_FAKE_BRIDGE_H
> +
> +#include <linux/types.h>
> +#include <linux/platform_device.h>
> +#include <kunit/test.h>
> +
> +struct fake_fpga_bridge_stats {
> +	bool enable;
> +	u32 cycles_count;
> +};
> +
> +/**
> + * struct fake_fpga_bridge - fake FPGA bridge context data structure
> + *
> + * @bridge: FPGA bridge.
> + * @pdev: platform device of the FPGA bridge.
> + * @stats: info from the low level driver.
> + */
> +struct fake_fpga_bridge {
> +	struct fpga_bridge *bridge;
> +	struct platform_device *pdev;
> +	struct fake_fpga_bridge_stats stats;
> +};
> +
> +struct fake_fpga_bridge *
> +fake_fpga_bridge_register(struct kunit *test, struct device *parent);
> +
> +void fake_fpga_bridge_unregister(struct fake_fpga_bridge *bridge_ctx);
> +
> +#endif /* __FPGA_FAKE_BRIDGE_H */
> -- 
> 2.40.1
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 3/4] fpga: add fake FPGA region
  2023-05-11 14:19 ` [RFC PATCH v5 3/4] fpga: add fake FPGA region Marco Pagani
@ 2023-05-13 16:05   ` Xu Yilun
  0 siblings, 0 replies; 12+ messages in thread
From: Xu Yilun @ 2023-05-13 16:05 UTC (permalink / raw)
  To: Marco Pagani; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga

On 2023-05-11 at 16:19:21 +0200, Marco Pagani wrote:
> Add fake FPGA region platform driver with support functions. This module
> is part of the KUnit tests for the FPGA subsystem.
> 
> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> ---
>  drivers/fpga/tests/fake-fpga-region.c | 245 ++++++++++++++++++++++++++
>  drivers/fpga/tests/fake-fpga-region.h |  40 +++++
>  2 files changed, 285 insertions(+)
>  create mode 100644 drivers/fpga/tests/fake-fpga-region.c
>  create mode 100644 drivers/fpga/tests/fake-fpga-region.h
> 
> diff --git a/drivers/fpga/tests/fake-fpga-region.c b/drivers/fpga/tests/fake-fpga-region.c
> new file mode 100644
> index 000000000000..020c3ad13812
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-region.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for the fake FPGA region
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/list.h>
> +#include <linux/platform_device.h>
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/fpga/fpga-region.h>
> +#include <linux/fpga/fpga-bridge.h>
> +#include <kunit/test.h>
> +
> +#include "fake-fpga-region.h"
> +
> +#define FAKE_FPGA_REGION_DEV_NAME	"fake_fpga_region"
> +
> +struct fake_region_pdata {
> +	struct kunit *test;
> +	struct fpga_manager *mgr;
> +};
> +
> +struct bridge_elem {
> +	struct fpga_bridge *bridge;
> +	struct list_head node;
> +};
> +
> +struct fake_region_priv {
> +	struct list_head bridge_list;
> +	struct fake_region_pdata *pdata;
> +};
> +
> +/**
> + * fake_fpga_region_register() - register a fake FPGA region.
> + * @mgr: associated FPGA manager.
> + * @parent: parent device.
> + * @test: KUnit test context object.
> + *
> + * Return: pointer to a new fake FPGA region on success, an ERR_PTR() encoded
> + * error code on failure.
> + */
> +struct fake_fpga_region *
> +fake_fpga_region_register(struct kunit *test, struct fpga_manager *mgr,
> +			  struct device *parent)
> +{
> +	struct fake_fpga_region *region_ctx;
> +	struct fake_region_pdata pdata;
> +	int ret;
> +
> +	region_ctx = kunit_kzalloc(test, sizeof(*region_ctx), GFP_KERNEL);
> +	if (!region_ctx) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	region_ctx->pdev = platform_device_alloc(FAKE_FPGA_REGION_DEV_NAME,
> +						 PLATFORM_DEVID_AUTO);
> +	if (!region_ctx->pdev) {
> +		pr_err("Fake FPGA region device allocation failed\n");
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	pdata.mgr = mgr;
> +	pdata.test = test;
> +	platform_device_add_data(region_ctx->pdev, &pdata, sizeof(pdata));
> +
> +	region_ctx->pdev->dev.parent = parent;
> +	ret = platform_device_add(region_ctx->pdev);
> +	if (ret) {
> +		pr_err("Fake FPGA region device add failed\n");
> +		goto err_pdev;
> +	}
> +
> +	region_ctx->region = platform_get_drvdata(region_ctx->pdev);
> +
> +	kunit_info(test, "Fake FPGA region %u: registered\n",
> +		   region_ctx->region->dev.id);
> +
> +	return region_ctx;
> +
> +err_pdev:
> +	platform_device_put(region_ctx->pdev);
> +err_mem:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_region_register);

Same as fake_fpga_mgr, move it to fpga-test? Same as below exported
functions.

> +
> +/**
> + * fake_fpga_region_unregister() - unregister a fake FPGA region.
> + * @region_ctx: fake FPGA region context data structure.
> + */
> +void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx)
> +{
> +	struct fake_region_priv *priv;
> +	struct kunit *test;
> +	u32 id;
> +
> +	if (!region_ctx)
> +		return;
> +
> +	id = region_ctx->region->dev.id;
> +	priv = region_ctx->region->priv;
> +	test = priv->pdata->test;
> +
> +	platform_device_unregister(region_ctx->pdev);
> +
> +	kunit_info(test, "Fake FPGA region %u: unregistered\n", id);
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_region_unregister);
> +
> +/**
> + * fake_fpga_region_add_bridge() - add a bridge to a fake FPGA region.
> + * @region_ctx: fake FPGA region context data structure.
> + * @bridge: FPGA bridge.
> + *
> + * Return: 0 if registration succeeded, an error code otherwise.
> + */
> +int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx,
> +				struct fpga_bridge *bridge)
> +{
> +	struct fake_region_priv *priv;
> +	struct bridge_elem *elem;
> +
> +	priv = region_ctx->region->priv;
> +
> +	elem = devm_kzalloc(&region_ctx->pdev->dev, sizeof(*elem), GFP_KERNEL);
> +	if (!elem)
> +		return -ENOMEM;
> +
> +	/* Add bridge to the list of bridges in the private context */
> +	elem->bridge = bridge;
> +	list_add(&elem->node, &priv->bridge_list);
> +
> +	kunit_info(priv->pdata->test, "Bridge: %u added to fake FPGA region: %u\n",
> +		   bridge->dev.id, region_ctx->region->dev.id);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_region_add_bridge);

I remember in previous version we are going to provide the bridges in
platform data.

> +
> +int fake_fpga_region_program(struct fake_fpga_region *region_ctx)
> +{
> +	int ret;
> +
> +	ret = fpga_region_program_fpga(region_ctx->region);
> +
> +	/* fpga_region_program_fpga() already puts the bridges in case of errors */
> +	if (!ret)
> +		fpga_bridges_put(&region_ctx->region->bridge_list);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(fake_fpga_region_program);

Move it to fpga-test if possible. Maintain all the region/mgr/bridge_ctx
in fpga_test.

Thanks,
Yilun

> +
> +static int fake_region_get_bridges(struct fpga_region *region)
> +{
> +	struct fake_region_priv *priv;
> +	struct bridge_elem *elem;
> +	int ret;
> +
> +	priv = region->priv;
> +
> +	/* Copy the list of bridges from the private context to the region */
> +	list_for_each_entry(elem, &priv->bridge_list, node) {
> +		ret = fpga_bridge_get_to_list(elem->bridge->dev.parent,
> +					      region->info,
> +					      &region->bridge_list);
> +		if (ret)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int fake_fpga_region_probe(struct platform_device *pdev)
> +{
> +	struct device *dev;
> +	struct fpga_region *region;
> +	struct fpga_manager *mgr;
> +	struct fake_region_pdata *pdata;
> +	struct fake_region_priv *priv;
> +	struct fpga_region_info info;
> +
> +	dev = &pdev->dev;
> +	pdata = dev_get_platdata(dev);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "Fake FPGA region probe: missing platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	mgr = fpga_mgr_get(pdata->mgr->dev.parent);
> +	if (IS_ERR(mgr))
> +		return PTR_ERR(mgr);
> +
> +	priv->pdata = pdata;
> +	INIT_LIST_HEAD(&priv->bridge_list);
> +
> +	memset(&info, 0, sizeof(info));
> +	info.priv = priv;
> +	info.mgr = mgr;
> +	info.get_bridges = fake_region_get_bridges;
> +
> +	region = fpga_region_register_full(dev, &info);
> +	if (IS_ERR(region)) {
> +		fpga_mgr_put(mgr);
> +		return PTR_ERR(region);
> +	}
> +
> +	platform_set_drvdata(pdev, region);
> +
> +	return 0;
> +}
> +
> +static int fake_fpga_region_remove(struct platform_device *pdev)
> +{
> +	struct fpga_region *region = platform_get_drvdata(pdev);
> +	struct fpga_manager *mgr = region->mgr;
> +
> +	fpga_mgr_put(mgr);
> +	fpga_region_unregister(region);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver fake_fpga_region_drv = {
> +	.driver = {
> +		.name = FAKE_FPGA_REGION_DEV_NAME
> +	},
> +	.probe = fake_fpga_region_probe,
> +	.remove = fake_fpga_region_remove,
> +};
> +
> +module_platform_driver(fake_fpga_region_drv);
> +
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/fpga/tests/fake-fpga-region.h b/drivers/fpga/tests/fake-fpga-region.h
> new file mode 100644
> index 000000000000..c72992cbb218
> --- /dev/null
> +++ b/drivers/fpga/tests/fake-fpga-region.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for the fake FPGA region
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#ifndef __FPGA_FAKE_RGN_H
> +#define __FPGA_FAKE_RGN_H
> +
> +#include <linux/platform_device.h>
> +#include <kunit/test.h>
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/fpga/fpga-bridge.h>
> +
> +/**
> + * struct fake_fpga_region - fake FPGA region context data structure
> + *
> + * @region: FPGA region.
> + * @pdev: platform device of the FPGA region.
> + */
> +struct fake_fpga_region {
> +	struct fpga_region *region;
> +	struct platform_device *pdev;
> +};
> +
> +struct fake_fpga_region *
> +fake_fpga_region_register(struct kunit *test, struct fpga_manager *mgr,
> +			  struct device *parent);
> +
> +int fake_fpga_region_add_bridge(struct fake_fpga_region *region_ctx,
> +				struct fpga_bridge *bridge);
> +
> +int fake_fpga_region_program(struct fake_fpga_region *region_ctx);
> +
> +void fake_fpga_region_unregister(struct fake_fpga_region *region_ctx);
> +
> +#endif /* __FPGA_FAKE_RGN_H */
> -- 
> 2.40.1
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 4/4] fpga: add initial KUnit test suites
  2023-05-11 14:19 ` [RFC PATCH v5 4/4] fpga: add initial KUnit test suites Marco Pagani
@ 2023-05-13 17:40   ` Xu Yilun
  2023-05-15 17:24     ` Marco Pagani
  0 siblings, 1 reply; 12+ messages in thread
From: Xu Yilun @ 2023-05-13 17:40 UTC (permalink / raw)
  To: Marco Pagani; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga

On 2023-05-11 at 16:19:22 +0200, Marco Pagani wrote:
> Introduce initial KUnit tests for the FPGA subsystem. Tests are organized
> into three test suites. The first suite tests the FPGA Manager.
> The second suite tests the FPGA Bridge. Finally, the last test suite
> models a complete FPGA platform and tests static and partial reconfiguration.
> 
> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> ---
>  drivers/fpga/Kconfig            |   2 +
>  drivers/fpga/Makefile           |   3 +
>  drivers/fpga/tests/.kunitconfig |   5 +
>  drivers/fpga/tests/Kconfig      |  11 +
>  drivers/fpga/tests/Makefile     |   6 +
>  drivers/fpga/tests/fpga-test.c  | 551 ++++++++++++++++++++++++++++++++
>  6 files changed, 578 insertions(+)
>  create mode 100644 drivers/fpga/tests/.kunitconfig
>  create mode 100644 drivers/fpga/tests/Kconfig
>  create mode 100644 drivers/fpga/tests/Makefile
>  create mode 100644 drivers/fpga/tests/fpga-test.c
> 
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 0a00763b9f28..2f689ac4ba3a 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -276,4 +276,6 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI
>  	  FPGA manager driver support for Lattice FPGAs programming over slave
>  	  SPI sysCONFIG interface.
>  
> +source "drivers/fpga/tests/Kconfig"
> +
>  endif # FPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 72e554b4d2f7..352a2612623e 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -55,3 +55,6 @@ obj-$(CONFIG_FPGA_DFL_NIOS_INTEL_PAC_N3000)	+= dfl-n3000-nios.o
>  
>  # Drivers for FPGAs which implement DFL
>  obj-$(CONFIG_FPGA_DFL_PCI)		+= dfl-pci.o
> +
> +# KUnit tests
> +obj-$(CONFIG_FPGA_KUNIT_TESTS)		+= tests/
> diff --git a/drivers/fpga/tests/.kunitconfig b/drivers/fpga/tests/.kunitconfig
> new file mode 100644
> index 000000000000..a1c2a2974c39
> --- /dev/null
> +++ b/drivers/fpga/tests/.kunitconfig
> @@ -0,0 +1,5 @@
> +CONFIG_KUNIT=y
> +CONFIG_FPGA=y
> +CONFIG_FPGA_REGION=y
> +CONFIG_FPGA_BRIDGE=y
> +CONFIG_FPGA_KUNIT_TESTS=y
> diff --git a/drivers/fpga/tests/Kconfig b/drivers/fpga/tests/Kconfig
> new file mode 100644
> index 000000000000..1cbea75dd29b
> --- /dev/null
> +++ b/drivers/fpga/tests/Kconfig
> @@ -0,0 +1,11 @@
> +config FPGA_KUNIT_TESTS
> +	tristate "KUnit test for the FPGA subsystem" if !KUNIT_ALL_TESTS
> +	depends on FPGA && FPGA_REGION && FPGA_BRIDGE && KUNIT
> +	default KUNIT_ALL_TESTS
> +        help
> +          This builds unit tests for the FPGA subsystem
> +
> +          For more information on KUnit and unit tests in general,
> +          please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> +          If unsure, say N.
> diff --git a/drivers/fpga/tests/Makefile b/drivers/fpga/tests/Makefile
> new file mode 100644
> index 000000000000..0b052570659b
> --- /dev/null
> +++ b/drivers/fpga/tests/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-mgr.o
> +obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-region.o
> +obj-$(CONFIG_FPGA_KUNIT_TESTS) += fake-fpga-bridge.o
> +obj-$(CONFIG_FPGA_KUNIT_TESTS) += fpga-test.o
> diff --git a/drivers/fpga/tests/fpga-test.c b/drivers/fpga/tests/fpga-test.c
> new file mode 100644
> index 000000000000..929684c1337e
> --- /dev/null
> +++ b/drivers/fpga/tests/fpga-test.c
> @@ -0,0 +1,551 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * KUnit tests for the FPGA subsystem
> + *
> + * Copyright (C) 2023 Red Hat, Inc.
> + *
> + * Author: Marco Pagani <marpagan@redhat.com>
> + */
> +
> +#include <kunit/test.h>
> +#include <linux/list.h>
> +#include <linux/platform_device.h>
> +#include <linux/scatterlist.h>
> +
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/fpga/fpga-region.h>
> +#include <linux/fpga/fpga-bridge.h>
> +
> +#include "fake-fpga-region.h"
> +#include "fake-fpga-bridge.h"
> +#include "fake-fpga-mgr.h"
> +
> +#define STATIC_IMG_BLOCKS	16
> +#define STATIC_IMG_SIZE		(FPGA_IMG_BLOCK * STATIC_IMG_BLOCKS)
> +
> +#define PARTIAL_IMG_BLOCKS	4
> +#define PARTIAL_IMG_SIZE	(FPGA_IMG_BLOCK * PARTIAL_IMG_BLOCKS)
> +
> +/**
> + * fill_test_header() - fill a buffer with the test header data.
> + * @buf: image buffer.
> + * @count: length of the header.
> + */
> +static void fill_test_header(u8 *buf, size_t count)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < count; i++)
> +		buf[i] = TEST_HEADER_MAGIC;
> +}
> +
> +/**
> + * buf_img_alloc() - Allocate a test FPGA image using a buffer.
> + * @test: KUnit test context object.
> + * @dev: owning device.
> + * @size: image size.
> + *
> + * Return: pointer to a struct fpga_image_info or NULL on failure.
> + */
> +static struct fpga_image_info *buf_img_alloc(struct kunit *test, struct device *dev,
> +					     size_t size)
> +{
> +	struct fpga_image_info *img_info;
> +	char *img_buf;
> +
> +	img_buf = kunit_kzalloc(test, size, GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf);
> +	fill_test_header(img_buf, TEST_HEADER_SIZE);
> +
> +	img_info = fpga_image_info_alloc(dev);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
> +
> +	img_info->count = size;
> +	img_info->buf = img_buf;
> +	img_info->header_size = TEST_HEADER_SIZE;
> +
> +	kunit_info(test, "FPGA image allocated in a buffer, size: %zu\n", size);
> +
> +	return img_info;
> +}
> +
> +/**
> + * sgt_img_alloc() - Allocate a test FPGA image using a scatter gather table.
> + * @test: KUnit test context object.
> + * @dev: owning device.
> + * @size: image size.
> + *
> + * Return: pointer to a struct fpga_image_info or NULL on failure.
> + */
> +static struct fpga_image_info *sgt_img_alloc(struct kunit *test, struct device *dev,
> +					     size_t size)
> +{
> +	struct fpga_image_info *img_info;
> +	char *img_buf;
> +	struct sg_table *sgt;
> +	int ret;
> +
> +	img_buf = kunit_kzalloc(test, size, GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_buf);
> +	fill_test_header(img_buf, TEST_HEADER_SIZE);
> +
> +	sgt = kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL);
> +	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +	sg_init_one(sgt->sgl, img_buf, size);
> +
> +	img_info = fpga_image_info_alloc(dev);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
> +
> +	img_info->sgt = sgt;
> +	img_info->header_size = TEST_HEADER_SIZE;
> +
> +	kunit_info(test, "FPGA image allocated in a scatter gather table, size: %zu\n",
> +		   size);
> +
> +	return img_info;
> +}
> +
> +/**
> + * img_free() - Free a test FPGA image
> + * @img_info: fpga image information struct.
> + *
> + */
> +static void img_free(struct fpga_image_info *img_info)
> +{
> +	if (!img_info)
> +		return;
> +
> +	if (img_info->sgt)
> +		sg_free_table(img_info->sgt);
> +
> +	fpga_image_info_free(img_info);
> +}
> +
> +/**
> + * fake_fpga_mgr_check_write() - check if the programming sequence is correct.
> + * @test: KUnit test context object.
> + * @mgr_ctx: fake FPGA manager context data structure.
> + */
> +static void fake_fpga_mgr_check_write(struct kunit *test,
> +				      const struct fake_fpga_mgr *mgr_ctx)
> +{
> +	KUNIT_EXPECT_TRUE(test, mgr_ctx->stats.header_done);
> +
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_parse_header_state,
> +			FPGA_MGR_STATE_PARSE_HEADER);
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_init_state,
> +			FPGA_MGR_STATE_WRITE_INIT);
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_state,
> +			FPGA_MGR_STATE_WRITE);
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_complete_state,
> +			FPGA_MGR_STATE_WRITE_COMPLETE);
> +
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_init_seq,
> +			mgr_ctx->stats.op_parse_header_seq + 1);
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_seq,
> +			mgr_ctx->stats.op_parse_header_seq + 2);
> +	KUNIT_EXPECT_EQ(test, mgr_ctx->stats.op_write_complete_seq,
> +			mgr_ctx->stats.op_parse_header_seq + 3);
> +}
> +
> +static int fpga_mgr_test_init(struct kunit *test)
> +{
> +	struct fake_fpga_mgr *mgr_ctx;
> +
> +	mgr_ctx = fake_fpga_mgr_register(test, NULL);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(mgr_ctx));
> +
> +	test->priv = mgr_ctx;
> +
> +	return 0;
> +}
> +
> +static void fpga_mgr_test_img_load_buf(struct kunit *test)
> +{
> +	struct fake_fpga_mgr *mgr_ctx;
> +	struct fpga_image_info *img_info;
> +	int ret;
> +
> +	mgr_ctx = test->priv;
> +
> +	/* Allocate an FPGA image using a buffer */
> +	img_info = buf_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
> +
> +	KUNIT_EXPECT_EQ(test, 0, mgr_ctx->stats.prog_count);
> +
> +	ret = fpga_mgr_load(mgr_ctx->mgr, img_info);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, mgr_ctx);
> +
> +	KUNIT_EXPECT_EQ(test, 1, mgr_ctx->stats.prog_count);
> +
> +	img_free(img_info);
> +}
> +
> +static void fpga_mgr_test_img_load_sgt(struct kunit *test)
> +{
> +	struct fake_fpga_mgr *mgr_ctx;
> +	struct fpga_image_info *img_info;
> +	int ret;
> +
> +	mgr_ctx = test->priv;
> +
> +	/* Allocate an FPGA image using a scatter gather table */
> +	img_info = sgt_img_alloc(test, &mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
> +
> +	KUNIT_EXPECT_EQ(test, 0, mgr_ctx->stats.prog_count);
> +
> +	ret = fpga_mgr_load(mgr_ctx->mgr, img_info);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, mgr_ctx);
> +
> +	KUNIT_EXPECT_EQ(test, 1, mgr_ctx->stats.prog_count);
> +
> +	img_free(img_info);
> +}
> +
> +static void fpga_mgr_test_exit(struct kunit *test)
> +{
> +	struct fake_fpga_mgr *mgr_ctx;
> +
> +	mgr_ctx = test->priv;
> +
> +	if (mgr_ctx)
> +		fake_fpga_mgr_unregister(mgr_ctx);
> +}
> +
> +static struct kunit_case fpga_mgr_test_cases[] = {
> +	KUNIT_CASE(fpga_mgr_test_img_load_buf),
> +	KUNIT_CASE(fpga_mgr_test_img_load_sgt),
> +	{}
> +};
> +
> +static struct kunit_suite fpga_mgr_suite = {
> +	.name = "fpga_mgr",
> +	.init = fpga_mgr_test_init,
> +	.exit = fpga_mgr_test_exit,
> +	.test_cases = fpga_mgr_test_cases,
> +};
> +
> +static int fpga_bridge_test_init(struct kunit *test)
> +{
> +	struct fake_fpga_bridge *bridge_ctx;
> +
> +	bridge_ctx = fake_fpga_bridge_register(test, NULL);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_ctx));
> +
> +	test->priv = bridge_ctx;
> +
> +	return 0;
> +}
> +
> +static void fpga_bridge_test_exit(struct kunit *test)
> +{
> +	struct fake_fpga_bridge *bridge_ctx;
> +
> +	bridge_ctx = test->priv;
> +
> +	if (bridge_ctx)
> +		fake_fpga_bridge_unregister(bridge_ctx);
> +}
> +
> +static void fpga_bridge_test_toggle(struct kunit *test)
> +{
> +	struct fake_fpga_bridge *bridge_ctx;
> +	int ret;
> +
> +	bridge_ctx = test->priv;
> +
> +	KUNIT_EXPECT_TRUE(test, bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, bridge_ctx->stats.cycles_count);
> +
> +	ret = fpga_bridge_disable(bridge_ctx->bridge);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +	KUNIT_EXPECT_FALSE(test, bridge_ctx->stats.enable);
> +
> +	ret = fpga_bridge_enable(bridge_ctx->bridge);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +	KUNIT_EXPECT_TRUE(test, bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, bridge_ctx->stats.cycles_count);
> +}
> +
> +static void fpga_bridge_test_get_put_list(struct kunit *test)
> +{
> +	struct list_head bridge_list;
> +	struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx;
> +	int ret;
> +
> +	bridge_0_ctx = test->priv;
> +
> +	/* Register another bridge for this test */
> +	bridge_1_ctx = fake_fpga_bridge_register(test, NULL);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx));

I think bridge_1 could also be initialized in test_init together with
bridge_0

> +
> +	INIT_LIST_HEAD(&bridge_list);
> +
> +	/* Get bridge_0 and add it to the list */
> +	ret = fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL,
> +				      &bridge_list);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +
> +	KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge,
> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));

Should operate on bridge_0_ctx?

> +
> +	/* Get bridge_1 and add it to the list */
> +	ret = fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL,
> +				      &bridge_list);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +
> +	KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge,
> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));

Should operate on bridge_1_ctx?

> +
> +	/* Disable an then enable both bridges from the list */
> +	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);

Why expect enable without fpga_bridges_enable()?

> +	KUNIT_EXPECT_EQ(test, 0, bridge_0_ctx->stats.cycles_count);
> +
> +	KUNIT_EXPECT_TRUE(test, bridge_1_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, bridge_1_ctx->stats.cycles_count);
> +
> +	ret = fpga_bridges_disable(&bridge_list);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +
> +	KUNIT_EXPECT_FALSE(test, bridge_0_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, bridge_0_ctx->stats.cycles_count);
> +
> +	KUNIT_EXPECT_FALSE(test, bridge_1_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, bridge_1_ctx->stats.cycles_count);
> +
> +	ret = fpga_bridges_enable(&bridge_list);
> +	KUNIT_EXPECT_EQ(test, ret, 0);
> +
> +	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, bridge_0_ctx->stats.cycles_count);
> +
> +	KUNIT_EXPECT_TRUE(test, bridge_1_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, bridge_1_ctx->stats.cycles_count);
> +
> +	/* Put and remove both bridges from the list */
> +	fpga_bridges_put(&bridge_list);
> +
> +	KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
> +
> +	fake_fpga_bridge_unregister(bridge_1_ctx);
> +}
> +
> +static struct kunit_case fpga_bridge_test_cases[] = {
> +	KUNIT_CASE(fpga_bridge_test_toggle),
> +	KUNIT_CASE(fpga_bridge_test_get_put_list),
> +	{}
> +};
> +
> +static struct kunit_suite fpga_bridge_suite = {
> +	.name = "fpga_bridge",
> +	.init = fpga_bridge_test_init,
> +	.exit = fpga_bridge_test_exit,
> +	.test_cases = fpga_bridge_test_cases,
> +};
> +
> +struct fpga_base_ctx {
> +	/*
> +	 * Base FPGA layout consisting of a single region
> +	 * controlled by a bridge and the FPGA manager
> +	 */
> +	struct fake_fpga_mgr *mgr_ctx;
> +	struct fake_fpga_bridge *bridge_ctx;
> +	struct fake_fpga_region *region_ctx;
> +};
> +
> +static int fpga_test_init(struct kunit *test)
> +{
> +	struct fpga_base_ctx *base_ctx;
> +	int ret;
> +
> +	base_ctx = kunit_kzalloc(test, sizeof(*base_ctx), GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, base_ctx);
> +	test->priv = base_ctx;
> +
> +	/* Build the base FPGA layout */
> +	base_ctx->mgr_ctx = fake_fpga_mgr_register(test, NULL);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->mgr_ctx));
> +
> +	base_ctx->bridge_ctx = fake_fpga_bridge_register(test, NULL);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->bridge_ctx));
> +
> +	/* The base region a child of the base bridge */
> +	base_ctx->region_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
> +							 &base_ctx->bridge_ctx->bridge->dev);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(base_ctx->region_ctx));
> +
> +	ret = fake_fpga_region_add_bridge(base_ctx->region_ctx, base_ctx->bridge_ctx->bridge);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	kunit_info(test, "FPGA base system built\n");
> +
> +	KUNIT_EXPECT_EQ(test, 0, base_ctx->mgr_ctx->stats.prog_count);
> +	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, base_ctx->bridge_ctx->stats.cycles_count);
> +
> +	return 0;
> +}
> +
> +static void fpga_test_exit(struct kunit *test)
> +{
> +	struct fpga_base_ctx *base_ctx;
> +
> +	base_ctx = test->priv;
> +
> +	if (!base_ctx)
> +		return;
> +
> +	if (base_ctx->region_ctx)
> +		fake_fpga_region_unregister(base_ctx->region_ctx);
> +
> +	if (base_ctx->bridge_ctx)
> +		fake_fpga_bridge_unregister(base_ctx->bridge_ctx);
> +
> +	if (base_ctx->mgr_ctx)
> +		fake_fpga_mgr_unregister(base_ctx->mgr_ctx);
> +}
> +
> +static void fpga_test_static_cfg(struct kunit *test)
> +{
> +	struct fpga_base_ctx *base_ctx;
> +	struct fpga_image_info *buf_img_info;
> +	struct fpga_image_info *sgt_img_info;
> +	int ret;
> +
> +	base_ctx = test->priv;
> +
> +	buf_img_info = buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf_img_info);
> +
> +	/* Configure the FPGA using the image in a buffer */
> +	base_ctx->region_ctx->region->info = buf_img_info;
> +	ret = fake_fpga_region_program(base_ctx->region_ctx);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
> +
> +	KUNIT_EXPECT_EQ(test, 1, base_ctx->mgr_ctx->stats.prog_count);
> +	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, base_ctx->bridge_ctx->stats.cycles_count);
> +
> +	kunit_info(test, "FPGA configuration completed using a buffer image\n");
> +
> +	sgt_img_info = sgt_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev, STATIC_IMG_SIZE);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sgt_img_info);
> +
> +	/* Re-configure the FPGA using the image in a scatter list */
> +	base_ctx->region_ctx->region->info = sgt_img_info;
> +	ret = fake_fpga_region_program(base_ctx->region_ctx);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
> +
> +	KUNIT_EXPECT_EQ(test, 2, base_ctx->mgr_ctx->stats.prog_count);
> +	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 2, base_ctx->bridge_ctx->stats.cycles_count);
> +
> +	kunit_info(test, "FPGA configuration completed using scatter gather table image\n");
> +
> +	img_free(sgt_img_info);
> +}
> +
> +static void fpga_test_partial_rcfg(struct kunit *test)
> +{
> +	struct fpga_base_ctx *base_ctx;
> +	struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx;
> +	struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx;
> +	struct fpga_image_info *partial_img_info;
> +	int ret;
> +
> +	base_ctx = test->priv;
> +
> +	/*
> +	 * Add two reconfigurable sub-regions, each controlled by a bridge. The
> +	 * reconfigurable sub-region are children of their bridges which are,
> +	 * in turn, children of the base region. For simplicity, the same image
> +	 * is used to configure reconfigurable regions
> +	 */
> +	sub_bridge_0_ctx = fake_fpga_bridge_register(test,
> +						     &base_ctx->region_ctx->region->dev);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx));
> +
> +	sub_region_0_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
> +						     &sub_bridge_0_ctx->bridge->dev);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx));
> +
> +	ret = fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->bridge);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	sub_bridge_1_ctx = fake_fpga_bridge_register(test,
> +						     &base_ctx->region_ctx->region->dev);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx));
> +
> +	sub_region_1_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
> +						     &sub_bridge_1_ctx->bridge->dev);
> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx));
> +
> +	ret = fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->bridge);
> +	KUNIT_ASSERT_EQ(test, ret, 0);

I'm wondering if we need to construct the topology for partial
reconfiguration test. The FPGA core doesn't actually check the topology.
It is OK to do partial reconfiguration for a region without parents as
long as its associated FPGA manager device has the capability.

Thanks,
Yilun

> +
> +	/* Allocate a partial image using a buffer */
> +	partial_img_info = buf_img_alloc(test, &base_ctx->mgr_ctx->pdev->dev,
> +					 PARTIAL_IMG_SIZE);
> +	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, partial_img_info);
> +	partial_img_info->flags = FPGA_MGR_PARTIAL_RECONFIG;
> +
> +	/* Re-configure sub-region 0 with the partial image */
> +	sub_region_0_ctx->region->info = partial_img_info;
> +	ret = fake_fpga_region_program(sub_region_0_ctx);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
> +	KUNIT_EXPECT_EQ(test, 1, base_ctx->mgr_ctx->stats.prog_count);
> +
> +	KUNIT_EXPECT_TRUE(test, sub_bridge_0_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, sub_bridge_0_ctx->stats.cycles_count);
> +
> +	/* Re-configure sub-region 1 with the partial image */
> +	sub_region_1_ctx->region->info = partial_img_info;
> +	ret = fake_fpga_region_program(sub_region_1_ctx);
> +	KUNIT_ASSERT_EQ(test, ret, 0);
> +
> +	fake_fpga_mgr_check_write(test, base_ctx->mgr_ctx);
> +	KUNIT_EXPECT_EQ(test, 2, base_ctx->mgr_ctx->stats.prog_count);
> +
> +	KUNIT_EXPECT_TRUE(test, sub_bridge_1_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 1, sub_bridge_1_ctx->stats.cycles_count);
> +
> +	/* Check that the base bridge has not been disabled during reconfiguration */
> +	KUNIT_EXPECT_TRUE(test, base_ctx->bridge_ctx->stats.enable);
> +	KUNIT_EXPECT_EQ(test, 0, base_ctx->bridge_ctx->stats.cycles_count);
> +
> +	img_free(partial_img_info);
> +	fake_fpga_region_unregister(sub_region_0_ctx);
> +	fake_fpga_bridge_unregister(sub_bridge_0_ctx);
> +	fake_fpga_region_unregister(sub_region_1_ctx);
> +	fake_fpga_bridge_unregister(sub_bridge_1_ctx);
> +}
> +
> +static struct kunit_case fpga_test_cases[] = {
> +	KUNIT_CASE(fpga_test_static_cfg),
> +	KUNIT_CASE(fpga_test_partial_rcfg),
> +	{}
> +};
> +
> +static struct kunit_suite fpga_suite = {
> +	.name = "fpga",
> +	.init = fpga_test_init,
> +	.exit = fpga_test_exit,
> +	.test_cases = fpga_test_cases,
> +};
> +
> +kunit_test_suites(&fpga_mgr_suite, &fpga_bridge_suite, &fpga_suite);
> +
> +MODULE_LICENSE("GPL");
> -- 
> 2.40.1
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 4/4] fpga: add initial KUnit test suites
  2023-05-13 17:40   ` Xu Yilun
@ 2023-05-15 17:24     ` Marco Pagani
  2023-05-17 10:04       ` Xu Yilun
  0 siblings, 1 reply; 12+ messages in thread
From: Marco Pagani @ 2023-05-15 17:24 UTC (permalink / raw)
  To: Xu Yilun; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga



On 2023-05-13 19:40, Xu Yilun wrote:
> On 2023-05-11 at 16:19:22 +0200, Marco Pagani wrote:
>> Introduce initial KUnit tests for the FPGA subsystem. Tests are organized
>> into three test suites. The first suite tests the FPGA Manager.
>> The second suite tests the FPGA Bridge. Finally, the last test suite
>> models a complete FPGA platform and tests static and partial reconfiguration.
>>
>> Signed-off-by: Marco Pagani <marpagan@redhat.com>

[...]

>> +static void fpga_bridge_test_get_put_list(struct kunit *test)
>> +{
>> +	struct list_head bridge_list;
>> +	struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx;
>> +	int ret;
>> +
>> +	bridge_0_ctx = test->priv;
>> +
>> +	/* Register another bridge for this test */
>> +	bridge_1_ctx = fake_fpga_bridge_register(test, NULL);
>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx));
> 
> I think bridge_1 could also be initialized in test_init together with
> bridge_0

I can do it, but it would remain unused in the previous test case.
 
>> +
>> +	INIT_LIST_HEAD(&bridge_list);
>> +
>> +	/* Get bridge_0 and add it to the list */
>> +	ret = fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL,
>> +				      &bridge_list);
>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>> +
>> +	KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge,
>> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
> 
> Should operate on bridge_0_ctx?

Yes, sorry. Code and comments are reversed. I'll fix it in the next version.

>> +
>> +	/* Get bridge_1 and add it to the list */
>> +	ret = fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL,
>> +				      &bridge_list);
>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>> +
>> +	KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge,
>> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
> 
> Should operate on bridge_1_ctx?

Same.

>> +
>> +	/* Disable an then enable both bridges from the list */
>> +	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
> 
> Why expect enable without fpga_bridges_enable()?

To check that the bridge is initialized in the correct (enabled) state.

[...]

>> +static void fpga_test_partial_rcfg(struct kunit *test)
>> +{
>> +	struct fpga_base_ctx *base_ctx;
>> +	struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx;
>> +	struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx;
>> +	struct fpga_image_info *partial_img_info;
>> +	int ret;
>> +
>> +	base_ctx = test->priv;
>> +
>> +	/*
>> +	 * Add two reconfigurable sub-regions, each controlled by a bridge. The
>> +	 * reconfigurable sub-region are children of their bridges which are,
>> +	 * in turn, children of the base region. For simplicity, the same image
>> +	 * is used to configure reconfigurable regions
>> +	 */
>> +	sub_bridge_0_ctx = fake_fpga_bridge_register(test,
>> +						     &base_ctx->region_ctx->region->dev);
>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx));
>> +
>> +	sub_region_0_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
>> +						     &sub_bridge_0_ctx->bridge->dev);
>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx));
>> +
>> +	ret = fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->bridge);
>> +	KUNIT_ASSERT_EQ(test, ret, 0);
>> +
>> +	sub_bridge_1_ctx = fake_fpga_bridge_register(test,
>> +						     &base_ctx->region_ctx->region->dev);
>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx));
>> +
>> +	sub_region_1_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
>> +						     &sub_bridge_1_ctx->bridge->dev);
>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx));
>> +
>> +	ret = fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->bridge);
>> +	KUNIT_ASSERT_EQ(test, ret, 0);
> 
> I'm wondering if we need to construct the topology for partial
> reconfiguration test. The FPGA core doesn't actually check the topology.
> It is OK to do partial reconfiguration for a region without parents as
> long as its associated FPGA manager device has the capability.
> 
> Thanks,
> Yilun

I agree with you. Creating a hierarchical layout is rather unnecessary.

Initially, the idea was to test that all components behave as expected
in a complete setup, e.g., only the bridge of the specific reconfigurable
region gets disabled during programming and then re-enabled.

However, after some iterations, I'm starting to think that it would be
better to restructure the whole test code into a set of self-contained
test modules, one for each core component. 

In that way, each module would contain the implementation of the fake/mock
low-level driver and the related tests. For instance, the manager module
would contain the implementation of the fake manager and the test_img_load_buf
and test_img_load_sgt test cases. Similarly, the bridge module would contain
the fake/mock bridge implementation and the test_toggle and test_get_put_list
cases.

I think that in this way, the code would be simpler and more adherent to the
unit testing methodology. The downside is that making tests that need multiple
components would be more cumbersome and possibly lead to code duplication.
For instance, testing the region's fpga_region_program_fpga() would require
implementing additional local mock/fakes for the manager and bridge.

What do you think?

Thanks,
Marco

[...]


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 4/4] fpga: add initial KUnit test suites
  2023-05-15 17:24     ` Marco Pagani
@ 2023-05-17 10:04       ` Xu Yilun
  2023-05-18 12:43         ` Marco Pagani
  0 siblings, 1 reply; 12+ messages in thread
From: Xu Yilun @ 2023-05-17 10:04 UTC (permalink / raw)
  To: Marco Pagani; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga

On 2023-05-15 at 19:24:07 +0200, Marco Pagani wrote:
> 
> 
> On 2023-05-13 19:40, Xu Yilun wrote:
> > On 2023-05-11 at 16:19:22 +0200, Marco Pagani wrote:
> >> Introduce initial KUnit tests for the FPGA subsystem. Tests are organized
> >> into three test suites. The first suite tests the FPGA Manager.
> >> The second suite tests the FPGA Bridge. Finally, the last test suite
> >> models a complete FPGA platform and tests static and partial reconfiguration.
> >>
> >> Signed-off-by: Marco Pagani <marpagan@redhat.com>
> 
> [...]
> 
> >> +static void fpga_bridge_test_get_put_list(struct kunit *test)
> >> +{
> >> +	struct list_head bridge_list;
> >> +	struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx;
> >> +	int ret;
> >> +
> >> +	bridge_0_ctx = test->priv;
> >> +
> >> +	/* Register another bridge for this test */
> >> +	bridge_1_ctx = fake_fpga_bridge_register(test, NULL);
> >> +	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx));
> > 
> > I think bridge_1 could also be initialized in test_init together with
> > bridge_0
> 
> I can do it, but it would remain unused in the previous test case.
>  
> >> +
> >> +	INIT_LIST_HEAD(&bridge_list);
> >> +
> >> +	/* Get bridge_0 and add it to the list */
> >> +	ret = fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL,
> >> +				      &bridge_list);
> >> +	KUNIT_EXPECT_EQ(test, ret, 0);
> >> +
> >> +	KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge,
> >> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
> > 
> > Should operate on bridge_0_ctx?
> 
> Yes, sorry. Code and comments are reversed. I'll fix it in the next version.
> 
> >> +
> >> +	/* Get bridge_1 and add it to the list */
> >> +	ret = fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL,
> >> +				      &bridge_list);
> >> +	KUNIT_EXPECT_EQ(test, ret, 0);
> >> +
> >> +	KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge,
> >> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
> > 
> > Should operate on bridge_1_ctx?
> 
> Same.
> 
> >> +
> >> +	/* Disable an then enable both bridges from the list */
> >> +	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
> > 
> > Why expect enable without fpga_bridges_enable()?
> 
> To check that the bridge is initialized in the correct (enabled) state.
> 
> [...]
> 
> >> +static void fpga_test_partial_rcfg(struct kunit *test)
> >> +{
> >> +	struct fpga_base_ctx *base_ctx;
> >> +	struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx;
> >> +	struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx;
> >> +	struct fpga_image_info *partial_img_info;
> >> +	int ret;
> >> +
> >> +	base_ctx = test->priv;
> >> +
> >> +	/*
> >> +	 * Add two reconfigurable sub-regions, each controlled by a bridge. The
> >> +	 * reconfigurable sub-region are children of their bridges which are,
> >> +	 * in turn, children of the base region. For simplicity, the same image
> >> +	 * is used to configure reconfigurable regions
> >> +	 */
> >> +	sub_bridge_0_ctx = fake_fpga_bridge_register(test,
> >> +						     &base_ctx->region_ctx->region->dev);
> >> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx));
> >> +
> >> +	sub_region_0_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
> >> +						     &sub_bridge_0_ctx->bridge->dev);
> >> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx));
> >> +
> >> +	ret = fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->bridge);
> >> +	KUNIT_ASSERT_EQ(test, ret, 0);
> >> +
> >> +	sub_bridge_1_ctx = fake_fpga_bridge_register(test,
> >> +						     &base_ctx->region_ctx->region->dev);
> >> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx));
> >> +
> >> +	sub_region_1_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
> >> +						     &sub_bridge_1_ctx->bridge->dev);
> >> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx));
> >> +
> >> +	ret = fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->bridge);
> >> +	KUNIT_ASSERT_EQ(test, ret, 0);
> > 
> > I'm wondering if we need to construct the topology for partial
> > reconfiguration test. The FPGA core doesn't actually check the topology.
> > It is OK to do partial reconfiguration for a region without parents as
> > long as its associated FPGA manager device has the capability.
> > 
> > Thanks,
> > Yilun
> 
> I agree with you. Creating a hierarchical layout is rather unnecessary.
>

I assume the following sections have nothing to do with hierarchial
layout, is it?
 
> Initially, the idea was to test that all components behave as expected
> in a complete setup, e.g., only the bridge of the specific reconfigurable
> region gets disabled during programming and then re-enabled.
> 
> However, after some iterations, I'm starting to think that it would be
> better to restructure the whole test code into a set of self-contained
> test modules, one for each core component. 
> 
> In that way, each module would contain the implementation of the fake/mock
> low-level driver and the related tests. For instance, the manager module
> would contain the implementation of the fake manager and the test_img_load_buf
> and test_img_load_sgt test cases. Similarly, the bridge module would contain
> the fake/mock bridge implementation and the test_toggle and test_get_put_list
> cases.
> 
> I think that in this way, the code would be simpler and more adherent to the
> unit testing methodology. The downside is that making tests that need multiple
> components would be more cumbersome and possibly lead to code duplication.
> For instance, testing the region's fpga_region_program_fpga() would require
> implementing additional local mock/fakes for the manager and bridge.

This way is good to me.

> 
> What do you think?
> 
> Thanks,
> Marco
> 
> [...]
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH v5 4/4] fpga: add initial KUnit test suites
  2023-05-17 10:04       ` Xu Yilun
@ 2023-05-18 12:43         ` Marco Pagani
  0 siblings, 0 replies; 12+ messages in thread
From: Marco Pagani @ 2023-05-18 12:43 UTC (permalink / raw)
  To: Xu Yilun; +Cc: Moritz Fischer, Wu Hao, Tom Rix, linux-kernel, linux-fpga



On 2023-05-17 12:04, Xu Yilun wrote:
> On 2023-05-15 at 19:24:07 +0200, Marco Pagani wrote:
>>
>>
>> On 2023-05-13 19:40, Xu Yilun wrote:
>>> On 2023-05-11 at 16:19:22 +0200, Marco Pagani wrote:
>>>> Introduce initial KUnit tests for the FPGA subsystem. Tests are organized
>>>> into three test suites. The first suite tests the FPGA Manager.
>>>> The second suite tests the FPGA Bridge. Finally, the last test suite
>>>> models a complete FPGA platform and tests static and partial reconfiguration.
>>>>
>>>> Signed-off-by: Marco Pagani <marpagan@redhat.com>
>>
>> [...]
>>
>>>> +static void fpga_bridge_test_get_put_list(struct kunit *test)
>>>> +{
>>>> +	struct list_head bridge_list;
>>>> +	struct fake_fpga_bridge *bridge_0_ctx, *bridge_1_ctx;
>>>> +	int ret;
>>>> +
>>>> +	bridge_0_ctx = test->priv;
>>>> +
>>>> +	/* Register another bridge for this test */
>>>> +	bridge_1_ctx = fake_fpga_bridge_register(test, NULL);
>>>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(bridge_1_ctx));
>>>
>>> I think bridge_1 could also be initialized in test_init together with
>>> bridge_0
>>
>> I can do it, but it would remain unused in the previous test case.
>>  
>>>> +
>>>> +	INIT_LIST_HEAD(&bridge_list);
>>>> +
>>>> +	/* Get bridge_0 and add it to the list */
>>>> +	ret = fpga_bridge_get_to_list(bridge_1_ctx->bridge->dev.parent, NULL,
>>>> +				      &bridge_list);
>>>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>>>> +
>>>> +	KUNIT_EXPECT_PTR_EQ(test, bridge_1_ctx->bridge,
>>>> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
>>>
>>> Should operate on bridge_0_ctx?
>>
>> Yes, sorry. Code and comments are reversed. I'll fix it in the next version.
>>
>>>> +
>>>> +	/* Get bridge_1 and add it to the list */
>>>> +	ret = fpga_bridge_get_to_list(bridge_0_ctx->bridge->dev.parent, NULL,
>>>> +				      &bridge_list);
>>>> +	KUNIT_EXPECT_EQ(test, ret, 0);
>>>> +
>>>> +	KUNIT_EXPECT_PTR_EQ(test, bridge_0_ctx->bridge,
>>>> +			    list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
>>>
>>> Should operate on bridge_1_ctx?
>>
>> Same.
>>
>>>> +
>>>> +	/* Disable an then enable both bridges from the list */
>>>> +	KUNIT_EXPECT_TRUE(test, bridge_0_ctx->stats.enable);
>>>
>>> Why expect enable without fpga_bridges_enable()?
>>
>> To check that the bridge is initialized in the correct (enabled) state.
>>
>> [...]
>>
>>>> +static void fpga_test_partial_rcfg(struct kunit *test)
>>>> +{
>>>> +	struct fpga_base_ctx *base_ctx;
>>>> +	struct fake_fpga_region *sub_region_0_ctx, *sub_region_1_ctx;
>>>> +	struct fake_fpga_bridge *sub_bridge_0_ctx, *sub_bridge_1_ctx;
>>>> +	struct fpga_image_info *partial_img_info;
>>>> +	int ret;
>>>> +
>>>> +	base_ctx = test->priv;
>>>> +
>>>> +	/*
>>>> +	 * Add two reconfigurable sub-regions, each controlled by a bridge. The
>>>> +	 * reconfigurable sub-region are children of their bridges which are,
>>>> +	 * in turn, children of the base region. For simplicity, the same image
>>>> +	 * is used to configure reconfigurable regions
>>>> +	 */
>>>> +	sub_bridge_0_ctx = fake_fpga_bridge_register(test,
>>>> +						     &base_ctx->region_ctx->region->dev);
>>>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_0_ctx));
>>>> +
>>>> +	sub_region_0_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
>>>> +						     &sub_bridge_0_ctx->bridge->dev);
>>>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_0_ctx));
>>>> +
>>>> +	ret = fake_fpga_region_add_bridge(sub_region_0_ctx, sub_bridge_0_ctx->bridge);
>>>> +	KUNIT_ASSERT_EQ(test, ret, 0);
>>>> +
>>>> +	sub_bridge_1_ctx = fake_fpga_bridge_register(test,
>>>> +						     &base_ctx->region_ctx->region->dev);
>>>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_bridge_1_ctx));
>>>> +
>>>> +	sub_region_1_ctx = fake_fpga_region_register(test, base_ctx->mgr_ctx->mgr,
>>>> +						     &sub_bridge_1_ctx->bridge->dev);
>>>> +	KUNIT_ASSERT_FALSE(test, IS_ERR(sub_region_1_ctx));
>>>> +
>>>> +	ret = fake_fpga_region_add_bridge(sub_region_1_ctx, sub_bridge_1_ctx->bridge);
>>>> +	KUNIT_ASSERT_EQ(test, ret, 0);
>>>
>>> I'm wondering if we need to construct the topology for partial
>>> reconfiguration test. The FPGA core doesn't actually check the topology.
>>> It is OK to do partial reconfiguration for a region without parents as
>>> long as its associated FPGA manager device has the capability.
>>>
>>> Thanks,
>>> Yilun
>>
>> I agree with you. Creating a hierarchical layout is rather unnecessary.
>>
> 
> I assume the following sections have nothing to do with hierarchial
> layout, is it?
>

It was a general summary to put things in perspective and ask your opinion
before moving forward with the next version.

>> Initially, the idea was to test that all components behave as expected
>> in a complete setup, e.g., only the bridge of the specific reconfigurable
>> region gets disabled during programming and then re-enabled.
>>
>> However, after some iterations, I'm starting to think that it would be
>> better to restructure the whole test code into a set of self-contained
>> test modules, one for each core component. 
>>
>> In that way, each module would contain the implementation of the fake/mock
>> low-level driver and the related tests. For instance, the manager module
>> would contain the implementation of the fake manager and the test_img_load_buf
>> and test_img_load_sgt test cases. Similarly, the bridge module would contain
>> the fake/mock bridge implementation and the test_toggle and test_get_put_list
>> cases.
>>
>> I think that in this way, the code would be simpler and more adherent to the
>> unit testing methodology. The downside is that making tests that need multiple
>> components would be more cumbersome and possibly lead to code duplication.
>> For instance, testing the region's fpga_region_program_fpga() would require
>> implementing additional local mock/fakes for the manager and bridge.
> 
> This way is good to me.
>

Okay, I'll move toward multiple test modules for v6.
 
>>
>> What do you think?
>>
>> Thanks,
>> Marco
>>
>> [...]
>>
>

Thanks,
Marco


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2023-05-18 12:46 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-05-11 14:19 [RFC PATCH v5 0/4] fpga: add initial KUnit tests for the subsystem Marco Pagani
2023-05-11 14:19 ` [RFC PATCH v5 1/4] fpga: add fake FPGA manager Marco Pagani
2023-05-13 15:44   ` Xu Yilun
2023-05-11 14:19 ` [RFC PATCH v5 2/4] fpga: add fake FPGA bridge Marco Pagani
2023-05-13 15:46   ` Xu Yilun
2023-05-11 14:19 ` [RFC PATCH v5 3/4] fpga: add fake FPGA region Marco Pagani
2023-05-13 16:05   ` Xu Yilun
2023-05-11 14:19 ` [RFC PATCH v5 4/4] fpga: add initial KUnit test suites Marco Pagani
2023-05-13 17:40   ` Xu Yilun
2023-05-15 17:24     ` Marco Pagani
2023-05-17 10:04       ` Xu Yilun
2023-05-18 12:43         ` Marco Pagani

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).