Re: [RFC PATCH v4 1/4] fpga: add fake FPGA manager

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2023-04-26 17:44, Marco Pagani wrote:
> 
> 
> On 2023-04-20 20:31, Xu Yilun wrote:
>> On 2023-04-17 at 14:23:05 +0200, Marco Pagani wrote:
>>> Add fake FPGA manager platform driver with support functions.
>>> The driver checks the programming sequence using KUnit expectations.
>>> This module is part of the KUnit tests for the FPGA subsystem.
>>>
>>> Signed-off-by: Marco Pagani <marpagan@xxxxxxxxxx>
>>> ---
>>>  drivers/fpga/tests/fake-fpga-mgr.c | 386 +++++++++++++++++++++++++++++
>>>  drivers/fpga/tests/fake-fpga-mgr.h |  43 ++++
>>>  2 files changed, 429 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..636df637b291
>>> --- /dev/null
>>> +++ b/drivers/fpga/tests/fake-fpga-mgr.c
>>> @@ -0,0 +1,386 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Driver for the fake FPGA manager
>>> + *
>>> + * Copyright (C) 2023 Red Hat, Inc.
>>> + *
>>> + * Author: Marco Pagani <marpagan@xxxxxxxxxx>
>>> + */
>>> +
>>> +#include <linux/types.h>
>>> +#include <linux/device.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/fpga/fpga-mgr.h>
>>> +#include <kunit/test.h>
>>> +
>>> +#include "fake-fpga-mgr.h"
>>> +
>>> +#define FAKE_FPGA_MGR_DEV_NAME	"fake_fpga_mgr"
>>> +
>>> +#define FAKE_HEADER_BYTE	0x3f
>>> +#define FAKE_HEADER_SIZE	FPGA_IMG_BLOCK
>>> +
>>> +struct fake_mgr_priv {
>>> +	int rcfg_count;
>>> +	bool op_parse_header;
>>> +	bool op_write_init;
>>> +	bool op_write;
>>> +	bool op_write_sg;
>>> +	bool op_write_complete;
>>> +	struct kunit *test;
>>> +};
>>> +
>>> +struct fake_mgr_data {
>>> +	struct kunit *test;
>>> +};
>>> +
>>> +static void check_header(struct kunit *test, const u8 *buf);
>>> +
>>> +static enum fpga_mgr_states op_state(struct fpga_manager *mgr)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test)
>>> +		kunit_info(priv->test, "Fake FPGA manager: state\n");
>>> +
>>> +	return FPGA_MGR_STATE_UNKNOWN;
>>> +}
>>> +
>>> +static u64 op_status(struct fpga_manager *mgr)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test)
>>> +		kunit_info(priv->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;
>>> +
>>> +	if (priv->test) {
>>> +		kunit_info(priv->test, "Fake FPGA manager: parse_header\n");
>>> +
>>> +		KUNIT_EXPECT_EQ(priv->test, mgr->state,
>>> +				FPGA_MGR_STATE_PARSE_HEADER);
>>> +
>>> +		check_header(priv->test, buf);
>>> +	}
>>> +
>>> +	priv->op_parse_header = true;
>>> +
>>> +	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;
>>> +
>>> +	if (priv->test) {
>>> +		kunit_info(priv->test, "Fake FPGA manager: write_init\n");
>>> +
>>> +		KUNIT_EXPECT_EQ(priv->test, mgr->state,
>>> +				FPGA_MGR_STATE_WRITE_INIT);
>>> +	}
>>> +
>>> +	priv->op_write_init = true;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int op_write(struct fpga_manager *mgr, const char *buf, size_t count)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test) {
>>> +		kunit_info(priv->test, "Fake FPGA manager: write\n");
>>> +
>>> +		KUNIT_EXPECT_EQ(priv->test, mgr->state,
>>> +				FPGA_MGR_STATE_WRITE);
>>> +	}
>>> +
>>> +	priv->op_write = true;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test) {
>>> +		kunit_info(priv->test, "Fake FPGA manager: write_sg\n");
>>> +
>>> +		KUNIT_EXPECT_EQ(priv->test, mgr->state,
>>> +				FPGA_MGR_STATE_WRITE);
>>> +	}
>>> +
>>> +	priv->op_write_sg = true;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test) {
>>> +		kunit_info(priv->test, "Fake FPGA manager: write_complete\n");
>>> +
>>> +		KUNIT_EXPECT_EQ(priv->test, mgr->state,
>>> +				FPGA_MGR_STATE_WRITE_COMPLETE);
>>> +	}
>>> +
>>> +	priv->op_write_complete = true;
>>> +	priv->rcfg_count++;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void op_fpga_remove(struct fpga_manager *mgr)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr->priv;
>>> +
>>> +	if (priv->test)
>>> +		kunit_info(priv->test, "Fake FPGA manager: remove\n");
>>> +}
>>> +
>>> +static const struct fpga_manager_ops fake_fpga_mgr_ops = {
>>> +	.initial_header_size = FAKE_HEADER_SIZE,
>>> +	.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.
>>> + * @mgr_ctx: fake FPGA manager context data structure.
>>> + * @test: KUnit test context object.
>>> + *
>>> + * 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_data pdata;
>>> +	int ret;
>>> +
>>> +	mgr_ctx = kzalloc(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) {
>>> +		pr_err("Fake FPGA manager device allocation failed\n");
>>> +		ret = -ENOMEM;
>>> +		goto err_mem;
>>> +	}
>>> +
>>> +	pdata.test = test;
>>> +	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) {
>>> +		pr_err("Fake FPGA manager device add failed\n");
>>> +		goto err_pdev;
>>> +	}
>>> +
>>> +	mgr_ctx->mgr = platform_get_drvdata(mgr_ctx->pdev);
>>> +
>>> +	if (test)
>>> +		kunit_info(test, "Fake FPGA manager registered\n");
>>> +
>>> +	return mgr_ctx;
>>> +
>>> +err_pdev:
>>> +	platform_device_put(mgr_ctx->pdev);
>>> +	kfree(mgr_ctx);
>>> +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->test;
>>> +
>>> +	if (mgr_ctx->pdev) {
>>> +		platform_device_unregister(mgr_ctx->pdev);
>>> +		if (test)
>>> +			kunit_info(test, "Fake FPGA manager unregistered\n");
>>> +	}
>>> +
>>> +	kfree(mgr_ctx);
>>> +}
>>> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_unregister);
>>> +
>>> +/**
>>> + * fake_fpga_mgr_get_rcfg_count() - get the number of reconfigurations.
>>> + * @mgr_ctx: fake FPGA manager context data structure.
>>> + *
>>> + * Return: number of reconfigurations.
>>> + */
>>> +int fake_fpga_mgr_get_rcfg_count(const struct fake_fpga_mgr *mgr_ctx)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr_ctx->mgr->priv;
>>> +
>>> +	return priv->rcfg_count;
>>> +}
>>> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_get_rcfg_count);
>>> +
>>> +/**
>>> + * fake_fpga_mgr_fill_header() - fill an image buffer with the test header.
>>> + * @buf: image buffer.
>>> + */
>>> +void fake_fpga_mgr_fill_header(u8 *buf)
>>> +{
>>> +	int i;
>>> +
>>> +	for (i = 0; i < FAKE_HEADER_SIZE; i++)
>>> +		buf[i] = FAKE_HEADER_BYTE;
>>> +}
>>> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_fill_header);
>>> +
>>> +static void check_header(struct kunit *test, const u8 *buf)
>>> +{
>>> +	int i;
>>> +
>>> +	for (i = 0; i < FAKE_HEADER_SIZE; i++)
>>> +		KUNIT_EXPECT_EQ(test, buf[i], FAKE_HEADER_BYTE);
>>> +}
>>> +
>>> +static void clear_op_flags(struct fake_mgr_priv *priv)
>>> +{
>>> +	priv->op_parse_header = false;
>>> +	priv->op_write_init = false;
>>> +	priv->op_write = false;
>>> +	priv->op_write_sg = false;
>>> +	priv->op_write_complete = false;
>>> +}
>>> +
>>> +/**
>>> + * fake_fpga_mgr_check_write_buf() - check if programming using a buffer succeeded.
>>> + * @mgr_ctx: fake FPGA manager context data structure.
>>> + */
>>> +void fake_fpga_mgr_check_write_buf(struct fake_fpga_mgr *mgr_ctx)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr_ctx->mgr->priv;
>>> +
>>> +	if (priv->test) {
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_parse_header, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write_init, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write_complete, true);
>>> +	}
>>> +
>>> +	clear_op_flags(priv);
>>> +}
>>> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_check_write_buf);
>>> +
>>> +/**
>>> + * fake_fpga_mgr_check_write_sgt() - check if programming using a s.g. table succeeded.
>>> + * @mgr_ctx: fake FPGA manager context data structure.
>>> + */
>>> +void fake_fpga_mgr_check_write_sgt(struct fake_fpga_mgr *mgr_ctx)
>>> +{
>>> +	struct fake_mgr_priv *priv;
>>> +
>>> +	priv = mgr_ctx->mgr->priv;
>>> +
>>> +	if (priv->test) {
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_parse_header, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write_init, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write_sg, true);
>>> +		KUNIT_EXPECT_EQ(priv->test, priv->op_write_complete, true);
>>> +	}
>>> +
>>> +	clear_op_flags(priv);
>>> +}
>>> +EXPORT_SYMBOL_GPL(fake_fpga_mgr_check_write_sgt);
>>
>> I'm wondering, if we could move all these exported functions out of
>> fake_fpga driver module. And make this driver module serves FPGA
>> mgr framework only, just like other fpga drivers do.
>>
>> I assume the main requirement is to check the statistics produced
>> by the fake fpga driver. Directly accessing mgr->priv outside the
>> driver could be unwanted.  To solve this, could we create a shared
>> buffer for the statistics and pass to fake drivers by platform data.
>>
>> I hope move all the tester's actions in fpga-test.c, so that people
>> could easily see from code what a user need to do to enable fpga
>> reprogramming and what are expected in one file. The fake drivers could
>> be kept as simple, they only move the process forward and produce
>> statistics.
>>
>> Thanks,
>> Yilun
>>
> 
> I agree with you. Initially, I wanted to keep all KUnit test assertions
> and expectations contained in fpga-test. However, I could not find a simple
> way to test that the FPGA manager performs the correct state transitions
> during programming. So I ended up putting KUnit assertions in the methods
> of the low-level fake driver as a first solution.
> 
> I like your suggestion of using a shared buffer to have a cleaner
> implementation. My only concern is that it would make the code more complex.
> I will work on this for V5.
> 

I experimented with a couple of alternatives to move all tests inside
fpga-test and remove the external functions. Unfortunately, each alternative
comes with its drawbacks.

Using a shared buffer (e.g., kfifo) to implement an events buffer between
fake mgr/bridge and the fpga-test overcomplicates the code (i.e., defining
message structs, enums for the operations, locks, etc.).

Moving fake modules' (mgr, bridge, region) implementations inside fpga-test
makes fpga-test monolithic and harder to understand and maintain.

Accessing modules' private data directly from fpga-test breaks encapsulation.

Overall, it seems to me that using external functions to get the state of fake
modules is the least-worst alternative. What are your thoughts and preferences?

Thanks,
Marco


>>> [...]




[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux