[PATCH v2 2/2] drm/xe: Add mutex locking to devcoredump
Matthew Brost
matthew.brost at intel.com
Thu Nov 21 23:44:27 UTC 2024
On Thu, Nov 21, 2024 at 02:55:42PM -0800, John.C.Harrison at Intel.com wrote:
> From: John Harrison <John.C.Harrison at Intel.com>
>
> There are now multiple places that can trigger a coredump. Some of
> which can happen in parallel. There is already a check against
> capturing multiple dumps sequentially, but without locking it doesn't
> guarantee to work against concurrent dumps. And if two dumps do happen
> in parallel, they can end up doing Bad Things such as one call stack
> freeing the data the other call stack is still processing. Which leads
> to a crashed kernel.
>
> Further, it is possible for the DRM timeout to expire and trigger a
> free of the capture while a user is still reading that capture out
> through sysfs. Again leading to dodgy pointer problems.
>
> So, add a mutext lock around the capture, read and free functions to
> prevent inteference.
>
> v2: Swap tiny scope spin_lock for larger scope mutex and fix
> kernel-doc comment (review feedback from Matthe Brost)
>
> Signed-off-by: John Harrison <John.C.Harrison at Intel.com>
> ---
> drivers/gpu/drm/xe/xe_devcoredump.c | 26 +++++++++++++++++++++--
> drivers/gpu/drm/xe/xe_devcoredump_types.h | 4 +++-
> 2 files changed, 27 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
> index dd48745a8a46..0621754ddfd2 100644
> --- a/drivers/gpu/drm/xe/xe_devcoredump.c
> +++ b/drivers/gpu/drm/xe/xe_devcoredump.c
> @@ -202,21 +202,29 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
> if (!coredump)
> return -ENODEV;
>
> + mutex_lock(&coredump->lock);
> +
I'll just explain my reclaim comment in the prior rev here.
'coredump->lock' is the path of reclaim as it can be called from the TDR
which signals dma-fences. This is why most of the devcoredump core uses
GFP_ATOMIC to capture smaller state which could lost quickly. We also
have worker, ss->work, which opportunisticly captures larger VM state /w
GFP_KERNEL. The worker is not in the path reclaim. Thus you cannot flush
the worker under 'coredump->lock' without getting potentail deadlocks.
With proper annotations lockdep complain.
e.g.
We should do this on driver load:
fs_reclaim_acquire();
might_lock();
fs_reclaim_recalim();
Our upper layers should also but may have gaps. Reguardless, priming
lockdep is a good practice and self-documenting.
> ss = &coredump->snapshot;
>
> /* Ensure delayed work is captured before continuing */
> flush_work(&ss->work);
>
So this is where the mutex should be locked.
> - if (!ss->read.buffer)
> + if (!ss->read.buffer) {
> + mutex_unlock(&coredump->lock);
> return -ENODEV;
> + }
>
> - if (offset >= ss->read.size)
> + if (offset >= ss->read.size) {
> + mutex_unlock(&coredump->lock);
> return 0;
> + }
>
> byte_copied = count < ss->read.size - offset ? count :
> ss->read.size - offset;
> memcpy(buffer, ss->read.buffer + offset, byte_copied);
>
> + mutex_unlock(&coredump->lock);
> +
> return byte_copied;
> }
>
> @@ -228,6 +236,8 @@ static void xe_devcoredump_free(void *data)
> if (!data || !coredump_to_xe(coredump))
> return;
>
> + mutex_lock(&coredump->lock);
> +
> cancel_work_sync(&coredump->snapshot.work);
>
Likewise, lock the mutex here.
Matt
> xe_devcoredump_snapshot_free(&coredump->snapshot);
> @@ -238,6 +248,8 @@ static void xe_devcoredump_free(void *data)
> coredump->captured = false;
> drm_info(&coredump_to_xe(coredump)->drm,
> "Xe device coredump has been deleted.\n");
> +
> + mutex_unlock(&coredump->lock);
> }
>
> static void devcoredump_snapshot(struct xe_devcoredump *coredump,
> @@ -312,8 +324,11 @@ void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const cha
> struct xe_devcoredump *coredump = &xe->devcoredump;
> va_list varg;
>
> + mutex_lock(&coredump->lock);
> +
> if (coredump->captured) {
> drm_dbg(&xe->drm, "Multiple hangs are occurring, but only the first snapshot was taken\n");
> + mutex_unlock(&coredump->lock);
> return;
> }
>
> @@ -332,6 +347,7 @@ void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const cha
> dev_coredumpm_timeout(xe->drm.dev, THIS_MODULE, coredump, 0, GFP_KERNEL,
> xe_devcoredump_read, xe_devcoredump_free,
> XE_COREDUMP_TIMEOUT_JIFFIES);
> + mutex_unlock(&coredump->lock);
> }
>
> static void xe_driver_devcoredump_fini(void *arg)
> @@ -343,6 +359,12 @@ static void xe_driver_devcoredump_fini(void *arg)
>
> int xe_devcoredump_init(struct xe_device *xe)
> {
> + int err;
> +
> + err = drmm_mutex_init(&xe->drm, &xe->devcoredump.lock);
> + if (err)
> + return err;
> +
> return devm_add_action_or_reset(xe->drm.dev, xe_driver_devcoredump_fini, &xe->drm);
> }
>
> diff --git a/drivers/gpu/drm/xe/xe_devcoredump_types.h b/drivers/gpu/drm/xe/xe_devcoredump_types.h
> index e6234e887102..1a1d16a96b2d 100644
> --- a/drivers/gpu/drm/xe/xe_devcoredump_types.h
> +++ b/drivers/gpu/drm/xe/xe_devcoredump_types.h
> @@ -80,7 +80,9 @@ struct xe_devcoredump_snapshot {
> * for reading the information.
> */
> struct xe_devcoredump {
> - /** @captured: The snapshot of the first hang has already been taken. */
> + /** @lock: protects access to entire structure */
> + struct mutex lock;
> + /** @captured: The snapshot of the first hang has already been taken */
> bool captured;
> /** @snapshot: Snapshot is captured at time of the first crash */
> struct xe_devcoredump_snapshot snapshot;
> --
> 2.47.0
>
More information about the Intel-xe
mailing list