linux-fpga.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v15 0/6] FPGA Image Load (previously Security Manager)
@ 2021-09-09  2:18 Russ Weight
  2021-09-09  2:18 ` [PATCH v15 1/6] fpga: image-load: fpga image load class driver Russ Weight
                   ` (6 more replies)
  0 siblings, 7 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

The FPGA Image Load class driver provides an API to upload image
files to an FPGA device. Image files are self-describing. They could
contain FPGA images, BMC images, Root Entry Hashes, or other device
specific files. It is up to the lower-level device driver and the
target device to authenticate and disposition the file data.

The n3000bmc-secure driver is the first driver to use the FPGA
Security Manager. This driver was previously submitted in the same
patch set, but was split into a separate patch set starting with v2.

Changelog v14 -> v15:
 - Changed the driver name from FPGA Security Manager to FPGA Image Load
 - Changed file, symbol, and config names to reflect the new driver name
 - Rewrote documentation.
 - Removed all sysfs files except for "status", which has moved to the
   parent directory.
 - Implemented FPGA_IMAGE_LOAD_WRITE, FPGA_IMAGE_LOAD_STATUS, and
   FPGA_IMAGE_LOAD_CANCEL IOCTLs to initiate, monitor, and cancel image
   uploads.
 - Fixed some error return values in fpga_image_load_register()
 - Added an eventfd which is signalled upon completion of an image load.
 - Minor changes to locking to accommodate the FPGA_IMAGE_LOAD_STATUS IOCTL
 - Removed signed-off/reviewed-by tags. Please resend as appropriate.

Changelog v13 -> v14:
 - Dropped the patch: fpga: sec-mgr: expose hardware error info
 - Updated copyrights to 2021
 - Updated ABI documentation date and kernel version
 - Removed the name sysfs entry from the class driver
 - Use xa_alloc() instead of ida_simple_get()
 - Rename dev to parent for parent devices
 - Remove fpga_sec_mgr_create(), devm_fpga_sec_mgr_create(), and
   fpga_sec_mgr_free() functions and update the fpga_sec_mgr_register()
   function to both create and register a new security manager.
 - Populate the fpga_sec_mgr_dev_release() function.
 - Added MAINTAINERS reference for
   Documentation/ABI/testing/sysfs-class-fpga-sec-mgr

Changelog v12 -> v13:
  - Change "if (count == 0 || " to "if (!count || "
  - Improve error message: "Attempt to register without all required ops\n"
  - Change set_error() to fpga_sec_set_error()
  - Change set_hw_errinfo() to fpga_sec_set_hw_errinfo()

Changelog v11 -> v12:
  - Updated Date and KernelVersion fields in ABI documentation
  - Removed size parameter from write_blk() op - it is now up to
    the lower-level driver to determine the appropriate size and
    to update smgr->remaining_size accordingly.
  - Changed syntax of sec_mgr_prog_str[] and sec_mgr_err_str array definitions
    from:
	"idle",			/* FPGA_SEC_PROG_IDLE */
    to:
	[FPGA_SEC_PROG_IDLE]	    = "idle",

Changelog v10 -> v11:
  - Fixed a spelling error in a comment
  - Initialize smgr->err_code and smgr->progress explicitly in
    fpga_sec_mgr_create() instead of accepting the default 0 value.

Changelog v9 -> v10:
  - Rebased to 5.12-rc2 next
  - Updated Date and KernelVersion in ABI documentation

Changelog v8 -> v9:
  - Rebased patches for 5.11-rc2
  - Updated Date and KernelVersion in ABI documentation

Changelog v7 -> v8:
  - Fixed grammatical error in Documentation/fpga/fpga-sec-mgr.rst

Changelog v6 -> v7:
  - Changed dates in documentation file to December 2020
  - Changed filename_store() to use kmemdup_nul() instead of
    kstrndup() and changed the count to not assume a line-return.

Changelog v5 -> v6:
  - Removed sysfs support and documentation for the display of the
    flash count, root entry hashes, and code-signing-key cancellation
    vectors from the class driver. This information can vary by device
    and will instead be displayed by the device-specific parent driver.

Changelog v4 -> v5:
  - Added the devm_fpga_sec_mgr_unregister() function, following recent
    changes to the fpga_manager() implementation.
  - Changed most of the *_show() functions to use sysfs_emit()
    instead of sprintf(
  - When checking the return values for functions of type enum
    fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0

Changelog v3 -> v4:
  - This driver is generic enough that it could be used for non Intel
    FPGA devices. Changed from "Intel FPGA Security Manager" to FPGA
    Security Manager" and removed unnecessary references to "Intel".
  - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
    Note that this also affects some filenames.

Changelog v2 -> v3:
  - Use dev_err() to report invalid progress in sec_progress()
  - Use dev_err() to report invalid error code in sec_error()
  - Modified sysfs handler check in check_sysfs_handler() to make
    it more readable.
  - Removed unnecessary "goto done"
  - Added a comment to explain imgr->driver_unload in
    ifpga_sec_mgr_unregister()

Changelog v1 -> v2:
  - Separated out the MAX10 BMC Security Engine to be submitted in
    a separate patch-set.
  - Bumped documentation dates and versions
  - Split ifpga_sec_mgr_register() into create() and register() functions
  - Added devm_ifpga_sec_mgr_create()
  - Added Documentation/fpga/ifpga-sec-mgr.rst 
  - Changed progress state "read_file" to "reading"
  - Added sec_error() function (similar to sec_progress())
  - Removed references to bmc_flash_count & smbus_flash_count (not supported)
  - Removed typedefs for imgr ops
  - Removed explicit value assignments in enums
  - Other minor code cleanup per review comments 

Russ Weight (6):
  fpga: image-load: fpga image load class driver
  fpga: image-load: enable image loads
  fpga: image-load: signal eventfd when complete
  fpga: image-load: add status ioctl
  fpga: image-load: create status sysfs node
  fpga: image-load: enable cancel of image upload

 .../ABI/testing/sysfs-class-fpga-image-load   |   7 +
 Documentation/fpga/fpga-image-load.rst        |  45 ++
 Documentation/fpga/index.rst                  |   1 +
 MAINTAINERS                                   |  10 +
 drivers/fpga/Kconfig                          |  10 +
 drivers/fpga/Makefile                         |   3 +
 drivers/fpga/fpga-image-load.c                | 466 ++++++++++++++++++
 include/linux/fpga/fpga-image-load.h          |  67 +++
 include/uapi/linux/fpga-image-load.h          |  78 +++
 9 files changed, 687 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-image-load
 create mode 100644 Documentation/fpga/fpga-image-load.rst
 create mode 100644 drivers/fpga/fpga-image-load.c
 create mode 100644 include/linux/fpga/fpga-image-load.h
 create mode 100644 include/uapi/linux/fpga-image-load.h

-- 
2.25.1


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

* [PATCH v15 1/6] fpga: image-load: fpga image load class driver
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-10  6:46   ` Xu Yilun
  2021-09-09  2:18 ` [PATCH v15 2/6] fpga: image-load: enable image loads Russ Weight
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

The FPGA Image Load class driver provides an API to transfer update
files to an FPGA device. Image files are self-describing. They could
contain FPGA images, BMC images, Root Entry Hashes, or other device
specific files. It is up to the device driver and the target device
to authenticate and disposition the file data.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v15:
 - Compare to previous patch:
     [PATCH v14 1/6] fpga: sec-mgr: fpga security manager class driver 
 - Changed file, symbol, and config names to reflect the new driver name
 - Rewrote documentation. The documentation will be added to in later patches.
 - Removed signed-off/reviewed-by tags
v14:
 - Updated copyright to 2021
 - Removed the name sysfs entry
 - Removed MAINTAINERS reference to
   Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
 - Use xa_alloc() instead of ida_simple_get()
 - Rename dev to parent for parent devices
 - Remove fpga_sec_mgr_create(), devm_fpga_sec_mgr_create(), and
   fpga_sec_mgr_free() functions and update the fpga_sec_mgr_register()
   function to both create and register a new security manager.
 - Populate the fpga_sec_mgr_dev_release() function.
v13:
  - No change
v12:
  - Updated Date and KernelVersion fields in ABI documentation
v11:
  - No change
v10:
  - Rebased to 5.12-rc2 next
  - Updated Date and KernelVersion in ABI documentation
v9:
  - Updated Date and KernelVersion in ABI documentation
v8:
  - Fixed grammatical error in Documentation/fpga/fpga-sec-mgr.rst
v7:
  - Changed Date in documentation file to December 2020
v6:
  - Removed sysfs support and documentation for the display of the
    flash count, root entry hashes, and code-signing-key cancelation
    vectors.
v5:
  - Added the devm_fpga_sec_mgr_unregister() function, following recent
    changes to the fpga_manager() implementation.
  - Changed some *_show() functions to use sysfs_emit() instead of sprintf(
v4:
  - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
    and removed unnecessary references to "Intel".
  - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
v3:
  - Modified sysfs handler check in check_sysfs_handler() to make
    it more readable.
v2:
  - Bumped documentation dates and versions
  - Added Documentation/fpga/ifpga-sec-mgr.rst
  - Removed references to bmc_flash_count & smbus_flash_count (not supported)
  - Split ifpga_sec_mgr_register() into create() and register() functions
  - Added devm_ifpga_sec_mgr_create()
  - Removed typedefs for imgr ops
---
---
 Documentation/fpga/fpga-image-load.rst |  10 ++
 Documentation/fpga/index.rst           |   1 +
 MAINTAINERS                            |   8 ++
 drivers/fpga/Kconfig                   |  10 ++
 drivers/fpga/Makefile                  |   3 +
 drivers/fpga/fpga-image-load.c         | 124 +++++++++++++++++++++++++
 include/linux/fpga/fpga-image-load.h   |  35 +++++++
 7 files changed, 191 insertions(+)
 create mode 100644 Documentation/fpga/fpga-image-load.rst
 create mode 100644 drivers/fpga/fpga-image-load.c
 create mode 100644 include/linux/fpga/fpga-image-load.h

diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
new file mode 100644
index 000000000000..a6e53ac66026
--- /dev/null
+++ b/Documentation/fpga/fpga-image-load.rst
@@ -0,0 +1,10 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+FPGA Image Load Class Driver
+============================
+
+The FPGA Image Load class driver provides a common API for user-space
+tools to manage image uploads to FPGA devices. Device drivers that
+instantiate the FPGA Image Load class driver will interact with the
+target device to transfer and authenticate the image data.
diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
index f80f95667ca2..85d25fb22c08 100644
--- a/Documentation/fpga/index.rst
+++ b/Documentation/fpga/index.rst
@@ -8,6 +8,7 @@ fpga
     :maxdepth: 1
 
     dfl
+    fpga-image-load
 
 .. only::  subproject and html
 
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c63415d2ac2..4e7f48fa7e5c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7358,6 +7358,14 @@ F:	Documentation/fpga/
 F:	drivers/fpga/
 F:	include/linux/fpga/
 
+FPGA SECURITY MANAGER DRIVERS
+M:	Russ Weight <russell.h.weight@intel.com>
+L:	linux-fpga@vger.kernel.org
+S:	Maintained
+F:	Documentation/fpga/fpga-image-load.rst
+F:	drivers/fpga/fpga-image-load.c
+F:	include/linux/fpga/fpga-image-load.h
+
 FPU EMULATOR
 M:	Bill Metzenthen <billm@melbpc.org.au>
 S:	Maintained
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 991b3f361ec9..c12a14e62fff 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -243,4 +243,14 @@ config FPGA_MGR_VERSAL_FPGA
 	  configure the programmable logic(PL).
 
 	  To compile this as a module, choose M here.
+
+config FPGA_IMAGE_LOAD
+	tristate "FPGA Image Load Driver"
+	help
+	  The FPGA Image Load class driver presents a common user API for
+	  uploading an image file to an FPGA device. The image file is
+	  expected to be self-describing. It is up to the device driver
+	  and/or the device itself to authenticate and disposition the
+	  image data.
+
 endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 0bff783d1b61..adf228ee4f5e 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -22,6 +22,9 @@ obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)      += versal-fpga.o
 obj-$(CONFIG_ALTERA_PR_IP_CORE)         += altera-pr-ip-core.o
 obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT)    += altera-pr-ip-core-plat.o
 
+# FPGA Image Load Framework
+obj-$(CONFIG_FPGA_IMAGE_LOAD)		+= fpga-image-load.o
+
 # FPGA Bridge Drivers
 obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
 obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
new file mode 100644
index 000000000000..7d75bbcff541
--- /dev/null
+++ b/drivers/fpga/fpga-image-load.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Image Load Class Driver
+ *
+ * Copyright (C) 2019-2021 Intel Corporation, Inc.
+ */
+
+#include <linux/fpga/fpga-image-load.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
+static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
+
+static struct class *fpga_image_load_class;
+
+#define to_image_load(d) container_of(d, struct fpga_image_load, dev)
+
+/**
+ * fpga_image_load_register - create and register an FPGA Image Load Device
+ *
+ * @parent: fpga image load device from pdev
+ * @lops:   pointer to a structure of image load callback functions
+ * @priv:   fpga image load private data
+ *
+ * Returns a struct fpga_image_load pointer on success, or ERR_PTR() on
+ * error. The caller of this function is responsible for calling
+ * fpga_image_load_unregister().
+ */
+struct fpga_image_load *
+fpga_image_load_register(struct device *parent,
+			 const struct fpga_image_load_ops *lops, void *priv)
+{
+	struct fpga_image_load *imgld;
+	int id, ret;
+
+	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
+	if (!imgld)
+		return NULL;
+
+	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
+		       GFP_KERNEL);
+	if (ret)
+		goto error_kfree;
+
+	mutex_init(&imgld->lock);
+
+	imgld->priv = priv;
+	imgld->lops = lops;
+
+	imgld->dev.class = fpga_image_load_class;
+	imgld->dev.parent = parent;
+
+	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
+	if (ret) {
+		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
+		goto error_device;
+	}
+
+	ret = device_register(&imgld->dev);
+	if (ret) {
+		put_device(&imgld->dev);
+		return ERR_PTR(ret);
+	}
+
+	return imgld;
+
+error_device:
+	xa_erase(&fpga_image_load_xa, imgld->dev.id);
+
+error_kfree:
+	kfree(imgld);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fpga_image_load_register);
+
+/**
+ * fpga_image_load_unregister - unregister an FPGA image load device
+ *
+ * @imgld: pointer to struct fpga_image_load
+ *
+ * This function is intended for use in an FPGA Image Load driver's
+ * remove() function.
+ */
+void fpga_image_load_unregister(struct fpga_image_load *imgld)
+{
+	device_unregister(&imgld->dev);
+}
+EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
+
+static void fpga_image_load_dev_release(struct device *dev)
+{
+	struct fpga_image_load *imgld = to_image_load(dev);
+
+	xa_erase(&fpga_image_load_xa, imgld->dev.id);
+	kfree(imgld);
+}
+
+static int __init fpga_image_load_class_init(void)
+{
+	pr_info("FPGA Image Load Driver\n");
+
+	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
+	if (IS_ERR(fpga_image_load_class))
+		return PTR_ERR(fpga_image_load_class);
+
+	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
+
+	return 0;
+}
+
+static void __exit fpga_image_load_class_exit(void)
+{
+	class_destroy(fpga_image_load_class);
+	WARN_ON(!xa_empty(&fpga_image_load_xa));
+}
+
+MODULE_DESCRIPTION("FPGA Image Load Driver");
+MODULE_LICENSE("GPL v2");
+
+subsys_initcall(fpga_image_load_class_init);
+module_exit(fpga_image_load_class_exit)
diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
new file mode 100644
index 000000000000..a9cef9e1056b
--- /dev/null
+++ b/include/linux/fpga/fpga-image-load.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for FPGA Image Load Driver
+ *
+ * Copyright (C) 2019-2021 Intel Corporation, Inc.
+ */
+#ifndef _LINUX_FPGA_IMAGE_LOAD_H
+#define _LINUX_FPGA_IMAGE_LOAD_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+struct fpga_image_load;
+
+/**
+ * struct fpga_image_load_ops - device specific operations
+ */
+struct fpga_image_load_ops {
+};
+
+struct fpga_image_load {
+	struct device dev;
+	const struct fpga_image_load_ops *lops;
+	struct mutex lock;		/* protect data structure contents */
+	void *priv;
+};
+
+struct fpga_image_load *
+fpga_image_load_register(struct device *dev,
+			 const struct fpga_image_load_ops *lops, void *priv);
+
+void fpga_image_load_unregister(struct fpga_image_load *imgld);
+
+#endif
-- 
2.25.1


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

* [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
  2021-09-09  2:18 ` [PATCH v15 1/6] fpga: image-load: fpga image load class driver Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-10  8:22   ` Xu Yilun
  2021-09-09  2:18 ` [PATCH v15 3/6] fpga: image-load: signal eventfd when complete Russ Weight
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

Extend the FPGA Image Load class driver to include IOCTL support
(FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
The IOCTL will return immediately, and the update will begin in the
context of a kernel worker thread.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v15:
 - Compare to previous patch:
     [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
 - Changed file, symbol, and config names to reflect the new driver name
 - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
   IOCTL for writing the image data to the FPGA card. The driver no longer
   uses the request_firmware framework.
 - Fixed some error return values in fpga_image_load_register()
 - Removed signed-off/reviewed-by tags
v14:
 - Added MAINTAINERS reference for
   Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
 - Updated ABI documentation date and kernel version
 - Updated copyright to 2021
v13:
  - Change "if (count == 0 || " to "if (!count || "
  - Improve error message: "Attempt to register without all required ops\n"
v12:
  - Updated Date and KernelVersion fields in ABI documentation
  - Removed size parameter from write_blk() op - it is now up to
    the lower-level driver to determine the appropriate size and
    to update smgr->remaining_size accordingly.
v11:
  - Fixed a spelling error in a comment
  - Initialize smgr->err_code and smgr->progress explicitly in
    fpga_sec_mgr_create() instead of accepting the default 0 value.
v10:
  - Rebased to 5.12-rc2 next
  - Updated Date and KernelVersion in ABI documentation
v9:
  - Updated Date and KernelVersion in ABI documentation
v8:
  - No change
v7:
  - Changed Date in documentation file to December 2020
  - Changed filename_store() to use kmemdup_nul() instead of
    kstrndup() and changed the count to not assume a line-return.
v6:
  - Changed "security update" to "secure update" in commit message
v5:
  - When checking the return values for functions of type enum
    fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
v4:
  - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
    and removed unnecessary references to "Intel".
  - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
v3:
  - Removed unnecessary "goto done"
  - Added a comment to explain imgr->driver_unload in
    ifpga_sec_mgr_unregister()
v2:
  - Bumped documentation date and version
  - Removed explicit value assignments in enums
  - Other minor code cleanup per review comments
---
 Documentation/fpga/fpga-image-load.rst |  21 +++
 MAINTAINERS                            |   1 +
 drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
 include/linux/fpga/fpga-image-load.h   |  29 ++++
 include/uapi/linux/fpga-image-load.h   |  58 +++++++
 5 files changed, 329 insertions(+), 4 deletions(-)
 create mode 100644 include/uapi/linux/fpga-image-load.h

diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
index a6e53ac66026..2ca8d2f0212d 100644
--- a/Documentation/fpga/fpga-image-load.rst
+++ b/Documentation/fpga/fpga-image-load.rst
@@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
 tools to manage image uploads to FPGA devices. Device drivers that
 instantiate the FPGA Image Load class driver will interact with the
 target device to transfer and authenticate the image data.
+
+User API
+========
+
+open
+----
+
+An FPGA Image Load device is opened exclusively to control an image load.
+Image loads are processed by a kernel worker thread. A user may choose
+close the device while the upload continues.
+
+ioctl
+-----
+
+FPGA_IMAGE_LOAD_WRITE:
+
+Start an image load with the provided image buffer. This IOCTL returns
+immediately after starting a kernel worker thread to process the image load
+which could take as long a 40 minutes depending on the actual device being
+updated. This is an exclusive operation; an attempt to start concurrent image
+load for the same device will fail with EBUSY.
diff --git a/MAINTAINERS b/MAINTAINERS
index 4e7f48fa7e5c..637bc003ca81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7365,6 +7365,7 @@ S:	Maintained
 F:	Documentation/fpga/fpga-image-load.rst
 F:	drivers/fpga/fpga-image-load.c
 F:	include/linux/fpga/fpga-image-load.h
+F:	include/uapi/linux/fpga-image-load.h
 
 FPU EMULATOR
 M:	Bill Metzenthen <billm@melbpc.org.au>
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
index 7d75bbcff541..f5ccfa9dd977 100644
--- a/drivers/fpga/fpga-image-load.c
+++ b/drivers/fpga/fpga-image-load.c
@@ -5,18 +5,181 @@
  * Copyright (C) 2019-2021 Intel Corporation, Inc.
  */
 
+#include <linux/delay.h>
 #include <linux/fpga/fpga-image-load.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 #include <linux/vmalloc.h>
 
 #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
 static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
 
 static struct class *fpga_image_load_class;
+static dev_t fpga_image_devt;
 
 #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
 
+static void fpga_image_dev_error(struct fpga_image_load *imgld,
+				 enum fpga_image_err err_code)
+{
+	imgld->err_code = err_code;
+	imgld->lops->cancel(imgld);
+}
+
+static void fpga_image_prog_complete(struct fpga_image_load *imgld)
+{
+	mutex_lock(&imgld->lock);
+	imgld->progress = FPGA_IMAGE_PROG_IDLE;
+	complete_all(&imgld->update_done);
+	mutex_unlock(&imgld->lock);
+}
+
+static void fpga_image_do_load(struct work_struct *work)
+{
+	struct fpga_image_load *imgld;
+	enum fpga_image_err ret;
+	u32 size, offset = 0;
+
+	imgld = container_of(work, struct fpga_image_load, work);
+	size = imgld->remaining_size;
+
+	get_device(&imgld->dev);
+	if (!try_module_get(imgld->dev.parent->driver->owner)) {
+		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
+		goto idle_exit;
+	}
+
+	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
+	ret = imgld->lops->prepare(imgld);
+	if (ret != FPGA_IMAGE_ERR_NONE) {
+		fpga_image_dev_error(imgld, ret);
+		goto modput_exit;
+	}
+
+	imgld->progress = FPGA_IMAGE_PROG_WRITING;
+	while (imgld->remaining_size) {
+		ret = imgld->lops->write_blk(imgld, offset);
+		if (ret != FPGA_IMAGE_ERR_NONE) {
+			fpga_image_dev_error(imgld, ret);
+			goto done;
+		}
+
+		offset = size - imgld->remaining_size;
+	}
+
+	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
+	ret = imgld->lops->poll_complete(imgld);
+	if (ret != FPGA_IMAGE_ERR_NONE)
+		fpga_image_dev_error(imgld, ret);
+
+done:
+	if (imgld->lops->cleanup)
+		imgld->lops->cleanup(imgld);
+
+modput_exit:
+	module_put(imgld->dev.parent->driver->owner);
+
+idle_exit:
+	/*
+	 * Note: imgld->remaining_size is left unmodified here to provide
+	 * additional information on errors. It will be reinitialized when
+	 * the next image load begins.
+	 */
+	vfree(imgld->data);
+	imgld->data = NULL;
+	put_device(&imgld->dev);
+	fpga_image_prog_complete(imgld);
+}
+
+static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
+				       unsigned long arg)
+{
+	struct fpga_image_write wb;
+	unsigned long minsz;
+	u8 *buf;
+
+	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
+		return -EBUSY;
+
+	minsz = offsetofend(struct fpga_image_write, buf);
+	if (copy_from_user(&wb, (void __user *)arg, minsz))
+		return -EFAULT;
+
+	if (wb.flags)
+		return -EINVAL;
+
+	/* Enforce 32-bit alignment on the write data */
+	if (wb.size & 0x3)
+		return -EINVAL;
+
+	buf = vzalloc(wb.size);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
+		vfree(buf);
+		return -EFAULT;
+	}
+
+	imgld->data = buf;
+	imgld->remaining_size = wb.size;
+	imgld->err_code = FPGA_IMAGE_ERR_NONE;
+	imgld->progress = FPGA_IMAGE_PROG_STARTING;
+	reinit_completion(&imgld->update_done);
+	schedule_work(&imgld->work);
+
+	return 0;
+}
+
+static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
+				  unsigned long arg)
+{
+	struct fpga_image_load *imgld = filp->private_data;
+	int ret = -ENOTTY;
+
+	switch (cmd) {
+	case FPGA_IMAGE_LOAD_WRITE:
+		mutex_lock(&imgld->lock);
+		ret = fpga_image_load_ioctl_write(imgld, arg);
+		mutex_unlock(&imgld->lock);
+		break;
+	}
+
+	return ret;
+}
+
+static int fpga_image_load_open(struct inode *inode, struct file *filp)
+{
+	struct fpga_image_load *imgld = container_of(inode->i_cdev,
+						     struct fpga_image_load, cdev);
+
+	if (test_and_set_bit(0, &imgld->opened))
+		return -EBUSY;
+
+	filp->private_data = imgld;
+
+	return 0;
+}
+
+static int fpga_image_load_release(struct inode *inode, struct file *filp)
+{
+	struct fpga_image_load *imgld = filp->private_data;
+
+	clear_bit(0, &imgld->opened);
+
+	return 0;
+}
+
+static const struct file_operations fpga_image_load_fops = {
+	.owner = THIS_MODULE,
+	.open = fpga_image_load_open,
+	.release = fpga_image_load_release,
+	.unlocked_ioctl = fpga_image_load_ioctl,
+};
+
 /**
  * fpga_image_load_register - create and register an FPGA Image Load Device
  *
@@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
 			 const struct fpga_image_load_ops *lops, void *priv)
 {
 	struct fpga_image_load *imgld;
-	int id, ret;
+	int ret;
+
+	if (!lops || !lops->cancel || !lops->prepare ||
+	    !lops->write_blk || !lops->poll_complete) {
+		dev_err(parent, "Attempt to register without all required ops\n");
+		return ERR_PTR(-ENOMEM);
+	}
 
 	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
 	if (!imgld)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
 		       GFP_KERNEL);
@@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
 
 	imgld->priv = priv;
 	imgld->lops = lops;
+	imgld->err_code = FPGA_IMAGE_ERR_NONE;
+	imgld->progress = FPGA_IMAGE_PROG_IDLE;
+	init_completion(&imgld->update_done);
+	INIT_WORK(&imgld->work, fpga_image_do_load);
 
 	imgld->dev.class = fpga_image_load_class;
 	imgld->dev.parent = parent;
+	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
 
-	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
+	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
 	if (ret) {
-		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
+		dev_err(parent, "Failed to set device name: fpga_image%d\n",
+			imgld->dev.id);
 		goto error_device;
 	}
 
@@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
 		return ERR_PTR(ret);
 	}
 
+	cdev_init(&imgld->cdev, &fpga_image_load_fops);
+	imgld->cdev.owner = parent->driver->owner;
+	imgld->cdev.kobj.parent = &imgld->dev.kobj;
+
+	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
+	if (ret) {
+		put_device(&imgld->dev);
+		return ERR_PTR(ret);
+	}
+
 	return imgld;
 
 error_device:
@@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
  *
  * This function is intended for use in an FPGA Image Load driver's
  * remove() function.
+ *
+ * For some devices, once authentication of the uploaded image has begun,
+ * the hardware cannot be signaled to stop, and the driver will not exit
+ * until the hardware signals completion.  This could be 30+ minutes of
+ * waiting. The driver_unload flag enables a force-unload of the driver
+ * (e.g. modprobe -r) by signaling the parent driver to exit even if the
+ * hardware update is incomplete. The driver_unload flag also prevents
+ * new updates from starting once the unregister process has begun.
  */
 void fpga_image_load_unregister(struct fpga_image_load *imgld)
 {
+	mutex_lock(&imgld->lock);
+	imgld->driver_unload = true;
+	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
+		mutex_unlock(&imgld->lock);
+		goto unregister;
+	}
+
+	mutex_unlock(&imgld->lock);
+	wait_for_completion(&imgld->update_done);
+
+unregister:
+	cdev_del(&imgld->cdev);
 	device_unregister(&imgld->dev);
 }
 EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
@@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
 
 static int __init fpga_image_load_class_init(void)
 {
+	int ret;
 	pr_info("FPGA Image Load Driver\n");
 
 	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
 	if (IS_ERR(fpga_image_load_class))
 		return PTR_ERR(fpga_image_load_class);
 
+	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
+				  "fpga_image_load");
+	if (ret)
+		goto exit_destroy_class;
+
 	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
 
 	return 0;
+
+exit_destroy_class:
+	class_destroy(fpga_image_load_class);
+	return ret;
 }
 
 static void __exit fpga_image_load_class_exit(void)
 {
+	unregister_chrdev_region(fpga_image_devt, MINORMASK);
 	class_destroy(fpga_image_load_class);
 	WARN_ON(!xa_empty(&fpga_image_load_xa));
 }
diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
index a9cef9e1056b..b3d790e5d943 100644
--- a/include/linux/fpga/fpga-image-load.h
+++ b/include/linux/fpga/fpga-image-load.h
@@ -7,22 +7,51 @@
 #ifndef _LINUX_FPGA_IMAGE_LOAD_H
 #define _LINUX_FPGA_IMAGE_LOAD_H
 
+#include <linux/cdev.h>
+#include <linux/completion.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
 #include <linux/types.h>
+#include <uapi/linux/fpga-image-load.h>
 
 struct fpga_image_load;
 
 /**
  * struct fpga_image_load_ops - device specific operations
+ * @prepare:		    Required: Prepare secure update
+ * @write_blk:		    Required: Write a block of data
+ * @poll_complete:	    Required: Check for the completion of the
+ *			    HW authentication/programming process. This
+ *			    function should check for imgld->driver_unload
+ *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
+ * @cancel:		    Required: Signal HW to cancel update
+ * @cleanup:		    Optional: Complements the prepare()
+ *			    function and is called at the completion
+ *			    of the update, whether success or failure,
+ *			    if the prepare function succeeded.
  */
 struct fpga_image_load_ops {
+	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
+	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
+	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
+	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
+	void (*cleanup)(struct fpga_image_load *imgld);
 };
 
 struct fpga_image_load {
 	struct device dev;
+	struct cdev cdev;
 	const struct fpga_image_load_ops *lops;
 	struct mutex lock;		/* protect data structure contents */
+	unsigned long opened;
+	struct work_struct work;
+	struct completion update_done;
+	const u8 *data;				/* pointer to update data */
+	u32 remaining_size;			/* size remaining to transfer */
+	enum fpga_image_prog progress;
+	enum fpga_image_prog err_progress;	/* progress at time of failure */
+	enum fpga_image_err err_code;		/* image load error code */
+	bool driver_unload;
 	void *priv;
 };
 
diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
new file mode 100644
index 000000000000..4146a0a9e408
--- /dev/null
+++ b/include/uapi/linux/fpga-image-load.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Header File for FPGA Image Load User API
+ *
+ * Copyright (C) 2019-2021 Intel Corporation, Inc.
+ *
+ */
+
+#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
+#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define FPGA_IMAGE_LOAD_MAGIC 0xB9
+
+/* Image load progress codes */
+enum fpga_image_prog {
+	FPGA_IMAGE_PROG_IDLE,
+	FPGA_IMAGE_PROG_STARTING,
+	FPGA_IMAGE_PROG_PREPARING,
+	FPGA_IMAGE_PROG_WRITING,
+	FPGA_IMAGE_PROG_PROGRAMMING,
+	FPGA_IMAGE_PROG_MAX
+};
+
+/* Image error progress codes */
+enum fpga_image_err {
+	FPGA_IMAGE_ERR_NONE,
+	FPGA_IMAGE_ERR_HW_ERROR,
+	FPGA_IMAGE_ERR_TIMEOUT,
+	FPGA_IMAGE_ERR_CANCELED,
+	FPGA_IMAGE_ERR_BUSY,
+	FPGA_IMAGE_ERR_INVALID_SIZE,
+	FPGA_IMAGE_ERR_RW_ERROR,
+	FPGA_IMAGE_ERR_WEAROUT,
+	FPGA_IMAGE_ERR_MAX
+};
+
+#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
+
+/**
+ * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
+ *				struct fpga_image_write)
+ *
+ * Upload a data buffer to the target device. The user must provide the
+ * data buffer, size, and an eventfd file descriptor.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+struct fpga_image_write {
+	/* Input */
+	__u32 flags;		/* Zero for now */
+	__u32 size;		/* Data size (in bytes) to be written */
+	__u64 buf;		/* User space address of source data */
+};
+
+#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
-- 
2.25.1


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

* [PATCH v15 3/6] fpga: image-load: signal eventfd when complete
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
  2021-09-09  2:18 ` [PATCH v15 1/6] fpga: image-load: fpga image load class driver Russ Weight
  2021-09-09  2:18 ` [PATCH v15 2/6] fpga: image-load: enable image loads Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-09  2:18 ` [PATCH v15 4/6] fpga: image-load: add status ioctl Russ Weight
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

Amend the FPGA_IMAGE_LOAD_WRITE IOCTL implementation to include and
eventfd file descriptor as a parameter. The eventfd will be triggered
when the image upload completes.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v15:
 - This patch is new to the patch-set, and adds an eventfd to the
   FPGA_IMAGE_LOAD_WRITE IOCTL. The eventfd is signalled upon completion
   of an update.
---
 Documentation/fpga/fpga-image-load.rst |  4 +++-
 drivers/fpga/fpga-image-load.c         | 23 ++++++++++++++++++++---
 include/linux/fpga/fpga-image-load.h   |  2 ++
 include/uapi/linux/fpga-image-load.h   |  1 +
 4 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
index 2ca8d2f0212d..739d735592a5 100644
--- a/Documentation/fpga/fpga-image-load.rst
+++ b/Documentation/fpga/fpga-image-load.rst
@@ -28,4 +28,6 @@ Start an image load with the provided image buffer. This IOCTL returns
 immediately after starting a kernel worker thread to process the image load
 which could take as long a 40 minutes depending on the actual device being
 updated. This is an exclusive operation; an attempt to start concurrent image
-load for the same device will fail with EBUSY.
+load for the same device will fail with EBUSY. An eventfd file descriptor
+parameter is provided to this IOCTL, and it will be signalled at the
+completion of the image load.
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
index f5ccfa9dd977..b784456765b0 100644
--- a/drivers/fpga/fpga-image-load.c
+++ b/drivers/fpga/fpga-image-load.c
@@ -33,6 +33,7 @@ static void fpga_image_prog_complete(struct fpga_image_load *imgld)
 {
 	mutex_lock(&imgld->lock);
 	imgld->progress = FPGA_IMAGE_PROG_IDLE;
+	eventfd_signal(imgld->finished, 1);
 	complete_all(&imgld->update_done);
 	mutex_unlock(&imgld->lock);
 }
@@ -92,6 +93,8 @@ static void fpga_image_do_load(struct work_struct *work)
 	imgld->data = NULL;
 	put_device(&imgld->dev);
 	fpga_image_prog_complete(imgld);
+	eventfd_ctx_put(imgld->finished);
+	imgld->finished = NULL;
 }
 
 static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
@@ -99,6 +102,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
 {
 	struct fpga_image_write wb;
 	unsigned long minsz;
+	int ret;
 	u8 *buf;
 
 	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
@@ -115,13 +119,23 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
 	if (wb.size & 0x3)
 		return -EINVAL;
 
+	if (wb.evtfd < 0)
+		return -EINVAL;
+
 	buf = vzalloc(wb.size);
 	if (!buf)
 		return -ENOMEM;
 
 	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
-		vfree(buf);
-		return -EFAULT;
+		ret = -EFAULT;
+		goto exit_free;
+	}
+
+	imgld->finished = eventfd_ctx_fdget(wb.evtfd);
+	if (IS_ERR(imgld->finished)) {
+		ret = PTR_ERR(imgld->finished);
+		imgld->finished = NULL;
+		goto exit_free;
 	}
 
 	imgld->data = buf;
@@ -130,8 +144,11 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
 	imgld->progress = FPGA_IMAGE_PROG_STARTING;
 	reinit_completion(&imgld->update_done);
 	schedule_work(&imgld->work);
-
 	return 0;
+
+exit_free:
+	vfree(buf);
+	return ret;
 }
 
 static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
index b3d790e5d943..68f3105b51d2 100644
--- a/include/linux/fpga/fpga-image-load.h
+++ b/include/linux/fpga/fpga-image-load.h
@@ -10,6 +10,7 @@
 #include <linux/cdev.h>
 #include <linux/completion.h>
 #include <linux/device.h>
+#include <linux/eventfd.h>
 #include <linux/mutex.h>
 #include <linux/types.h>
 #include <uapi/linux/fpga-image-load.h>
@@ -52,6 +53,7 @@ struct fpga_image_load {
 	enum fpga_image_prog err_progress;	/* progress at time of failure */
 	enum fpga_image_err err_code;		/* image load error code */
 	bool driver_unload;
+	struct eventfd_ctx *finished;
 	void *priv;
 };
 
diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
index 4146a0a9e408..a60da115adf5 100644
--- a/include/uapi/linux/fpga-image-load.h
+++ b/include/uapi/linux/fpga-image-load.h
@@ -52,6 +52,7 @@ struct fpga_image_write {
 	/* Input */
 	__u32 flags;		/* Zero for now */
 	__u32 size;		/* Data size (in bytes) to be written */
+	__s32 evtfd;		/* File descriptor for completion signal */
 	__u64 buf;		/* User space address of source data */
 };
 
-- 
2.25.1


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

* [PATCH v15 4/6] fpga: image-load: add status ioctl
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
                   ` (2 preceding siblings ...)
  2021-09-09  2:18 ` [PATCH v15 3/6] fpga: image-load: signal eventfd when complete Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-10  8:50   ` Xu Yilun
  2021-09-09  2:18 ` [PATCH v15 5/6] fpga: image-load: create status sysfs node Russ Weight
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

Extend the FPGA Image Load class driver to include an
FPGA_IMAGE_LOAD_STATUS IOCTL that can be used to monitor the progress
of an ongoing image load.  The status returned includes how much data
remains to be transferred, the progress of the image load, and error
information in the case of a failure.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
V15:
 - This patch is new to the patchset and provides an FPGA_IMAGE_LOAD_STATUS
   IOCTL to return the current values for: remaining_size, progress,
   err_progress, and err_code.
 - This patch has elements of the following three patches from the previous
   patch-set:
     [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
     [PATCH v14 4/6] fpga: sec-mgr: expose sec-mgr update errors
     [PATCH v14 5/6] fpga: sec-mgr: expose sec-mgr update size
 - Changed file, symbol, and config names to reflect the new driver name
 - There are some minor changes to locking to enable this ioctl to return
   coherent data.
---
 Documentation/fpga/fpga-image-load.rst |  6 +++
 drivers/fpga/fpga-image-load.c         | 53 ++++++++++++++++++++++----
 include/uapi/linux/fpga-image-load.h   | 18 +++++++++
 3 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
index 739d735592a5..3d5eb51223e3 100644
--- a/Documentation/fpga/fpga-image-load.rst
+++ b/Documentation/fpga/fpga-image-load.rst
@@ -31,3 +31,9 @@ updated. This is an exclusive operation; an attempt to start concurrent image
 load for the same device will fail with EBUSY. An eventfd file descriptor
 parameter is provided to this IOCTL, and it will be signalled at the
 completion of the image load.
+
+FPGA_IMAGE_LOAD_STATUS:
+
+Collect status for an on-going image upload. The status returned includes
+how much data remains to be transferred, the progress of the image load,
+and error information in the case of a failure.
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
index b784456765b0..99a47b21c995 100644
--- a/drivers/fpga/fpga-image-load.c
+++ b/drivers/fpga/fpga-image-load.c
@@ -22,10 +22,27 @@ static dev_t fpga_image_devt;
 
 #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
 
-static void fpga_image_dev_error(struct fpga_image_load *imgld,
+static void fpga_image_update_progress(struct fpga_image_load *imgld,
+				       enum fpga_image_prog new_progress)
+{
+	mutex_lock(&imgld->lock);
+	imgld->progress = new_progress;
+	mutex_unlock(&imgld->lock);
+}
+
+static void fpga_image_set_error(struct fpga_image_load *imgld,
 				 enum fpga_image_err err_code)
 {
+	mutex_lock(&imgld->lock);
+	imgld->err_progress = imgld->progress;
 	imgld->err_code = err_code;
+	mutex_unlock(&imgld->lock);
+}
+
+static void fpga_image_dev_error(struct fpga_image_load *imgld,
+				 enum fpga_image_err err_code)
+{
+	fpga_image_set_error(imgld, err_code);
 	imgld->lops->cancel(imgld);
 }
 
@@ -49,18 +66,18 @@ static void fpga_image_do_load(struct work_struct *work)
 
 	get_device(&imgld->dev);
 	if (!try_module_get(imgld->dev.parent->driver->owner)) {
-		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
+		fpga_image_set_error(imgld, FPGA_IMAGE_ERR_BUSY);
 		goto idle_exit;
 	}
 
-	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
+	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PREPARING);
 	ret = imgld->lops->prepare(imgld);
 	if (ret != FPGA_IMAGE_ERR_NONE) {
 		fpga_image_dev_error(imgld, ret);
 		goto modput_exit;
 	}
 
-	imgld->progress = FPGA_IMAGE_PROG_WRITING;
+	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
 	while (imgld->remaining_size) {
 		ret = imgld->lops->write_blk(imgld, offset);
 		if (ret != FPGA_IMAGE_ERR_NONE) {
@@ -71,7 +88,7 @@ static void fpga_image_do_load(struct work_struct *work)
 		offset = size - imgld->remaining_size;
 	}
 
-	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
+	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
 	ret = imgld->lops->poll_complete(imgld);
 	if (ret != FPGA_IMAGE_ERR_NONE)
 		fpga_image_dev_error(imgld, ret);
@@ -151,20 +168,42 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
 	return ret;
 }
 
+static int fpga_image_load_ioctl_status(struct fpga_image_load *imgld,
+					unsigned long arg)
+{
+	struct fpga_image_status status;
+
+	memset(&status, 0, sizeof(status));
+	status.progress = imgld->progress;
+	status.remaining_size = imgld->remaining_size;
+	status.err_progress = imgld->err_progress;
+	status.err_code = imgld->err_code;
+
+	if (copy_to_user((void __user *)arg, &status, sizeof(status)))
+		return -EFAULT;
+
+	return 0;
+}
+
 static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
 				  unsigned long arg)
 {
 	struct fpga_image_load *imgld = filp->private_data;
 	int ret = -ENOTTY;
 
+	mutex_lock(&imgld->lock);
+
 	switch (cmd) {
 	case FPGA_IMAGE_LOAD_WRITE:
-		mutex_lock(&imgld->lock);
 		ret = fpga_image_load_ioctl_write(imgld, arg);
-		mutex_unlock(&imgld->lock);
+		break;
+	case FPGA_IMAGE_LOAD_STATUS:
+		ret = fpga_image_load_ioctl_status(imgld, arg);
 		break;
 	}
 
+	mutex_unlock(&imgld->lock);
+
 	return ret;
 }
 
diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
index a60da115adf5..6a995bcc0fb7 100644
--- a/include/uapi/linux/fpga-image-load.h
+++ b/include/uapi/linux/fpga-image-load.h
@@ -38,6 +38,7 @@ enum fpga_image_err {
 };
 
 #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
+#define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
 
 /**
  * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
@@ -56,4 +57,21 @@ struct fpga_image_write {
 	__u64 buf;		/* User space address of source data */
 };
 
+/**
+ * FPGA_IMAGE_LOAD_STATUS - _IOR(FPGA_IMAGE_LOAD_MAGIC, 1,
+ *				 struct fpga_image_status)
+ *
+ * Request status information for an ongoing update.
+ * data buffer, size, and an eventfd file descriptor.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+struct fpga_image_status {
+	/* Output */
+	__u32 remaining_size;			/* size remaining to transfer */
+	enum fpga_image_prog progress;		/* current progress of image load */
+	enum fpga_image_prog err_progress;	/* progress at time of error */
+	enum fpga_image_err err_code;		/* error code */
+};
+
 #endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
-- 
2.25.1


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

* [PATCH v15 5/6] fpga: image-load: create status sysfs node
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
                   ` (3 preceding siblings ...)
  2021-09-09  2:18 ` [PATCH v15 4/6] fpga: image-load: add status ioctl Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-10  8:52   ` Xu Yilun
  2021-09-09  2:18 ` [PATCH v15 6/6] fpga: image-load: enable cancel of image upload Russ Weight
       [not found] ` <20210912023739.4078-1-hdanton@sina.com>
  6 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

Extend the FPGA Image Load class driver to include a status sysfs node that
can be viewed to determine from the command line if an image load is in
progress. Status will be one of: idle, starting, preparing, writing, or
programming.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v15:
 - Compare to previous patch:
     [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
 - Changed file, symbol, and config names to reflect the new driver name
 - Removed signed-off/reviewed-by tags
v14:
 - Updated ABI documentation date and kernel version
v13:
  - No change
v12:
  - Updated Date and KernelVersion fields in ABI documentation
  - Changed syntax of sec_mgr_prog_str[] array definition from:
	"idle",			/* FPGA_SEC_PROG_IDLE */
    to:
	[FPGA_SEC_PROG_IDLE]	    = "idle",
v11:
  - No change
v10:
  - Rebased to 5.12-rc2 next
  - Updated Date and KernelVersion in ABI documentation
v9:
  - Updated Date and KernelVersion in ABI documentation
v8:
  - No change
v7:
  - Changed Date in documentation file to December 2020
v6:
  - No change
v5:
  - Use new function sysfs_emit() in the status_show() function
v4:
  - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
    and removed unnecessary references to "Intel".
  - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
v3:
  - Use a local variable to read progress once in status_show()
  - Use dev_err to report invalid progress status
v2:
  - Bumped documentation date and version
  - Changed progress state "read_file" to "reading"
---
 .../ABI/testing/sysfs-class-fpga-image-load   |  7 ++++
 MAINTAINERS                                   |  1 +
 drivers/fpga/fpga-image-load.c                | 33 +++++++++++++++++++
 3 files changed, 41 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-image-load

diff --git a/Documentation/ABI/testing/sysfs-class-fpga-image-load b/Documentation/ABI/testing/sysfs-class-fpga-image-load
new file mode 100644
index 000000000000..6c04a49f01cc
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-fpga-image-load
@@ -0,0 +1,7 @@
+What: 		/sys/class/fpga_image_load/fpga_imageX/status
+Date:		Aug 2021
+KernelVersion:	5.15
+Contact:	Russ Weight <russell.h.weight@intel.com>
+Description:	Read-only. Returns a string describing the current status
+		of an FPGA image upload. The string will be one of the
+		following: idle, starting, preparing, writing, programming.
diff --git a/MAINTAINERS b/MAINTAINERS
index 637bc003ca81..e3fbc1bde9bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7362,6 +7362,7 @@ FPGA SECURITY MANAGER DRIVERS
 M:	Russ Weight <russell.h.weight@intel.com>
 L:	linux-fpga@vger.kernel.org
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-class-fpga-image-load
 F:	Documentation/fpga/fpga-image-load.rst
 F:	drivers/fpga/fpga-image-load.c
 F:	include/linux/fpga/fpga-image-load.h
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
index 99a47b21c995..6ec0a39f07b3 100644
--- a/drivers/fpga/fpga-image-load.c
+++ b/drivers/fpga/fpga-image-load.c
@@ -236,6 +236,38 @@ static const struct file_operations fpga_image_load_fops = {
 	.unlocked_ioctl = fpga_image_load_ioctl,
 };
 
+static const char * const image_load_prog_str[] = {
+	[FPGA_IMAGE_PROG_IDLE]	      = "idle",
+	[FPGA_IMAGE_PROG_STARTING]    = "starting",
+	[FPGA_IMAGE_PROG_PREPARING]   = "preparing",
+	[FPGA_IMAGE_PROG_WRITING]     = "writing",
+	[FPGA_IMAGE_PROG_PROGRAMMING] = "programming"
+};
+
+static ssize_t
+status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fpga_image_load *imgld = to_image_load(dev);
+	const char *status = "unknown-status";
+	enum fpga_image_prog progress;
+
+	progress = imgld->progress;
+	if (progress < FPGA_IMAGE_PROG_MAX)
+		status = image_load_prog_str[progress];
+	else
+		dev_err(dev, "Invalid status during secure update: %d\n",
+			progress);
+
+	return sysfs_emit(buf, "%s\n", status);
+}
+static DEVICE_ATTR_RO(status);
+
+static struct attribute *fpga_image_load_attrs[] = {
+	&dev_attr_status.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(fpga_image_load);
+
 /**
  * fpga_image_load_register - create and register an FPGA Image Load Device
  *
@@ -373,6 +405,7 @@ static int __init fpga_image_load_class_init(void)
 	if (ret)
 		goto exit_destroy_class;
 
+	fpga_image_load_class->dev_groups = fpga_image_load_groups;
 	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
 
 	return 0;
-- 
2.25.1


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

* [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
                   ` (4 preceding siblings ...)
  2021-09-09  2:18 ` [PATCH v15 5/6] fpga: image-load: create status sysfs node Russ Weight
@ 2021-09-09  2:18 ` Russ Weight
  2021-09-10 14:55   ` Xu Yilun
       [not found] ` <20210912023739.4078-1-hdanton@sina.com>
  6 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-09  2:18 UTC (permalink / raw)
  To: mdf, linux-fpga, linux-kernel
  Cc: trix, lgoncalv, yilun.xu, hao.wu, matthew.gerlach, Russ Weight

Extend the FPGA Image Load class driver to include a cancel IOCTL that
can be used to request that an image upload be canceled. The IOCTL may
return EBUSY if it cannot be canceled by software or ENODEV if there
is no update in progress.

Signed-off-by: Russ Weight <russell.h.weight@intel.com>
---
v15:
 - Compare to previous patch:
     [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
 - Changed file, symbol, and config names to reflect the new driver name
 - Cancel is now initiated by IOCT instead of sysfs
 - Removed signed-off/reviewed-by tags
v14:
 - Updated ABI documentation date and kernel version
v13:
  - No change
v12:
  - Updated Date and KernelVersion fields in ABI documentation
v11:
  - No change
v10:
  - Rebased to 5.12-rc2 next
  - Updated Date and KernelVersion in ABI documentation
v9:
  - Updated Date and KernelVersion in ABI documentation
v8:
  - No change
v7:
  - Changed Date in documentation file to December 2020
v6:
  - No change
v5:
  - No change
v4:
  - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
    and removed unnecessary references to "Intel".
  - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
v3:
  - No change
v2:
  - Bumped documentation date and version
  - Minor code cleanup per review comments
---
---
 Documentation/fpga/fpga-image-load.rst |  6 ++++
 drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
 include/linux/fpga/fpga-image-load.h   |  1 +
 include/uapi/linux/fpga-image-load.h   |  1 +
 4 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
index 3d5eb51223e3..763e7833a6ea 100644
--- a/Documentation/fpga/fpga-image-load.rst
+++ b/Documentation/fpga/fpga-image-load.rst
@@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
 Collect status for an on-going image upload. The status returned includes
 how much data remains to be transferred, the progress of the image load,
 and error information in the case of a failure.
+
+FPGA_IMAGE_LOAD_CANCEL:
+
+Request that a on-going image upload be cancelled. This IOCTL may return
+EBUSY if it cannot be cancelled by software or ENODEV if there is no update
+in progress.
diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
index 6ec0a39f07b3..c32e4b1ea35a 100644
--- a/drivers/fpga/fpga-image-load.c
+++ b/drivers/fpga/fpga-image-load.c
@@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
 	imgld->lops->cancel(imgld);
 }
 
+static int fpga_image_prog_transition(struct fpga_image_load *imgld,
+				      enum fpga_image_prog new_progress)
+{
+	int ret = 0;
+
+	mutex_lock(&imgld->lock);
+	if (imgld->request_cancel) {
+		imgld->err_progress = imgld->progress;
+		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
+		imgld->lops->cancel(imgld);
+		ret = -ECANCELED;
+	} else {
+		imgld->progress = new_progress;
+	}
+	mutex_unlock(&imgld->lock);
+	return ret;
+}
+
 static void fpga_image_prog_complete(struct fpga_image_load *imgld)
 {
 	mutex_lock(&imgld->lock);
@@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
 		goto modput_exit;
 	}
 
-	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
-	while (imgld->remaining_size) {
+	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
+		goto done;
+
+	while (imgld->remaining_size && !imgld->request_cancel) {
 		ret = imgld->lops->write_blk(imgld, offset);
 		if (ret != FPGA_IMAGE_ERR_NONE) {
 			fpga_image_dev_error(imgld, ret);
@@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
 		offset = size - imgld->remaining_size;
 	}
 
-	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
+	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
+		goto done;
+
 	ret = imgld->lops->poll_complete(imgld);
 	if (ret != FPGA_IMAGE_ERR_NONE)
 		fpga_image_dev_error(imgld, ret);
@@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
 	imgld->remaining_size = wb.size;
 	imgld->err_code = FPGA_IMAGE_ERR_NONE;
 	imgld->progress = FPGA_IMAGE_PROG_STARTING;
+	imgld->request_cancel = false;
 	reinit_completion(&imgld->update_done);
 	schedule_work(&imgld->work);
 	return 0;
@@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
 				  unsigned long arg)
 {
 	struct fpga_image_load *imgld = filp->private_data;
-	int ret = -ENOTTY;
+	int ret = 0;
 
 	mutex_lock(&imgld->lock);
 
@@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
 	case FPGA_IMAGE_LOAD_STATUS:
 		ret = fpga_image_load_ioctl_status(imgld, arg);
 		break;
+	case FPGA_IMAGE_LOAD_CANCEL:
+		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
+			ret = -EBUSY;
+		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
+			ret = -ENODEV;
+		else
+			imgld->request_cancel = true;
+		break;
+	default:
+		ret = -ENOTTY;
+		break;
 	}
 
 	mutex_unlock(&imgld->lock);
@@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
 		goto unregister;
 	}
 
+	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
+		imgld->request_cancel = true;
+
 	mutex_unlock(&imgld->lock);
 	wait_for_completion(&imgld->update_done);
 
diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
index 68f3105b51d2..4e51b9fd1724 100644
--- a/include/linux/fpga/fpga-image-load.h
+++ b/include/linux/fpga/fpga-image-load.h
@@ -52,6 +52,7 @@ struct fpga_image_load {
 	enum fpga_image_prog progress;
 	enum fpga_image_prog err_progress;	/* progress at time of failure */
 	enum fpga_image_err err_code;		/* image load error code */
+	bool request_cancel;
 	bool driver_unload;
 	struct eventfd_ctx *finished;
 	void *priv;
diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
index 6a995bcc0fb7..8d0dfa1f9b77 100644
--- a/include/uapi/linux/fpga-image-load.h
+++ b/include/uapi/linux/fpga-image-load.h
@@ -39,6 +39,7 @@ enum fpga_image_err {
 
 #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
 #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
+#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
 
 /**
  * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
-- 
2.25.1


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

* Re: [PATCH v15 1/6] fpga: image-load: fpga image load class driver
  2021-09-09  2:18 ` [PATCH v15 1/6] fpga: image-load: fpga image load class driver Russ Weight
@ 2021-09-10  6:46   ` Xu Yilun
  2021-09-10 20:47     ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-10  6:46 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Wed, Sep 08, 2021 at 07:18:41PM -0700, Russ Weight wrote:
> The FPGA Image Load class driver provides an API to transfer update
> files to an FPGA device. Image files are self-describing. They could
> contain FPGA images, BMC images, Root Entry Hashes, or other device
> specific files. It is up to the device driver and the target device
> to authenticate and disposition the file data.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v15:
>  - Compare to previous patch:
>      [PATCH v14 1/6] fpga: sec-mgr: fpga security manager class driver 
>  - Changed file, symbol, and config names to reflect the new driver name
>  - Rewrote documentation. The documentation will be added to in later patches.
>  - Removed signed-off/reviewed-by tags
> v14:
>  - Updated copyright to 2021
>  - Removed the name sysfs entry
>  - Removed MAINTAINERS reference to
>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
>  - Use xa_alloc() instead of ida_simple_get()
>  - Rename dev to parent for parent devices
>  - Remove fpga_sec_mgr_create(), devm_fpga_sec_mgr_create(), and
>    fpga_sec_mgr_free() functions and update the fpga_sec_mgr_register()
>    function to both create and register a new security manager.
>  - Populate the fpga_sec_mgr_dev_release() function.
> v13:
>   - No change
> v12:
>   - Updated Date and KernelVersion fields in ABI documentation
> v11:
>   - No change
> v10:
>   - Rebased to 5.12-rc2 next
>   - Updated Date and KernelVersion in ABI documentation
> v9:
>   - Updated Date and KernelVersion in ABI documentation
> v8:
>   - Fixed grammatical error in Documentation/fpga/fpga-sec-mgr.rst
> v7:
>   - Changed Date in documentation file to December 2020
> v6:
>   - Removed sysfs support and documentation for the display of the
>     flash count, root entry hashes, and code-signing-key cancelation
>     vectors.
> v5:
>   - Added the devm_fpga_sec_mgr_unregister() function, following recent
>     changes to the fpga_manager() implementation.
>   - Changed some *_show() functions to use sysfs_emit() instead of sprintf(
> v4:
>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>     and removed unnecessary references to "Intel".
>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> v3:
>   - Modified sysfs handler check in check_sysfs_handler() to make
>     it more readable.
> v2:
>   - Bumped documentation dates and versions
>   - Added Documentation/fpga/ifpga-sec-mgr.rst
>   - Removed references to bmc_flash_count & smbus_flash_count (not supported)
>   - Split ifpga_sec_mgr_register() into create() and register() functions
>   - Added devm_ifpga_sec_mgr_create()
>   - Removed typedefs for imgr ops
> ---
> ---
>  Documentation/fpga/fpga-image-load.rst |  10 ++
>  Documentation/fpga/index.rst           |   1 +
>  MAINTAINERS                            |   8 ++
>  drivers/fpga/Kconfig                   |  10 ++
>  drivers/fpga/Makefile                  |   3 +
>  drivers/fpga/fpga-image-load.c         | 124 +++++++++++++++++++++++++
>  include/linux/fpga/fpga-image-load.h   |  35 +++++++
>  7 files changed, 191 insertions(+)
>  create mode 100644 Documentation/fpga/fpga-image-load.rst
>  create mode 100644 drivers/fpga/fpga-image-load.c
>  create mode 100644 include/linux/fpga/fpga-image-load.h
> 
> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> new file mode 100644
> index 000000000000..a6e53ac66026
> --- /dev/null
> +++ b/Documentation/fpga/fpga-image-load.rst
> @@ -0,0 +1,10 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +============================
> +FPGA Image Load Class Driver
> +============================
> +
> +The FPGA Image Load class driver provides a common API for user-space
> +tools to manage image uploads to FPGA devices. Device drivers that
> +instantiate the FPGA Image Load class driver will interact with the
> +target device to transfer and authenticate the image data.
> diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
> index f80f95667ca2..85d25fb22c08 100644
> --- a/Documentation/fpga/index.rst
> +++ b/Documentation/fpga/index.rst
> @@ -8,6 +8,7 @@ fpga
>      :maxdepth: 1
>  
>      dfl
> +    fpga-image-load
>  
>  .. only::  subproject and html
>  
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 6c63415d2ac2..4e7f48fa7e5c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7358,6 +7358,14 @@ F:	Documentation/fpga/
>  F:	drivers/fpga/
>  F:	include/linux/fpga/
>  
> +FPGA SECURITY MANAGER DRIVERS
> +M:	Russ Weight <russell.h.weight@intel.com>
> +L:	linux-fpga@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/fpga/fpga-image-load.rst
> +F:	drivers/fpga/fpga-image-load.c
> +F:	include/linux/fpga/fpga-image-load.h
> +
>  FPU EMULATOR
>  M:	Bill Metzenthen <billm@melbpc.org.au>
>  S:	Maintained
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 991b3f361ec9..c12a14e62fff 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -243,4 +243,14 @@ config FPGA_MGR_VERSAL_FPGA
>  	  configure the programmable logic(PL).
>  
>  	  To compile this as a module, choose M here.
> +
> +config FPGA_IMAGE_LOAD
> +	tristate "FPGA Image Load Driver"

Maybe we don't call it "Driver". A framework or "FPGA Image load support",
is it better?

There are more descriptions about "driver" below, maybe you need to change
them all.

> +	help
> +	  The FPGA Image Load class driver presents a common user API for
> +	  uploading an image file to an FPGA device. The image file is
> +	  expected to be self-describing. It is up to the device driver
> +	  and/or the device itself to authenticate and disposition the
> +	  image data.
> +
>  endif # FPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 0bff783d1b61..adf228ee4f5e 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -22,6 +22,9 @@ obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)      += versal-fpga.o
>  obj-$(CONFIG_ALTERA_PR_IP_CORE)         += altera-pr-ip-core.o
>  obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT)    += altera-pr-ip-core-plat.o
>  
> +# FPGA Image Load Framework
> +obj-$(CONFIG_FPGA_IMAGE_LOAD)		+= fpga-image-load.o
> +
>  # FPGA Bridge Drivers
>  obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
>  obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> new file mode 100644
> index 000000000000..7d75bbcff541
> --- /dev/null
> +++ b/drivers/fpga/fpga-image-load.c
> @@ -0,0 +1,124 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FPGA Image Load Class Driver
> + *
> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
> + */
> +
> +#include <linux/fpga/fpga-image-load.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +
> +#define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
> +static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
> +
> +static struct class *fpga_image_load_class;
> +
> +#define to_image_load(d) container_of(d, struct fpga_image_load, dev)
> +
> +/**
> + * fpga_image_load_register - create and register an FPGA Image Load Device
> + *
> + * @parent: fpga image load device from pdev
> + * @lops:   pointer to a structure of image load callback functions

Maybe "ops" is just good, some more below.

> + * @priv:   fpga image load private data
> + *
> + * Returns a struct fpga_image_load pointer on success, or ERR_PTR() on
> + * error. The caller of this function is responsible for calling
> + * fpga_image_load_unregister().
> + */
> +struct fpga_image_load *
> +fpga_image_load_register(struct device *parent,
> +			 const struct fpga_image_load_ops *lops, void *priv)
> +{
> +	struct fpga_image_load *imgld;
> +	int id, ret;
> +
> +	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
> +	if (!imgld)
> +		return NULL;
> +
> +	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
> +		       GFP_KERNEL);
> +	if (ret)
> +		goto error_kfree;
> +
> +	mutex_init(&imgld->lock);
> +
> +	imgld->priv = priv;
> +	imgld->lops = lops;
> +
> +	imgld->dev.class = fpga_image_load_class;
> +	imgld->dev.parent = parent;
> +
> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);

Is it better "fpga_image_load%d"?

> +	if (ret) {
> +		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
> +		goto error_device;
> +	}
> +
> +	ret = device_register(&imgld->dev);
> +	if (ret) {
> +		put_device(&imgld->dev);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return imgld;
> +
> +error_device:
> +	xa_erase(&fpga_image_load_xa, imgld->dev.id);
> +
> +error_kfree:
> +	kfree(imgld);
> +
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(fpga_image_load_register);
> +
> +/**
> + * fpga_image_load_unregister - unregister an FPGA image load device
> + *
> + * @imgld: pointer to struct fpga_image_load
> + *
> + * This function is intended for use in an FPGA Image Load driver's
> + * remove() function.
> + */
> +void fpga_image_load_unregister(struct fpga_image_load *imgld)
> +{
> +	device_unregister(&imgld->dev);
> +}
> +EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
> +
> +static void fpga_image_load_dev_release(struct device *dev)
> +{
> +	struct fpga_image_load *imgld = to_image_load(dev);
> +
> +	xa_erase(&fpga_image_load_xa, imgld->dev.id);
> +	kfree(imgld);
> +}
> +
> +static int __init fpga_image_load_class_init(void)
> +{
> +	pr_info("FPGA Image Load Driver\n");
> +
> +	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
> +	if (IS_ERR(fpga_image_load_class))
> +		return PTR_ERR(fpga_image_load_class);
> +
> +	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
> +
> +	return 0;
> +}
> +
> +static void __exit fpga_image_load_class_exit(void)
> +{
> +	class_destroy(fpga_image_load_class);
> +	WARN_ON(!xa_empty(&fpga_image_load_xa));
> +}
> +
> +MODULE_DESCRIPTION("FPGA Image Load Driver");
> +MODULE_LICENSE("GPL v2");
> +
> +subsys_initcall(fpga_image_load_class_init);
> +module_exit(fpga_image_load_class_exit)
> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> new file mode 100644
> index 000000000000..a9cef9e1056b
> --- /dev/null
> +++ b/include/linux/fpga/fpga-image-load.h
> @@ -0,0 +1,35 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for FPGA Image Load Driver
> + *
> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
> + */
> +#ifndef _LINUX_FPGA_IMAGE_LOAD_H
> +#define _LINUX_FPGA_IMAGE_LOAD_H
> +
> +#include <linux/device.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +
> +struct fpga_image_load;
> +
> +/**
> + * struct fpga_image_load_ops - device specific operations
> + */
> +struct fpga_image_load_ops {
> +};
> +
> +struct fpga_image_load {
> +	struct device dev;
> +	const struct fpga_image_load_ops *lops;
> +	struct mutex lock;		/* protect data structure contents */
> +	void *priv;
> +};
> +
> +struct fpga_image_load *
> +fpga_image_load_register(struct device *dev,
> +			 const struct fpga_image_load_ops *lops, void *priv);
> +
> +void fpga_image_load_unregister(struct fpga_image_load *imgld);
> +
> +#endif
> -- 
> 2.25.1

Thanks,
Yilun

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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-09  2:18 ` [PATCH v15 2/6] fpga: image-load: enable image loads Russ Weight
@ 2021-09-10  8:22   ` Xu Yilun
  2021-09-10 23:18     ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-10  8:22 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote:
> Extend the FPGA Image Load class driver to include IOCTL support
> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
> The IOCTL will return immediately, and the update will begin in the
> context of a kernel worker thread.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v15:
>  - Compare to previous patch:
>      [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
>  - Changed file, symbol, and config names to reflect the new driver name
>  - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
>    IOCTL for writing the image data to the FPGA card. The driver no longer
>    uses the request_firmware framework.
>  - Fixed some error return values in fpga_image_load_register()
>  - Removed signed-off/reviewed-by tags
> v14:
>  - Added MAINTAINERS reference for
>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
>  - Updated ABI documentation date and kernel version
>  - Updated copyright to 2021
> v13:
>   - Change "if (count == 0 || " to "if (!count || "
>   - Improve error message: "Attempt to register without all required ops\n"
> v12:
>   - Updated Date and KernelVersion fields in ABI documentation
>   - Removed size parameter from write_blk() op - it is now up to
>     the lower-level driver to determine the appropriate size and
>     to update smgr->remaining_size accordingly.
> v11:
>   - Fixed a spelling error in a comment
>   - Initialize smgr->err_code and smgr->progress explicitly in
>     fpga_sec_mgr_create() instead of accepting the default 0 value.
> v10:
>   - Rebased to 5.12-rc2 next
>   - Updated Date and KernelVersion in ABI documentation
> v9:
>   - Updated Date and KernelVersion in ABI documentation
> v8:
>   - No change
> v7:
>   - Changed Date in documentation file to December 2020
>   - Changed filename_store() to use kmemdup_nul() instead of
>     kstrndup() and changed the count to not assume a line-return.
> v6:
>   - Changed "security update" to "secure update" in commit message
> v5:
>   - When checking the return values for functions of type enum
>     fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
> v4:
>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>     and removed unnecessary references to "Intel".
>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> v3:
>   - Removed unnecessary "goto done"
>   - Added a comment to explain imgr->driver_unload in
>     ifpga_sec_mgr_unregister()
> v2:
>   - Bumped documentation date and version
>   - Removed explicit value assignments in enums
>   - Other minor code cleanup per review comments
> ---
>  Documentation/fpga/fpga-image-load.rst |  21 +++
>  MAINTAINERS                            |   1 +
>  drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
>  include/linux/fpga/fpga-image-load.h   |  29 ++++
>  include/uapi/linux/fpga-image-load.h   |  58 +++++++
>  5 files changed, 329 insertions(+), 4 deletions(-)
>  create mode 100644 include/uapi/linux/fpga-image-load.h
> 
> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> index a6e53ac66026..2ca8d2f0212d 100644
> --- a/Documentation/fpga/fpga-image-load.rst
> +++ b/Documentation/fpga/fpga-image-load.rst
> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
>  tools to manage image uploads to FPGA devices. Device drivers that
>  instantiate the FPGA Image Load class driver will interact with the
>  target device to transfer and authenticate the image data.
> +
> +User API
> +========
> +
> +open
> +----
> +
> +An FPGA Image Load device is opened exclusively to control an image load.
> +Image loads are processed by a kernel worker thread. A user may choose
> +close the device while the upload continues.

Why we allow the user to close the dev while the uploading is ongoing?
Seems it introduces more checking effort when another user open the dev
again and try another uploading.

> +
> +ioctl
> +-----
> +
> +FPGA_IMAGE_LOAD_WRITE:
> +
> +Start an image load with the provided image buffer. This IOCTL returns
> +immediately after starting a kernel worker thread to process the image load
> +which could take as long a 40 minutes depending on the actual device being
> +updated. This is an exclusive operation; an attempt to start concurrent image
> +load for the same device will fail with EBUSY.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 4e7f48fa7e5c..637bc003ca81 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7365,6 +7365,7 @@ S:	Maintained
>  F:	Documentation/fpga/fpga-image-load.rst
>  F:	drivers/fpga/fpga-image-load.c
>  F:	include/linux/fpga/fpga-image-load.h
> +F:	include/uapi/linux/fpga-image-load.h
>  
>  FPU EMULATOR
>  M:	Bill Metzenthen <billm@melbpc.org.au>
> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> index 7d75bbcff541..f5ccfa9dd977 100644
> --- a/drivers/fpga/fpga-image-load.c
> +++ b/drivers/fpga/fpga-image-load.c
> @@ -5,18 +5,181 @@
>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
>   */
>  
> +#include <linux/delay.h>
>  #include <linux/fpga/fpga-image-load.h>
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
>  #include <linux/module.h>
>  #include <linux/slab.h>
> +#include <linux/uaccess.h>
>  #include <linux/vmalloc.h>
>  
>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
>  
>  static struct class *fpga_image_load_class;
> +static dev_t fpga_image_devt;
>  
>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>  
> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
> +				 enum fpga_image_err err_code)
> +{
> +	imgld->err_code = err_code;
> +	imgld->lops->cancel(imgld);
> +}
> +
> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
> +{
> +	mutex_lock(&imgld->lock);
> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> +	complete_all(&imgld->update_done);
> +	mutex_unlock(&imgld->lock);
> +}
> +
> +static void fpga_image_do_load(struct work_struct *work)
> +{
> +	struct fpga_image_load *imgld;
> +	enum fpga_image_err ret;
> +	u32 size, offset = 0;
> +
> +	imgld = container_of(work, struct fpga_image_load, work);
> +	size = imgld->remaining_size;
> +
> +	get_device(&imgld->dev);
> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
> +		goto idle_exit;
> +	}
> +
> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
> +	ret = imgld->lops->prepare(imgld);
> +	if (ret != FPGA_IMAGE_ERR_NONE) {
> +		fpga_image_dev_error(imgld, ret);
> +		goto modput_exit;
> +	}
> +
> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
> +	while (imgld->remaining_size) {
> +		ret = imgld->lops->write_blk(imgld, offset);
> +		if (ret != FPGA_IMAGE_ERR_NONE) {
> +			fpga_image_dev_error(imgld, ret);
> +			goto done;
> +		}
> +
> +		offset = size - imgld->remaining_size;

The low level driver is required to update the "remaining_size" in
write_blk ops?

The API seems ambiguous. The framework asks for writing a block of data,
but no block size is specified.

> +	}
> +
> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
> +	ret = imgld->lops->poll_complete(imgld);
> +	if (ret != FPGA_IMAGE_ERR_NONE)
> +		fpga_image_dev_error(imgld, ret);
> +
> +done:
> +	if (imgld->lops->cleanup)
> +		imgld->lops->cleanup(imgld);
> +
> +modput_exit:
> +	module_put(imgld->dev.parent->driver->owner);
> +
> +idle_exit:
> +	/*
> +	 * Note: imgld->remaining_size is left unmodified here to provide
> +	 * additional information on errors. It will be reinitialized when
> +	 * the next image load begins.
> +	 */
> +	vfree(imgld->data);
> +	imgld->data = NULL;
> +	put_device(&imgld->dev);
> +	fpga_image_prog_complete(imgld);
> +}
> +
> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
> +				       unsigned long arg)
> +{
> +	struct fpga_image_write wb;
> +	unsigned long minsz;
> +	u8 *buf;
> +
> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
> +		return -EBUSY;
> +
> +	minsz = offsetofend(struct fpga_image_write, buf);
> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
> +		return -EFAULT;
> +
> +	if (wb.flags)
> +		return -EINVAL;
> +
> +	/* Enforce 32-bit alignment on the write data */
> +	if (wb.size & 0x3)
> +		return -EINVAL;
> +
> +	buf = vzalloc(wb.size);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
> +		vfree(buf);
> +		return -EFAULT;
> +	}
> +
> +	imgld->data = buf;
> +	imgld->remaining_size = wb.size;
> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
> +	reinit_completion(&imgld->update_done);
> +	schedule_work(&imgld->work);
> +
> +	return 0;
> +}
> +
> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> +				  unsigned long arg)
> +{
> +	struct fpga_image_load *imgld = filp->private_data;
> +	int ret = -ENOTTY;
> +
> +	switch (cmd) {
> +	case FPGA_IMAGE_LOAD_WRITE:
> +		mutex_lock(&imgld->lock);
> +		ret = fpga_image_load_ioctl_write(imgld, arg);
> +		mutex_unlock(&imgld->lock);
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
> +{
> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
> +						     struct fpga_image_load, cdev);
> +
> +	if (test_and_set_bit(0, &imgld->opened))

Some more flags to add for "opened" field? But the field name indicates
it is a single flag.

> +		return -EBUSY;
> +
> +	filp->private_data = imgld;
> +
> +	return 0;
> +}
> +
> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
> +{
> +	struct fpga_image_load *imgld = filp->private_data;
> +
> +	clear_bit(0, &imgld->opened);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations fpga_image_load_fops = {
> +	.owner = THIS_MODULE,
> +	.open = fpga_image_load_open,
> +	.release = fpga_image_load_release,
> +	.unlocked_ioctl = fpga_image_load_ioctl,
> +};
> +
>  /**
>   * fpga_image_load_register - create and register an FPGA Image Load Device
>   *
> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
>  			 const struct fpga_image_load_ops *lops, void *priv)
>  {
>  	struct fpga_image_load *imgld;
> -	int id, ret;
> +	int ret;
> +
> +	if (!lops || !lops->cancel || !lops->prepare ||
> +	    !lops->write_blk || !lops->poll_complete) {
> +		dev_err(parent, "Attempt to register without all required ops\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
>  
>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
>  	if (!imgld)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);

This is the fix for Patch #1? If yes please merge it to Patch #1.

>  
>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
>  		       GFP_KERNEL);
> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
>  
>  	imgld->priv = priv;
>  	imgld->lops = lops;
> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> +	init_completion(&imgld->update_done);
> +	INIT_WORK(&imgld->work, fpga_image_do_load);
>  
>  	imgld->dev.class = fpga_image_load_class;
>  	imgld->dev.parent = parent;
> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
>  
> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);

Another fix? Please merge it to Patch #1.

>  	if (ret) {
> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
> +			imgld->dev.id);

Ditto

>  		goto error_device;
>  	}
>  
> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
>  		return ERR_PTR(ret);
>  	}
>  
> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
> +	imgld->cdev.owner = parent->driver->owner;
> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
> +
> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
> +	if (ret) {
> +		put_device(&imgld->dev);
> +		return ERR_PTR(ret);
> +	}
> +
>  	return imgld;
>  
>  error_device:
> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
>   *
>   * This function is intended for use in an FPGA Image Load driver's
>   * remove() function.
> + *
> + * For some devices, once authentication of the uploaded image has begun,
> + * the hardware cannot be signaled to stop, and the driver will not exit
> + * until the hardware signals completion.  This could be 30+ minutes of
> + * waiting. The driver_unload flag enables a force-unload of the driver
> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the

How does the driver_unload enables the force unload of the parent
driver? I didn't find the code.

> + * hardware update is incomplete. The driver_unload flag also prevents
> + * new updates from starting once the unregister process has begun.
>   */
>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
>  {
> +	mutex_lock(&imgld->lock);
> +	imgld->driver_unload = true;
> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
> +		mutex_unlock(&imgld->lock);
> +		goto unregister;
> +	}
> +
> +	mutex_unlock(&imgld->lock);
> +	wait_for_completion(&imgld->update_done);
> +
> +unregister:
> +	cdev_del(&imgld->cdev);
>  	device_unregister(&imgld->dev);
>  }
>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
>  
>  static int __init fpga_image_load_class_init(void)
>  {
> +	int ret;
>  	pr_info("FPGA Image Load Driver\n");
>  
>  	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
>  	if (IS_ERR(fpga_image_load_class))
>  		return PTR_ERR(fpga_image_load_class);
>  
> +	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
> +				  "fpga_image_load");
> +	if (ret)
> +		goto exit_destroy_class;
> +
>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>  
>  	return 0;
> +
> +exit_destroy_class:
> +	class_destroy(fpga_image_load_class);
> +	return ret;
>  }
>  
>  static void __exit fpga_image_load_class_exit(void)
>  {
> +	unregister_chrdev_region(fpga_image_devt, MINORMASK);
>  	class_destroy(fpga_image_load_class);
>  	WARN_ON(!xa_empty(&fpga_image_load_xa));
>  }
> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> index a9cef9e1056b..b3d790e5d943 100644
> --- a/include/linux/fpga/fpga-image-load.h
> +++ b/include/linux/fpga/fpga-image-load.h
> @@ -7,22 +7,51 @@
>  #ifndef _LINUX_FPGA_IMAGE_LOAD_H
>  #define _LINUX_FPGA_IMAGE_LOAD_H
>  
> +#include <linux/cdev.h>
> +#include <linux/completion.h>
>  #include <linux/device.h>
>  #include <linux/mutex.h>
>  #include <linux/types.h>
> +#include <uapi/linux/fpga-image-load.h>
>  
>  struct fpga_image_load;
>  
>  /**
>   * struct fpga_image_load_ops - device specific operations
> + * @prepare:		    Required: Prepare secure update
> + * @write_blk:		    Required: Write a block of data
> + * @poll_complete:	    Required: Check for the completion of the
> + *			    HW authentication/programming process. This
> + *			    function should check for imgld->driver_unload
> + *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
> + * @cancel:		    Required: Signal HW to cancel update
> + * @cleanup:		    Optional: Complements the prepare()
> + *			    function and is called at the completion
> + *			    of the update, whether success or failure,
> + *			    if the prepare function succeeded.
>   */
>  struct fpga_image_load_ops {
> +	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
> +	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
> +	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
> +	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
> +	void (*cleanup)(struct fpga_image_load *imgld);
>  };
>  
>  struct fpga_image_load {
>  	struct device dev;
> +	struct cdev cdev;
>  	const struct fpga_image_load_ops *lops;
>  	struct mutex lock;		/* protect data structure contents */
> +	unsigned long opened;
> +	struct work_struct work;
> +	struct completion update_done;
> +	const u8 *data;				/* pointer to update data */
> +	u32 remaining_size;			/* size remaining to transfer */
> +	enum fpga_image_prog progress;
> +	enum fpga_image_prog err_progress;	/* progress at time of failure */

This field is not used in this patch? So could you introduce it later?

> +	enum fpga_image_err err_code;		/* image load error code */
> +	bool driver_unload;
>  	void *priv;
>  };
>  
> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> new file mode 100644
> index 000000000000..4146a0a9e408
> --- /dev/null
> +++ b/include/uapi/linux/fpga-image-load.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Header File for FPGA Image Load User API
> + *
> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
> + *
> + */
> +
> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> +
> +#include <linux/types.h>
> +#include <linux/ioctl.h>
> +
> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9
> +
> +/* Image load progress codes */
> +enum fpga_image_prog {
> +	FPGA_IMAGE_PROG_IDLE,
> +	FPGA_IMAGE_PROG_STARTING,
> +	FPGA_IMAGE_PROG_PREPARING,
> +	FPGA_IMAGE_PROG_WRITING,
> +	FPGA_IMAGE_PROG_PROGRAMMING,
> +	FPGA_IMAGE_PROG_MAX
> +};
> +
> +/* Image error progress codes */
> +enum fpga_image_err {
> +	FPGA_IMAGE_ERR_NONE,
> +	FPGA_IMAGE_ERR_HW_ERROR,
> +	FPGA_IMAGE_ERR_TIMEOUT,
> +	FPGA_IMAGE_ERR_CANCELED,
> +	FPGA_IMAGE_ERR_BUSY,
> +	FPGA_IMAGE_ERR_INVALID_SIZE,
> +	FPGA_IMAGE_ERR_RW_ERROR,
> +	FPGA_IMAGE_ERR_WEAROUT,
> +	FPGA_IMAGE_ERR_MAX
> +};
> +
> +#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)

Put the cmd word definition under the comments and parameter definition.

> +
> +/**
> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> + *				struct fpga_image_write)
> + *
> + * Upload a data buffer to the target device. The user must provide the
> + * data buffer, size, and an eventfd file descriptor.

I didn't find the eventfd.

Thanks,
Yilun

> + *
> + * Return: 0 on success, -errno on failure.
> + */
> +struct fpga_image_write {
> +	/* Input */
> +	__u32 flags;		/* Zero for now */
> +	__u32 size;		/* Data size (in bytes) to be written */
> +	__u64 buf;		/* User space address of source data */
> +};
> +
> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
> -- 
> 2.25.1

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

* Re: [PATCH v15 4/6] fpga: image-load: add status ioctl
  2021-09-09  2:18 ` [PATCH v15 4/6] fpga: image-load: add status ioctl Russ Weight
@ 2021-09-10  8:50   ` Xu Yilun
  2021-09-10 23:23     ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-10  8:50 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Wed, Sep 08, 2021 at 07:18:44PM -0700, Russ Weight wrote:
> Extend the FPGA Image Load class driver to include an
> FPGA_IMAGE_LOAD_STATUS IOCTL that can be used to monitor the progress
> of an ongoing image load.  The status returned includes how much data
> remains to be transferred, the progress of the image load, and error
> information in the case of a failure.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> V15:
>  - This patch is new to the patchset and provides an FPGA_IMAGE_LOAD_STATUS
>    IOCTL to return the current values for: remaining_size, progress,
>    err_progress, and err_code.
>  - This patch has elements of the following three patches from the previous
>    patch-set:
>      [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>      [PATCH v14 4/6] fpga: sec-mgr: expose sec-mgr update errors
>      [PATCH v14 5/6] fpga: sec-mgr: expose sec-mgr update size
>  - Changed file, symbol, and config names to reflect the new driver name
>  - There are some minor changes to locking to enable this ioctl to return
>    coherent data.
> ---
>  Documentation/fpga/fpga-image-load.rst |  6 +++
>  drivers/fpga/fpga-image-load.c         | 53 ++++++++++++++++++++++----
>  include/uapi/linux/fpga-image-load.h   | 18 +++++++++
>  3 files changed, 70 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> index 739d735592a5..3d5eb51223e3 100644
> --- a/Documentation/fpga/fpga-image-load.rst
> +++ b/Documentation/fpga/fpga-image-load.rst
> @@ -31,3 +31,9 @@ updated. This is an exclusive operation; an attempt to start concurrent image
>  load for the same device will fail with EBUSY. An eventfd file descriptor
>  parameter is provided to this IOCTL, and it will be signalled at the
>  completion of the image load.
> +
> +FPGA_IMAGE_LOAD_STATUS:
> +
> +Collect status for an on-going image upload. The status returned includes
> +how much data remains to be transferred, the progress of the image load,
> +and error information in the case of a failure.
> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> index b784456765b0..99a47b21c995 100644
> --- a/drivers/fpga/fpga-image-load.c
> +++ b/drivers/fpga/fpga-image-load.c
> @@ -22,10 +22,27 @@ static dev_t fpga_image_devt;
>  
>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>  
> -static void fpga_image_dev_error(struct fpga_image_load *imgld,
> +static void fpga_image_update_progress(struct fpga_image_load *imgld,
> +				       enum fpga_image_prog new_progress)
> +{
> +	mutex_lock(&imgld->lock);
> +	imgld->progress = new_progress;
> +	mutex_unlock(&imgld->lock);
> +}
> +
> +static void fpga_image_set_error(struct fpga_image_load *imgld,
>  				 enum fpga_image_err err_code)
>  {
> +	mutex_lock(&imgld->lock);
> +	imgld->err_progress = imgld->progress;
>  	imgld->err_code = err_code;
> +	mutex_unlock(&imgld->lock);
> +}
> +
> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
> +				 enum fpga_image_err err_code)
> +{
> +	fpga_image_set_error(imgld, err_code);
>  	imgld->lops->cancel(imgld);
>  }
>  
> @@ -49,18 +66,18 @@ static void fpga_image_do_load(struct work_struct *work)
>  
>  	get_device(&imgld->dev);
>  	if (!try_module_get(imgld->dev.parent->driver->owner)) {
> -		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
> +		fpga_image_set_error(imgld, FPGA_IMAGE_ERR_BUSY);
>  		goto idle_exit;
>  	}
>  
> -	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PREPARING);
>  	ret = imgld->lops->prepare(imgld);
>  	if (ret != FPGA_IMAGE_ERR_NONE) {
>  		fpga_image_dev_error(imgld, ret);
>  		goto modput_exit;
>  	}
>  
> -	imgld->progress = FPGA_IMAGE_PROG_WRITING;
> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>  	while (imgld->remaining_size) {
>  		ret = imgld->lops->write_blk(imgld, offset);
>  		if (ret != FPGA_IMAGE_ERR_NONE) {
> @@ -71,7 +88,7 @@ static void fpga_image_do_load(struct work_struct *work)
>  		offset = size - imgld->remaining_size;
>  	}
>  
> -	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>  	ret = imgld->lops->poll_complete(imgld);
>  	if (ret != FPGA_IMAGE_ERR_NONE)
>  		fpga_image_dev_error(imgld, ret);
> @@ -151,20 +168,42 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>  	return ret;
>  }
>  
> +static int fpga_image_load_ioctl_status(struct fpga_image_load *imgld,
> +					unsigned long arg)
> +{
> +	struct fpga_image_status status;
> +
> +	memset(&status, 0, sizeof(status));
> +	status.progress = imgld->progress;
> +	status.remaining_size = imgld->remaining_size;
> +	status.err_progress = imgld->err_progress;
> +	status.err_code = imgld->err_code;
> +
> +	if (copy_to_user((void __user *)arg, &status, sizeof(status)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
>  static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>  				  unsigned long arg)
>  {
>  	struct fpga_image_load *imgld = filp->private_data;
>  	int ret = -ENOTTY;
>  
> +	mutex_lock(&imgld->lock);
> +
>  	switch (cmd) {
>  	case FPGA_IMAGE_LOAD_WRITE:
> -		mutex_lock(&imgld->lock);
>  		ret = fpga_image_load_ioctl_write(imgld, arg);
> -		mutex_unlock(&imgld->lock);
> +		break;
> +	case FPGA_IMAGE_LOAD_STATUS:
> +		ret = fpga_image_load_ioctl_status(imgld, arg);
>  		break;
>  	}
>  
> +	mutex_unlock(&imgld->lock);
> +
>  	return ret;
>  }
>  
> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> index a60da115adf5..6a995bcc0fb7 100644
> --- a/include/uapi/linux/fpga-image-load.h
> +++ b/include/uapi/linux/fpga-image-load.h
> @@ -38,6 +38,7 @@ enum fpga_image_err {
>  };
>  
>  #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> +#define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>  
>  /**
>   * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> @@ -56,4 +57,21 @@ struct fpga_image_write {
>  	__u64 buf;		/* User space address of source data */
>  };
>  
> +/**
> + * FPGA_IMAGE_LOAD_STATUS - _IOR(FPGA_IMAGE_LOAD_MAGIC, 1,
> + *				 struct fpga_image_status)
> + *
> + * Request status information for an ongoing update.
> + * data buffer, size, and an eventfd file descriptor.

This line is a typo.

> + *
> + * Return: 0 on success, -errno on failure.
> + */
> +struct fpga_image_status {
> +	/* Output */
> +	__u32 remaining_size;			/* size remaining to transfer */
> +	enum fpga_image_prog progress;		/* current progress of image load */
> +	enum fpga_image_prog err_progress;	/* progress at time of error */
> +	enum fpga_image_err err_code;		/* error code */

Should we use __u32 for these enumeration types?

Thanks,
Yilun

> +};
> +
>  #endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
> -- 
> 2.25.1

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

* Re: [PATCH v15 5/6] fpga: image-load: create status sysfs node
  2021-09-09  2:18 ` [PATCH v15 5/6] fpga: image-load: create status sysfs node Russ Weight
@ 2021-09-10  8:52   ` Xu Yilun
  2021-09-10 23:30     ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-10  8:52 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Wed, Sep 08, 2021 at 07:18:45PM -0700, Russ Weight wrote:
> Extend the FPGA Image Load class driver to include a status sysfs node that
> can be viewed to determine from the command line if an image load is in
> progress. Status will be one of: idle, starting, preparing, writing, or
> programming.

The FPGA_IMAGE_LOAD_STATUS ioctl already provides the progress info.
Why we need 2 user interfaces for the same information?

Thanks,
Yilun

> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v15:
>  - Compare to previous patch:
>      [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>  - Changed file, symbol, and config names to reflect the new driver name
>  - Removed signed-off/reviewed-by tags
> v14:
>  - Updated ABI documentation date and kernel version
> v13:
>   - No change
> v12:
>   - Updated Date and KernelVersion fields in ABI documentation
>   - Changed syntax of sec_mgr_prog_str[] array definition from:
> 	"idle",			/* FPGA_SEC_PROG_IDLE */
>     to:
> 	[FPGA_SEC_PROG_IDLE]	    = "idle",
> v11:
>   - No change
> v10:
>   - Rebased to 5.12-rc2 next
>   - Updated Date and KernelVersion in ABI documentation
> v9:
>   - Updated Date and KernelVersion in ABI documentation
> v8:
>   - No change
> v7:
>   - Changed Date in documentation file to December 2020
> v6:
>   - No change
> v5:
>   - Use new function sysfs_emit() in the status_show() function
> v4:
>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>     and removed unnecessary references to "Intel".
>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> v3:
>   - Use a local variable to read progress once in status_show()
>   - Use dev_err to report invalid progress status
> v2:
>   - Bumped documentation date and version
>   - Changed progress state "read_file" to "reading"
> ---
>  .../ABI/testing/sysfs-class-fpga-image-load   |  7 ++++
>  MAINTAINERS                                   |  1 +
>  drivers/fpga/fpga-image-load.c                | 33 +++++++++++++++++++
>  3 files changed, 41 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-image-load
> 
> diff --git a/Documentation/ABI/testing/sysfs-class-fpga-image-load b/Documentation/ABI/testing/sysfs-class-fpga-image-load
> new file mode 100644
> index 000000000000..6c04a49f01cc
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-fpga-image-load
> @@ -0,0 +1,7 @@
> +What: 		/sys/class/fpga_image_load/fpga_imageX/status
> +Date:		Aug 2021
> +KernelVersion:	5.15
> +Contact:	Russ Weight <russell.h.weight@intel.com>
> +Description:	Read-only. Returns a string describing the current status
> +		of an FPGA image upload. The string will be one of the
> +		following: idle, starting, preparing, writing, programming.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 637bc003ca81..e3fbc1bde9bc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7362,6 +7362,7 @@ FPGA SECURITY MANAGER DRIVERS
>  M:	Russ Weight <russell.h.weight@intel.com>
>  L:	linux-fpga@vger.kernel.org
>  S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-class-fpga-image-load
>  F:	Documentation/fpga/fpga-image-load.rst
>  F:	drivers/fpga/fpga-image-load.c
>  F:	include/linux/fpga/fpga-image-load.h
> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> index 99a47b21c995..6ec0a39f07b3 100644
> --- a/drivers/fpga/fpga-image-load.c
> +++ b/drivers/fpga/fpga-image-load.c
> @@ -236,6 +236,38 @@ static const struct file_operations fpga_image_load_fops = {
>  	.unlocked_ioctl = fpga_image_load_ioctl,
>  };
>  
> +static const char * const image_load_prog_str[] = {
> +	[FPGA_IMAGE_PROG_IDLE]	      = "idle",
> +	[FPGA_IMAGE_PROG_STARTING]    = "starting",
> +	[FPGA_IMAGE_PROG_PREPARING]   = "preparing",
> +	[FPGA_IMAGE_PROG_WRITING]     = "writing",
> +	[FPGA_IMAGE_PROG_PROGRAMMING] = "programming"
> +};
> +
> +static ssize_t
> +status_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct fpga_image_load *imgld = to_image_load(dev);
> +	const char *status = "unknown-status";
> +	enum fpga_image_prog progress;
> +
> +	progress = imgld->progress;
> +	if (progress < FPGA_IMAGE_PROG_MAX)
> +		status = image_load_prog_str[progress];
> +	else
> +		dev_err(dev, "Invalid status during secure update: %d\n",
> +			progress);
> +
> +	return sysfs_emit(buf, "%s\n", status);
> +}
> +static DEVICE_ATTR_RO(status);
> +
> +static struct attribute *fpga_image_load_attrs[] = {
> +	&dev_attr_status.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(fpga_image_load);
> +
>  /**
>   * fpga_image_load_register - create and register an FPGA Image Load Device
>   *
> @@ -373,6 +405,7 @@ static int __init fpga_image_load_class_init(void)
>  	if (ret)
>  		goto exit_destroy_class;
>  
> +	fpga_image_load_class->dev_groups = fpga_image_load_groups;
>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>  
>  	return 0;
> -- 
> 2.25.1

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

* Re: [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-09  2:18 ` [PATCH v15 6/6] fpga: image-load: enable cancel of image upload Russ Weight
@ 2021-09-10 14:55   ` Xu Yilun
  2021-09-10 23:38     ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-10 14:55 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Wed, Sep 08, 2021 at 07:18:46PM -0700, Russ Weight wrote:
> Extend the FPGA Image Load class driver to include a cancel IOCTL that
> can be used to request that an image upload be canceled. The IOCTL may
> return EBUSY if it cannot be canceled by software or ENODEV if there
> is no update in progress.
> 
> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> ---
> v15:
>  - Compare to previous patch:
>      [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
>  - Changed file, symbol, and config names to reflect the new driver name
>  - Cancel is now initiated by IOCT instead of sysfs
>  - Removed signed-off/reviewed-by tags
> v14:
>  - Updated ABI documentation date and kernel version
> v13:
>   - No change
> v12:
>   - Updated Date and KernelVersion fields in ABI documentation
> v11:
>   - No change
> v10:
>   - Rebased to 5.12-rc2 next
>   - Updated Date and KernelVersion in ABI documentation
> v9:
>   - Updated Date and KernelVersion in ABI documentation
> v8:
>   - No change
> v7:
>   - Changed Date in documentation file to December 2020
> v6:
>   - No change
> v5:
>   - No change
> v4:
>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>     and removed unnecessary references to "Intel".
>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> v3:
>   - No change
> v2:
>   - Bumped documentation date and version
>   - Minor code cleanup per review comments
> ---
> ---
>  Documentation/fpga/fpga-image-load.rst |  6 ++++
>  drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
>  include/linux/fpga/fpga-image-load.h   |  1 +
>  include/uapi/linux/fpga-image-load.h   |  1 +
>  4 files changed, 49 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> index 3d5eb51223e3..763e7833a6ea 100644
> --- a/Documentation/fpga/fpga-image-load.rst
> +++ b/Documentation/fpga/fpga-image-load.rst
> @@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
>  Collect status for an on-going image upload. The status returned includes
>  how much data remains to be transferred, the progress of the image load,
>  and error information in the case of a failure.
> +
> +FPGA_IMAGE_LOAD_CANCEL:
> +
> +Request that a on-going image upload be cancelled. This IOCTL may return
> +EBUSY if it cannot be cancelled by software or ENODEV if there is no update
> +in progress.
> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> index 6ec0a39f07b3..c32e4b1ea35a 100644
> --- a/drivers/fpga/fpga-image-load.c
> +++ b/drivers/fpga/fpga-image-load.c
> @@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
>  	imgld->lops->cancel(imgld);
>  }
>  
> +static int fpga_image_prog_transition(struct fpga_image_load *imgld,
> +				      enum fpga_image_prog new_progress)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&imgld->lock);
> +	if (imgld->request_cancel) {
> +		imgld->err_progress = imgld->progress;
> +		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
> +		imgld->lops->cancel(imgld);
> +		ret = -ECANCELED;
> +	} else {
> +		imgld->progress = new_progress;
> +	}
> +	mutex_unlock(&imgld->lock);
> +	return ret;
> +}
> +
>  static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>  {
>  	mutex_lock(&imgld->lock);
> @@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
>  		goto modput_exit;
>  	}
>  
> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
> -	while (imgld->remaining_size) {
> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
> +		goto done;
> +
> +	while (imgld->remaining_size && !imgld->request_cancel) {
>  		ret = imgld->lops->write_blk(imgld, offset);
>  		if (ret != FPGA_IMAGE_ERR_NONE) {
>  			fpga_image_dev_error(imgld, ret);
> @@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
>  		offset = size - imgld->remaining_size;
>  	}
>  
> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
> +		goto done;
> +
>  	ret = imgld->lops->poll_complete(imgld);
>  	if (ret != FPGA_IMAGE_ERR_NONE)
>  		fpga_image_dev_error(imgld, ret);
> @@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>  	imgld->remaining_size = wb.size;
>  	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>  	imgld->progress = FPGA_IMAGE_PROG_STARTING;
> +	imgld->request_cancel = false;
>  	reinit_completion(&imgld->update_done);
>  	schedule_work(&imgld->work);
>  	return 0;
> @@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>  				  unsigned long arg)
>  {
>  	struct fpga_image_load *imgld = filp->private_data;
> -	int ret = -ENOTTY;
> +	int ret = 0;
>  
>  	mutex_lock(&imgld->lock);
>  
> @@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>  	case FPGA_IMAGE_LOAD_STATUS:
>  		ret = fpga_image_load_ioctl_status(imgld, arg);
>  		break;
> +	case FPGA_IMAGE_LOAD_CANCEL:
> +		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
> +			ret = -EBUSY;
> +		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
> +			ret = -ENODEV;
> +		else
> +			imgld->request_cancel = true;
> +		break;
> +	default:
> +		ret = -ENOTTY;
> +		break;
>  	}
>  
>  	mutex_unlock(&imgld->lock);
> @@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
>  		goto unregister;
>  	}
>  
> +	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
> +		imgld->request_cancel = true;
> +

Why we cancel the programing rather than waiting for programing done?

Thanks,
Yilun

>  	mutex_unlock(&imgld->lock);
>  	wait_for_completion(&imgld->update_done);
>  
> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> index 68f3105b51d2..4e51b9fd1724 100644
> --- a/include/linux/fpga/fpga-image-load.h
> +++ b/include/linux/fpga/fpga-image-load.h
> @@ -52,6 +52,7 @@ struct fpga_image_load {
>  	enum fpga_image_prog progress;
>  	enum fpga_image_prog err_progress;	/* progress at time of failure */
>  	enum fpga_image_err err_code;		/* image load error code */
> +	bool request_cancel;
>  	bool driver_unload;
>  	struct eventfd_ctx *finished;
>  	void *priv;
> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> index 6a995bcc0fb7..8d0dfa1f9b77 100644
> --- a/include/uapi/linux/fpga-image-load.h
> +++ b/include/uapi/linux/fpga-image-load.h
> @@ -39,6 +39,7 @@ enum fpga_image_err {
>  
>  #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>  #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
> +#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
>  
>  /**
>   * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> -- 
> 2.25.1

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

* Re: [PATCH v15 1/6] fpga: image-load: fpga image load class driver
  2021-09-10  6:46   ` Xu Yilun
@ 2021-09-10 20:47     ` Russ Weight
  0 siblings, 0 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-10 20:47 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/9/21 11:46 PM, Xu Yilun wrote:
> On Wed, Sep 08, 2021 at 07:18:41PM -0700, Russ Weight wrote:
>> The FPGA Image Load class driver provides an API to transfer update
>> files to an FPGA device. Image files are self-describing. They could
>> contain FPGA images, BMC images, Root Entry Hashes, or other device
>> specific files. It is up to the device driver and the target device
>> to authenticate and disposition the file data.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v15:
>>  - Compare to previous patch:
>>      [PATCH v14 1/6] fpga: sec-mgr: fpga security manager class driver 
>>  - Changed file, symbol, and config names to reflect the new driver name
>>  - Rewrote documentation. The documentation will be added to in later patches.
>>  - Removed signed-off/reviewed-by tags
>> v14:
>>  - Updated copyright to 2021
>>  - Removed the name sysfs entry
>>  - Removed MAINTAINERS reference to
>>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
>>  - Use xa_alloc() instead of ida_simple_get()
>>  - Rename dev to parent for parent devices
>>  - Remove fpga_sec_mgr_create(), devm_fpga_sec_mgr_create(), and
>>    fpga_sec_mgr_free() functions and update the fpga_sec_mgr_register()
>>    function to both create and register a new security manager.
>>  - Populate the fpga_sec_mgr_dev_release() function.
>> v13:
>>   - No change
>> v12:
>>   - Updated Date and KernelVersion fields in ABI documentation
>> v11:
>>   - No change
>> v10:
>>   - Rebased to 5.12-rc2 next
>>   - Updated Date and KernelVersion in ABI documentation
>> v9:
>>   - Updated Date and KernelVersion in ABI documentation
>> v8:
>>   - Fixed grammatical error in Documentation/fpga/fpga-sec-mgr.rst
>> v7:
>>   - Changed Date in documentation file to December 2020
>> v6:
>>   - Removed sysfs support and documentation for the display of the
>>     flash count, root entry hashes, and code-signing-key cancelation
>>     vectors.
>> v5:
>>   - Added the devm_fpga_sec_mgr_unregister() function, following recent
>>     changes to the fpga_manager() implementation.
>>   - Changed some *_show() functions to use sysfs_emit() instead of sprintf(
>> v4:
>>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>     and removed unnecessary references to "Intel".
>>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>> v3:
>>   - Modified sysfs handler check in check_sysfs_handler() to make
>>     it more readable.
>> v2:
>>   - Bumped documentation dates and versions
>>   - Added Documentation/fpga/ifpga-sec-mgr.rst
>>   - Removed references to bmc_flash_count & smbus_flash_count (not supported)
>>   - Split ifpga_sec_mgr_register() into create() and register() functions
>>   - Added devm_ifpga_sec_mgr_create()
>>   - Removed typedefs for imgr ops
>> ---
>> ---
>>  Documentation/fpga/fpga-image-load.rst |  10 ++
>>  Documentation/fpga/index.rst           |   1 +
>>  MAINTAINERS                            |   8 ++
>>  drivers/fpga/Kconfig                   |  10 ++
>>  drivers/fpga/Makefile                  |   3 +
>>  drivers/fpga/fpga-image-load.c         | 124 +++++++++++++++++++++++++
>>  include/linux/fpga/fpga-image-load.h   |  35 +++++++
>>  7 files changed, 191 insertions(+)
>>  create mode 100644 Documentation/fpga/fpga-image-load.rst
>>  create mode 100644 drivers/fpga/fpga-image-load.c
>>  create mode 100644 include/linux/fpga/fpga-image-load.h
>>
>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>> new file mode 100644
>> index 000000000000..a6e53ac66026
>> --- /dev/null
>> +++ b/Documentation/fpga/fpga-image-load.rst
>> @@ -0,0 +1,10 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +============================
>> +FPGA Image Load Class Driver
>> +============================
>> +
>> +The FPGA Image Load class driver provides a common API for user-space
>> +tools to manage image uploads to FPGA devices. Device drivers that
>> +instantiate the FPGA Image Load class driver will interact with the
>> +target device to transfer and authenticate the image data.
>> diff --git a/Documentation/fpga/index.rst b/Documentation/fpga/index.rst
>> index f80f95667ca2..85d25fb22c08 100644
>> --- a/Documentation/fpga/index.rst
>> +++ b/Documentation/fpga/index.rst
>> @@ -8,6 +8,7 @@ fpga
>>      :maxdepth: 1
>>  
>>      dfl
>> +    fpga-image-load
>>  
>>  .. only::  subproject and html
>>  
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 6c63415d2ac2..4e7f48fa7e5c 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7358,6 +7358,14 @@ F:	Documentation/fpga/
>>  F:	drivers/fpga/
>>  F:	include/linux/fpga/
>>  
>> +FPGA SECURITY MANAGER DRIVERS
>> +M:	Russ Weight <russell.h.weight@intel.com>
>> +L:	linux-fpga@vger.kernel.org
>> +S:	Maintained
>> +F:	Documentation/fpga/fpga-image-load.rst
>> +F:	drivers/fpga/fpga-image-load.c
>> +F:	include/linux/fpga/fpga-image-load.h
>> +
>>  FPU EMULATOR
>>  M:	Bill Metzenthen <billm@melbpc.org.au>
>>  S:	Maintained
>> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>> index 991b3f361ec9..c12a14e62fff 100644
>> --- a/drivers/fpga/Kconfig
>> +++ b/drivers/fpga/Kconfig
>> @@ -243,4 +243,14 @@ config FPGA_MGR_VERSAL_FPGA
>>  	  configure the programmable logic(PL).
>>  
>>  	  To compile this as a module, choose M here.
>> +
>> +config FPGA_IMAGE_LOAD
>> +	tristate "FPGA Image Load Driver"
> Maybe we don't call it "Driver". A framework or "FPGA Image load support",
> is it better?
>
> There are more descriptions about "driver" below, maybe you need to change
> them all.

Yeah - I think that language sounds better.
>
>> +	help
>> +	  The FPGA Image Load class driver presents a common user API for
>> +	  uploading an image file to an FPGA device. The image file is
>> +	  expected to be self-describing. It is up to the device driver
>> +	  and/or the device itself to authenticate and disposition the
>> +	  image data.
>> +
>>  endif # FPGA
>> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
>> index 0bff783d1b61..adf228ee4f5e 100644
>> --- a/drivers/fpga/Makefile
>> +++ b/drivers/fpga/Makefile
>> @@ -22,6 +22,9 @@ obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)      += versal-fpga.o
>>  obj-$(CONFIG_ALTERA_PR_IP_CORE)         += altera-pr-ip-core.o
>>  obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT)    += altera-pr-ip-core-plat.o
>>  
>> +# FPGA Image Load Framework
>> +obj-$(CONFIG_FPGA_IMAGE_LOAD)		+= fpga-image-load.o
>> +
>>  # FPGA Bridge Drivers
>>  obj-$(CONFIG_FPGA_BRIDGE)		+= fpga-bridge.o
>>  obj-$(CONFIG_SOCFPGA_FPGA_BRIDGE)	+= altera-hps2fpga.o altera-fpga2sdram.o
>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>> new file mode 100644
>> index 000000000000..7d75bbcff541
>> --- /dev/null
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -0,0 +1,124 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * FPGA Image Load Class Driver
>> + *
>> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
>> + */
>> +
>> +#include <linux/fpga/fpga-image-load.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/vmalloc.h>
>> +
>> +#define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
>> +static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
>> +
>> +static struct class *fpga_image_load_class;
>> +
>> +#define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>> +
>> +/**
>> + * fpga_image_load_register - create and register an FPGA Image Load Device
>> + *
>> + * @parent: fpga image load device from pdev
>> + * @lops:   pointer to a structure of image load callback functions
> Maybe "ops" is just good, some more below.

OK
>
>> + * @priv:   fpga image load private data
>> + *
>> + * Returns a struct fpga_image_load pointer on success, or ERR_PTR() on
>> + * error. The caller of this function is responsible for calling
>> + * fpga_image_load_unregister().
>> + */
>> +struct fpga_image_load *
>> +fpga_image_load_register(struct device *parent,
>> +			 const struct fpga_image_load_ops *lops, void *priv)
>> +{
>> +	struct fpga_image_load *imgld;
>> +	int id, ret;
>> +
>> +	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
>> +	if (!imgld)
>> +		return NULL;
>> +
>> +	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
>> +		       GFP_KERNEL);
>> +	if (ret)
>> +		goto error_kfree;
>> +
>> +	mutex_init(&imgld->lock);
>> +
>> +	imgld->priv = priv;
>> +	imgld->lops = lops;
>> +
>> +	imgld->dev.class = fpga_image_load_class;
>> +	imgld->dev.parent = parent;
>> +
>> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
> Is it better "fpga_image_load%d"?
OK

Thanks for the comments,
- Russ
>
>> +	if (ret) {
>> +		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
>> +		goto error_device;
>> +	}
>> +
>> +	ret = device_register(&imgld->dev);
>> +	if (ret) {
>> +		put_device(&imgld->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>> +	return imgld;
>> +
>> +error_device:
>> +	xa_erase(&fpga_image_load_xa, imgld->dev.id);
>> +
>> +error_kfree:
>> +	kfree(imgld);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(fpga_image_load_register);
>> +
>> +/**
>> + * fpga_image_load_unregister - unregister an FPGA image load device
>> + *
>> + * @imgld: pointer to struct fpga_image_load
>> + *
>> + * This function is intended for use in an FPGA Image Load driver's
>> + * remove() function.
>> + */
>> +void fpga_image_load_unregister(struct fpga_image_load *imgld)
>> +{
>> +	device_unregister(&imgld->dev);
>> +}
>> +EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
>> +
>> +static void fpga_image_load_dev_release(struct device *dev)
>> +{
>> +	struct fpga_image_load *imgld = to_image_load(dev);
>> +
>> +	xa_erase(&fpga_image_load_xa, imgld->dev.id);
>> +	kfree(imgld);
>> +}
>> +
>> +static int __init fpga_image_load_class_init(void)
>> +{
>> +	pr_info("FPGA Image Load Driver\n");
>> +
>> +	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
>> +	if (IS_ERR(fpga_image_load_class))
>> +		return PTR_ERR(fpga_image_load_class);
>> +
>> +	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>> +
>> +	return 0;
>> +}
>> +
>> +static void __exit fpga_image_load_class_exit(void)
>> +{
>> +	class_destroy(fpga_image_load_class);
>> +	WARN_ON(!xa_empty(&fpga_image_load_xa));
>> +}
>> +
>> +MODULE_DESCRIPTION("FPGA Image Load Driver");
>> +MODULE_LICENSE("GPL v2");
>> +
>> +subsys_initcall(fpga_image_load_class_init);
>> +module_exit(fpga_image_load_class_exit)
>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>> new file mode 100644
>> index 000000000000..a9cef9e1056b
>> --- /dev/null
>> +++ b/include/linux/fpga/fpga-image-load.h
>> @@ -0,0 +1,35 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Header file for FPGA Image Load Driver
>> + *
>> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
>> + */
>> +#ifndef _LINUX_FPGA_IMAGE_LOAD_H
>> +#define _LINUX_FPGA_IMAGE_LOAD_H
>> +
>> +#include <linux/device.h>
>> +#include <linux/mutex.h>
>> +#include <linux/types.h>
>> +
>> +struct fpga_image_load;
>> +
>> +/**
>> + * struct fpga_image_load_ops - device specific operations
>> + */
>> +struct fpga_image_load_ops {
>> +};
>> +
>> +struct fpga_image_load {
>> +	struct device dev;
>> +	const struct fpga_image_load_ops *lops;
>> +	struct mutex lock;		/* protect data structure contents */
>> +	void *priv;
>> +};
>> +
>> +struct fpga_image_load *
>> +fpga_image_load_register(struct device *dev,
>> +			 const struct fpga_image_load_ops *lops, void *priv);
>> +
>> +void fpga_image_load_unregister(struct fpga_image_load *imgld);
>> +
>> +#endif
>> -- 
>> 2.25.1
> Thanks,
> Yilun


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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-10  8:22   ` Xu Yilun
@ 2021-09-10 23:18     ` Russ Weight
  2021-09-13  6:48       ` Xu Yilun
  0 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-10 23:18 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/10/21 1:22 AM, Xu Yilun wrote:
> On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote:
>> Extend the FPGA Image Load class driver to include IOCTL support
>> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
>> The IOCTL will return immediately, and the update will begin in the
>> context of a kernel worker thread.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v15:
>>  - Compare to previous patch:
>>      [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
>>  - Changed file, symbol, and config names to reflect the new driver name
>>  - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
>>    IOCTL for writing the image data to the FPGA card. The driver no longer
>>    uses the request_firmware framework.
>>  - Fixed some error return values in fpga_image_load_register()
>>  - Removed signed-off/reviewed-by tags
>> v14:
>>  - Added MAINTAINERS reference for
>>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
>>  - Updated ABI documentation date and kernel version
>>  - Updated copyright to 2021
>> v13:
>>   - Change "if (count == 0 || " to "if (!count || "
>>   - Improve error message: "Attempt to register without all required ops\n"
>> v12:
>>   - Updated Date and KernelVersion fields in ABI documentation
>>   - Removed size parameter from write_blk() op - it is now up to
>>     the lower-level driver to determine the appropriate size and
>>     to update smgr->remaining_size accordingly.
>> v11:
>>   - Fixed a spelling error in a comment
>>   - Initialize smgr->err_code and smgr->progress explicitly in
>>     fpga_sec_mgr_create() instead of accepting the default 0 value.
>> v10:
>>   - Rebased to 5.12-rc2 next
>>   - Updated Date and KernelVersion in ABI documentation
>> v9:
>>   - Updated Date and KernelVersion in ABI documentation
>> v8:
>>   - No change
>> v7:
>>   - Changed Date in documentation file to December 2020
>>   - Changed filename_store() to use kmemdup_nul() instead of
>>     kstrndup() and changed the count to not assume a line-return.
>> v6:
>>   - Changed "security update" to "secure update" in commit message
>> v5:
>>   - When checking the return values for functions of type enum
>>     fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
>> v4:
>>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>     and removed unnecessary references to "Intel".
>>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>> v3:
>>   - Removed unnecessary "goto done"
>>   - Added a comment to explain imgr->driver_unload in
>>     ifpga_sec_mgr_unregister()
>> v2:
>>   - Bumped documentation date and version
>>   - Removed explicit value assignments in enums
>>   - Other minor code cleanup per review comments
>> ---
>>  Documentation/fpga/fpga-image-load.rst |  21 +++
>>  MAINTAINERS                            |   1 +
>>  drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
>>  include/linux/fpga/fpga-image-load.h   |  29 ++++
>>  include/uapi/linux/fpga-image-load.h   |  58 +++++++
>>  5 files changed, 329 insertions(+), 4 deletions(-)
>>  create mode 100644 include/uapi/linux/fpga-image-load.h
>>
>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>> index a6e53ac66026..2ca8d2f0212d 100644
>> --- a/Documentation/fpga/fpga-image-load.rst
>> +++ b/Documentation/fpga/fpga-image-load.rst
>> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
>>  tools to manage image uploads to FPGA devices. Device drivers that
>>  instantiate the FPGA Image Load class driver will interact with the
>>  target device to transfer and authenticate the image data.
>> +
>> +User API
>> +========
>> +
>> +open
>> +----
>> +
>> +An FPGA Image Load device is opened exclusively to control an image load.
>> +Image loads are processed by a kernel worker thread. A user may choose
>> +close the device while the upload continues.
> Why we allow the user to close the dev while the uploading is ongoing?
> Seems it introduces more checking effort when another user open the dev
> again and try another uploading.

A user could choose to write their own tools. How would we prevent
them from closing the device while an update is in progress? Should
we attempt to cancel the update if they close the device?

Concurrent updates are already prevented by returning EBUSY for the
FPGA_IMAGE_LOAD_WRITE IOCTL if an update is already in progress.

A single IOCTL is sufficient to do a full update. Maybe a user
would want to have three small tools: update_start, update_status,
update_cancel, each of which would open the device, do an IOCTL,
and then close the device. This is analogous to the sysfs
implementation (no open/close that bounds the entire sequence).

With the current design, we do an exclusive open. As long as the user
keeps the device open, no other process can open the device to start
a new update, cancel, or collect status.

Those are my thoughts. What do you think? Is it OK as is? Or should
I make some changes here?

>
>> +
>> +ioctl
>> +-----
>> +
>> +FPGA_IMAGE_LOAD_WRITE:
>> +
>> +Start an image load with the provided image buffer. This IOCTL returns
>> +immediately after starting a kernel worker thread to process the image load
>> +which could take as long a 40 minutes depending on the actual device being
>> +updated. This is an exclusive operation; an attempt to start concurrent image
>> +load for the same device will fail with EBUSY.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 4e7f48fa7e5c..637bc003ca81 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7365,6 +7365,7 @@ S:	Maintained
>>  F:	Documentation/fpga/fpga-image-load.rst
>>  F:	drivers/fpga/fpga-image-load.c
>>  F:	include/linux/fpga/fpga-image-load.h
>> +F:	include/uapi/linux/fpga-image-load.h
>>  
>>  FPU EMULATOR
>>  M:	Bill Metzenthen <billm@melbpc.org.au>
>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>> index 7d75bbcff541..f5ccfa9dd977 100644
>> --- a/drivers/fpga/fpga-image-load.c
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -5,18 +5,181 @@
>>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
>>   */
>>  
>> +#include <linux/delay.h>
>>  #include <linux/fpga/fpga-image-load.h>
>> +#include <linux/fs.h>
>> +#include <linux/kernel.h>
>>  #include <linux/module.h>
>>  #include <linux/slab.h>
>> +#include <linux/uaccess.h>
>>  #include <linux/vmalloc.h>
>>  
>>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
>>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
>>  
>>  static struct class *fpga_image_load_class;
>> +static dev_t fpga_image_devt;
>>  
>>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>>  
>> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
>> +				 enum fpga_image_err err_code)
>> +{
>> +	imgld->err_code = err_code;
>> +	imgld->lops->cancel(imgld);
>> +}
>> +
>> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>> +{
>> +	mutex_lock(&imgld->lock);
>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>> +	complete_all(&imgld->update_done);
>> +	mutex_unlock(&imgld->lock);
>> +}
>> +
>> +static void fpga_image_do_load(struct work_struct *work)
>> +{
>> +	struct fpga_image_load *imgld;
>> +	enum fpga_image_err ret;
>> +	u32 size, offset = 0;
>> +
>> +	imgld = container_of(work, struct fpga_image_load, work);
>> +	size = imgld->remaining_size;
>> +
>> +	get_device(&imgld->dev);
>> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
>> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
>> +		goto idle_exit;
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
>> +	ret = imgld->lops->prepare(imgld);
>> +	if (ret != FPGA_IMAGE_ERR_NONE) {
>> +		fpga_image_dev_error(imgld, ret);
>> +		goto modput_exit;
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
>> +	while (imgld->remaining_size) {
>> +		ret = imgld->lops->write_blk(imgld, offset);
>> +		if (ret != FPGA_IMAGE_ERR_NONE) {
>> +			fpga_image_dev_error(imgld, ret);
>> +			goto done;
>> +		}
>> +
>> +		offset = size - imgld->remaining_size;
> The low level driver is required to update the "remaining_size" in
> write_blk ops?
>
> The API seems ambiguous. The framework asks for writing a block of data,
> but no block size is specified.

This change was made two or three iterations ago at Richard Gong's
suggestion. He asserted that the lower level driver should be in
control of the block size (based on write speeds). What do you
think? Should the class driver impose a fixed size? Or allow the
lower-level driver to determine the size? Is it OK to update
remaining_size in the lower-level driver? Or should there be
another call-back to request the size?
 
>
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
>> +	ret = imgld->lops->poll_complete(imgld);
>> +	if (ret != FPGA_IMAGE_ERR_NONE)
>> +		fpga_image_dev_error(imgld, ret);
>> +
>> +done:
>> +	if (imgld->lops->cleanup)
>> +		imgld->lops->cleanup(imgld);
>> +
>> +modput_exit:
>> +	module_put(imgld->dev.parent->driver->owner);
>> +
>> +idle_exit:
>> +	/*
>> +	 * Note: imgld->remaining_size is left unmodified here to provide
>> +	 * additional information on errors. It will be reinitialized when
>> +	 * the next image load begins.
>> +	 */
>> +	vfree(imgld->data);
>> +	imgld->data = NULL;
>> +	put_device(&imgld->dev);
>> +	fpga_image_prog_complete(imgld);
>> +}
>> +
>> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>> +				       unsigned long arg)
>> +{
>> +	struct fpga_image_write wb;
>> +	unsigned long minsz;
>> +	u8 *buf;
>> +
>> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
>> +		return -EBUSY;
>> +
>> +	minsz = offsetofend(struct fpga_image_write, buf);
>> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
>> +		return -EFAULT;
>> +
>> +	if (wb.flags)
>> +		return -EINVAL;
>> +
>> +	/* Enforce 32-bit alignment on the write data */
>> +	if (wb.size & 0x3)
>> +		return -EINVAL;
>> +
>> +	buf = vzalloc(wb.size);
>> +	if (!buf)
>> +		return -ENOMEM;
>> +
>> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
>> +		vfree(buf);
>> +		return -EFAULT;
>> +	}
>> +
>> +	imgld->data = buf;
>> +	imgld->remaining_size = wb.size;
>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>> +	reinit_completion(&imgld->update_done);
>> +	schedule_work(&imgld->work);
>> +
>> +	return 0;
>> +}
>> +
>> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>> +				  unsigned long arg)
>> +{
>> +	struct fpga_image_load *imgld = filp->private_data;
>> +	int ret = -ENOTTY;
>> +
>> +	switch (cmd) {
>> +	case FPGA_IMAGE_LOAD_WRITE:
>> +		mutex_lock(&imgld->lock);
>> +		ret = fpga_image_load_ioctl_write(imgld, arg);
>> +		mutex_unlock(&imgld->lock);
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
>> +{
>> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
>> +						     struct fpga_image_load, cdev);
>> +
>> +	if (test_and_set_bit(0, &imgld->opened))
> Some more flags to add for "opened" field? But the field name indicates
> it is a single flag.
Can you explain your comment further? I'm not understanding. What
"more flags" are you referring to?

This test_and_set_bit() operation and the "opened" structure member
were borrowed from the production driver implementation. opened is a
single value that is expected to be either 1 or 0.

>
>> +		return -EBUSY;
>> +
>> +	filp->private_data = imgld;
>> +
>> +	return 0;
>> +}
>> +
>> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
>> +{
>> +	struct fpga_image_load *imgld = filp->private_data;
>> +
>> +	clear_bit(0, &imgld->opened);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct file_operations fpga_image_load_fops = {
>> +	.owner = THIS_MODULE,
>> +	.open = fpga_image_load_open,
>> +	.release = fpga_image_load_release,
>> +	.unlocked_ioctl = fpga_image_load_ioctl,
>> +};
>> +
>>  /**
>>   * fpga_image_load_register - create and register an FPGA Image Load Device
>>   *
>> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
>>  			 const struct fpga_image_load_ops *lops, void *priv)
>>  {
>>  	struct fpga_image_load *imgld;
>> -	int id, ret;
>> +	int ret;
>> +
>> +	if (!lops || !lops->cancel || !lops->prepare ||
>> +	    !lops->write_blk || !lops->poll_complete) {
>> +		dev_err(parent, "Attempt to register without all required ops\n");
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>>  
>>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
>>  	if (!imgld)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
> This is the fix for Patch #1? If yes please merge it to Patch #1.
Good catch. Yes, I'll move it to patch #1.
>
>>  
>>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
>>  		       GFP_KERNEL);
>> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
>>  
>>  	imgld->priv = priv;
>>  	imgld->lops = lops;
>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>> +	init_completion(&imgld->update_done);
>> +	INIT_WORK(&imgld->work, fpga_image_do_load);
>>  
>>  	imgld->dev.class = fpga_image_load_class;
>>  	imgld->dev.parent = parent;
>> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
>>  
>> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
>> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
> Another fix? Please merge it to Patch #1.
Yes.

>
>>  	if (ret) {
>> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
>> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
>> +			imgld->dev.id);
> Ditto
Yes.
>
>>  		goto error_device;
>>  	}
>>  
>> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
>>  		return ERR_PTR(ret);
>>  	}
>>  
>> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
>> +	imgld->cdev.owner = parent->driver->owner;
>> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
>> +
>> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
>> +	if (ret) {
>> +		put_device(&imgld->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>>  	return imgld;
>>  
>>  error_device:
>> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
>>   *
>>   * This function is intended for use in an FPGA Image Load driver's
>>   * remove() function.
>> + *
>> + * For some devices, once authentication of the uploaded image has begun,
>> + * the hardware cannot be signaled to stop, and the driver will not exit
>> + * until the hardware signals completion.  This could be 30+ minutes of
>> + * waiting. The driver_unload flag enables a force-unload of the driver
>> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the
> How does the driver_unload enables the force unload of the parent
> driver? I didn't find the code.

The driver_unload field is tested in the lower-level (parent) driver in
m10bmc_sec_poll_complete(), allowing the kernel worker thread to exit
even if the firmware update is still in progress.

>
>> + * hardware update is incomplete. The driver_unload flag also prevents
>> + * new updates from starting once the unregister process has begun.
>>   */
>>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>  {
>> +	mutex_lock(&imgld->lock);
>> +	imgld->driver_unload = true;
>> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
>> +		mutex_unlock(&imgld->lock);
>> +		goto unregister;
>> +	}
>> +
>> +	mutex_unlock(&imgld->lock);
>> +	wait_for_completion(&imgld->update_done);
>> +
>> +unregister:
>> +	cdev_del(&imgld->cdev);
>>  	device_unregister(&imgld->dev);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
>> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
>>  
>>  static int __init fpga_image_load_class_init(void)
>>  {
>> +	int ret;
>>  	pr_info("FPGA Image Load Driver\n");
>>  
>>  	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
>>  	if (IS_ERR(fpga_image_load_class))
>>  		return PTR_ERR(fpga_image_load_class);
>>  
>> +	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
>> +				  "fpga_image_load");
>> +	if (ret)
>> +		goto exit_destroy_class;
>> +
>>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>>  
>>  	return 0;
>> +
>> +exit_destroy_class:
>> +	class_destroy(fpga_image_load_class);
>> +	return ret;
>>  }
>>  
>>  static void __exit fpga_image_load_class_exit(void)
>>  {
>> +	unregister_chrdev_region(fpga_image_devt, MINORMASK);
>>  	class_destroy(fpga_image_load_class);
>>  	WARN_ON(!xa_empty(&fpga_image_load_xa));
>>  }
>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>> index a9cef9e1056b..b3d790e5d943 100644
>> --- a/include/linux/fpga/fpga-image-load.h
>> +++ b/include/linux/fpga/fpga-image-load.h
>> @@ -7,22 +7,51 @@
>>  #ifndef _LINUX_FPGA_IMAGE_LOAD_H
>>  #define _LINUX_FPGA_IMAGE_LOAD_H
>>  
>> +#include <linux/cdev.h>
>> +#include <linux/completion.h>
>>  #include <linux/device.h>
>>  #include <linux/mutex.h>
>>  #include <linux/types.h>
>> +#include <uapi/linux/fpga-image-load.h>
>>  
>>  struct fpga_image_load;
>>  
>>  /**
>>   * struct fpga_image_load_ops - device specific operations
>> + * @prepare:		    Required: Prepare secure update
>> + * @write_blk:		    Required: Write a block of data
>> + * @poll_complete:	    Required: Check for the completion of the
>> + *			    HW authentication/programming process. This
>> + *			    function should check for imgld->driver_unload
>> + *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
>> + * @cancel:		    Required: Signal HW to cancel update
>> + * @cleanup:		    Optional: Complements the prepare()
>> + *			    function and is called at the completion
>> + *			    of the update, whether success or failure,
>> + *			    if the prepare function succeeded.
>>   */
>>  struct fpga_image_load_ops {
>> +	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
>> +	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
>> +	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
>> +	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
>> +	void (*cleanup)(struct fpga_image_load *imgld);
>>  };
>>  
>>  struct fpga_image_load {
>>  	struct device dev;
>> +	struct cdev cdev;
>>  	const struct fpga_image_load_ops *lops;
>>  	struct mutex lock;		/* protect data structure contents */
>> +	unsigned long opened;
>> +	struct work_struct work;
>> +	struct completion update_done;
>> +	const u8 *data;				/* pointer to update data */
>> +	u32 remaining_size;			/* size remaining to transfer */
>> +	enum fpga_image_prog progress;
>> +	enum fpga_image_prog err_progress;	/* progress at time of failure */
> This field is not used in this patch? So could you introduce it later?
Yes - I'll move it. Thanks.
>
>> +	enum fpga_image_err err_code;		/* image load error code */
>> +	bool driver_unload;
>>  	void *priv;
>>  };
>>  
>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>> new file mode 100644
>> index 000000000000..4146a0a9e408
>> --- /dev/null
>> +++ b/include/uapi/linux/fpga-image-load.h
>> @@ -0,0 +1,58 @@
>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>> +/*
>> + * Header File for FPGA Image Load User API
>> + *
>> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
>> + *
>> + */
>> +
>> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
>> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/ioctl.h>
>> +
>> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9
>> +
>> +/* Image load progress codes */
>> +enum fpga_image_prog {
>> +	FPGA_IMAGE_PROG_IDLE,
>> +	FPGA_IMAGE_PROG_STARTING,
>> +	FPGA_IMAGE_PROG_PREPARING,
>> +	FPGA_IMAGE_PROG_WRITING,
>> +	FPGA_IMAGE_PROG_PROGRAMMING,
>> +	FPGA_IMAGE_PROG_MAX
>> +};
>> +
>> +/* Image error progress codes */
>> +enum fpga_image_err {
>> +	FPGA_IMAGE_ERR_NONE,
>> +	FPGA_IMAGE_ERR_HW_ERROR,
>> +	FPGA_IMAGE_ERR_TIMEOUT,
>> +	FPGA_IMAGE_ERR_CANCELED,
>> +	FPGA_IMAGE_ERR_BUSY,
>> +	FPGA_IMAGE_ERR_INVALID_SIZE,
>> +	FPGA_IMAGE_ERR_RW_ERROR,
>> +	FPGA_IMAGE_ERR_WEAROUT,
>> +	FPGA_IMAGE_ERR_MAX
>> +};
>> +
>> +#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> Put the cmd word definition under the comments and parameter definition.
OK
>
>> +
>> +/**
>> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>> + *				struct fpga_image_write)
>> + *
>> + * Upload a data buffer to the target device. The user must provide the
>> + * data buffer, size, and an eventfd file descriptor.
> I didn't find the eventfd.
It is added in a later patch. I'll change the comment accordingly.

Thanks,
- Russ
>
> Thanks,
> Yilun
>
>> + *
>> + * Return: 0 on success, -errno on failure.
>> + */
>> +struct fpga_image_write {
>> +	/* Input */
>> +	__u32 flags;		/* Zero for now */
>> +	__u32 size;		/* Data size (in bytes) to be written */
>> +	__u64 buf;		/* User space address of source data */
>> +};
>> +
>> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
>> -- 
>> 2.25.1


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

* Re: [PATCH v15 4/6] fpga: image-load: add status ioctl
  2021-09-10  8:50   ` Xu Yilun
@ 2021-09-10 23:23     ` Russ Weight
  2021-09-11 18:12       ` Tom Rix
  0 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-10 23:23 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/10/21 1:50 AM, Xu Yilun wrote:
> On Wed, Sep 08, 2021 at 07:18:44PM -0700, Russ Weight wrote:
>> Extend the FPGA Image Load class driver to include an
>> FPGA_IMAGE_LOAD_STATUS IOCTL that can be used to monitor the progress
>> of an ongoing image load.  The status returned includes how much data
>> remains to be transferred, the progress of the image load, and error
>> information in the case of a failure.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> V15:
>>  - This patch is new to the patchset and provides an FPGA_IMAGE_LOAD_STATUS
>>    IOCTL to return the current values for: remaining_size, progress,
>>    err_progress, and err_code.
>>  - This patch has elements of the following three patches from the previous
>>    patch-set:
>>      [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>>      [PATCH v14 4/6] fpga: sec-mgr: expose sec-mgr update errors
>>      [PATCH v14 5/6] fpga: sec-mgr: expose sec-mgr update size
>>  - Changed file, symbol, and config names to reflect the new driver name
>>  - There are some minor changes to locking to enable this ioctl to return
>>    coherent data.
>> ---
>>  Documentation/fpga/fpga-image-load.rst |  6 +++
>>  drivers/fpga/fpga-image-load.c         | 53 ++++++++++++++++++++++----
>>  include/uapi/linux/fpga-image-load.h   | 18 +++++++++
>>  3 files changed, 70 insertions(+), 7 deletions(-)
>>
>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>> index 739d735592a5..3d5eb51223e3 100644
>> --- a/Documentation/fpga/fpga-image-load.rst
>> +++ b/Documentation/fpga/fpga-image-load.rst
>> @@ -31,3 +31,9 @@ updated. This is an exclusive operation; an attempt to start concurrent image
>>  load for the same device will fail with EBUSY. An eventfd file descriptor
>>  parameter is provided to this IOCTL, and it will be signalled at the
>>  completion of the image load.
>> +
>> +FPGA_IMAGE_LOAD_STATUS:
>> +
>> +Collect status for an on-going image upload. The status returned includes
>> +how much data remains to be transferred, the progress of the image load,
>> +and error information in the case of a failure.
>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>> index b784456765b0..99a47b21c995 100644
>> --- a/drivers/fpga/fpga-image-load.c
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -22,10 +22,27 @@ static dev_t fpga_image_devt;
>>  
>>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>>  
>> -static void fpga_image_dev_error(struct fpga_image_load *imgld,
>> +static void fpga_image_update_progress(struct fpga_image_load *imgld,
>> +				       enum fpga_image_prog new_progress)
>> +{
>> +	mutex_lock(&imgld->lock);
>> +	imgld->progress = new_progress;
>> +	mutex_unlock(&imgld->lock);
>> +}
>> +
>> +static void fpga_image_set_error(struct fpga_image_load *imgld,
>>  				 enum fpga_image_err err_code)
>>  {
>> +	mutex_lock(&imgld->lock);
>> +	imgld->err_progress = imgld->progress;
>>  	imgld->err_code = err_code;
>> +	mutex_unlock(&imgld->lock);
>> +}
>> +
>> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
>> +				 enum fpga_image_err err_code)
>> +{
>> +	fpga_image_set_error(imgld, err_code);
>>  	imgld->lops->cancel(imgld);
>>  }
>>  
>> @@ -49,18 +66,18 @@ static void fpga_image_do_load(struct work_struct *work)
>>  
>>  	get_device(&imgld->dev);
>>  	if (!try_module_get(imgld->dev.parent->driver->owner)) {
>> -		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
>> +		fpga_image_set_error(imgld, FPGA_IMAGE_ERR_BUSY);
>>  		goto idle_exit;
>>  	}
>>  
>> -	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PREPARING);
>>  	ret = imgld->lops->prepare(imgld);
>>  	if (ret != FPGA_IMAGE_ERR_NONE) {
>>  		fpga_image_dev_error(imgld, ret);
>>  		goto modput_exit;
>>  	}
>>  
>> -	imgld->progress = FPGA_IMAGE_PROG_WRITING;
>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>>  	while (imgld->remaining_size) {
>>  		ret = imgld->lops->write_blk(imgld, offset);
>>  		if (ret != FPGA_IMAGE_ERR_NONE) {
>> @@ -71,7 +88,7 @@ static void fpga_image_do_load(struct work_struct *work)
>>  		offset = size - imgld->remaining_size;
>>  	}
>>  
>> -	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>>  	ret = imgld->lops->poll_complete(imgld);
>>  	if (ret != FPGA_IMAGE_ERR_NONE)
>>  		fpga_image_dev_error(imgld, ret);
>> @@ -151,20 +168,42 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>  	return ret;
>>  }
>>  
>> +static int fpga_image_load_ioctl_status(struct fpga_image_load *imgld,
>> +					unsigned long arg)
>> +{
>> +	struct fpga_image_status status;
>> +
>> +	memset(&status, 0, sizeof(status));
>> +	status.progress = imgld->progress;
>> +	status.remaining_size = imgld->remaining_size;
>> +	status.err_progress = imgld->err_progress;
>> +	status.err_code = imgld->err_code;
>> +
>> +	if (copy_to_user((void __user *)arg, &status, sizeof(status)))
>> +		return -EFAULT;
>> +
>> +	return 0;
>> +}
>> +
>>  static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>  				  unsigned long arg)
>>  {
>>  	struct fpga_image_load *imgld = filp->private_data;
>>  	int ret = -ENOTTY;
>>  
>> +	mutex_lock(&imgld->lock);
>> +
>>  	switch (cmd) {
>>  	case FPGA_IMAGE_LOAD_WRITE:
>> -		mutex_lock(&imgld->lock);
>>  		ret = fpga_image_load_ioctl_write(imgld, arg);
>> -		mutex_unlock(&imgld->lock);
>> +		break;
>> +	case FPGA_IMAGE_LOAD_STATUS:
>> +		ret = fpga_image_load_ioctl_status(imgld, arg);
>>  		break;
>>  	}
>>  
>> +	mutex_unlock(&imgld->lock);
>> +
>>  	return ret;
>>  }
>>  
>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>> index a60da115adf5..6a995bcc0fb7 100644
>> --- a/include/uapi/linux/fpga-image-load.h
>> +++ b/include/uapi/linux/fpga-image-load.h
>> @@ -38,6 +38,7 @@ enum fpga_image_err {
>>  };
>>  
>>  #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>> +#define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>>  
>>  /**
>>   * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>> @@ -56,4 +57,21 @@ struct fpga_image_write {
>>  	__u64 buf;		/* User space address of source data */
>>  };
>>  
>> +/**
>> + * FPGA_IMAGE_LOAD_STATUS - _IOR(FPGA_IMAGE_LOAD_MAGIC, 1,
>> + *				 struct fpga_image_status)
>> + *
>> + * Request status information for an ongoing update.
>> + * data buffer, size, and an eventfd file descriptor.
> This line is a typo.
Thanks - I'll fix it.
>
>> + *
>> + * Return: 0 on success, -errno on failure.
>> + */
>> +struct fpga_image_status {
>> +	/* Output */
>> +	__u32 remaining_size;			/* size remaining to transfer */
>> +	enum fpga_image_prog progress;		/* current progress of image load */
>> +	enum fpga_image_prog err_progress;	/* progress at time of error */
>> +	enum fpga_image_err err_code;		/* error code */
> Should we use __u32 for these enumeration types?
We could, but I did see a lot of uapi header file examples that use
enum for IOCTL structures. Is there a reason to prefer __u32? If I change
these to __u32, then I suppose I need to change from enum declarations to
#define macros?

- Russ

>
> Thanks,
> Yilun
>
>> +};
>> +
>>  #endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
>> -- 
>> 2.25.1


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

* Re: [PATCH v15 5/6] fpga: image-load: create status sysfs node
  2021-09-10  8:52   ` Xu Yilun
@ 2021-09-10 23:30     ` Russ Weight
  2021-09-11 17:58       ` Tom Rix
  0 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-10 23:30 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/10/21 1:52 AM, Xu Yilun wrote:
> On Wed, Sep 08, 2021 at 07:18:45PM -0700, Russ Weight wrote:
>> Extend the FPGA Image Load class driver to include a status sysfs node that
>> can be viewed to determine from the command line if an image load is in
>> progress. Status will be one of: idle, starting, preparing, writing, or
>> programming.
> The FPGA_IMAGE_LOAD_STATUS ioctl already provides the progress info.
> Why we need 2 user interfaces for the same information?

Updates on Vista Creek can take up to 40 minutes. I thought it might
be helpful to have a simple way, from the command line, to verify
whether or not there is an image upload in progress. Do you agree? Or
do you think this is unnecessary? Should I remove it? Or save it for
a later patch (after the main patches have been accepted)?

- Russ

>
> Thanks,
> Yilun
>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v15:
>>  - Compare to previous patch:
>>      [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>>  - Changed file, symbol, and config names to reflect the new driver name
>>  - Removed signed-off/reviewed-by tags
>> v14:
>>  - Updated ABI documentation date and kernel version
>> v13:
>>   - No change
>> v12:
>>   - Updated Date and KernelVersion fields in ABI documentation
>>   - Changed syntax of sec_mgr_prog_str[] array definition from:
>> 	"idle",			/* FPGA_SEC_PROG_IDLE */
>>     to:
>> 	[FPGA_SEC_PROG_IDLE]	    = "idle",
>> v11:
>>   - No change
>> v10:
>>   - Rebased to 5.12-rc2 next
>>   - Updated Date and KernelVersion in ABI documentation
>> v9:
>>   - Updated Date and KernelVersion in ABI documentation
>> v8:
>>   - No change
>> v7:
>>   - Changed Date in documentation file to December 2020
>> v6:
>>   - No change
>> v5:
>>   - Use new function sysfs_emit() in the status_show() function
>> v4:
>>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>     and removed unnecessary references to "Intel".
>>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>> v3:
>>   - Use a local variable to read progress once in status_show()
>>   - Use dev_err to report invalid progress status
>> v2:
>>   - Bumped documentation date and version
>>   - Changed progress state "read_file" to "reading"
>> ---
>>  .../ABI/testing/sysfs-class-fpga-image-load   |  7 ++++
>>  MAINTAINERS                                   |  1 +
>>  drivers/fpga/fpga-image-load.c                | 33 +++++++++++++++++++
>>  3 files changed, 41 insertions(+)
>>  create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-image-load
>>
>> diff --git a/Documentation/ABI/testing/sysfs-class-fpga-image-load b/Documentation/ABI/testing/sysfs-class-fpga-image-load
>> new file mode 100644
>> index 000000000000..6c04a49f01cc
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-class-fpga-image-load
>> @@ -0,0 +1,7 @@
>> +What: 		/sys/class/fpga_image_load/fpga_imageX/status
>> +Date:		Aug 2021
>> +KernelVersion:	5.15
>> +Contact:	Russ Weight <russell.h.weight@intel.com>
>> +Description:	Read-only. Returns a string describing the current status
>> +		of an FPGA image upload. The string will be one of the
>> +		following: idle, starting, preparing, writing, programming.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 637bc003ca81..e3fbc1bde9bc 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7362,6 +7362,7 @@ FPGA SECURITY MANAGER DRIVERS
>>  M:	Russ Weight <russell.h.weight@intel.com>
>>  L:	linux-fpga@vger.kernel.org
>>  S:	Maintained
>> +F:	Documentation/ABI/testing/sysfs-class-fpga-image-load
>>  F:	Documentation/fpga/fpga-image-load.rst
>>  F:	drivers/fpga/fpga-image-load.c
>>  F:	include/linux/fpga/fpga-image-load.h
>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>> index 99a47b21c995..6ec0a39f07b3 100644
>> --- a/drivers/fpga/fpga-image-load.c
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -236,6 +236,38 @@ static const struct file_operations fpga_image_load_fops = {
>>  	.unlocked_ioctl = fpga_image_load_ioctl,
>>  };
>>  
>> +static const char * const image_load_prog_str[] = {
>> +	[FPGA_IMAGE_PROG_IDLE]	      = "idle",
>> +	[FPGA_IMAGE_PROG_STARTING]    = "starting",
>> +	[FPGA_IMAGE_PROG_PREPARING]   = "preparing",
>> +	[FPGA_IMAGE_PROG_WRITING]     = "writing",
>> +	[FPGA_IMAGE_PROG_PROGRAMMING] = "programming"
>> +};
>> +
>> +static ssize_t
>> +status_show(struct device *dev, struct device_attribute *attr, char *buf)
>> +{
>> +	struct fpga_image_load *imgld = to_image_load(dev);
>> +	const char *status = "unknown-status";
>> +	enum fpga_image_prog progress;
>> +
>> +	progress = imgld->progress;
>> +	if (progress < FPGA_IMAGE_PROG_MAX)
>> +		status = image_load_prog_str[progress];
>> +	else
>> +		dev_err(dev, "Invalid status during secure update: %d\n",
>> +			progress);
>> +
>> +	return sysfs_emit(buf, "%s\n", status);
>> +}
>> +static DEVICE_ATTR_RO(status);
>> +
>> +static struct attribute *fpga_image_load_attrs[] = {
>> +	&dev_attr_status.attr,
>> +	NULL,
>> +};
>> +ATTRIBUTE_GROUPS(fpga_image_load);
>> +
>>  /**
>>   * fpga_image_load_register - create and register an FPGA Image Load Device
>>   *
>> @@ -373,6 +405,7 @@ static int __init fpga_image_load_class_init(void)
>>  	if (ret)
>>  		goto exit_destroy_class;
>>  
>> +	fpga_image_load_class->dev_groups = fpga_image_load_groups;
>>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>>  
>>  	return 0;
>> -- 
>> 2.25.1


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

* Re: [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-10 14:55   ` Xu Yilun
@ 2021-09-10 23:38     ` Russ Weight
  2021-09-11 13:13       ` Tom Rix
  0 siblings, 1 reply; 28+ messages in thread
From: Russ Weight @ 2021-09-10 23:38 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/10/21 7:55 AM, Xu Yilun wrote:
> On Wed, Sep 08, 2021 at 07:18:46PM -0700, Russ Weight wrote:
>> Extend the FPGA Image Load class driver to include a cancel IOCTL that
>> can be used to request that an image upload be canceled. The IOCTL may
>> return EBUSY if it cannot be canceled by software or ENODEV if there
>> is no update in progress.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
>> v15:
>>  - Compare to previous patch:
>>      [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
>>  - Changed file, symbol, and config names to reflect the new driver name
>>  - Cancel is now initiated by IOCT instead of sysfs
>>  - Removed signed-off/reviewed-by tags
>> v14:
>>  - Updated ABI documentation date and kernel version
>> v13:
>>   - No change
>> v12:
>>   - Updated Date and KernelVersion fields in ABI documentation
>> v11:
>>   - No change
>> v10:
>>   - Rebased to 5.12-rc2 next
>>   - Updated Date and KernelVersion in ABI documentation
>> v9:
>>   - Updated Date and KernelVersion in ABI documentation
>> v8:
>>   - No change
>> v7:
>>   - Changed Date in documentation file to December 2020
>> v6:
>>   - No change
>> v5:
>>   - No change
>> v4:
>>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>     and removed unnecessary references to "Intel".
>>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>> v3:
>>   - No change
>> v2:
>>   - Bumped documentation date and version
>>   - Minor code cleanup per review comments
>> ---
>> ---
>>  Documentation/fpga/fpga-image-load.rst |  6 ++++
>>  drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
>>  include/linux/fpga/fpga-image-load.h   |  1 +
>>  include/uapi/linux/fpga-image-load.h   |  1 +
>>  4 files changed, 49 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>> index 3d5eb51223e3..763e7833a6ea 100644
>> --- a/Documentation/fpga/fpga-image-load.rst
>> +++ b/Documentation/fpga/fpga-image-load.rst
>> @@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
>>  Collect status for an on-going image upload. The status returned includes
>>  how much data remains to be transferred, the progress of the image load,
>>  and error information in the case of a failure.
>> +
>> +FPGA_IMAGE_LOAD_CANCEL:
>> +
>> +Request that a on-going image upload be cancelled. This IOCTL may return
>> +EBUSY if it cannot be cancelled by software or ENODEV if there is no update
>> +in progress.
>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>> index 6ec0a39f07b3..c32e4b1ea35a 100644
>> --- a/drivers/fpga/fpga-image-load.c
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>  	imgld->lops->cancel(imgld);
>>  }
>>  
>> +static int fpga_image_prog_transition(struct fpga_image_load *imgld,
>> +				      enum fpga_image_prog new_progress)
>> +{
>> +	int ret = 0;
>> +
>> +	mutex_lock(&imgld->lock);
>> +	if (imgld->request_cancel) {
>> +		imgld->err_progress = imgld->progress;
>> +		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
>> +		imgld->lops->cancel(imgld);
>> +		ret = -ECANCELED;
>> +	} else {
>> +		imgld->progress = new_progress;
>> +	}
>> +	mutex_unlock(&imgld->lock);
>> +	return ret;
>> +}
>> +
>>  static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>>  {
>>  	mutex_lock(&imgld->lock);
>> @@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
>>  		goto modput_exit;
>>  	}
>>  
>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>> -	while (imgld->remaining_size) {
>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
>> +		goto done;
>> +
>> +	while (imgld->remaining_size && !imgld->request_cancel) {
>>  		ret = imgld->lops->write_blk(imgld, offset);
>>  		if (ret != FPGA_IMAGE_ERR_NONE) {
>>  			fpga_image_dev_error(imgld, ret);
>> @@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
>>  		offset = size - imgld->remaining_size;
>>  	}
>>  
>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
>> +		goto done;
>> +
>>  	ret = imgld->lops->poll_complete(imgld);
>>  	if (ret != FPGA_IMAGE_ERR_NONE)
>>  		fpga_image_dev_error(imgld, ret);
>> @@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>  	imgld->remaining_size = wb.size;
>>  	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>>  	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>> +	imgld->request_cancel = false;
>>  	reinit_completion(&imgld->update_done);
>>  	schedule_work(&imgld->work);
>>  	return 0;
>> @@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>  				  unsigned long arg)
>>  {
>>  	struct fpga_image_load *imgld = filp->private_data;
>> -	int ret = -ENOTTY;
>> +	int ret = 0;
>>  
>>  	mutex_lock(&imgld->lock);
>>  
>> @@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>  	case FPGA_IMAGE_LOAD_STATUS:
>>  		ret = fpga_image_load_ioctl_status(imgld, arg);
>>  		break;
>> +	case FPGA_IMAGE_LOAD_CANCEL:
>> +		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
>> +			ret = -EBUSY;
>> +		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
>> +			ret = -ENODEV;
>> +		else
>> +			imgld->request_cancel = true;
>> +		break;
>> +	default:
>> +		ret = -ENOTTY;
>> +		break;
>>  	}
>>  
>>  	mutex_unlock(&imgld->lock);
>> @@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>  		goto unregister;
>>  	}
>>  
>> +	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
>> +		imgld->request_cancel = true;
>> +
> Why we cancel the programing rather than waiting for programing done?

This isn't new - it is the way the security manager was implemented.

Updates can take up to 40 minutes for the N3000. If a person tries
to unload the driver modules, should we hang for 40 minutes? Or should
we try to cancel the update and allow the module to be unloaded?
I think it is reasonable to cancel the update (if possible) under these
circumstances. What do you think?

- Russ

>
> Thanks,
> Yilun
>
>>  	mutex_unlock(&imgld->lock);
>>  	wait_for_completion(&imgld->update_done);
>>  
>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>> index 68f3105b51d2..4e51b9fd1724 100644
>> --- a/include/linux/fpga/fpga-image-load.h
>> +++ b/include/linux/fpga/fpga-image-load.h
>> @@ -52,6 +52,7 @@ struct fpga_image_load {
>>  	enum fpga_image_prog progress;
>>  	enum fpga_image_prog err_progress;	/* progress at time of failure */
>>  	enum fpga_image_err err_code;		/* image load error code */
>> +	bool request_cancel;
>>  	bool driver_unload;
>>  	struct eventfd_ctx *finished;
>>  	void *priv;
>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>> index 6a995bcc0fb7..8d0dfa1f9b77 100644
>> --- a/include/uapi/linux/fpga-image-load.h
>> +++ b/include/uapi/linux/fpga-image-load.h
>> @@ -39,6 +39,7 @@ enum fpga_image_err {
>>  
>>  #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>>  #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>> +#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
>>  
>>  /**
>>   * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>> -- 
>> 2.25.1


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

* Re: [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-10 23:38     ` Russ Weight
@ 2021-09-11 13:13       ` Tom Rix
  2021-09-13 10:00         ` Xu Yilun
  0 siblings, 1 reply; 28+ messages in thread
From: Tom Rix @ 2021-09-11 13:13 UTC (permalink / raw)
  To: Russ Weight, Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu, matthew.gerlach


On 9/10/21 4:38 PM, Russ Weight wrote:
>
> On 9/10/21 7:55 AM, Xu Yilun wrote:
>> On Wed, Sep 08, 2021 at 07:18:46PM -0700, Russ Weight wrote:
>>> Extend the FPGA Image Load class driver to include a cancel IOCTL that
>>> can be used to request that an image upload be canceled. The IOCTL may
>>> return EBUSY if it cannot be canceled by software or ENODEV if there
>>> is no update in progress.
>>>
>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>> ---
>>> v15:
>>>   - Compare to previous patch:
>>>       [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
>>>   - Changed file, symbol, and config names to reflect the new driver name
>>>   - Cancel is now initiated by IOCT instead of sysfs
>>>   - Removed signed-off/reviewed-by tags
>>> v14:
>>>   - Updated ABI documentation date and kernel version
>>> v13:
>>>    - No change
>>> v12:
>>>    - Updated Date and KernelVersion fields in ABI documentation
>>> v11:
>>>    - No change
>>> v10:
>>>    - Rebased to 5.12-rc2 next
>>>    - Updated Date and KernelVersion in ABI documentation
>>> v9:
>>>    - Updated Date and KernelVersion in ABI documentation
>>> v8:
>>>    - No change
>>> v7:
>>>    - Changed Date in documentation file to December 2020
>>> v6:
>>>    - No change
>>> v5:
>>>    - No change
>>> v4:
>>>    - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>>      and removed unnecessary references to "Intel".
>>>    - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>>> v3:
>>>    - No change
>>> v2:
>>>    - Bumped documentation date and version
>>>    - Minor code cleanup per review comments
>>> ---
>>> ---
>>>   Documentation/fpga/fpga-image-load.rst |  6 ++++
>>>   drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
>>>   include/linux/fpga/fpga-image-load.h   |  1 +
>>>   include/uapi/linux/fpga-image-load.h   |  1 +
>>>   4 files changed, 49 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>>> index 3d5eb51223e3..763e7833a6ea 100644
>>> --- a/Documentation/fpga/fpga-image-load.rst
>>> +++ b/Documentation/fpga/fpga-image-load.rst
>>> @@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
>>>   Collect status for an on-going image upload. The status returned includes
>>>   how much data remains to be transferred, the progress of the image load,
>>>   and error information in the case of a failure.
>>> +
>>> +FPGA_IMAGE_LOAD_CANCEL:
>>> +
>>> +Request that a on-going image upload be cancelled. This IOCTL may return
>>> +EBUSY if it cannot be cancelled by software or ENODEV if there is no update
>>> +in progress.
>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>>> index 6ec0a39f07b3..c32e4b1ea35a 100644
>>> --- a/drivers/fpga/fpga-image-load.c
>>> +++ b/drivers/fpga/fpga-image-load.c
>>> @@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>>   	imgld->lops->cancel(imgld);
>>>   }
>>>   
>>> +static int fpga_image_prog_transition(struct fpga_image_load *imgld,
>>> +				      enum fpga_image_prog new_progress)
>>> +{
>>> +	int ret = 0;
>>> +
>>> +	mutex_lock(&imgld->lock);
>>> +	if (imgld->request_cancel) {
>>> +		imgld->err_progress = imgld->progress;
>>> +		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
>>> +		imgld->lops->cancel(imgld);
>>> +		ret = -ECANCELED;
>>> +	} else {
>>> +		imgld->progress = new_progress;
>>> +	}
>>> +	mutex_unlock(&imgld->lock);
>>> +	return ret;
>>> +}
>>> +
>>>   static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>>>   {
>>>   	mutex_lock(&imgld->lock);
>>> @@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
>>>   		goto modput_exit;
>>>   	}
>>>   
>>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>>> -	while (imgld->remaining_size) {
>>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
>>> +		goto done;
>>> +
>>> +	while (imgld->remaining_size && !imgld->request_cancel) {
>>>   		ret = imgld->lops->write_blk(imgld, offset);
>>>   		if (ret != FPGA_IMAGE_ERR_NONE) {
>>>   			fpga_image_dev_error(imgld, ret);
>>> @@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
>>>   		offset = size - imgld->remaining_size;
>>>   	}
>>>   
>>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
>>> +		goto done;
>>> +
>>>   	ret = imgld->lops->poll_complete(imgld);
>>>   	if (ret != FPGA_IMAGE_ERR_NONE)
>>>   		fpga_image_dev_error(imgld, ret);
>>> @@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>>   	imgld->remaining_size = wb.size;
>>>   	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>>>   	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>>> +	imgld->request_cancel = false;
>>>   	reinit_completion(&imgld->update_done);
>>>   	schedule_work(&imgld->work);
>>>   	return 0;
>>> @@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>   				  unsigned long arg)
>>>   {
>>>   	struct fpga_image_load *imgld = filp->private_data;
>>> -	int ret = -ENOTTY;
>>> +	int ret = 0;
>>>   
>>>   	mutex_lock(&imgld->lock);
>>>   
>>> @@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>   	case FPGA_IMAGE_LOAD_STATUS:
>>>   		ret = fpga_image_load_ioctl_status(imgld, arg);
>>>   		break;
>>> +	case FPGA_IMAGE_LOAD_CANCEL:
>>> +		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
>>> +			ret = -EBUSY;
>>> +		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
>>> +			ret = -ENODEV;
>>> +		else
>>> +			imgld->request_cancel = true;
>>> +		break;
>>> +	default:
>>> +		ret = -ENOTTY;
>>> +		break;
>>>   	}
>>>   
>>>   	mutex_unlock(&imgld->lock);
>>> @@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>>   		goto unregister;
>>>   	}
>>>   
>>> +	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
>>> +		imgld->request_cancel = true;
>>> +
>> Why we cancel the programing rather than waiting for programing done?
> This isn't new - it is the way the security manager was implemented.
>
> Updates can take up to 40 minutes for the N3000. If a person tries
> to unload the driver modules, should we hang for 40 minutes? Or should
> we try to cancel the update and allow the module to be unloaded?
> I think it is reasonable to cancel the update (if possible) under these
> circumstances. What do you think?

I do not think stalling a shutdown for 40 minutes is good.

Is there a chance that cancelling would brick the card?

After restarting, can the hw detect an interrupted update ?

Tom

>
> - Russ
>
>> Thanks,
>> Yilun
>>
>>>   	mutex_unlock(&imgld->lock);
>>>   	wait_for_completion(&imgld->update_done);
>>>   
>>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>>> index 68f3105b51d2..4e51b9fd1724 100644
>>> --- a/include/linux/fpga/fpga-image-load.h
>>> +++ b/include/linux/fpga/fpga-image-load.h
>>> @@ -52,6 +52,7 @@ struct fpga_image_load {
>>>   	enum fpga_image_prog progress;
>>>   	enum fpga_image_prog err_progress;	/* progress at time of failure */
>>>   	enum fpga_image_err err_code;		/* image load error code */
>>> +	bool request_cancel;
>>>   	bool driver_unload;
>>>   	struct eventfd_ctx *finished;
>>>   	void *priv;
>>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>>> index 6a995bcc0fb7..8d0dfa1f9b77 100644
>>> --- a/include/uapi/linux/fpga-image-load.h
>>> +++ b/include/uapi/linux/fpga-image-load.h
>>> @@ -39,6 +39,7 @@ enum fpga_image_err {
>>>   
>>>   #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>>>   #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>>> +#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
>>>   
>>>   /**
>>>    * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>>> -- 
>>> 2.25.1


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

* Re: [PATCH v15 5/6] fpga: image-load: create status sysfs node
  2021-09-10 23:30     ` Russ Weight
@ 2021-09-11 17:58       ` Tom Rix
  2021-09-13  8:27         ` Xu Yilun
  0 siblings, 1 reply; 28+ messages in thread
From: Tom Rix @ 2021-09-11 17:58 UTC (permalink / raw)
  To: Russ Weight, Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu, matthew.gerlach


On 9/10/21 4:30 PM, Russ Weight wrote:
>
> On 9/10/21 1:52 AM, Xu Yilun wrote:
>> On Wed, Sep 08, 2021 at 07:18:45PM -0700, Russ Weight wrote:
>>> Extend the FPGA Image Load class driver to include a status sysfs node that
>>> can be viewed to determine from the command line if an image load is in
>>> progress. Status will be one of: idle, starting, preparing, writing, or
>>> programming.
>> The FPGA_IMAGE_LOAD_STATUS ioctl already provides the progress info.
>> Why we need 2 user interfaces for the same information?
> Updates on Vista Creek can take up to 40 minutes. I thought it might
> be helpful to have a simple way, from the command line, to verify
> whether or not there is an image upload in progress. Do you agree? Or
> do you think this is unnecessary? Should I remove it? Or save it for
> a later patch (after the main patches have been accepted)?

I agree, there should not be two methods to doing the same thing.

I prefer the ioctl since it handles other cmd's as well.

The user has to use the ioctl to start the write so it would for more 
natural to use the ioctl over sysfs to the check the status

Remove it.

Make a strong case for it's need when/if you want to add it back later.

Tom

>
> - Russ
>
>> Thanks,
>> Yilun
>>
>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>> ---
>>> v15:
>>>   - Compare to previous patch:
>>>       [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>>>   - Changed file, symbol, and config names to reflect the new driver name
>>>   - Removed signed-off/reviewed-by tags
>>> v14:
>>>   - Updated ABI documentation date and kernel version
>>> v13:
>>>    - No change
>>> v12:
>>>    - Updated Date and KernelVersion fields in ABI documentation
>>>    - Changed syntax of sec_mgr_prog_str[] array definition from:
>>> 	"idle",			/* FPGA_SEC_PROG_IDLE */
>>>      to:
>>> 	[FPGA_SEC_PROG_IDLE]	    = "idle",
>>> v11:
>>>    - No change
>>> v10:
>>>    - Rebased to 5.12-rc2 next
>>>    - Updated Date and KernelVersion in ABI documentation
>>> v9:
>>>    - Updated Date and KernelVersion in ABI documentation
>>> v8:
>>>    - No change
>>> v7:
>>>    - Changed Date in documentation file to December 2020
>>> v6:
>>>    - No change
>>> v5:
>>>    - Use new function sysfs_emit() in the status_show() function
>>> v4:
>>>    - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>>      and removed unnecessary references to "Intel".
>>>    - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>>> v3:
>>>    - Use a local variable to read progress once in status_show()
>>>    - Use dev_err to report invalid progress status
>>> v2:
>>>    - Bumped documentation date and version
>>>    - Changed progress state "read_file" to "reading"
>>> ---
>>>   .../ABI/testing/sysfs-class-fpga-image-load   |  7 ++++
>>>   MAINTAINERS                                   |  1 +
>>>   drivers/fpga/fpga-image-load.c                | 33 +++++++++++++++++++
>>>   3 files changed, 41 insertions(+)
>>>   create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-image-load
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-class-fpga-image-load b/Documentation/ABI/testing/sysfs-class-fpga-image-load
>>> new file mode 100644
>>> index 000000000000..6c04a49f01cc
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-class-fpga-image-load
>>> @@ -0,0 +1,7 @@
>>> +What: 		/sys/class/fpga_image_load/fpga_imageX/status
>>> +Date:		Aug 2021
>>> +KernelVersion:	5.15
>>> +Contact:	Russ Weight <russell.h.weight@intel.com>
>>> +Description:	Read-only. Returns a string describing the current status
>>> +		of an FPGA image upload. The string will be one of the
>>> +		following: idle, starting, preparing, writing, programming.
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 637bc003ca81..e3fbc1bde9bc 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -7362,6 +7362,7 @@ FPGA SECURITY MANAGER DRIVERS
>>>   M:	Russ Weight <russell.h.weight@intel.com>
>>>   L:	linux-fpga@vger.kernel.org
>>>   S:	Maintained
>>> +F:	Documentation/ABI/testing/sysfs-class-fpga-image-load
>>>   F:	Documentation/fpga/fpga-image-load.rst
>>>   F:	drivers/fpga/fpga-image-load.c
>>>   F:	include/linux/fpga/fpga-image-load.h
>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>>> index 99a47b21c995..6ec0a39f07b3 100644
>>> --- a/drivers/fpga/fpga-image-load.c
>>> +++ b/drivers/fpga/fpga-image-load.c
>>> @@ -236,6 +236,38 @@ static const struct file_operations fpga_image_load_fops = {
>>>   	.unlocked_ioctl = fpga_image_load_ioctl,
>>>   };
>>>   
>>> +static const char * const image_load_prog_str[] = {
>>> +	[FPGA_IMAGE_PROG_IDLE]	      = "idle",
>>> +	[FPGA_IMAGE_PROG_STARTING]    = "starting",
>>> +	[FPGA_IMAGE_PROG_PREPARING]   = "preparing",
>>> +	[FPGA_IMAGE_PROG_WRITING]     = "writing",
>>> +	[FPGA_IMAGE_PROG_PROGRAMMING] = "programming"
>>> +};
>>> +
>>> +static ssize_t
>>> +status_show(struct device *dev, struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct fpga_image_load *imgld = to_image_load(dev);
>>> +	const char *status = "unknown-status";
>>> +	enum fpga_image_prog progress;
>>> +
>>> +	progress = imgld->progress;
>>> +	if (progress < FPGA_IMAGE_PROG_MAX)
>>> +		status = image_load_prog_str[progress];
>>> +	else
>>> +		dev_err(dev, "Invalid status during secure update: %d\n",
>>> +			progress);
>>> +
>>> +	return sysfs_emit(buf, "%s\n", status);
>>> +}
>>> +static DEVICE_ATTR_RO(status);
>>> +
>>> +static struct attribute *fpga_image_load_attrs[] = {
>>> +	&dev_attr_status.attr,
>>> +	NULL,
>>> +};
>>> +ATTRIBUTE_GROUPS(fpga_image_load);
>>> +
>>>   /**
>>>    * fpga_image_load_register - create and register an FPGA Image Load Device
>>>    *
>>> @@ -373,6 +405,7 @@ static int __init fpga_image_load_class_init(void)
>>>   	if (ret)
>>>   		goto exit_destroy_class;
>>>   
>>> +	fpga_image_load_class->dev_groups = fpga_image_load_groups;
>>>   	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>>>   
>>>   	return 0;
>>> -- 
>>> 2.25.1


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

* Re: [PATCH v15 4/6] fpga: image-load: add status ioctl
  2021-09-10 23:23     ` Russ Weight
@ 2021-09-11 18:12       ` Tom Rix
  2021-09-13  8:24         ` Xu Yilun
  0 siblings, 1 reply; 28+ messages in thread
From: Tom Rix @ 2021-09-11 18:12 UTC (permalink / raw)
  To: Russ Weight, Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu, matthew.gerlach


On 9/10/21 4:23 PM, Russ Weight wrote:
>
> On 9/10/21 1:50 AM, Xu Yilun wrote:
>> On Wed, Sep 08, 2021 at 07:18:44PM -0700, Russ Weight wrote:
>>> Extend the FPGA Image Load class driver to include an
>>> FPGA_IMAGE_LOAD_STATUS IOCTL that can be used to monitor the progress
>>> of an ongoing image load.  The status returned includes how much data
>>> remains to be transferred, the progress of the image load, and error
>>> information in the case of a failure.
>>>
>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>> ---
>>> V15:
>>>   - This patch is new to the patchset and provides an FPGA_IMAGE_LOAD_STATUS
>>>     IOCTL to return the current values for: remaining_size, progress,
>>>     err_progress, and err_code.
>>>   - This patch has elements of the following three patches from the previous
>>>     patch-set:
>>>       [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
>>>       [PATCH v14 4/6] fpga: sec-mgr: expose sec-mgr update errors
>>>       [PATCH v14 5/6] fpga: sec-mgr: expose sec-mgr update size
>>>   - Changed file, symbol, and config names to reflect the new driver name
>>>   - There are some minor changes to locking to enable this ioctl to return
>>>     coherent data.
>>> ---
>>>   Documentation/fpga/fpga-image-load.rst |  6 +++
>>>   drivers/fpga/fpga-image-load.c         | 53 ++++++++++++++++++++++----
>>>   include/uapi/linux/fpga-image-load.h   | 18 +++++++++
>>>   3 files changed, 70 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>>> index 739d735592a5..3d5eb51223e3 100644
>>> --- a/Documentation/fpga/fpga-image-load.rst
>>> +++ b/Documentation/fpga/fpga-image-load.rst
>>> @@ -31,3 +31,9 @@ updated. This is an exclusive operation; an attempt to start concurrent image
>>>   load for the same device will fail with EBUSY. An eventfd file descriptor
>>>   parameter is provided to this IOCTL, and it will be signalled at the
>>>   completion of the image load.
>>> +
>>> +FPGA_IMAGE_LOAD_STATUS:
>>> +
>>> +Collect status for an on-going image upload. The status returned includes
>>> +how much data remains to be transferred, the progress of the image load,
>>> +and error information in the case of a failure.
>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>>> index b784456765b0..99a47b21c995 100644
>>> --- a/drivers/fpga/fpga-image-load.c
>>> +++ b/drivers/fpga/fpga-image-load.c
>>> @@ -22,10 +22,27 @@ static dev_t fpga_image_devt;
>>>   
>>>   #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>>>   
>>> -static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>> +static void fpga_image_update_progress(struct fpga_image_load *imgld,
>>> +				       enum fpga_image_prog new_progress)
>>> +{
>>> +	mutex_lock(&imgld->lock);
>>> +	imgld->progress = new_progress;
>>> +	mutex_unlock(&imgld->lock);
>>> +}
>>> +
>>> +static void fpga_image_set_error(struct fpga_image_load *imgld,
>>>   				 enum fpga_image_err err_code)
>>>   {
>>> +	mutex_lock(&imgld->lock);
>>> +	imgld->err_progress = imgld->progress;
>>>   	imgld->err_code = err_code;
>>> +	mutex_unlock(&imgld->lock);
>>> +}
>>> +
>>> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>> +				 enum fpga_image_err err_code)
>>> +{
>>> +	fpga_image_set_error(imgld, err_code);
>>>   	imgld->lops->cancel(imgld);
>>>   }
>>>   
>>> @@ -49,18 +66,18 @@ static void fpga_image_do_load(struct work_struct *work)
>>>   
>>>   	get_device(&imgld->dev);
>>>   	if (!try_module_get(imgld->dev.parent->driver->owner)) {
>>> -		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
>>> +		fpga_image_set_error(imgld, FPGA_IMAGE_ERR_BUSY);
>>>   		goto idle_exit;
>>>   	}
>>>   
>>> -	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
>>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PREPARING);
>>>   	ret = imgld->lops->prepare(imgld);
>>>   	if (ret != FPGA_IMAGE_ERR_NONE) {
>>>   		fpga_image_dev_error(imgld, ret);
>>>   		goto modput_exit;
>>>   	}
>>>   
>>> -	imgld->progress = FPGA_IMAGE_PROG_WRITING;
>>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>>>   	while (imgld->remaining_size) {
>>>   		ret = imgld->lops->write_blk(imgld, offset);
>>>   		if (ret != FPGA_IMAGE_ERR_NONE) {
>>> @@ -71,7 +88,7 @@ static void fpga_image_do_load(struct work_struct *work)
>>>   		offset = size - imgld->remaining_size;
>>>   	}
>>>   
>>> -	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
>>> +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>>>   	ret = imgld->lops->poll_complete(imgld);
>>>   	if (ret != FPGA_IMAGE_ERR_NONE)
>>>   		fpga_image_dev_error(imgld, ret);
>>> @@ -151,20 +168,42 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>>   	return ret;
>>>   }
>>>   
>>> +static int fpga_image_load_ioctl_status(struct fpga_image_load *imgld,
>>> +					unsigned long arg)
>>> +{
>>> +	struct fpga_image_status status;
>>> +
>>> +	memset(&status, 0, sizeof(status));
>>> +	status.progress = imgld->progress;
>>> +	status.remaining_size = imgld->remaining_size;
>>> +	status.err_progress = imgld->err_progress;
>>> +	status.err_code = imgld->err_code;
>>> +
>>> +	if (copy_to_user((void __user *)arg, &status, sizeof(status)))
>>> +		return -EFAULT;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>>   static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>   				  unsigned long arg)
>>>   {
>>>   	struct fpga_image_load *imgld = filp->private_data;
>>>   	int ret = -ENOTTY;
>>>   
>>> +	mutex_lock(&imgld->lock);
>>> +
>>>   	switch (cmd) {
>>>   	case FPGA_IMAGE_LOAD_WRITE:
>>> -		mutex_lock(&imgld->lock);
>>>   		ret = fpga_image_load_ioctl_write(imgld, arg);
>>> -		mutex_unlock(&imgld->lock);
>>> +		break;
>>> +	case FPGA_IMAGE_LOAD_STATUS:
>>> +		ret = fpga_image_load_ioctl_status(imgld, arg);
>>>   		break;
>>>   	}
>>>   
>>> +	mutex_unlock(&imgld->lock);
>>> +
>>>   	return ret;
>>>   }
>>>   
>>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>>> index a60da115adf5..6a995bcc0fb7 100644
>>> --- a/include/uapi/linux/fpga-image-load.h
>>> +++ b/include/uapi/linux/fpga-image-load.h
>>> @@ -38,6 +38,7 @@ enum fpga_image_err {
>>>   };
>>>   
>>>   #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>>> +#define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>>>   
>>>   /**
>>>    * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>>> @@ -56,4 +57,21 @@ struct fpga_image_write {
>>>   	__u64 buf;		/* User space address of source data */
>>>   };
>>>   
>>> +/**
>>> + * FPGA_IMAGE_LOAD_STATUS - _IOR(FPGA_IMAGE_LOAD_MAGIC, 1,
>>> + *				 struct fpga_image_status)
>>> + *
>>> + * Request status information for an ongoing update.
>>> + * data buffer, size, and an eventfd file descriptor.
>> This line is a typo.
> Thanks - I'll fix it.
>>> + *
>>> + * Return: 0 on success, -errno on failure.
>>> + */
>>> +struct fpga_image_status {
>>> +	/* Output */
>>> +	__u32 remaining_size;			/* size remaining to transfer */
>>> +	enum fpga_image_prog progress;		/* current progress of image load */
>>> +	enum fpga_image_prog err_progress;	/* progress at time of error */
>>> +	enum fpga_image_err err_code;		/* error code */
>> Should we use __u32 for these enumeration types?
> We could, but I did see a lot of uapi header file examples that use
> enum for IOCTL structures. Is there a reason to prefer __u32? If I change
> these to __u32, then I suppose I need to change from enum declarations to
> #define macros?

The __u32 makes sure the struct is a constant size.

yes, change the enums to #defines.

The layout of a new api should be consistent with similar older api.

review uapi/linux/fpga-dfl.h , the dfl_fpga_port_region_info struct.

Tom


> - Russ
>
>> Thanks,
>> Yilun
>>
>>> +};
>>> +
>>>   #endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
>>> -- 
>>> 2.25.1


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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-10 23:18     ` Russ Weight
@ 2021-09-13  6:48       ` Xu Yilun
  2021-09-13  9:36         ` Xu Yilun
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-13  6:48 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Fri, Sep 10, 2021 at 04:18:26PM -0700, Russ Weight wrote:
> 
> 
> On 9/10/21 1:22 AM, Xu Yilun wrote:
> > On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote:
> >> Extend the FPGA Image Load class driver to include IOCTL support
> >> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
> >> The IOCTL will return immediately, and the update will begin in the
> >> context of a kernel worker thread.
> >>
> >> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> >> ---
> >> v15:
> >>  - Compare to previous patch:
> >>      [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
> >>  - Changed file, symbol, and config names to reflect the new driver name
> >>  - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
> >>    IOCTL for writing the image data to the FPGA card. The driver no longer
> >>    uses the request_firmware framework.
> >>  - Fixed some error return values in fpga_image_load_register()
> >>  - Removed signed-off/reviewed-by tags
> >> v14:
> >>  - Added MAINTAINERS reference for
> >>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
> >>  - Updated ABI documentation date and kernel version
> >>  - Updated copyright to 2021
> >> v13:
> >>   - Change "if (count == 0 || " to "if (!count || "
> >>   - Improve error message: "Attempt to register without all required ops\n"
> >> v12:
> >>   - Updated Date and KernelVersion fields in ABI documentation
> >>   - Removed size parameter from write_blk() op - it is now up to
> >>     the lower-level driver to determine the appropriate size and
> >>     to update smgr->remaining_size accordingly.
> >> v11:
> >>   - Fixed a spelling error in a comment
> >>   - Initialize smgr->err_code and smgr->progress explicitly in
> >>     fpga_sec_mgr_create() instead of accepting the default 0 value.
> >> v10:
> >>   - Rebased to 5.12-rc2 next
> >>   - Updated Date and KernelVersion in ABI documentation
> >> v9:
> >>   - Updated Date and KernelVersion in ABI documentation
> >> v8:
> >>   - No change
> >> v7:
> >>   - Changed Date in documentation file to December 2020
> >>   - Changed filename_store() to use kmemdup_nul() instead of
> >>     kstrndup() and changed the count to not assume a line-return.
> >> v6:
> >>   - Changed "security update" to "secure update" in commit message
> >> v5:
> >>   - When checking the return values for functions of type enum
> >>     fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
> >> v4:
> >>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
> >>     and removed unnecessary references to "Intel".
> >>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> >> v3:
> >>   - Removed unnecessary "goto done"
> >>   - Added a comment to explain imgr->driver_unload in
> >>     ifpga_sec_mgr_unregister()
> >> v2:
> >>   - Bumped documentation date and version
> >>   - Removed explicit value assignments in enums
> >>   - Other minor code cleanup per review comments
> >> ---
> >>  Documentation/fpga/fpga-image-load.rst |  21 +++
> >>  MAINTAINERS                            |   1 +
> >>  drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
> >>  include/linux/fpga/fpga-image-load.h   |  29 ++++
> >>  include/uapi/linux/fpga-image-load.h   |  58 +++++++
> >>  5 files changed, 329 insertions(+), 4 deletions(-)
> >>  create mode 100644 include/uapi/linux/fpga-image-load.h
> >>
> >> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> >> index a6e53ac66026..2ca8d2f0212d 100644
> >> --- a/Documentation/fpga/fpga-image-load.rst
> >> +++ b/Documentation/fpga/fpga-image-load.rst
> >> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
> >>  tools to manage image uploads to FPGA devices. Device drivers that
> >>  instantiate the FPGA Image Load class driver will interact with the
> >>  target device to transfer and authenticate the image data.
> >> +
> >> +User API
> >> +========
> >> +
> >> +open
> >> +----
> >> +
> >> +An FPGA Image Load device is opened exclusively to control an image load.
> >> +Image loads are processed by a kernel worker thread. A user may choose
> >> +close the device while the upload continues.
> > Why we allow the user to close the dev while the uploading is ongoing?
> > Seems it introduces more checking effort when another user open the dev
> > again and try another uploading.
> 
> A user could choose to write their own tools. How would we prevent
> them from closing the device while an update is in progress? Should
> we attempt to cancel the update if they close the device?

I think we could try to cancel the update when close(), if we cannot
cancel we block on close().

The driver should be responsible for the integrity of the update flow,
it is not prefered a user starts an update and leaves, but canceled by
another user.

> 
> Concurrent updates are already prevented by returning EBUSY for the
> FPGA_IMAGE_LOAD_WRITE IOCTL if an update is already in progress.
> 
> A single IOCTL is sufficient to do a full update. Maybe a user
> would want to have three small tools: update_start, update_status,
> update_cancel, each of which would open the device, do an IOCTL,
> and then close the device. This is analogous to the sysfs
> implementation (no open/close that bounds the entire sequence).
> 
> With the current design, we do an exclusive open. As long as the user
> keeps the device open, no other process can open the device to start
> a new update, cancel, or collect status.
> 
> Those are my thoughts. What do you think? Is it OK as is? Or should
> I make some changes here?
> 
> >
> >> +
> >> +ioctl
> >> +-----
> >> +
> >> +FPGA_IMAGE_LOAD_WRITE:
> >> +
> >> +Start an image load with the provided image buffer. This IOCTL returns
> >> +immediately after starting a kernel worker thread to process the image load
> >> +which could take as long a 40 minutes depending on the actual device being
> >> +updated. This is an exclusive operation; an attempt to start concurrent image
> >> +load for the same device will fail with EBUSY.
> >> diff --git a/MAINTAINERS b/MAINTAINERS
> >> index 4e7f48fa7e5c..637bc003ca81 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -7365,6 +7365,7 @@ S:	Maintained
> >>  F:	Documentation/fpga/fpga-image-load.rst
> >>  F:	drivers/fpga/fpga-image-load.c
> >>  F:	include/linux/fpga/fpga-image-load.h
> >> +F:	include/uapi/linux/fpga-image-load.h
> >>  
> >>  FPU EMULATOR
> >>  M:	Bill Metzenthen <billm@melbpc.org.au>
> >> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> >> index 7d75bbcff541..f5ccfa9dd977 100644
> >> --- a/drivers/fpga/fpga-image-load.c
> >> +++ b/drivers/fpga/fpga-image-load.c
> >> @@ -5,18 +5,181 @@
> >>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
> >>   */
> >>  
> >> +#include <linux/delay.h>
> >>  #include <linux/fpga/fpga-image-load.h>
> >> +#include <linux/fs.h>
> >> +#include <linux/kernel.h>
> >>  #include <linux/module.h>
> >>  #include <linux/slab.h>
> >> +#include <linux/uaccess.h>
> >>  #include <linux/vmalloc.h>
> >>  
> >>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
> >>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
> >>  
> >>  static struct class *fpga_image_load_class;
> >> +static dev_t fpga_image_devt;
> >>  
> >>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
> >>  
> >> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
> >> +				 enum fpga_image_err err_code)
> >> +{
> >> +	imgld->err_code = err_code;
> >> +	imgld->lops->cancel(imgld);
> >> +}
> >> +
> >> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
> >> +{
> >> +	mutex_lock(&imgld->lock);
> >> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> >> +	complete_all(&imgld->update_done);
> >> +	mutex_unlock(&imgld->lock);
> >> +}
> >> +
> >> +static void fpga_image_do_load(struct work_struct *work)
> >> +{
> >> +	struct fpga_image_load *imgld;
> >> +	enum fpga_image_err ret;
> >> +	u32 size, offset = 0;
> >> +
> >> +	imgld = container_of(work, struct fpga_image_load, work);
> >> +	size = imgld->remaining_size;
> >> +
> >> +	get_device(&imgld->dev);
> >> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
> >> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
> >> +		goto idle_exit;
> >> +	}
> >> +
> >> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
> >> +	ret = imgld->lops->prepare(imgld);
> >> +	if (ret != FPGA_IMAGE_ERR_NONE) {
> >> +		fpga_image_dev_error(imgld, ret);
> >> +		goto modput_exit;
> >> +	}
> >> +
> >> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
> >> +	while (imgld->remaining_size) {
> >> +		ret = imgld->lops->write_blk(imgld, offset);
> >> +		if (ret != FPGA_IMAGE_ERR_NONE) {
> >> +			fpga_image_dev_error(imgld, ret);
> >> +			goto done;
> >> +		}
> >> +
> >> +		offset = size - imgld->remaining_size;
> > The low level driver is required to update the "remaining_size" in
> > write_blk ops?
> >
> > The API seems ambiguous. The framework asks for writing a block of data,
> > but no block size is specified.
> 
> This change was made two or three iterations ago at Richard Gong's
> suggestion. He asserted that the lower level driver should be in
> control of the block size (based on write speeds). What do you

I think it is reasonable.

> think? Should the class driver impose a fixed size? Or allow the

But the definition of the API is hard to comprehend, either we input the
offset & the blk size, or we record them all in imgld structure, is it
better.

If the framework provides the blk size, the low level driver could
return the written size so it could still control its own batch.

> lower-level driver to determine the size? Is it OK to update
> remaining_size in the lower-level driver? Or should there be
> another call-back to request the size?
>  
> >
> >> +	}
> >> +
> >> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
> >> +	ret = imgld->lops->poll_complete(imgld);
> >> +	if (ret != FPGA_IMAGE_ERR_NONE)
> >> +		fpga_image_dev_error(imgld, ret);
> >> +
> >> +done:
> >> +	if (imgld->lops->cleanup)
> >> +		imgld->lops->cleanup(imgld);
> >> +
> >> +modput_exit:
> >> +	module_put(imgld->dev.parent->driver->owner);
> >> +
> >> +idle_exit:
> >> +	/*
> >> +	 * Note: imgld->remaining_size is left unmodified here to provide
> >> +	 * additional information on errors. It will be reinitialized when
> >> +	 * the next image load begins.
> >> +	 */
> >> +	vfree(imgld->data);
> >> +	imgld->data = NULL;
> >> +	put_device(&imgld->dev);
> >> +	fpga_image_prog_complete(imgld);
> >> +}
> >> +
> >> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
> >> +				       unsigned long arg)
> >> +{
> >> +	struct fpga_image_write wb;
> >> +	unsigned long minsz;
> >> +	u8 *buf;
> >> +
> >> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
> >> +		return -EBUSY;
> >> +
> >> +	minsz = offsetofend(struct fpga_image_write, buf);
> >> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
> >> +		return -EFAULT;
> >> +
> >> +	if (wb.flags)
> >> +		return -EINVAL;
> >> +
> >> +	/* Enforce 32-bit alignment on the write data */
> >> +	if (wb.size & 0x3)
> >> +		return -EINVAL;
> >> +
> >> +	buf = vzalloc(wb.size);
> >> +	if (!buf)
> >> +		return -ENOMEM;
> >> +
> >> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
> >> +		vfree(buf);
> >> +		return -EFAULT;
> >> +	}
> >> +
> >> +	imgld->data = buf;
> >> +	imgld->remaining_size = wb.size;
> >> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> >> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
> >> +	reinit_completion(&imgld->update_done);
> >> +	schedule_work(&imgld->work);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> >> +				  unsigned long arg)
> >> +{
> >> +	struct fpga_image_load *imgld = filp->private_data;
> >> +	int ret = -ENOTTY;
> >> +
> >> +	switch (cmd) {
> >> +	case FPGA_IMAGE_LOAD_WRITE:
> >> +		mutex_lock(&imgld->lock);
> >> +		ret = fpga_image_load_ioctl_write(imgld, arg);
> >> +		mutex_unlock(&imgld->lock);
> >> +		break;
> >> +	}
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
> >> +{
> >> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
> >> +						     struct fpga_image_load, cdev);
> >> +
> >> +	if (test_and_set_bit(0, &imgld->opened))
> > Some more flags to add for "opened" field? But the field name indicates
> > it is a single flag.
> Can you explain your comment further? I'm not understanding. What
> "more flags" are you referring to?
> 

The test_and_set_bit() makes me feel there are more bits to be
controlled in "opened" for other purposes. The name "opened" indicates
this is a single state, maybe you don't have to use bitops for it,
atomic ops for int maybe better.

> This test_and_set_bit() operation and the "opened" structure member
> were borrowed from the production driver implementation. opened is a
> single value that is expected to be either 1 or 0.
> 
> >
> >> +		return -EBUSY;
> >> +
> >> +	filp->private_data = imgld;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
> >> +{
> >> +	struct fpga_image_load *imgld = filp->private_data;
> >> +
> >> +	clear_bit(0, &imgld->opened);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const struct file_operations fpga_image_load_fops = {
> >> +	.owner = THIS_MODULE,
> >> +	.open = fpga_image_load_open,
> >> +	.release = fpga_image_load_release,
> >> +	.unlocked_ioctl = fpga_image_load_ioctl,
> >> +};
> >> +
> >>  /**
> >>   * fpga_image_load_register - create and register an FPGA Image Load Device
> >>   *
> >> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
> >>  			 const struct fpga_image_load_ops *lops, void *priv)
> >>  {
> >>  	struct fpga_image_load *imgld;
> >> -	int id, ret;
> >> +	int ret;
> >> +
> >> +	if (!lops || !lops->cancel || !lops->prepare ||
> >> +	    !lops->write_blk || !lops->poll_complete) {
> >> +		dev_err(parent, "Attempt to register without all required ops\n");
> >> +		return ERR_PTR(-ENOMEM);
> >> +	}
> >>  
> >>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
> >>  	if (!imgld)
> >> -		return NULL;
> >> +		return ERR_PTR(-ENOMEM);
> > This is the fix for Patch #1? If yes please merge it to Patch #1.
> Good catch. Yes, I'll move it to patch #1.
> >
> >>  
> >>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
> >>  		       GFP_KERNEL);
> >> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
> >>  
> >>  	imgld->priv = priv;
> >>  	imgld->lops = lops;
> >> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> >> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> >> +	init_completion(&imgld->update_done);
> >> +	INIT_WORK(&imgld->work, fpga_image_do_load);
> >>  
> >>  	imgld->dev.class = fpga_image_load_class;
> >>  	imgld->dev.parent = parent;
> >> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
> >>  
> >> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
> >> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
> > Another fix? Please merge it to Patch #1.
> Yes.
> 
> >
> >>  	if (ret) {
> >> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
> >> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
> >> +			imgld->dev.id);
> > Ditto
> Yes.
> >
> >>  		goto error_device;
> >>  	}
> >>  
> >> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
> >>  		return ERR_PTR(ret);
> >>  	}
> >>  
> >> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
> >> +	imgld->cdev.owner = parent->driver->owner;
> >> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
> >> +
> >> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
> >> +	if (ret) {
> >> +		put_device(&imgld->dev);
> >> +		return ERR_PTR(ret);
> >> +	}
> >> +
> >>  	return imgld;
> >>  
> >>  error_device:
> >> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
> >>   *
> >>   * This function is intended for use in an FPGA Image Load driver's
> >>   * remove() function.
> >> + *
> >> + * For some devices, once authentication of the uploaded image has begun,
> >> + * the hardware cannot be signaled to stop, and the driver will not exit
> >> + * until the hardware signals completion.  This could be 30+ minutes of
> >> + * waiting. The driver_unload flag enables a force-unload of the driver
> >> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the
> > How does the driver_unload enables the force unload of the parent
> > driver? I didn't find the code.
> 
> The driver_unload field is tested in the lower-level (parent) driver in
> m10bmc_sec_poll_complete(), allowing the kernel worker thread to exit
> even if the firmware update is still in progress.

I didn't see the signaling implementation here. Maybe you need to move the
comments somewhere else, or in another patch.

Thanks,
Yilun

> 
> >
> >> + * hardware update is incomplete. The driver_unload flag also prevents
> >> + * new updates from starting once the unregister process has begun.
> >>   */
> >>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
> >>  {
> >> +	mutex_lock(&imgld->lock);
> >> +	imgld->driver_unload = true;
> >> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
> >> +		mutex_unlock(&imgld->lock);
> >> +		goto unregister;
> >> +	}
> >> +
> >> +	mutex_unlock(&imgld->lock);
> >> +	wait_for_completion(&imgld->update_done);
> >> +
> >> +unregister:
> >> +	cdev_del(&imgld->cdev);
> >>  	device_unregister(&imgld->dev);
> >>  }
> >>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
> >> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
> >>  
> >>  static int __init fpga_image_load_class_init(void)
> >>  {
> >> +	int ret;
> >>  	pr_info("FPGA Image Load Driver\n");
> >>  
> >>  	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
> >>  	if (IS_ERR(fpga_image_load_class))
> >>  		return PTR_ERR(fpga_image_load_class);
> >>  
> >> +	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
> >> +				  "fpga_image_load");
> >> +	if (ret)
> >> +		goto exit_destroy_class;
> >> +
> >>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
> >>  
> >>  	return 0;
> >> +
> >> +exit_destroy_class:
> >> +	class_destroy(fpga_image_load_class);
> >> +	return ret;
> >>  }
> >>  
> >>  static void __exit fpga_image_load_class_exit(void)
> >>  {
> >> +	unregister_chrdev_region(fpga_image_devt, MINORMASK);
> >>  	class_destroy(fpga_image_load_class);
> >>  	WARN_ON(!xa_empty(&fpga_image_load_xa));
> >>  }
> >> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> >> index a9cef9e1056b..b3d790e5d943 100644
> >> --- a/include/linux/fpga/fpga-image-load.h
> >> +++ b/include/linux/fpga/fpga-image-load.h
> >> @@ -7,22 +7,51 @@
> >>  #ifndef _LINUX_FPGA_IMAGE_LOAD_H
> >>  #define _LINUX_FPGA_IMAGE_LOAD_H
> >>  
> >> +#include <linux/cdev.h>
> >> +#include <linux/completion.h>
> >>  #include <linux/device.h>
> >>  #include <linux/mutex.h>
> >>  #include <linux/types.h>
> >> +#include <uapi/linux/fpga-image-load.h>
> >>  
> >>  struct fpga_image_load;
> >>  
> >>  /**
> >>   * struct fpga_image_load_ops - device specific operations
> >> + * @prepare:		    Required: Prepare secure update
> >> + * @write_blk:		    Required: Write a block of data
> >> + * @poll_complete:	    Required: Check for the completion of the
> >> + *			    HW authentication/programming process. This
> >> + *			    function should check for imgld->driver_unload
> >> + *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
> >> + * @cancel:		    Required: Signal HW to cancel update
> >> + * @cleanup:		    Optional: Complements the prepare()
> >> + *			    function and is called at the completion
> >> + *			    of the update, whether success or failure,
> >> + *			    if the prepare function succeeded.
> >>   */
> >>  struct fpga_image_load_ops {
> >> +	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
> >> +	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
> >> +	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
> >> +	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
> >> +	void (*cleanup)(struct fpga_image_load *imgld);
> >>  };
> >>  
> >>  struct fpga_image_load {
> >>  	struct device dev;
> >> +	struct cdev cdev;
> >>  	const struct fpga_image_load_ops *lops;
> >>  	struct mutex lock;		/* protect data structure contents */
> >> +	unsigned long opened;
> >> +	struct work_struct work;
> >> +	struct completion update_done;
> >> +	const u8 *data;				/* pointer to update data */
> >> +	u32 remaining_size;			/* size remaining to transfer */
> >> +	enum fpga_image_prog progress;
> >> +	enum fpga_image_prog err_progress;	/* progress at time of failure */
> > This field is not used in this patch? So could you introduce it later?
> Yes - I'll move it. Thanks.
> >
> >> +	enum fpga_image_err err_code;		/* image load error code */
> >> +	bool driver_unload;
> >>  	void *priv;
> >>  };
> >>  
> >> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> >> new file mode 100644
> >> index 000000000000..4146a0a9e408
> >> --- /dev/null
> >> +++ b/include/uapi/linux/fpga-image-load.h
> >> @@ -0,0 +1,58 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> >> +/*
> >> + * Header File for FPGA Image Load User API
> >> + *
> >> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
> >> + *
> >> + */
> >> +
> >> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> >> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> >> +
> >> +#include <linux/types.h>
> >> +#include <linux/ioctl.h>
> >> +
> >> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9
> >> +
> >> +/* Image load progress codes */
> >> +enum fpga_image_prog {
> >> +	FPGA_IMAGE_PROG_IDLE,
> >> +	FPGA_IMAGE_PROG_STARTING,
> >> +	FPGA_IMAGE_PROG_PREPARING,
> >> +	FPGA_IMAGE_PROG_WRITING,
> >> +	FPGA_IMAGE_PROG_PROGRAMMING,
> >> +	FPGA_IMAGE_PROG_MAX
> >> +};
> >> +
> >> +/* Image error progress codes */
> >> +enum fpga_image_err {
> >> +	FPGA_IMAGE_ERR_NONE,
> >> +	FPGA_IMAGE_ERR_HW_ERROR,
> >> +	FPGA_IMAGE_ERR_TIMEOUT,
> >> +	FPGA_IMAGE_ERR_CANCELED,
> >> +	FPGA_IMAGE_ERR_BUSY,
> >> +	FPGA_IMAGE_ERR_INVALID_SIZE,
> >> +	FPGA_IMAGE_ERR_RW_ERROR,
> >> +	FPGA_IMAGE_ERR_WEAROUT,
> >> +	FPGA_IMAGE_ERR_MAX
> >> +};
> >> +
> >> +#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> > Put the cmd word definition under the comments and parameter definition.
> OK
> >
> >> +
> >> +/**
> >> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> >> + *				struct fpga_image_write)
> >> + *
> >> + * Upload a data buffer to the target device. The user must provide the
> >> + * data buffer, size, and an eventfd file descriptor.
> > I didn't find the eventfd.
> It is added in a later patch. I'll change the comment accordingly.
> 
> Thanks,
> - Russ
> >
> > Thanks,
> > Yilun
> >
> >> + *
> >> + * Return: 0 on success, -errno on failure.
> >> + */
> >> +struct fpga_image_write {
> >> +	/* Input */
> >> +	__u32 flags;		/* Zero for now */
> >> +	__u32 size;		/* Data size (in bytes) to be written */
> >> +	__u64 buf;		/* User space address of source data */
> >> +};
> >> +
> >> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
> >> -- 
> >> 2.25.1

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

* Re: [PATCH v15 4/6] fpga: image-load: add status ioctl
  2021-09-11 18:12       ` Tom Rix
@ 2021-09-13  8:24         ` Xu Yilun
  0 siblings, 0 replies; 28+ messages in thread
From: Xu Yilun @ 2021-09-13  8:24 UTC (permalink / raw)
  To: Tom Rix
  Cc: Russ Weight, mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu,
	matthew.gerlach

On Sat, Sep 11, 2021 at 11:12:50AM -0700, Tom Rix wrote:
> 
> On 9/10/21 4:23 PM, Russ Weight wrote:
> > 
> > On 9/10/21 1:50 AM, Xu Yilun wrote:
> > > On Wed, Sep 08, 2021 at 07:18:44PM -0700, Russ Weight wrote:
> > > > Extend the FPGA Image Load class driver to include an
> > > > FPGA_IMAGE_LOAD_STATUS IOCTL that can be used to monitor the progress
> > > > of an ongoing image load.  The status returned includes how much data
> > > > remains to be transferred, the progress of the image load, and error
> > > > information in the case of a failure.
> > > > 
> > > > Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> > > > ---
> > > > V15:
> > > >   - This patch is new to the patchset and provides an FPGA_IMAGE_LOAD_STATUS
> > > >     IOCTL to return the current values for: remaining_size, progress,
> > > >     err_progress, and err_code.
> > > >   - This patch has elements of the following three patches from the previous
> > > >     patch-set:
> > > >       [PATCH v14 3/6] fpga: sec-mgr: expose sec-mgr update status
> > > >       [PATCH v14 4/6] fpga: sec-mgr: expose sec-mgr update errors
> > > >       [PATCH v14 5/6] fpga: sec-mgr: expose sec-mgr update size
> > > >   - Changed file, symbol, and config names to reflect the new driver name
> > > >   - There are some minor changes to locking to enable this ioctl to return
> > > >     coherent data.
> > > > ---
> > > >   Documentation/fpga/fpga-image-load.rst |  6 +++
> > > >   drivers/fpga/fpga-image-load.c         | 53 ++++++++++++++++++++++----
> > > >   include/uapi/linux/fpga-image-load.h   | 18 +++++++++
> > > >   3 files changed, 70 insertions(+), 7 deletions(-)
> > > > 
> > > > diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> > > > index 739d735592a5..3d5eb51223e3 100644
> > > > --- a/Documentation/fpga/fpga-image-load.rst
> > > > +++ b/Documentation/fpga/fpga-image-load.rst
> > > > @@ -31,3 +31,9 @@ updated. This is an exclusive operation; an attempt to start concurrent image
> > > >   load for the same device will fail with EBUSY. An eventfd file descriptor
> > > >   parameter is provided to this IOCTL, and it will be signalled at the
> > > >   completion of the image load.
> > > > +
> > > > +FPGA_IMAGE_LOAD_STATUS:
> > > > +
> > > > +Collect status for an on-going image upload. The status returned includes
> > > > +how much data remains to be transferred, the progress of the image load,
> > > > +and error information in the case of a failure.
> > > > diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> > > > index b784456765b0..99a47b21c995 100644
> > > > --- a/drivers/fpga/fpga-image-load.c
> > > > +++ b/drivers/fpga/fpga-image-load.c
> > > > @@ -22,10 +22,27 @@ static dev_t fpga_image_devt;
> > > >   #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
> > > > -static void fpga_image_dev_error(struct fpga_image_load *imgld,
> > > > +static void fpga_image_update_progress(struct fpga_image_load *imgld,
> > > > +				       enum fpga_image_prog new_progress)
> > > > +{
> > > > +	mutex_lock(&imgld->lock);
> > > > +	imgld->progress = new_progress;
> > > > +	mutex_unlock(&imgld->lock);
> > > > +}
> > > > +
> > > > +static void fpga_image_set_error(struct fpga_image_load *imgld,
> > > >   				 enum fpga_image_err err_code)
> > > >   {
> > > > +	mutex_lock(&imgld->lock);
> > > > +	imgld->err_progress = imgld->progress;
> > > >   	imgld->err_code = err_code;
> > > > +	mutex_unlock(&imgld->lock);
> > > > +}
> > > > +
> > > > +static void fpga_image_dev_error(struct fpga_image_load *imgld,
> > > > +				 enum fpga_image_err err_code)
> > > > +{
> > > > +	fpga_image_set_error(imgld, err_code);
> > > >   	imgld->lops->cancel(imgld);
> > > >   }
> > > > @@ -49,18 +66,18 @@ static void fpga_image_do_load(struct work_struct *work)
> > > >   	get_device(&imgld->dev);
> > > >   	if (!try_module_get(imgld->dev.parent->driver->owner)) {
> > > > -		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
> > > > +		fpga_image_set_error(imgld, FPGA_IMAGE_ERR_BUSY);
> > > >   		goto idle_exit;
> > > >   	}
> > > > -	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
> > > > +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PREPARING);
> > > >   	ret = imgld->lops->prepare(imgld);
> > > >   	if (ret != FPGA_IMAGE_ERR_NONE) {
> > > >   		fpga_image_dev_error(imgld, ret);
> > > >   		goto modput_exit;
> > > >   	}
> > > > -	imgld->progress = FPGA_IMAGE_PROG_WRITING;
> > > > +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
> > > >   	while (imgld->remaining_size) {
> > > >   		ret = imgld->lops->write_blk(imgld, offset);
> > > >   		if (ret != FPGA_IMAGE_ERR_NONE) {
> > > > @@ -71,7 +88,7 @@ static void fpga_image_do_load(struct work_struct *work)
> > > >   		offset = size - imgld->remaining_size;
> > > >   	}
> > > > -	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
> > > > +	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
> > > >   	ret = imgld->lops->poll_complete(imgld);
> > > >   	if (ret != FPGA_IMAGE_ERR_NONE)
> > > >   		fpga_image_dev_error(imgld, ret);
> > > > @@ -151,20 +168,42 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
> > > >   	return ret;
> > > >   }
> > > > +static int fpga_image_load_ioctl_status(struct fpga_image_load *imgld,
> > > > +					unsigned long arg)
> > > > +{
> > > > +	struct fpga_image_status status;
> > > > +
> > > > +	memset(&status, 0, sizeof(status));
> > > > +	status.progress = imgld->progress;
> > > > +	status.remaining_size = imgld->remaining_size;
> > > > +	status.err_progress = imgld->err_progress;
> > > > +	status.err_code = imgld->err_code;
> > > > +
> > > > +	if (copy_to_user((void __user *)arg, &status, sizeof(status)))
> > > > +		return -EFAULT;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > >   static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> > > >   				  unsigned long arg)
> > > >   {
> > > >   	struct fpga_image_load *imgld = filp->private_data;
> > > >   	int ret = -ENOTTY;
> > > > +	mutex_lock(&imgld->lock);
> > > > +
> > > >   	switch (cmd) {
> > > >   	case FPGA_IMAGE_LOAD_WRITE:
> > > > -		mutex_lock(&imgld->lock);
> > > >   		ret = fpga_image_load_ioctl_write(imgld, arg);
> > > > -		mutex_unlock(&imgld->lock);
> > > > +		break;
> > > > +	case FPGA_IMAGE_LOAD_STATUS:
> > > > +		ret = fpga_image_load_ioctl_status(imgld, arg);
> > > >   		break;
> > > >   	}
> > > > +	mutex_unlock(&imgld->lock);
> > > > +
> > > >   	return ret;
> > > >   }
> > > > diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> > > > index a60da115adf5..6a995bcc0fb7 100644
> > > > --- a/include/uapi/linux/fpga-image-load.h
> > > > +++ b/include/uapi/linux/fpga-image-load.h
> > > > @@ -38,6 +38,7 @@ enum fpga_image_err {
> > > >   };
> > > >   #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> > > > +#define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
> > > >   /**
> > > >    * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> > > > @@ -56,4 +57,21 @@ struct fpga_image_write {
> > > >   	__u64 buf;		/* User space address of source data */
> > > >   };
> > > > +/**
> > > > + * FPGA_IMAGE_LOAD_STATUS - _IOR(FPGA_IMAGE_LOAD_MAGIC, 1,
> > > > + *				 struct fpga_image_status)
> > > > + *
> > > > + * Request status information for an ongoing update.
> > > > + * data buffer, size, and an eventfd file descriptor.
> > > This line is a typo.
> > Thanks - I'll fix it.
> > > > + *
> > > > + * Return: 0 on success, -errno on failure.
> > > > + */
> > > > +struct fpga_image_status {
> > > > +	/* Output */
> > > > +	__u32 remaining_size;			/* size remaining to transfer */
> > > > +	enum fpga_image_prog progress;		/* current progress of image load */
> > > > +	enum fpga_image_prog err_progress;	/* progress at time of error */
> > > > +	enum fpga_image_err err_code;		/* error code */
> > > Should we use __u32 for these enumeration types?
> > We could, but I did see a lot of uapi header file examples that use
> > enum for IOCTL structures. Is there a reason to prefer __u32? If I change
> > these to __u32, then I suppose I need to change from enum declarations to
> > #define macros?
> 
> The __u32 makes sure the struct is a constant size.
> 
> yes, change the enums to #defines.
> 
> The layout of a new api should be consistent with similar older api.
> 
> review uapi/linux/fpga-dfl.h , the dfl_fpga_port_region_info struct.

Thanks for clarification, yes this is what I expect.

Thanks,
Yilun

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

* Re: [PATCH v15 5/6] fpga: image-load: create status sysfs node
  2021-09-11 17:58       ` Tom Rix
@ 2021-09-13  8:27         ` Xu Yilun
  0 siblings, 0 replies; 28+ messages in thread
From: Xu Yilun @ 2021-09-13  8:27 UTC (permalink / raw)
  To: Tom Rix
  Cc: Russ Weight, mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu,
	matthew.gerlach

On Sat, Sep 11, 2021 at 10:58:16AM -0700, Tom Rix wrote:
> 
> On 9/10/21 4:30 PM, Russ Weight wrote:
> > 
> > On 9/10/21 1:52 AM, Xu Yilun wrote:
> > > On Wed, Sep 08, 2021 at 07:18:45PM -0700, Russ Weight wrote:
> > > > Extend the FPGA Image Load class driver to include a status sysfs node that
> > > > can be viewed to determine from the command line if an image load is in
> > > > progress. Status will be one of: idle, starting, preparing, writing, or
> > > > programming.
> > > The FPGA_IMAGE_LOAD_STATUS ioctl already provides the progress info.
> > > Why we need 2 user interfaces for the same information?
> > Updates on Vista Creek can take up to 40 minutes. I thought it might
> > be helpful to have a simple way, from the command line, to verify
> > whether or not there is an image upload in progress. Do you agree? Or
> > do you think this is unnecessary? Should I remove it? Or save it for
> > a later patch (after the main patches have been accepted)?
> 
> I agree, there should not be two methods to doing the same thing.
> 
> I prefer the ioctl since it handles other cmd's as well.
> 
> The user has to use the ioctl to start the write so it would for more
> natural to use the ioctl over sysfs to the check the status
> 
> Remove it.
> 
> Make a strong case for it's need when/if you want to add it back later.

Yes, remove it. Not a strong use case for this interface now.

Thanks,
Yilun

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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-13  6:48       ` Xu Yilun
@ 2021-09-13  9:36         ` Xu Yilun
  2021-09-21 19:08           ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-13  9:36 UTC (permalink / raw)
  To: Russ Weight
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach

On Mon, Sep 13, 2021 at 02:48:45PM +0800, Xu Yilun wrote:
> On Fri, Sep 10, 2021 at 04:18:26PM -0700, Russ Weight wrote:
> > 
> > 
> > On 9/10/21 1:22 AM, Xu Yilun wrote:
> > > On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote:
> > >> Extend the FPGA Image Load class driver to include IOCTL support
> > >> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
> > >> The IOCTL will return immediately, and the update will begin in the
> > >> context of a kernel worker thread.
> > >>
> > >> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> > >> ---
> > >> v15:
> > >>  - Compare to previous patch:
> > >>      [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
> > >>  - Changed file, symbol, and config names to reflect the new driver name
> > >>  - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
> > >>    IOCTL for writing the image data to the FPGA card. The driver no longer
> > >>    uses the request_firmware framework.
> > >>  - Fixed some error return values in fpga_image_load_register()
> > >>  - Removed signed-off/reviewed-by tags
> > >> v14:
> > >>  - Added MAINTAINERS reference for
> > >>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
> > >>  - Updated ABI documentation date and kernel version
> > >>  - Updated copyright to 2021
> > >> v13:
> > >>   - Change "if (count == 0 || " to "if (!count || "
> > >>   - Improve error message: "Attempt to register without all required ops\n"
> > >> v12:
> > >>   - Updated Date and KernelVersion fields in ABI documentation
> > >>   - Removed size parameter from write_blk() op - it is now up to
> > >>     the lower-level driver to determine the appropriate size and
> > >>     to update smgr->remaining_size accordingly.
> > >> v11:
> > >>   - Fixed a spelling error in a comment
> > >>   - Initialize smgr->err_code and smgr->progress explicitly in
> > >>     fpga_sec_mgr_create() instead of accepting the default 0 value.
> > >> v10:
> > >>   - Rebased to 5.12-rc2 next
> > >>   - Updated Date and KernelVersion in ABI documentation
> > >> v9:
> > >>   - Updated Date and KernelVersion in ABI documentation
> > >> v8:
> > >>   - No change
> > >> v7:
> > >>   - Changed Date in documentation file to December 2020
> > >>   - Changed filename_store() to use kmemdup_nul() instead of
> > >>     kstrndup() and changed the count to not assume a line-return.
> > >> v6:
> > >>   - Changed "security update" to "secure update" in commit message
> > >> v5:
> > >>   - When checking the return values for functions of type enum
> > >>     fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
> > >> v4:
> > >>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
> > >>     and removed unnecessary references to "Intel".
> > >>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> > >> v3:
> > >>   - Removed unnecessary "goto done"
> > >>   - Added a comment to explain imgr->driver_unload in
> > >>     ifpga_sec_mgr_unregister()
> > >> v2:
> > >>   - Bumped documentation date and version
> > >>   - Removed explicit value assignments in enums
> > >>   - Other minor code cleanup per review comments
> > >> ---
> > >>  Documentation/fpga/fpga-image-load.rst |  21 +++
> > >>  MAINTAINERS                            |   1 +
> > >>  drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
> > >>  include/linux/fpga/fpga-image-load.h   |  29 ++++
> > >>  include/uapi/linux/fpga-image-load.h   |  58 +++++++
> > >>  5 files changed, 329 insertions(+), 4 deletions(-)
> > >>  create mode 100644 include/uapi/linux/fpga-image-load.h
> > >>
> > >> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> > >> index a6e53ac66026..2ca8d2f0212d 100644
> > >> --- a/Documentation/fpga/fpga-image-load.rst
> > >> +++ b/Documentation/fpga/fpga-image-load.rst
> > >> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
> > >>  tools to manage image uploads to FPGA devices. Device drivers that
> > >>  instantiate the FPGA Image Load class driver will interact with the
> > >>  target device to transfer and authenticate the image data.
> > >> +
> > >> +User API
> > >> +========
> > >> +
> > >> +open
> > >> +----
> > >> +
> > >> +An FPGA Image Load device is opened exclusively to control an image load.
> > >> +Image loads are processed by a kernel worker thread. A user may choose
> > >> +close the device while the upload continues.
> > > Why we allow the user to close the dev while the uploading is ongoing?
> > > Seems it introduces more checking effort when another user open the dev
> > > again and try another uploading.
> > 
> > A user could choose to write their own tools. How would we prevent
> > them from closing the device while an update is in progress? Should
> > we attempt to cancel the update if they close the device?
> 
> I think we could try to cancel the update when close(), if we cannot
> cancel we block on close().
> 
> The driver should be responsible for the integrity of the update flow,
> it is not prefered a user starts an update and leaves, but canceled by
> another user.
> 
> > 
> > Concurrent updates are already prevented by returning EBUSY for the
> > FPGA_IMAGE_LOAD_WRITE IOCTL if an update is already in progress.
> > 
> > A single IOCTL is sufficient to do a full update. Maybe a user
> > would want to have three small tools: update_start, update_status,
> > update_cancel, each of which would open the device, do an IOCTL,
> > and then close the device. This is analogous to the sysfs
> > implementation (no open/close that bounds the entire sequence).
> > 
> > With the current design, we do an exclusive open. As long as the user
> > keeps the device open, no other process can open the device to start
> > a new update, cancel, or collect status.
> > 
> > Those are my thoughts. What do you think? Is it OK as is? Or should
> > I make some changes here?
> > 
> > >
> > >> +
> > >> +ioctl
> > >> +-----
> > >> +
> > >> +FPGA_IMAGE_LOAD_WRITE:
> > >> +
> > >> +Start an image load with the provided image buffer. This IOCTL returns
> > >> +immediately after starting a kernel worker thread to process the image load
> > >> +which could take as long a 40 minutes depending on the actual device being
> > >> +updated. This is an exclusive operation; an attempt to start concurrent image
> > >> +load for the same device will fail with EBUSY.
> > >> diff --git a/MAINTAINERS b/MAINTAINERS
> > >> index 4e7f48fa7e5c..637bc003ca81 100644
> > >> --- a/MAINTAINERS
> > >> +++ b/MAINTAINERS
> > >> @@ -7365,6 +7365,7 @@ S:	Maintained
> > >>  F:	Documentation/fpga/fpga-image-load.rst
> > >>  F:	drivers/fpga/fpga-image-load.c
> > >>  F:	include/linux/fpga/fpga-image-load.h
> > >> +F:	include/uapi/linux/fpga-image-load.h
> > >>  
> > >>  FPU EMULATOR
> > >>  M:	Bill Metzenthen <billm@melbpc.org.au>
> > >> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> > >> index 7d75bbcff541..f5ccfa9dd977 100644
> > >> --- a/drivers/fpga/fpga-image-load.c
> > >> +++ b/drivers/fpga/fpga-image-load.c
> > >> @@ -5,18 +5,181 @@
> > >>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
> > >>   */
> > >>  
> > >> +#include <linux/delay.h>
> > >>  #include <linux/fpga/fpga-image-load.h>
> > >> +#include <linux/fs.h>
> > >> +#include <linux/kernel.h>
> > >>  #include <linux/module.h>
> > >>  #include <linux/slab.h>
> > >> +#include <linux/uaccess.h>
> > >>  #include <linux/vmalloc.h>
> > >>  
> > >>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
> > >>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
> > >>  
> > >>  static struct class *fpga_image_load_class;
> > >> +static dev_t fpga_image_devt;
> > >>  
> > >>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
> > >>  
> > >> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
> > >> +				 enum fpga_image_err err_code)
> > >> +{
> > >> +	imgld->err_code = err_code;
> > >> +	imgld->lops->cancel(imgld);
> > >> +}
> > >> +
> > >> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
> > >> +{
> > >> +	mutex_lock(&imgld->lock);
> > >> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> > >> +	complete_all(&imgld->update_done);
> > >> +	mutex_unlock(&imgld->lock);
> > >> +}
> > >> +
> > >> +static void fpga_image_do_load(struct work_struct *work)
> > >> +{
> > >> +	struct fpga_image_load *imgld;
> > >> +	enum fpga_image_err ret;
> > >> +	u32 size, offset = 0;
> > >> +
> > >> +	imgld = container_of(work, struct fpga_image_load, work);
> > >> +	size = imgld->remaining_size;
> > >> +
> > >> +	get_device(&imgld->dev);
> > >> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
> > >> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
> > >> +		goto idle_exit;
> > >> +	}
> > >> +
> > >> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
> > >> +	ret = imgld->lops->prepare(imgld);
> > >> +	if (ret != FPGA_IMAGE_ERR_NONE) {
> > >> +		fpga_image_dev_error(imgld, ret);
> > >> +		goto modput_exit;
> > >> +	}
> > >> +
> > >> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
> > >> +	while (imgld->remaining_size) {
> > >> +		ret = imgld->lops->write_blk(imgld, offset);
> > >> +		if (ret != FPGA_IMAGE_ERR_NONE) {
> > >> +			fpga_image_dev_error(imgld, ret);
> > >> +			goto done;
> > >> +		}
> > >> +
> > >> +		offset = size - imgld->remaining_size;
> > > The low level driver is required to update the "remaining_size" in
> > > write_blk ops?
> > >
> > > The API seems ambiguous. The framework asks for writing a block of data,
> > > but no block size is specified.
> > 
> > This change was made two or three iterations ago at Richard Gong's
> > suggestion. He asserted that the lower level driver should be in
> > control of the block size (based on write speeds). What do you
> 
> I think it is reasonable.
> 
> > think? Should the class driver impose a fixed size? Or allow the
> 
> But the definition of the API is hard to comprehend, either we input the
> offset & the blk size, or we record them all in imgld structure, is it
> better.
> 
> If the framework provides the blk size, the low level driver could
> return the written size so it could still control its own batch.
> 
> > lower-level driver to determine the size? Is it OK to update
> > remaining_size in the lower-level driver? Or should there be
> > another call-back to request the size?
> >  
> > >
> > >> +	}
> > >> +
> > >> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
> > >> +	ret = imgld->lops->poll_complete(imgld);
> > >> +	if (ret != FPGA_IMAGE_ERR_NONE)
> > >> +		fpga_image_dev_error(imgld, ret);
> > >> +
> > >> +done:
> > >> +	if (imgld->lops->cleanup)
> > >> +		imgld->lops->cleanup(imgld);
> > >> +
> > >> +modput_exit:
> > >> +	module_put(imgld->dev.parent->driver->owner);
> > >> +
> > >> +idle_exit:
> > >> +	/*
> > >> +	 * Note: imgld->remaining_size is left unmodified here to provide
> > >> +	 * additional information on errors. It will be reinitialized when
> > >> +	 * the next image load begins.
> > >> +	 */
> > >> +	vfree(imgld->data);
> > >> +	imgld->data = NULL;
> > >> +	put_device(&imgld->dev);
> > >> +	fpga_image_prog_complete(imgld);
> > >> +}
> > >> +
> > >> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
> > >> +				       unsigned long arg)
> > >> +{
> > >> +	struct fpga_image_write wb;
> > >> +	unsigned long minsz;
> > >> +	u8 *buf;
> > >> +
> > >> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
> > >> +		return -EBUSY;
> > >> +
> > >> +	minsz = offsetofend(struct fpga_image_write, buf);
> > >> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
> > >> +		return -EFAULT;
> > >> +
> > >> +	if (wb.flags)
> > >> +		return -EINVAL;
> > >> +
> > >> +	/* Enforce 32-bit alignment on the write data */
> > >> +	if (wb.size & 0x3)
> > >> +		return -EINVAL;
> > >> +
> > >> +	buf = vzalloc(wb.size);
> > >> +	if (!buf)
> > >> +		return -ENOMEM;
> > >> +
> > >> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
> > >> +		vfree(buf);
> > >> +		return -EFAULT;
> > >> +	}
> > >> +
> > >> +	imgld->data = buf;
> > >> +	imgld->remaining_size = wb.size;
> > >> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> > >> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
> > >> +	reinit_completion(&imgld->update_done);
> > >> +	schedule_work(&imgld->work);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> > >> +				  unsigned long arg)
> > >> +{
> > >> +	struct fpga_image_load *imgld = filp->private_data;
> > >> +	int ret = -ENOTTY;
> > >> +
> > >> +	switch (cmd) {
> > >> +	case FPGA_IMAGE_LOAD_WRITE:
> > >> +		mutex_lock(&imgld->lock);
> > >> +		ret = fpga_image_load_ioctl_write(imgld, arg);
> > >> +		mutex_unlock(&imgld->lock);
> > >> +		break;
> > >> +	}
> > >> +
> > >> +	return ret;
> > >> +}
> > >> +
> > >> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
> > >> +{
> > >> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
> > >> +						     struct fpga_image_load, cdev);
> > >> +
> > >> +	if (test_and_set_bit(0, &imgld->opened))
> > > Some more flags to add for "opened" field? But the field name indicates
> > > it is a single flag.
> > Can you explain your comment further? I'm not understanding. What
> > "more flags" are you referring to?
> > 
> 
> The test_and_set_bit() makes me feel there are more bits to be
> controlled in "opened" for other purposes. The name "opened" indicates
> this is a single state, maybe you don't have to use bitops for it,
> atomic ops for int maybe better.
> 
> > This test_and_set_bit() operation and the "opened" structure member
> > were borrowed from the production driver implementation. opened is a
> > single value that is expected to be either 1 or 0.
> > 
> > >
> > >> +		return -EBUSY;
> > >> +
> > >> +	filp->private_data = imgld;
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
> > >> +{
> > >> +	struct fpga_image_load *imgld = filp->private_data;
> > >> +
> > >> +	clear_bit(0, &imgld->opened);
> > >> +
> > >> +	return 0;
> > >> +}
> > >> +
> > >> +static const struct file_operations fpga_image_load_fops = {
> > >> +	.owner = THIS_MODULE,
> > >> +	.open = fpga_image_load_open,
> > >> +	.release = fpga_image_load_release,
> > >> +	.unlocked_ioctl = fpga_image_load_ioctl,
> > >> +};
> > >> +
> > >>  /**
> > >>   * fpga_image_load_register - create and register an FPGA Image Load Device
> > >>   *
> > >> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
> > >>  			 const struct fpga_image_load_ops *lops, void *priv)
> > >>  {
> > >>  	struct fpga_image_load *imgld;
> > >> -	int id, ret;
> > >> +	int ret;
> > >> +
> > >> +	if (!lops || !lops->cancel || !lops->prepare ||
> > >> +	    !lops->write_blk || !lops->poll_complete) {
> > >> +		dev_err(parent, "Attempt to register without all required ops\n");
> > >> +		return ERR_PTR(-ENOMEM);
> > >> +	}
> > >>  
> > >>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
> > >>  	if (!imgld)
> > >> -		return NULL;
> > >> +		return ERR_PTR(-ENOMEM);
> > > This is the fix for Patch #1? If yes please merge it to Patch #1.
> > Good catch. Yes, I'll move it to patch #1.
> > >
> > >>  
> > >>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
> > >>  		       GFP_KERNEL);
> > >> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
> > >>  
> > >>  	imgld->priv = priv;
> > >>  	imgld->lops = lops;
> > >> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> > >> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
> > >> +	init_completion(&imgld->update_done);
> > >> +	INIT_WORK(&imgld->work, fpga_image_do_load);
> > >>  
> > >>  	imgld->dev.class = fpga_image_load_class;
> > >>  	imgld->dev.parent = parent;
> > >> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
> > >>  
> > >> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
> > >> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
> > > Another fix? Please merge it to Patch #1.
> > Yes.
> > 
> > >
> > >>  	if (ret) {
> > >> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
> > >> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
> > >> +			imgld->dev.id);
> > > Ditto
> > Yes.
> > >
> > >>  		goto error_device;
> > >>  	}
> > >>  
> > >> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
> > >>  		return ERR_PTR(ret);
> > >>  	}
> > >>  
> > >> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
> > >> +	imgld->cdev.owner = parent->driver->owner;
> > >> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
> > >> +
> > >> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
> > >> +	if (ret) {
> > >> +		put_device(&imgld->dev);
> > >> +		return ERR_PTR(ret);
> > >> +	}
> > >> +
> > >>  	return imgld;
> > >>  
> > >>  error_device:
> > >> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
> > >>   *
> > >>   * This function is intended for use in an FPGA Image Load driver's
> > >>   * remove() function.
> > >> + *
> > >> + * For some devices, once authentication of the uploaded image has begun,
> > >> + * the hardware cannot be signaled to stop, and the driver will not exit
> > >> + * until the hardware signals completion.  This could be 30+ minutes of
> > >> + * waiting. The driver_unload flag enables a force-unload of the driver
> > >> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the
> > > How does the driver_unload enables the force unload of the parent
> > > driver? I didn't find the code.
> > 
> > The driver_unload field is tested in the lower-level (parent) driver in
> > m10bmc_sec_poll_complete(), allowing the kernel worker thread to exit
> > even if the firmware update is still in progress.
> 
> I didn't see the signaling implementation here. Maybe you need to move the
> comments somewhere else, or in another patch.

I have another concern. the design requires the low level driver poll
for a field of the input parameter that the framework manages. It is
unnormal, like an implicit cancel call to the driver.

How about the framework explicitly call cancel() when needed? Of cource
we need to declare the cancel() could be called when other callback is
in progress.

Thanks,
Yilun

> 
> Thanks,
> Yilun
> 
> > 
> > >
> > >> + * hardware update is incomplete. The driver_unload flag also prevents
> > >> + * new updates from starting once the unregister process has begun.
> > >>   */
> > >>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
> > >>  {
> > >> +	mutex_lock(&imgld->lock);
> > >> +	imgld->driver_unload = true;
> > >> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
> > >> +		mutex_unlock(&imgld->lock);
> > >> +		goto unregister;
> > >> +	}
> > >> +
> > >> +	mutex_unlock(&imgld->lock);
> > >> +	wait_for_completion(&imgld->update_done);
> > >> +
> > >> +unregister:
> > >> +	cdev_del(&imgld->cdev);
> > >>  	device_unregister(&imgld->dev);
> > >>  }
> > >>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
> > >> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
> > >>  
> > >>  static int __init fpga_image_load_class_init(void)
> > >>  {
> > >> +	int ret;
> > >>  	pr_info("FPGA Image Load Driver\n");
> > >>  
> > >>  	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
> > >>  	if (IS_ERR(fpga_image_load_class))
> > >>  		return PTR_ERR(fpga_image_load_class);
> > >>  
> > >> +	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
> > >> +				  "fpga_image_load");
> > >> +	if (ret)
> > >> +		goto exit_destroy_class;
> > >> +
> > >>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
> > >>  
> > >>  	return 0;
> > >> +
> > >> +exit_destroy_class:
> > >> +	class_destroy(fpga_image_load_class);
> > >> +	return ret;
> > >>  }
> > >>  
> > >>  static void __exit fpga_image_load_class_exit(void)
> > >>  {
> > >> +	unregister_chrdev_region(fpga_image_devt, MINORMASK);
> > >>  	class_destroy(fpga_image_load_class);
> > >>  	WARN_ON(!xa_empty(&fpga_image_load_xa));
> > >>  }
> > >> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> > >> index a9cef9e1056b..b3d790e5d943 100644
> > >> --- a/include/linux/fpga/fpga-image-load.h
> > >> +++ b/include/linux/fpga/fpga-image-load.h
> > >> @@ -7,22 +7,51 @@
> > >>  #ifndef _LINUX_FPGA_IMAGE_LOAD_H
> > >>  #define _LINUX_FPGA_IMAGE_LOAD_H
> > >>  
> > >> +#include <linux/cdev.h>
> > >> +#include <linux/completion.h>
> > >>  #include <linux/device.h>
> > >>  #include <linux/mutex.h>
> > >>  #include <linux/types.h>
> > >> +#include <uapi/linux/fpga-image-load.h>
> > >>  
> > >>  struct fpga_image_load;
> > >>  
> > >>  /**
> > >>   * struct fpga_image_load_ops - device specific operations
> > >> + * @prepare:		    Required: Prepare secure update
> > >> + * @write_blk:		    Required: Write a block of data
> > >> + * @poll_complete:	    Required: Check for the completion of the
> > >> + *			    HW authentication/programming process. This
> > >> + *			    function should check for imgld->driver_unload
> > >> + *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
> > >> + * @cancel:		    Required: Signal HW to cancel update
> > >> + * @cleanup:		    Optional: Complements the prepare()
> > >> + *			    function and is called at the completion
> > >> + *			    of the update, whether success or failure,
> > >> + *			    if the prepare function succeeded.
> > >>   */
> > >>  struct fpga_image_load_ops {
> > >> +	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
> > >> +	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
> > >> +	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
> > >> +	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
> > >> +	void (*cleanup)(struct fpga_image_load *imgld);
> > >>  };
> > >>  
> > >>  struct fpga_image_load {
> > >>  	struct device dev;
> > >> +	struct cdev cdev;
> > >>  	const struct fpga_image_load_ops *lops;
> > >>  	struct mutex lock;		/* protect data structure contents */
> > >> +	unsigned long opened;
> > >> +	struct work_struct work;
> > >> +	struct completion update_done;
> > >> +	const u8 *data;				/* pointer to update data */
> > >> +	u32 remaining_size;			/* size remaining to transfer */
> > >> +	enum fpga_image_prog progress;
> > >> +	enum fpga_image_prog err_progress;	/* progress at time of failure */
> > > This field is not used in this patch? So could you introduce it later?
> > Yes - I'll move it. Thanks.
> > >
> > >> +	enum fpga_image_err err_code;		/* image load error code */
> > >> +	bool driver_unload;
> > >>  	void *priv;
> > >>  };
> > >>  
> > >> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> > >> new file mode 100644
> > >> index 000000000000..4146a0a9e408
> > >> --- /dev/null
> > >> +++ b/include/uapi/linux/fpga-image-load.h
> > >> @@ -0,0 +1,58 @@
> > >> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > >> +/*
> > >> + * Header File for FPGA Image Load User API
> > >> + *
> > >> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
> > >> + *
> > >> + */
> > >> +
> > >> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> > >> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
> > >> +
> > >> +#include <linux/types.h>
> > >> +#include <linux/ioctl.h>
> > >> +
> > >> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9
> > >> +
> > >> +/* Image load progress codes */
> > >> +enum fpga_image_prog {
> > >> +	FPGA_IMAGE_PROG_IDLE,
> > >> +	FPGA_IMAGE_PROG_STARTING,
> > >> +	FPGA_IMAGE_PROG_PREPARING,
> > >> +	FPGA_IMAGE_PROG_WRITING,
> > >> +	FPGA_IMAGE_PROG_PROGRAMMING,
> > >> +	FPGA_IMAGE_PROG_MAX
> > >> +};
> > >> +
> > >> +/* Image error progress codes */
> > >> +enum fpga_image_err {
> > >> +	FPGA_IMAGE_ERR_NONE,
> > >> +	FPGA_IMAGE_ERR_HW_ERROR,
> > >> +	FPGA_IMAGE_ERR_TIMEOUT,
> > >> +	FPGA_IMAGE_ERR_CANCELED,
> > >> +	FPGA_IMAGE_ERR_BUSY,
> > >> +	FPGA_IMAGE_ERR_INVALID_SIZE,
> > >> +	FPGA_IMAGE_ERR_RW_ERROR,
> > >> +	FPGA_IMAGE_ERR_WEAROUT,
> > >> +	FPGA_IMAGE_ERR_MAX
> > >> +};
> > >> +
> > >> +#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> > > Put the cmd word definition under the comments and parameter definition.
> > OK
> > >
> > >> +
> > >> +/**
> > >> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> > >> + *				struct fpga_image_write)
> > >> + *
> > >> + * Upload a data buffer to the target device. The user must provide the
> > >> + * data buffer, size, and an eventfd file descriptor.
> > > I didn't find the eventfd.
> > It is added in a later patch. I'll change the comment accordingly.
> > 
> > Thanks,
> > - Russ
> > >
> > > Thanks,
> > > Yilun
> > >
> > >> + *
> > >> + * Return: 0 on success, -errno on failure.
> > >> + */
> > >> +struct fpga_image_write {
> > >> +	/* Input */
> > >> +	__u32 flags;		/* Zero for now */
> > >> +	__u32 size;		/* Data size (in bytes) to be written */
> > >> +	__u64 buf;		/* User space address of source data */
> > >> +};
> > >> +
> > >> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
> > >> -- 
> > >> 2.25.1

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

* Re: [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-11 13:13       ` Tom Rix
@ 2021-09-13 10:00         ` Xu Yilun
  2021-09-21 20:43           ` Russ Weight
  0 siblings, 1 reply; 28+ messages in thread
From: Xu Yilun @ 2021-09-13 10:00 UTC (permalink / raw)
  To: Tom Rix
  Cc: Russ Weight, mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu,
	matthew.gerlach

On Sat, Sep 11, 2021 at 06:13:08AM -0700, Tom Rix wrote:
> 
> On 9/10/21 4:38 PM, Russ Weight wrote:
> > 
> > On 9/10/21 7:55 AM, Xu Yilun wrote:
> > > On Wed, Sep 08, 2021 at 07:18:46PM -0700, Russ Weight wrote:
> > > > Extend the FPGA Image Load class driver to include a cancel IOCTL that
> > > > can be used to request that an image upload be canceled. The IOCTL may
> > > > return EBUSY if it cannot be canceled by software or ENODEV if there
> > > > is no update in progress.
> > > > 
> > > > Signed-off-by: Russ Weight <russell.h.weight@intel.com>
> > > > ---
> > > > v15:
> > > >   - Compare to previous patch:
> > > >       [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
> > > >   - Changed file, symbol, and config names to reflect the new driver name
> > > >   - Cancel is now initiated by IOCT instead of sysfs
> > > >   - Removed signed-off/reviewed-by tags
> > > > v14:
> > > >   - Updated ABI documentation date and kernel version
> > > > v13:
> > > >    - No change
> > > > v12:
> > > >    - Updated Date and KernelVersion fields in ABI documentation
> > > > v11:
> > > >    - No change
> > > > v10:
> > > >    - Rebased to 5.12-rc2 next
> > > >    - Updated Date and KernelVersion in ABI documentation
> > > > v9:
> > > >    - Updated Date and KernelVersion in ABI documentation
> > > > v8:
> > > >    - No change
> > > > v7:
> > > >    - Changed Date in documentation file to December 2020
> > > > v6:
> > > >    - No change
> > > > v5:
> > > >    - No change
> > > > v4:
> > > >    - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
> > > >      and removed unnecessary references to "Intel".
> > > >    - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
> > > > v3:
> > > >    - No change
> > > > v2:
> > > >    - Bumped documentation date and version
> > > >    - Minor code cleanup per review comments
> > > > ---
> > > > ---
> > > >   Documentation/fpga/fpga-image-load.rst |  6 ++++
> > > >   drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
> > > >   include/linux/fpga/fpga-image-load.h   |  1 +
> > > >   include/uapi/linux/fpga-image-load.h   |  1 +
> > > >   4 files changed, 49 insertions(+), 4 deletions(-)
> > > > 
> > > > diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
> > > > index 3d5eb51223e3..763e7833a6ea 100644
> > > > --- a/Documentation/fpga/fpga-image-load.rst
> > > > +++ b/Documentation/fpga/fpga-image-load.rst
> > > > @@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
> > > >   Collect status for an on-going image upload. The status returned includes
> > > >   how much data remains to be transferred, the progress of the image load,
> > > >   and error information in the case of a failure.
> > > > +
> > > > +FPGA_IMAGE_LOAD_CANCEL:
> > > > +
> > > > +Request that a on-going image upload be cancelled. This IOCTL may return
> > > > +EBUSY if it cannot be cancelled by software or ENODEV if there is no update
> > > > +in progress.
> > > > diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
> > > > index 6ec0a39f07b3..c32e4b1ea35a 100644
> > > > --- a/drivers/fpga/fpga-image-load.c
> > > > +++ b/drivers/fpga/fpga-image-load.c
> > > > @@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
> > > >   	imgld->lops->cancel(imgld);
> > > >   }
> > > > +static int fpga_image_prog_transition(struct fpga_image_load *imgld,
> > > > +				      enum fpga_image_prog new_progress)
> > > > +{
> > > > +	int ret = 0;
> > > > +
> > > > +	mutex_lock(&imgld->lock);
> > > > +	if (imgld->request_cancel) {
> > > > +		imgld->err_progress = imgld->progress;
> > > > +		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
> > > > +		imgld->lops->cancel(imgld);
> > > > +		ret = -ECANCELED;
> > > > +	} else {
> > > > +		imgld->progress = new_progress;
> > > > +	}
> > > > +	mutex_unlock(&imgld->lock);
> > > > +	return ret;
> > > > +}
> > > > +
> > > >   static void fpga_image_prog_complete(struct fpga_image_load *imgld)
> > > >   {
> > > >   	mutex_lock(&imgld->lock);
> > > > @@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
> > > >   		goto modput_exit;
> > > >   	}
> > > > -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
> > > > -	while (imgld->remaining_size) {
> > > > +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
> > > > +		goto done;
> > > > +
> > > > +	while (imgld->remaining_size && !imgld->request_cancel) {
> > > >   		ret = imgld->lops->write_blk(imgld, offset);
> > > >   		if (ret != FPGA_IMAGE_ERR_NONE) {
> > > >   			fpga_image_dev_error(imgld, ret);
> > > > @@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
> > > >   		offset = size - imgld->remaining_size;
> > > >   	}
> > > > -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
> > > > +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
> > > > +		goto done;
> > > > +
> > > >   	ret = imgld->lops->poll_complete(imgld);
> > > >   	if (ret != FPGA_IMAGE_ERR_NONE)
> > > >   		fpga_image_dev_error(imgld, ret);
> > > > @@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
> > > >   	imgld->remaining_size = wb.size;
> > > >   	imgld->err_code = FPGA_IMAGE_ERR_NONE;
> > > >   	imgld->progress = FPGA_IMAGE_PROG_STARTING;
> > > > +	imgld->request_cancel = false;
> > > >   	reinit_completion(&imgld->update_done);
> > > >   	schedule_work(&imgld->work);
> > > >   	return 0;
> > > > @@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> > > >   				  unsigned long arg)
> > > >   {
> > > >   	struct fpga_image_load *imgld = filp->private_data;
> > > > -	int ret = -ENOTTY;
> > > > +	int ret = 0;
> > > >   	mutex_lock(&imgld->lock);
> > > > @@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
> > > >   	case FPGA_IMAGE_LOAD_STATUS:
> > > >   		ret = fpga_image_load_ioctl_status(imgld, arg);
> > > >   		break;
> > > > +	case FPGA_IMAGE_LOAD_CANCEL:
> > > > +		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
> > > > +			ret = -EBUSY;
> > > > +		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
> > > > +			ret = -ENODEV;
> > > > +		else
> > > > +			imgld->request_cancel = true;
> > > > +		break;
> > > > +	default:
> > > > +		ret = -ENOTTY;
> > > > +		break;
> > > >   	}
> > > >   	mutex_unlock(&imgld->lock);
> > > > @@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
> > > >   		goto unregister;
> > > >   	}
> > > > +	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
> > > > +		imgld->request_cancel = true;
> > > > +
> > > Why we cancel the programing rather than waiting for programing done?
> > This isn't new - it is the way the security manager was implemented.
> > 
> > Updates can take up to 40 minutes for the N3000. If a person tries
> > to unload the driver modules, should we hang for 40 minutes? Or should
> > we try to cancel the update and allow the module to be unloaded?
> > I think it is reasonable to cancel the update (if possible) under these
> > circumstances. What do you think?
> 
> I do not think stalling a shutdown for 40 minutes is good.
> 
> Is there a chance that cancelling would brick the card?

If the cancel cannot be possible, may be we could wait. Or the user
image would be corrupted with a simple poweroff command.

Thanks,
Yilun

> 
> After restarting, can the hw detect an interrupted update ?
> 
> Tom
> 
> > 
> > - Russ
> > 
> > > Thanks,
> > > Yilun
> > > 
> > > >   	mutex_unlock(&imgld->lock);
> > > >   	wait_for_completion(&imgld->update_done);
> > > > diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
> > > > index 68f3105b51d2..4e51b9fd1724 100644
> > > > --- a/include/linux/fpga/fpga-image-load.h
> > > > +++ b/include/linux/fpga/fpga-image-load.h
> > > > @@ -52,6 +52,7 @@ struct fpga_image_load {
> > > >   	enum fpga_image_prog progress;
> > > >   	enum fpga_image_prog err_progress;	/* progress at time of failure */
> > > >   	enum fpga_image_err err_code;		/* image load error code */
> > > > +	bool request_cancel;
> > > >   	bool driver_unload;
> > > >   	struct eventfd_ctx *finished;
> > > >   	void *priv;
> > > > diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
> > > > index 6a995bcc0fb7..8d0dfa1f9b77 100644
> > > > --- a/include/uapi/linux/fpga-image-load.h
> > > > +++ b/include/uapi/linux/fpga-image-load.h
> > > > @@ -39,6 +39,7 @@ enum fpga_image_err {
> > > >   #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
> > > >   #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
> > > > +#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
> > > >   /**
> > > >    * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
> > > > -- 
> > > > 2.25.1

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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
       [not found] ` <20210912023739.4078-1-hdanton@sina.com>
@ 2021-09-21 18:36   ` Russ Weight
  0 siblings, 0 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-21 18:36 UTC (permalink / raw)
  To: Hillf Danton; +Cc: linux-fpga, trix, linux-kernel



On 9/11/21 7:37 PM, Hillf Danton wrote:
> On Wed,  8 Sep 2021 19:18:42 -0700 Russ Weight wrote:
>> Extend the FPGA Image Load class driver to include IOCTL support
>> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
>> The IOCTL will return immediately, and the update will begin in the
>> context of a kernel worker thread.
>>
>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>> ---
> Good work overall. Nits below.
> [...]
>
>> +++ b/drivers/fpga/fpga-image-load.c
>> @@ -5,18 +5,181 @@
>>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
>>   */
>>  
>> +#include <linux/delay.h>
>>  #include <linux/fpga/fpga-image-load.h>
>> +#include <linux/fs.h>
>> +#include <linux/kernel.h>
>>  #include <linux/module.h>
>>  #include <linux/slab.h>
>> +#include <linux/uaccess.h>
>>  #include <linux/vmalloc.h>
>>  
>>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
>>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
>>  
>>  static struct class *fpga_image_load_class;
>> +static dev_t fpga_image_devt;
>>  
>>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>>  
>> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
>> +				 enum fpga_image_err err_code)
>> +{
>> +	imgld->err_code = err_code;
>> +	imgld->lops->cancel(imgld);
>> +}
>> +
>> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>> +{
>> +	mutex_lock(&imgld->lock);
>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>> +	complete_all(&imgld->update_done);
>> +	mutex_unlock(&imgld->lock);
>> +}
>> +
>> +static void fpga_image_do_load(struct work_struct *work)
>> +{
>> +	struct fpga_image_load *imgld;
>> +	enum fpga_image_err ret;
>> +	u32 size, offset = 0;
>> +
>> +	imgld = container_of(work, struct fpga_image_load, work);
>> +	size = imgld->remaining_size;
> Bail out if imgld->driver_unload is set.
I'll add this.
>
>> +
>> +	get_device(&imgld->dev);
>> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
>> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
>> +		goto idle_exit;
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
>> +	ret = imgld->lops->prepare(imgld);
>> +	if (ret != FPGA_IMAGE_ERR_NONE) {
>> +		fpga_image_dev_error(imgld, ret);
>> +		goto modput_exit;
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
>> +	while (imgld->remaining_size) {
>> +		ret = imgld->lops->write_blk(imgld, offset);
>> +		if (ret != FPGA_IMAGE_ERR_NONE) {
>> +			fpga_image_dev_error(imgld, ret);
>> +			goto done;
>> +		}
>> +
>> +		offset = size - imgld->remaining_size;
> If cond_resched() is needed here then make imgld->work unbound.
>
> 	queue_work(system_unbound_wq, &imgld->work);
Given that class driver does not have control over the size of
the data or the actual implementation of the writes, it is
probably best to add these changes. I'll do this for the next
submission of theses patches.

>
>> +	}
>> +
>> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
>> +	ret = imgld->lops->poll_complete(imgld);
>> +	if (ret != FPGA_IMAGE_ERR_NONE)
>> +		fpga_image_dev_error(imgld, ret);
>> +
>> +done:
>> +	if (imgld->lops->cleanup)
>> +		imgld->lops->cleanup(imgld);
>> +
>> +modput_exit:
>> +	module_put(imgld->dev.parent->driver->owner);
>> +
>> +idle_exit:
>> +	/*
>> +	 * Note: imgld->remaining_size is left unmodified here to provide
>> +	 * additional information on errors. It will be reinitialized when
>> +	 * the next image load begins.
>> +	 */
>> +	vfree(imgld->data);
>> +	imgld->data = NULL;
>> +	put_device(&imgld->dev);
>> +	fpga_image_prog_complete(imgld);
>> +}
>> +
>> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>> +				       unsigned long arg)
>> +{
>> +	struct fpga_image_write wb;
>> +	unsigned long minsz;
>> +	u8 *buf;
>> +
>> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
>> +		return -EBUSY;
>> +
>> +	minsz = offsetofend(struct fpga_image_write, buf);
>> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
>> +		return -EFAULT;
>> +
>> +	if (wb.flags)
>> +		return -EINVAL;
>> +
>> +	/* Enforce 32-bit alignment on the write data */
>> +	if (wb.size & 0x3)
>> +		return -EINVAL;
>> +
>> +	buf = vzalloc(wb.size);
>> +	if (!buf)
>> +		return -ENOMEM;
>> +
>> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
>> +		vfree(buf);
>> +		return -EFAULT;
>> +	}
>> +
>> +	imgld->data = buf;
>> +	imgld->remaining_size = wb.size;
>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>> +	reinit_completion(&imgld->update_done);
>> +	schedule_work(&imgld->work);
>> +
>> +	return 0;
>> +}
>> +
>> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>> +				  unsigned long arg)
>> +{
>> +	struct fpga_image_load *imgld = filp->private_data;
>> +	int ret = -ENOTTY;
>> +
>> +	switch (cmd) {
>> +	case FPGA_IMAGE_LOAD_WRITE:
>> +		mutex_lock(&imgld->lock);
>> +		ret = fpga_image_load_ioctl_write(imgld, arg);
>> +		mutex_unlock(&imgld->lock);
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
>> +{
>> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
>> +						     struct fpga_image_load, cdev);
>> +
>> +	if (test_and_set_bit(0, &imgld->opened))
>> +		return -EBUSY;
>> +
>> +	filp->private_data = imgld;
>> +
>> +	return 0;
>> +}
>> +
>> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
>> +{
>> +	struct fpga_image_load *imgld = filp->private_data;
>> +
>> +	clear_bit(0, &imgld->opened);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct file_operations fpga_image_load_fops = {
>> +	.owner = THIS_MODULE,
>> +	.open = fpga_image_load_open,
>> +	.release = fpga_image_load_release,
>> +	.unlocked_ioctl = fpga_image_load_ioctl,
>> +};
>> +
>>  /**
>>   * fpga_image_load_register - create and register an FPGA Image Load Device
>>   *
>> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
>>  			 const struct fpga_image_load_ops *lops, void *priv)
>>  {
>>  	struct fpga_image_load *imgld;
>> -	int id, ret;
>> +	int ret;
>> +
>> +	if (!lops || !lops->cancel || !lops->prepare ||
>> +	    !lops->write_blk || !lops->poll_complete) {
>> +		dev_err(parent, "Attempt to register without all required ops\n");
>> +		return ERR_PTR(-ENOMEM);
>> +	}
>>  
>>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
>>  	if (!imgld)
>> -		return NULL;
>> +		return ERR_PTR(-ENOMEM);
>>  
>>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
>>  		       GFP_KERNEL);
>> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
>>  
>>  	imgld->priv = priv;
>>  	imgld->lops = lops;
>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>> +	init_completion(&imgld->update_done);
>> +	INIT_WORK(&imgld->work, fpga_image_do_load);
>>  
>>  	imgld->dev.class = fpga_image_load_class;
>>  	imgld->dev.parent = parent;
>> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
>>  
>> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
>> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
>>  	if (ret) {
>> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
>> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
>> +			imgld->dev.id);
>>  		goto error_device;
>>  	}
>>  
>> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
>>  		return ERR_PTR(ret);
>>  	}
>>  
>> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
>> +	imgld->cdev.owner = parent->driver->owner;
>> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
>> +
>> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
>> +	if (ret) {
>> +		put_device(&imgld->dev);
>> +		return ERR_PTR(ret);
>> +	}
>> +
>>  	return imgld;
>>  
>>  error_device:
>> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
>>   *
>>   * This function is intended for use in an FPGA Image Load driver's
>>   * remove() function.
>> + *
>> + * For some devices, once authentication of the uploaded image has begun,
>> + * the hardware cannot be signaled to stop, and the driver will not exit
>> + * until the hardware signals completion.  This could be 30+ minutes of
>> + * waiting. The driver_unload flag enables a force-unload of the driver
>> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the
>> + * hardware update is incomplete. The driver_unload flag also prevents
>> + * new updates from starting once the unregister process has begun.
>>   */
>>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>  {
>> +	mutex_lock(&imgld->lock);
>> +	imgld->driver_unload = true;
>> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
>> +		mutex_unlock(&imgld->lock);
>> +		goto unregister;
>> +	}
>> +
>> +	mutex_unlock(&imgld->lock);
>> +	wait_for_completion(&imgld->update_done);
> You can cut update_done if wait_for_completion() is replaced with
> flush_work() or cancel_work_sync(&imgld->work).
Thanks! I'll use flush_work() and eliminate the update_done completion
instance.

Thanks for the suggestions!

- Russ
>
>> +
>> +unregister:
>> +	cdev_del(&imgld->cdev);
>>  	device_unregister(&imgld->dev);
>>  }
>>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);


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

* Re: [PATCH v15 2/6] fpga: image-load: enable image loads
  2021-09-13  9:36         ` Xu Yilun
@ 2021-09-21 19:08           ` Russ Weight
  0 siblings, 0 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-21 19:08 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, linux-fpga, linux-kernel, trix, lgoncalv, hao.wu, matthew.gerlach



On 9/13/21 2:36 AM, Xu Yilun wrote:
> On Mon, Sep 13, 2021 at 02:48:45PM +0800, Xu Yilun wrote:
>> On Fri, Sep 10, 2021 at 04:18:26PM -0700, Russ Weight wrote:
>>>
>>> On 9/10/21 1:22 AM, Xu Yilun wrote:
>>>> On Wed, Sep 08, 2021 at 07:18:42PM -0700, Russ Weight wrote:
>>>>> Extend the FPGA Image Load class driver to include IOCTL support
>>>>> (FPGA_IMAGE_LOAD_WRITE) for initiating an upload of an image to a device.
>>>>> The IOCTL will return immediately, and the update will begin in the
>>>>> context of a kernel worker thread.
>>>>>
>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>> ---
>>>>> v15:
>>>>>  - Compare to previous patch:
>>>>>      [PATCH v14 2/6] fpga: sec-mgr: enable secure updates
>>>>>  - Changed file, symbol, and config names to reflect the new driver name
>>>>>  - Removed update/filename sysfs file and added the FPGA_IMAGE_LOAD_WRITE
>>>>>    IOCTL for writing the image data to the FPGA card. The driver no longer
>>>>>    uses the request_firmware framework.
>>>>>  - Fixed some error return values in fpga_image_load_register()
>>>>>  - Removed signed-off/reviewed-by tags
>>>>> v14:
>>>>>  - Added MAINTAINERS reference for
>>>>>    Documentation/ABI/testing/sysfs-class-fpga-sec-mgr
>>>>>  - Updated ABI documentation date and kernel version
>>>>>  - Updated copyright to 2021
>>>>> v13:
>>>>>   - Change "if (count == 0 || " to "if (!count || "
>>>>>   - Improve error message: "Attempt to register without all required ops\n"
>>>>> v12:
>>>>>   - Updated Date and KernelVersion fields in ABI documentation
>>>>>   - Removed size parameter from write_blk() op - it is now up to
>>>>>     the lower-level driver to determine the appropriate size and
>>>>>     to update smgr->remaining_size accordingly.
>>>>> v11:
>>>>>   - Fixed a spelling error in a comment
>>>>>   - Initialize smgr->err_code and smgr->progress explicitly in
>>>>>     fpga_sec_mgr_create() instead of accepting the default 0 value.
>>>>> v10:
>>>>>   - Rebased to 5.12-rc2 next
>>>>>   - Updated Date and KernelVersion in ABI documentation
>>>>> v9:
>>>>>   - Updated Date and KernelVersion in ABI documentation
>>>>> v8:
>>>>>   - No change
>>>>> v7:
>>>>>   - Changed Date in documentation file to December 2020
>>>>>   - Changed filename_store() to use kmemdup_nul() instead of
>>>>>     kstrndup() and changed the count to not assume a line-return.
>>>>> v6:
>>>>>   - Changed "security update" to "secure update" in commit message
>>>>> v5:
>>>>>   - When checking the return values for functions of type enum
>>>>>     fpga_sec_err err_code, test for FPGA_SEC_ERR_NONE instead of 0
>>>>> v4:
>>>>>   - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>>>>     and removed unnecessary references to "Intel".
>>>>>   - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>>>>> v3:
>>>>>   - Removed unnecessary "goto done"
>>>>>   - Added a comment to explain imgr->driver_unload in
>>>>>     ifpga_sec_mgr_unregister()
>>>>> v2:
>>>>>   - Bumped documentation date and version
>>>>>   - Removed explicit value assignments in enums
>>>>>   - Other minor code cleanup per review comments
>>>>> ---
>>>>>  Documentation/fpga/fpga-image-load.rst |  21 +++
>>>>>  MAINTAINERS                            |   1 +
>>>>>  drivers/fpga/fpga-image-load.c         | 224 ++++++++++++++++++++++++-
>>>>>  include/linux/fpga/fpga-image-load.h   |  29 ++++
>>>>>  include/uapi/linux/fpga-image-load.h   |  58 +++++++
>>>>>  5 files changed, 329 insertions(+), 4 deletions(-)
>>>>>  create mode 100644 include/uapi/linux/fpga-image-load.h
>>>>>
>>>>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>>>>> index a6e53ac66026..2ca8d2f0212d 100644
>>>>> --- a/Documentation/fpga/fpga-image-load.rst
>>>>> +++ b/Documentation/fpga/fpga-image-load.rst
>>>>> @@ -8,3 +8,24 @@ The FPGA Image Load class driver provides a common API for user-space
>>>>>  tools to manage image uploads to FPGA devices. Device drivers that
>>>>>  instantiate the FPGA Image Load class driver will interact with the
>>>>>  target device to transfer and authenticate the image data.
>>>>> +
>>>>> +User API
>>>>> +========
>>>>> +
>>>>> +open
>>>>> +----
>>>>> +
>>>>> +An FPGA Image Load device is opened exclusively to control an image load.
>>>>> +Image loads are processed by a kernel worker thread. A user may choose
>>>>> +close the device while the upload continues.
>>>> Why we allow the user to close the dev while the uploading is ongoing?
>>>> Seems it introduces more checking effort when another user open the dev
>>>> again and try another uploading.
>>> A user could choose to write their own tools. How would we prevent
>>> them from closing the device while an update is in progress? Should
>>> we attempt to cancel the update if they close the device?
>> I think we could try to cancel the update when close(), if we cannot
>> cancel we block on close().
>>
>> The driver should be responsible for the integrity of the update flow,
>> it is not prefered a user starts an update and leaves, but canceled by
>> another user.
OK - I can make those changes for the next version.

>>> Concurrent updates are already prevented by returning EBUSY for the
>>> FPGA_IMAGE_LOAD_WRITE IOCTL if an update is already in progress.
>>>
>>> A single IOCTL is sufficient to do a full update. Maybe a user
>>> would want to have three small tools: update_start, update_status,
>>> update_cancel, each of which would open the device, do an IOCTL,
>>> and then close the device. This is analogous to the sysfs
>>> implementation (no open/close that bounds the entire sequence).
>>>
>>> With the current design, we do an exclusive open. As long as the user
>>> keeps the device open, no other process can open the device to start
>>> a new update, cancel, or collect status.
>>>
>>> Those are my thoughts. What do you think? Is it OK as is? Or should
>>> I make some changes here?
>>>
>>>>> +
>>>>> +ioctl
>>>>> +-----
>>>>> +
>>>>> +FPGA_IMAGE_LOAD_WRITE:
>>>>> +
>>>>> +Start an image load with the provided image buffer. This IOCTL returns
>>>>> +immediately after starting a kernel worker thread to process the image load
>>>>> +which could take as long a 40 minutes depending on the actual device being
>>>>> +updated. This is an exclusive operation; an attempt to start concurrent image
>>>>> +load for the same device will fail with EBUSY.
>>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>>> index 4e7f48fa7e5c..637bc003ca81 100644
>>>>> --- a/MAINTAINERS
>>>>> +++ b/MAINTAINERS
>>>>> @@ -7365,6 +7365,7 @@ S:	Maintained
>>>>>  F:	Documentation/fpga/fpga-image-load.rst
>>>>>  F:	drivers/fpga/fpga-image-load.c
>>>>>  F:	include/linux/fpga/fpga-image-load.h
>>>>> +F:	include/uapi/linux/fpga-image-load.h
>>>>>  
>>>>>  FPU EMULATOR
>>>>>  M:	Bill Metzenthen <billm@melbpc.org.au>
>>>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>>>>> index 7d75bbcff541..f5ccfa9dd977 100644
>>>>> --- a/drivers/fpga/fpga-image-load.c
>>>>> +++ b/drivers/fpga/fpga-image-load.c
>>>>> @@ -5,18 +5,181 @@
>>>>>   * Copyright (C) 2019-2021 Intel Corporation, Inc.
>>>>>   */
>>>>>  
>>>>> +#include <linux/delay.h>
>>>>>  #include <linux/fpga/fpga-image-load.h>
>>>>> +#include <linux/fs.h>
>>>>> +#include <linux/kernel.h>
>>>>>  #include <linux/module.h>
>>>>>  #include <linux/slab.h>
>>>>> +#include <linux/uaccess.h>
>>>>>  #include <linux/vmalloc.h>
>>>>>  
>>>>>  #define IMAGE_LOAD_XA_LIMIT	XA_LIMIT(0, INT_MAX)
>>>>>  static DEFINE_XARRAY_ALLOC(fpga_image_load_xa);
>>>>>  
>>>>>  static struct class *fpga_image_load_class;
>>>>> +static dev_t fpga_image_devt;
>>>>>  
>>>>>  #define to_image_load(d) container_of(d, struct fpga_image_load, dev)
>>>>>  
>>>>> +static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>>>> +				 enum fpga_image_err err_code)
>>>>> +{
>>>>> +	imgld->err_code = err_code;
>>>>> +	imgld->lops->cancel(imgld);
>>>>> +}
>>>>> +
>>>>> +static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>>>>> +{
>>>>> +	mutex_lock(&imgld->lock);
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>>>>> +	complete_all(&imgld->update_done);
>>>>> +	mutex_unlock(&imgld->lock);
>>>>> +}
>>>>> +
>>>>> +static void fpga_image_do_load(struct work_struct *work)
>>>>> +{
>>>>> +	struct fpga_image_load *imgld;
>>>>> +	enum fpga_image_err ret;
>>>>> +	u32 size, offset = 0;
>>>>> +
>>>>> +	imgld = container_of(work, struct fpga_image_load, work);
>>>>> +	size = imgld->remaining_size;
>>>>> +
>>>>> +	get_device(&imgld->dev);
>>>>> +	if (!try_module_get(imgld->dev.parent->driver->owner)) {
>>>>> +		imgld->err_code = FPGA_IMAGE_ERR_BUSY;
>>>>> +		goto idle_exit;
>>>>> +	}
>>>>> +
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_PREPARING;
>>>>> +	ret = imgld->lops->prepare(imgld);
>>>>> +	if (ret != FPGA_IMAGE_ERR_NONE) {
>>>>> +		fpga_image_dev_error(imgld, ret);
>>>>> +		goto modput_exit;
>>>>> +	}
>>>>> +
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_WRITING;
>>>>> +	while (imgld->remaining_size) {
>>>>> +		ret = imgld->lops->write_blk(imgld, offset);
>>>>> +		if (ret != FPGA_IMAGE_ERR_NONE) {
>>>>> +			fpga_image_dev_error(imgld, ret);
>>>>> +			goto done;
>>>>> +		}
>>>>> +
>>>>> +		offset = size - imgld->remaining_size;
>>>> The low level driver is required to update the "remaining_size" in
>>>> write_blk ops?
>>>>
>>>> The API seems ambiguous. The framework asks for writing a block of data,
>>>> but no block size is specified.
>>> This change was made two or three iterations ago at Richard Gong's
>>> suggestion. He asserted that the lower level driver should be in
>>> control of the block size (based on write speeds). What do you
>> I think it is reasonable.
>>
>>> think? Should the class driver impose a fixed size? Or allow the
>> But the definition of the API is hard to comprehend, either we input the
>> offset & the blk size, or we record them all in imgld structure, is it
>> better.
>>
>> If the framework provides the blk size, the low level driver could
>> return the written size so it could still control its own batch.
I'll clean up the API for this function in the next version of
the patches.

>>
>>> lower-level driver to determine the size? Is it OK to update
>>> remaining_size in the lower-level driver? Or should there be
>>> another call-back to request the size?
>>>  
>>>>> +	}
>>>>> +
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_PROGRAMMING;
>>>>> +	ret = imgld->lops->poll_complete(imgld);
>>>>> +	if (ret != FPGA_IMAGE_ERR_NONE)
>>>>> +		fpga_image_dev_error(imgld, ret);
>>>>> +
>>>>> +done:
>>>>> +	if (imgld->lops->cleanup)
>>>>> +		imgld->lops->cleanup(imgld);
>>>>> +
>>>>> +modput_exit:
>>>>> +	module_put(imgld->dev.parent->driver->owner);
>>>>> +
>>>>> +idle_exit:
>>>>> +	/*
>>>>> +	 * Note: imgld->remaining_size is left unmodified here to provide
>>>>> +	 * additional information on errors. It will be reinitialized when
>>>>> +	 * the next image load begins.
>>>>> +	 */
>>>>> +	vfree(imgld->data);
>>>>> +	imgld->data = NULL;
>>>>> +	put_device(&imgld->dev);
>>>>> +	fpga_image_prog_complete(imgld);
>>>>> +}
>>>>> +
>>>>> +static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>>>> +				       unsigned long arg)
>>>>> +{
>>>>> +	struct fpga_image_write wb;
>>>>> +	unsigned long minsz;
>>>>> +	u8 *buf;
>>>>> +
>>>>> +	if (imgld->driver_unload || imgld->progress != FPGA_IMAGE_PROG_IDLE)
>>>>> +		return -EBUSY;
>>>>> +
>>>>> +	minsz = offsetofend(struct fpga_image_write, buf);
>>>>> +	if (copy_from_user(&wb, (void __user *)arg, minsz))
>>>>> +		return -EFAULT;
>>>>> +
>>>>> +	if (wb.flags)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	/* Enforce 32-bit alignment on the write data */
>>>>> +	if (wb.size & 0x3)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	buf = vzalloc(wb.size);
>>>>> +	if (!buf)
>>>>> +		return -ENOMEM;
>>>>> +
>>>>> +	if (copy_from_user(buf, u64_to_user_ptr(wb.buf), wb.size)) {
>>>>> +		vfree(buf);
>>>>> +		return -EFAULT;
>>>>> +	}
>>>>> +
>>>>> +	imgld->data = buf;
>>>>> +	imgld->remaining_size = wb.size;
>>>>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>>>>> +	reinit_completion(&imgld->update_done);
>>>>> +	schedule_work(&imgld->work);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>>> +				  unsigned long arg)
>>>>> +{
>>>>> +	struct fpga_image_load *imgld = filp->private_data;
>>>>> +	int ret = -ENOTTY;
>>>>> +
>>>>> +	switch (cmd) {
>>>>> +	case FPGA_IMAGE_LOAD_WRITE:
>>>>> +		mutex_lock(&imgld->lock);
>>>>> +		ret = fpga_image_load_ioctl_write(imgld, arg);
>>>>> +		mutex_unlock(&imgld->lock);
>>>>> +		break;
>>>>> +	}
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int fpga_image_load_open(struct inode *inode, struct file *filp)
>>>>> +{
>>>>> +	struct fpga_image_load *imgld = container_of(inode->i_cdev,
>>>>> +						     struct fpga_image_load, cdev);
>>>>> +
>>>>> +	if (test_and_set_bit(0, &imgld->opened))
>>>> Some more flags to add for "opened" field? But the field name indicates
>>>> it is a single flag.
>>> Can you explain your comment further? I'm not understanding. What
>>> "more flags" are you referring to?
>>>
>> The test_and_set_bit() makes me feel there are more bits to be
>> controlled in "opened" for other purposes. The name "opened" indicates
>> this is a single state, maybe you don't have to use bitops for it,
>> atomic ops for int maybe better.
OK - I'll use an atomic operation here.
>>
>>> This test_and_set_bit() operation and the "opened" structure member
>>> were borrowed from the production driver implementation. opened is a
>>> single value that is expected to be either 1 or 0.
>>>
>>>>> +		return -EBUSY;
>>>>> +
>>>>> +	filp->private_data = imgld;
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int fpga_image_load_release(struct inode *inode, struct file *filp)
>>>>> +{
>>>>> +	struct fpga_image_load *imgld = filp->private_data;
>>>>> +
>>>>> +	clear_bit(0, &imgld->opened);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct file_operations fpga_image_load_fops = {
>>>>> +	.owner = THIS_MODULE,
>>>>> +	.open = fpga_image_load_open,
>>>>> +	.release = fpga_image_load_release,
>>>>> +	.unlocked_ioctl = fpga_image_load_ioctl,
>>>>> +};
>>>>> +
>>>>>  /**
>>>>>   * fpga_image_load_register - create and register an FPGA Image Load Device
>>>>>   *
>>>>> @@ -33,11 +196,17 @@ fpga_image_load_register(struct device *parent,
>>>>>  			 const struct fpga_image_load_ops *lops, void *priv)
>>>>>  {
>>>>>  	struct fpga_image_load *imgld;
>>>>> -	int id, ret;
>>>>> +	int ret;
>>>>> +
>>>>> +	if (!lops || !lops->cancel || !lops->prepare ||
>>>>> +	    !lops->write_blk || !lops->poll_complete) {
>>>>> +		dev_err(parent, "Attempt to register without all required ops\n");
>>>>> +		return ERR_PTR(-ENOMEM);
>>>>> +	}
>>>>>  
>>>>>  	imgld = kzalloc(sizeof(*imgld), GFP_KERNEL);
>>>>>  	if (!imgld)
>>>>> -		return NULL;
>>>>> +		return ERR_PTR(-ENOMEM);
>>>> This is the fix for Patch #1? If yes please merge it to Patch #1.
>>> Good catch. Yes, I'll move it to patch #1.
>>>>>  
>>>>>  	ret = xa_alloc(&fpga_image_load_xa, &imgld->dev.id, imgld, IMAGE_LOAD_XA_LIMIT,
>>>>>  		       GFP_KERNEL);
>>>>> @@ -48,13 +217,19 @@ fpga_image_load_register(struct device *parent,
>>>>>  
>>>>>  	imgld->priv = priv;
>>>>>  	imgld->lops = lops;
>>>>> +	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>>>>> +	imgld->progress = FPGA_IMAGE_PROG_IDLE;
>>>>> +	init_completion(&imgld->update_done);
>>>>> +	INIT_WORK(&imgld->work, fpga_image_do_load);
>>>>>  
>>>>>  	imgld->dev.class = fpga_image_load_class;
>>>>>  	imgld->dev.parent = parent;
>>>>> +	imgld->dev.devt = MKDEV(MAJOR(fpga_image_devt), imgld->dev.id);
>>>>>  
>>>>> -	ret = dev_set_name(&imgld->dev, "fpga_image%d", id);
>>>>> +	ret = dev_set_name(&imgld->dev, "fpga_image%d", imgld->dev.id);
>>>> Another fix? Please merge it to Patch #1.
>>> Yes.
>>>
>>>>>  	if (ret) {
>>>>> -		dev_err(parent, "Failed to set device name: fpga_image%d\n", id);
>>>>> +		dev_err(parent, "Failed to set device name: fpga_image%d\n",
>>>>> +			imgld->dev.id);
>>>> Ditto
>>> Yes.
>>>>>  		goto error_device;
>>>>>  	}
>>>>>  
>>>>> @@ -64,6 +239,16 @@ fpga_image_load_register(struct device *parent,
>>>>>  		return ERR_PTR(ret);
>>>>>  	}
>>>>>  
>>>>> +	cdev_init(&imgld->cdev, &fpga_image_load_fops);
>>>>> +	imgld->cdev.owner = parent->driver->owner;
>>>>> +	imgld->cdev.kobj.parent = &imgld->dev.kobj;
>>>>> +
>>>>> +	ret = cdev_add(&imgld->cdev, imgld->dev.devt, 1);
>>>>> +	if (ret) {
>>>>> +		put_device(&imgld->dev);
>>>>> +		return ERR_PTR(ret);
>>>>> +	}
>>>>> +
>>>>>  	return imgld;
>>>>>  
>>>>>  error_device:
>>>>> @@ -83,9 +268,29 @@ EXPORT_SYMBOL_GPL(fpga_image_load_register);
>>>>>   *
>>>>>   * This function is intended for use in an FPGA Image Load driver's
>>>>>   * remove() function.
>>>>> + *
>>>>> + * For some devices, once authentication of the uploaded image has begun,
>>>>> + * the hardware cannot be signaled to stop, and the driver will not exit
>>>>> + * until the hardware signals completion.  This could be 30+ minutes of
>>>>> + * waiting. The driver_unload flag enables a force-unload of the driver
>>>>> + * (e.g. modprobe -r) by signaling the parent driver to exit even if the
>>>> How does the driver_unload enables the force unload of the parent
>>>> driver? I didn't find the code.
>>> The driver_unload field is tested in the lower-level (parent) driver in
>>> m10bmc_sec_poll_complete(), allowing the kernel worker thread to exit
>>> even if the firmware update is still in progress.
>> I didn't see the signaling implementation here. Maybe you need to move the
>> comments somewhere else, or in another patch.
> I have another concern. the design requires the low level driver poll
> for a field of the input parameter that the framework manages. It is
> unnormal, like an implicit cancel call to the driver.
I'm cleaning up the driver_unload. I'm also changing the release
(close) function to set the request_cancel flag when need. I think
I have addressed all of your concerns for the next version of the
patches.

Thanks,
- Russ
>
> How about the framework explicitly call cancel() when needed? Of cource
> we need to declare the cancel() could be called when other callback is
> in progress.
>
> Thanks,
> Yilun
>
>> Thanks,
>> Yilun
>>
>>>>> + * hardware update is incomplete. The driver_unload flag also prevents
>>>>> + * new updates from starting once the unregister process has begun.
>>>>>   */
>>>>>  void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>>>>  {
>>>>> +	mutex_lock(&imgld->lock);
>>>>> +	imgld->driver_unload = true;
>>>>> +	if (imgld->progress == FPGA_IMAGE_PROG_IDLE) {
>>>>> +		mutex_unlock(&imgld->lock);
>>>>> +		goto unregister;
>>>>> +	}
>>>>> +
>>>>> +	mutex_unlock(&imgld->lock);
>>>>> +	wait_for_completion(&imgld->update_done);
>>>>> +
>>>>> +unregister:
>>>>> +	cdev_del(&imgld->cdev);
>>>>>  	device_unregister(&imgld->dev);
>>>>>  }
>>>>>  EXPORT_SYMBOL_GPL(fpga_image_load_unregister);
>>>>> @@ -100,19 +305,30 @@ static void fpga_image_load_dev_release(struct device *dev)
>>>>>  
>>>>>  static int __init fpga_image_load_class_init(void)
>>>>>  {
>>>>> +	int ret;
>>>>>  	pr_info("FPGA Image Load Driver\n");
>>>>>  
>>>>>  	fpga_image_load_class = class_create(THIS_MODULE, "fpga_image_load");
>>>>>  	if (IS_ERR(fpga_image_load_class))
>>>>>  		return PTR_ERR(fpga_image_load_class);
>>>>>  
>>>>> +	ret = alloc_chrdev_region(&fpga_image_devt, 0, MINORMASK,
>>>>> +				  "fpga_image_load");
>>>>> +	if (ret)
>>>>> +		goto exit_destroy_class;
>>>>> +
>>>>>  	fpga_image_load_class->dev_release = fpga_image_load_dev_release;
>>>>>  
>>>>>  	return 0;
>>>>> +
>>>>> +exit_destroy_class:
>>>>> +	class_destroy(fpga_image_load_class);
>>>>> +	return ret;
>>>>>  }
>>>>>  
>>>>>  static void __exit fpga_image_load_class_exit(void)
>>>>>  {
>>>>> +	unregister_chrdev_region(fpga_image_devt, MINORMASK);
>>>>>  	class_destroy(fpga_image_load_class);
>>>>>  	WARN_ON(!xa_empty(&fpga_image_load_xa));
>>>>>  }
>>>>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>>>>> index a9cef9e1056b..b3d790e5d943 100644
>>>>> --- a/include/linux/fpga/fpga-image-load.h
>>>>> +++ b/include/linux/fpga/fpga-image-load.h
>>>>> @@ -7,22 +7,51 @@
>>>>>  #ifndef _LINUX_FPGA_IMAGE_LOAD_H
>>>>>  #define _LINUX_FPGA_IMAGE_LOAD_H
>>>>>  
>>>>> +#include <linux/cdev.h>
>>>>> +#include <linux/completion.h>
>>>>>  #include <linux/device.h>
>>>>>  #include <linux/mutex.h>
>>>>>  #include <linux/types.h>
>>>>> +#include <uapi/linux/fpga-image-load.h>
>>>>>  
>>>>>  struct fpga_image_load;
>>>>>  
>>>>>  /**
>>>>>   * struct fpga_image_load_ops - device specific operations
>>>>> + * @prepare:		    Required: Prepare secure update
>>>>> + * @write_blk:		    Required: Write a block of data
>>>>> + * @poll_complete:	    Required: Check for the completion of the
>>>>> + *			    HW authentication/programming process. This
>>>>> + *			    function should check for imgld->driver_unload
>>>>> + *			    and abort with FPGA_IMAGE_ERR_CANCELED when true.
>>>>> + * @cancel:		    Required: Signal HW to cancel update
>>>>> + * @cleanup:		    Optional: Complements the prepare()
>>>>> + *			    function and is called at the completion
>>>>> + *			    of the update, whether success or failure,
>>>>> + *			    if the prepare function succeeded.
>>>>>   */
>>>>>  struct fpga_image_load_ops {
>>>>> +	enum fpga_image_err (*prepare)(struct fpga_image_load *imgld);
>>>>> +	enum fpga_image_err (*write_blk)(struct fpga_image_load *imgld, u32 offset);
>>>>> +	enum fpga_image_err (*poll_complete)(struct fpga_image_load *imgld);
>>>>> +	enum fpga_image_err (*cancel)(struct fpga_image_load *imgld);
>>>>> +	void (*cleanup)(struct fpga_image_load *imgld);
>>>>>  };
>>>>>  
>>>>>  struct fpga_image_load {
>>>>>  	struct device dev;
>>>>> +	struct cdev cdev;
>>>>>  	const struct fpga_image_load_ops *lops;
>>>>>  	struct mutex lock;		/* protect data structure contents */
>>>>> +	unsigned long opened;
>>>>> +	struct work_struct work;
>>>>> +	struct completion update_done;
>>>>> +	const u8 *data;				/* pointer to update data */
>>>>> +	u32 remaining_size;			/* size remaining to transfer */
>>>>> +	enum fpga_image_prog progress;
>>>>> +	enum fpga_image_prog err_progress;	/* progress at time of failure */
>>>> This field is not used in this patch? So could you introduce it later?
>>> Yes - I'll move it. Thanks.
>>>>> +	enum fpga_image_err err_code;		/* image load error code */
>>>>> +	bool driver_unload;
>>>>>  	void *priv;
>>>>>  };
>>>>>  
>>>>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>>>>> new file mode 100644
>>>>> index 000000000000..4146a0a9e408
>>>>> --- /dev/null
>>>>> +++ b/include/uapi/linux/fpga-image-load.h
>>>>> @@ -0,0 +1,58 @@
>>>>> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
>>>>> +/*
>>>>> + * Header File for FPGA Image Load User API
>>>>> + *
>>>>> + * Copyright (C) 2019-2021 Intel Corporation, Inc.
>>>>> + *
>>>>> + */
>>>>> +
>>>>> +#ifndef _UAPI_LINUX_FPGA_IMAGE_LOAD_H
>>>>> +#define _UAPI_LINUX_FPGA_IMAGE_LOAD_H
>>>>> +
>>>>> +#include <linux/types.h>
>>>>> +#include <linux/ioctl.h>
>>>>> +
>>>>> +#define FPGA_IMAGE_LOAD_MAGIC 0xB9
>>>>> +
>>>>> +/* Image load progress codes */
>>>>> +enum fpga_image_prog {
>>>>> +	FPGA_IMAGE_PROG_IDLE,
>>>>> +	FPGA_IMAGE_PROG_STARTING,
>>>>> +	FPGA_IMAGE_PROG_PREPARING,
>>>>> +	FPGA_IMAGE_PROG_WRITING,
>>>>> +	FPGA_IMAGE_PROG_PROGRAMMING,
>>>>> +	FPGA_IMAGE_PROG_MAX
>>>>> +};
>>>>> +
>>>>> +/* Image error progress codes */
>>>>> +enum fpga_image_err {
>>>>> +	FPGA_IMAGE_ERR_NONE,
>>>>> +	FPGA_IMAGE_ERR_HW_ERROR,
>>>>> +	FPGA_IMAGE_ERR_TIMEOUT,
>>>>> +	FPGA_IMAGE_ERR_CANCELED,
>>>>> +	FPGA_IMAGE_ERR_BUSY,
>>>>> +	FPGA_IMAGE_ERR_INVALID_SIZE,
>>>>> +	FPGA_IMAGE_ERR_RW_ERROR,
>>>>> +	FPGA_IMAGE_ERR_WEAROUT,
>>>>> +	FPGA_IMAGE_ERR_MAX
>>>>> +};
>>>>> +
>>>>> +#define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>>>> Put the cmd word definition under the comments and parameter definition.
>>> OK
>>>>> +
>>>>> +/**
>>>>> + * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>>>>> + *				struct fpga_image_write)
>>>>> + *
>>>>> + * Upload a data buffer to the target device. The user must provide the
>>>>> + * data buffer, size, and an eventfd file descriptor.
>>>> I didn't find the eventfd.
>>> It is added in a later patch. I'll change the comment accordingly.
>>>
>>> Thanks,
>>> - Russ
>>>> Thanks,
>>>> Yilun
>>>>
>>>>> + *
>>>>> + * Return: 0 on success, -errno on failure.
>>>>> + */
>>>>> +struct fpga_image_write {
>>>>> +	/* Input */
>>>>> +	__u32 flags;		/* Zero for now */
>>>>> +	__u32 size;		/* Data size (in bytes) to be written */
>>>>> +	__u64 buf;		/* User space address of source data */
>>>>> +};
>>>>> +
>>>>> +#endif /* _UAPI_LINUX_FPGA_IMAGE_LOAD_H */
>>>>> -- 
>>>>> 2.25.1


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

* Re: [PATCH v15 6/6] fpga: image-load: enable cancel of image upload
  2021-09-13 10:00         ` Xu Yilun
@ 2021-09-21 20:43           ` Russ Weight
  0 siblings, 0 replies; 28+ messages in thread
From: Russ Weight @ 2021-09-21 20:43 UTC (permalink / raw)
  To: Xu Yilun, Tom Rix
  Cc: mdf, linux-fpga, linux-kernel, lgoncalv, hao.wu, matthew.gerlach



On 9/13/21 3:00 AM, Xu Yilun wrote:
> On Sat, Sep 11, 2021 at 06:13:08AM -0700, Tom Rix wrote:
>> On 9/10/21 4:38 PM, Russ Weight wrote:
>>> On 9/10/21 7:55 AM, Xu Yilun wrote:
>>>> On Wed, Sep 08, 2021 at 07:18:46PM -0700, Russ Weight wrote:
>>>>> Extend the FPGA Image Load class driver to include a cancel IOCTL that
>>>>> can be used to request that an image upload be canceled. The IOCTL may
>>>>> return EBUSY if it cannot be canceled by software or ENODEV if there
>>>>> is no update in progress.
>>>>>
>>>>> Signed-off-by: Russ Weight <russell.h.weight@intel.com>
>>>>> ---
>>>>> v15:
>>>>>   - Compare to previous patch:
>>>>>       [PATCH v14 6/6] fpga: sec-mgr: enable cancel of secure update
>>>>>   - Changed file, symbol, and config names to reflect the new driver name
>>>>>   - Cancel is now initiated by IOCT instead of sysfs
>>>>>   - Removed signed-off/reviewed-by tags
>>>>> v14:
>>>>>   - Updated ABI documentation date and kernel version
>>>>> v13:
>>>>>    - No change
>>>>> v12:
>>>>>    - Updated Date and KernelVersion fields in ABI documentation
>>>>> v11:
>>>>>    - No change
>>>>> v10:
>>>>>    - Rebased to 5.12-rc2 next
>>>>>    - Updated Date and KernelVersion in ABI documentation
>>>>> v9:
>>>>>    - Updated Date and KernelVersion in ABI documentation
>>>>> v8:
>>>>>    - No change
>>>>> v7:
>>>>>    - Changed Date in documentation file to December 2020
>>>>> v6:
>>>>>    - No change
>>>>> v5:
>>>>>    - No change
>>>>> v4:
>>>>>    - Changed from "Intel FPGA Security Manager" to FPGA Security Manager"
>>>>>      and removed unnecessary references to "Intel".
>>>>>    - Changed: iops -> sops, imgr -> smgr, IFPGA_ -> FPGA_, ifpga_ to fpga_
>>>>> v3:
>>>>>    - No change
>>>>> v2:
>>>>>    - Bumped documentation date and version
>>>>>    - Minor code cleanup per review comments
>>>>> ---
>>>>> ---
>>>>>   Documentation/fpga/fpga-image-load.rst |  6 ++++
>>>>>   drivers/fpga/fpga-image-load.c         | 45 +++++++++++++++++++++++---
>>>>>   include/linux/fpga/fpga-image-load.h   |  1 +
>>>>>   include/uapi/linux/fpga-image-load.h   |  1 +
>>>>>   4 files changed, 49 insertions(+), 4 deletions(-)
>>>>>
>>>>> diff --git a/Documentation/fpga/fpga-image-load.rst b/Documentation/fpga/fpga-image-load.rst
>>>>> index 3d5eb51223e3..763e7833a6ea 100644
>>>>> --- a/Documentation/fpga/fpga-image-load.rst
>>>>> +++ b/Documentation/fpga/fpga-image-load.rst
>>>>> @@ -37,3 +37,9 @@ FPGA_IMAGE_LOAD_STATUS:
>>>>>   Collect status for an on-going image upload. The status returned includes
>>>>>   how much data remains to be transferred, the progress of the image load,
>>>>>   and error information in the case of a failure.
>>>>> +
>>>>> +FPGA_IMAGE_LOAD_CANCEL:
>>>>> +
>>>>> +Request that a on-going image upload be cancelled. This IOCTL may return
>>>>> +EBUSY if it cannot be cancelled by software or ENODEV if there is no update
>>>>> +in progress.
>>>>> diff --git a/drivers/fpga/fpga-image-load.c b/drivers/fpga/fpga-image-load.c
>>>>> index 6ec0a39f07b3..c32e4b1ea35a 100644
>>>>> --- a/drivers/fpga/fpga-image-load.c
>>>>> +++ b/drivers/fpga/fpga-image-load.c
>>>>> @@ -46,6 +46,24 @@ static void fpga_image_dev_error(struct fpga_image_load *imgld,
>>>>>   	imgld->lops->cancel(imgld);
>>>>>   }
>>>>> +static int fpga_image_prog_transition(struct fpga_image_load *imgld,
>>>>> +				      enum fpga_image_prog new_progress)
>>>>> +{
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	mutex_lock(&imgld->lock);
>>>>> +	if (imgld->request_cancel) {
>>>>> +		imgld->err_progress = imgld->progress;
>>>>> +		imgld->err_code = FPGA_IMAGE_ERR_CANCELED;
>>>>> +		imgld->lops->cancel(imgld);
>>>>> +		ret = -ECANCELED;
>>>>> +	} else {
>>>>> +		imgld->progress = new_progress;
>>>>> +	}
>>>>> +	mutex_unlock(&imgld->lock);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>>   static void fpga_image_prog_complete(struct fpga_image_load *imgld)
>>>>>   {
>>>>>   	mutex_lock(&imgld->lock);
>>>>> @@ -77,8 +95,10 @@ static void fpga_image_do_load(struct work_struct *work)
>>>>>   		goto modput_exit;
>>>>>   	}
>>>>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_WRITING);
>>>>> -	while (imgld->remaining_size) {
>>>>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_WRITING))
>>>>> +		goto done;
>>>>> +
>>>>> +	while (imgld->remaining_size && !imgld->request_cancel) {
>>>>>   		ret = imgld->lops->write_blk(imgld, offset);
>>>>>   		if (ret != FPGA_IMAGE_ERR_NONE) {
>>>>>   			fpga_image_dev_error(imgld, ret);
>>>>> @@ -88,7 +108,9 @@ static void fpga_image_do_load(struct work_struct *work)
>>>>>   		offset = size - imgld->remaining_size;
>>>>>   	}
>>>>> -	fpga_image_update_progress(imgld, FPGA_IMAGE_PROG_PROGRAMMING);
>>>>> +	if (fpga_image_prog_transition(imgld, FPGA_IMAGE_PROG_PROGRAMMING))
>>>>> +		goto done;
>>>>> +
>>>>>   	ret = imgld->lops->poll_complete(imgld);
>>>>>   	if (ret != FPGA_IMAGE_ERR_NONE)
>>>>>   		fpga_image_dev_error(imgld, ret);
>>>>> @@ -159,6 +181,7 @@ static int fpga_image_load_ioctl_write(struct fpga_image_load *imgld,
>>>>>   	imgld->remaining_size = wb.size;
>>>>>   	imgld->err_code = FPGA_IMAGE_ERR_NONE;
>>>>>   	imgld->progress = FPGA_IMAGE_PROG_STARTING;
>>>>> +	imgld->request_cancel = false;
>>>>>   	reinit_completion(&imgld->update_done);
>>>>>   	schedule_work(&imgld->work);
>>>>>   	return 0;
>>>>> @@ -189,7 +212,7 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>>>   				  unsigned long arg)
>>>>>   {
>>>>>   	struct fpga_image_load *imgld = filp->private_data;
>>>>> -	int ret = -ENOTTY;
>>>>> +	int ret = 0;
>>>>>   	mutex_lock(&imgld->lock);
>>>>> @@ -200,6 +223,17 @@ static long fpga_image_load_ioctl(struct file *filp, unsigned int cmd,
>>>>>   	case FPGA_IMAGE_LOAD_STATUS:
>>>>>   		ret = fpga_image_load_ioctl_status(imgld, arg);
>>>>>   		break;
>>>>> +	case FPGA_IMAGE_LOAD_CANCEL:
>>>>> +		if (imgld->progress == FPGA_IMAGE_PROG_PROGRAMMING)
>>>>> +			ret = -EBUSY;
>>>>> +		else if (imgld->progress == FPGA_IMAGE_PROG_IDLE)
>>>>> +			ret = -ENODEV;
>>>>> +		else
>>>>> +			imgld->request_cancel = true;
>>>>> +		break;
>>>>> +	default:
>>>>> +		ret = -ENOTTY;
>>>>> +		break;
>>>>>   	}
>>>>>   	mutex_unlock(&imgld->lock);
>>>>> @@ -374,6 +408,9 @@ void fpga_image_load_unregister(struct fpga_image_load *imgld)
>>>>>   		goto unregister;
>>>>>   	}
>>>>> +	if (imgld->progress != FPGA_IMAGE_PROG_PROGRAMMING)
>>>>> +		imgld->request_cancel = true;
>>>>> +
>>>> Why we cancel the programing rather than waiting for programing done?
>>> This isn't new - it is the way the security manager was implemented.
>>>
>>> Updates can take up to 40 minutes for the N3000. If a person tries
>>> to unload the driver modules, should we hang for 40 minutes? Or should
>>> we try to cancel the update and allow the module to be unloaded?
>>> I think it is reasonable to cancel the update (if possible) under these
>>> circumstances. What do you think?
>> I do not think stalling a shutdown for 40 minutes is good.
>>
>> Is there a chance that cancelling would brick the card?
> If the cancel cannot be possible, may be we could wait. Or the user
> image would be corrupted with a simple poweroff command.

OK - I'm leaving the above code in for cancelling. The check for
FPGA_IMAGE_PROG_PROGRAMMING ensures that it can be safely canceled.
Once programming begins, then we will block until it is complete.
I'm still using the driver-unload flag to prevent new uploads from
starting, but driver_unload will no longer be used to force an exit
from the lower-level driver.

The maximum wait for programming is less than 40 minutes - probably
more like 20 minutes?

- Russ

>
> Thanks,
> Yilun
>
>> After restarting, can the hw detect an interrupted update ?
>>
>> Tom
>>
>>> - Russ
>>>
>>>> Thanks,
>>>> Yilun
>>>>
>>>>>   	mutex_unlock(&imgld->lock);
>>>>>   	wait_for_completion(&imgld->update_done);
>>>>> diff --git a/include/linux/fpga/fpga-image-load.h b/include/linux/fpga/fpga-image-load.h
>>>>> index 68f3105b51d2..4e51b9fd1724 100644
>>>>> --- a/include/linux/fpga/fpga-image-load.h
>>>>> +++ b/include/linux/fpga/fpga-image-load.h
>>>>> @@ -52,6 +52,7 @@ struct fpga_image_load {
>>>>>   	enum fpga_image_prog progress;
>>>>>   	enum fpga_image_prog err_progress;	/* progress at time of failure */
>>>>>   	enum fpga_image_err err_code;		/* image load error code */
>>>>> +	bool request_cancel;
>>>>>   	bool driver_unload;
>>>>>   	struct eventfd_ctx *finished;
>>>>>   	void *priv;
>>>>> diff --git a/include/uapi/linux/fpga-image-load.h b/include/uapi/linux/fpga-image-load.h
>>>>> index 6a995bcc0fb7..8d0dfa1f9b77 100644
>>>>> --- a/include/uapi/linux/fpga-image-load.h
>>>>> +++ b/include/uapi/linux/fpga-image-load.h
>>>>> @@ -39,6 +39,7 @@ enum fpga_image_err {
>>>>>   #define FPGA_IMAGE_LOAD_WRITE	_IOW(FPGA_IMAGE_LOAD_MAGIC, 0, struct fpga_image_write)
>>>>>   #define FPGA_IMAGE_LOAD_STATUS	_IOR(FPGA_IMAGE_LOAD_MAGIC, 1, struct fpga_image_status)
>>>>> +#define FPGA_IMAGE_LOAD_CANCEL	_IO(FPGA_IMAGE_LOAD_MAGIC, 2)
>>>>>   /**
>>>>>    * FPGA_IMAGE_LOAD_WRITE - _IOW(FPGA_IMAGE_LOAD_MAGIC, 0,
>>>>> -- 
>>>>> 2.25.1


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

end of thread, other threads:[~2021-09-21 20:44 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-09  2:18 [PATCH v15 0/6] FPGA Image Load (previously Security Manager) Russ Weight
2021-09-09  2:18 ` [PATCH v15 1/6] fpga: image-load: fpga image load class driver Russ Weight
2021-09-10  6:46   ` Xu Yilun
2021-09-10 20:47     ` Russ Weight
2021-09-09  2:18 ` [PATCH v15 2/6] fpga: image-load: enable image loads Russ Weight
2021-09-10  8:22   ` Xu Yilun
2021-09-10 23:18     ` Russ Weight
2021-09-13  6:48       ` Xu Yilun
2021-09-13  9:36         ` Xu Yilun
2021-09-21 19:08           ` Russ Weight
2021-09-09  2:18 ` [PATCH v15 3/6] fpga: image-load: signal eventfd when complete Russ Weight
2021-09-09  2:18 ` [PATCH v15 4/6] fpga: image-load: add status ioctl Russ Weight
2021-09-10  8:50   ` Xu Yilun
2021-09-10 23:23     ` Russ Weight
2021-09-11 18:12       ` Tom Rix
2021-09-13  8:24         ` Xu Yilun
2021-09-09  2:18 ` [PATCH v15 5/6] fpga: image-load: create status sysfs node Russ Weight
2021-09-10  8:52   ` Xu Yilun
2021-09-10 23:30     ` Russ Weight
2021-09-11 17:58       ` Tom Rix
2021-09-13  8:27         ` Xu Yilun
2021-09-09  2:18 ` [PATCH v15 6/6] fpga: image-load: enable cancel of image upload Russ Weight
2021-09-10 14:55   ` Xu Yilun
2021-09-10 23:38     ` Russ Weight
2021-09-11 13:13       ` Tom Rix
2021-09-13 10:00         ` Xu Yilun
2021-09-21 20:43           ` Russ Weight
     [not found] ` <20210912023739.4078-1-hdanton@sina.com>
2021-09-21 18:36   ` [PATCH v15 2/6] fpga: image-load: enable image loads Russ Weight

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).