From: "Grodzovsky, Andrey" <Andrey.Grodzovsky@amd.com>
To: Steven Price <steven.price@arm.com>,
Daniel Vetter <daniel@ffwll.ch>, David Airlie <airlied@linux.ie>,
"Koenig, Christian" <Christian.Koenig@amd.com>
Cc: "Deucher, Alexander" <Alexander.Deucher@amd.com>,
Nayan Deshmukh <nayan26deshmukh@gmail.com>,
Sharat Masetty <smasetty@codeaurora.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"dri-devel@lists.freedesktop.org"
<dri-devel@lists.freedesktop.org>
Subject: Re: [PATCH] drm: Don't free jobs in wait_event_interruptible()
Date: Wed, 25 Sep 2019 16:07:34 +0000 [thread overview]
Message-ID: <da04cf92-f4c7-e896-5070-4d2a9be273aa@amd.com> (raw)
In-Reply-To: <078332cf-ef58-5f76-5c49-8034435f7bea@arm.com>
On 9/25/19 12:00 PM, Steven Price wrote:
> On 25/09/2019 16:56, Grodzovsky, Andrey wrote:
>> On 9/25/19 11:14 AM, Steven Price wrote:
>>
>>> drm_sched_cleanup_jobs() attempts to free finished jobs, however because
>>> it is called as the condition of wait_event_interruptible() it must not
>>> sleep. Unfortunately some free callbacks (notably for Panfrost) do sleep.
>>>
>>> Instead let's rename drm_sched_cleanup_jobs() to
>>> drm_sched_get_cleanup_job() and simply return a job for processing if
>>> there is one. The caller can then call the free_job() callback outside
>>> the wait_event_interruptible() where sleeping is possible before
>>> re-checking and returning to sleep if necessary.
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> ---
>>> drivers/gpu/drm/scheduler/sched_main.c | 44 ++++++++++++++------------
>>> 1 file changed, 24 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
>>> index 9a0ee74d82dc..0ed4aaa4e6d1 100644
>>> --- a/drivers/gpu/drm/scheduler/sched_main.c
>>> +++ b/drivers/gpu/drm/scheduler/sched_main.c
>>> @@ -622,43 +622,41 @@ static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
>>> }
>>>
>>> /**
>>> - * drm_sched_cleanup_jobs - destroy finished jobs
>>> + * drm_sched_get_cleanup_job - fetch the next finished job to be destroyed
>>> *
>>> * @sched: scheduler instance
>>> *
>>> - * Remove all finished jobs from the mirror list and destroy them.
>>> + * Returns the next finished job from the mirror list (if there is one)
>>> + * ready for it to be destroyed.
>>> */
>>> -static void drm_sched_cleanup_jobs(struct drm_gpu_scheduler *sched)
>>> +static struct drm_sched_job *
>>> +drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
>>> {
>>> + struct drm_sched_job *job = NULL;
>>> unsigned long flags;
>>>
>>> /* Don't destroy jobs while the timeout worker is running */
>>> if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
>>> !cancel_delayed_work(&sched->work_tdr))
>>> - return;
>>> -
>>> -
>>> - while (!list_empty(&sched->ring_mirror_list)) {
>>> - struct drm_sched_job *job;
>>> + return NULL;
>>>
>>> - job = list_first_entry(&sched->ring_mirror_list,
>>> + job = list_first_entry_or_null(&sched->ring_mirror_list,
>>> struct drm_sched_job, node);
>>> - if (!dma_fence_is_signaled(&job->s_fence->finished))
>>> - break;
>>>
>>> - spin_lock_irqsave(&sched->job_list_lock, flags);
>>> + spin_lock_irqsave(&sched->job_list_lock, flags);
>>> +
>>> + if (job && dma_fence_is_signaled(&job->s_fence->finished)) {
>>> /* remove job from ring_mirror_list */
>>> list_del_init(&job->node);
>>> - spin_unlock_irqrestore(&sched->job_list_lock, flags);
>>> -
>>> - sched->ops->free_job(job);
>>> + } else {
>>> + job = NULL;
>>> + /* queue timeout for next job */
>>> + drm_sched_start_timeout(sched);
>>> }
>>>
>>> - /* queue timeout for next job */
>>> - spin_lock_irqsave(&sched->job_list_lock, flags);
>>> - drm_sched_start_timeout(sched);
>>> spin_unlock_irqrestore(&sched->job_list_lock, flags);
>>>
>>> + return job;
>>> }
>>>
>>> /**
>>> @@ -698,12 +696,18 @@ static int drm_sched_main(void *param)
>>> struct drm_sched_fence *s_fence;
>>> struct drm_sched_job *sched_job;
>>> struct dma_fence *fence;
>>> + struct drm_sched_job *cleanup_job = NULL;
>>>
>>> wait_event_interruptible(sched->wake_up_worker,
>>> - (drm_sched_cleanup_jobs(sched),
>>> + (cleanup_job = drm_sched_get_cleanup_job(sched)) ||
>>> (!drm_sched_blocked(sched) &&
>>> (entity = drm_sched_select_entity(sched))) ||
>>> - kthread_should_stop()));
>>> + kthread_should_stop());
>>
>> Can't we just call drm_sched_cleanup_jobs right here, remove all the
>> conditions in wait_event_interruptible (make it always true) and after
>> drm_sched_cleanup_jobs is called test for all those conditions and
>> return to sleep if they evaluate to false ? drm_sched_cleanup_jobs is
>> called unconditionally inside wait_event_interruptible anyway... This is
>> more of a question to Christian.
> Christian may know better than me, but I think those conditions need to
> be in wait_event_interruptible() to avoid race conditions. If we simply
> replace all the conditions with a literal "true" then
> wait_event_interruptible() will never actually sleep.
>
> Steve
Yes you right, it won't work as I missed that condition is evaluated as
first step in wait_event_interruptible before it sleeps.
Andrey
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
WARNING: multiple messages have this Message-ID (diff)
From: "Grodzovsky, Andrey" <Andrey.Grodzovsky@amd.com>
To: Steven Price <steven.price@arm.com>,
Daniel Vetter <daniel@ffwll.ch>, David Airlie <airlied@linux.ie>,
"Koenig, Christian" <Christian.Koenig@amd.com>
Cc: "Deucher, Alexander" <Alexander.Deucher@amd.com>,
Nayan Deshmukh <nayan26deshmukh@gmail.com>,
Sharat Masetty <smasetty@codeaurora.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"dri-devel@lists.freedesktop.org"
<dri-devel@lists.freedesktop.org>
Subject: Re: [PATCH] drm: Don't free jobs in wait_event_interruptible()
Date: Wed, 25 Sep 2019 16:07:34 +0000 [thread overview]
Message-ID: <da04cf92-f4c7-e896-5070-4d2a9be273aa@amd.com> (raw)
In-Reply-To: <078332cf-ef58-5f76-5c49-8034435f7bea@arm.com>
On 9/25/19 12:00 PM, Steven Price wrote:
> On 25/09/2019 16:56, Grodzovsky, Andrey wrote:
>> On 9/25/19 11:14 AM, Steven Price wrote:
>>
>>> drm_sched_cleanup_jobs() attempts to free finished jobs, however because
>>> it is called as the condition of wait_event_interruptible() it must not
>>> sleep. Unfortunately some free callbacks (notably for Panfrost) do sleep.
>>>
>>> Instead let's rename drm_sched_cleanup_jobs() to
>>> drm_sched_get_cleanup_job() and simply return a job for processing if
>>> there is one. The caller can then call the free_job() callback outside
>>> the wait_event_interruptible() where sleeping is possible before
>>> re-checking and returning to sleep if necessary.
>>>
>>> Signed-off-by: Steven Price <steven.price@arm.com>
>>> ---
>>> drivers/gpu/drm/scheduler/sched_main.c | 44 ++++++++++++++------------
>>> 1 file changed, 24 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
>>> index 9a0ee74d82dc..0ed4aaa4e6d1 100644
>>> --- a/drivers/gpu/drm/scheduler/sched_main.c
>>> +++ b/drivers/gpu/drm/scheduler/sched_main.c
>>> @@ -622,43 +622,41 @@ static void drm_sched_process_job(struct dma_fence *f, struct dma_fence_cb *cb)
>>> }
>>>
>>> /**
>>> - * drm_sched_cleanup_jobs - destroy finished jobs
>>> + * drm_sched_get_cleanup_job - fetch the next finished job to be destroyed
>>> *
>>> * @sched: scheduler instance
>>> *
>>> - * Remove all finished jobs from the mirror list and destroy them.
>>> + * Returns the next finished job from the mirror list (if there is one)
>>> + * ready for it to be destroyed.
>>> */
>>> -static void drm_sched_cleanup_jobs(struct drm_gpu_scheduler *sched)
>>> +static struct drm_sched_job *
>>> +drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
>>> {
>>> + struct drm_sched_job *job = NULL;
>>> unsigned long flags;
>>>
>>> /* Don't destroy jobs while the timeout worker is running */
>>> if (sched->timeout != MAX_SCHEDULE_TIMEOUT &&
>>> !cancel_delayed_work(&sched->work_tdr))
>>> - return;
>>> -
>>> -
>>> - while (!list_empty(&sched->ring_mirror_list)) {
>>> - struct drm_sched_job *job;
>>> + return NULL;
>>>
>>> - job = list_first_entry(&sched->ring_mirror_list,
>>> + job = list_first_entry_or_null(&sched->ring_mirror_list,
>>> struct drm_sched_job, node);
>>> - if (!dma_fence_is_signaled(&job->s_fence->finished))
>>> - break;
>>>
>>> - spin_lock_irqsave(&sched->job_list_lock, flags);
>>> + spin_lock_irqsave(&sched->job_list_lock, flags);
>>> +
>>> + if (job && dma_fence_is_signaled(&job->s_fence->finished)) {
>>> /* remove job from ring_mirror_list */
>>> list_del_init(&job->node);
>>> - spin_unlock_irqrestore(&sched->job_list_lock, flags);
>>> -
>>> - sched->ops->free_job(job);
>>> + } else {
>>> + job = NULL;
>>> + /* queue timeout for next job */
>>> + drm_sched_start_timeout(sched);
>>> }
>>>
>>> - /* queue timeout for next job */
>>> - spin_lock_irqsave(&sched->job_list_lock, flags);
>>> - drm_sched_start_timeout(sched);
>>> spin_unlock_irqrestore(&sched->job_list_lock, flags);
>>>
>>> + return job;
>>> }
>>>
>>> /**
>>> @@ -698,12 +696,18 @@ static int drm_sched_main(void *param)
>>> struct drm_sched_fence *s_fence;
>>> struct drm_sched_job *sched_job;
>>> struct dma_fence *fence;
>>> + struct drm_sched_job *cleanup_job = NULL;
>>>
>>> wait_event_interruptible(sched->wake_up_worker,
>>> - (drm_sched_cleanup_jobs(sched),
>>> + (cleanup_job = drm_sched_get_cleanup_job(sched)) ||
>>> (!drm_sched_blocked(sched) &&
>>> (entity = drm_sched_select_entity(sched))) ||
>>> - kthread_should_stop()));
>>> + kthread_should_stop());
>>
>> Can't we just call drm_sched_cleanup_jobs right here, remove all the
>> conditions in wait_event_interruptible (make it always true) and after
>> drm_sched_cleanup_jobs is called test for all those conditions and
>> return to sleep if they evaluate to false ? drm_sched_cleanup_jobs is
>> called unconditionally inside wait_event_interruptible anyway... This is
>> more of a question to Christian.
> Christian may know better than me, but I think those conditions need to
> be in wait_event_interruptible() to avoid race conditions. If we simply
> replace all the conditions with a literal "true" then
> wait_event_interruptible() will never actually sleep.
>
> Steve
Yes you right, it won't work as I missed that condition is evaluated as
first step in wait_event_interruptible before it sleeps.
Andrey
next prev parent reply other threads:[~2019-09-25 16:07 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-25 15:14 [PATCH] drm: Don't free jobs in wait_event_interruptible() Steven Price
2019-09-25 15:56 ` Grodzovsky, Andrey
2019-09-25 15:56 ` Grodzovsky, Andrey
2019-09-25 16:00 ` Steven Price
2019-09-25 16:07 ` Grodzovsky, Andrey [this message]
2019-09-25 16:07 ` Grodzovsky, Andrey
2019-09-25 20:09 ` Grodzovsky, Andrey
2019-09-25 20:09 ` Grodzovsky, Andrey
2019-09-26 9:41 ` Steven Price
2019-09-26 9:41 ` Steven Price
2019-09-26 14:31 ` Grodzovsky, Andrey
2019-09-26 14:31 ` Grodzovsky, Andrey
2019-09-26 7:07 ` Koenig, Christian
2019-09-26 7:07 ` Koenig, Christian
2019-09-26 9:47 ` Steven Price
2019-09-26 11:17 ` Koenig, Christian
2019-09-26 14:58 ` Grodzovsky, Andrey
2019-09-26 14:58 ` Grodzovsky, Andrey
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=da04cf92-f4c7-e896-5070-4d2a9be273aa@amd.com \
--to=andrey.grodzovsky@amd.com \
--cc=Alexander.Deucher@amd.com \
--cc=Christian.Koenig@amd.com \
--cc=airlied@linux.ie \
--cc=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nayan26deshmukh@gmail.com \
--cc=smasetty@codeaurora.org \
--cc=steven.price@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.