On Thu, Apr 15, 2021 at 09:12:14PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 15.04.21 um 14:57 schrieb Daniel Vetter:
> > On Thu, Apr 15, 2021 at 08:56:20AM +0200, Thomas Zimmermann wrote:
> > > Hi
> > > 
> > > Am 09.04.21 um 11:22 schrieb Daniel Vetter:
> > > > > Is it that easy? simepldrm's detach function has code to
> > > > > synchronize
> with
> > > > > concurrent hotplug removals. If we can use drm_dev_unplug() for 
> > > > > everything,
> > > > > I'm all for it.
> > > > 
> > > > Uh, I should have looked at the code instead of just asking silly
> > > > questions :-)
> > > > 
> > > > Now I'm even more scared, and also more convinced that we're recreating
> > > a
> > > > bad version of some of the core driver model concepts.
> > > > 
> > > > I think the ideal option here would be if drm_aperture could unload
> > > > (unbind really) the platform driver for us, through the driver
> > > > model.
> Then
> > > > there's only one place that keeps track whether the driver is
> > > > unbound
> or
> > > > not. I'm not sure whether this can be done fully generic on a struct
> > > > device, or whether we need special code for each type. Since atm we only
> > > > have simpledrm we can just specialize on platform_device and it's good
> > > > enough.
> > > 
> > > I meanwhile found that calling platform_device_unregister() is the right
> > > thing to do. It is like a hot-unplug event. It's simple to implement and
> > > removes the generic device as well. Any memory ranges for the generic 
> > > device
> > > are gone as well. Only the native driver's native device will remain. 
> > > That's
> > > better than the existing simplefb driver.
> > 
> > That sounds great.
> > 
> > > Which unregister function to call still driver-specific, so I kept the
> > > callback.
> > 
> > Could we have the callback in core code, and you do something like
> > drm_aperture_acquire_platform (and later on drm_aperture_acquire_pci or
> > whatever, although tbh I'm not sure we ever get anything else than
> > platform). That function can do a runtime check that drm_device->dev is
> > actually a platform dev.
> 
> Somehow I knew you wouldn't like the current abstraction. :)
> 
> > 
> > Another idea: Do the runtime casting in the core without anything? Atm we
> > have platform that needs support, maybe pci device, so we could easily
> > extend this and just let it do the right thing. Then no callback is
> > needed. I.e.
> > 
> >     if (is_platform_dev(drm_device->dev))
> >             platform_device_unregister(drm_device->dev);
> >     else
> >             WARN(1, "not yet implemented\n");
> > 
> > or something like that.
> 
> I don't like that. I spend time to remove the usb and pci device pointers
> from code and structs. I don't want to introduce a new hard-coded special
> case here.
> 
> > 
> > I just find the callback to essentially unregister a device a bit
> > redundant.
> 
> I'd like to go with your first idea. The callback would be internal and the
> public acquire function is specifically for firmware-based platform devices.
> That covers simple-framebuffer, VESA, EFI, and probably any other generic
> interface that fbdev supported in the last 20+ yrs. I don't think we'll ever
> need anything else.
> 
> Still, I'd like to have some abstraction between the internals of the
> aperture helpers and our actual use case. I'll update the patchset
> accordingly.

Makes sense and I'm happy with that pick of color choice. That keeps the
noise out of drivers, and also keeps the concepts clean internally.
-Daniel


> 
> Best regards
> Thomas
> 
> > -Daniel
> > 
> > > 
> > > Best regards
> > > Thomas
> > > 
> > > > 
> > > > I think best here would be to Cc: gregkh on this patch and the simpledrm
> > > > ->detach implementatation, and ask for his feedback as driver model
> > > > maintainer. Maybe if you could hack together the platform_device unbind
> > > > path as proof of concept would be even better.
> > > > 
> > > > Either way, this is really tricky.
> > > > -Daniel
> > > > 
> > > > > 
> > > > > Best regards
> > > > > Thomas
> > > > > 
> > > > > > 
> > > > > > Or maybe we should tie this more into the struct device mode and 
> > > > > > force an
> > > > > > unload that way? That way devm cleanup would work as one expects, 
> > > > > > and
> > > > > > avoid the need for anything specific (hopefully) in this detach 
> > > > > > callback.
> > > > > > 
> > > > > > Just feels a bit like we're reinventing half of the driver model 
> > > > > > here,
> > > > > > badly.
> > > > > > 
> > > > > > > + *       };
> > > > > > > + *
> > > > > > > + *       static int acquire_framebuffers(struct
> > > > > > > drm_device *dev, struct
> pci_dev *pdev)
> > > > > > > + *       {
> > > > > > > + *               resource_size_t start, len;
> > > > > > > + *               struct drm_aperture *ap;
> > > > > > > + *
> > > > > > > + *               base = pci_resource_start(pdev, 0);
> > > > > > > + *               size = pci_resource_len(pdev, 0);
> > > > > > > + *
> > > > > > > + *               ap = devm_acquire_aperture(dev, base, size, 
> > > > > > > &ap_funcs);
> > > > > > > + *               if (IS_ERR(ap))
> > > > > > > + *                       return PTR_ERR(ap);
> > > > > > > + *
> > > > > > > + *               return 0;
> > > > > > > + *       }
> > > > > > > + *
> > > > > > > + *       static int probe(struct pci_dev *pdev)
> > > > > > > + *       {
> > > > > > > + *               struct drm_device *dev;
> > > > > > > + *               int ret;
> > > > > > > + *
> > > > > > > + *               // ... Initialize the device...
> > > > > > > + *               dev = devm_drm_dev_alloc();
> > > > > > > + *               ...
> > > > > > > + *
> > > > > > > + *               // ... and acquire ownership of the framebuffer.
> > > > > > > + *               ret = acquire_framebuffers(dev, pdev);
> > > > > > > + *               if (ret)
> > > > > > > + *                       return ret;
> > > > > > > + *
> > > > > > > + *               drm_dev_register();
> > > > > > > + *
> > > > > > > + *               return 0;
> > > > > > > + *       }
> > > > > > > + *
> > > > > > > + * The generic driver is now subject to forced removal by other 
> > > > > > > drivers. This
> > > > > > > + * is when the detach function in struct &drm_aperture_funcs 
> > > > > > > comes into play.
> > > > > > > + * When a driver calls 
> > > > > > > drm_fb_helper_remove_conflicting_framebuffers() et al
> > > > > > > + * for the registered framebuffer range, the DRM core calls 
> > > > > > > struct
> > > > > > > + * &drm_aperture_funcs.detach and the generic driver has to 
> > > > > > > onload itself. It
> > > > > > > + * may not access the device's registers, framebuffer memory, 
> > > > > > > ROM, etc after
> > > > > > > + * detach returned. If the driver supports hotplugging, detach 
> > > > > > > can be treated
> > > > > > > + * like an unplug event.
> > > > > > > + *
> > > > > > > + * .. code-block:: c
> > > > > > > + *
> > > > > > > + *       static void detach_from_device(struct drm_device *dev,
> > > > > > > + *                                      resource_size_t base,
> > > > > > > + *                                      resource_size_t size)
> > > > > > > + *       {
> > > > > > > + *               // Signal unplug
> > > > > > > + *               drm_dev_unplug(dev);
> > > > > > > + *
> > > > > > > + *               // Maybe do other clean-up operations
> > > > > > > + *               ...
> > > > > > > + *       }
> > > > > > > + *
> > > > > > > + *       static struct drm_aperture_funcs ap_funcs = {
> > > > > > > + *               .detach = detach_from_device,
> > > > > > > + *       };
> > > > > > > + */
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * struct drm_aperture - Represents a DRM framebuffer aperture
> > > > > > > + *
> > > > > > > + * This structure has no public fields.
> > > > > > > + */
> > > > > > > +struct drm_aperture {
> > > > > > > + struct drm_device *dev;
> > > > > > > + resource_size_t base;
> > > > > > > + resource_size_t size;
> > > > > > > +
> > > > > > > + const struct drm_aperture_funcs *funcs;
> > > > > > > +
> > > > > > > + struct list_head lh;
> > > > > > > +};
> > > > > > > +
> > > > > > > +static LIST_HEAD(drm_apertures);
> > > > > > > +
> > > > > > > +static DEFINE_MUTEX(drm_apertures_lock);
> > > > > > > +
> > > > > > > +static bool overlap(resource_size_t base1, resource_size_t end1,
> > > > > > > +             resource_size_t base2, resource_size_t end2)
> > > > > > > +{
> > > > > > > + return (base1 < end2) && (end1 > base2);
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void devm_aperture_acquire_release(void *data)
> > > > > > > +{
> > > > > > > + struct drm_aperture *ap = data;
> > > > > > > + bool detached = !ap->dev;
> > > > > > > +
> > > > > > > + if (!detached)
> > > > > > 
> > > > > > Uh this needs a comment that if ap->dev is NULL then we're called 
> > > > > > from
> > > > > > drm_aperture_detach_drivers() and hence the lock is already held.
> > > > > > 
> > > > > > > +         mutex_lock(&drm_apertures_lock);
> > > > > > 
> > > > > > and an
> > > > > > 
> > > > > >     else
> > > > > >             locdep_assert_held(&drm_apertures_lock);
> > > > > > 
> > > > > > here to check that. I was scratching my head first quite a bit how 
> > > > > > you'd
> > > > > > solve the deadlock, this is a neat solution (much simpler than 
> > > > > > anything I
> > > > > > came up with in my head). But needs comments.
> > > > > > 
> > > > > > > +
> > > > > > > + list_del(&ap->lh);
> > > > > > > +
> > > > > > > + if (!detached)
> > > > > > > +         mutex_unlock(&drm_apertures_lock);
> > > > > > > +}
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * devm_aperture_acquire - Acquires ownership of a
> > > > > > > framebuffer on
> behalf of a DRM driver.
> > > > > > > + * @dev: the DRM device to own the framebuffer memory
> > > > > > > + * @base:        the framebuffer's byte offset in physical memory
> > > > > > > + * @size:        the framebuffer size in bytes
> > > > > > > + * @funcs:       callback functions
> > > > > > > + *
> > > > > > > + * Installs the given device as the new owner. The
> > > > > > > function fails
> if the
> > > > > > > + * framebuffer range, or parts of it, is currently owned by
> > > > > > > another
> > > driver.
> > > > > > > + * To evict current owners, callers should use
> > > > > > > + * drm_fb_helper_remove_conflicting_framebuffers() et al. before 
> > > > > > > calling this
> > > > > > > + * function. Acquired apertures are released
> > > > > > > automatically if the
> underlying
> > > > > > > + * device goes away.
> > > > > > > + *
> > > > > > > + * Returns:
> > > > > > > + * An instance of struct &drm_aperture on success, or a 
> > > > > > > pointer-encoded
> > > > > > > + * errno value otherwise.
> > > > > > > + */
> > > > > > > +struct drm_aperture *
> > > > > > > +devm_aperture_acquire(struct drm_device *dev,
> > > > > > > +               resource_size_t base, resource_size_t size,
> > > > > > > +               const struct drm_aperture_funcs *funcs)
> > > > > > > +{
> > > > > > > + size_t end = base + size;
> > > > > > > + struct list_head *pos;
> > > > > > > + struct drm_aperture *ap;
> > > > > > > + int ret;
> > > > > > > +
> > > > > > > + mutex_lock(&drm_apertures_lock);
> > > > > > > +
> > > > > > > + list_for_each(pos, &drm_apertures) {
> > > > > > > +         ap = container_of(pos, struct drm_aperture, lh);
> > > > > > > +         if (overlap(base, end, ap->base, ap->base + ap->size))
> > > > > > > +                 return ERR_PTR(-EBUSY);
> > > > > > > + }
> > > > > > > +
> > > > > > > + ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL);
> > > > > > > + if (!ap)
> > > > > > > +         return ERR_PTR(-ENOMEM);
> > > > > > > +
> > > > > > > + ap->dev = dev;
> > > > > > > + ap->base = base;
> > > > > > > + ap->size = size;
> > > > > > > + ap->funcs = funcs;
> > > > > > > + INIT_LIST_HEAD(&ap->lh);
> > > > > > > +
> > > > > > > + list_add(&ap->lh, &drm_apertures);
> > > > > > > +
> > > > > > > + mutex_unlock(&drm_apertures_lock);
> > > > > > > +
> > > > > > > + ret = devm_add_action_or_reset(dev->dev, 
> > > > > > > devm_aperture_acquire_release, ap);
> > > > > > > + if (ret)
> > > > > > > +         return ERR_PTR(ret);
> > > > > > > +
> > > > > > > + return ap;
> > > > > > > +}
> > > > > > > +EXPORT_SYMBOL(devm_aperture_acquire);
> > > > > > > +
> > > > > > > +void drm_aperture_detach_drivers(resource_size_t base, 
> > > > > > > resource_size_t size)
> > > > > > > +{
> > > > > > > + resource_size_t end = base + size;
> > > > > > > + struct list_head *pos, *n;
> > > > > > > +
> > > > > > > + mutex_lock(&drm_apertures_lock);
> > > > > > > +
> > > > > > > + list_for_each_safe(pos, n, &drm_apertures) {
> > > > > > > +         struct drm_aperture *ap =
> > > > > > > +                 container_of(pos, struct drm_aperture, lh);
> > > > > > > +         struct drm_device *dev = ap->dev;
> > > > > > > +
> > > > > > > +         if (!overlap(base, end, ap->base, ap->base + ap->size))
> > > > > > > +                 continue;
> > > > > > > +
> > > > > > > +         ap->dev = NULL; /* detach from device */
> > > > > > > +         if (drm_WARN_ON(dev, !ap->funcs->detach))
> > > > > > > +                 continue;
> > > > > > > +         ap->funcs->detach(dev, ap->base, ap->size);
> > > > > > > + }
> > > > > > > +
> > > > > > > + mutex_unlock(&drm_apertures_lock);
> > > > > > > +}
> > > > > > > +EXPORT_SYMBOL(drm_aperture_detach_drivers);
> > > > > > 
> > > > > > Is this just exported because of the inline functions in the
> > > > > > headers?
> > > Imo
> > > > > > better to make them proper functions (they're big after your 
> > > > > > patch&not
> > > > > > perf critical, so not good candidates for inlining anyway).
> > > > > > 
> > > > > > > diff --git a/include/drm/drm_aperture.h 
> > > > > > > b/include/drm/drm_aperture.h
> > > > > > > index 13766efe9517..696cec75ef78 100644
> > > > > > > --- a/include/drm/drm_aperture.h
> > > > > > > +++ b/include/drm/drm_aperture.h
> > > > > > > @@ -4,8 +4,30 @@
> > > > > > >     #define _DRM_APERTURE_H_
> > > > > > >     #include <linux/fb.h>
> > > > > > > +#include <linux/pci.h>
> > > > > > >     #include <linux/vgaarb.h>
> > > > > > > +struct drm_aperture;
> > > > > > > +struct drm_device;
> > > > > > > +
> > > > > > > +struct drm_aperture_funcs {
> > > > > > > + void (*detach)(struct drm_device *dev, resource_size_t base, 
> > > > > > > resource_size_t size);
> > > > > > > +};
> > > > > > > +
> > > > > > > +struct drm_aperture *
> > > > > > > +devm_aperture_acquire(struct drm_device *dev,
> > > > > > > +               resource_size_t base, resource_size_t size,
> > > > > > > +               const struct drm_aperture_funcs *funcs);
> > > > > > > +
> > > > > > > +#if defined(CONFIG_DRM_APERTURE)
> > > > > > > +void drm_aperture_detach_drivers(resource_size_t base, 
> > > > > > > resource_size_t size);
> > > > > > > +#else
> > > > > > > +static inline void
> > > > > > > +drm_aperture_detach_drivers(resource_size_t base,
> > > > > > > resource_size_t
> size)
> > > > > > > +{
> > > > > > > +}
> > > > > > > +#endif
> > > > > > > +
> > > > > > >     /**
> > > > > > >      * drm_fb_helper_remove_conflicting_framebuffers - remove 
> > > > > > > firmware-configured framebuffers
> > > > > > >      * @a: memory range, users of which are to be removed
> > > > > > > @@ -20,6 +42,11 @@ static inline int
> > > > > > >     drm_fb_helper_remove_conflicting_framebuffers(struct 
> > > > > > > apertures_struct *a,
> > > > > > >                                                 const char *name, 
> > > > > > > bool primary)
> > > > > > >     {
> > > > > > > + int i;
> > > > > > > +
> > > > > > > + for (i = 0; i < a->count; ++i)
> > > > > > > +         drm_aperture_detach_drivers(a->ranges[i].base, 
> > > > > > > a->ranges[i].size);
> > > > > > > +
> > > > > > >     #if IS_REACHABLE(CONFIG_FB)
> > > > > > >           return remove_conflicting_framebuffers(a, name, 
> > > > > > > primary);
> > > > > > >     #else
> > > > > > > @@ -43,7 +70,16 @@ static inline int
> > > > > > >     drm_fb_helper_remove_conflicting_pci_framebuffers(struct 
> > > > > > > pci_dev *pdev,
> > > > > > >                                                     const char 
> > > > > > > *name)
> > > > > > >     {
> > > > > > > - int ret = 0;
> > > > > > > + resource_size_t base, size;
> > > > > > > + int bar, ret = 0;
> > > > > > > +
> > > > > > > + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
> > > > > > > +         if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
> > > > > > > +                 continue;
> > > > > > > +         base = pci_resource_start(pdev, bar);
> > > > > > > +         size = pci_resource_len(pdev, bar);
> > > > > > > +         drm_aperture_detach_drivers(base, size);
> > > > > > > + }
> > > > > > >           /*
> > > > > > >            * WARNING: Apparently we must kick fbdev drivers 
> > > > > > > before vgacon,
> > > > > > > -- 
> > > > > > > 2.30.1
> > > > > > > 
> > > > > > 
> > > > > 
> > > > > -- 
> > > > > Thomas Zimmermann
> > > > > Graphics Driver Developer
> > > > > SUSE Software Solutions Germany GmbH
> > > > > Maxfeldstr. 5, 90409 Nürnberg, Germany
> > > > > (HRB 36809, AG Nürnberg)
> > > > > Geschäftsführer: Felix Imendörffer
> > > > > 
> > > > 
> > > > 
> > > > 
> > > > 
> > > 
> > > -- 
> > > Thomas Zimmermann
> > > Graphics Driver Developer
> > > SUSE Software Solutions Germany GmbH
> > > Maxfeldstr. 5, 90409 Nürnberg, Germany
> > > (HRB 36809, AG Nürnberg)
> > > Geschäftsführer: Felix Imendörffer
> > > 
> > 
> > 
> > 
> > 
> 
> -- 
> Thomas Zimmermann
> Graphics Driver Developer
> SUSE Software Solutions Germany GmbH
> Maxfeldstr. 5, 90409 Nürnberg, Germany
> (HRB 36809, AG Nürnberg)
> Geschäftsführer: Felix Imendörffer
> 




-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to