diff mbox series

[01/13] video: fb_defio: preserve user fb_ops

Message ID 448995ffd954e0cd2287089cb686e351cc095834.1574871797.git.jani.nikula@intel.com (mailing list archive)
State New, archived
Headers show
Series video, drm: constify fbops in struct fb_info | expand

Commit Message

Jani Nikula Nov. 27, 2019, 4:31 p.m. UTC
Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
and then resetting it to NULL afterwards causes problems all over the
place. First, it prevents making the fbops member of struct fb_info a
const pointer, which means we can't make struct fb_ops const
anywhere. Second, a few places have to go out of their way to restore
the original fb_mmap pointer that gets reset to NULL.

Preserve the passed in fb_ops by making a copy of it and modifying that
instead. Add a deferred_io_private member to struct fb_info to store the
pointer to the old fb_ops, and restore that at cleanup.

Cc: Jaya Kumar <jayalk@intworks.biz>
Cc: linux-fbdev@vger.kernel.org
Signed-off-by: Jani Nikula <jani.nikula@intel.com>

---

Note: If the approach is acceptable, we'll also need to handle the error
returns on memory allocation failures at fb_deferred_io_init() call
sites. There are 13.
---
 drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
 include/linux/fb.h                  |  3 ++-
 2 files changed, 24 insertions(+), 4 deletions(-)

Comments

Ville Syrjälä Nov. 27, 2019, 5:01 p.m. UTC | #1
On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> and then resetting it to NULL afterwards causes problems all over the
> place. First, it prevents making the fbops member of struct fb_info a
> const pointer, which means we can't make struct fb_ops const
> anywhere. Second, a few places have to go out of their way to restore
> the original fb_mmap pointer that gets reset to NULL.
> 
> Preserve the passed in fb_ops by making a copy of it and modifying that
> instead. Add a deferred_io_private member to struct fb_info to store the
> pointer to the old fb_ops, and restore that at cleanup.

Quite the horror show. I wonder how hard it would be to just have each
driver that can use defio provide a mmap hook that either dispatches
to the defio one or the native one depending if defio is used or not...

A few drivers at least seem to always use defio so those should be
trivial. Some others seem to make the decision dynamically, which
would require custom .mmap(). Though I suppose all of them could just
be of the form
foo_mmap_wrap{)
{
	if (info->defio)
		defio_mmap()
	else
		foo_mmap();
}

Hmm. Actually is .fb_mmap() called from anywhere but fb_mmap()? If not
we could just shove the defio check there? Or if it's called from
several places we could try to wrap all calls in _fb_mmap() or somesuch.

> 
> Cc: Jaya Kumar <jayalk@intworks.biz>
> Cc: linux-fbdev@vger.kernel.org
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> 
> ---
> 
> Note: If the approach is acceptable, we'll also need to handle the error
> returns on memory allocation failures at fb_deferred_io_init() call
> sites. There are 13.
> ---
>  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
>  include/linux/fb.h                  |  3 ++-
>  2 files changed, 24 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> index 82c20c6047b0..36697844c1e0 100644
> --- a/drivers/video/fbdev/core/fb_defio.c
> +++ b/drivers/video/fbdev/core/fb_defio.c
> @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
>  	mutex_unlock(&fbdefio->lock);
>  }
>  
> -void fb_deferred_io_init(struct fb_info *info)
> +int fb_deferred_io_init(struct fb_info *info)
>  {
>  	struct fb_deferred_io *fbdefio = info->fbdefio;
> +	struct fb_ops *fbops;
>  
>  	BUG_ON(!fbdefio);
> +
> +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> +	if (!fbops)
> +		return -ENOMEM;
> +
> +	fbops->fb_mmap = fb_deferred_io_mmap;
> +	info->deferred_io_private = info->fbops;
> +	info->fbops = fbops;
> +
>  	mutex_init(&fbdefio->lock);
> -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> +
>  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>  	INIT_LIST_HEAD(&fbdefio->pagelist);
>  	if (fbdefio->delay == 0) /* set a default of 1 s */
> @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>  	int i;
>  
>  	BUG_ON(!fbdefio);
> +
> +	/* sanity check against misuse */
> +	if (WARN_ON(!info->deferred_io_private ||
> +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> +		return;
> +
>  	cancel_delayed_work_sync(&info->deferred_work);
>  
>  	/* clear out the mapping that we setup */
> @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>  		page->mapping = NULL;
>  	}
>  
> -	info->fbops->fb_mmap = NULL;
> +	kfree(info->fbops);
> +	info->fbops = info->deferred_io_private;
> +	info->deferred_io_private = NULL;
> +
>  	mutex_destroy(&fbdefio->lock);
>  }
>  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> diff --git a/include/linux/fb.h b/include/linux/fb.h
> index a6ad528990de..65f2abd47745 100644
> --- a/include/linux/fb.h
> +++ b/include/linux/fb.h
> @@ -470,6 +470,7 @@ struct fb_info {
>  #ifdef CONFIG_FB_DEFERRED_IO
>  	struct delayed_work deferred_work;
>  	struct fb_deferred_io *fbdefio;
> +	void *deferred_io_private;
>  #endif
>  
>  	struct fb_ops *fbops;
> @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
>  
>  /* drivers/video/fb_defio.c */
>  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> -extern void fb_deferred_io_init(struct fb_info *info);
> +extern int fb_deferred_io_init(struct fb_info *info);
>  extern void fb_deferred_io_open(struct fb_info *info,
>  				struct inode *inode,
>  				struct file *file);
> -- 
> 2.20.1
Daniel Vetter Nov. 27, 2019, 6:09 p.m. UTC | #2
On Wed, Nov 27, 2019 at 07:01:16PM +0200, Ville Syrjälä wrote:
> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> > and then resetting it to NULL afterwards causes problems all over the
> > place. First, it prevents making the fbops member of struct fb_info a
> > const pointer, which means we can't make struct fb_ops const
> > anywhere. Second, a few places have to go out of their way to restore
> > the original fb_mmap pointer that gets reset to NULL.
> > 
> > Preserve the passed in fb_ops by making a copy of it and modifying that
> > instead. Add a deferred_io_private member to struct fb_info to store the
> > pointer to the old fb_ops, and restore that at cleanup.
> 
> Quite the horror show. I wonder how hard it would be to just have each
> driver that can use defio provide a mmap hook that either dispatches
> to the defio one or the native one depending if defio is used or not...
> 
> A few drivers at least seem to always use defio so those should be
> trivial. Some others seem to make the decision dynamically, which
> would require custom .mmap(). Though I suppose all of them could just
> be of the form
> foo_mmap_wrap{)
> {
> 	if (info->defio)
> 		defio_mmap()
> 	else
> 		foo_mmap();
> }
> 
> Hmm. Actually is .fb_mmap() called from anywhere but fb_mmap()? If not
> we could just shove the defio check there? Or if it's called from
> several places we could try to wrap all calls in _fb_mmap() or somesuch.

While we're spinning this garn about fb_mmap improvements: My cunning
plan, documented in todo.rst even, is to have our own fb_mmap
implementation for the drm fbdev helper, which will handle all this
internally.

And then just quietly burn down the fbdev defio stuff to the ground,
because it's the stuff for nightmares.

https://dri.freedesktop.org/docs/drm/gpu/todo.html#generic-fbdev-defio-support

tldr; Imo no point investing too much in polishing the defio stuff.
-Daniel

> 
> > 
> > Cc: Jaya Kumar <jayalk@intworks.biz>
> > Cc: linux-fbdev@vger.kernel.org
> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> > 
> > ---
> > 
> > Note: If the approach is acceptable, we'll also need to handle the error
> > returns on memory allocation failures at fb_deferred_io_init() call
> > sites. There are 13.
> > ---
> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
> >  include/linux/fb.h                  |  3 ++-
> >  2 files changed, 24 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> > index 82c20c6047b0..36697844c1e0 100644
> > --- a/drivers/video/fbdev/core/fb_defio.c
> > +++ b/drivers/video/fbdev/core/fb_defio.c
> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
> >  	mutex_unlock(&fbdefio->lock);
> >  }
> >  
> > -void fb_deferred_io_init(struct fb_info *info)
> > +int fb_deferred_io_init(struct fb_info *info)
> >  {
> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
> > +	struct fb_ops *fbops;
> >  
> >  	BUG_ON(!fbdefio);
> > +
> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> > +	if (!fbops)
> > +		return -ENOMEM;
> > +
> > +	fbops->fb_mmap = fb_deferred_io_mmap;
> > +	info->deferred_io_private = info->fbops;
> > +	info->fbops = fbops;
> > +
> >  	mutex_init(&fbdefio->lock);
> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> > +
> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >  	int i;
> >  
> >  	BUG_ON(!fbdefio);
> > +
> > +	/* sanity check against misuse */
> > +	if (WARN_ON(!info->deferred_io_private ||
> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> > +		return;
> > +
> >  	cancel_delayed_work_sync(&info->deferred_work);
> >  
> >  	/* clear out the mapping that we setup */
> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >  		page->mapping = NULL;
> >  	}
> >  
> > -	info->fbops->fb_mmap = NULL;
> > +	kfree(info->fbops);
> > +	info->fbops = info->deferred_io_private;
> > +	info->deferred_io_private = NULL;
> > +
> >  	mutex_destroy(&fbdefio->lock);
> >  }
> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> > diff --git a/include/linux/fb.h b/include/linux/fb.h
> > index a6ad528990de..65f2abd47745 100644
> > --- a/include/linux/fb.h
> > +++ b/include/linux/fb.h
> > @@ -470,6 +470,7 @@ struct fb_info {
> >  #ifdef CONFIG_FB_DEFERRED_IO
> >  	struct delayed_work deferred_work;
> >  	struct fb_deferred_io *fbdefio;
> > +	void *deferred_io_private;
> >  #endif
> >  
> >  	struct fb_ops *fbops;
> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
> >  
> >  /* drivers/video/fb_defio.c */
> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> > -extern void fb_deferred_io_init(struct fb_info *info);
> > +extern int fb_deferred_io_init(struct fb_info *info);
> >  extern void fb_deferred_io_open(struct fb_info *info,
> >  				struct inode *inode,
> >  				struct file *file);
> > -- 
> > 2.20.1
> 
> -- 
> Ville Syrjälä
> Intel
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Daniel Vetter Nov. 27, 2019, 6:17 p.m. UTC | #3
On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> and then resetting it to NULL afterwards causes problems all over the
> place. First, it prevents making the fbops member of struct fb_info a
> const pointer, which means we can't make struct fb_ops const
> anywhere. Second, a few places have to go out of their way to restore
> the original fb_mmap pointer that gets reset to NULL.
> 
> Preserve the passed in fb_ops by making a copy of it and modifying that
> instead. Add a deferred_io_private member to struct fb_info to store the
> pointer to the old fb_ops, and restore that at cleanup.
> 
> Cc: Jaya Kumar <jayalk@intworks.biz>
> Cc: linux-fbdev@vger.kernel.org
> Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> 
> ---
> 
> Note: If the approach is acceptable, we'll also need to handle the error
> returns on memory allocation failures at fb_deferred_io_init() call
> sites. There are 13.

it's fbdev defio, I think we can do worse with less effort. Just embed a
copy of fb_ops into fb_info, and use that, and tada! no memory allocation
needed :-)

I'd totally r-b that patch.

Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
the _init function and in fb_mmap call fb_deferred_io_mmap for that case
instead of the driver's fb_ops->fb_mmap. There's only one caller of that
in the entire tree, in fbmem.c. Also, we could/should nuke the
EXPORT_SYMBOL(fb_deferred_io_mmap) I think.

That version would also get my r-b stamp. So up to you what you prefer.
-Daniel

> ---
>  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
>  include/linux/fb.h                  |  3 ++-
>  2 files changed, 24 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> index 82c20c6047b0..36697844c1e0 100644
> --- a/drivers/video/fbdev/core/fb_defio.c
> +++ b/drivers/video/fbdev/core/fb_defio.c
> @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
>  	mutex_unlock(&fbdefio->lock);
>  }
>  
> -void fb_deferred_io_init(struct fb_info *info)
> +int fb_deferred_io_init(struct fb_info *info)
>  {
>  	struct fb_deferred_io *fbdefio = info->fbdefio;
> +	struct fb_ops *fbops;
>  
>  	BUG_ON(!fbdefio);
> +
> +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> +	if (!fbops)
> +		return -ENOMEM;
> +
> +	fbops->fb_mmap = fb_deferred_io_mmap;
> +	info->deferred_io_private = info->fbops;
> +	info->fbops = fbops;
> +
>  	mutex_init(&fbdefio->lock);
> -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> +
>  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>  	INIT_LIST_HEAD(&fbdefio->pagelist);
>  	if (fbdefio->delay == 0) /* set a default of 1 s */
> @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>  	int i;
>  
>  	BUG_ON(!fbdefio);
> +
> +	/* sanity check against misuse */
> +	if (WARN_ON(!info->deferred_io_private ||
> +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> +		return;
> +
>  	cancel_delayed_work_sync(&info->deferred_work);
>  
>  	/* clear out the mapping that we setup */
> @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>  		page->mapping = NULL;
>  	}
>  
> -	info->fbops->fb_mmap = NULL;
> +	kfree(info->fbops);
> +	info->fbops = info->deferred_io_private;
> +	info->deferred_io_private = NULL;
> +
>  	mutex_destroy(&fbdefio->lock);
>  }
>  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> diff --git a/include/linux/fb.h b/include/linux/fb.h
> index a6ad528990de..65f2abd47745 100644
> --- a/include/linux/fb.h
> +++ b/include/linux/fb.h
> @@ -470,6 +470,7 @@ struct fb_info {
>  #ifdef CONFIG_FB_DEFERRED_IO
>  	struct delayed_work deferred_work;
>  	struct fb_deferred_io *fbdefio;
> +	void *deferred_io_private;
>  #endif
>  
>  	struct fb_ops *fbops;
> @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
>  
>  /* drivers/video/fb_defio.c */
>  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> -extern void fb_deferred_io_init(struct fb_info *info);
> +extern int fb_deferred_io_init(struct fb_info *info);
>  extern void fb_deferred_io_open(struct fb_info *info,
>  				struct inode *inode,
>  				struct file *file);
> -- 
> 2.20.1
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Daniel Vetter Nov. 27, 2019, 6:21 p.m. UTC | #4
On Wed, Nov 27, 2019 at 07:17:41PM +0100, Daniel Vetter wrote:
> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> > and then resetting it to NULL afterwards causes problems all over the
> > place. First, it prevents making the fbops member of struct fb_info a
> > const pointer, which means we can't make struct fb_ops const
> > anywhere. Second, a few places have to go out of their way to restore
> > the original fb_mmap pointer that gets reset to NULL.
> > 
> > Preserve the passed in fb_ops by making a copy of it and modifying that
> > instead. Add a deferred_io_private member to struct fb_info to store the
> > pointer to the old fb_ops, and restore that at cleanup.
> > 
> > Cc: Jaya Kumar <jayalk@intworks.biz>
> > Cc: linux-fbdev@vger.kernel.org
> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> > 
> > ---
> > 
> > Note: If the approach is acceptable, we'll also need to handle the error
> > returns on memory allocation failures at fb_deferred_io_init() call
> > sites. There are 13.
> 
> it's fbdev defio, I think we can do worse with less effort. Just embed a
> copy of fb_ops into fb_info, and use that, and tada! no memory allocation
> needed :-)
> 
> I'd totally r-b that patch.
> 
> Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
> the _init function and in fb_mmap call fb_deferred_io_mmap for that case
> instead of the driver's fb_ops->fb_mmap. There's only one caller of that
> in the entire tree, in fbmem.c. Also, we could/should nuke the
> EXPORT_SYMBOL(fb_deferred_io_mmap) I think.

I just realized that fb_info->fbdefio is a pointer, so this would be
really simple to pull off I think.
-Daniel

> 
> That version would also get my r-b stamp. So up to you what you prefer.
> -Daniel
> 
> > ---
> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
> >  include/linux/fb.h                  |  3 ++-
> >  2 files changed, 24 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> > index 82c20c6047b0..36697844c1e0 100644
> > --- a/drivers/video/fbdev/core/fb_defio.c
> > +++ b/drivers/video/fbdev/core/fb_defio.c
> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
> >  	mutex_unlock(&fbdefio->lock);
> >  }
> >  
> > -void fb_deferred_io_init(struct fb_info *info)
> > +int fb_deferred_io_init(struct fb_info *info)
> >  {
> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
> > +	struct fb_ops *fbops;
> >  
> >  	BUG_ON(!fbdefio);
> > +
> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> > +	if (!fbops)
> > +		return -ENOMEM;
> > +
> > +	fbops->fb_mmap = fb_deferred_io_mmap;
> > +	info->deferred_io_private = info->fbops;
> > +	info->fbops = fbops;
> > +
> >  	mutex_init(&fbdefio->lock);
> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> > +
> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >  	int i;
> >  
> >  	BUG_ON(!fbdefio);
> > +
> > +	/* sanity check against misuse */
> > +	if (WARN_ON(!info->deferred_io_private ||
> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> > +		return;
> > +
> >  	cancel_delayed_work_sync(&info->deferred_work);
> >  
> >  	/* clear out the mapping that we setup */
> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >  		page->mapping = NULL;
> >  	}
> >  
> > -	info->fbops->fb_mmap = NULL;
> > +	kfree(info->fbops);
> > +	info->fbops = info->deferred_io_private;
> > +	info->deferred_io_private = NULL;
> > +
> >  	mutex_destroy(&fbdefio->lock);
> >  }
> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> > diff --git a/include/linux/fb.h b/include/linux/fb.h
> > index a6ad528990de..65f2abd47745 100644
> > --- a/include/linux/fb.h
> > +++ b/include/linux/fb.h
> > @@ -470,6 +470,7 @@ struct fb_info {
> >  #ifdef CONFIG_FB_DEFERRED_IO
> >  	struct delayed_work deferred_work;
> >  	struct fb_deferred_io *fbdefio;
> > +	void *deferred_io_private;
> >  #endif
> >  
> >  	struct fb_ops *fbops;
> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
> >  
> >  /* drivers/video/fb_defio.c */
> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> > -extern void fb_deferred_io_init(struct fb_info *info);
> > +extern int fb_deferred_io_init(struct fb_info *info);
> >  extern void fb_deferred_io_open(struct fb_info *info,
> >  				struct inode *inode,
> >  				struct file *file);
> > -- 
> > 2.20.1
> > 
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
Jani Nikula Nov. 28, 2019, 9:09 a.m. UTC | #5
On Wed, 27 Nov 2019, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Wed, Nov 27, 2019 at 07:17:41PM +0100, Daniel Vetter wrote:
>> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
>> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
>> > and then resetting it to NULL afterwards causes problems all over the
>> > place. First, it prevents making the fbops member of struct fb_info a
>> > const pointer, which means we can't make struct fb_ops const
>> > anywhere. Second, a few places have to go out of their way to restore
>> > the original fb_mmap pointer that gets reset to NULL.
>> > 
>> > Preserve the passed in fb_ops by making a copy of it and modifying that
>> > instead. Add a deferred_io_private member to struct fb_info to store the
>> > pointer to the old fb_ops, and restore that at cleanup.
>> > 
>> > Cc: Jaya Kumar <jayalk@intworks.biz>
>> > Cc: linux-fbdev@vger.kernel.org
>> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
>> > 
>> > ---
>> > 
>> > Note: If the approach is acceptable, we'll also need to handle the error
>> > returns on memory allocation failures at fb_deferred_io_init() call
>> > sites. There are 13.
>> 
>> it's fbdev defio, I think we can do worse with less effort. Just embed a
>> copy of fb_ops into fb_info, and use that, and tada! no memory allocation
>> needed :-)
>> 
>> I'd totally r-b that patch.
>> 
>> Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
>> the _init function and in fb_mmap call fb_deferred_io_mmap for that case
>> instead of the driver's fb_ops->fb_mmap. There's only one caller of that
>> in the entire tree, in fbmem.c. Also, we could/should nuke the
>> EXPORT_SYMBOL(fb_deferred_io_mmap) I think.
>
> I just realized that fb_info->fbdefio is a pointer, so this would be
> really simple to pull off I think.

Heh, having a

	int (*fb_deferred_io_mmap)(struct fb_info *, struct vm_area_struct *);

member in struct fb_info, and using that in fbmem.c if non-NULL, was
actually my first idea. I didn't think it was particularly pretty, but
if we don't care about aesthetics...

Would you like that instead of the patch at hand?


BR,
Jani.


> -Daniel
>
>> 
>> That version would also get my r-b stamp. So up to you what you prefer.
>> -Daniel
>> 
>> > ---
>> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
>> >  include/linux/fb.h                  |  3 ++-
>> >  2 files changed, 24 insertions(+), 4 deletions(-)
>> > 
>> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
>> > index 82c20c6047b0..36697844c1e0 100644
>> > --- a/drivers/video/fbdev/core/fb_defio.c
>> > +++ b/drivers/video/fbdev/core/fb_defio.c
>> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
>> >  	mutex_unlock(&fbdefio->lock);
>> >  }
>> >  
>> > -void fb_deferred_io_init(struct fb_info *info)
>> > +int fb_deferred_io_init(struct fb_info *info)
>> >  {
>> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
>> > +	struct fb_ops *fbops;
>> >  
>> >  	BUG_ON(!fbdefio);
>> > +
>> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
>> > +	if (!fbops)
>> > +		return -ENOMEM;
>> > +
>> > +	fbops->fb_mmap = fb_deferred_io_mmap;
>> > +	info->deferred_io_private = info->fbops;
>> > +	info->fbops = fbops;
>> > +
>> >  	mutex_init(&fbdefio->lock);
>> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
>> > +
>> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
>> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
>> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>> >  	int i;
>> >  
>> >  	BUG_ON(!fbdefio);
>> > +
>> > +	/* sanity check against misuse */
>> > +	if (WARN_ON(!info->deferred_io_private ||
>> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
>> > +		return;
>> > +
>> >  	cancel_delayed_work_sync(&info->deferred_work);
>> >  
>> >  	/* clear out the mapping that we setup */
>> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>> >  		page->mapping = NULL;
>> >  	}
>> >  
>> > -	info->fbops->fb_mmap = NULL;
>> > +	kfree(info->fbops);
>> > +	info->fbops = info->deferred_io_private;
>> > +	info->deferred_io_private = NULL;
>> > +
>> >  	mutex_destroy(&fbdefio->lock);
>> >  }
>> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
>> > diff --git a/include/linux/fb.h b/include/linux/fb.h
>> > index a6ad528990de..65f2abd47745 100644
>> > --- a/include/linux/fb.h
>> > +++ b/include/linux/fb.h
>> > @@ -470,6 +470,7 @@ struct fb_info {
>> >  #ifdef CONFIG_FB_DEFERRED_IO
>> >  	struct delayed_work deferred_work;
>> >  	struct fb_deferred_io *fbdefio;
>> > +	void *deferred_io_private;
>> >  #endif
>> >  
>> >  	struct fb_ops *fbops;
>> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
>> >  
>> >  /* drivers/video/fb_defio.c */
>> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
>> > -extern void fb_deferred_io_init(struct fb_info *info);
>> > +extern int fb_deferred_io_init(struct fb_info *info);
>> >  extern void fb_deferred_io_open(struct fb_info *info,
>> >  				struct inode *inode,
>> >  				struct file *file);
>> > -- 
>> > 2.20.1
>> > 
>> > _______________________________________________
>> > Intel-gfx mailing list
>> > Intel-gfx@lists.freedesktop.org
>> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>> 
>> -- 
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
Daniel Vetter Nov. 28, 2019, 10:05 a.m. UTC | #6
On Thu, Nov 28, 2019 at 11:09:46AM +0200, Jani Nikula wrote:
> On Wed, 27 Nov 2019, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Wed, Nov 27, 2019 at 07:17:41PM +0100, Daniel Vetter wrote:
> >> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> >> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> >> > and then resetting it to NULL afterwards causes problems all over the
> >> > place. First, it prevents making the fbops member of struct fb_info a
> >> > const pointer, which means we can't make struct fb_ops const
> >> > anywhere. Second, a few places have to go out of their way to restore
> >> > the original fb_mmap pointer that gets reset to NULL.
> >> > 
> >> > Preserve the passed in fb_ops by making a copy of it and modifying that
> >> > instead. Add a deferred_io_private member to struct fb_info to store the
> >> > pointer to the old fb_ops, and restore that at cleanup.
> >> > 
> >> > Cc: Jaya Kumar <jayalk@intworks.biz>
> >> > Cc: linux-fbdev@vger.kernel.org
> >> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> >> > 
> >> > ---
> >> > 
> >> > Note: If the approach is acceptable, we'll also need to handle the error
> >> > returns on memory allocation failures at fb_deferred_io_init() call
> >> > sites. There are 13.
> >> 
> >> it's fbdev defio, I think we can do worse with less effort. Just embed a
> >> copy of fb_ops into fb_info, and use that, and tada! no memory allocation
> >> needed :-)
> >> 
> >> I'd totally r-b that patch.
> >> 
> >> Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
> >> the _init function and in fb_mmap call fb_deferred_io_mmap for that case
> >> instead of the driver's fb_ops->fb_mmap. There's only one caller of that
> >> in the entire tree, in fbmem.c. Also, we could/should nuke the
> >> EXPORT_SYMBOL(fb_deferred_io_mmap) I think.
> >
> > I just realized that fb_info->fbdefio is a pointer, so this would be
> > really simple to pull off I think.
> 
> Heh, having a
> 
> 	int (*fb_deferred_io_mmap)(struct fb_info *, struct vm_area_struct *);
> 
> member in struct fb_info, and using that in fbmem.c if non-NULL, was
> actually my first idea. I didn't think it was particularly pretty, but
> if we don't care about aesthetics...
> 
> Would you like that instead of the patch at hand?


diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 82c20c6047b0..9275c6bd71da 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -206,13 +206,11 @@ void fb_deferred_io_init(struct fb_info *info)
 
 	BUG_ON(!fbdefio);
 	mutex_init(&fbdefio->lock);
-	info->fbops->fb_mmap = fb_deferred_io_mmap;
 	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
 	INIT_LIST_HEAD(&fbdefio->pagelist);
 	if (fbdefio->delay == 0) /* set a default of 1 s */
 		fbdefio->delay = HZ;
 }
-EXPORT_SYMBOL_GPL(fb_deferred_io_init);
 
 void fb_deferred_io_open(struct fb_info *info,
 			 struct inode *inode,
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 86b06a599f96..6af627f281c3 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1341,7 +1341,16 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
 		return -ENODEV;
 	fb = info->fbops;
 	mutex_lock(&info->mm_lock);
-	if (fb->fb_mmap) {
+	if (fb->fbdefio) {
+		/*
+		 * The framebuffer needs to be accessed decrypted, be sure
+		 * SME protection is removed ahead of the call
+		 */
+		vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+		res = fb_deferred_io_mmap(info, vma);
+		mutex_unlock(&info->mm_lock);
+		return res;
+	} else if (fb->fb_mmap) {
 		int res;
 
 		/*

Is what I was thinking off as the pretty solution. Add an explicit
fb_info->fbdefio_enabled boolean if you don't feel like auditing all the
drivers for whether they really call defio_init() every time they assign
something to that pointer. A quick scan brought some nasties to light in
that area.

I think a function pointer here is pointless because we clearly don't need
it, and with all the panic around function pointers a direct call feels
much better :-)
-Daniel

> 
> BR,
> Jani.
> 
> 
> > -Daniel
> >
> >> 
> >> That version would also get my r-b stamp. So up to you what you prefer.
> >> -Daniel
> >> 
> >> > ---
> >> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
> >> >  include/linux/fb.h                  |  3 ++-
> >> >  2 files changed, 24 insertions(+), 4 deletions(-)
> >> > 
> >> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> >> > index 82c20c6047b0..36697844c1e0 100644
> >> > --- a/drivers/video/fbdev/core/fb_defio.c
> >> > +++ b/drivers/video/fbdev/core/fb_defio.c
> >> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
> >> >  	mutex_unlock(&fbdefio->lock);
> >> >  }
> >> >  
> >> > -void fb_deferred_io_init(struct fb_info *info)
> >> > +int fb_deferred_io_init(struct fb_info *info)
> >> >  {
> >> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
> >> > +	struct fb_ops *fbops;
> >> >  
> >> >  	BUG_ON(!fbdefio);
> >> > +
> >> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> >> > +	if (!fbops)
> >> > +		return -ENOMEM;
> >> > +
> >> > +	fbops->fb_mmap = fb_deferred_io_mmap;
> >> > +	info->deferred_io_private = info->fbops;
> >> > +	info->fbops = fbops;
> >> > +
> >> >  	mutex_init(&fbdefio->lock);
> >> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> >> > +
> >> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
> >> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
> >> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
> >> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >> >  	int i;
> >> >  
> >> >  	BUG_ON(!fbdefio);
> >> > +
> >> > +	/* sanity check against misuse */
> >> > +	if (WARN_ON(!info->deferred_io_private ||
> >> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> >> > +		return;
> >> > +
> >> >  	cancel_delayed_work_sync(&info->deferred_work);
> >> >  
> >> >  	/* clear out the mapping that we setup */
> >> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> >> >  		page->mapping = NULL;
> >> >  	}
> >> >  
> >> > -	info->fbops->fb_mmap = NULL;
> >> > +	kfree(info->fbops);
> >> > +	info->fbops = info->deferred_io_private;
> >> > +	info->deferred_io_private = NULL;
> >> > +
> >> >  	mutex_destroy(&fbdefio->lock);
> >> >  }
> >> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> >> > diff --git a/include/linux/fb.h b/include/linux/fb.h
> >> > index a6ad528990de..65f2abd47745 100644
> >> > --- a/include/linux/fb.h
> >> > +++ b/include/linux/fb.h
> >> > @@ -470,6 +470,7 @@ struct fb_info {
> >> >  #ifdef CONFIG_FB_DEFERRED_IO
> >> >  	struct delayed_work deferred_work;
> >> >  	struct fb_deferred_io *fbdefio;
> >> > +	void *deferred_io_private;
> >> >  #endif
> >> >  
> >> >  	struct fb_ops *fbops;
> >> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
> >> >  
> >> >  /* drivers/video/fb_defio.c */
> >> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> >> > -extern void fb_deferred_io_init(struct fb_info *info);
> >> > +extern int fb_deferred_io_init(struct fb_info *info);
> >> >  extern void fb_deferred_io_open(struct fb_info *info,
> >> >  				struct inode *inode,
> >> >  				struct file *file);
> >> > -- 
> >> > 2.20.1
> >> > 
> >> > _______________________________________________
> >> > Intel-gfx mailing list
> >> > Intel-gfx@lists.freedesktop.org
> >> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
> >> 
> >> -- 
> >> Daniel Vetter
> >> Software Engineer, Intel Corporation
> >> http://blog.ffwll.ch
> 
> -- 
> Jani Nikula, Intel Open Source Graphics Center
Daniel Vetter Nov. 28, 2019, 10:08 a.m. UTC | #7
On Thu, Nov 28, 2019 at 11:05:57AM +0100, Daniel Vetter wrote:
> On Thu, Nov 28, 2019 at 11:09:46AM +0200, Jani Nikula wrote:
> > On Wed, 27 Nov 2019, Daniel Vetter <daniel@ffwll.ch> wrote:
> > > On Wed, Nov 27, 2019 at 07:17:41PM +0100, Daniel Vetter wrote:
> > >> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
> > >> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
> > >> > and then resetting it to NULL afterwards causes problems all over the
> > >> > place. First, it prevents making the fbops member of struct fb_info a
> > >> > const pointer, which means we can't make struct fb_ops const
> > >> > anywhere. Second, a few places have to go out of their way to restore
> > >> > the original fb_mmap pointer that gets reset to NULL.
> > >> > 
> > >> > Preserve the passed in fb_ops by making a copy of it and modifying that
> > >> > instead. Add a deferred_io_private member to struct fb_info to store the
> > >> > pointer to the old fb_ops, and restore that at cleanup.
> > >> > 
> > >> > Cc: Jaya Kumar <jayalk@intworks.biz>
> > >> > Cc: linux-fbdev@vger.kernel.org
> > >> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
> > >> > 
> > >> > ---
> > >> > 
> > >> > Note: If the approach is acceptable, we'll also need to handle the error
> > >> > returns on memory allocation failures at fb_deferred_io_init() call
> > >> > sites. There are 13.
> > >> 
> > >> it's fbdev defio, I think we can do worse with less effort. Just embed a
> > >> copy of fb_ops into fb_info, and use that, and tada! no memory allocation
> > >> needed :-)
> > >> 
> > >> I'd totally r-b that patch.
> > >> 
> > >> Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
> > >> the _init function and in fb_mmap call fb_deferred_io_mmap for that case
> > >> instead of the driver's fb_ops->fb_mmap. There's only one caller of that
> > >> in the entire tree, in fbmem.c. Also, we could/should nuke the
> > >> EXPORT_SYMBOL(fb_deferred_io_mmap) I think.
> > >
> > > I just realized that fb_info->fbdefio is a pointer, so this would be
> > > really simple to pull off I think.
> > 
> > Heh, having a
> > 
> > 	int (*fb_deferred_io_mmap)(struct fb_info *, struct vm_area_struct *);
> > 
> > member in struct fb_info, and using that in fbmem.c if non-NULL, was
> > actually my first idea. I didn't think it was particularly pretty, but
> > if we don't care about aesthetics...
> > 
> > Would you like that instead of the patch at hand?
> 
> 
> diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> index 82c20c6047b0..9275c6bd71da 100644
> --- a/drivers/video/fbdev/core/fb_defio.c
> +++ b/drivers/video/fbdev/core/fb_defio.c
> @@ -206,13 +206,11 @@ void fb_deferred_io_init(struct fb_info *info)
>  
>  	BUG_ON(!fbdefio);
>  	mutex_init(&fbdefio->lock);
> -	info->fbops->fb_mmap = fb_deferred_io_mmap;
>  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>  	INIT_LIST_HEAD(&fbdefio->pagelist);
>  	if (fbdefio->delay == 0) /* set a default of 1 s */
>  		fbdefio->delay = HZ;
>  }
> -EXPORT_SYMBOL_GPL(fb_deferred_io_init);
>  
>  void fb_deferred_io_open(struct fb_info *info,
>  			 struct inode *inode,
> diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
> index 86b06a599f96..6af627f281c3 100644
> --- a/drivers/video/fbdev/core/fbmem.c
> +++ b/drivers/video/fbdev/core/fbmem.c
> @@ -1341,7 +1341,16 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
>  		return -ENODEV;
>  	fb = info->fbops;
>  	mutex_lock(&info->mm_lock);
> -	if (fb->fb_mmap) {
> +	if (fb->fbdefio) {
> +		/*
> +		 * The framebuffer needs to be accessed decrypted, be sure
> +		 * SME protection is removed ahead of the call
> +		 */
> +		vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
> +		res = fb_deferred_io_mmap(info, vma);
> +		mutex_unlock(&info->mm_lock);
> +		return res;
> +	} else if (fb->fb_mmap) {
>  		int res;
>  
>  		/*
> 
> Is what I was thinking off as the pretty solution. Add an explicit
> fb_info->fbdefio_enabled boolean if you don't feel like auditing all the
> drivers for whether they really call defio_init() every time they assign
> something to that pointer. A quick scan brought some nasties to light in
> that area.

Correction, brain wasn't awake yet, I've done the audit and the above diff
should work afaict.
-Daniel

> 
> I think a function pointer here is pointless because we clearly don't need
> it, and with all the panic around function pointers a direct call feels
> much better :-)
> -Daniel
> 
> > 
> > BR,
> > Jani.
> > 
> > 
> > > -Daniel
> > >
> > >> 
> > >> That version would also get my r-b stamp. So up to you what you prefer.
> > >> -Daniel
> > >> 
> > >> > ---
> > >> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
> > >> >  include/linux/fb.h                  |  3 ++-
> > >> >  2 files changed, 24 insertions(+), 4 deletions(-)
> > >> > 
> > >> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
> > >> > index 82c20c6047b0..36697844c1e0 100644
> > >> > --- a/drivers/video/fbdev/core/fb_defio.c
> > >> > +++ b/drivers/video/fbdev/core/fb_defio.c
> > >> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
> > >> >  	mutex_unlock(&fbdefio->lock);
> > >> >  }
> > >> >  
> > >> > -void fb_deferred_io_init(struct fb_info *info)
> > >> > +int fb_deferred_io_init(struct fb_info *info)
> > >> >  {
> > >> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
> > >> > +	struct fb_ops *fbops;
> > >> >  
> > >> >  	BUG_ON(!fbdefio);
> > >> > +
> > >> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
> > >> > +	if (!fbops)
> > >> > +		return -ENOMEM;
> > >> > +
> > >> > +	fbops->fb_mmap = fb_deferred_io_mmap;
> > >> > +	info->deferred_io_private = info->fbops;
> > >> > +	info->fbops = fbops;
> > >> > +
> > >> >  	mutex_init(&fbdefio->lock);
> > >> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
> > >> > +
> > >> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
> > >> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
> > >> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
> > >> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> > >> >  	int i;
> > >> >  
> > >> >  	BUG_ON(!fbdefio);
> > >> > +
> > >> > +	/* sanity check against misuse */
> > >> > +	if (WARN_ON(!info->deferred_io_private ||
> > >> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
> > >> > +		return;
> > >> > +
> > >> >  	cancel_delayed_work_sync(&info->deferred_work);
> > >> >  
> > >> >  	/* clear out the mapping that we setup */
> > >> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
> > >> >  		page->mapping = NULL;
> > >> >  	}
> > >> >  
> > >> > -	info->fbops->fb_mmap = NULL;
> > >> > +	kfree(info->fbops);
> > >> > +	info->fbops = info->deferred_io_private;
> > >> > +	info->deferred_io_private = NULL;
> > >> > +
> > >> >  	mutex_destroy(&fbdefio->lock);
> > >> >  }
> > >> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
> > >> > diff --git a/include/linux/fb.h b/include/linux/fb.h
> > >> > index a6ad528990de..65f2abd47745 100644
> > >> > --- a/include/linux/fb.h
> > >> > +++ b/include/linux/fb.h
> > >> > @@ -470,6 +470,7 @@ struct fb_info {
> > >> >  #ifdef CONFIG_FB_DEFERRED_IO
> > >> >  	struct delayed_work deferred_work;
> > >> >  	struct fb_deferred_io *fbdefio;
> > >> > +	void *deferred_io_private;
> > >> >  #endif
> > >> >  
> > >> >  	struct fb_ops *fbops;
> > >> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
> > >> >  
> > >> >  /* drivers/video/fb_defio.c */
> > >> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
> > >> > -extern void fb_deferred_io_init(struct fb_info *info);
> > >> > +extern int fb_deferred_io_init(struct fb_info *info);
> > >> >  extern void fb_deferred_io_open(struct fb_info *info,
> > >> >  				struct inode *inode,
> > >> >  				struct file *file);
> > >> > -- 
> > >> > 2.20.1
> > >> > 
> > >> > _______________________________________________
> > >> > Intel-gfx mailing list
> > >> > Intel-gfx@lists.freedesktop.org
> > >> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
> > >> 
> > >> -- 
> > >> Daniel Vetter
> > >> Software Engineer, Intel Corporation
> > >> http://blog.ffwll.ch
> > 
> > -- 
> > Jani Nikula, Intel Open Source Graphics Center
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
Jani Nikula Nov. 28, 2019, 10:34 a.m. UTC | #8
On Thu, 28 Nov 2019, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Thu, Nov 28, 2019 at 11:05:57AM +0100, Daniel Vetter wrote:
>> On Thu, Nov 28, 2019 at 11:09:46AM +0200, Jani Nikula wrote:
>> > On Wed, 27 Nov 2019, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > > On Wed, Nov 27, 2019 at 07:17:41PM +0100, Daniel Vetter wrote:
>> > >> On Wed, Nov 27, 2019 at 06:31:57PM +0200, Jani Nikula wrote:
>> > >> > Modifying fb_ops directly to override fb_mmap with fb_deferred_io_mmap
>> > >> > and then resetting it to NULL afterwards causes problems all over the
>> > >> > place. First, it prevents making the fbops member of struct fb_info a
>> > >> > const pointer, which means we can't make struct fb_ops const
>> > >> > anywhere. Second, a few places have to go out of their way to restore
>> > >> > the original fb_mmap pointer that gets reset to NULL.
>> > >> > 
>> > >> > Preserve the passed in fb_ops by making a copy of it and modifying that
>> > >> > instead. Add a deferred_io_private member to struct fb_info to store the
>> > >> > pointer to the old fb_ops, and restore that at cleanup.
>> > >> > 
>> > >> > Cc: Jaya Kumar <jayalk@intworks.biz>
>> > >> > Cc: linux-fbdev@vger.kernel.org
>> > >> > Signed-off-by: Jani Nikula <jani.nikula@intel.com>
>> > >> > 
>> > >> > ---
>> > >> > 
>> > >> > Note: If the approach is acceptable, we'll also need to handle the error
>> > >> > returns on memory allocation failures at fb_deferred_io_init() call
>> > >> > sites. There are 13.
>> > >> 
>> > >> it's fbdev defio, I think we can do worse with less effort. Just embed a
>> > >> copy of fb_ops into fb_info, and use that, and tada! no memory allocation
>> > >> needed :-)
>> > >> 
>> > >> I'd totally r-b that patch.
>> > >> 
>> > >> Or do what Ville suggested, add an fb_info->fbdefio.enabled, set that in
>> > >> the _init function and in fb_mmap call fb_deferred_io_mmap for that case
>> > >> instead of the driver's fb_ops->fb_mmap. There's only one caller of that
>> > >> in the entire tree, in fbmem.c. Also, we could/should nuke the
>> > >> EXPORT_SYMBOL(fb_deferred_io_mmap) I think.
>> > >
>> > > I just realized that fb_info->fbdefio is a pointer, so this would be
>> > > really simple to pull off I think.
>> > 
>> > Heh, having a
>> > 
>> > 	int (*fb_deferred_io_mmap)(struct fb_info *, struct vm_area_struct *);
>> > 
>> > member in struct fb_info, and using that in fbmem.c if non-NULL, was
>> > actually my first idea. I didn't think it was particularly pretty, but
>> > if we don't care about aesthetics...
>> > 
>> > Would you like that instead of the patch at hand?
>> 
>> 
>> diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
>> index 82c20c6047b0..9275c6bd71da 100644
>> --- a/drivers/video/fbdev/core/fb_defio.c
>> +++ b/drivers/video/fbdev/core/fb_defio.c
>> @@ -206,13 +206,11 @@ void fb_deferred_io_init(struct fb_info *info)
>>  
>>  	BUG_ON(!fbdefio);
>>  	mutex_init(&fbdefio->lock);
>> -	info->fbops->fb_mmap = fb_deferred_io_mmap;
>>  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>>  	INIT_LIST_HEAD(&fbdefio->pagelist);
>>  	if (fbdefio->delay == 0) /* set a default of 1 s */
>>  		fbdefio->delay = HZ;
>>  }
>> -EXPORT_SYMBOL_GPL(fb_deferred_io_init);
>>  
>>  void fb_deferred_io_open(struct fb_info *info,
>>  			 struct inode *inode,
>> diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
>> index 86b06a599f96..6af627f281c3 100644
>> --- a/drivers/video/fbdev/core/fbmem.c
>> +++ b/drivers/video/fbdev/core/fbmem.c
>> @@ -1341,7 +1341,16 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
>>  		return -ENODEV;
>>  	fb = info->fbops;
>>  	mutex_lock(&info->mm_lock);
>> -	if (fb->fb_mmap) {
>> +	if (fb->fbdefio) {
>> +		/*
>> +		 * The framebuffer needs to be accessed decrypted, be sure
>> +		 * SME protection is removed ahead of the call
>> +		 */
>> +		vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
>> +		res = fb_deferred_io_mmap(info, vma);
>> +		mutex_unlock(&info->mm_lock);
>> +		return res;
>> +	} else if (fb->fb_mmap) {
>>  		int res;
>>  
>>  		/*
>> 
>> Is what I was thinking off as the pretty solution. Add an explicit
>> fb_info->fbdefio_enabled boolean if you don't feel like auditing all the
>> drivers for whether they really call defio_init() every time they assign
>> something to that pointer. A quick scan brought some nasties to light in
>> that area.
>
> Correction, brain wasn't awake yet, I've done the audit and the above diff
> should work afaict.

It just felt sketchy to me to depend on that pointer being set as the
decider, independent of defio init. But if you think that's the way to
go, I'm not going to argue. I'll bet it won't go unnoticed if
fb_deferred_io_mmap() gets called without the init. ;)

BR,
Jani.



> -Daniel
>
>> 
>> I think a function pointer here is pointless because we clearly don't need
>> it, and with all the panic around function pointers a direct call feels
>> much better :-)
>> -Daniel
>> 
>> > 
>> > BR,
>> > Jani.
>> > 
>> > 
>> > > -Daniel
>> > >
>> > >> 
>> > >> That version would also get my r-b stamp. So up to you what you prefer.
>> > >> -Daniel
>> > >> 
>> > >> > ---
>> > >> >  drivers/video/fbdev/core/fb_defio.c | 25 ++++++++++++++++++++++---
>> > >> >  include/linux/fb.h                  |  3 ++-
>> > >> >  2 files changed, 24 insertions(+), 4 deletions(-)
>> > >> > 
>> > >> > diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
>> > >> > index 82c20c6047b0..36697844c1e0 100644
>> > >> > --- a/drivers/video/fbdev/core/fb_defio.c
>> > >> > +++ b/drivers/video/fbdev/core/fb_defio.c
>> > >> > @@ -200,13 +200,23 @@ static void fb_deferred_io_work(struct work_struct *work)
>> > >> >  	mutex_unlock(&fbdefio->lock);
>> > >> >  }
>> > >> >  
>> > >> > -void fb_deferred_io_init(struct fb_info *info)
>> > >> > +int fb_deferred_io_init(struct fb_info *info)
>> > >> >  {
>> > >> >  	struct fb_deferred_io *fbdefio = info->fbdefio;
>> > >> > +	struct fb_ops *fbops;
>> > >> >  
>> > >> >  	BUG_ON(!fbdefio);
>> > >> > +
>> > >> > +	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
>> > >> > +	if (!fbops)
>> > >> > +		return -ENOMEM;
>> > >> > +
>> > >> > +	fbops->fb_mmap = fb_deferred_io_mmap;
>> > >> > +	info->deferred_io_private = info->fbops;
>> > >> > +	info->fbops = fbops;
>> > >> > +
>> > >> >  	mutex_init(&fbdefio->lock);
>> > >> > -	info->fbops->fb_mmap = fb_deferred_io_mmap;
>> > >> > +
>> > >> >  	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
>> > >> >  	INIT_LIST_HEAD(&fbdefio->pagelist);
>> > >> >  	if (fbdefio->delay == 0) /* set a default of 1 s */
>> > >> > @@ -229,6 +239,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>> > >> >  	int i;
>> > >> >  
>> > >> >  	BUG_ON(!fbdefio);
>> > >> > +
>> > >> > +	/* sanity check against misuse */
>> > >> > +	if (WARN_ON(!info->deferred_io_private ||
>> > >> > +		    info->fbops->fb_mmap != fb_deferred_io_mmap))
>> > >> > +		return;
>> > >> > +
>> > >> >  	cancel_delayed_work_sync(&info->deferred_work);
>> > >> >  
>> > >> >  	/* clear out the mapping that we setup */
>> > >> > @@ -237,7 +253,10 @@ void fb_deferred_io_cleanup(struct fb_info *info)
>> > >> >  		page->mapping = NULL;
>> > >> >  	}
>> > >> >  
>> > >> > -	info->fbops->fb_mmap = NULL;
>> > >> > +	kfree(info->fbops);
>> > >> > +	info->fbops = info->deferred_io_private;
>> > >> > +	info->deferred_io_private = NULL;
>> > >> > +
>> > >> >  	mutex_destroy(&fbdefio->lock);
>> > >> >  }
>> > >> >  EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
>> > >> > diff --git a/include/linux/fb.h b/include/linux/fb.h
>> > >> > index a6ad528990de..65f2abd47745 100644
>> > >> > --- a/include/linux/fb.h
>> > >> > +++ b/include/linux/fb.h
>> > >> > @@ -470,6 +470,7 @@ struct fb_info {
>> > >> >  #ifdef CONFIG_FB_DEFERRED_IO
>> > >> >  	struct delayed_work deferred_work;
>> > >> >  	struct fb_deferred_io *fbdefio;
>> > >> > +	void *deferred_io_private;
>> > >> >  #endif
>> > >> >  
>> > >> >  	struct fb_ops *fbops;
>> > >> > @@ -658,7 +659,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
>> > >> >  
>> > >> >  /* drivers/video/fb_defio.c */
>> > >> >  int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
>> > >> > -extern void fb_deferred_io_init(struct fb_info *info);
>> > >> > +extern int fb_deferred_io_init(struct fb_info *info);
>> > >> >  extern void fb_deferred_io_open(struct fb_info *info,
>> > >> >  				struct inode *inode,
>> > >> >  				struct file *file);
>> > >> > -- 
>> > >> > 2.20.1
>> > >> > 
>> > >> > _______________________________________________
>> > >> > Intel-gfx mailing list
>> > >> > Intel-gfx@lists.freedesktop.org
>> > >> > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
>> > >> 
>> > >> -- 
>> > >> Daniel Vetter
>> > >> Software Engineer, Intel Corporation
>> > >> http://blog.ffwll.ch
>> > 
>> > -- 
>> > Jani Nikula, Intel Open Source Graphics Center
>> 
>> -- 
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
diff mbox series

Patch

diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 82c20c6047b0..36697844c1e0 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -200,13 +200,23 @@  static void fb_deferred_io_work(struct work_struct *work)
 	mutex_unlock(&fbdefio->lock);
 }
 
-void fb_deferred_io_init(struct fb_info *info)
+int fb_deferred_io_init(struct fb_info *info)
 {
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct fb_ops *fbops;
 
 	BUG_ON(!fbdefio);
+
+	fbops = kmemdup(info->fbops, sizeof(*fbops), GFP_KERNEL);
+	if (!fbops)
+		return -ENOMEM;
+
+	fbops->fb_mmap = fb_deferred_io_mmap;
+	info->deferred_io_private = info->fbops;
+	info->fbops = fbops;
+
 	mutex_init(&fbdefio->lock);
-	info->fbops->fb_mmap = fb_deferred_io_mmap;
+
 	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
 	INIT_LIST_HEAD(&fbdefio->pagelist);
 	if (fbdefio->delay == 0) /* set a default of 1 s */
@@ -229,6 +239,12 @@  void fb_deferred_io_cleanup(struct fb_info *info)
 	int i;
 
 	BUG_ON(!fbdefio);
+
+	/* sanity check against misuse */
+	if (WARN_ON(!info->deferred_io_private ||
+		    info->fbops->fb_mmap != fb_deferred_io_mmap))
+		return;
+
 	cancel_delayed_work_sync(&info->deferred_work);
 
 	/* clear out the mapping that we setup */
@@ -237,7 +253,10 @@  void fb_deferred_io_cleanup(struct fb_info *info)
 		page->mapping = NULL;
 	}
 
-	info->fbops->fb_mmap = NULL;
+	kfree(info->fbops);
+	info->fbops = info->deferred_io_private;
+	info->deferred_io_private = NULL;
+
 	mutex_destroy(&fbdefio->lock);
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index a6ad528990de..65f2abd47745 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -470,6 +470,7 @@  struct fb_info {
 #ifdef CONFIG_FB_DEFERRED_IO
 	struct delayed_work deferred_work;
 	struct fb_deferred_io *fbdefio;
+	void *deferred_io_private;
 #endif
 
 	struct fb_ops *fbops;
@@ -658,7 +659,7 @@  static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 
 /* drivers/video/fb_defio.c */
 int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
-extern void fb_deferred_io_init(struct fb_info *info);
+extern int fb_deferred_io_init(struct fb_info *info);
 extern void fb_deferred_io_open(struct fb_info *info,
 				struct inode *inode,
 				struct file *file);