Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
To: Nitin Gote <nitin.r.gote@intel.com>, igt-dev@lists.freedesktop.org
Cc: Matthew Auld <matthew.auld@intel.com>,
	Kamil Konieczny <kamil.konieczny@linux.intel.com>
Subject: Re: [PATCH] tests/core_hotunplug: add *-with-load subtests exercising GPU workload during hotplug
Date: Fri, 21 Aug 2026 11:43:09 +0200	[thread overview]
Message-ID: <341d0d27d10daf87a9d2f4e191082b04dd69b9c1.camel@linux.intel.com> (raw)
In-Reply-To: <20260821065838.1865108-2-nitin.r.gote@intel.com>

Hi Nitin,

Good job, however, I think we still need some improvements.

On Fri, 2026-08-21 at 12:28 +0530, Nitin Gote wrote:
> The six fd-holding hot* subtests keep a DRM fd open across unbind/unplug
> but leave the GPU idle, so the kernel hotplug path is never exercised
> against an active workload.
> 
> Add six new dedicated *-with-load subtests that run a GPU spinner across
> the unbind/unplug sequence so the device is removed while a workload is
> actively using it.
> 
> The workload helpers use an explicit chipset dispatch block in
> workload_start()/stop() so that other vendors can contribute
> support by adding an else-if branch. New subtests are gated on
> workload_available(), which currently covers DRIVER_XE and DRIVER_INTEL,
> and returns false (SKIP) on any other driver.
> 
> igt_spin_free() is avoided in workload_stop() since the device may be
> gone by then -- igt_spin_end() writes the stop condition via a userspace
> mmap and lets the DRM fd close reclaim kernel-side resources.
> 
> Validated on BMG and DG2 with a KASAN-enabled kernel:
> all six new subtests pass with no KASAN reports.
> 
> v4: Rebase to latest igt.
>     Also the failure seen with the new core_hotunplug "with-load"
>     IGT subtests is fixed by kmd patch:
>     https://patchwork.freedesktop.org/series/171989/
> 
> v3: Extend struct gpu_workload with a union of vendor-named members
>     to match the chipset dispatch in workload_start()/stop(). (Janusz)
> 
> v2: Pass the hotunplug priv struct to workload_start()/stop()
>     and dispatch via an explicit if/else-if chipset block to invite
>     other vendors to contribute. (Janusz)
> 
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Cc: Kamil Konieczny <kamil.konieczny@linux.intel.com>
> Reviewed-by: Janusz Krzysztofik <janusz.krzysztofik@linux.intel.com>
> Signed-off-by: Nitin Gote <nitin.r.gote@intel.com>
> ---
>  tests/core_hotunplug.c | 294 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 294 insertions(+)
> 
> diff --git a/tests/core_hotunplug.c b/tests/core_hotunplug.c
> index 33de65369..b11a4231b 100644
> --- a/tests/core_hotunplug.c
> +++ b/tests/core_hotunplug.c
> @@ -80,6 +80,34 @@
>   * SUBTEST: unplug-rescan
>   * Description: Check if a device believed to be closed can be cleanly
>   *		unplugged, then restored
> + *
> + * SUBTEST: hotunbind-rebind-with-load
> + * Description: Check if the driver can be cleanly unbound from an open device
> + *		with a background GPU workload in flight, then released and
> + *		rebound
> + *
> + * SUBTEST: hotunplug-rescan-with-load
> + * Description: Check if an open device with a background GPU workload in
> + *		flight can be cleanly unplugged, then released and restored
> + *
> + * SUBTEST: hotrebind-with-load
> + * Description: Check if the driver can be cleanly rebound to a device with a
> + *		still open hot unbound driver instance while a background GPU
> + *		workload is in flight
> + *
> + * SUBTEST: hotreplug-with-load
> + * Description: Check if a hot unplugged and still open device can be cleanly
> + *		restored while a background GPU workload is in flight
> + *
> + * SUBTEST: hotrebind-lateclose-with-load
> + * Description: Check if a hot unbound driver instance still open after hot
> + *		rebind with a background GPU workload in flight can be cleanly
> + *		released
> + *
> + * SUBTEST: hotreplug-lateclose-with-load
> + * Description: Check if an instance of a still open while hot replugged
> + *		device with a background GPU workload in flight can be cleanly
> + *		released
>   */
>  
>  IGT_TEST_DESCRIPTION("Examine behavior of a driver on device hot unplug");
> @@ -550,6 +578,56 @@ static void post_healthcheck(struct hotunplug *priv)
>  	cleanup(priv);
>  }
>  
> +/* GPU workload helpers */
> +
> +struct gpu_workload {
> +	union {
> +		struct {
> +			igt_spin_t *spin;
> +			uint64_t ahnd;
> +		} intel; /* Xe and i915 */
> +	};
> +};
> +
> +static bool workload_available(int chipset)
> +{
> +	return chipset == DRIVER_XE || chipset == DRIVER_INTEL;

NIT: I think a switch (chipset) { case DRIVER_XE: ... } construct, here 
and consequently below in workload_start/stop, could be more effective and 
more easily extendable with potential future additions from other vendors, 
but that can be refactored later, on a first such addition.

> +}
> +
> +static void workload_start(struct hotunplug *priv, int fd,
> +			   struct gpu_workload *w)
> +{
> +	if (priv->chipset == DRIVER_XE || priv->chipset == DRIVER_INTEL) {
> +		local_debug("%s\n", "starting GPU spinner");
> +		priv->failure = "GPU workload start failure!";
> +		w->intel.ahnd = intel_allocator_open(fd, 0, INTEL_ALLOCATOR_RELOC);
> +		w->intel.spin = igt_spin_new(fd, .ahnd = w->intel.ahnd);
> +		priv->failure = NULL;
> +	}
> +}
> +
> +static void workload_stop(struct hotunplug *priv, struct gpu_workload *w)
> +{
> +	if (priv->chipset == DRIVER_XE || priv->chipset == DRIVER_INTEL) {
> +		if (!w->intel.spin)
> +			return;

Since igt_fail() can be called from inside igt_spin_new(), workload_start()
can fail leaving an open ahnd but no spin.  We need to handle that case.

> +
> +		/*
> +		 * The device may be gone (unbind/unplug), so avoid
> +		 * igt_spin_free(): on Xe it would wait forever on a syncobj
> +		 * that never signals; on i915 it would touch the dead fd via
> +		 * gem_munmap()/gem_close(). Instead, end the spinner via a
> +		 * userspace mmap write and let the DRM fd close reclaim the
> +		 * kernel-side resources.
> +		 */
> +		local_debug("%s\n", "stopping GPU spinner");
> +		igt_spin_end(w->intel.spin);
> +		put_ahnd(w->intel.ahnd);
> +		w->intel.spin = NULL;
> +		w->intel.ahnd = 0;
> +	}
> +}
> +
>  /* Subtests */
>  
>  static void unbind_rebind(struct hotunplug *priv)
> @@ -664,6 +742,150 @@ static void hotreplug_lateclose(struct hotunplug *priv)
>  	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
>  }
>  
> +static void hotunbind_rebind_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot unbind");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	driver_unbind(priv, "hot ", 0);

Since igt_fail() can be called from driver_unbind(), as well as from other 
functions called between workload_start() and workload_stop() in other 
subtests below, we need to address those cases to be on the safe side.
We could e.g.:
- embed struct gpu_workload inside struct hotunplug,
- extend cleanup() (or recover()) function with another workload_stop().

Thanks,
Janusz


> +
> +	workload_stop(priv, &w);
> +
> +	priv->fd.drm = close_device(priv->fd.drm, "late ", "unbound ");
> +	igt_assert_eq(priv->fd.drm, -1);
> +
> +	driver_bind(priv, 0);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
> +static void hotunplug_rescan_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot unplug");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	device_unplug(priv, "hot ", 0);
> +
> +	workload_stop(priv, &w);
> +
> +	priv->fd.drm = close_device(priv->fd.drm, "late ", "removed ");
> +	igt_assert_eq(priv->fd.drm, -1);
> +
> +	bus_rescan(priv, 0);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
> +static void hotrebind_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot rebind");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	driver_unbind(priv, "hot ", 60);
> +
> +	driver_bind(priv, 0);
> +
> +	workload_stop(priv, &w);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
> +static void hotreplug_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot replug");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	device_unplug(priv, "hot ", 60);
> +
> +	bus_rescan(priv, 0);
> +
> +	workload_stop(priv, &w);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
> +static void hotrebind_lateclose_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot rebind");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	driver_unbind(priv, "hot ", 60);
> +
> +	driver_bind(priv, 0);
> +
> +	workload_stop(priv, &w);
> +
> +	priv->fd.drm = close_device(priv->fd.drm, "late ", "unbound ");
> +	igt_assert_eq(priv->fd.drm, -1);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
> +static void hotreplug_lateclose_with_load(struct hotunplug *priv)
> +{
> +	struct gpu_workload w = { 0 };
> +
> +	pre_check(priv);
> +
> +	igt_require_f(workload_available(priv->chipset),
> +		      "No GPU workload support for this driver\n");
> +
> +	priv->fd.drm = local_drm_open_driver(false, "", " for hot replug");
> +
> +	workload_start(priv, priv->fd.drm, &w);
> +
> +	device_unplug(priv, "hot ", 60);
> +
> +	bus_rescan(priv, 0);
> +
> +	workload_stop(priv, &w);
> +
> +	priv->fd.drm = close_device(priv->fd.drm, "late ", "removed ");
> +	igt_assert_eq(priv->fd.drm, -1);
> +
> +	igt_assert_f(healthcheck(priv, false), "%s\n", priv->failure);
> +}
> +
>  /* Main */
>  
>  int igt_main()
> @@ -802,6 +1024,78 @@ int igt_main()
>  			recover(&priv);
>  	}
>  
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if the driver can be cleanly unbound from an open device with a background GPU workload in flight, then released and rebound");
> +		igt_subtest("hotunbind-rebind-with-load")
> +			hotunbind_rebind_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if an open device with a background GPU workload in flight can be cleanly unplugged, then released and restored");
> +		igt_subtest("hotunplug-rescan-with-load")
> +			hotunplug_rescan_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if the driver can be cleanly rebound to a device with a still open hot unbound driver instance while a background GPU workload is in flight");
> +		igt_subtest("hotrebind-with-load")
> +			hotrebind_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if a hot unplugged and still open device can be cleanly restored while a background GPU workload is in flight");
> +		igt_subtest("hotreplug-with-load")
> +			hotreplug_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if a hot unbound driver instance still open after hot rebind with a background GPU workload in flight can be cleanly released");
> +		igt_subtest("hotrebind-lateclose-with-load")
> +			hotrebind_lateclose_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
> +	igt_fixture()
> +		post_healthcheck(&priv);
> +
> +	igt_subtest_group() {
> +		igt_describe("Check if an instance of a still open while hot replugged device with a background GPU workload in flight can be cleanly released");
> +		igt_subtest("hotreplug-lateclose-with-load")
> +			hotreplug_lateclose_with_load(&priv);
> +
> +		igt_fixture()
> +			recover(&priv);
> +	}
> +
>  	igt_fixture() {
>  		post_healthcheck(&priv);
>  

  parent reply	other threads:[~2026-08-21  9:43 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-21  6:58 [PATCH] tests/core_hotunplug: add *-with-load subtests exercising GPU workload during hotplug Nitin Gote
2026-08-21  7:40 ` ✓ Xe.CI.BAT: success for tests/core_hotunplug: add *-with-load subtests exercising GPU workload during hotplug (rev6) Patchwork
2026-08-21  8:13 ` ✓ i915.CI.BAT: " Patchwork
2026-08-21  8:43 ` ✓ Xe.CI.FULL: " Patchwork
2026-08-21  9:43 ` Janusz Krzysztofik [this message]
2026-08-21 11:03 ` ✗ i915.CI.Full: failure " Patchwork
  -- strict thread matches above, loose matches on Subject: below --
2026-05-18 15:48 [PATCH] tests/core_hotunplug: add *-with-load subtests exercising GPU workload during hotplug Nitin Gote
2026-05-20 13:38 ` Janusz Krzysztofik
2026-05-21 12:46 ` Kamil Konieczny
2026-05-18 12:01 Nitin Gote
2026-05-18 12:53 ` Janusz Krzysztofik
2026-05-18  8:27 Nitin Gote
2026-05-18  8:48 ` Janusz Krzysztofik
2026-05-18  9:59   ` Gote, Nitin R

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=341d0d27d10daf87a9d2f4e191082b04dd69b9c1.camel@linux.intel.com \
    --to=janusz.krzysztofik@linux.intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kamil.konieczny@linux.intel.com \
    --cc=matthew.auld@intel.com \
    --cc=nitin.r.gote@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox