From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 19715CAC598 for ; Wed, 17 Sep 2025 07:25:05 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 4C5DB10E38C; Wed, 17 Sep 2025 07:25:04 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="AXCrOhso"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.10]) by gabe.freedesktop.org (Postfix) with ESMTPS id 353D310E38C for ; Wed, 17 Sep 2025 07:25:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1758093903; x=1789629903; h=message-id:date:mime-version:subject:to:cc:references: from:in-reply-to:content-transfer-encoding; bh=ko5BPHcXKmBio9vHEciFkZuI3/oFk1LvEQSRa2TdNRo=; b=AXCrOhsoQZPoQxzixYuT5v39buqj3mwJRHfnBTjov2ekGZ5ePASMhHta 1ps+g/CvY8d6FtfjtKUm7lH8KvacFy4oFe3bGCmPHeSG9OoyKwMEJ/FNk 4F3dm/ogeCw1P+IP9tOWcyCrzYeLUcXmq99c8hej1ztOuU1v67m71J6n5 KAPY2HDVV1A0gdG54peYnMsY6jkuzc/csmmE+J5acE6l75FXANYmm5GBe PuzVyhjn0IRgeuAe3Eo7dsVQG36R8sMidAjwX/7Ua0caTdjMfGMqInV79 Q2thnfDjCVNeLCOxGig0q32Su4QM/bURQWJgBPXSMg6gSPIiXqqJxQUZV Q==; X-CSE-ConnectionGUID: W+ZMQjCnS2ed1yO/5IJ7vw== X-CSE-MsgGUID: D5H5m9dRQpmvQgoTVBib0w== X-IronPort-AV: E=McAfee;i="6800,10657,11555"; a="71755656" X-IronPort-AV: E=Sophos;i="6.18,271,1751266800"; d="scan'208";a="71755656" Received: from orviesa005.jf.intel.com ([10.64.159.145]) by fmvoesa104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Sep 2025 00:25:02 -0700 X-CSE-ConnectionGUID: RzIhOGkTTsWKb/X2jgJioQ== X-CSE-MsgGUID: 6mRjKvGjSoyOHExsvnDWqQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.18,271,1751266800"; d="scan'208";a="180307512" Received: from unknown (HELO [10.102.88.152]) ([10.102.88.152]) by orviesa005-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Sep 2025 00:24:59 -0700 Message-ID: <9a179c12-94e5-4e4c-9567-974023b5f2f7@linux.intel.com> Date: Wed, 17 Sep 2025 09:24:58 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] accel/ivpu: Add support for user-managed preemption buffer To: Lizhi Hou , dri-devel@lists.freedesktop.org Cc: oded.gabbay@gmail.com, jeff.hugo@oss.qualcomm.com, maciej.falkowski@linux.intel.com, Andrzej Kacprowski References: <20250915103437.830086-1-karol.wachowski@linux.intel.com> <072bd85b-5c74-e9f4-13e4-151ee24acd62@amd.com> <4a8a7543-37b8-490e-a2f2-abe6d3352719@linux.intel.com> <8d93f6d1-654e-3c93-0bd7-182ec7bc56bb@amd.com> Content-Language: en-US From: Karol Wachowski Organization: Intel Technology Poland sp. z o.o. - ul. Slowackiego 173, 80-298 Gdansk - KRS 101882 - NIP 957-07-52-316 In-Reply-To: <8d93f6d1-654e-3c93-0bd7-182ec7bc56bb@amd.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" On 9/16/2025 5:29 PM, Lizhi Hou wrote: > > On 9/16/25 01:25, Karol Wachowski wrote: >> On 9/15/2025 10:33 PM, Lizhi Hou wrote: >>> On 9/15/25 03:34, Karol Wachowski wrote: >>>> From: Andrzej Kacprowski >>>> >>>> Allow user mode drivers to manage preemption buffers, enabling >>>> memory savings by sharing a single buffer across multiple >>>> command queues within the same memory context. >>>> >>>> Introduce DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE to report the required >>>> preemption buffer size as specified by the firmware. >>>> >>>> The preemption buffer is now passed from user space as an entry >>>> in the BO list of DRM_IVPU_CMDQ_SUBMIT. The buffer must be >>>> non-mappable and large enough to hold preemption data. >>>> >>>> For backward compatibility, the kernel will allocate an internal >>>> preemption buffer if user space does not provide one. >>>> >>>> User space can only provide a single preemption buffer, >>>> simplifying the ioctl interface and parameter validation. >>>> A separate secondary preemption buffer is only needed >>>> to save below 4GB address space on 37xx and only if preemption >>>> buffers are not shared. >>>> >>>> Signed-off-by: Andrzej Kacprowski >>>> Signed-off-by: Karol Wachowski >>>> --- >>>>    drivers/accel/ivpu/ivpu_drv.c |  3 ++ >>>>    drivers/accel/ivpu/ivpu_fw.c  | 57 +++++++++++++++++---- >>>>    drivers/accel/ivpu/ivpu_fw.h  |  7 ++- >>>>    drivers/accel/ivpu/ivpu_gem.h |  7 ++- >>>>    drivers/accel/ivpu/ivpu_job.c | 96 >>>> ++++++++++++++++++++++++----------- >>>>    drivers/accel/ivpu/ivpu_job.h |  4 +- >>>>    include/uapi/drm/ivpu_accel.h | 11 ++++ >>>>    7 files changed, 141 insertions(+), 44 deletions(-) >>>> >>>> diff --git a/drivers/accel/ivpu/ivpu_drv.c >>>> b/drivers/accel/ivpu/ivpu_drv.c >>>> index 3d6d52492536..61c4f627c7c7 100644 >>>> --- a/drivers/accel/ivpu/ivpu_drv.c >>>> +++ b/drivers/accel/ivpu/ivpu_drv.c >>>> @@ -200,6 +200,9 @@ static int ivpu_get_param_ioctl(struct drm_device >>>> *dev, void *data, struct drm_f >>>>        case DRM_IVPU_PARAM_CAPABILITIES: >>>>            args->value = ivpu_is_capable(vdev, args->index); >>>>            break; >>>> +    case DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE: >>>> +        args->value = ivpu_fw_preempt_buf_size(vdev); >>>> +        break; >>>>        default: >>>>            ret = -EINVAL; >>>>            break; >>>> diff --git a/drivers/accel/ivpu/ivpu_fw.c >>>> b/drivers/accel/ivpu/ivpu_fw.c >>>> index 9db741695401..6e0941d324a8 100644 >>>> --- a/drivers/accel/ivpu/ivpu_fw.c >>>> +++ b/drivers/accel/ivpu/ivpu_fw.c >>>> @@ -26,6 +26,8 @@ >>>>    #define FW_RUNTIME_MIN_ADDR    (FW_GLOBAL_MEM_START) >>>>    #define FW_RUNTIME_MAX_ADDR    (FW_GLOBAL_MEM_END - >>>> FW_SHARED_MEM_SIZE) >>>>    #define FW_FILE_IMAGE_OFFSET    (VPU_FW_HEADER_SIZE + >>>> FW_VERSION_HEADER_SIZE) >>>> +#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K >>>> +#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M >>>>      #define WATCHDOG_MSS_REDIRECT    32 >>>>    #define WATCHDOG_NCE_REDIRECT    33 >>>> @@ -151,6 +153,47 @@ ivpu_fw_sched_mode_select(struct ivpu_device >>>> *vdev, const struct vpu_firmware_he >>>>        return VPU_SCHEDULING_MODE_HW; >>>>    } >>>>    +static void >>>> +ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct >>>> vpu_firmware_header *fw_hdr) >>>> +{ >>>> +    struct ivpu_fw_info *fw = vdev->fw; >>>> +    u32 primary_preempt_buf_size, secondary_preempt_buf_size; >>>> + >>>> +    if (fw_hdr->preemption_buffer_1_max_size) >>>> +        primary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_1_max_size; >>>> +    else >>>> +        primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size; >>>> + >>>> +    if (fw_hdr->preemption_buffer_2_max_size) >>>> +        secondary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_2_max_size; >>>> +    else >>>> +        secondary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_2_size; >>>> + >>>> +    ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, >>>> secondary: %u\n", >>>> +         primary_preempt_buf_size, secondary_preempt_buf_size); >>>> + >>>> +    if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE || >>>> +        secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) { >>>> +        ivpu_warn(vdev, "Preemption buffers size too small\n"); >>>> +        return; >>>> +    } >>>> + >>>> +    if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE || >>>> +        secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) { >>>> +        ivpu_warn(vdev, "Preemption buffers size too big\n"); >>>> +        return; >>>> +    } >>>> + >>>> +    if (fw->sched_mode != VPU_SCHEDULING_MODE_HW) >>>> +        return; >>>> + >>>> +    if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE) >>>> +        return; >>>> + >>>> +    vdev->fw->primary_preempt_buf_size = >>>> ALIGN(primary_preempt_buf_size, PAGE_SIZE); >>>> +    vdev->fw->secondary_preempt_buf_size = >>>> ALIGN(secondary_preempt_buf_size, PAGE_SIZE); >>>> +} >>>> + >>>>    static int ivpu_fw_parse(struct ivpu_device *vdev) >>>>    { >>>>        struct ivpu_fw_info *fw = vdev->fw; >>>> @@ -235,17 +278,9 @@ static int ivpu_fw_parse(struct ivpu_device >>>> *vdev) >>>>        fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr); >>>>        ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : >>>> "OS"); >>>>    -    if (fw_hdr->preemption_buffer_1_max_size) >>>> -        fw->primary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_1_max_size; >>>> -    else >>>> -        fw->primary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_1_size; >>>> - >>>> -    if (fw_hdr->preemption_buffer_2_max_size) >>>> -        fw->secondary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_2_max_size; >>>> -    else >>>> -        fw->secondary_preempt_buf_size = >>>> fw_hdr->preemption_buffer_2_size; >>>> -    ivpu_dbg(vdev, FW_BOOT, "Preemption buffer sizes: primary %u, >>>> secondary %u\n", >>>> -         fw->primary_preempt_buf_size, >>>> fw->secondary_preempt_buf_size); >>>> +    ivpu_preemption_config_parse(vdev, fw_hdr); >>>> +    ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s >>>> supported\n", >>>> +         ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not"); >>>>          if (fw_hdr->ro_section_start_address && >>>> !is_within_range(fw_hdr->ro_section_start_address, >>>>                                     fw_hdr->ro_section_size, >>>> diff --git a/drivers/accel/ivpu/ivpu_fw.h >>>> b/drivers/accel/ivpu/ivpu_fw.h >>>> index 7081913fb0dd..6fe2917abda6 100644 >>>> --- a/drivers/accel/ivpu/ivpu_fw.h >>>> +++ b/drivers/accel/ivpu/ivpu_fw.h >>>> @@ -1,6 +1,6 @@ >>>>    /* SPDX-License-Identifier: GPL-2.0-only */ >>>>    /* >>>> - * Copyright (C) 2020-2024 Intel Corporation >>>> + * Copyright (C) 2020-2025 Intel Corporation >>>>     */ >>>>      #ifndef __IVPU_FW_H__ >>>> @@ -52,4 +52,9 @@ static inline bool ivpu_fw_is_cold_boot(struct >>>> ivpu_device *vdev) >>>>        return vdev->fw->entry_point == >>>> vdev->fw->cold_boot_entry_point; >>>>    } >>>>    +static inline u32 ivpu_fw_preempt_buf_size(struct ivpu_device >>>> *vdev) >>>> +{ >>>> +    return vdev->fw->primary_preempt_buf_size + >>>> vdev->fw->secondary_preempt_buf_size; >>>> +} >>>> + >>>>    #endif /* __IVPU_FW_H__ */ >>>> diff --git a/drivers/accel/ivpu/ivpu_gem.h >>>> b/drivers/accel/ivpu/ivpu_gem.h >>>> index aa8ff14f7aae..3ee996d503b2 100644 >>>> --- a/drivers/accel/ivpu/ivpu_gem.h >>>> +++ b/drivers/accel/ivpu/ivpu_gem.h >>>> @@ -1,6 +1,6 @@ >>>>    /* SPDX-License-Identifier: GPL-2.0-only */ >>>>    /* >>>> - * Copyright (C) 2020-2023 Intel Corporation >>>> + * Copyright (C) 2020-2025 Intel Corporation >>>>     */ >>>>    #ifndef __IVPU_GEM_H__ >>>>    #define __IVPU_GEM_H__ >>>> @@ -96,4 +96,9 @@ static inline u32 cpu_to_vpu_addr(struct ivpu_bo >>>> *bo, void *cpu_addr) >>>>        return bo->vpu_addr + (cpu_addr - ivpu_bo_vaddr(bo)); >>>>    } >>>>    +static inline bool ivpu_bo_is_mappable(struct ivpu_bo *bo) >>>> +{ >>>> +    return bo->flags & DRM_IVPU_BO_MAPPABLE; >>>> +} >>>> + >>>>    #endif /* __IVPU_GEM_H__ */ >>>> diff --git a/drivers/accel/ivpu/ivpu_job.c >>>> b/drivers/accel/ivpu/ivpu_job.c >>>> index fa1720fa06a4..521b7ac6e35e 100644 >>>> --- a/drivers/accel/ivpu/ivpu_job.c >>>> +++ b/drivers/accel/ivpu/ivpu_job.c >>>> @@ -34,22 +34,20 @@ static void ivpu_cmdq_ring_db(struct ivpu_device >>>> *vdev, struct ivpu_cmdq *cmdq) >>>>    static int ivpu_preemption_buffers_create(struct ivpu_device *vdev, >>>>                          struct ivpu_file_priv *file_priv, struct >>>> ivpu_cmdq *cmdq) >>>>    { >>>> -    u64 primary_size = ALIGN(vdev->fw->primary_preempt_buf_size, >>>> PAGE_SIZE); >>>> -    u64 secondary_size = ALIGN(vdev->fw->secondary_preempt_buf_size, >>>> PAGE_SIZE); >>>> - >>>> -    if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW || >>>> -        ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE) >>>> +    if (ivpu_fw_preempt_buf_size(vdev) == 0) >>>>            return 0; >>>>          cmdq->primary_preempt_buf = ivpu_bo_create(vdev, >>>> &file_priv->ctx, &vdev->hw->ranges.user, >>>> -                           primary_size, DRM_IVPU_BO_WC); >>>> +                           vdev->fw->primary_preempt_buf_size, >>>> +                           DRM_IVPU_BO_WC); >>> Could vdev->fw->primary_preempt_buf_size be zero? >> This can be zero and zero is intended to indicate that preemption >> buffers are disabled and should not be allocated by the driver. >>> ivpu_fw_preempt_buf_size(vdev) make sure primary+secondary is not zero. >> Either of them or both can be zero. In case of both of them being zero >> function returns early not allocating any of the buffers. > > ivpu_bo_create will generate a warning stack if size is zero. So just > checking if both are zero is not good enough.  Thank you for your feedback. I apologize for my earlier inaccurate statement regarding the possibility of zeroing the buffers separately. To clarify, the driver ensures that both preemption buffer sizes are either non-zero or both zero. This is already handled during the firmware header parsing,  specifically in the ivpu_preemption_config_parse() function.  That function guarantees that both preemption buffer sizes are set to non-zero values together, or both are explicitly set to zero. Therefore, whenever ivpu_preemption_buffers_create() is invoked, both buffers are either allocated with non-zero sizes or not allocated at all.  Adding individual checks within this function would reduce readability, as we only ever allocate both buffers together or neither. Thanks, Karol > > Lizhi > >> >> Best regards, >> >> Karol >> >>> Lizhi >>> >>>>        if (!cmdq->primary_preempt_buf) { >>>>            ivpu_err(vdev, "Failed to create primary preemption >>>> buffer\n"); >>>>            return -ENOMEM; >>>>        } >>>>          cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, >>>> &file_priv->ctx, &vdev->hw->ranges.dma, >>>> -                             secondary_size, DRM_IVPU_BO_WC); >>>> +                             vdev->fw->secondary_preempt_buf_size, >>>> +                             DRM_IVPU_BO_WC); >>>>        if (!cmdq->secondary_preempt_buf) { >>>>            ivpu_err(vdev, "Failed to create secondary preemption >>>> buffer\n"); >>>>            goto err_free_primary; >>>> @@ -66,20 +64,39 @@ static int ivpu_preemption_buffers_create(struct >>>> ivpu_device *vdev, >>>>    static void ivpu_preemption_buffers_free(struct ivpu_device *vdev, >>>>                         struct ivpu_file_priv *file_priv, struct >>>> ivpu_cmdq *cmdq) >>>>    { >>>> -    if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW) >>>> -        return; >>>> - >>>>        if (cmdq->primary_preempt_buf) >>>>            ivpu_bo_free(cmdq->primary_preempt_buf); >>>>        if (cmdq->secondary_preempt_buf) >>>>            ivpu_bo_free(cmdq->secondary_preempt_buf); >>>>    } >>>>    +static int ivpu_preemption_job_init(struct ivpu_device *vdev, >>>> struct ivpu_file_priv *file_priv, >>>> +                    struct ivpu_cmdq *cmdq, struct ivpu_job *job) >>>> +{ >>>> +    int ret; >>>> + >>>> +    /* Use preemption buffer provided by the user space */ >>>> +    if (job->primary_preempt_buf) >>>> +        return 0; >>>> + >>>> +    if (!cmdq->primary_preempt_buf) { >>>> +        /* Allocate per command queue preemption buffers */ >>>> +        ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq); >>>> +        if (ret) >>>> +            return ret; >>>> +    } >>>> + >>>> +    /* Use preemption buffers allocated by the kernel */ >>>> +    job->primary_preempt_buf = cmdq->primary_preempt_buf; >>>> +    job->secondary_preempt_buf = cmdq->secondary_preempt_buf; >>>> + >>>> +    return 0; >>>> +} >>>> + >>>>    static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv >>>> *file_priv) >>>>    { >>>>        struct ivpu_device *vdev = file_priv->vdev; >>>>        struct ivpu_cmdq *cmdq; >>>> -    int ret; >>>>          cmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL); >>>>        if (!cmdq) >>>> @@ -89,10 +106,6 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct >>>> ivpu_file_priv *file_priv) >>>>        if (!cmdq->mem) >>>>            goto err_free_cmdq; >>>>    -    ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq); >>>> -    if (ret) >>>> -        ivpu_warn(vdev, "Failed to allocate preemption buffers, >>>> preemption limited\n"); >>>> - >>>>        return cmdq; >>>>      err_free_cmdq: >>>> @@ -429,17 +442,14 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq >>>> *cmdq, struct ivpu_job *job) >>>>        if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION)) >>>>            entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK; >>>>    -    if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) { >>>> -        if (cmdq->primary_preempt_buf) { >>>> -            entry->primary_preempt_buf_addr = >>>> cmdq->primary_preempt_buf->vpu_addr; >>>> -            entry->primary_preempt_buf_size = >>>> ivpu_bo_size(cmdq->primary_preempt_buf); >>>> -        } >>>> +    if (job->primary_preempt_buf) { >>>> +        entry->primary_preempt_buf_addr = >>>> job->primary_preempt_buf->vpu_addr; >>>> +        entry->primary_preempt_buf_size = >>>> ivpu_bo_size(job->primary_preempt_buf); >>>> +    } >>>>    -        if (cmdq->secondary_preempt_buf) { >>>> -            entry->secondary_preempt_buf_addr = >>>> cmdq->secondary_preempt_buf->vpu_addr; >>>> -            entry->secondary_preempt_buf_size = >>>> -                ivpu_bo_size(cmdq->secondary_preempt_buf); >>>> -        } >>>> +    if (job->secondary_preempt_buf) { >>>> +        entry->secondary_preempt_buf_addr = >>>> job->secondary_preempt_buf->vpu_addr; >>>> +        entry->secondary_preempt_buf_size = >>>> ivpu_bo_size(job->secondary_preempt_buf); >>>>        } >>>>          wmb(); /* Ensure that tail is updated after filling entry */ >>>> @@ -663,6 +673,13 @@ static int ivpu_job_submit(struct ivpu_job *job, >>>> u8 priority, u32 cmdq_id) >>>>            goto err_unlock; >>>>        } >>>>    +    ret = ivpu_preemption_job_init(vdev, file_priv, cmdq, job); >>>> +    if (ret) { >>>> +        ivpu_err(vdev, "Failed to initialize preemption buffers for >>>> job %d: %d\n", >>>> +             job->job_id, ret); >>>> +        goto err_unlock; >>>> +    } >>>> + >>>>        job->cmdq_id = cmdq->id; >>>>          is_first_job = xa_empty(&vdev->submitted_jobs_xa); >>>> @@ -716,7 +733,7 @@ static int ivpu_job_submit(struct ivpu_job *job, >>>> u8 priority, u32 cmdq_id) >>>>      static int >>>>    ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct >>>> ivpu_job *job, u32 *buf_handles, >>>> -                u32 buf_count, u32 commands_offset) >>>> +                u32 buf_count, u32 commands_offset, u32 >>>> preempt_buffer_index) >>>>    { >>>>        struct ivpu_file_priv *file_priv = job->file_priv; >>>>        struct ivpu_device *vdev = file_priv->vdev; >>>> @@ -752,6 +769,20 @@ ivpu_job_prepare_bos_for_submit(struct drm_file >>>> *file, struct ivpu_job *job, u32 >>>>          job->cmd_buf_vpu_addr = bo->vpu_addr + commands_offset; >>>>    +    if (preempt_buffer_index) { >>>> +        struct ivpu_bo *preempt_bo = job->bos[preempt_buffer_index]; >>>> + >>>> +        if (ivpu_bo_size(preempt_bo) < >>>> ivpu_fw_preempt_buf_size(vdev)) { >>>> +            ivpu_warn(vdev, "Preemption buffer is too small\n"); >>>> +            return -EINVAL; >>>> +        } >>>> +        if (ivpu_bo_is_mappable(preempt_bo)) { >>>> +            ivpu_warn(vdev, "Preemption buffer cannot be >>>> mappable\n"); >>>> +            return -EINVAL; >>>> +        } >>>> +        job->primary_preempt_buf = preempt_bo; >>>> +    } >>>> + >>>>        ret = drm_gem_lock_reservations((struct drm_gem_object >>>> **)job->bos, buf_count, >>>>                        &acquire_ctx); >>>>        if (ret) { >>>> @@ -782,7 +813,7 @@ ivpu_job_prepare_bos_for_submit(struct drm_file >>>> *file, struct ivpu_job *job, u32 >>>>      static int ivpu_submit(struct drm_file *file, struct >>>> ivpu_file_priv *file_priv, u32 cmdq_id, >>>>                   u32 buffer_count, u32 engine, void __user >>>> *buffers_ptr, u32 cmds_offset, >>>> -               u8 priority) >>>> +               u32 preempt_buffer_index, u8 priority) >>>>    { >>>>        struct ivpu_device *vdev = file_priv->vdev; >>>>        struct ivpu_job *job; >>>> @@ -814,7 +845,8 @@ static int ivpu_submit(struct drm_file *file, >>>> struct ivpu_file_priv *file_priv, >>>>            goto err_exit_dev; >>>>        } >>>>    -    ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, >>>> buffer_count, cmds_offset); >>>> +    ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, >>>> buffer_count, cmds_offset, >>>> +                          preempt_buffer_index); >>>>        if (ret) { >>>>            ivpu_err(vdev, "Failed to prepare job: %d\n", ret); >>>>            goto err_destroy_job; >>>> @@ -868,7 +900,7 @@ int ivpu_submit_ioctl(struct drm_device *dev, >>>> void *data, struct drm_file *file) >>>>        priority = ivpu_job_to_jsm_priority(args->priority); >>>>          return ivpu_submit(file, file_priv, 0, args->buffer_count, >>>> args->engine, >>>> -               (void __user *)args->buffers_ptr, >>>> args->commands_offset, priority); >>>> +               (void __user *)args->buffers_ptr, >>>> args->commands_offset, 0, priority); >>>>    } >>>>      int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, >>>> struct drm_file *file) >>>> @@ -885,6 +917,9 @@ int ivpu_cmdq_submit_ioctl(struct drm_device >>>> *dev, void *data, struct drm_file * >>>>        if (args->buffer_count == 0 || args->buffer_count > >>>> JOB_MAX_BUFFER_COUNT) >>>>            return -EINVAL; >>>>    +    if (args->preempt_buffer_index >= args->buffer_count) >>>> +        return -EINVAL; >>>> + >>>>        if (!IS_ALIGNED(args->commands_offset, 8)) >>>>            return -EINVAL; >>>>    @@ -895,7 +930,8 @@ int ivpu_cmdq_submit_ioctl(struct drm_device >>>> *dev, void *data, struct drm_file * >>>>            return -EBADFD; >>>>          return ivpu_submit(file, file_priv, args->cmdq_id, >>>> args->buffer_count, VPU_ENGINE_COMPUTE, >>>> -               (void __user *)args->buffers_ptr, >>>> args->commands_offset, 0); >>>> +               (void __user *)args->buffers_ptr, >>>> args->commands_offset, >>>> +               args->preempt_buffer_index, 0); >>>>    } >>>>      int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, >>>> struct drm_file *file) >>>> diff --git a/drivers/accel/ivpu/ivpu_job.h >>>> b/drivers/accel/ivpu/ivpu_job.h >>>> index 2e301c2eea7b..6c8b9c739b51 100644 >>>> --- a/drivers/accel/ivpu/ivpu_job.h >>>> +++ b/drivers/accel/ivpu/ivpu_job.h >>>> @@ -1,6 +1,6 @@ >>>>    /* SPDX-License-Identifier: GPL-2.0-only */ >>>>    /* >>>> - * Copyright (C) 2020-2024 Intel Corporation >>>> + * Copyright (C) 2020-2025 Intel Corporation >>>>     */ >>>>      #ifndef __IVPU_JOB_H__ >>>> @@ -55,6 +55,8 @@ struct ivpu_job { >>>>        u32 job_id; >>>>        u32 engine_idx; >>>>        size_t bo_count; >>>> +    struct ivpu_bo *primary_preempt_buf; >>>> +    struct ivpu_bo *secondary_preempt_buf; >>>>        struct ivpu_bo *bos[] __counted_by(bo_count); >>>>    }; >>>>    diff --git a/include/uapi/drm/ivpu_accel.h >>>> b/include/uapi/drm/ivpu_accel.h >>>> index 160ee1411d4a..e470b0221e02 100644 >>>> --- a/include/uapi/drm/ivpu_accel.h >>>> +++ b/include/uapi/drm/ivpu_accel.h >>>> @@ -90,6 +90,7 @@ extern "C" { >>>>    #define DRM_IVPU_PARAM_TILE_CONFIG        11 >>>>    #define DRM_IVPU_PARAM_SKU            12 >>>>    #define DRM_IVPU_PARAM_CAPABILITIES        13 >>>> +#define DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE  14 >>>>      #define DRM_IVPU_PLATFORM_TYPE_SILICON        0 >>>>    @@ -176,6 +177,9 @@ struct drm_ivpu_param { >>>>         * >>>>         * %DRM_IVPU_PARAM_CAPABILITIES: >>>>         * Supported capabilities (read-only) >>>> +     * >>>> +     * %DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE: >>>> +     * Size of the preemption buffer (read-only) >>>>         */ >>>>        __u32 param; >>>>    @@ -371,6 +375,13 @@ struct drm_ivpu_cmdq_submit { >>>>         * to be executed. The offset has to be 8-byte aligned. >>>>         */ >>>>        __u32 commands_offset; >>>> +    /** >>>> +     * @preempt_buffer_index: >>>> +     * >>>> +     * Index of the preemption buffer in the buffers_ptr array. >>>> +     */ >>>> +    __u32 preempt_buffer_index; >>>> +    __u32 reserved; >>>>    }; >>>>      /* drm_ivpu_bo_wait job status codes */