Message ID | ZJTuDi1HNp9L2zxY@windriver.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [RFC] tentative fix for drm/i915/gt regression on preempt-rt | expand |
On 2023-06-22 20:57:50 [-0400], Paul Gortmaker wrote: [ longer report about what is broken.] Commit ade8a0f598443 ("drm/i915: Make all GPU resets atomic") introduces a preempt_disable() section around the invocation of the reset callback. I can't find an explanation why this is needed. There was a comment saying | * We want to perform per-engine reset from atomic context (e.g. | * softirq), which imposes the constraint that we cannot sleep. but it does not state the problem with being preempted while waiting for the reset. The commit itself does not explain why an atomic reset is needed, it just states that it is a requirement now. On !RT we could pull packets from a NICs and forward them other NICs for 2ms. I've been looking over the reset callbacks and gen8_reset_engines() + gen6_reset_engines() acquire a spinlock_t. Since this becomes a sleeping lock on PREEMPT_RT it must not be acquired with disabled preemption. i915_do_reset() acquires no lock but then has two udelay()s of 50us each. Not good latency wise in a preempt-off section. Could someone please explain why atomic is needed during reset, what problems are introduced by a possible preemption? Sebastian
Hi, On 30/06/2023 14:09, Sebastian Andrzej Siewior wrote: > On 2023-06-22 20:57:50 [-0400], Paul Gortmaker wrote: > [ longer report about what is broken.] > > Commit ade8a0f598443 ("drm/i915: Make all GPU resets atomic") introduces > a preempt_disable() section around the invocation of the reset callback. > I can't find an explanation why this is needed. There was a comment > saying > | * We want to perform per-engine reset from atomic context (e.g. > | * softirq), which imposes the constraint that we cannot sleep. > > but it does not state the problem with being preempted while waiting for > the reset. The commit itself does not explain why an atomic reset is > needed, it just states that it is a requirement now. On !RT we could > pull packets from a NICs and forward them other NICs for 2ms. > > I've been looking over the reset callbacks and gen8_reset_engines() + > gen6_reset_engines() acquire a spinlock_t. Since this becomes a sleeping > lock on PREEMPT_RT it must not be acquired with disabled preemption. > i915_do_reset() acquires no lock but then has two udelay()s of 50us > each. Not good latency wise in a preempt-off section. > > Could someone please explain why atomic is needed during reset, what > problems are introduced by a possible preemption? Atomic requirement from that commit text is likely referring to removing the old big sleeping mutex we had in the reset path. So it looks plausible that preempt_disable() section is not strictly needed and perhaps motivation simply was, given those 20-50us polls on hw registers involved, to make them happen as fast as possible and so minimize visual glitching during resets. Although that reasoning would only apply on some hw generations, where the irqsave spinlock is not held across the whole sequence anyway. And I suspect those same platforms would be the annoying ones, if one simply wanted to try without the preempt_disable section, given our wait_for_atomic macro will complain loudly if not used from an atomic context. But I think we do have a macro for short register waits which works with preempting enabled. I will try and cook up a patch and submit to our CI during the week, then see what happens. Or even moving the preempt_disable down so it just encompasses the register write + wait. That would then be under the spinlock which is presumable okay on RT? (Yes I know it wouldn't' solve one half of your "complaint" but lets just entertain the idea for now.) Regards, Tvrtko
On 2023-07-03 16:30:01 [+0100], Tvrtko Ursulin wrote: > Hi, Hi, > > Atomic requirement from that commit text is likely referring to removing the > old big sleeping mutex we had in the reset path. So it looks plausible that > preempt_disable() section is not strictly needed and perhaps motivation > simply was, given those 20-50us polls on hw registers involved, to make them > happen as fast as possible and so minimize visual glitching during resets. > > Although that reasoning would only apply on some hw generations, where the > irqsave spinlock is not held across the whole sequence anyway. > > And I suspect those same platforms would be the annoying ones, if one simply > wanted to try without the preempt_disable section, given our wait_for_atomic > macro will complain loudly if not used from an atomic context. It does not complain on RT, I did patch it out. https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git/tree/patches/0005-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_R.patch?h=linux-6.4.y-rt-patches Interesting, the link there refers to an older posting but this patch is not included. For the other I didn't receive feedback at which point I stopped pushing and put it on the list for later… > But I think we do have a macro for short register waits which works with > preempting enabled. I will try and cook up a patch and submit to our CI > during the week, then see what happens. > > Or even moving the preempt_disable down so it just encompasses the register > write + wait. That would then be under the spinlock which is presumable okay > on RT? (Yes I know it wouldn't' solve one half of your "complaint" but lets > just entertain the idea for now.) You can't preempt_disable(); spin_lock(); You could spin_lock(); preempt_disable(); But if there is no need then there is no need ;) What I worry a bit the udelays… Thanks! > Regards, > > Tvrtko Sebastian
On 03/07/2023 17:12, Sebastian Andrzej Siewior wrote: > On 2023-07-03 16:30:01 [+0100], Tvrtko Ursulin wrote: >> Hi, > Hi, > >> >> Atomic requirement from that commit text is likely referring to removing the >> old big sleeping mutex we had in the reset path. So it looks plausible that >> preempt_disable() section is not strictly needed and perhaps motivation >> simply was, given those 20-50us polls on hw registers involved, to make them >> happen as fast as possible and so minimize visual glitching during resets. >> >> Although that reasoning would only apply on some hw generations, where the >> irqsave spinlock is not held across the whole sequence anyway. >> >> And I suspect those same platforms would be the annoying ones, if one simply >> wanted to try without the preempt_disable section, given our wait_for_atomic >> macro will complain loudly if not used from an atomic context. > > It does not complain on RT, I did patch it out. > https://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git/tree/patches/0005-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_R.patch?h=linux-6.4.y-rt-patches It does not complain when you patch out the complaint, correct. ;) Only purpose of that complaint is to let developers now they are needlessly using the atomic wait_for - the busy looping one intended for very short delays. So if those wait_for_atomic are not really needed (in code paths which are not under a spinlock) I'll try converting them to non-atomic wait_for_us. Or so, will see. > Interesting, the link there refers to an older posting but this patch is > not included. For the other I didn't receive feedback at which point I > stopped pushing and put it on the list for later… > >> But I think we do have a macro for short register waits which works with >> preempting enabled. I will try and cook up a patch and submit to our CI >> during the week, then see what happens. >> >> Or even moving the preempt_disable down so it just encompasses the register >> write + wait. That would then be under the spinlock which is presumable okay >> on RT? (Yes I know it wouldn't' solve one half of your "complaint" but lets >> just entertain the idea for now.) > > You can't > preempt_disable(); > spin_lock(); > > You could > spin_lock(); > preempt_disable(); > > But if there is no need then there is no need ;) > What I worry a bit the udelays… Lets make it a two patch series and then see. First patch to see if we can really get away without the top level preempt_disable, and then second patch to see if we can get away with preemptible short sleeps too. I guess on RT the top priority is consistent scheduling latency and not so much potential UI latency in some edge cases? Saying that because if waiting on the hw reset is made preemptible, _in theory_ it can prolong the reset completion (as observed by i915), and so result in more UI glitching than it normally would. Could be a theoretical point only because it requires both CPU over-subscribe and GPU hangs. It could also easily be that the reset path is only one path, and not so interesting one even, which can cause this on RT. Regards, Tvrtko
On 2023-07-04 09:02:07 [+0100], Tvrtko Ursulin wrote: > Lets make it a two patch series and then see. First patch to see if we can > really get away without the top level preempt_disable, and then second patch > to see if we can get away with preemptible short sleeps too. oki. > I guess on RT the top priority is consistent scheduling latency and not so > much potential UI latency in some edge cases? Saying that because if waiting I would says, yes. If you do RT and you provide some kind of GUI then you prefer to meet your deadlines for your RT load over some UI latency. > on the hw reset is made preemptible, _in theory_ it can prolong the reset > completion (as observed by i915), and so result in more UI glitching than it > normally would. Could be a theoretical point only because it requires both > CPU over-subscribe and GPU hangs. It could also easily be that the reset > path is only one path, and not so interesting one even, which can cause this > on RT. I see. > Regards, > > Tvrtko Sebastian
diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index 9dc244b70ce4..341833c364fe 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -736,9 +736,9 @@ int __intel_gt_reset(struct intel_gt *gt, intel_engine_mask_t engine_mask) intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL); for (retry = 0; ret == -ETIMEDOUT && retry < retries; retry++) { GT_TRACE(gt, "engine_mask=%x\n", engine_mask); - preempt_disable(); + migrate_disable(); ret = reset(gt, engine_mask, retry); - preempt_enable(); + migrate_enable(); } intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL);