diff mbox series

drm/ssd130x: Add drm_panic support

Message ID 20240620222222.155933-1-javierm@redhat.com (mailing list archive)
State Rejected, archived
Headers show
Series drm/ssd130x: Add drm_panic support | expand

Commit Message

Javier Martinez Canillas June 20, 2024, 10:22 p.m. UTC
Add support for the drm_panic infrastructure, which allows to display
a user friendly message on the screen when a Linux kernel panic occurs.

The display controller doesn't scanout the framebuffer, but instead the
pixels are sent to the device using a transport bus. For this reason, a
.panic_flush handler is needed to flush the panic image to the display.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---

 drivers/gpu/drm/solomon/ssd130x.c | 64 +++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

Comments

Jocelyn Falempe June 21, 2024, 12:56 p.m. UTC | #1
On 21/06/2024 00:22, Javier Martinez Canillas wrote:
> Add support for the drm_panic infrastructure, which allows to display
> a user friendly message on the screen when a Linux kernel panic occurs.
> 
> The display controller doesn't scanout the framebuffer, but instead the
> pixels are sent to the device using a transport bus. For this reason, a
> .panic_flush handler is needed to flush the panic image to the display.

Thanks for this patch, that's really cool that drm_panic can work on 
this device too.

> 
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> ---
> 
>   drivers/gpu/drm/solomon/ssd130x.c | 64 +++++++++++++++++++++++++++++++
>   1 file changed, 64 insertions(+)
> 
> diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
> index 6f51bcf774e2..0bac97bd39b9 100644
> --- a/drivers/gpu/drm/solomon/ssd130x.c
> +++ b/drivers/gpu/drm/solomon/ssd130x.c
> @@ -32,6 +32,7 @@
>   #include <drm/drm_managed.h>
>   #include <drm/drm_modes.h>
>   #include <drm/drm_rect.h>
> +#include <drm/drm_panic.h>
>   #include <drm/drm_probe_helper.h>
>   
>   #include "ssd130x.h"
> @@ -1386,6 +1387,63 @@ static void ssd133x_primary_plane_atomic_disable(struct drm_plane *plane,
>   	drm_dev_exit(idx);
>   }
>   
> +static int ssd130x_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
> +							   struct drm_scanout_buffer *sb)
> +{
> +	struct drm_plane_state *plane_state = plane->state;
> +	struct drm_shadow_plane_state *shadow_plane_state;
> +
> +	if (!plane_state || !plane_state->fb || !plane_state->crtc)
> +		return -EINVAL;
> +
> +	shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> +
> +	sb->format = plane->state->fb->format;
> +	sb->width = plane->state->fb->width;
> +	sb->height = plane->state->fb->height;
> +	sb->pitch[0] = plane->state->fb->pitches[0];
> +	sb->map[0] = shadow_plane_state->data[0];
> +
> +	return 0;
> +}
> +
> +static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
> +{
> +	struct drm_plane_state *plane_state = plane->state;
> +	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> +	struct drm_crtc *crtc = plane_state->crtc;
> +	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
> +
> +	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
> +			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
> +			     &shadow_plane_state->fmtcnv_state);

ssd130x_fb_blit_rect() will call regmap->write(), which involve mutex 
and might sleep. And if the mutex is taken when the panic occurs, it 
might deadlock the panic handling.
One solution would be to configure the regmap with config->fast_io and 
config->use_raw_spinlock, and check that the lock is available with 
try_lock(map->raw_spin_lock)
But that means it will waste cpu cycle with busy waiting for normal 
operation, which is not good.

So for this particular device, I think it's ok, because it's unlikely 
you'll run kdump or other kernel panic handlers.
But I would like to know what others think about it, and if it's 
acceptable or not.
Javier Martinez Canillas June 21, 2024, 2:22 p.m. UTC | #2
Jocelyn Falempe <jfalempe@redhat.com> writes:

Hello Jocelyn, thanks for your feedback!

> On 21/06/2024 00:22, Javier Martinez Canillas wrote:
>> Add support for the drm_panic infrastructure, which allows to display
>> a user friendly message on the screen when a Linux kernel panic occurs.
>> 
>> The display controller doesn't scanout the framebuffer, but instead the
>> pixels are sent to the device using a transport bus. For this reason, a
>> .panic_flush handler is needed to flush the panic image to the display.
>
> Thanks for this patch, that's really cool that drm_panic can work on 
> this device too.
>

Indeed, that's why I did it. Just to see if it could work :)

[...]

>> +static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
>> +{
>> +	struct drm_plane_state *plane_state = plane->state;
>> +	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
>> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
>> +	struct drm_crtc *crtc = plane_state->crtc;
>> +	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
>> +
>> +	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
>> +			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
>> +			     &shadow_plane_state->fmtcnv_state);
>
> ssd130x_fb_blit_rect() will call regmap->write(), which involve mutex 
> and might sleep. And if the mutex is taken when the panic occurs, it 
> might deadlock the panic handling.

That's a good point and I something haven't considered...

> One solution would be to configure the regmap with config->fast_io and 
> config->use_raw_spinlock, and check that the lock is available with 
> try_lock(map->raw_spin_lock)
> But that means it will waste cpu cycle with busy waiting for normal 
> operation, which is not good.
>

Yeah, I would prefer to not change the driver for normal operation.

> So for this particular device, I think it's ok, because it's unlikely 
> you'll run kdump or other kernel panic handlers.
> But I would like to know what others think about it, and if it's 
> acceptable or not.
>

I don't know either. I guess that after a panic everything is best effort
anyways so it may be acceptable? But let's see what others think about it.

> -- 
>
> Jocelyn
Javier Martinez Canillas June 21, 2024, 3:42 p.m. UTC | #3
Javier Martinez Canillas <javierm@redhat.com> writes:

> Jocelyn Falempe <jfalempe@redhat.com> writes:
>
> Hello Jocelyn, thanks for your feedback!
>
>> On 21/06/2024 00:22, Javier Martinez Canillas wrote:
>>> Add support for the drm_panic infrastructure, which allows to display
>>> a user friendly message on the screen when a Linux kernel panic occurs.
>>> 
>>> The display controller doesn't scanout the framebuffer, but instead the
>>> pixels are sent to the device using a transport bus. For this reason, a
>>> .panic_flush handler is needed to flush the panic image to the display.
>>
>> Thanks for this patch, that's really cool that drm_panic can work on 
>> this device too.
>>
>
> Indeed, that's why I did it. Just to see if it could work :)
>
> [...]
>
>>> +static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
>>> +{
>>> +	struct drm_plane_state *plane_state = plane->state;
>>> +	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
>>> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
>>> +	struct drm_crtc *crtc = plane_state->crtc;
>>> +	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
>>> +
>>> +	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
>>> +			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
>>> +			     &shadow_plane_state->fmtcnv_state);
>>
>> ssd130x_fb_blit_rect() will call regmap->write(), which involve mutex 
>> and might sleep. And if the mutex is taken when the panic occurs, it 
>> might deadlock the panic handling.
>
> That's a good point and I something haven't considered...
>
>> One solution would be to configure the regmap with config->fast_io and 
>> config->use_raw_spinlock, and check that the lock is available with 
>> try_lock(map->raw_spin_lock)
>> But that means it will waste cpu cycle with busy waiting for normal 
>> operation, which is not good.
>>
>
> Yeah, I would prefer to not change the driver for normal operation.
>

Another option, that I believe makes more sense, is to just disable the
regmap locking (using struct regmap_config.disable_locking field [0]).

Since this regmap is not shared with other drivers and so any concurrent
access should already be prevented by the DRM core locking scheme.

Is my understanding correct?

[0]: https://elixir.bootlin.com/linux/v6.10-rc1/source/include/linux/regmap.h#L326
Daniel Vetter June 21, 2024, 4:37 p.m. UTC | #4
On Fri, Jun 21, 2024 at 05:42:53PM +0200, Javier Martinez Canillas wrote:
> Javier Martinez Canillas <javierm@redhat.com> writes:
> 
> > Jocelyn Falempe <jfalempe@redhat.com> writes:
> >
> > Hello Jocelyn, thanks for your feedback!
> >
> >> On 21/06/2024 00:22, Javier Martinez Canillas wrote:
> >>> Add support for the drm_panic infrastructure, which allows to display
> >>> a user friendly message on the screen when a Linux kernel panic occurs.
> >>> 
> >>> The display controller doesn't scanout the framebuffer, but instead the
> >>> pixels are sent to the device using a transport bus. For this reason, a
> >>> .panic_flush handler is needed to flush the panic image to the display.
> >>
> >> Thanks for this patch, that's really cool that drm_panic can work on 
> >> this device too.
> >>
> >
> > Indeed, that's why I did it. Just to see if it could work :)
> >
> > [...]
> >
> >>> +static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
> >>> +{
> >>> +	struct drm_plane_state *plane_state = plane->state;
> >>> +	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
> >>> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
> >>> +	struct drm_crtc *crtc = plane_state->crtc;
> >>> +	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
> >>> +
> >>> +	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
> >>> +			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
> >>> +			     &shadow_plane_state->fmtcnv_state);
> >>
> >> ssd130x_fb_blit_rect() will call regmap->write(), which involve mutex 
> >> and might sleep. And if the mutex is taken when the panic occurs, it 
> >> might deadlock the panic handling.
> >
> > That's a good point and I something haven't considered...
> >
> >> One solution would be to configure the regmap with config->fast_io and 
> >> config->use_raw_spinlock, and check that the lock is available with 
> >> try_lock(map->raw_spin_lock)
> >> But that means it will waste cpu cycle with busy waiting for normal 
> >> operation, which is not good.
> >>
> >
> > Yeah, I would prefer to not change the driver for normal operation.
> >
> 
> Another option, that I believe makes more sense, is to just disable the
> regmap locking (using struct regmap_config.disable_locking field [0]).
> 
> Since this regmap is not shared with other drivers and so any concurrent
> access should already be prevented by the DRM core locking scheme.
> 
> Is my understanding correct?

Quick irc discussion summary: Since these are panels that sit on i2c/spi
buses, you need to put the raw spinlock panic locking into these
subsystems. Which is going to be extreme amounts of fun, becuase:

- You need to protect innermost register access with a raw spinlock, but
  enough so that every access is still consistent.

- You need separate panic paths which avoid all the existing subsystem
  locking (i2c/spi have userspace apis, so they need mutexes) and only
  rely on the caller having done the raw spinlock trylocking.

- Bonus points: Who even owns that raw spinlock ...

I'm afraid, this is going to be a tough nut to crack :-/

Cheers, Sima
Javier Martinez Canillas June 21, 2024, 7:34 p.m. UTC | #5
Daniel Vetter <daniel@ffwll.ch> writes:

Hello Sima,

Thanks for your comment and explanations.

> On Fri, Jun 21, 2024 at 05:42:53PM +0200, Javier Martinez Canillas wrote:
>> Javier Martinez Canillas <javierm@redhat.com> writes:
>> 
>> > Jocelyn Falempe <jfalempe@redhat.com> writes:
>> >
>> > Hello Jocelyn, thanks for your feedback!
>> >
>> >> On 21/06/2024 00:22, Javier Martinez Canillas wrote:
>> >>> Add support for the drm_panic infrastructure, which allows to display
>> >>> a user friendly message on the screen when a Linux kernel panic occurs.
>> >>> 
>> >>> The display controller doesn't scanout the framebuffer, but instead the
>> >>> pixels are sent to the device using a transport bus. For this reason, a
>> >>> .panic_flush handler is needed to flush the panic image to the display.
>> >>
>> >> Thanks for this patch, that's really cool that drm_panic can work on 
>> >> this device too.
>> >>
>> >
>> > Indeed, that's why I did it. Just to see if it could work :)
>> >
>> > [...]
>> >
>> >>> +static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
>> >>> +{
>> >>> +	struct drm_plane_state *plane_state = plane->state;
>> >>> +	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
>> >>> +	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
>> >>> +	struct drm_crtc *crtc = plane_state->crtc;
>> >>> +	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
>> >>> +
>> >>> +	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
>> >>> +			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
>> >>> +			     &shadow_plane_state->fmtcnv_state);
>> >>
>> >> ssd130x_fb_blit_rect() will call regmap->write(), which involve mutex 
>> >> and might sleep. And if the mutex is taken when the panic occurs, it 
>> >> might deadlock the panic handling.
>> >
>> > That's a good point and I something haven't considered...
>> >
>> >> One solution would be to configure the regmap with config->fast_io and 
>> >> config->use_raw_spinlock, and check that the lock is available with 
>> >> try_lock(map->raw_spin_lock)
>> >> But that means it will waste cpu cycle with busy waiting for normal 
>> >> operation, which is not good.
>> >>
>> >
>> > Yeah, I would prefer to not change the driver for normal operation.
>> >
>> 
>> Another option, that I believe makes more sense, is to just disable the
>> regmap locking (using struct regmap_config.disable_locking field [0]).
>> 
>> Since this regmap is not shared with other drivers and so any concurrent
>> access should already be prevented by the DRM core locking scheme.
>> 
>> Is my understanding correct?
>
> Quick irc discussion summary: Since these are panels that sit on i2c/spi
> buses, you need to put the raw spinlock panic locking into these
> subsystems. Which is going to be extreme amounts of fun, becuase:
>
> - You need to protect innermost register access with a raw spinlock, but
>   enough so that every access is still consistent.
>
> - You need separate panic paths which avoid all the existing subsystem
>   locking (i2c/spi have userspace apis, so they need mutexes) and only
>   rely on the caller having done the raw spinlock trylocking.
>
> - Bonus points: Who even owns that raw spinlock ...
>
> I'm afraid, this is going to be a tough nut to crack :-/
>

Yeah, not worth the effort then. I'll just drop this patch.

> Cheers, Sima
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 6f51bcf774e2..0bac97bd39b9 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -32,6 +32,7 @@ 
 #include <drm/drm_managed.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_rect.h>
+#include <drm/drm_panic.h>
 #include <drm/drm_probe_helper.h>
 
 #include "ssd130x.h"
@@ -1386,6 +1387,63 @@  static void ssd133x_primary_plane_atomic_disable(struct drm_plane *plane,
 	drm_dev_exit(idx);
 }
 
+static int ssd130x_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
+							   struct drm_scanout_buffer *sb)
+{
+	struct drm_plane_state *plane_state = plane->state;
+	struct drm_shadow_plane_state *shadow_plane_state;
+
+	if (!plane_state || !plane_state->fb || !plane_state->crtc)
+		return -EINVAL;
+
+	shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+
+	sb->format = plane->state->fb->format;
+	sb->width = plane->state->fb->width;
+	sb->height = plane->state->fb->height;
+	sb->pitch[0] = plane->state->fb->pitches[0];
+	sb->map[0] = shadow_plane_state->data[0];
+
+	return 0;
+}
+
+static void ssd130x_primary_plane_helper_panic_flush(struct drm_plane *plane)
+{
+	struct drm_plane_state *plane_state = plane->state;
+	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
+	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+	struct drm_crtc *crtc = plane_state->crtc;
+	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
+
+	ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
+			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
+			     &shadow_plane_state->fmtcnv_state);
+}
+
+static void ssd132x_primary_plane_helper_panic_flush(struct drm_plane *plane)
+{
+	struct drm_plane_state *plane_state = plane->state;
+	struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state);
+	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+	struct drm_crtc *crtc = plane_state->crtc;
+	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
+
+	ssd132x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
+			     ssd130x_plane_state->buffer, ssd130x_crtc_state->data_array,
+			     &shadow_plane_state->fmtcnv_state);
+}
+
+static void ssd133x_primary_plane_helper_panic_flush(struct drm_plane *plane)
+{
+	struct drm_plane_state *plane_state = plane->state;
+	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+	struct drm_crtc *crtc = plane_state->crtc;
+	struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc->state);
+
+	ssd133x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst,
+			     ssd130x_crtc_state->data_array, &shadow_plane_state->fmtcnv_state);
+}
+
 /* Called during init to allocate the plane's atomic state. */
 static void ssd130x_primary_plane_reset(struct drm_plane *plane)
 {
@@ -1442,18 +1500,24 @@  static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs[]
 		.atomic_check = ssd130x_primary_plane_atomic_check,
 		.atomic_update = ssd130x_primary_plane_atomic_update,
 		.atomic_disable = ssd130x_primary_plane_atomic_disable,
+		.get_scanout_buffer = ssd130x_primary_plane_helper_get_scanout_buffer,
+		.panic_flush = ssd130x_primary_plane_helper_panic_flush,
 	},
 	[SSD132X_FAMILY] = {
 		DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
 		.atomic_check = ssd132x_primary_plane_atomic_check,
 		.atomic_update = ssd132x_primary_plane_atomic_update,
 		.atomic_disable = ssd132x_primary_plane_atomic_disable,
+		.get_scanout_buffer = ssd130x_primary_plane_helper_get_scanout_buffer,
+		.panic_flush = ssd132x_primary_plane_helper_panic_flush,
 	},
 	[SSD133X_FAMILY] = {
 		DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
 		.atomic_check = ssd133x_primary_plane_atomic_check,
 		.atomic_update = ssd133x_primary_plane_atomic_update,
 		.atomic_disable = ssd133x_primary_plane_atomic_disable,
+		.get_scanout_buffer = ssd130x_primary_plane_helper_get_scanout_buffer,
+		.panic_flush = ssd133x_primary_plane_helper_panic_flush,
 	}
 };