On 05/05/2015 11:42 AM, Daniel Vetter wrote: > On Tue, May 05, 2015 at 10:36:24AM -0400, Peter Hurley wrote: >> On 05/04/2015 12:52 AM, Mario Kleiner wrote: >>> On 04/16/2015 03:03 PM, Daniel Vetter wrote: >>>> On Thu, Apr 16, 2015 at 08:30:55AM -0400, Peter Hurley wrote: >>>>> On 04/15/2015 01:31 PM, Daniel Vetter wrote: >>>>>> On Wed, Apr 15, 2015 at 09:00:04AM -0400, Peter Hurley wrote: >>>>>>> Hi Daniel, >>>>>>> >>>>>>> On 04/15/2015 03:17 AM, Daniel Vetter wrote: >>>>>>>> This was a bit too much cargo-culted, so lets make it solid: >>>>>>>> - vblank->count doesn't need to be an atomic, writes are always done >>>>>>>> under the protection of dev->vblank_time_lock. Switch to an unsigned >>>>>>>> long instead and update comments. Note that atomic_read is just a >>>>>>>> normal read of a volatile variable, so no need to audit all the >>>>>>>> read-side access specifically. >>>>>>>> >>>>>>>> - The barriers for the vblank counter seqlock weren't complete: The >>>>>>>> read-side was missing the first barrier between the counter read and >>>>>>>> the timestamp read, it only had a barrier between the ts and the >>>>>>>> counter read. We need both. >>>>>>>> >>>>>>>> - Barriers weren't properly documented. Since barriers only work if >>>>>>>> you have them on boths sides of the transaction it's prudent to >>>>>>>> reference where the other side is. To avoid duplicating the >>>>>>>> write-side comment 3 times extract a little store_vblank() helper. >>>>>>>> In that helper also assert that we do indeed hold >>>>>>>> dev->vblank_time_lock, since in some cases the lock is acquired a >>>>>>>> few functions up in the callchain. >>>>>>>> >>>>>>>> Spotted while reviewing a patch from Chris Wilson to add a fastpath to >>>>>>>> the vblank_wait ioctl. >>>>>>>> >>>>>>>> Cc: Chris Wilson <ch...@chris-wilson.co.uk> >>>>>>>> Cc: Mario Kleiner <mario.kleiner...@gmail.com> >>>>>>>> Cc: Ville Syrjälä <ville.syrj...@linux.intel.com> >>>>>>>> Cc: Michel Dänzer <mic...@daenzer.net> >>>>>>>> Signed-off-by: Daniel Vetter <daniel.vet...@intel.com> >>>>>>>> --- >>>>>>>> drivers/gpu/drm/drm_irq.c | 92 >>>>>>>> ++++++++++++++++++++++++----------------------- >>>>>>>> include/drm/drmP.h | 8 +++-- >>>>>>>> 2 files changed, 54 insertions(+), 46 deletions(-) >>>>>>>> >>>>>>>> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c >>>>>>>> index c8a34476570a..23bfbc61a494 100644 >>>>>>>> --- a/drivers/gpu/drm/drm_irq.c >>>>>>>> +++ b/drivers/gpu/drm/drm_irq.c >>>>>>>> @@ -74,6 +74,33 @@ module_param_named(vblankoffdelay, >>>>>>>> drm_vblank_offdelay, int, 0600); >>>>>>>> module_param_named(timestamp_precision_usec, >>>>>>>> drm_timestamp_precision, int, 0600); >>>>>>>> module_param_named(timestamp_monotonic, drm_timestamp_monotonic, >>>>>>>> int, 0600); >>>>>>>> >>>>>>>> +static void store_vblank(struct drm_device *dev, int crtc, >>>>>>>> + unsigned vblank_count_inc, >>>>>>>> + struct timeval *t_vblank) >>>>>>>> +{ >>>>>>>> + struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; >>>>>>>> + u32 tslot; >>>>>>>> + >>>>>>>> + assert_spin_locked(&dev->vblank_time_lock); >>>>>>>> + >>>>>>>> + if (t_vblank) { >>>>>>>> + tslot = vblank->count + vblank_count_inc; >>>>>>>> + vblanktimestamp(dev, crtc, tslot) = *t_vblank; >>>>>>>> + } >>>>>>>> + >>>>>>>> + /* >>>>>>>> + * vblank timestamp updates are protected on the write side with >>>>>>>> + * vblank_time_lock, but on the read side done locklessly using a >>>>>>>> + * sequence-lock on the vblank counter. Ensure correct ordering >>>>>>>> using >>>>>>>> + * memory barrriers. We need the barrier both before and also >>>>>>>> after the >>>>>>>> + * counter update to synchronize with the next timestamp write. >>>>>>>> + * The read-side barriers for this are in >>>>>>>> drm_vblank_count_and_time. >>>>>>>> + */ >>>>>>>> + smp_wmb(); >>>>>>>> + vblank->count += vblank_count_inc; >>>>>>>> + smp_wmb(); >>>>>>> >>>>>>> The comment and the code are each self-contradictory. >>>>>>> >>>>>>> If vblank->count writes are always protected by vblank_time_lock >>>>>>> (something I >>>>>>> did not verify but that the comment above asserts), then the trailing >>>>>>> write >>>>>>> barrier is not required (and the assertion that it is in the comment is >>>>>>> incorrect). >>>>>>> >>>>>>> A spin unlock operation is always a write barrier. >>>>>> >>>>>> Hm yeah. Otoh to me that's bordering on "code too clever for my own >>>>>> good". >>>>>> That the spinlock is held I can assure. That no one goes around and does >>>>>> multiple vblank updates (because somehow that code raced with the hw >>>>>> itself) I can't easily assure with a simple assert or something similar. >>>>>> It's not the case right now, but that can changes. >>>>> >>>>> The algorithm would be broken if multiple updates for the same vblank >>>>> count were allowed; that's why it checks to see if the vblank count has >>>>> not advanced before storing a new timestamp. >>>>> >>>>> Otherwise, the read side would not be able to determine that the >>>>> timestamp is valid by double-checking that the vblank count has not >>>>> changed. >>>>> >>>>> And besides, even if the code looped without dropping the spinlock, >>>>> the correct write order would still be observed because it would still >>>>> be executing on the same cpu. >>>>> >>>>> My objection to the write memory barrier is not about optimization; >>>>> it's about correct code. >>>> >>>> Well diff=0 is not allowed, I guess I could enforce this with some >>>> WARN_ON. And I still think my point of non-local correctness is solid. >>>> With the smp_wmb() removed the following still works correctly: >>>> >>>> spin_lock(vblank_time_lock); >>>> store_vblank(dev, crtc, 1, ts1); >>>> spin_unlock(vblank_time_lock); >>>> >>>> spin_lock(vblank_time_lock); >>>> store_vblank(dev, crtc, 1, ts2); >>>> spin_unlock(vblank_time_lock); >>>> >>>> But with the smp_wmb(); removed the following would be broken: >>>> >>>> spin_lock(vblank_time_lock); >>>> store_vblank(dev, crtc, 1, ts1); >>>> store_vblank(dev, crtc, 1, ts2); >>>> spin_unlock(vblank_time_lock); >>>> >>>> because the compiler/cpu is free to reorder the store for vblank->count >>>> _ahead_ of the store for the timestamp. And that would trick readers into >>>> believing that they have a valid timestamp when they potentially raced. >>>> >>>> Now you're correct that right now there's no such thing going on, and it's >>>> unlikely to happen (given the nature of vblank updates). But my point is >>>> that if we optimize this then the correctness can't be proven locally >>>> anymore by just looking at store_vblank, but instead you must audit all >>>> the callers. And leaking locking/barriers like that is too fragile design >>>> for my taste. >>>> >>>> But you insist that my approach is broken somehow and dropping the smp_wmb >>>> is needed for correctness. I don't see how that's the case at all. >> >> Daniel, >> >> I've been really busy this last week; my apologies for not replying promptly. >> >>> Fwiw, i spent some time reeducating myself about memory barriers (thanks >>> for your explanations) and thinking about this, and the last version of >>> your patch looks good to me. It also makes sense to me to leave that last >>> smb_wmb() in place to make future use of the helper robust - for non-local >>> correctness, to avoid having to audit all future callers of that helper. >> >> My concern wrt to unnecessary barriers in this algorithm is that the trailing >> barrier now appears mandatory, when in fact it is not. >> >> Moreover, this algorithm is, in general, fragile and not designed to handle >> random or poorly-researched changes. > > Less fragility is exactly why I want that surplus barrier. But I've run > out of new ideas for how to explain that ... > >> For example, if only the read and store operations are considered, it's >> obviously >> unsafe, since a read may unwittingly retrieve an store in progress. >> >> >> CPU 0 | CPU 1 >> | >> /* vblank->count == 0 */ >> | >> drm_vblank_count_and_time() | store_vblank(.., inc = 2, ...) >> | >> cur_vblank <= LOAD vblank->count | >> | tslot = vblank->count + 2 >> | /* tslot == 2 */ >> | STORE vblanktime[0] > > This line here is wrong, it should be "STORE vblanktime[2]" > > The "STORE vblanktime[0]" happened way earlier, before 2 smp_wmb and the > previous updating of vblank->count.
&vblanktime[0] == &vblanktime[2] That's why I keep trying to explain you actually have to look at and understand the algorithm before blindly assuming local behavior is sufficient. > I'm also somewhat confused about how you to a line across both cpus for > barriers because barriers only have cpu-local effects (which is why we > always need a barrier on both ends of a transaction). > > In short I still don't follow what's wrong. > -Daniel > >> - - - - - - - - - - - - - - - - - - - - - smp_wmb() - - - - - - - - - - >> /* cur_vblank == 0 */ | >> local <= LOAD vblanktime[0] | >> smp_rmb - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >> | >> * cpu has loaded the wrong timestamp * | >> | >> local <= LOAD vblank->count | >> cur_vblank == local? | >> yes - exit loop | >> | vblank->count += 2 >> - - - - - - - - - - - - - - - - - - - - - smp_wmb() - - - - - - - - - - >> >> Regards, >> Peter Hurley >> >> >>> I also tested your patch + a slightly modified version of Chris vblank >>> delayed disable / instant query patches + my fixes using my own stress >>> tests and hardware timing test equipment on both intel and nouveau, and >>> everything seems to work fine. >>> >>> So i'm all for including this patch and it has my >>> >>> Reviewed-and-tested-by: Mario Kleiner <mario.kleiner...@gmail.com> >>> >>> I just sent out an updated version of my patches, so they don't conflict >>> with this one and also fix a compile failure of drm/qxl with yours. >>> >>> Thanks, >>> -mario >> > _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx