Linux Documentation
 help / color / mirror / Atom feed
* Re: [RFC v2 0/2] add kconfirm
From: Jan Engelhardt @ 2026-05-10 23:28 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: Julian Braha, nathan, nsc, jani.nikula, akpm, gary, ljs, arnd,
	gregkh, masahiroy, ojeda, corbet, qingfang.deng, linux-kernel,
	rust-for-linux, linux-doc, linux-kbuild
In-Reply-To: <CANiq72kUD=s7VkOUBNFLbcASvDoO_qFXHziOcSFdDqtg5NXoUw@mail.gmail.com>


On Sunday 2026-05-10 11:49, Miguel Ojeda wrote:
>On Sun, May 10, 2026 at 7:06 AM Jan Engelhardt <ej@inai.de> wrote:
>>
>> Good lord, how is anyone supposed to review that amount –
>> or is it just getting rubberstamped anyway?
>
>[...] another option for that may be using the distribution's
>registry

That should really be the target. After all, Linux is not bundling
e.g. ncurses (for `make menuconfig`, if one wants to use it)
or libelf (mandatory for objtool and thus everything) either.

>the versions need to fit, one still needs to provide a way
>to do it for distributions that do not match, etc.

Linux, and many other projects, have run on a "The system version is
king" model for a long time. If libelf, binutils, gcc, libx11, or
whatever the dependency in question may be, the project trying to use
a dependency would add a few-liner patch to broaden the accepted
range, rather than trying to re-provide the dependency as a whole.

^ permalink raw reply

* Re: [RFC v2 0/2] add kconfirm
From: Julian Braha @ 2026-05-10 23:06 UTC (permalink / raw)
  To: Miguel Ojeda, Jan Engelhardt
  Cc: nathan, nsc, jani.nikula, akpm, gary, ljs, arnd, gregkh,
	masahiroy, ojeda, corbet, qingfang.deng, linux-kernel,
	rust-for-linux, linux-doc, linux-kbuild
In-Reply-To: <CANiq72kUD=s7VkOUBNFLbcASvDoO_qFXHziOcSFdDqtg5NXoUw@mail.gmail.com>

On 5/10/26 10:49, Miguel Ojeda wrote:
> In any case, when we discussed offline building in v1, that did not
> necessarily mean vendoring every dependency manually into the tree,
> but rather let the user set up the dependencies before (i.e.
> connecting is fine) so that then the actual `make` steps can proceed
> offline. For instance, using `cargo vendor`
Hi Miguel, thanks for clarifying. I think I like this path (user
downloads dependencies themselves, outside of `make`) the most. But I'll
wait to see other opinions.

In this second submission, I had attempted to filter out non-linux
platform code and tests, docs, etc. from the dependencies by using
cargo-vendor-filterer, but yeah, it seems to have missed some things.

