Re: [PATCH v4] drm/ssd130x: Allocate buffers in the plane's .atomic_check callback

From: Maxime Ripard
Date: Wed Jul 26 2023 - 06:00:20 EST


On Tue, Jul 25, 2023 at 09:21:52PM +0200, Geert Uytterhoeven wrote:
> > --- a/drivers/gpu/drm/solomon/ssd130x.c
> > +++ b/drivers/gpu/drm/solomon/ssd130x.c
> > @@ -141,12 +141,26 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = {
> > };
> > EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X);
> >
> > +struct ssd130x_plane_state {
> > + struct drm_plane_state base;
> > + /* Intermediate buffer to convert pixels from XRGB8888 to R1 */
> > + u8 *buffer;
> > + /* Buffer that contains R1 pixels to be written to the panel */
> > + u8 *data_array;
>
> The second buffer actually contains pixels in hardware format.
> For now that is a transposed buffer of R1 pixels, but that will change
> when you will add support for greyscale displays.
>
> So I'd write "hardware format" instead of R1 for both.
>
> BTW, I still think data_array should be allocated during probing,
> as it is related to the hardware, not to a plane.

I somewhat disagree.

If I understood right during our discussion with Javier, the buffer size
derives from the mode size (height and width).

In KMS, the mode is tied to the KMS state, and thus you can expect the
mode to change every state commit. So the more logical thing to do is to
tie the buffer size (and thus the buffer pointer) to the state since
it's only valid for that particular state for all we know.

Of course, our case is allows use to simplify things since it's a fixed
mode, but one of Javier's goal with this driver was to provide a good
example we can refer people to, so I think it's worth keeping.

> > @@ -159,23 +173,23 @@ static int ssd130x_buf_alloc(struct ssd130x_device *ssd130x)
> >
> > pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
> >
> > - ssd130x->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL);
> > - if (!ssd130x->buffer)
> > + ssd130x_state->buffer = kcalloc(pitch, ssd130x->height, GFP_KERNEL);
> > + if (!ssd130x_state->buffer)
> > return -ENOMEM;
> >
> > - ssd130x->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL);
> > - if (!ssd130x->data_array) {
> > - kfree(ssd130x->buffer);
> > + ssd130x_state->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL);
> > + if (!ssd130x_state->data_array) {
> > + kfree(ssd130x_state->buffer);
>
> Should ssd130x_state->buffer be reset to NULL here?
> I.e. if .atomic_check() fails, will .atomic_destroy_state() be called,
> leading to a double free?

That's a good question.

I never really thought of that, but yeah, AFAIK atomic_destroy_state()
will be called when atomic_check() fails.

Which means that it's probably broken in a lot of drivers.

Also, Javier pointed me to a discussion you had on IRC about using devm
allocation here. We can't really do that. KMS devices are only freed
once the last userspace application closes its fd to the device file, so
you have an unbounded window during which the driver is still callable
by userspace (and thus can still trigger an atomic commit) but the
buffer would have been freed for a while.

The driver could use a bit more work on that area (by adding
drm_dev_enter/drm_dev_exit) but it still creates unnecessary risks to
use devm there.

> > +static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane)
> > +{
> > + struct ssd130x_plane_state *old_ssd130x_state;
> > + struct ssd130x_plane_state *ssd130x_state;
> > +
> > + if (WARN_ON(!plane->state))
> > + return NULL;
> > +
> > + old_ssd130x_state = to_ssd130x_plane_state(plane->state);
> > + ssd130x_state = kmemdup(old_ssd130x_state, sizeof(*ssd130x_state), GFP_KERNEL);
>
> I know this is the standard pattern, but the "dup" part is a bit
> silly here:
> - The ssd130x-specific parts in ssd130x_plane_state are zeroed below,
> - The base part is copied again in
> __drm_atomic_helper_plane_duplicate_state).
> So (for now) you might as well just call kzalloc() ;-)

Still if we ever add a field in the state structure, it will be
surprising that it's not copied over.

The code is there and looks good to me, so I'd rather keep it.

Attachment: signature.asc
Description: PGP signature