So in RFC v3, I'll likely use this design of having users run `cargo
vendor` first.

> By the way, another option for that may be using the distribution's
> registry (e.g. Debian and Fedora provide one through the package
> manager).

Unfortunately, it seems that there's no built-in way to fall back for
other distros:
https://github.com/rust-lang/cargo/issues/3066

The workaround could be to create various Cargo config.toml files, and
instruct users that, for example, if they want to use the debian
packages, they can download their dependencies using:
`cargo vendor --config debian.toml`
But I need to test this and confirm first since I don't use any of these
distros.

I appreciate your thorough feedback!

- Julian Braha

^ permalink raw reply

* [PATCH RFC v3 0/3] Add splash DRM client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla

Hello,

this is the third (and hopefully last) RFC version for the DRM-based
splash screen.

Motivation behind the work can be found in v1 [0]; in a nutshell, the
splash DRM client can draw a splashscreen using:

  - the BMP image supplied by the EFI BGRT;
  - a BMP image loaded as firmware (either built-in or loaded from the
    filesystem);
  - a colored background.

This revision greatly simplifies the image seletion logic; now the EFI
BGRT is always used as first source if enabled, with a fallback to BMP
image loaded as firmware and then to a plain color.

Sanity checks on the EFI BGRT image have been borrowed from the efifb
driver. More complete splash providers (e.g.: Plymouth) have an
extensive management of platform-specific quirks, but I don't think it
would be reasonable to introduce such complexity here.

Additional notes:
  - Rotation is still not managed (and probably won't?).
  - Support for tiled screens is untested.
  - Plain color and BMP sources were tested on QEMU, Beagleplay and
    i.MX93 FRDM.
  - EFI BGRT support was tested using QEMU+OVMF.

Thank you in advance for any feedback.

Best regards,
Francesco

[0] https://lore.kernel.org/all/20251027-drm_client_splash-v1-0-00698933b34a@valla.it

Signed-off-by: Francesco Valla <francesco@valla.it>
---
Changes in v3:
  - Simplified the image selection and management logic, with direct
    fallback from EFI BGRT to MP as firmware
  - Used new drm_draw_can_convert_from_xrgb8888() API
  - Added proper get_unaligned_ calls for EFI BGRT access
  - Fixed Kconfig dependencies
  - Link to v2: https://lore.kernel.org/r/20260106-drm_client_splash-v2-0-6e86a7434b59@valla.it

Changes in v2:
  - Moved from raw dump to BMP format for static image source
  - Removed support for configurable message
  - Removed support for progress bar
  - Added EFI BGRT as image source
Link to v1: https://lore.kernel.org/r/20251027-drm_client_splash-v1-0-00698933b34a@valla.it

To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
To: Maxime Ripard <mripard@kernel.org>
To: Thomas Zimmermann <tzimmermann@suse.de>
To: David Airlie <airlied@gmail.com>
To: Simona Vetter <simona@ffwll.ch>
To: Francesco Valla <francesco@valla.it>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux-kernel@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-doc@vger.kernel.org

---
Francesco Valla (3):
      drm: client: add splash client
      MAINTAINERS: add entry for DRM splash client
      drm: docs: remove bootsplash from TODO

 Documentation/gpu/todo.rst                    |  17 -
 MAINTAINERS                                   |   7 +
 drivers/gpu/drm/clients/Kconfig               |  63 +-
 drivers/gpu/drm/clients/Makefile              |   1 +
 drivers/gpu/drm/clients/drm_client_internal.h |   9 +
 drivers/gpu/drm/clients/drm_client_setup.c    |   8 +
 drivers/gpu/drm/clients/drm_splash.c          | 932 ++++++++++++++++++++++++++
 7 files changed, 1019 insertions(+), 18 deletions(-)
---
base-commit: afaa0a477099cb7256e26fe11289c753a225ac97
change-id: 20251026-drm_client_splash-e10d7d663e7f

Best regards,
--  
Francesco Valla <francesco@valla.it>


^ permalink raw reply

* [PATCH RFC v3 3/3] drm: docs: remove bootsplash from TODO
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Now that a splash client exists, remove the bootsplash task from the
TODO list for the DRM subsystem.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 Documentation/gpu/todo.rst | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index bc9f14c8a2ec..f367d8980a87 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -802,23 +802,6 @@ See drivers/gpu/drm/amd/display/TODO for tasks.
 
 Contact: Harry Wentland, Alex Deucher
 
-Bootsplash
-==========
-
-There is support in place now for writing internal DRM clients making it
-possible to pick up the bootsplash work that was rejected because it was written
-for fbdev.
-
-- [v6,8/8] drm/client: Hack: Add bootsplash example
-  https://patchwork.freedesktop.org/patch/306579/
-
-- [RFC PATCH v2 00/13] Kernel based bootsplash
-  https://lore.kernel.org/r/20171213194755.3409-1-mstaudt@suse.de
-
-Contact: Sam Ravnborg
-
-Level: Advanced
-
 Brightness handling on devices with multiple internal panels
 ============================================================
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH RFC v3 1/3] drm: client: add splash client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Add a DRM client that draws a simple splash, with possibility to show:

  - the logo provided by EFI BGRT;
  - a static BMP image (loaded as firmware);
  - a colored background.

The client is not meant to replace a full-featured bootsplash, but
rather to remove some complexity (and hopefully boot time) on small
embedded platforms or on systems with a limited scope (e.g: recovery
or manufacturing images).

The background color can be set either at build time from a dedicated
config option or at runtime through the drm_client_lib.splash_color
command line parameter. Any color in RGB888 format can be used.

If enabled, the static BMP image is loaded using the kernel firmware
infrastructure; a valid BMP image with 24bpp color and no compression
is expected. The name of the image can be set through the
drm_client_lib.splash_bmp kernel command line parameter, with the
default being 'drm_splash.bmp'.

Just like the existing DRM clients, the splash can be enabled from the
kernel command line using drm_client_lib.active=splash.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 drivers/gpu/drm/clients/Kconfig               |  63 +-
 drivers/gpu/drm/clients/Makefile              |   1 +
 drivers/gpu/drm/clients/drm_client_internal.h |   9 +
 drivers/gpu/drm/clients/drm_client_setup.c    |   8 +
 drivers/gpu/drm/clients/drm_splash.c          | 932 ++++++++++++++++++++++++++
 5 files changed, 1012 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/clients/Kconfig b/drivers/gpu/drm/clients/Kconfig
index 6096c623d9d5..dc114429c1f1 100644
--- a/drivers/gpu/drm/clients/Kconfig
+++ b/drivers/gpu/drm/clients/Kconfig
@@ -12,6 +12,7 @@ config DRM_CLIENT_LIB
 config DRM_CLIENT_SELECTION
 	tristate
 	depends on DRM
+	select DRM_CLIENT_LIB if DRM_CLIENT_SPLASH
 	select DRM_CLIENT_LIB if DRM_CLIENT_LOG
 	select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION
 	help
@@ -85,10 +86,63 @@ config DRM_CLIENT_LOG
 	  If you only need logs, but no terminal, or if you prefer userspace
 	  terminal, say "Y".
 
+config DRM_CLIENT_SPLASH
+	bool "Display graphic splash"
+	depends on DRM_CLIENT_SELECTION
+	select DRM_CLIENT
+	select DRM_CLIENT_SETUP
+	select DRM_DRAW
+	help
+	  This enables a splash drm client, able to display either a plain
+	  color or a static image until the userspace is ready to take over.
+	  The splash will be displayed on all screens available at boot, if
+	  any, or on the ones part of the first hotplug event.
+
+config DRM_CLIENT_SPLASH_BACKGROUND_COLOR
+	hex "Splash background color"
+	depends on DRM_CLIENT_SPLASH
+	default 0x000000
+	help
+	  The default splash background color, in RGB888 format.
+
+	  The color can be overridden through the drm_client_lib.splash_color
+	  kernel command line parameter.
+
+config DRM_CLIENT_SPLASH_SRC_BGRT
+	bool "EFI BGRT as splash source"
+	depends on DRM_CLIENT_SPLASH
+	depends on ACPI_BGRT
+	select DRM_CLIENT_SPLASH_BMP_SUPPORT
+	help
+	  Use the BGRT image provided by the EFI bootloader. If the image is
+	  smaller than the display(s), it will be centered and the color
+	  specified through the DRM_CLIENT_SPLASH_BACKGROUND_COLOR config
+	  option will be used as background.
+
+config DRM_CLIENT_SPLASH_SRC_BMP
+	bool "BMP image as splash source"
+	depends on DRM_CLIENT_SPLASH
+	select DRM_CLIENT_SPLASH_BMP_SUPPORT
+	select FW_LOADER
+	help
+	  Use a BMP (bitmap) image as splash. If the image is smaller than the
+	  display(s), it will be centered and the color specified through the
+	  DRM_CLIENT_SPLASH_BACKGROUND_COLOR config option will be used as
+	  background.
+
+	  The image will be loaded using the firmware loading facility the
+	  kernel provides; it shall use 24 bits per pixel and shall not be
+	  compressed. The name of the file can be set through the
+	  drm_client_lib.splash_bmp command line parameter, with the default
+	  being 'drm_splash.bmp'.
+
+config DRM_CLIENT_SPLASH_BMP_SUPPORT
+	bool
+
 choice
 	prompt "Default DRM Client"
 	depends on DRM_CLIENT_SELECTION
-	depends on DRM_FBDEV_EMULATION || DRM_CLIENT_LOG
+	depends on DRM_FBDEV_EMULATION || DRM_CLIENT_LOG || DRM_CLIENT_SPLASH
 	default DRM_CLIENT_DEFAULT_FBDEV
 	help
 	  Selects the default drm client.
@@ -111,6 +165,12 @@ config DRM_CLIENT_DEFAULT_LOG
 	  screen, but doesn't implement a full terminal. For that you will need
 	  a userspace terminal using drm/kms.
 
+config DRM_CLIENT_DEFAULT_SPLASH
+	bool "splash"
+	depends on DRM_CLIENT_SPLASH
+	help
+	  Use splash as default drm client.
+
 endchoice
 
 config DRM_CLIENT_DEFAULT
@@ -118,6 +178,7 @@ config DRM_CLIENT_DEFAULT
        depends on DRM_CLIENT
        default "fbdev" if DRM_CLIENT_DEFAULT_FBDEV
        default "log" if DRM_CLIENT_DEFAULT_LOG
+       default "splash" if DRM_CLIENT_DEFAULT_SPLASH
        default ""
 
 endmenu
diff --git a/drivers/gpu/drm/clients/Makefile b/drivers/gpu/drm/clients/Makefile
index c16addbc327f..3df02d10cd18 100644
--- a/drivers/gpu/drm/clients/Makefile
+++ b/drivers/gpu/drm/clients/Makefile
@@ -5,4 +5,5 @@ subdir-ccflags-y += -I$(src)/..
 drm_client_lib-y := drm_client_setup.o
 drm_client_lib-$(CONFIG_DRM_CLIENT_LOG) += drm_log.o
 drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o
+drm_client_lib-$(CONFIG_DRM_CLIENT_SPLASH) += drm_splash.o
 obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o
diff --git a/drivers/gpu/drm/clients/drm_client_internal.h b/drivers/gpu/drm/clients/drm_client_internal.h
index 6dc078bf6503..48ee0c1c2529 100644
--- a/drivers/gpu/drm/clients/drm_client_internal.h
+++ b/drivers/gpu/drm/clients/drm_client_internal.h
@@ -22,4 +22,13 @@ void drm_log_register(struct drm_device *dev);
 static inline void drm_log_register(struct drm_device *dev) {}
 #endif
 
+#ifdef CONFIG_DRM_CLIENT_SPLASH
+void drm_splash_register(struct drm_device *dev,
+			 const struct drm_format_info *format);
+#else
+static inline void drm_splash_register(struct drm_device *dev,
+				       const struct drm_format_info *format)
+{}
+#endif
+
 #endif
diff --git a/drivers/gpu/drm/clients/drm_client_setup.c b/drivers/gpu/drm/clients/drm_client_setup.c
index 515aceac22b1..c19498938ee3 100644
--- a/drivers/gpu/drm/clients/drm_client_setup.c
+++ b/drivers/gpu/drm/clients/drm_client_setup.c
@@ -56,6 +56,14 @@ void drm_client_setup(struct drm_device *dev, const struct drm_format_info *form
 		return;
 	}
 #endif
+
+#ifdef CONFIG_DRM_CLIENT_SPLASH
+	if (!strcmp(drm_client_default, "splash")) {
+		drm_splash_register(dev, format);
+		return;
+	}
+#endif
+
 	if (strcmp(drm_client_default, ""))
 		drm_warn(dev, "Unknown DRM client %s\n", drm_client_default);
 }
diff --git a/drivers/gpu/drm/clients/drm_splash.c b/drivers/gpu/drm/clients/drm_splash.c
new file mode 100644
index 000000000000..a038807019c0
--- /dev/null
+++ b/drivers/gpu/drm/clients/drm_splash.c
@@ -0,0 +1,932 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/*
+ * Copyright (c) 2025-2026 Francesco Valla <francesco@valla.it>
+ */
+
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/efi-bgrt.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/iosys-map.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include <acpi/actbl1.h>
+
+#include <drm/drm_client.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "drm_client_internal.h"
+#include "drm_draw_internal.h"
+#include "drm_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * This is a simple graphic bootsplash, able to display either a plain color or
+ * a static image.
+ */
+
+static unsigned int splash_color = CONFIG_DRM_CLIENT_SPLASH_BACKGROUND_COLOR;
+module_param(splash_color, uint, 0400);
+MODULE_PARM_DESC(splash_color, "Splash background color (RGB888)");
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP)
+#define DEFAULT_SPLASH_BMP "drm_splash.bmp"
+static char *splash_bmp = DEFAULT_SPLASH_BMP;
+module_param(splash_bmp, charp, 0400);
+MODULE_PARM_DESC(splash_bmp, "Name of splash image (default: \"" DEFAULT_SPLASH_BMP "\")");
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BMP
+
+#define BMP_FILE_MAGIC_ID 0x4d42
+
+/* BMP header structures copied from drivers/video/fbdev/efifb.c */
+struct bmp_file_header {
+	__le16 id;
+	__le32 file_size;
+	__le32 reserved;
+	__le32 bitmap_offset;
+} __packed;
+
+struct bmp_dib_header {
+	__le32 dib_header_size;
+	__le32 width;
+	__le32 height;
+	__le16 planes;
+	__le16 bpp;
+	__le32 compression;
+	__le32 bitmap_size;
+	__le32 horz_resolution;
+	__le32 vert_resolution;
+	__le32 colors_used;
+	__le32 colors_important;
+} __packed;
+
+struct drm_splash_scanout {
+	int id;
+	u32 format;
+	unsigned int width;
+	unsigned int height;
+	struct drm_client_buffer *buffer;
+	bool bg_drawn;
+	bool img_drawn;
+};
+
+struct drm_splash {
+	struct drm_client_dev client;
+	u32 preferred_format;
+	struct device dev;
+
+	struct task_struct *thread;
+	atomic_t pending;
+
+	struct mutex hotplug_lock;
+	bool initialized;
+
+	u32 n_scanout;
+	struct drm_splash_scanout *scanout;
+
+	spinlock_t fw_lock;
+	const struct firmware *fw;
+	void *map_data;
+
+	bool use_bgrt;
+};
+
+static struct drm_splash *client_to_drm_splash(struct drm_client_dev *client)
+{
+	return container_of_const(client, struct drm_splash, client);
+}
+
+static struct drm_splash_scanout *
+get_scanout_from_tile_group(struct drm_splash *splash, int id)
+{
+	int j;
+
+	for (j = 0; j < splash->n_scanout; j++)
+		if (splash->scanout[j].id == id)
+			return &splash->scanout[j];
+
+	return NULL;
+}
+
+static inline void drm_splash_wake_render_thread(struct drm_splash *splash)
+{
+	wake_up_process(splash->thread);
+}
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP)
+static int drm_splash_fw_load(struct drm_splash *splash, const u8 **data,
+			      size_t *size)
+{
+	const struct firmware *fw;
+
+	scoped_guard(spinlock, &splash->fw_lock)
+		fw = splash->fw;
+
+	if (!fw)
+		return -ENOENT;
+
+	*data = fw->data;
+	*size = fw->size;
+
+	return 0;
+}
+
+static void drm_splash_fw_callback(const struct firmware *fw, void *context)
+{
+	struct drm_splash *splash = context;
+	struct drm_client_dev *client = &splash->client;
+
+	if (!fw || !fw->data) {
+		drm_err(client->dev, "splash: no firmware");
+		return;
+	}
+
+	scoped_guard(spinlock, &splash->fw_lock)
+		splash->fw = fw;
+
+	/* Wake the render thread */
+	drm_dbg(client->dev, "splash: firmware loaded, wake up drawing thread");
+	atomic_set(&splash->pending, 1);
+	drm_splash_wake_render_thread(splash);
+}
+
+static int drm_splash_fw_request_bmp(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	drm_info(client->dev, "splash: request %s as firmware", splash_bmp);
+
+	return request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+				       splash_bmp, client->dev->dev, GFP_KERNEL,
+				       splash, drm_splash_fw_callback);
+}
+#else
+static inline int drm_splash_fw_load(struct drm_splash *splash, const u8 **data,
+				     size_t *size)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int drm_splash_fw_request_bmp(struct drm_splash *splash)
+{
+	return -EOPNOTSUPP;
+}
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BMP
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT)
+static bool drm_splash_bgrt_available(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	if (!bgrt_tab.image_address) {
+		drm_info(client->dev, "splash: no BGRT found");
+		return false;
+	}
+
+	if (bgrt_tab.status & ACPI_BGRT_ORIENTATION_OFFSET) {
+		drm_info(client->dev, "splash: BGRT rotation bits set, skipping");
+		return false;
+	}
+
+	return true;
+}
+
+static inline unsigned int drm_splash_bgrt_get_xoffset(void)
+{
+	return bgrt_tab.image_offset_x;
+}
+
+static inline unsigned int drm_splash_bgrt_get_yoffset(void)
+{
+	return bgrt_tab.image_offset_y;
+}
+
+static int drm_splash_bgrt_load(struct drm_splash *splash, const u8 **data,
+				size_t *size)
+{
+	struct drm_client_dev *client = &splash->client;
+
+	if (!drm_splash_bgrt_available(splash))
+		return -ENOENT;
+
+	drm_dbg(client->dev, "splash: BGRT image is at 0x%016llx, size=%zX",
+		bgrt_tab.image_address, bgrt_image_size);
+
+	splash->map_data = memremap(bgrt_tab.image_address, bgrt_image_size,
+				    MEMREMAP_WB);
+	if (!splash->map_data) {
+		drm_warn(client->dev, "splash: failed to map BGRT image memory");
+		return -ENOMEM;
+	}
+
+	*data = splash->map_data;
+	*size = bgrt_image_size;
+
+	return 0;
+}
+#else
+static inline bool drm_splash_bgrt_available(struct drm_splash *splash)
+{
+	return false;
+}
+
+static inline unsigned int drm_splash_bgrt_get_xoffset(void)
+{
+	return 0;
+}
+
+static inline unsigned int drm_splash_bgrt_get_yoffset(void)
+{
+	return 0;
+}
+
+static inline int drm_splash_bgrt_load(struct drm_splash *splash,
+				       const u8 **data, size_t *size)
+{
+	return -EOPNOTSUPP;
+}
+#endif // CONFIG_DRM_CLIENT_SPLASH_SRC_BGRT
+
+static u32 drm_splash_find_usable_format(struct drm_plane *plane,
+					 u32 preferred_format)
+{
+	int i;
+
+	/* Check if the preferred format can be used */
+	for (i = 0; i < plane->format_count; i++)
+		if (plane->format_types[i] == preferred_format)
+			return preferred_format;
+
+	/* Otherwise, find the first format that can be converted from XRGB8888 */
+	for (i = 0; i < plane->format_count; i++)
+		if (drm_draw_can_convert_from_xrgb8888(plane->format_types[i]))
+			return plane->format_types[i];
+
+	return DRM_FORMAT_INVALID;
+}
+
+static void drm_splash_fill(struct iosys_map *map, unsigned int dst_pitch,
+			    unsigned int height, unsigned int width,
+			    u32 px_width, u32 color)
+{
+	switch (px_width) {
+	case 2:
+		drm_draw_fill16(map, dst_pitch, height, width, color);
+		break;
+	case 3:
+		drm_draw_fill24(map, dst_pitch, height, width, color);
+		break;
+	case 4:
+		drm_draw_fill32(map, dst_pitch, height, width, color);
+		break;
+	default:
+		WARN_ONCE(1, "Can't fill with pixel width %d", px_width);
+	}
+}
+
+static int drm_splash_fill_solid_color(struct drm_client_buffer *buffer,
+				       u32 color)
+{
+	struct drm_client_dev *client = buffer->client;
+	struct drm_framebuffer *fb = buffer->fb;
+	struct drm_rect r = DRM_RECT_INIT(0, 0, fb->width, fb->height);
+	u32 px_width = fb->format->cpp[0];
+	struct iosys_map map;
+	int ret;
+
+	ret = drm_client_buffer_vmap_local(buffer, &map);
+	if (ret) {
+		drm_err(client->dev, "splash: cannot vmap buffer: %d", ret);
+		return ret;
+	}
+
+	drm_splash_fill(&map, fb->pitches[0], drm_rect_height(&r),
+			drm_rect_width(&r), px_width, color);
+
+	drm_client_buffer_vunmap_local(buffer);
+
+	return drm_client_buffer_flush(buffer, &r);
+}
+
+#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_BMP_SUPPORT)
+static void drm_splash_blit_pix16(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * sizeof(u16);
+
+			iosys_map_wr(map, wr_off, u16, dcolor);
+		}
+	}
+}
+
+static void drm_splash_blit_pix24(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * 3;
+
+			iosys_map_wr(map, wr_off, u8, (dcolor & 0x000000FF) >> 0);
+			iosys_map_wr(map, wr_off + 1, u8, (dcolor & 0x0000FF00) >> 8);
+			iosys_map_wr(map, wr_off + 2, u8, (dcolor & 0x00FF0000) >> 16);
+		}
+	}
+}
+
+static void drm_splash_blit_pix32(struct iosys_map *map, unsigned int dpitch,
+				  unsigned int x_pad, unsigned int y_pad,
+				  const u8 *sbuf8, unsigned int spitch,
+				  unsigned int width, unsigned int height,
+				  bool invert_y, u32 format)
+{
+	unsigned int x, y, src_offset, dst_offset;
+	u32 scolor, dcolor, wr_off;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch;
+
+		for (x = 0; x < width; x++) {
+			scolor = *(const u32 *)(&sbuf8[src_offset + 3 * x]);
+			dcolor = drm_draw_color_from_xrgb8888(scolor, format);
+			wr_off = dst_offset + (x_pad + x) * sizeof(u32);
+
+			iosys_map_wr(map, wr_off, u32, dcolor);
+		}
+	}
+}
+
+static void drm_splash_blit_rgb888(struct iosys_map *map, unsigned int dpitch,
+				   unsigned int x_pad, unsigned int y_pad,
+				   const u8 *sbuf8, unsigned int spitch,
+				   unsigned int width, unsigned int height,
+				   bool invert_y)
+{
+	unsigned int y, src_offset, dst_offset;
+
+	for (y = 0; y < height; y++) {
+		src_offset = (invert_y ? (height - y - 1) : y) * spitch;
+		dst_offset = (y_pad + y) * dpitch + x_pad * 3;
+
+		iosys_map_memcpy_to(map, dst_offset, &sbuf8[src_offset], width * 3);
+	}
+}
+
+static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
+				     struct drm_splash_scanout *scanout,
+				     const u8 *data, size_t data_len)
+
+{
+	struct drm_client_buffer *buffer = scanout->buffer;
+	struct drm_client_dev *client = buffer->client;
+	struct drm_framebuffer *fb = buffer->fb;
+	u32 px_width = fb->format->cpp[0];
+	const struct bmp_file_header *file_header;
+	const struct bmp_dib_header *dib_header;
+	u32 dib_header_size;
+	u16 bmp_id, bmp_bpp, bmp_planes;
+	u32 bmp_compression, bmp_pitch;
+	s32 bmp_width, bmp_height;
+	bool bmp_invert_y;
+	u32 bitmap_offset;
+	unsigned int x_pad, y_pad;
+	const u8 *image_data;
+	struct iosys_map map;
+	struct drm_rect r;
+	int ret;
+
+	if (data_len < (sizeof(*file_header) + sizeof(*dib_header))) {
+		drm_err(client->dev, "splash: BMP file too short");
+		return -EINVAL;
+	}
+
+	file_header = (const struct bmp_file_header *)data;
+
+	bmp_id = get_unaligned_le16(&file_header->id);
+	if (bmp_id != BMP_FILE_MAGIC_ID) {
+		drm_err(client->dev, "splash: invalid BMP magic 0x%04X", bmp_id);
+		return -EINVAL;
+	}
+
+	bitmap_offset = get_unaligned_le32(&file_header->bitmap_offset);
+
+	dib_header = (const struct bmp_dib_header *)(data + sizeof(*file_header));
+
+	dib_header_size = get_unaligned_le32(&dib_header->dib_header_size);
+
+	bmp_width = (s32)get_unaligned_le32(&dib_header->width);
+	bmp_height = (s32)get_unaligned_le32(&dib_header->height);
+	bmp_planes = get_unaligned_le16(&dib_header->planes);
+	bmp_bpp = get_unaligned_le16(&dib_header->bpp);
+	bmp_compression = get_unaligned_le32(&dib_header->compression);
+
+	/* Restrict supported format to uncompressed, 24bit RGB888 */
+	if (dib_header_size != 40 || bmp_width < 0 || bmp_planes != 1 ||
+	    bmp_compression != 0 || bmp_bpp != 24) {
+		drm_err(client->dev, "splash: invalid BMP format");
+		return -EINVAL;
+	}
+
+	bmp_pitch = round_up(3 * bmp_width, 4);
+
+	/* A positive height means bottom-to-top scan direction */
+	bmp_invert_y = (bmp_height > 0);
+	bmp_height = abs(bmp_height);
+
+	if ((bitmap_offset + bmp_pitch * bmp_height) > data_len) {
+		drm_err(client->dev, "splash: invalid BMP size");
+		return -EINVAL;
+	}
+
+	if (bmp_width > scanout->width || bmp_height > scanout->height) {
+		drm_err(client->dev, "splash: BMP image is too big for the screen");
+		return -EINVAL;
+	}
+
+	if (splash->use_bgrt) {
+		x_pad = drm_splash_bgrt_get_xoffset();
+		y_pad = drm_splash_bgrt_get_yoffset();
+
+		if ((x_pad + bmp_width) > scanout->width ||
+		    (y_pad + bmp_height) > scanout->height) {
+			drm_err(client->dev, "splash: BGRT image would overflow");
+			return -EINVAL;
+		}
+
+#ifdef CONFIG_X86
+		/*
+		 * BGRT sanity check, taken from efifb.c:
+		 *
+		 * On x86 some firmwares use a low non native resolution for
+		 * the display when they have shown some text messages. While
+		 * keeping the bgrt filled with info for the native resolution.
+		 * If the bgrt image intended for the native resolution still
+		 * fits, it will be displayed very close to the right edge of
+		 * the display looking quite bad.
+		 */
+
+		if (x_pad != (scanout->width - bmp_width) / 2) {
+			drm_err(client->dev, "splash: BGRT sanity check failed");
+			return -EINVAL;
+		}
+#endif
+	} else {
+		/* Center X and Y */
+		x_pad = (scanout->width - bmp_width) / 2;
+		y_pad = (scanout->height - bmp_height) / 2;
+	}
+
+	image_data = data + bitmap_offset;
+
+	ret = drm_client_buffer_vmap_local(buffer, &map);
+	if (ret) {
+		drm_err(client->dev, "splash: cannot vmap buffer: %d", ret);
+		return ret;
+	}
+
+	r = DRM_RECT_INIT(x_pad, y_pad, bmp_width, bmp_height);
+
+	/* In case the target format is RGB888, source data can be copied to
+	 * the video buffer line by line, avoiding some overhead.
+	 */
+	if (scanout->format == DRM_FORMAT_RGB888) {
+		drm_splash_blit_rgb888(&map, fb->pitches[0], x_pad, y_pad,
+				       image_data, bmp_pitch, bmp_width,
+				       bmp_height, bmp_invert_y);
+	} else {
+		switch (px_width) {
+		case 2:
+			drm_splash_blit_pix16(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		case 3:
+			drm_splash_blit_pix24(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		case 4:
+			drm_splash_blit_pix32(&map, fb->pitches[0], x_pad,
+					      y_pad, image_data, bmp_pitch,
+					      bmp_width, bmp_height,
+					      bmp_invert_y, scanout->format);
+			break;
+		default:
+			drm_warn_once(client->dev,
+				      "splash: can't blit with pixel width %d",
+				      px_width);
+		}
+	}
+
+	drm_client_buffer_vunmap_local(buffer);
+
+	return drm_client_buffer_flush(buffer, &r);
+}
+#else
+static inline int drm_splash_bmp_to_scanout(struct drm_splash *splash,
+					    struct drm_splash_scanout *scanout,
+					    const u8 *data, size_t data_len)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+static int drm_splash_image_load(struct drm_splash *splash, const u8 **img_data,
+				 size_t *img_data_len)
+{
+	int ret = 0;
+
+	if (splash->use_bgrt) {
+		ret = drm_splash_bgrt_load(splash, img_data, img_data_len);
+		if (ret)
+			splash->use_bgrt = false;
+	}
+
+	/* BGRT failed to load */
+	if (!splash->use_bgrt)
+		ret = drm_splash_fw_load(splash, img_data, img_data_len);
+
+	return ret;
+}
+
+static void drm_splash_image_cleanup(struct drm_splash *splash)
+{
+	const struct firmware *fw = NULL;
+
+	memunmap(splash->map_data);
+
+	scoped_guard(spinlock, &splash->fw_lock) {
+		fw = splash->fw;
+		splash->fw = NULL;
+	}
+
+	release_firmware(fw);
+}
+
+static int drm_splash_render_thread(void *data)
+{
+	struct drm_splash *splash = data;
+	struct drm_client_dev *client = &splash->client;
+
+	while (!kthread_should_stop()) {
+		unsigned int draw_count = 0;
+		const u8 *img_data = NULL;
+		size_t img_data_len = 0;
+		bool img_loaded;
+		int i, ret;
+
+		drm_dbg(client->dev, "splash: run render thread...");
+
+		ret = drm_splash_image_load(splash, &img_data, &img_data_len);
+		img_loaded = (ret == 0);
+
+		for (i = 0; i < splash->n_scanout; i++) {
+			struct drm_splash_scanout *scanout = &splash->scanout[i];
+
+			if (!scanout->buffer) {
+				drm_err(client->dev,
+					"splash: no buffer for scanout %d", i);
+				continue;
+			}
+
+			if (!scanout->bg_drawn) {
+				drm_dbg(client->dev, "draw background for scanout %d", i);
+				u32 color = drm_draw_color_from_xrgb8888(splash_color,
+									 scanout->format);
+				drm_splash_fill_solid_color(scanout->buffer, color);
+				scanout->bg_drawn = true;
+			}
+
+			if (img_loaded && !scanout->img_drawn) {
+				drm_dbg(client->dev, "draw image for scanout %d", i);
+				/* Ignore the return value, since the solid
+				 * color has already been drawn to screen.
+				 */
+				ret = drm_splash_bmp_to_scanout(splash, scanout,
+								img_data,
+								img_data_len);
+				scanout->img_drawn = (ret == 0);
+			}
+
+			draw_count++;
+		}
+
+		if (img_loaded)
+			drm_splash_image_cleanup(splash);
+
+		if (draw_count > 0) {
+			ret = drm_client_modeset_commit(client);
+			/* If commit returns EBUSY, another master showed up.
+			 * This means that the splash is no more required.
+			 */
+			if (ret == -EBUSY) {
+				drm_info(client->dev,
+					"splash: not master anymore, exiting");
+				break;
+			}
+		}
+
+		if (img_loaded)
+			break;
+
+		/* If no changes arrived in the mean time, wait to be awaken,
+		 * e.g.by a firmware callback.
+		 */
+		if (atomic_xchg(&splash->pending, 0) == 0)
+			set_current_state(TASK_UNINTERRUPTIBLE);
+
+		schedule();
+	}
+
+	return 0;
+}
+
+static int drm_splash_init_client(struct drm_splash *splash)
+{
+	struct drm_client_dev *client = &splash->client;
+	struct drm_mode_set *modeset;
+	unsigned int modeset_mask = 0;
+	unsigned int fb_count = 0;
+	int j;
+
+	if (drm_client_modeset_probe(client, 0, 0))
+		return -1;
+
+	j = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		struct drm_splash_scanout *tmp;
+		struct drm_splash_scanout *scanout;
+		u32 format;
+		int id = -1;
+
+		/* Skip modesets without a mode */
+		if (!modeset->mode)
+			continue;
+
+		if (modeset->connectors[0]->has_tile) {
+			struct drm_splash_scanout *tiled;
+			int new_id = modeset->connectors[0]->tile_group->id;
+
+			/* Tiled modesets contribute to a single framebuffer,
+			 * check if this tiled group has already been seen.
+			 */
+			tiled = get_scanout_from_tile_group(splash, new_id);
+			if (tiled != NULL) {
+				if (!modeset->x)
+					tiled->width += modeset->mode->vdisplay;
+				if (!modeset->y)
+					tiled->height += modeset->mode->hdisplay;
+				modeset->fb = tiled->buffer->fb;
+				continue;
+			}
+
+			/* New tile group, save its ID for later */
+			id = new_id;
+		}
+
+		format = drm_splash_find_usable_format(modeset->crtc->primary,
+						       splash->preferred_format);
+		if (format == DRM_FORMAT_INVALID) {
+			drm_warn(client->dev,
+				 "splash: can't find a usable format for modeset");
+			continue;
+		}
+
+		tmp = krealloc(splash->scanout,
+			       (splash->n_scanout + 1) * sizeof(*splash->scanout),
+			       GFP_KERNEL);
+		if (!tmp) {
+			drm_warn(client->dev,
+				 "splash: can't reallocate the scanout array");
+			break;
+		}
+
+		splash->scanout = tmp;
+		scanout = &splash->scanout[splash->n_scanout];
+		splash->n_scanout++;
+
+		memset(scanout, 0, sizeof(*scanout));
+		scanout->id = id;
+		scanout->format = format;
+		scanout->width = modeset->mode->hdisplay;
+		scanout->height = modeset->mode->vdisplay;
+
+		modeset_mask |= BIT(j);
+		j++;
+	}
+
+	/* Now that all sensible modesets have been collected, allocate buffers */
+	j = 0;
+	drm_client_for_each_modeset(modeset, client) {
+		struct drm_splash_scanout *scanout;
+
+		if (!(modeset_mask & BIT(j)))
+			continue;
+
+		scanout = &splash->scanout[j];
+		j++;
+
+		scanout->buffer = drm_client_buffer_create_dumb(client,
+								scanout->width,
+								scanout->height,
+								scanout->format);
+		if (IS_ERR(scanout->buffer)) {
+			drm_warn(client->dev,
+				 "splash: can't create dumb buffer %d %d %p4cc",
+				 scanout->width, scanout->height, &scanout->format);
+			continue;
+		}
+
+		drm_info(client->dev, "splash: created dumb buffer %d %d %p4cc",
+			 scanout->width, scanout->height, &scanout->format);
+
+		modeset->fb = scanout->buffer->fb;
+		fb_count++;
+	}
+
+	return (fb_count == 0) ? -ENODEV : 0;
+}
+
+static void drm_splash_free_scanout(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	int i;
+
+	if (splash->n_scanout) {
+		for (i = 0; i < splash->n_scanout; i++)
+			drm_client_buffer_delete(splash->scanout[i].buffer);
+
+		splash->n_scanout = 0;
+		kfree(splash->scanout);
+		splash->scanout = NULL;
+	}
+}
+
+static int drm_splash_client_hotplug(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	int ret = 0;
+
+	guard(mutex)(&splash->hotplug_lock);
+
+	/* The modesets that get a splash are defined at first hotplug event */
+	if (splash->initialized)
+		return 0;
+
+	ret = drm_splash_init_client(splash);
+	if (ret == -ENODEV) {
+		drm_info(client->dev, "splash: no modeset found");
+		return 0;
+	} else if (ret) {
+		drm_err(client->dev,
+			"splash: failed to init client: %d", ret);
+		return ret;
+	}
+
+	/* Create the render thread, waken later */
+	splash->thread = kthread_create(drm_splash_render_thread,
+					splash, "drm_splash_%s",
+					client->dev->unique);
+	if (IS_ERR(splash->thread)) {
+		ret = PTR_ERR(splash->thread);
+		drm_err(client->dev,
+			"splash: failed to create render thread: %d", ret);
+		drm_splash_free_scanout(client);
+		return ret;
+	}
+
+	splash->use_bgrt = drm_splash_bgrt_available(splash);
+
+	/* If no other image has been loaded, try to load a BMP as firmware */
+	if (IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_SRC_BMP) && !splash->use_bgrt) {
+		ret = drm_splash_fw_request_bmp(splash);
+		if (ret) {
+			drm_err(client->dev,
+				"splash: failed to kick image load: %d", ret);
+			kthread_stop(splash->thread);
+			drm_splash_free_scanout(client);
+			return ret;
+		}
+	}
+
+	/* Wake the render thread to show initial contents */
+	drm_splash_wake_render_thread(splash);
+
+	splash->initialized = true;
+
+	return 0;
+}
+
+static int drm_splash_client_restore(struct drm_client_dev *client, bool force)
+{
+	int ret;
+
+	if (force)
+		ret = drm_client_modeset_commit_locked(client);
+	else
+		ret = drm_client_modeset_commit(client);
+
+	return ret;
+}
+
+static void drm_splash_client_unregister(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+
+	kthread_stop(splash->thread);
+	drm_splash_free_scanout(client);
+	drm_client_release(client);
+
+	drm_splash_image_cleanup(splash);
+}
+
+static void drm_splash_client_free(struct drm_client_dev *client)
+{
+	struct drm_splash *splash = client_to_drm_splash(client);
+	struct drm_device *dev = client->dev;
+
+	mutex_destroy(&splash->hotplug_lock);
+	kfree(splash);
+
+	drm_dbg(dev, "Unregistered with drm splash");
+}
+
+static const struct drm_client_funcs drm_splash_client_funcs = {
+	.owner		= THIS_MODULE,
+	.hotplug	= drm_splash_client_hotplug,
+	.restore	= drm_splash_client_restore,
+	.unregister	= drm_splash_client_unregister,
+	.free		= drm_splash_client_free,
+};
+
+/**
+ * drm_splash_register() - Register a drm device to drm_splash
+ * @dev: the drm device to register.
+ * @format: drm device preferred format.
+ */
+void drm_splash_register(struct drm_device *dev,
+			 const struct drm_format_info *format)
+{
+	struct drm_splash *splash;
+
+	splash = kzalloc_obj(*splash);
+	if (!splash)
+		goto err_warn;
+
+	mutex_init(&splash->hotplug_lock);
+	spin_lock_init(&splash->fw_lock);
+
+	if (format && format->num_planes == 1)
+		splash->preferred_format = format->format;
+	else
+		splash->preferred_format = DRM_FORMAT_RGB888;
+
+	if (drm_client_init(dev, &splash->client, "drm_splash",
+			    &drm_splash_client_funcs))
+		goto err_free;
+
+	drm_client_register(&splash->client);
+	drm_dbg(dev, "Registered with drm splash");
+
+	return;
+
+err_free:
+	kfree(splash);
+err_warn:
+	drm_warn(dev, "Failed to register with drm splash");
+}

-- 
2.54.0


^ permalink raw reply related

* [PATCH RFC v3 2/3] MAINTAINERS: add entry for DRM splash client
From: Francesco Valla @ 2026-05-10 21:29 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jonathan Corbet, Jocelyn Falempe,
	Javier Martinez Canillas, Shuah Khan
  Cc: Sam Ravnborg, Mario Limonciello, linux-kernel, dri-devel,
	linux-doc, linux-embedded, Francesco Valla
In-Reply-To: <20260510-drm_client_splash-v3-0-a9aee9f0b2fc@valla.it>

Add myself as maintainer for the DRM splash client.

Signed-off-by: Francesco Valla <francesco@valla.it>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..6c132139e87c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8933,6 +8933,13 @@ T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
 F:	drivers/gpu/drm/drm_privacy_screen*
 F:	include/drm/drm_privacy_screen*
 
+DRM SPLASH
+M:	Francesco Valla <francesco@valla.it>
+L:	dri-devel@lists.freedesktop.org
+S:	Maintained
+T:	git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F:	drivers/gpu/drm/clients/drm_splash.c
+
 DRM TTM SUBSYSTEM
 M:	Christian Koenig <christian.koenig@amd.com>
 M:	Huang Rui <ray.huang@amd.com>

-- 
2.54.0


^ permalink raw reply related

* [PATCH v4 32/30] KVM: x86: Re-synchronize TSC after KVM_SET_TSC_KHZ
From: David Woodhouse @ 2026-05-10 21:05 UTC (permalink / raw)
  To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Sean Christopherson,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Vitaly Kuznetsov, Juergen Gross, Boris Ostrovsky,
	David Woodhouse, Paul Durrant, Jonathan Cameron, Sascha Bischoff,
	Marc Zyngier, Dongli Zhang, Jack Allister, Joe Jin, Joey Gouly,
	kvm, linux-doc, linux-kernel, xen-devel, linux-kselftest
In-Reply-To: <20260509224824.3264567-1-dwmw2@infradead.org>

[-- Attachment #1: Type: text/plain, Size: 2047 bytes --]

From: David Woodhouse <dwmw@amazon.co.uk>

KVM_SET_TSC_KHZ changes the vCPU's TSC scaling ratio but does not
update the VM-wide cur_tsc_scaling_ratio used by get_kvmclock().
This causes get_kvmclock() to use a stale (default 1:1) ratio when
computing the KVM clock, leading to drift between the host-side
kvmclock and what the guest observes.

Fix this by calling kvm_synchronize_tsc() after changing the TSC
frequency. This:
 - Updates cur_tsc_scaling_ratio (consumed by pvclock_update_vm_gtod_copy)
 - Ensures the TSC value is continuous across the frequency change
 - Triggers kvm_track_tsc_matching() for proper masterclock handling
 - Allows subsequent vCPUs to synchronize via the 1-second slop hack

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 arch/x86/kvm/x86.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ac982652e5e0..833a4f119e22 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -206,6 +206,7 @@ module_param(mitigate_smt_rsb, bool, 0444);
 #ifdef CONFIG_X86_64
 static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);
 #endif
+static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value);
 #define KVM_MAX_NR_USER_RETURN_MSRS 16
 
 struct kvm_user_return_msrs {
@@ -2611,7 +2612,20 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
 			 user_tsc_khz, thresh_lo, thresh_hi);
 		use_scaling = 1;
 	}
-	return set_tsc_khz(vcpu, user_tsc_khz, use_scaling);
+	if (set_tsc_khz(vcpu, user_tsc_khz, use_scaling))
+		return -1;
+
+	/*
+	 * Re-synchronize the TSC after changing frequency. This ensures
+	 * cur_tsc_scaling_ratio is updated (used by get_kvmclock) and
+	 * the TSC value is continuous across the frequency change.
+	 */
+	{
+		u64 tsc = kvm_read_l1_tsc(vcpu, rdtsc());
+
+		kvm_synchronize_tsc(vcpu, &tsc);
+	}
+	return 0;
 }
 
 static s64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns)
-- 
2.43.0



[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply related

* [PATCH v4 31/30] KVM: selftests: Add Xen/generic CPUID timing leaf test
From: David Woodhouse @ 2026-05-10 20:58 UTC (permalink / raw)
  To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Sean Christopherson,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Vitaly Kuznetsov, Juergen Gross, Boris Ostrovsky,
	David Woodhouse, Paul Durrant, Jonathan Cameron, Sascha Bischoff,
	Marc Zyngier, Dongli Zhang, Jack Allister, Joe Jin, Joey Gouly,
	kvm, linux-doc, linux-kernel, xen-devel, linux-kselftest
In-Reply-To: <20260509224824.3264567-1-dwmw2@infradead.org>

[-- Attachment #1: Type: text/plain, Size: 8759 bytes --]

From: David Woodhouse <dwmw@amazon.co.uk>

Verify that userspace can correctly populate Xen and generic CPUID
timing leaves using the KVM_VCPU_TSC_EFFECTIVE_FREQ and
KVM_VCPU_TSC_SCALE attributes.

This validates that the removal of KVM's runtime Xen CPUID modification
doesn't break guests: userspace queries the effective TSC and bus
frequencies, computes the pvclock mul/shift, populates the CPUID leaves,
and the guest verifies the values match.

The test exercises:
 - KVM_VCPU_TSC_EFFECTIVE_FREQ at native and scaled frequencies
 - KVM_VCPU_TSC_SCALE ratio verification against effective frequency
 - Generic timing leaf 0x40000010 (EAX=tsc_khz, EBX=bus_khz)
 - Xen leaf 3 sub-leaf 0 (ECX=guest TSC kHz)
 - Xen leaf 3 sub-leaf 1 (ECX=mul, EDX=shift)

Gracefully skips TSC scaling tests on hardware without support.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../selftests/kvm/x86/xen_cpuid_timing_test.c | 232 ++++++++++++++++++
 2 files changed, 233 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index fb935ae3bf38..50f02116249f 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -139,6 +139,7 @@ TEST_GEN_PROGS_x86 += x86/xss_msr_test
 TEST_GEN_PROGS_x86 += x86/debug_regs
 TEST_GEN_PROGS_x86 += x86/tsc_msrs_test
 TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test
+TEST_GEN_PROGS_x86 += x86/xen_cpuid_timing_test
 TEST_GEN_PROGS_x86 += x86/xen_shinfo_test
 TEST_GEN_PROGS_x86 += x86/xen_vmcall_test
 TEST_GEN_PROGS_x86 += x86/sev_init2_tests
diff --git a/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c
new file mode 100644
index 000000000000..f574343ed449
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test that userspace can correctly populate Xen and generic CPUID
+ * timing leaves using KVM_VCPU_TSC_EFFECTIVE_FREQ.
+ *
+ * This validates that the removal of KVM's runtime Xen CPUID modification
+ * doesn't break guests, because userspace has all the information needed.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#define XEN_CPUID_BASE		0x40000100
+#define XEN_CPUID_LEAF(n)	(XEN_CPUID_BASE + (n))
+#define GENERIC_TIMING_LEAF	0x40000010
+
+/* Values set by host, verified by guest */
+static uint32_t expected_tsc_khz;
+static uint32_t expected_bus_khz;
+static uint32_t expected_tsc_mul;
+static int8_t   expected_tsc_shift;
+static uint64_t host_khz;
+
+static void guest_code(void)
+{
+	uint32_t eax, ebx, ecx, edx;
+
+	/* Check generic timing leaf 0x40000010 */
+	__cpuid(GENERIC_TIMING_LEAF, 0, &eax, &ebx, &ecx, &edx);
+	GUEST_ASSERT_EQ(eax, expected_tsc_khz);
+	GUEST_ASSERT_EQ(ebx, expected_bus_khz);
+
+	/* Check Xen leaf 3, sub-leaf 0: ECX = guest TSC frequency */
+	__cpuid(XEN_CPUID_LEAF(3), 0, &eax, &ebx, &ecx, &edx);
+	GUEST_ASSERT_EQ(ecx, expected_tsc_khz);
+
+	/* Check Xen leaf 3, sub-leaf 1: ECX = mul, EDX = shift */
+	__cpuid(XEN_CPUID_LEAF(3), 1, &eax, &ebx, &ecx, &edx);
+	GUEST_ASSERT_EQ(ecx, expected_tsc_mul);
+	GUEST_ASSERT_EQ((int8_t)edx, expected_tsc_shift);
+
+	GUEST_SYNC(0);
+}
+
+static void add_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function,
+			    uint32_t index, uint32_t eax, uint32_t ebx,
+			    uint32_t ecx, uint32_t edx)
+{
+	struct kvm_cpuid2 *cpuid = vcpu->cpuid;
+	struct kvm_cpuid_entry2 *entry;
+	int n = cpuid->nent;
+
+	vcpu->cpuid = realloc(vcpu->cpuid,
+			      sizeof(*cpuid) + (n + 1) * sizeof(*entry));
+	cpuid = vcpu->cpuid;
+	cpuid->nent = n + 1;
+
+	entry = &cpuid->entries[n];
+	memset(entry, 0, sizeof(*entry));
+	entry->function = function;
+	entry->index = index;
+	entry->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+	entry->eax = eax;
+	entry->ebx = ebx;
+	entry->ecx = ecx;
+	entry->edx = edx;
+}
+
+/*
+ * Compute pvclock mul/shift from frequency, matching kvm_get_time_scale().
+ */
+static void compute_tsc_mul_shift(uint64_t tsc_hz, uint32_t *mul, int8_t *shift)
+{
+	uint64_t scaled = 1000000000ULL;
+	uint64_t base = tsc_hz;
+	int32_t s = 0;
+	uint32_t base32;
+
+	while (base > scaled * 2 || base >> 32) {
+		base >>= 1;
+		s--;
+	}
+	base32 = (uint32_t)base;
+	while (base32 <= scaled || scaled >> 32) {
+		if (scaled >> 32 || base32 & (1U << 31))
+			scaled >>= 1;
+		else
+			base32 <<= 1;
+		s++;
+	}
+	*mul = (uint32_t)((scaled << 32) / base32);
+	*shift = (int8_t)s;
+}
+
+static void run_test(uint64_t tsc_khz)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	struct { uint32_t tsc_khz; uint32_t bus_khz; } freq;
+	struct kvm_device_attr freq_attr = {
+		.group = KVM_VCPU_TSC_CTRL,
+		.attr = 2, /* KVM_VCPU_TSC_EFFECTIVE_FREQ */
+		.addr = (uint64_t)(uintptr_t)&freq,
+	};
+
+	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+
+	if (tsc_khz) {
+		pr_info("Testing at TSC frequency %lu kHz\n", tsc_khz);
+		vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)tsc_khz);
+	} else {
+		pr_info("Testing at native TSC frequency\n");
+	}
+
+	vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &freq_attr);
+
+	/* If scaling wasn't applied, skip this frequency */
+	if (tsc_khz && freq.tsc_khz == host_khz) {
+		pr_info("  TSC scaling not available, skipping\n");
+		kvm_vm_release(vm);
+		return;
+	}
+
+	pr_info("  Effective TSC: %u kHz, Bus: %u kHz\n", freq.tsc_khz, freq.bus_khz);
+
+	/* Also exercise KVM_VCPU_TSC_SCALE if available */
+	{
+		struct { uint64_t ratio; uint64_t frac_bits; } scale;
+		struct kvm_device_attr scale_attr = {
+			.group = KVM_VCPU_TSC_CTRL,
+			.attr = 1, /* KVM_VCPU_TSC_SCALE */
+			.addr = (uint64_t)(uintptr_t)&scale,
+		};
+
+		if (!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &scale_attr)) {
+			vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &scale_attr);
+			pr_info("  TSC scale: ratio=%lu frac_bits=%lu\n",
+				scale.ratio, scale.frac_bits);
+
+			/*
+			 * Verify: applying the ratio to the host TSC frequency
+			 * should give approximately the effective frequency.
+			 */
+			if (tsc_khz) {
+				uint64_t computed = ((__uint128_t)host_khz * scale.ratio) >> scale.frac_bits;
+				int64_t diff = (int64_t)computed - (int64_t)freq.tsc_khz;
+
+				TEST_ASSERT(diff >= -1 && diff <= 1,
+					    "TSC_SCALE ratio mismatch: computed %lu vs effective %u (diff %ld)",
+					    computed, freq.tsc_khz, diff);
+			}
+		}
+	}
+
+	compute_tsc_mul_shift((uint64_t)freq.tsc_khz * 1000,
+			      &expected_tsc_mul, &expected_tsc_shift);
+
+	expected_tsc_khz = freq.tsc_khz;
+	expected_bus_khz = freq.bus_khz;
+
+	sync_global_to_guest(vm, expected_tsc_khz);
+	sync_global_to_guest(vm, expected_bus_khz);
+	sync_global_to_guest(vm, expected_tsc_mul);
+	sync_global_to_guest(vm, expected_tsc_shift);
+
+	/* Populate CPUID leaves as a VMM would */
+	add_cpuid_entry(vcpu, GENERIC_TIMING_LEAF, 0,
+			freq.tsc_khz, freq.bus_khz, 0, 0);
+	add_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 0,
+			0, 0, freq.tsc_khz, 0);
+	add_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 1,
+			0, 0, expected_tsc_mul,
+			(uint32_t)(uint8_t)expected_tsc_shift);
+
+	vcpu_set_cpuid(vcpu);
+
+	pr_info("  pvclock mul=%u shift=%d\n", expected_tsc_mul, expected_tsc_shift);
+
+	vcpu_run(vcpu);
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+	switch (get_ucall(vcpu, &uc)) {
+	case UCALL_ABORT:
+		REPORT_GUEST_ASSERT(uc);
+		break;
+	case UCALL_SYNC:
+		break;
+	default:
+		TEST_FAIL("Unexpected ucall");
+	}
+
+	kvm_vm_release(vm);
+}
+
+int main(void)
+{
+	uint64_t freq;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct kvm_device_attr attr = {
+		.group = KVM_VCPU_TSC_CTRL,
+		.attr = 2,
+	};
+
+	TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
+
+	/* Check KVM_VCPU_TSC_EFFECTIVE_FREQ is supported */
+	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+	TEST_REQUIRE(!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &attr));
+	host_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);
+	kvm_vm_release(vm);
+
+	/* Native frequency */
+	run_test(0);
+
+	/* Scaled frequencies — skip if TSC scaling not available */
+	for (freq = 1000000; freq <= 4000000; freq += 1000000) {
+		if (freq == host_khz)
+			continue;
+		run_test(freq);
+	}
+
+	pr_info("PASS: All CPUID timing leaf tests passed\n");
+	return 0;
+}
-- 
2.43.0



[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply related

* [PATCH v4 33/30] KVM: selftests: Add Xen runstate migration test
From: David Woodhouse @ 2026-05-10 20:56 UTC (permalink / raw)
  To: Paolo Bonzini, Jonathan Corbet, Shuah Khan, Sean Christopherson,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Vitaly Kuznetsov, Juergen Gross, Boris Ostrovsky,
	David Woodhouse, Paul Durrant, Jonathan Cameron, Sascha Bischoff,
	Marc Zyngier, Dongli Zhang, Jack Allister, Joe Jin, Joey Gouly,
	kvm, linux-doc, linux-kernel, xen-devel, linux-kselftest
In-Reply-To: <20260509224824.3264567-1-dwmw2@infradead.org>

[-- Attachment #1: Type: text/plain, Size: 7851 bytes --]

From: David Woodhouse <dwmw@amazon.co.uk>

Test that Xen runstate (steal time) is correctly accounted across a
simulated live migration using KVM_XEN_VCPU_ATTR and KVM_[GS]ET_CLOCK_GUEST.

The test simulates what a real VMM does during migration:
1. Creates a VM with Xen HVM config and runstate tracking
2. Runs the guest to accumulate some kvmclock time
3. Saves clock (KVM_GET_CLOCK_GUEST), TSC offset, and runstate
4. Marks the saved state as RUNSTATE_runnable (vCPU not running)
5. Destroys the source VM
6. Sleeps 10ms (simulating migration network transfer time)
7. Creates a new VM and restores all state precisely as saved
8. Runs the guest and verifies the migration gap appears as steal

The kernel accounts the gap because: on vcpu_load, it transitions from
RUNSTATE_runnable to RUNSTATE_running, computing delta = kvmclock_now -
state_entry_time. Since kvmclock has advanced past the saved entry time
(real time elapsed during migration), the delta is added to time_runnable.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 .../selftests/kvm/x86/xen_migration_test.c    | 194 ++++++++++++++++++
 1 file changed, 194 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/xen_migration_test.c

diff --git a/tools/testing/selftests/kvm/x86/xen_migration_test.c b/tools/testing/selftests/kvm/x86/xen_migration_test.c
new file mode 100644
index 000000000000..37e8ace00611
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/xen_migration_test.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test Xen runstate (steal time) preservation across simulated migration.
+ *
+ * Verifies that the kernel correctly accounts the migration gap as
+ * steal time (runnable) when runstate data is saved and restored
+ * precisely, but real time elapses during the migration.
+ *
+ * The key insight: userspace saves the runstate with state=RUNSTATE_runnable
+ * (the vCPU is not running during migration). On restore, the kernel sees
+ * that kvmclock has advanced past state_entry_time, and accounts the
+ * difference as time spent in the runnable state.
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#define SHINFO_GPA	0xc0000000ULL
+#define RUNSTATE_GPA	(SHINFO_GPA + 0x1000)
+
+#define RUNSTATE_running  0
+#define RUNSTATE_runnable 1
+#define RUNSTATE_blocked  2
+#define RUNSTATE_offline  3
+
+struct vcpu_runstate_info {
+	uint32_t state;
+	uint64_t state_entry_time;
+	uint64_t time[4];
+} __attribute__((packed));
+
+static void guest_code(void)
+{
+	volatile struct vcpu_runstate_info *rs =
+		(void *)(unsigned long)RUNSTATE_GPA;
+
+	/* Report runstate times — no need to enable kvmclock MSR,
+	 * the kernel writes runstate using its internal kvmclock. */
+	GUEST_SYNC_ARGS(0, rs->time[RUNSTATE_runnable],
+			rs->time[RUNSTATE_running], 0, 0);
+}
+
+static struct kvm_vm *create_xen_vm(struct kvm_vcpu **vcpu)
+{
+	struct kvm_vm *vm;
+	int xen_caps;
+
+	vm = vm_create_with_one_vcpu(vcpu, guest_code);
+
+	xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM);
+	TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO);
+	TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_RUNSTATE);
+
+	/* Map pages */
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+				    SHINFO_GPA, 1, 2, 0);
+	virt_map(vm, SHINFO_GPA, SHINFO_GPA, 2);
+
+	/* Enable Xen HVM with MSR interception (enables runstate tracking) */
+	struct kvm_xen_hvm_config cfg = {
+		.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
+		.msr = 0x40000000,
+	};
+	vm_ioctl(vm, KVM_XEN_HVM_CONFIG, &cfg);
+
+	/* Set shared_info */
+	struct kvm_xen_hvm_attr ha = {
+		.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
+		.u.shared_info.gfn = SHINFO_GPA >> 12,
+	};
+	vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &ha);
+
+	/* Set runstate address */
+	struct kvm_xen_vcpu_attr rs_addr = {
+		.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR,
+		.u.gpa = RUNSTATE_GPA,
+	};
+	vcpu_ioctl(*vcpu, KVM_XEN_VCPU_SET_ATTR, &rs_addr);
+
+	return vm;
+}
+
+int main(void)
+{
+	struct pvclock_vcpu_time_info pvti;
+	struct kvm_xen_vcpu_attr runstate_save;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	uint64_t tsc_offset;
+	int ret;
+
+	/* === SOURCE SIDE === */
+	pr_info("=== Source: create VM and run guest ===\n");
+	vm = create_xen_vm(&vcpu);
+
+	/* Run guest once to accumulate some runstate time */
+	vcpu_run(vcpu);
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+	TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+	pr_info("  Guest sees: runnable=%" PRIu64 " running=%" PRIu64 "\n",
+		uc.args[2], uc.args[3]);
+
+	/* Save clock state */
+	ret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, &pvti);
+	TEST_ASSERT(!ret, "KVM_GET_CLOCK_GUEST failed");
+
+	/* Save TSC offset */
+	tsc_offset = vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST);
+
+	/* Save runstate — the vCPU is now "runnable" (not running) */
+	runstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
+	vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &runstate_save);
+
+	/*
+	 * Transition to runnable state before saving — the vCPU is
+	 * not running during migration.
+	 */
+	runstate_save.u.runstate.state = RUNSTATE_runnable;
+
+	pr_info("  Saved runstate: running=%" PRIu64 " runnable=%" PRIu64
+		" entry=%" PRIu64 "\n",
+		(uint64_t)runstate_save.u.runstate.time_running,
+		(uint64_t)runstate_save.u.runstate.time_runnable,
+		(uint64_t)runstate_save.u.runstate.state_entry_time);
+
+	uint64_t saved_runnable = runstate_save.u.runstate.time_runnable;
+
+	kvm_vm_release(vm);
+
+	/* === MIGRATION GAP === */
+	pr_info("=== Simulating migration (sleeping 10ms) ===\n");
+	usleep(10000);
+
+	/* === DESTINATION SIDE === */
+	pr_info("=== Destination: create new VM and restore ===\n");
+	vm = create_xen_vm(&vcpu);
+
+	/* Restore TSC offset */
+	vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, tsc_offset);
+
+	/* Restore clock — kvmclock will now be ~10ms ahead of the snapshot */
+	vcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, &pvti);
+
+	/* Restore runstate exactly as saved (state=runnable) */
+	runstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
+	ret = __vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &runstate_save);
+	TEST_ASSERT(!ret, "Restore runstate failed: errno %d", errno);
+
+	/*
+	 * Run the guest. When the vCPU enters vcpu_run, the kernel
+	 * transitions from RUNSTATE_runnable to RUNSTATE_running.
+	 * It computes: delta = kvmclock_now - state_entry_time
+	 * This delta (which includes the migration gap) is added to
+	 * time_runnable (steal time).
+	 */
+	vcpu_run(vcpu);
+	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+	TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+	uint64_t guest_runnable = uc.args[2];
+	uint64_t guest_running = uc.args[3];
+
+	pr_info("  Guest sees: runnable=%" PRIu64 " running=%" PRIu64 "\n",
+		guest_runnable, guest_running);
+
+	uint64_t steal_increase = guest_runnable - saved_runnable;
+	pr_info("  Steal time increase: %" PRIu64 " ns (migration gap)\n",
+		steal_increase);
+
+	/*
+	 * The steal time increase should be at least 10ms (the sleep)
+	 * but not more than 5s (allowing for VM creation overhead).
+	 * The actual gap is from the source's state_entry_time to the
+	 * destination's kvmclock "now" at vcpu_load time.
+	 */
+	TEST_ASSERT(steal_increase >= 10000000ULL &&
+		    steal_increase < 5000000000ULL,
+		    "Steal time increase %" PRIu64 " ns not in expected range "
+		    "[10ms, 5s]", steal_increase);
+
+	kvm_vm_release(vm);
+	pr_info("PASS: Migration gap correctly accounted as steal time\n");
+	return 0;
+}
-- 
2.43.0



[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply related

* Re: [PATCH net-next v3 0/8] net: devmem: support devmem with netkit devices
From: Zhu Yanjun @ 2026-05-10 20:33 UTC (permalink / raw)
  To: Bobby Eshleman, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Alex Shi, Yanteng Si, Dongliang Mu, Michael Chan,
	Pavan Chebbi, Joshua Washington, Harshitha Ramamurthy,
	Saeed Mahameed, Tariq Toukan, Mark Bloch, Leon Romanovsky,
	Alexander Duyck, kernel-team, Daniel Borkmann,
	Nikolay Aleksandrov, Shuah Khan, yanjun.zhu@linux.dev
  Cc: dw, sdf.kernel, mohsin.bashr, willemb, jiang.kun2, xu.xin16,
	wang.yaxin, netdev, linux-doc, linux-kernel, linux-rdma, bpf,
	linux-kselftest, Stanislav Fomichev, Mina Almasry, Bobby Eshleman
In-Reply-To: <20260507-tcp-dm-netkit-v3-0-52821445867c@meta.com>

在 2026/5/7 19:27, Bobby Eshleman 写道:
> This series enables TCP devmem TX through netkit devices.
> 
> Netkit now supports queue leasing. A physical NIC's RX queue can be
> leased to a netkit guest interface inside a container namespace. This
> gives the container a devmem-capable data path on the RX side (bind-rx,
> etc...). On the TX side, the container process binds to its netkit guest
> interface and sends traffic that netkit redirects (via BPF or ip
> forwarding) to the physical NIC for DMA.
> 
> Two things in the existing devmem TX path prevent this from working:
> 
> 1. validate_xmit_unreadable_skb() requires dev->netmem_tx before it will
>     forward a dmabuf-backed (unreadable) skb. This protects skbs from
>     landing on devices that don't have the IOMMU mappings for the backing
>     dmabuf or that don't speak netmem. Netkit, however, does not support
>     DMA, doesn't attempt to read unreadable skb pages and so doesn't
>     break netmem (it is pure skb routing and redirection). It is
>     functionally capable of routing unreadable skbs, but there is no way
>     for the TX validation pathway to distinguish between a device that
>     will actually attempt DMA-ing the skb and another device
>     (like netkit) that does not DMA but also does not break
>     netmem.
> 
> 2. bind_tx_doit uses the bound device as the DMA device.  When the user
>     binds devmem TX to the netkit guest, the bind handler attempts to
>     create DMA mappings against netkit, which has no DMA capability and
>     no IOMMU mappings.
> 
> This series solves these problems as follows:
> 
> 1. Extend netmem_tx to two bits, assigned to one of three values:
> 
>     NETMEM_TX_NONE   - netmem not supported
>     NETMEM_TX_DMA    - netmem supported and performs DMA
>     NETMEM_TX_NO_DMA - netmem supported, but does not DMA
> 
>     With these bits, phys devices can set NETMEM_TX_DMA and devices like
>     netkit set NETMEM_TX_NO_DMA. The validation TX path ensures that any
>     DMA-capable netdev exactly matches the bound device, guaranteeing the
>     correct mapping of the bound dmabuf. The validation TX path also
>     allows devices with NETMEM_TX_NO_DMA to pass, knowing these devices
>     will not misuse netmem or run into IOMMU faults. After redirection or
>     routing and the skb finally makes its way through the stack to a
>     physical device's TX path, the above NETMEM_TX_DMA check is performed
>     again to guarantee the device has the appropriate binding/mappings.
> 
> 2. On TX bind, the bind handler recognizes NETMEM_TX_NO_DMA devices and
>     finds the phys TX device and binds to that instead. For the netkit
>     case, if it has been leased a queue from a DMA-capable device
>     already, then the bind action is performed on the DMA-capable device
>     instead and the dmabuf is mapped correctly.
> 
> ---
> Changes in v3:
> - Fix validate_xmit_unreadable_skb() logic for non-devmem
>    unreadable niovs (should not be dropped) (Sashiko)
> - Simplify lock handling in bind_tx, no premature release (Jakub)
> - split NO_DMA changes into separate patch (Jakub)
> - fixed some pylint issues, one required an additional patch ("selftests:
>    drv-net: make attr _nk_guest_ifname public") to rename a variable from
>    private to public
> - see per-patch changelist for more detailed changes
> - Link to v2: https://lore.kernel.org/r/20260504-tcp-dm-netkit-v2-0-56d52ac72fd4@meta.com
> 
> Changes in v2:
> - Squash driver conversion patches (2-5) into patch 1 (Jakub)
> - In validate_xmit_unreadable_skb() to check netmem_tx mode before inspecting
>    frags (Jakub)
> - Lock bind_dev around netdev_queue_get_dma_dev() when bind_dev != netdev to
>    fix lockdep (Sashiko)
> - Move require_devmem() into individual test functions so KsftSkipEx goes up to
>    ksft_run() (Sashiko)
> - Add nk_devmem.py to TEST_PROGS in Makefile (Sashiko)
> - Link to v1:
>    https://lore.kernel.org/all/20260428-tcp-dm-netkit-v1-0-719280eba4d2@meta.com/
> 
> Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
> 
> ---
> Bobby Eshleman (8):
>        net: convert netmem_tx flag to enum
>        net: netkit: declare NETMEM_TX_NO_DMA mode
>        net: devmem: support TX over NETMEM_TX_NO_DMA devices

I applied this patchset in my local kernel tree and built a new kernel 
image. I loaded this new kernel image in my test environment. It seems 
that all the testcases can pass.

I think that this patchset would not cause any regression problem in my 
test environment.

Zhu Yanjun

>        selftests: drv-net: ncdevmem: add -n flag to skip NIC configuration
>        selftests: drv-net: make attr _nk_guest_ifname public
>        selftests: drv-net: refactor devmem command builders into lib module
>        selftests: drv-net: add primary_rx_redirect support to NetDrvContEnv
>        selftests: drv-net: add netkit devmem tests
> 
>   .../networking/net_cachelines/net_device.rst       |   2 +-
>   Documentation/networking/netmem.rst                |   8 +-
>   .../translations/zh_CN/networking/netmem.rst       |   7 +-
>   drivers/net/ethernet/broadcom/bnxt/bnxt.c          |   2 +-
>   drivers/net/ethernet/google/gve/gve_main.c         |   2 +-
>   drivers/net/ethernet/mellanox/mlx5/core/en_main.c  |   2 +-
>   drivers/net/ethernet/meta/fbnic/fbnic_netdev.c     |   2 +-
>   drivers/net/netkit.c                               |   1 +
>   include/linux/netdevice.h                          |  11 +-
>   net/core/dev.c                                     |   5 +-
>   net/core/devmem.c                                  |   6 +-
>   net/core/devmem.h                                  |   9 +-
>   net/core/netdev-genl.c                             |  65 +++++-
>   tools/testing/selftests/drivers/net/hw/Makefile    |   1 +
>   tools/testing/selftests/drivers/net/hw/devmem.py   |  77 ++------
>   .../selftests/drivers/net/hw/lib/py/devmem.py      | 218 +++++++++++++++++++++
>   tools/testing/selftests/drivers/net/hw/ncdevmem.c  |  58 +++---
>   .../testing/selftests/drivers/net/hw/nk_devmem.py  |  55 ++++++
>   .../drivers/net/hw/nk_primary_rx_redirect.bpf.c    |  39 ++++
>   .../testing/selftests/drivers/net/hw/nk_qlease.py  |   8 +-
>   tools/testing/selftests/drivers/net/lib/py/env.py  | 109 ++++++++---
>   21 files changed, 549 insertions(+), 138 deletions(-)
> ---
> base-commit: 790ead9394860e7d70c5e0e50a35b243e909a618
> change-id: 20260423-tcp-dm-netkit-2bd78b638d30
> 
> Best regards,


^ permalink raw reply

* Re: [PATCH] docs: Document panic_on_rcu_stall default behavior
From: Paul E. McKenney @ 2026-05-10 19:54 UTC (permalink / raw)
  To: Kunwu Chan; +Cc: corbet, skhan, linux-doc, linux-kernel, gustavold, Kunwu Chan
In-Reply-To: <20260509091214.1679194-1-kunwu.chan@linux.dev>

On Sat, May 09, 2026 at 05:12:14PM +0800, Kunwu Chan wrote:
> From: Kunwu Chan <kunwu.chan@gmail.com>
> 
> Commit ab875b3e179f ("rcu: Add BOOTPARAM_RCU_STALL_PANIC
> Kconfig option") made the default value of
> kernel.panic_on_rcu_stall depend on
> CONFIG_BOOTPARAM_RCU_STALL_PANIC.
> 
> Document this in kernel.rst
> 
> Signed-off-by: Kunwu Chan <kunwu.chan@gmail.com>

This commit depends on the commit you call out above, which, given Linus
Torvalds's reaction, is unlikely to make it into mainline.  :-(

A likely workaround is to use the existing sysctl kernel boot parameter,
as in: "sysctl.kernel.panic_on_rcu_stall=1".

This can also be embedded into the kernel image using the bootconfig
facility.  To do this, build your kernel with the following Kconfig
options:

	CONFIG_BOOT_CONFIG=y
	CONFIG_BOOT_CONFIG_FORCE=y
	CONFIG_BOOT_CONFIG_EMBED=y
	CONFIG_BOOT_CONFIG_EMBED_FILE=".bootconfig"

Then create your ".bootconfig" file in the top-level directory of your
Linux-kernel source tree:

	kernel {
		sysctl.kernel.panic_on_rcu_stall=1
	}

You can also pass parameters to the "init" process by adding an "init"
stanza to your .bootconfig file.  See the Linux-kernel bootconfig
documentation for more information:

	Documentation/admin-guide/bootconfig.rst

							Thanx, Paul

> ---
>  Documentation/admin-guide/sysctl/kernel.rst | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
> index c6994e55d141..99598a83f830 100644
> --- a/Documentation/admin-guide/sysctl/kernel.rst
> +++ b/Documentation/admin-guide/sysctl/kernel.rst
> @@ -948,6 +948,10 @@ panic_on_rcu_stall
>  When set to 1, calls panic() after RCU stall detection messages. This
>  is useful to define the root cause of RCU stalls using a vmcore.
>  
> +The default value can be configured at build time via
> +``CONFIG_BOOTPARAM_RCU_STALL_PANIC``. Runtime updates to this sysctl
> +always override the built-in default.
> +
>  = ============================================================
>  0 Do not panic() when RCU stall takes place, default behavior.
>  1 panic() after printing RCU stall messages.
> -- 
> 2.43.0
> 

^ permalink raw reply

* Re: [PATCH] docs: leds: uleds: Make the documentation match the code.
From: Björn Persson @ 2026-05-10 19:43 UTC (permalink / raw)
  To: Lee Jones
  Cc: Pavel Machek, Jonathan Corbet, Shuah Khan, linux-leds, linux-doc,
	linux-kernel
In-Reply-To: <20260507131128.GM305027@google.com>

[-- Attachment #1: Type: text/plain, Size: 1086 bytes --]

Lee Jones wrote:
> On Fri, 24 Apr 2026, Björn Persson wrote:
> 
> > Lee Jones wrote:  
> > > On Thu, 02 Apr 2026, Björn Persson wrote:
> > >   
> > > > +The current brightness is found by reading a whole int from the character    
> > > 
> > > Try not to shorten names in documentation "integer".  
> > 
> > The type is named "int" in C. There are many integer types, but it would
> > be wrong to try to read a uint16_t or a size_t or any other integer
> > type. The document needs to use the actual type name to make it clear to
> > the reader that they must read sizeof(int) bytes.  
> 
> Right, but you're not writing in C.

That's technically true, as I wrote my program in C++. It's far from my
favorite, but I had to use a language that can include C header files
and use C types, because /dev/uleds is a very C-centric interface.

If API documentation isn't allowed to name a type, then I withdraw the
patch. It's pointless to continue. The next programmer will also have to
read the code to find out what the true API is, like I did.

Björn Persson

[-- Attachment #2: OpenPGP digital signatur --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* [PATCH v2] cpufreq-stats: document limitations on modern cpufreq drivers
From: NicoErdmann @ 2026-05-10 19:33 UTC (permalink / raw)
  To: linux-pm; +Cc: linux-doc, rafael, viresh.kumar, corbet, skhan, NicoErdmann
In-Reply-To: <b23cee4d-dd82-4828-9f38-72cfb05eff32@infradead.org>

Add a note clarifying that cpufreq-stats may not be present or may not provide meaningful statistics depending
on the active CPU frequency scaling driver.

In particular, drivers such as intel_pstate and amd_pstate may use alternative mechanisms for frequency scaling
and accounting.

v2:
 - Add missing period at end of sentence (reported by Randy)

Signed-off-by: NicoErdmann <nicobsc4@yahoo.com>
---
 Documentation/cpu-freq/cpufreq-stats.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Documentation/cpu-freq/cpufreq-stats.rst b/Documentation/cpu-freq/cpufreq-stats.rst
index 9ad695b1c7db..6ffa5a6a63c9 100644
--- a/Documentation/cpu-freq/cpufreq-stats.rst
+++ b/Documentation/cpu-freq/cpufreq-stats.rst
@@ -28,6 +28,13 @@ Various statistics will form read_only files under this directory.
 This driver is designed to be independent of any particular cpufreq_driver
 that may be running on your CPU. So, it will work with any cpufreq_driver.
 
+.. note::
+	
+   On some modern systems, this interface may not be available or may not
+   expose meaningful statistics depending on the active CPU frequency scaling driver.
+
+   In particular, drivers such as intel_pstate or amd_pstate may use alternative
+   mechanisms for frequency scaling and accounting.
 
 2. Statistics Provided (with example)
 =====================================
-- 
2.54.0


^ permalink raw reply related

* [RFC V2 net-next 2/2] net/mlx5: Apply devlink boot defaults during init
From: Mark Bloch @ 2026-05-10 18:54 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller, Jiri Pirko
  Cc: Jonathan Corbet, Shuah Khan, Simon Horman, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Andrew Morton,
	Borislav Petkov (AMD), Randy Dunlap, Petr Mladek,
	Peter Zijlstra (Intel), Christian Brauner, Thomas Gleixner,
	Dapeng Mi, Kees Cook, Marco Elver, Li RongQing, Eric Biggers,
	Paul E. McKenney, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260510185424.2041415-1-mbloch@nvidia.com>

Apply devlink boot defaults for mlx5 devices after successful device
initialization while holding the devlink instance lock.

At this point the devlink instance is registered and the mlx5 devlink
operations are available, so eswitch mode defaults can be applied to
the matching PCI devlink handle.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 296c5223cf61..deea7150084f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -1470,6 +1470,8 @@ int mlx5_init_one(struct mlx5_core_dev *dev)
 	err = mlx5_init_one_devl_locked(dev);
 	if (err)
 		devl_unregister(devlink);
+	else
+		devl_apply_defaults(devlink);
 unlock:
 	devl_unlock(devlink);
 	return err;
-- 
2.34.1


^ permalink raw reply related

* [RFC V2 net-next 1/2] devlink: Add eswitch mode boot defaults
From: Mark Bloch @ 2026-05-10 18:54 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller, Jiri Pirko
  Cc: Jonathan Corbet, Shuah Khan, Simon Horman, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Andrew Morton,
	Borislav Petkov (AMD), Randy Dunlap, Petr Mladek,
	Peter Zijlstra (Intel), Christian Brauner, Thomas Gleixner,
	Dapeng Mi, Kees Cook, Marco Elver, Li RongQing, Eric Biggers,
	Paul E. McKenney, linux-doc, linux-kernel, netdev, linux-rdma
In-Reply-To: <20260510185424.2041415-1-mbloch@nvidia.com>

Add devlink= kernel command line support for configuring devlink
eswitch mode during device initialization.

The supported syntax selects either all devlink handles or one explicit
comma-separated handle list:

  devlink=[*]:esw:mode:<mode>
  devlink=[<handle>[,<handle>...]]:esw:mode:<mode>

where <mode> is one of legacy, switchdev or switchdev_inactive. All
selected handles receive the same mode. Comma-separated default groups are
not supported.

The default is applied through the existing eswitch_mode_set() devlink
operation, matching the userspace devlink eswitch set command.

Document the devlink= syntax and duplicate handle handling.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 .../admin-guide/kernel-parameters.txt         |  24 ++
 .../networking/devlink/devlink-defaults.rst   |  93 ++++++
 Documentation/networking/devlink/index.rst    |   1 +
 include/net/devlink.h                         |   1 +
 net/devlink/core.c                            | 269 ++++++++++++++++++
 5 files changed, 388 insertions(+)
 create mode 100644 Documentation/networking/devlink/devlink-defaults.rst

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 7834ee927310..46435bdfe039 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1278,6 +1278,30 @@ Kernel parameters
 	dell_smm_hwmon.fan_max=
 			[HW] Maximum configurable fan speed.
 
+	devlink=	[NET]
+			Format:
+			[<selector>]:esw:mode:<mode>
+
+			<selector>:
+			* | <handle>[,<handle>...]
+
+			<handle>:
+			<bus-name>/<dev-name>
+
+			Configure default devlink settings for matching
+			devlink instances during device initialization.
+
+			Currently supported settings:
+			esw:mode:{ legacy | switchdev | switchdev_inactive }
+
+			Examples:
+			devlink=[*]:esw:mode:switchdev
+			devlink=[pci/0000:08:00.0]:esw:mode:switchdev
+			devlink=[pci/0000:08:00.0,pci/0000:09:00.1]:esw:mode:legacy
+
+			See Documentation/networking/devlink/devlink-defaults.rst
+			for the full syntax.
+
 	dfltcc=		[HW,S390]
 			Format: { on | off | def_only | inf_only | always }
 			on:       s390 zlib hardware support for compression on
diff --git a/Documentation/networking/devlink/devlink-defaults.rst b/Documentation/networking/devlink/devlink-defaults.rst
new file mode 100644
index 000000000000..4db3025c540f
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-defaults.rst
@@ -0,0 +1,93 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================
+Devlink Defaults
+================
+
+Devlink defaults allow selected devlink settings to be provided on the
+kernel command line and applied to matching devlink instances during device
+initialization.
+
+The devlink device is selected by its devlink handle. For PCI devices this is
+the same handle shown by ``devlink dev show``, for example
+``pci/0000:08:00.0``.
+
+Kernel command line syntax
+==========================
+
+Defaults are specified with the ``devlink=`` kernel command line parameter.
+
+The general syntax is::
+
+  devlink=[<selector>]:esw:mode:<mode>
+
+``<selector>`` is either ``*`` or one or more devlink handles::
+
+  * | <bus-name>/<dev-name>[,<bus-name>/<dev-name>...]
+
+``*`` applies the default to every devlink instance. All handles in the same
+``[]`` list receive the same eswitch mode setting.
+
+``<mode>`` is one of ``legacy``, ``switchdev`` or ``switchdev_inactive``.
+
+Syntax rules
+------------
+
+The following syntax rules apply:
+
+* Specify the default in one ``devlink=`` parameter. Repeated ``devlink=``
+  parameters are not accumulated.
+* The ``devlink=`` value is limited by the kernel command line size.
+* Whitespace is not allowed within the parameter value.
+* ``<selector>`` must be either ``*`` or a handle list. ``*`` cannot be
+  combined with explicit handles.
+* ``<bus-name>`` and ``<dev-name>`` must not be empty.
+* ``<bus-name>`` must not contain ``:``.
+* ``<dev-name>`` may contain ``:``. This allows PCI names such as
+  ``0000:08:00.0``.
+* Handles must not contain whitespace, ``[``, ``]``, ``*`` or more than one
+  ``/``.
+* A comma inside ``[]`` separates handles.
+* Comma-separated default groups are not supported.
+* Duplicate handles are rejected and the devlink default is ignored.
+
+Supported defaults
+==================
+
+The supported command is ``esw``:
+
+.. list-table::
+   :widths: 10 25 35
+   :header-rows: 1
+
+   * - Command
+     - Options
+     - Values
+   * - ``esw``
+     - ``mode:<mode>``
+     - ``legacy``, ``switchdev``, ``switchdev_inactive``
+
+The ``esw:mode`` default corresponds to the userspace command::
+
+  devlink dev eswitch set <handle> mode <value>
+
+
+Examples
+========
+
+Set all devlink instances to switchdev mode::
+
+  devlink=[*]:esw:mode:switchdev
+
+Set one PCI devlink instance to switchdev mode::
+
+  devlink=[pci/0000:08:00.0]:esw:mode:switchdev
+
+Set two PCI devlink instances to legacy mode::
+
+  devlink=[pci/0000:08:00.0,pci/0000:09:00.1]:esw:mode:legacy
+
+The following is invalid because comma-separated default groups are not
+supported::
+
+  devlink=[pci/0000:08:00.0]:esw:mode:switchdev,[pci/0000:09:00.0]:esw:mode:switchdev_inactive
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index f7ba7dcf477d..0d27a7008b14 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
    :maxdepth: 1
 
    devlink-dpipe
+   devlink-defaults
    devlink-eswitch-attr
    devlink-flash
    devlink-health
diff --git a/include/net/devlink.h b/include/net/devlink.h
index bcd31de1f890..058654d6800f 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1622,6 +1622,7 @@ int devl_trylock(struct devlink *devlink);
 void devl_unlock(struct devlink *devlink);
 void devl_assert_locked(struct devlink *devlink);
 bool devl_lock_is_held(struct devlink *devlink);
+int devl_apply_defaults(struct devlink *devlink);
 DEFINE_GUARD(devl, struct devlink *, devl_lock(_T), devl_unlock(_T));
 
 struct ib_device;
diff --git a/net/devlink/core.c b/net/devlink/core.c
index eeb6a71f5f56..2cfd50f9393b 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -4,6 +4,10 @@
  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  */
 
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
 #include <net/genetlink.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/devlink.h>
@@ -16,6 +20,247 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
 
 DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
 
+static char *devlink_default;
+static bool devlink_default_match_all;
+static enum devlink_eswitch_mode devlink_default_eswitch_mode;
+static LIST_HEAD(devlink_default_nodes);
+
+struct devlink_default_node {
+	struct list_head list;
+	char *bus_name;
+	char *dev_name;
+};
+
+static int __init
+devlink_default_esw_mode_to_value(const char *str,
+				  enum devlink_eswitch_mode *mode)
+{
+	if (!strcmp(str, "legacy")) {
+		*mode = DEVLINK_ESWITCH_MODE_LEGACY;
+		return 0;
+	}
+	if (!strcmp(str, "switchdev")) {
+		*mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+		return 0;
+	}
+	if (!strcmp(str, "switchdev_inactive")) {
+		*mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int __init
+devlink_default_cmd_parse(char *str)
+{
+	char *cmd;
+	char *attr;
+	char *mode;
+
+	cmd = strsep(&str, ":");
+	attr = strsep(&str, ":");
+	mode = strsep(&str, ":");
+	if (!cmd || strcmp(cmd, "esw") || !attr || strcmp(attr, "mode") ||
+	    !mode || !*mode || str)
+		return -EINVAL;
+
+	return devlink_default_esw_mode_to_value(mode,
+						 &devlink_default_eswitch_mode);
+}
+
+static int devlink_default_eswitch_apply(struct devlink *devlink)
+{
+	const struct devlink_ops *ops = devlink->ops;
+
+	if (!ops->eswitch_mode_set)
+		return -EOPNOTSUPP;
+
+	return ops->eswitch_mode_set(devlink, devlink_default_eswitch_mode,
+				     NULL);
+}
+
+static int __init
+devlink_default_handle_parse(char *handle, char **bus_name, char **dev_name)
+{
+	char *slash;
+	char *p;
+
+	if (!handle || !*handle)
+		return -EINVAL;
+
+	for (p = handle; *p; p++) {
+		if (*p == '[' || *p == ']' || *p == '*')
+			return -EINVAL;
+	}
+
+	slash = strchr(handle, '/');
+	if (!slash || slash == handle || !slash[1])
+		return -EINVAL;
+	if (strchr(slash + 1, '/'))
+		return -EINVAL;
+
+	*slash = '\0';
+	if (strchr(handle, ':'))
+		return -EINVAL;
+
+	*bus_name = handle;
+	*dev_name = slash + 1;
+	return 0;
+}
+
+static struct devlink_default_node *
+devlink_default_node_find(const char *bus_name, const char *dev_name)
+{
+	struct devlink_default_node *node;
+
+	list_for_each_entry(node, &devlink_default_nodes, list) {
+		if (!strcmp(node->bus_name, bus_name) &&
+		    !strcmp(node->dev_name, dev_name))
+			return node;
+	}
+
+	return NULL;
+}
+
+static int __init
+devlink_default_node_add(const char *bus_name, const char *dev_name)
+{
+	struct devlink_default_node *node;
+
+	if (devlink_default_node_find(bus_name, dev_name))
+		return -EEXIST;
+
+	node = kzalloc_obj(*node);
+	if (!node)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&node->list);
+	node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+	node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+	if (!node->bus_name || !node->dev_name) {
+		kfree(node->bus_name);
+		kfree(node->dev_name);
+		kfree(node);
+		return -ENOMEM;
+	}
+
+	list_add_tail(&node->list, &devlink_default_nodes);
+	return 0;
+}
+
+static int __init devlink_default_handles_parse(char *handles)
+{
+	char *handle;
+	int err;
+
+	if (!strcmp(handles, "*")) {
+		devlink_default_match_all = true;
+		return 0;
+	}
+
+	while ((handle = strsep(&handles, ",")) != NULL) {
+		char *bus_name;
+		char *dev_name;
+
+		err = devlink_default_handle_parse(handle, &bus_name,
+						   &dev_name);
+		if (err)
+			return err;
+
+		err = devlink_default_node_add(bus_name, dev_name);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void __init devlink_default_node_free(struct devlink_default_node *node)
+{
+	kfree(node->bus_name);
+	kfree(node->dev_name);
+	kfree(node);
+}
+
+static void __init devlink_default_nodes_clear(void)
+{
+	struct devlink_default_node *node;
+	struct devlink_default_node *node_tmp;
+
+	list_for_each_entry_safe(node, node_tmp, &devlink_default_nodes, list) {
+		list_del(&node->list);
+		devlink_default_node_free(node);
+	}
+
+	devlink_default_match_all = false;
+}
+
+static int __init devlink_default_parse(char *str)
+{
+	char *handles_end;
+	char *handles;
+	char *cmd;
+	int err;
+
+	if (!str || *str != '[')
+		return -EINVAL;
+
+	handles = str + 1;
+	handles_end = strchr(handles, ']');
+	if (!handles_end || handles_end[1] != ':' || !handles_end[2])
+		return -EINVAL;
+
+	*handles_end = '\0';
+	cmd = handles_end + 2;
+	if (!*handles)
+		return -EINVAL;
+
+	err = devlink_default_cmd_parse(cmd);
+	if (err)
+		return err;
+
+	err = devlink_default_handles_parse(handles);
+	if (err)
+		devlink_default_nodes_clear();
+
+	return err;
+}
+
+/**
+ * devl_apply_defaults - Apply defaults matching the devlink instance
+ * @devlink: devlink
+ *
+ * The caller must hold the devlink instance lock.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int devl_apply_defaults(struct devlink *devlink)
+{
+	const char *bus_name = devlink_bus_name(devlink);
+	const char *dev_name = devlink_dev_name(devlink);
+	struct devlink_default_node *node;
+
+	devl_assert_locked(devlink);
+
+	if (devlink_default_match_all)
+		return devlink_default_eswitch_apply(devlink);
+
+	node = devlink_default_node_find(bus_name, dev_name);
+	if (node)
+		return devlink_default_eswitch_apply(devlink);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devl_apply_defaults);
+
+static int __init devlink_default_setup(char *str)
+{
+	devlink_default = str;
+	return 1;
+}
+__setup("devlink=", devlink_default_setup);
+
 static struct devlink *devlinks_xa_get(unsigned long index)
 {
 	struct devlink *devlink;
@@ -578,6 +823,27 @@ static int __init devlink_init(void)
 {
 	int err;
 
+	if (devlink_default) {
+		char *def;
+
+		def = kstrdup(devlink_default, GFP_KERNEL);
+		if (!def) {
+			err = -ENOMEM;
+			goto out;
+		}
+		err = devlink_default_parse(def);
+		kfree(def);
+		if (err == -EEXIST) {
+			devlink_default = NULL;
+			pr_warn("devlink: duplicate handles ignored\n");
+		} else if (err == -EINVAL) {
+			devlink_default = NULL;
+			pr_warn("devlink: invalid command line parameter ignored\n");
+		} else if (err) {
+			goto out;
+		}
+	}
+
 	err = register_pernet_subsys(&devlink_pernet_ops);
 	if (err)
 		goto out;
@@ -593,7 +859,10 @@ static int __init devlink_init(void)
 out_unreg_pernet_subsys:
 	unregister_pernet_subsys(&devlink_pernet_ops);
 out:
+	if (err)
+		devlink_default_nodes_clear();
 	WARN_ON(err);
+
 	return err;
 }
 
-- 
2.34.1


^ permalink raw reply related

* [RFC V2 net-next 0/2] devlink: Add boot-time defaults
From: Mark Bloch @ 2026-05-10 18:54 UTC (permalink / raw)
  To: Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
	David S. Miller, Jiri Pirko
  Cc: Jonathan Corbet, Shuah Khan, Simon Horman, Saeed Mahameed,
	Leon Romanovsky, Tariq Toukan, Mark Bloch, Andrew Morton,
	Borislav Petkov (AMD), Randy Dunlap, Petr Mladek,
	Peter Zijlstra (Intel), Christian Brauner, Thomas Gleixner,
	Dapeng Mi, Kees Cook, Marco Elver, Li RongQing, Eric Biggers,
	Paul E. McKenney, linux-doc, linux-kernel, netdev, linux-rdma

This series adds a devlink= kernel command line parameter for applying
selected devlink settings during device initialization.

Following a discussion with Jakub[1], I am sending this RFC to get the
conversation moving. I started from Jakub's example/request and extended
it to cover requirements from production systems and configurations that
customers use. The implementation is intended to support the following
properties:

- A system may have multiple devlink devices that usually need the same
configuration. For a configuration such as eswitch mode switchdev, a user
can specify either all devlink devices or an explicit list of devices to
which that configuration applies.

- Deployments can set devlink defaults before normal userspace
orchestration runs, while still using devlink concepts and driver callbacks
rather than adding driver-specific module parameters.

A default is scoped to either all devlink handles or to a comma-separated
list of devlink handles, for example:

devlink=[*]:esw:mode:switchdev
devlink=[pci/0000:08:00.0,pci/0000:09:00.1]:esw:mode:switchdev_inactive

The first supported command is eswitch mode configuration.

mlx5 wires this into device initialization after the devlink instance is
registered and after mlx5 devlink operations are available, so eswitch mode
defaults can be applied to matching PCI devlink devices.

Patch 1 adds the devlink boot-default parser, storage,
devl_apply_defaults() API, eswitch mode default support and documentation
for the devlink= syntax.

Patch 2 calls devl_apply_defaults() from mlx5 device initialization.

Changelog:

V1 -> V2

- Reduced the series from 4 patches to 2 patches by folding the generic
boot default infrastructure and eswitch mode support into one devlink
patch.

- Narrowed the scope to eswitch mode defaults only. Dropped runtime
devlink parameter boot defaults and simplified parser.

- Added [*] selector support to apply the same eswitch mode default to all
devlink instances.

- Removed support for multiple comma-separated default groups and for
assigning different defaults to different handles in the same devlink=
parameter.

[1] https://lore.kernel.org/all/20260502184153.4fd8d06f@kernel.org/
V1 : https://lore.kernel.org/all/20260506123739.1959770-1-mbloch@nvidia.com/

Mark Bloch (2):
  devlink: Add eswitch mode boot defaults
  net/mlx5: Apply devlink boot defaults during init

 .../admin-guide/kernel-parameters.txt         |  24 ++
 .../networking/devlink/devlink-defaults.rst   |  93 ++++++
 Documentation/networking/devlink/index.rst    |   1 +
 .../net/ethernet/mellanox/mlx5/core/main.c    |   2 +
 include/net/devlink.h                         |   1 +
 net/devlink/core.c                            | 269 ++++++++++++++++++
 6 files changed, 390 insertions(+)
 create mode 100644 Documentation/networking/devlink/devlink-defaults.rst


base-commit: a93245816556ba03549bb626de48ea2b11c7f9d2
-- 
2.34.1


^ permalink raw reply

* [PATCH] docs/filesystems/9p: fix broken external links
From: Aayush Patil @ 2026-05-10 18:28 UTC (permalink / raw)
  To: ericvh, lucho, asmadeus
  Cc: linux_oss, corbet, skhan, v9fs, linux-doc, linux-kernel,
	Aayush Patil

The xcpu.org links for xcpu-talk, kvmfs, and cellfs-talk are dead
with no archived snapshots available on the Wayback Machine, so
remove them. The PROSE I/O link redirects to a dead server; replace
it with an archived version from web.archive.org.S

Signed-off-by: Aayush Patil <aayushpatilsch@gmail.com>
---
 Documentation/filesystems/9p.rst | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/Documentation/filesystems/9p.rst b/Documentation/filesystems/9p.rst
index be3504ca034a..65809a1dad21 100644
--- a/Documentation/filesystems/9p.rst
+++ b/Documentation/filesystems/9p.rst
@@ -23,13 +23,10 @@ the 9p client is available in the form of a USENIX paper:
 Other applications are described in the following papers:
 
 	* XCPU & Clustering
-	  http://xcpu.org/papers/xcpu-talk.pdf
 	* KVMFS: control file system for KVM
-	  http://xcpu.org/papers/kvmfs.pdf
 	* CellFS: A New Programming Model for the Cell BE
-	  http://xcpu.org/papers/cellfs-talk.pdf
 	* PROSE I/O: Using 9p to enable Application Partitions
-	  http://plan9.escet.urjc.es/iwp9/cready/PROSE_iwp9_2006.pdf
+	  http://web.archive.org/web/20110101152020/http://plan9.escet.urjc.es/iwp9/cready/PROSE_iwp9_2006.pdf
 	* VirtFS: A Virtualization Aware File System pass-through
 	  https://kernel.org/doc/ols/2010/ols2010-pages-109-120.pdf
 
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] crypto: af_alg - Document the deprecation of AF_ALG
From: Andy Lutomirski @ 2026-05-10 18:06 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Kamran Khan, Jeff Barnes, linux-crypto@vger.kernel.org,
	Herbert Xu, linux-doc@vger.kernel.org, linux-api@vger.kernel.org,
	linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	Linus Torvalds
In-Reply-To: <20260510163204.GA2279@sol>

On Sun, May 10, 2026 at 9:33 AM Eric Biggers <ebiggers@kernel.org> wrote:

> In any case, any hypothetical security benefit provided by AF_ALG would
> have to be *very high* to outweigh the continuous stream of
> vulnerabilities in it.  I understand that people using AF_ALG might not
> be familiar with that continuous stream of vulnerabilities, but it would
> be worth spending some time researching what has been going on.


It would not be completely crazy to have a simple, straightforward
interface by which user code could ask the kernel to do a
cryptographic operation.  Think:

int compute_keyed_hash(int key_fd, const void *data, size_t len);

where key_fd encodes both the key and the hash type (HMAC-SHA256 or
whatever), and there is a very, very small menu of hashes to choose
from.

But this is not really obviously worth the hassle.  And AF_ALG is
definitely not the right interface.

^ permalink raw reply

* Re: [PATCH] cpufreq-stats: document limitations on modern cpufreq drivers
From: Randy Dunlap @ 2026-05-10 17:33 UTC (permalink / raw)
  To: NicoErdmann, linux-pm; +Cc: linux-doc, rafael, viresh.kumar, corbet, skhan
In-Reply-To: <20260510143303.120863-1-nicobsc4@yahoo.com>



On 5/10/26 7:33 AM, NicoErdmann wrote:
> Signed-off-by: NicoErdmann <nicobsc4@yahoo.com>
> ---
>  Documentation/cpu-freq/cpufreq-stats.rst | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/Documentation/cpu-freq/cpufreq-stats.rst b/Documentation/cpu-freq/cpufreq-stats.rst
> index 9ad695b1c7db..e8a8b3a85ad8 100644
> --- a/Documentation/cpu-freq/cpufreq-stats.rst
> +++ b/Documentation/cpu-freq/cpufreq-stats.rst
> @@ -28,6 +28,13 @@ Various statistics will form read_only files under this directory.
>  This driver is designed to be independent of any particular cpufreq_driver
>  that may be running on your CPU. So, it will work with any cpufreq_driver.
>  
> +.. note::
> +	
> +   On some modern systems, this interface may not be available or may not
> +   expose meaningful statistics depending on the active CPU frequency scaling driver.
> +
> +   In particular, drivers such as intel_pstate or amd_pstate may use alternative
> +   mechanisms for frequency scaling and accounting

Missing an ending period.

>  
>  2. Statistics Provided (with example)
>  =====================================

-- 
~Randy


^ permalink raw reply

* Re: [PATCH 1/2] Doc: deprecated.rst: add strlcat()
From: Randy Dunlap @ 2026-05-10 17:32 UTC (permalink / raw)
  To: Manuel Ebner
  Cc: andy.shevchenko, apw, corbet, dwaipayanray1, joe, kees, linux-doc,
	linux-kernel, lukas.bulwahn, skhan, workflows
In-Reply-To: <20260510165451.57674-2-manuelebner@mailbox.org>



On 5/10/26 9:54 AM, Manuel Ebner wrote:
> add strlcat and alternatives
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> ---
>  Documentation/process/deprecated.rst | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst
> index fed56864d036..b8a65c19796c 100644
> --- a/Documentation/process/deprecated.rst
> +++ b/Documentation/process/deprecated.rst
> @@ -162,6 +162,12 @@ if a source string is not NUL-terminated. The safe replacement is strscpy(),
>  though care must be given to any cases where the return value of strlcpy()
>  is used, since strscpy() will return negative errno values when it truncates.
>  
> +strlcat()
> +---------
> +strlcat() must re-scan the destination string from the beginning on each
> +call (O(n^2) behavior). Alternatives are seq_buf_puts(), seq_buf_printf(),
> +snprintf() and scnprintf()

Add an ending period.

> +
>  %p format specifier
>  -------------------
>  Traditionally, using "%p" in format strings would lead to regular address

-- 
~Randy


^ permalink raw reply

* Re: [PATCH 2/2] scripts: checkpatch.pl: add warning for strlcat()
From: Randy Dunlap @ 2026-05-10 17:31 UTC (permalink / raw)
  To: Manuel Ebner
  Cc: andy.shevchenko, apw, corbet, dwaipayanray1, joe, kees, linux-doc,
	linux-kernel, lukas.bulwahn, skhan, workflows
In-Reply-To: <20260510165649.57880-2-manuelebner@mailbox.org>



On 5/10/26 9:56 AM, Manuel Ebner wrote:
> add a warning for strlcat()
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> ---
>  scripts/checkpatch.pl | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
> index 0492d6afc9a1..ca1a8e67d529 100755
> --- a/scripts/checkpatch.pl
> +++ b/scripts/checkpatch.pl
> @@ -7085,6 +7085,12 @@ sub process {
>  			     "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr);
>  		}
>  
> +# strlcat uses that should likely be

should likely be what?

> +		if ($line =~ /\bstrlcat\s*\(/ && !is_userspace($realfile)) {
> +			WARN("STRLCAT",
> +			     "Prefer seq_buf_printf() over strlcat - see: https://github.com/KSPP/linux/issues/370\n" . $herecurr);
> +		}
> +
>  # strncpy uses that should likely be strscpy or strscpy_pad
>  		if ($line =~ /\bstrncpy\s*\(/ && !is_userspace($realfile)) {
>  			WARN("STRNCPY",

-- 
~Randy


^ permalink raw reply

* [PATCH 2/2] scripts: checkpatch.pl: add warning for strlcat()
From: Manuel Ebner @ 2026-05-10 16:56 UTC (permalink / raw)
  To: manuelebner
  Cc: andy.shevchenko, apw, corbet, dwaipayanray1, joe, kees, linux-doc,
	linux-kernel, lukas.bulwahn, skhan, workflows
In-Reply-To: <20260510164907.57176-2-manuelebner@mailbox.org>

add a warning for strlcat()

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 scripts/checkpatch.pl | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 0492d6afc9a1..ca1a8e67d529 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -7085,6 +7085,12 @@ sub process {
 			     "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr);
 		}
 
+# strlcat uses that should likely be
+		if ($line =~ /\bstrlcat\s*\(/ && !is_userspace($realfile)) {
+			WARN("STRLCAT",
+			     "Prefer seq_buf_printf() over strlcat - see: https://github.com/KSPP/linux/issues/370\n" . $herecurr);
+		}
+
 # strncpy uses that should likely be strscpy or strscpy_pad
 		if ($line =~ /\bstrncpy\s*\(/ && !is_userspace($realfile)) {
 			WARN("STRNCPY",
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/2] Doc: deprecated.rst: add strlcat()
From: Manuel Ebner @ 2026-05-10 16:54 UTC (permalink / raw)
  To: manuelebner
  Cc: andy.shevchenko, apw, corbet, dwaipayanray1, joe, kees, linux-doc,
	linux-kernel, lukas.bulwahn, skhan, workflows
In-Reply-To: <20260510164907.57176-2-manuelebner@mailbox.org>

add strlcat and alternatives

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 Documentation/process/deprecated.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst
index fed56864d036..b8a65c19796c 100644
--- a/Documentation/process/deprecated.rst
+++ b/Documentation/process/deprecated.rst
@@ -162,6 +162,12 @@ if a source string is not NUL-terminated. The safe replacement is strscpy(),
 though care must be given to any cases where the return value of strlcpy()
 is used, since strscpy() will return negative errno values when it truncates.
 
+strlcat()
+---------
+strlcat() must re-scan the destination string from the beginning on each
+call (O(n^2) behavior). Alternatives are seq_buf_puts(), seq_buf_printf(),
+snprintf() and scnprintf()
+
 %p format specifier
 -------------------
 Traditionally, using "%p" in format strings would lead to regular address
-- 
2.54.0


^ permalink raw reply related

* [PATCH 1/2] [PATCH 1/2] Doc: deprecated.rst: add strlcat()
From: Manuel Ebner @ 2026-05-10 16:52 UTC (permalink / raw)
  To: manuelebner
  Cc: andy.shevchenko, apw, corbet, dwaipayanray1, joe, kees, linux-doc,
	linux-kernel, lukas.bulwahn, skhan, workflows
In-Reply-To: <20260510164907.57176-2-manuelebner@mailbox.org>

add strlcat and alternatives
---
 Documentation/process/deprecated.rst | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/process/deprecated.rst b/Documentation/process/deprecated.rst
index fed56864d036..b8a65c19796c 100644
--- a/Documentation/process/deprecated.rst
+++ b/Documentation/process/deprecated.rst
@@ -162,6 +162,12 @@ if a source string is not NUL-terminated. The safe replacement is strscpy(),
 though care must be given to any cases where the return value of strlcpy()
 is used, since strscpy() will return negative errno values when it truncates.
 
+strlcat()
+---------
+strlcat() must re-scan the destination string from the beginning on each
+call (O(n^2) behavior). Alternatives are seq_buf_puts(), seq_buf_printf(),
+snprintf() and scnprintf()
+
 %p format specifier
 -------------------
 Traditionally, using "%p" in format strings would lead to regular address
-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/2] Doc, scripts: facilitate phaseout of strlcat
From: Manuel Ebner @ 2026-05-10 16:49 UTC (permalink / raw)
  To: Andy Shevchenko, Kees Cook, Jonathan Corbet, Shuah Khan,
	Andy Whitcroft, Joe Perches, Dwaipayan Ray, Lukas Bulwahn,
	open list:DOCUMENTATION PROCESS, open list:DOCUMENTATION,
	open list
  Cc: Manuel Ebner

The goal of this series is to facilitate the transition from strlcat to better
alternatives.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox