From nobody Mon Aug 24 04:17:54 2026 Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C77563CC303 for ; Sun, 16 Aug 2026 19:43:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909401; cv=none; b=A7H87fS27N929LDD1WjLOm+FIZ16wgNuONkNDOfi7fVq+NVhNuO2PQHN/ZJmdwkQ8WVEda6KbyeyCJZ5Wi9Mi8UhIj7AITKcAN2st/HeCf+AJMMaNFVc5OLvFnDL/qYqSyhVJphQ2ois955zm4Y1r1OBFQx5YShdSNZUiGdR1TY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909401; c=relaxed/simple; bh=V6Vta2LVRAm12vCpi7RtdujFKx7yC7EukxxOPcdPjLY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OIwQJ649g4r7TN8LBy9RWeUd7QF3WKx/xeAgEto0AyYO6w55A/D1hxsR4zMSdmZFyxxBT7bG0edjzbmHzooZXJzr2FGjbCqnCR3YoPOp7DAZY1kNTx4MV3BAXBghAMO4fziI9frEXtLcigsheiVQXdr3z6bcD5qMG3PPrsiFagw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Xl+NYBMj; arc=none smtp.client-ip=209.85.216.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Xl+NYBMj" Received: by mail-pj1-f44.google.com with SMTP id 98e67ed59e1d1-38ec1402b05so1980734a91.2 for ; Sun, 16 Aug 2026 12:43:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786909399; x=1787514199; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=vQV907MxENOHehwyCp9rz0FCgUqiCVS0Fmif7CccKAA=; b=Xl+NYBMj5u02XK9MQ9LLXEUtAW/YzI6EhTUcTAr6eU9MdIYDQFmOKCjNXy4C/jb904 D74lSsCcfx9+yaLHYvp0fZzii4DW495D2cx674KuYQza5HAUn8ZvWxxPEDjTj1IomA6z XVxtjNLsPy9zDmqUqyguMVnrJl7kpP5x4bFwgjIB+eXDJXU6AC0QVmn15mNJIamJFpO1 L9/DACvSPoi9lgVRftHkM9c/crhwBfN0QyTDixJ7Nabr5EEvvFIe9j/8RAXjWKwnByTt htEFZ/LGe11PYF6LIRfAKDGTd16YyfSqo4reR1eN9tGV2Op9OotcLIidLvl8IrvPeyAv Z1fA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786909399; x=1787514199; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=vQV907MxENOHehwyCp9rz0FCgUqiCVS0Fmif7CccKAA=; b=juJP62cqcEBzHYhWHIuolofvP3/3rXTBraUkIaLoK1OR8tiUD1OfGGE+0U1dE2pe8U 4haMw/Zs4ugfe1vJl0ZUVkeLoosZpg53DORsXnSS0tJbtV+SmlpfWuB3lAGWhrJPqF+l dsYWLBceN4ptrtPs2C506VWl5+XFCYuNYpMs9ofkbH408TnKtPQt6Jrw/OxF+1/vsElI A0rJjR43/lb9ZZfeGS+z0ThSHJQFLHfQWNac7kN99Dxhorod4dHwSCXz/ZcLZd87k5wR hy3fQrMHVruNAYDhOUwae2U8Llbr4xrwUGS2z0H47KV/T+Fz8y6QQdFhudzSegyHHLbj Omag== X-Forwarded-Encrypted: i=1; AHgh+RqW6ScPzazY/nSb7/Rq2oWny26PTcKlsl5Ys3imEUg9xwiV/F6gk7Cza5NqeszVwh2npfgwRAek5JLC9DU=@vger.kernel.org X-Gm-Message-State: AOJu0YyJ6mG+1gWHAl0FgiIBYoM4IDOsvHuHCBbDk9FZ5J2LxNQ39xAf O+MPXob3mpwkgmIMNhsX+2pXAPJ/NgVGONwQhYKt06zrDHBg+y5ycXdP X-Gm-Gg: AR+sD11j64axcZbzCjpB+L+ht1CIM+/289l0CRuOsIfISVTjl7n2msAwkEwn7p4u6/H XnSz4rSeVodwNkpRhzB9tug26Ky98ATvgK7E4B/0DXbX1luC1FEdzcaUHIG4a3lVJ2qidl4KnuE xW6snlwnUo+5yJTXXMz8St46LrxIxQxyTWe1IT3hfOZr4mK289TrHikNElxLaBXHwx0wRu0rvYZ VvAikhEglMM4peyqUrJW8rT2HwY+QVjUL0v7un/kUd1ssbaqrTymiZ0yWQ6LWm6l/8aKhGmBOan jRu03gy07/6BVKYxLTo0bsN2Rjwl/j8LCYld6/6kO6UAdk+DC0ofBpGJqca+5GgJ3B4oiX8WqJZ xaX3reJhVbRS1LhQGPBHFpNpKBAzgXG5NwEbMNa7CKIypmYhxMhH1PMX/TU04T5RxbATneYwMmL EONizgSUPaO9FWApLu9bzey5oWkTcg4UQmDH4/E62JLOhtErELlopyBA== X-Received: by 2002:a17:90b:5606:b0:381:25ce:bcc2 with SMTP id 98e67ed59e1d1-3933b78b698mr21706032a91.6.1786909398914; Sun, 16 Aug 2026 12:43:18 -0700 (PDT) Received: from [127.0.1.1] ([1.243.227.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-395320229f4sm2141310a91.15.2026.08.16.12.43.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Aug 2026 12:43:18 -0700 (PDT) From: Gyeyoung Baek Date: Mon, 17 Aug 2026 04:42:08 +0900 Subject: [PATCH 1/4] drm/imagination: Fix the vm_bo split mappings are linked to Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-pvr-vm-bind-v1-1-0a0f21be7d38@gmail.com> References: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> In-Reply-To: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> To: Alessio Belle , Luigi Santivetti , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Matt Coster , Donald Robson , Sarah Walker , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= Cc: imagination@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Gyeyoung Baek X-Mailer: b4 0.15.1 A remap does not map anything new. It splits an existing mapping and re-creates the parts the request did not cover, and that mapping is handed to the callback in op->remap.unmap->va, carrying the &drm_gpuvm_bo those parts belong to. However, pvr_vm_gpuva_remap() linked the parts to pvr_vm_bind_op::gpuvm_bo, which belongs to the object the request maps rather than the object the parts came from. Fixes: ff5f643de0bf ("drm/imagination: Add GEM and VM related code") Signed-off-by: Gyeyoung Baek --- drivers/gpu/drm/imagination/pvr_vm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagina= tion/pvr_vm.c index 396d349fb6c..608e8c0467d 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -418,6 +418,8 @@ pvr_vm_gpuva_unmap(struct drm_gpuva_op *op, void *op_ct= x) static int pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_ctx) { + struct drm_gpuva *unmap_va =3D op->remap.unmap->va; + struct drm_gpuvm_bo *vm_bo =3D unmap_va->vm_bo; struct pvr_vm_bind_op *ctx =3D op_ctx; u64 va_start =3D 0, va_range =3D 0; int err; @@ -434,18 +436,18 @@ pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_= ctx) =20 if (op->remap.prev) { pvr_gem_object_get(gem_to_pvr_gem(ctx->prev_va->base.gem.obj)); - drm_gpuva_link(&ctx->prev_va->base, ctx->gpuvm_bo); + drm_gpuva_link(&ctx->prev_va->base, vm_bo); ctx->prev_va =3D NULL; } =20 if (op->remap.next) { pvr_gem_object_get(gem_to_pvr_gem(ctx->next_va->base.gem.obj)); - drm_gpuva_link(&ctx->next_va->base, ctx->gpuvm_bo); + drm_gpuva_link(&ctx->next_va->base, vm_bo); ctx->next_va =3D NULL; } =20 - drm_gpuva_unlink(op->remap.unmap->va); - kfree(to_pvr_vm_gpuva(op->remap.unmap->va)); + drm_gpuva_unlink(unmap_va); + kfree(to_pvr_vm_gpuva(unmap_va)); =20 return 0; } --=20 2.43.0 From nobody Mon Aug 24 04:17:54 2026 Received: from mail-pj1-f47.google.com (mail-pj1-f47.google.com [209.85.216.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 02EB83CDBC3 for ; Sun, 16 Aug 2026 19:43:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909405; cv=none; b=YUtBMN+0o2WSdcuz07r5sFHOf1fqIss9rfwFkiCTgM6kGzLRBQhnTZjzkvy7V34yR8ifXRnpQya2UxpLXmD5s231CFgIVS0i1+nrNs4P/sSvL7rh9B6kex6lgmnQqdSGn00R7GMRdwvm500meFshIPvWzTfiG7tY4L6daP+fDys= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909405; c=relaxed/simple; bh=sYxnPVEhCDpcjqtHmfMq97hrhqo0Eo0qcmO6jrU5/zg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UToiZc5SxKki+Jnh7LoQF5pJTKw5h1KSjjdPpMjYdb7FMqG8JhFYwQEK1MqJDG6vfb6OX89jVrKck0IJAk9N85JcRRpY3Ku0Eize4d2mwDNmJfZe/0RTfcYrm9hGuneCY8oR1ZS71geSORBfg5KlGP7CaIO5J1Lg1R4/gQwB5BI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iXIaSy+e; arc=none smtp.client-ip=209.85.216.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iXIaSy+e" Received: by mail-pj1-f47.google.com with SMTP id 98e67ed59e1d1-38dc69c74b8so2821999a91.0 for ; Sun, 16 Aug 2026 12:43:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786909403; x=1787514203; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=+SXbBJ78nn/6ntIYDKfcWHsXpFCebrOGrhFTWiZhXyw=; b=iXIaSy+e4rtCdscObIPSklSSt7yqyP2gHw6Z+xXcgGQ29Wxy7VWNBT+JoG0RVQRHaG Nn59tHggdpt1PH+U/HlXP6gLrWM9j8t78MTEzsa4lRbvuorqMauxF2iBQZpMyqCFNG51 be1d1TiCoiLbToSE8vDDtA0ON37q776PdXMHCK0dCbp1OF4YcVaWtIE73HvKtyltCmYI EWBOpZAsA4g5XmG27ZP8665WcqEt6DhfvxyEfaYdY4bOmAvVVtFcJLtU24M15oPSLpKq TA8uVsPhQDpuKi14H40jkvo4SaRQPynONr291QgX6v1i+lf6ObgaUiAOhpEnoATR2OrB B5Ag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786909403; x=1787514203; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=+SXbBJ78nn/6ntIYDKfcWHsXpFCebrOGrhFTWiZhXyw=; b=FdPeA2yloeoFUsm54J8v8T0NWGO7llNzDv0x91EUbNI6IaWOiMaztwkpZ+BCihV/RD p/nXEEFDwz3+7rEfqkJRD71tDhUJVN8SKh/kmcyHh6XSfknFjmWjC9Ggff0Ylnm9rmbb w/UVdNUfk4IoHDu3CHLX0uR5LFSTmVFiEY+kdIU7J9EOs150z33Ds6GB9GcQGyDYrpa+ M9RbiS0gqm3GpNUtae74B4CC2aGOBN0LtoiTwqTzYsmkvmQYz5Af+2HSAI1qEb+8CLir Um+1P4FjLkU1cPqYOtdmFgrYUgD8EE3XTddHO3Y/+Ozi+aH91zfbyIvwd4hWr6WizwrE kCUQ== X-Forwarded-Encrypted: i=1; AHgh+Royf/CHGrQRirOJZX7nd0k7UvEVhqvWh27ykzRtLsU/keLmHcrFbMISTt1+a0VVDH7BVtm+aGBHEfnhzSQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yxph7X/4tuCTuy33vMKRV1Nit5Mv03AqcherAhkV06sS8lk1v9b uKheO8Xn5g+a+SplG01OZBvhE/DbMzvSxje+UvTnQ+NhcOgWkYjC43MG X-Gm-Gg: AR+sD12sGflTdHFoYEG2fvmV/5NbbdA4hJjokTQGC4BG+ri27p87Kaj7I96IA4RoACj y8Hqc/bjD3TpYYfVvDOuHWz6RWs5N231kDmuomWU25r4+yH/+ZmBD2ieTqhLrvKt377lmdaJAP/ p21SkcCKgQbXyvVOtD1G0Z1spW8bZXS90zDleZb1BQ4lCNX/T7DccQCJuOldaT416WycaCqyH2a C9CKx3eZAz9kzVvD9dx4uyGUEcFjIIdhDTxdgDSgbC1CmuSu2FUcOQEwm2LgxVLSu7DMtuNm/bM hUgNui1ew7roGeSdrNq5/jK57bkrgHNqiCZ6KgBjuZeAKxzW/LTxeqlKMAP2y0lzBZ/FmivTmN2 Q7yTvpWjajqZI5dbYRIXtrfl5wvbiQ//Nw/0P8ZBXE8mTHgt1uBCMnqZOwFPlxrySFt6NWkLf09 DTGvA2gAF2ZEVxSy7O1wrpKr7gLBqJ5HLMHmp4X48PJ4gNQCxERmPecQ== X-Received: by 2002:a17:90b:1651:b0:38c:a59b:5189 with SMTP id 98e67ed59e1d1-3933e633478mr19319456a91.15.1786909403036; Sun, 16 Aug 2026 12:43:23 -0700 (PDT) Received: from [127.0.1.1] ([1.243.227.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-395320229f4sm2141310a91.15.2026.08.16.12.43.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Aug 2026 12:43:22 -0700 (PDT) From: Gyeyoung Baek Date: Mon, 17 Aug 2026 04:42:09 +0900 Subject: [PATCH 2/4] drm/imagination: Switch the GPUVM to immediate mode Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-pvr-vm-bind-v1-2-0a0f21be7d38@gmail.com> References: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> In-Reply-To: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> To: Alessio Belle , Luigi Santivetti , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Matt Coster , Donald Robson , Sarah Walker , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= Cc: imagination@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Gyeyoung Baek X-Mailer: b4 0.15.1 In preparation for the asynchronous VM_BIND that follows. Why DRM_GPUVM_IMMEDIATE_MODE is needed: Running binds from a async-job makes a deadlock. run_job() takes a BO's dma_resv to update that BO's gpuva list, and has to signal a fence once done(=3Dsignalling section). Another thread can be holding the same dma_resv while waiting on that fence, so the two wait on each other. Switching to DRM_GPUVM_IMMEDIATE_MODE requires two things: 1. Drop drm_gpuvm_exec. It calls dma_resv_lock() internally, which is the path that deadlocks. It did also serve as the address space tree lock, so take pvr_vm_context::lock explicitly instead. 2. Let the GEM's gpuva.lock protect the list. That lock is only ever held across list surgery, so no fence waiter can be holding it. Panthor went this way first, for the same reason. No functional change intended. Signed-off-by: Gyeyoung Baek --- drivers/gpu/drm/imagination/pvr_vm.c | 80 +++++++++++++-------------------= ---- 1 file changed, 29 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagina= tion/pvr_vm.c index 608e8c0467d..45df76e61f7 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -214,7 +214,7 @@ static int pvr_vm_bind_op_exec(struct pvr_vm_bind_op *b= ind_op) =20 static void pvr_vm_bind_op_fini(struct pvr_vm_bind_op *bind_op) { - drm_gpuvm_bo_put(bind_op->gpuvm_bo); + drm_gpuvm_bo_put_deferred(bind_op->gpuvm_bo); =20 kfree(bind_op->new_va); kfree(bind_op->prev_va); @@ -255,11 +255,11 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_o= p, =20 bind_op->type =3D PVR_VM_BIND_TYPE_MAP; =20 - dma_resv_lock(obj->resv, NULL); - bind_op->gpuvm_bo =3D drm_gpuvm_bo_obtain_locked(&vm_ctx->gpuvm_mgr, obj); - dma_resv_unlock(obj->resv); - if (IS_ERR(bind_op->gpuvm_bo)) - return PTR_ERR(bind_op->gpuvm_bo); + bind_op->gpuvm_bo =3D drm_gpuvm_bo_create(&vm_ctx->gpuvm_mgr, obj); + if (!bind_op->gpuvm_bo) + return -ENOMEM; + + bind_op->gpuvm_bo =3D drm_gpuvm_bo_obtain_prealloc(bind_op->gpuvm_bo); =20 bind_op->new_va =3D kzalloc_obj(*bind_op->new_va); bind_op->prev_va =3D kzalloc_obj(*bind_op->prev_va); @@ -366,7 +366,11 @@ pvr_vm_gpuva_map(struct drm_gpuva_op *op, void *op_ctx) return err; =20 drm_gpuva_map(&ctx->vm_ctx->gpuvm_mgr, &ctx->new_va->base, &op->map); + + mutex_lock(&op->map.gem.obj->gpuva.lock); drm_gpuva_link(&ctx->new_va->base, ctx->gpuvm_bo); + mutex_unlock(&op->map.gem.obj->gpuva.lock); + ctx->new_va =3D NULL; =20 return 0; @@ -396,7 +400,7 @@ pvr_vm_gpuva_unmap(struct drm_gpuva_op *op, void *op_ct= x) return err; =20 drm_gpuva_unmap(&op->unmap); - drm_gpuva_unlink(op->unmap.va); + drm_gpuva_unlink_defer(op->unmap.va); kfree(to_pvr_vm_gpuva(op->unmap.va)); =20 return 0; @@ -419,6 +423,7 @@ static int pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_ctx) { struct drm_gpuva *unmap_va =3D op->remap.unmap->va; + struct drm_gem_object *obj =3D unmap_va->gem.obj; struct drm_gpuvm_bo *vm_bo =3D unmap_va->vm_bo; struct pvr_vm_bind_op *ctx =3D op_ctx; u64 va_start =3D 0, va_range =3D 0; @@ -436,17 +441,21 @@ pvr_vm_gpuva_remap(struct drm_gpuva_op *op, void *op_= ctx) =20 if (op->remap.prev) { pvr_gem_object_get(gem_to_pvr_gem(ctx->prev_va->base.gem.obj)); + mutex_lock(&obj->gpuva.lock); drm_gpuva_link(&ctx->prev_va->base, vm_bo); + mutex_unlock(&obj->gpuva.lock); ctx->prev_va =3D NULL; } =20 if (op->remap.next) { pvr_gem_object_get(gem_to_pvr_gem(ctx->next_va->base.gem.obj)); + mutex_lock(&obj->gpuva.lock); drm_gpuva_link(&ctx->next_va->base, vm_bo); + mutex_unlock(&obj->gpuva.lock); ctx->next_va =3D NULL; } =20 - drm_gpuva_unlink(unmap_va); + drm_gpuva_unlink_defer(unmap_va); kfree(to_pvr_vm_gpuva(unmap_va)); =20 return 0; @@ -590,7 +599,8 @@ pvr_vm_create_context(struct pvr_device *pvr_dev, bool = is_userspace_context) drm_gem_private_object_init(&pvr_dev->base, &vm_ctx->dummy_gem, 0); drm_gpuvm_init(&vm_ctx->gpuvm_mgr, is_userspace_context ? "PowerVR-user-VM" : "PowerVR-FW-VM", - 0, &pvr_dev->base, &vm_ctx->dummy_gem, + DRM_GPUVM_IMMEDIATE_MODE, &pvr_dev->base, + &vm_ctx->dummy_gem, 0, 1ULL << device_addr_bits, 0, 0, &pvr_vm_gpuva_ops); =20 mutex_init(&vm_ctx->lock); @@ -624,6 +634,7 @@ pvr_vm_context_release(struct kref *ref_count) pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj); =20 pvr_vm_unmap_all(vm_ctx); + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); =20 pvr_mmu_context_destroy(vm_ctx->mmu_ctx); drm_gem_private_object_fini(&vm_ctx->dummy_gem); @@ -693,16 +704,6 @@ void pvr_destroy_vm_contexts_for_file(struct pvr_file = *pvr_file) } } =20 -static int -pvr_vm_lock_extra(struct drm_gpuvm_exec *vm_exec) -{ - struct pvr_vm_bind_op *bind_op =3D vm_exec->extra.priv; - struct pvr_gem_object *pvr_obj =3D bind_op->pvr_obj; - - /* Acquire lock on the GEM object being mapped/unmapped. */ - return drm_exec_lock_obj(&vm_exec->exec, gem_from_pvr_gem(pvr_obj)); -} - /** * pvr_vm_map() - Map a section of physical memory into a section of * device-virtual memory. @@ -730,15 +731,6 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_g= em_object *pvr_obj, u64 pvr_obj_offset, u64 device_addr, u64 size) { struct pvr_vm_bind_op bind_op =3D {0}; - struct drm_gpuvm_exec vm_exec =3D { - .vm =3D &vm_ctx->gpuvm_mgr, - .flags =3D DRM_EXEC_INTERRUPTIBLE_WAIT | - DRM_EXEC_IGNORE_DUPLICATES, - .extra =3D { - .fn =3D pvr_vm_lock_extra, - .priv =3D &bind_op, - }, - }; =20 int err =3D pvr_vm_bind_op_map_init(&bind_op, vm_ctx, pvr_obj, pvr_obj_offset, device_addr, @@ -749,16 +741,12 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_= gem_object *pvr_obj, =20 pvr_gem_object_get(pvr_obj); =20 - err =3D drm_gpuvm_exec_lock(&vm_exec); - if (err) - goto err_cleanup; - + mutex_lock(&vm_ctx->lock); err =3D pvr_vm_bind_op_exec(&bind_op); + mutex_unlock(&vm_ctx->lock); =20 - drm_gpuvm_exec_unlock(&vm_exec); - -err_cleanup: pvr_vm_bind_op_fini(&bind_op); + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); =20 return err; } @@ -787,15 +775,6 @@ pvr_vm_unmap_obj_locked(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size) { struct pvr_vm_bind_op bind_op =3D {0}; - struct drm_gpuvm_exec vm_exec =3D { - .vm =3D &vm_ctx->gpuvm_mgr, - .flags =3D DRM_EXEC_INTERRUPTIBLE_WAIT | - DRM_EXEC_IGNORE_DUPLICATES, - .extra =3D { - .fn =3D pvr_vm_lock_extra, - .priv =3D &bind_op, - }, - }; =20 int err =3D pvr_vm_bind_op_unmap_init(&bind_op, vm_ctx, pvr_obj, device_addr, size); @@ -804,15 +783,8 @@ pvr_vm_unmap_obj_locked(struct pvr_vm_context *vm_ctx, =20 pvr_gem_object_get(pvr_obj); =20 - err =3D drm_gpuvm_exec_lock(&vm_exec); - if (err) - goto err_cleanup; - err =3D pvr_vm_bind_op_exec(&bind_op); =20 - drm_gpuvm_exec_unlock(&vm_exec); - -err_cleanup: pvr_vm_bind_op_fini(&bind_op); =20 return err; @@ -840,6 +812,8 @@ pvr_vm_unmap_obj(struct pvr_vm_context *vm_ctx, struct = pvr_gem_object *pvr_obj, err =3D pvr_vm_unmap_obj_locked(vm_ctx, pvr_obj, device_addr, size); mutex_unlock(&vm_ctx->lock); =20 + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); + return err; } =20 @@ -874,6 +848,8 @@ pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_= addr, u64 size) =20 mutex_unlock(&vm_ctx->lock); =20 + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); + return err; } =20 @@ -906,6 +882,8 @@ pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx) } =20 mutex_unlock(&vm_ctx->lock); + + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); } =20 /* Static data areas are determined by firmware. */ --=20 2.43.0 From nobody Mon Aug 24 04:17:54 2026 Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com [209.85.215.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 221983CFF62 for ; Sun, 16 Aug 2026 19:43:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909410; cv=none; b=aOCWunHWohXGC9GLl/i2TJ2TO5yFp8lB9j84quPGa69o5kzPOdWFPaxhN+DF/jXqVWiSgMU3GmHgkjPqa1VgLe/hjLMTnfB7EpXfxN/cqUbpZ3lDwoo8W9D0T/kyIEUiOMZp9tP/yyOkTXgRmDUkUQE9bQWTk7W0QypD7tOsp2M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909410; c=relaxed/simple; bh=lZ55R6T8i8MnN0/cyicBb1xIl0QabrrSlA3uaFVyUBw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IKWWM18EE5DhcKPWpAgHPZd01UGozYsr6cw5HOjg8shG1nCOs/vPNY7y2Dx1eV4aAF0Rlyib3qqtczo218Mxwmwk+1+X5W4lKkPyOxcw7EYsBwjBVPgO2MSvSTT6YBWmlXiKoTozk+pUWO3xH4DMgl/zB5FGm6rKAA8q2oRhI8k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=X6TucZ2O; arc=none smtp.client-ip=209.85.215.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="X6TucZ2O" Received: by mail-pg1-f176.google.com with SMTP id 41be03b00d2f7-cbedf6a6fe8so1911546a12.3 for ; Sun, 16 Aug 2026 12:43:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786909407; x=1787514207; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Qw9lm5WHODTA8mgreGjGqN+F7jtyssQd06aikBSp6SQ=; b=X6TucZ2OofUPXT9PrO8baD8aPYrj90ZVbns3tr1KKAbHQ9Cd3FGFOQBBjRfT486hI0 DK4UQIhJY1G6O/1VkUc5nqpJ3x2yEY6qNpe6sq6+V/HLV4P6fXZ2wmEHTAKZFiRD6vcP FUkfyQkgDO2kKInZXKkM+Mgul5VSSxmGjwo5JhFDMeICTjrL3AFxkmFgGZRBB4BYvKZF 01y8Wo72R6da2r/OShc2azDgfrzks4FybZDUWXhZ5PwfrDNBIX3q029kDYFNb50ClFXw zJ2Gebg9EMnKtNt8jWBuh7nP9ZMu3Zn/kptSazg0WGOtVhmLVN1OYmup3ziDqs59wcW3 qOgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786909407; x=1787514207; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Qw9lm5WHODTA8mgreGjGqN+F7jtyssQd06aikBSp6SQ=; b=XRx9YS3r0jPE5+mxtop+6o1MEH9jweaSCKkIV0Q1txIg4MZCjarY6RTOtc/RQrUPo2 dvLJW16nTeY+jj/XQroUk+9slE2HC4kHb9eTaVew4mFfU41FUa+engCvXf0Gq/kEkomp gVX/3ngniARMUNVeD+s69+GGdxK8+i1MqKhr+8XDAaskCXp39jksowOxZnyJ467qfWE2 z3fedrlIqdxvHA40Hz1Q09uWh26uNmiOgB8ydyFCcOMM/Pd5GVA5B70xxJ/gY1VcBsY2 noyRD0b6gcz2kNvpLoksSRUKd0DZ2qjIRga8M75RVZJkGWqPicW0Bhkvp9Ek93CmtcFN 6jEg== X-Forwarded-Encrypted: i=1; AHgh+RrJwsON620bF9kuYYk7n6qVf94O0/+Tbl8zQM0ldqbVy4xa0QNw7XkD5mMLdY8SXO9TlAbu3CBa5NeSM94=@vger.kernel.org X-Gm-Message-State: AOJu0YxGhhJajQuMkYtbAABgrtLf0qA7As4M8PMxBjKMQQ+93Se5bLk0 BLQMMdUzOVuFeSHaAqQJYvy5spUN6PAdAx13b5wK3r1kq4c83W5UfBiG X-Gm-Gg: AR+sD11ivmmYSCi5jeYZvvtBo9NeSvPvh2LOsrxh+M2rPu+3fspSrE73iAiUgX68LRe TpzPRNJ/2bxSL5ybm8iQYGE3oCc1FMz+3u2Ld+NYyJibE9H9fu01sLHeV59uHxCwu7iiAyjfkTb IBZw1w2pUfIf4tnQ9Q5h6zDSe3pY1/PRCHtOVo3Pt/zqvr2G9cI75xlZFIdJWxcT9YdvsxSBGAF pv40wHqBBpSgnak/PHEZ/YVkp6cq4SFfDGXEdtZs3LBgd8PNdOnSklCQsuTyW36uQbcIYqcWKkK cayrJrTt0Ggp1HFhBw/nb/YV++c0VTswdhGKSRuOzzpO3CTLRphLITZoNBClxHf7Rl6h3ANE/PO 6+1TUqUpz74jVzbbc7cP5FfH51cZ2DKQrek0SLqLotaVM9JL8C9a9uxHjI4b9zYIGtNbAcJVQbh KQ57gCsN5w+gLU9i2Z5GFxHuGhLWkEnYAlEBy+zLt2ccS9cIwnzOmb0PEry1Ck7nH7vQ== X-Received: by 2002:a17:90b:590d:b0:38f:23d6:b4a9 with SMTP id 98e67ed59e1d1-3933ed66651mr24307375a91.21.1786909407219; Sun, 16 Aug 2026 12:43:27 -0700 (PDT) Received: from [127.0.1.1] ([1.243.227.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-395320229f4sm2141310a91.15.2026.08.16.12.43.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Aug 2026 12:43:26 -0700 (PDT) From: Gyeyoung Baek Date: Mon, 17 Aug 2026 04:42:10 +0900 Subject: [PATCH 3/4] drm/imagination: Add async VM_BIND ioctl Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-pvr-vm-bind-v1-3-0a0f21be7d38@gmail.com> References: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> In-Reply-To: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> To: Alessio Belle , Luigi Santivetti , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Matt Coster , Donald Robson , Sarah Walker , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= Cc: imagination@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Gyeyoung Baek X-Mailer: b4 0.15.1 DRM_IOCTL_PVR_VM_MAP and DRM_IOCTL_PVR_VM_UNMAP fall short of what Vulkan sparse binding needs in two ways: 1. Each carries a single operation, while vkQueueBindSparse hands over an array, so one request turns into hundreds of ioctls. 2. Neither takes sync operations. A bind can neither wait on a fence nor signal one, so ordering it against GPU work means blocking the CPU. Add DRM_IOCTL_PVR_VM_BIND, which extends them with an array of bind operations and an array of sync operations. With DRM_PVR_VM_BIND_ASYNC a request runs asynchronously, as a drm_sched job. The interface follows panthor throughout. Routing VM_MAP and VM_UNMAP through VM_BIND is left to a follow-up. Signed-off-by: Gyeyoung Baek --- drivers/gpu/drm/imagination/pvr_drv.c | 88 ++++++ drivers/gpu/drm/imagination/pvr_drv.h | 4 +- drivers/gpu/drm/imagination/pvr_job.c | 6 + drivers/gpu/drm/imagination/pvr_queue.c | 3 + drivers/gpu/drm/imagination/pvr_vm.c | 504 ++++++++++++++++++++++++++++= ++++ drivers/gpu/drm/imagination/pvr_vm.h | 27 ++ include/uapi/drm/pvr_drm.h | 115 ++++++++ 7 files changed, 746 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagin= ation/pvr_drv.c index 5c965ef0274..ec6ed610e29 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1130,6 +1130,93 @@ pvr_ioctl_vm_unmap(struct drm_device *drm_dev, void = *raw_args, return err; } =20 +/** + * pvr_ioctl_vm_bind() - IOCTL to apply a batch of VM bind operations. + * @drm_dev: [IN] DRM device. + * @raw_args: [IN] Arguments passed to this IOCTL. This must be of type + * &struct drm_pvr_ioctl_vm_bind_args. + * @file: [IN] DRM file private data. + * + * Called from userspace with %DRM_IOCTL_PVR_VM_BIND. + * + * Return: + * * 0 on success, + * * -%EINVAL if arguments are invalid, or + * * Any error returned by pvr_vm_bind(). + */ +static int +pvr_ioctl_vm_bind(struct drm_device *drm_dev, void *raw_args, + struct drm_file *file) +{ + struct drm_pvr_ioctl_vm_bind_args *args =3D raw_args; + struct pvr_file *pvr_file =3D to_pvr_file(file); + struct drm_pvr_vm_bind_op *uapi_ops =3D NULL; + struct drm_pvr_sync_op *sync_ops =3D NULL; + struct pvr_vm_context *vm_ctx; + struct pvr_vm_bind_req req; + int idx; + int err; + + if (!drm_dev_enter(drm_dev, &idx)) + return -EIO; + + if (args->flags & ~DRM_PVR_VM_BIND_FLAGS_MASK) { + err =3D -EINVAL; + goto err_drm_dev_exit; + } + + if (!(args->flags & DRM_PVR_VM_BIND_ASYNC) && args->sync_ops.count) { + err =3D -EINVAL; + goto err_drm_dev_exit; + } + + if (!args->ops.count && !args->sync_ops.count) { + err =3D 0; + goto err_drm_dev_exit; + } + + vm_ctx =3D pvr_vm_context_lookup(pvr_file, args->vm_context_handle); + if (!vm_ctx) { + err =3D -EINVAL; + goto err_drm_dev_exit; + } + + if (args->ops.count) { + err =3D PVR_UOBJ_GET_ARRAY(uapi_ops, &args->ops); + if (err) + goto err_put_vm_context; + } + + if (args->sync_ops.count) { + err =3D PVR_UOBJ_GET_ARRAY(sync_ops, &args->sync_ops); + if (err) + goto err_free_uapi_ops; + } + + req =3D (struct pvr_vm_bind_req){ + .ops =3D uapi_ops, + .op_count =3D args->ops.count, + .sync_ops =3D sync_ops, + .sync_op_count =3D args->sync_ops.count, + .async =3D args->flags & DRM_PVR_VM_BIND_ASYNC, + }; + + err =3D pvr_vm_bind(vm_ctx, pvr_file, &req); + + kvfree(sync_ops); + +err_free_uapi_ops: + kvfree(uapi_ops); + +err_put_vm_context: + pvr_vm_context_put(vm_ctx); + +err_drm_dev_exit: + drm_dev_exit(idx); + + return err; +} + /* * pvr_ioctl_submit_job() - IOCTL to submit a job to the GPU * @drm_dev: [IN] DRM device. @@ -1290,6 +1377,7 @@ static const struct drm_ioctl_desc pvr_drm_driver_ioc= tls[] =3D { DRM_PVR_IOCTL(CREATE_HWRT_DATASET, create_hwrt_dataset, DRM_RENDER_ALLOW), DRM_PVR_IOCTL(DESTROY_HWRT_DATASET, destroy_hwrt_dataset, DRM_RENDER_ALLO= W), DRM_PVR_IOCTL(SUBMIT_JOBS, submit_jobs, DRM_RENDER_ALLOW), + DRM_PVR_IOCTL(VM_BIND, vm_bind, DRM_RENDER_ALLOW), }; =20 /* clang-format on */ diff --git a/drivers/gpu/drm/imagination/pvr_drv.h b/drivers/gpu/drm/imagin= ation/pvr_drv.h index 7fa147312dd..9ca8f8780a3 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.h +++ b/drivers/gpu/drm/imagination/pvr_drv.h @@ -13,9 +13,10 @@ /* * Driver interface version: * - 1.0: Initial interface + * - 1.1: adds DRM_IOCTL_PVR_VM_BIND */ #define PVR_DRIVER_MAJOR 1 -#define PVR_DRIVER_MINOR 0 +#define PVR_DRIVER_MINOR 1 #define PVR_DRIVER_PATCHLEVEL 0 =20 int pvr_get_uobj(u64 usr_ptr, u32 usr_size, u32 min_size, u32 obj_size, vo= id *out); @@ -60,6 +61,7 @@ int pvr_set_uobj_array(const struct drm_pvr_obj_array *ou= t, u32 min_stride, u32 #define PVR_UOBJ_MIN_SIZE(_obj_name) _Generic(_obj_name \ PVR_UOBJ_DECL(struct drm_pvr_job, hwrt) \ PVR_UOBJ_DECL(struct drm_pvr_sync_op, value) \ + PVR_UOBJ_DECL(struct drm_pvr_vm_bind_op, size) \ PVR_UOBJ_DECL(struct drm_pvr_dev_query_gpu_info, num_phantoms) \ PVR_UOBJ_DECL(struct drm_pvr_dev_query_runtime_info, cdm_max_local_mem_si= ze_regs) \ PVR_UOBJ_DECL(struct drm_pvr_dev_query_quirks, _padding_c) \ diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagin= ation/pvr_job.c index b8a58d81700..04f920aaf12 100644 --- a/drivers/gpu/drm/imagination/pvr_job.c +++ b/drivers/gpu/drm/imagination/pvr_job.c @@ -15,6 +15,7 @@ #include "pvr_stream_defs.h" #include "pvr_sync.h" #include "pvr_trace.h" +#include "pvr_vm.h" =20 #include #include @@ -434,6 +435,11 @@ create_job(struct pvr_device *pvr_dev, goto err_put_job; } =20 + if (pvr_vm_context_is_unusable(job->ctx->vm_ctx)) { + err =3D -ECANCELED; + goto err_put_job; + } + if (args->hwrt.set_handle) { job->hwrt =3D pvr_hwrt_data_lookup(pvr_file, args->hwrt.set_handle, args->hwrt.data_index); diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imag= ination/pvr_queue.c index 09993e858df..6b83734604d 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -761,6 +761,9 @@ static struct dma_fence *pvr_queue_run_job(struct drm_s= ched_job *sched_job) return dma_fence_get(job->done_fence); } =20 + if (pvr_vm_context_is_unusable(job->ctx->vm_ctx)) + return ERR_PTR(-ECANCELED); + /* The only kind of jobs that can be paired are geometry and fragment, and * we bail out early if we see a fragment job that's paired with a geomet= ry job. * Paired jobs must also target the same context and point to the same HW= RT. diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagina= tion/pvr_vm.c index 45df76e61f7..f92bfeacd7a 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -9,20 +9,27 @@ #include "pvr_mmu.h" #include "pvr_rogue_fwif.h" #include "pvr_rogue_heap_config.h" +#include "pvr_sync.h" =20 #include #include #include #include +#include =20 #include #include +#include #include #include #include #include #include +#include +#include #include +#include +#include =20 /** * DOC: Memory context @@ -50,6 +57,32 @@ struct pvr_vm_context { /** @lock: Global lock on this VM. */ struct mutex lock; =20 + /** + * @sched: Scheduler used to serialise asynchronous VM_BIND requests. + * + * Only initialised for userspace VM contexts; see @sched_initialised. + */ + struct drm_gpu_scheduler sched; + + /** @entity: Scheduling entity feeding @sched. */ + struct drm_sched_entity entity; + + /** @sched_initialised: True if @sched and @entity need tearing down. */ + bool sched_initialised; + + /** + * @unusable: An asynchronous bind failed part way through, leaving the + * address space in a state nobody can reason about. + * + * Only the asynchronous path sets this; a synchronous failure reaches + * its caller directly, who then owns the recovery. Set once and never + * cleared: further operations are rejected with -%ECANCELED and the + * context has to be destroyed and recreated. + * + * Written under @lock, read without it. + */ + bool unusable; + /** * @fw_mem_ctx_obj: Firmware object representing firmware memory * context. @@ -72,6 +105,9 @@ struct pvr_vm_context *to_pvr_vm_context(struct drm_gpuv= m *gpuvm) return container_of(gpuvm, struct pvr_vm_context, gpuvm_mgr); } =20 +static int pvr_vm_bind_sched_init(struct pvr_vm_context *vm_ctx); +static void pvr_vm_bind_sched_fini(struct pvr_vm_context *vm_ctx); + struct pvr_vm_context *pvr_vm_context_get(struct pvr_vm_context *vm_ctx) { if (vm_ctx) @@ -606,8 +642,26 @@ pvr_vm_create_context(struct pvr_device *pvr_dev, bool= is_userspace_context) mutex_init(&vm_ctx->lock); kref_init(&vm_ctx->ref_count); =20 + if (is_userspace_context) { + err =3D pvr_vm_bind_sched_init(vm_ctx); + if (err) + goto err_gpuvm_put; + } + return vm_ctx; =20 +err_gpuvm_put: + if (vm_ctx->fw_mem_ctx_obj) + pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj); + + pvr_mmu_context_destroy(vm_ctx->mmu_ctx); + drm_gem_private_object_fini(&vm_ctx->dummy_gem); + mutex_destroy(&vm_ctx->lock); + + drm_gpuvm_put(&vm_ctx->gpuvm_mgr); + + return ERR_PTR(err); + err_page_table_destroy: pvr_mmu_context_destroy(vm_ctx->mmu_ctx); =20 @@ -630,6 +684,8 @@ pvr_vm_context_release(struct kref *ref_count) struct pvr_vm_context *vm_ctx =3D container_of(ref_count, struct pvr_vm_context, ref_count); =20 + pvr_vm_bind_sched_fini(vm_ctx); + if (vm_ctx->fw_mem_ctx_obj) pvr_fw_object_destroy(vm_ctx->fw_mem_ctx_obj); =20 @@ -853,6 +909,18 @@ pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device= _addr, u64 size) return err; } =20 +/** + * pvr_vm_context_is_unusable() - Test whether a VM context has been left = in an + * undefined state by a failed operation. + * @vm_ctx: Target VM context. + * + * Return: %true if the context rejects further operations. + */ +bool pvr_vm_context_is_unusable(struct pvr_vm_context *vm_ctx) +{ + return READ_ONCE(vm_ctx->unusable); +} + /** * pvr_vm_unmap_all() - Unmap all mappings associated with a VM context. * @vm_ctx: Target VM context. @@ -1174,3 +1242,439 @@ pvr_vm_get_fw_mem_context(struct pvr_vm_context *vm= _ctx) { return vm_ctx->fw_mem_ctx_obj; } + +/** + * DOC: Asynchronous VM_BIND + * + * %DRM_IOCTL_PVR_VM_BIND can queue a batch of bind operations instead of + * applying them inline. Each request becomes a &pvr_vm_bind_job pushed to= a + * per-VM-context &drm_gpu_scheduler, which guarantees that requests targe= ting + * the same VM context are applied in submission order. + * + * Everything that can fail or allocate - argument validation, page table + * pre-allocation, page pinning - happens while building the job, because + * &drm_sched_backend_ops.run_job executes inside the dma-fence signalling + * critical path. For the same reason the GPUVM is initialised with + * %DRM_GPUVM_IMMEDIATE_MODE, so that mappings are tracked under the GEM's + * gpuva.lock rather than its dma_resv. + */ + +/** + * struct pvr_vm_bind_job - A queued batch of VM bind operations. + */ +struct pvr_vm_bind_job { + /** @base: Inherited &drm_sched_job object. */ + struct drm_sched_job base; + + /** @vm_ctx: VM context targeted by this job. Holds a reference. */ + struct pvr_vm_context *vm_ctx; + + /** @op_count: Number of entries in @ops. */ + u32 op_count; + + /** @ops: Prepared bind operations, applied in array order. */ + struct pvr_vm_bind_op *ops; + + /** + * @cleanup_work: Releases @ops and the reference on @vm_ctx. + * + * free_job() cannot do this itself: dropping what may be the last VM + * context reference there would call drm_sched_fini(), which flushes + * the very worker free_job() runs on. + */ + struct work_struct cleanup_work; +}; + +#define to_pvr_vm_bind_job(sched_job) \ + container_of((sched_job), struct pvr_vm_bind_job, base) + +/** + * pvr_vm_bind_ops_free() - Release an array of prepared bind operations. + * @ops: Array to release. May be %NULL. + * @count: Number of prepared entries in @ops. + */ +static void pvr_vm_bind_ops_free(struct pvr_vm_bind_op *ops, u32 count) +{ + if (!ops) + return; + + for (u32 i =3D 0; i < count; i++) + pvr_vm_bind_op_fini(&ops[i]); + + kvfree(ops); +} + +static void pvr_vm_bind_job_free(struct pvr_vm_bind_job *job) +{ + if (!job) + return; + + pvr_vm_bind_ops_free(job->ops, job->op_count); + + if (job->vm_ctx) { + drm_gpuvm_bo_deferred_cleanup(&job->vm_ctx->gpuvm_mgr); + pvr_vm_context_put(job->vm_ctx); + } + + kfree(job); +} + +static void pvr_vm_bind_job_cleanup_work(struct work_struct *work) +{ + struct pvr_vm_bind_job *job =3D + container_of(work, struct pvr_vm_bind_job, cleanup_work); + + pvr_vm_bind_job_free(job); +} + +static struct dma_fence * +pvr_vm_bind_run_job(struct drm_sched_job *sched_job) +{ + struct pvr_vm_bind_job *job =3D to_pvr_vm_bind_job(sched_job); + struct pvr_vm_context *vm_ctx =3D job->vm_ctx; + int err =3D 0; + bool cookie; + + if (pvr_vm_context_is_unusable(vm_ctx)) + return ERR_PTR(-ECANCELED); + + cookie =3D dma_fence_begin_signalling(); + + mutex_lock(&vm_ctx->lock); + + for (u32 i =3D 0; i < job->op_count; i++) { + err =3D pvr_vm_bind_op_exec(&job->ops[i]); + if (err) + break; + } + + if (err) + WRITE_ONCE(vm_ctx->unusable, true); + + mutex_unlock(&vm_ctx->lock); + + dma_fence_end_signalling(cookie); + + /* NULL completes the job: the page tables are already updated. */ + return err ? ERR_PTR(err) : NULL; +} + +static enum drm_gpu_sched_stat +pvr_vm_bind_timedout_job(struct drm_sched_job *sched_job) +{ + WARN(1, "VM bind jobs run on a CPU worker and cannot hang\n"); + + return DRM_GPU_SCHED_STAT_RESET; +} + +static void pvr_vm_bind_free_job(struct drm_sched_job *sched_job) +{ + struct pvr_vm_bind_job *job =3D to_pvr_vm_bind_job(sched_job); + + drm_sched_job_cleanup(sched_job); + + /* Flushed before the device goes away, so it cannot outlive it. */ + queue_work(job->vm_ctx->pvr_dev->sched_wq, &job->cleanup_work); +} + +static const struct drm_sched_backend_ops pvr_vm_bind_sched_ops =3D { + .run_job =3D pvr_vm_bind_run_job, + .timedout_job =3D pvr_vm_bind_timedout_job, + .free_job =3D pvr_vm_bind_free_job, +}; + +/** + * pvr_vm_bind_sched_init() - Set up the VM_BIND scheduler of a VM context. + * @vm_ctx: Target VM context. + * + * Return: + * * 0 on success, or + * * Any error returned by drm_sched_init() or drm_sched_entity_init(). + */ +static int pvr_vm_bind_sched_init(struct pvr_vm_context *vm_ctx) +{ + struct pvr_device *pvr_dev =3D vm_ctx->pvr_dev; + struct drm_gpu_scheduler *sched =3D &vm_ctx->sched; + const struct drm_sched_init_args sched_args =3D { + .ops =3D &pvr_vm_bind_sched_ops, + .submit_wq =3D pvr_dev->sched_wq, + .credit_limit =3D 1, + .hang_limit =3D 0, + /* Bind jobs run on a CPU worker and cannot hang. */ + .timeout =3D MAX_SCHEDULE_TIMEOUT, + .name =3D "pvr-vm-bind", + .dev =3D from_pvr_device(pvr_dev)->dev, + }; + int err; + + err =3D drm_sched_init(sched, &sched_args); + if (err) + return err; + + err =3D drm_sched_entity_init(&vm_ctx->entity, DRM_SCHED_PRIORITY_NORMAL, + &sched, 1, NULL); + if (err) + goto err_sched_fini; + + vm_ctx->sched_initialised =3D true; + + return 0; + +err_sched_fini: + drm_sched_fini(sched); + + return err; +} + +/** + * pvr_vm_bind_sched_fini() - Tear down the VM_BIND scheduler of a VM cont= ext. + * @vm_ctx: Target VM context. + * + * Waits for all queued bind jobs to be applied before returning. + */ +static void pvr_vm_bind_sched_fini(struct pvr_vm_context *vm_ctx) +{ + if (!vm_ctx->sched_initialised) + return; + + drm_sched_entity_destroy(&vm_ctx->entity); + drm_sched_fini(&vm_ctx->sched); + vm_ctx->sched_initialised =3D false; +} + +/** + * pvr_vm_bind_op_init_from_uapi() - Prepare a single bind op from its + * userspace description. + * @bind_op: Bind op to initialise. + * @vm_ctx: Target VM context. + * @pvr_file: PowerVR file used to resolve buffer object handles. + * @uapi_op: Userspace description of the operation. + * + * On success @bind_op owns every resource it needs to be executed later, + * and must be released with pvr_vm_bind_op_fini(). + * + * Return: + * * 0 on success, + * * -%EINVAL if @uapi_op is malformed, or + * * -%ENOENT if @uapi_op refers to an unknown buffer object. + */ +static int +pvr_vm_bind_op_init_from_uapi(struct pvr_vm_bind_op *bind_op, + struct pvr_vm_context *vm_ctx, + struct pvr_file *pvr_file, + const struct drm_pvr_vm_bind_op *uapi_op) +{ + struct pvr_gem_object *pvr_obj; + int err; + + if (uapi_op->flags & ~DRM_PVR_VM_BIND_OP_FLAGS_MASK) + return -EINVAL; + + if (!uapi_op->size) + return -EINVAL; + + switch (uapi_op->flags & DRM_PVR_VM_BIND_OP_TYPE_MASK) { + case DRM_PVR_VM_BIND_OP_TYPE_MAP: + pvr_obj =3D pvr_gem_object_from_handle(pvr_file, uapi_op->handle); + if (!pvr_obj) + return -ENOENT; + + err =3D pvr_vm_bind_op_map_init(bind_op, vm_ctx, pvr_obj, + uapi_op->offset, + uapi_op->device_addr, + uapi_op->size); + if (err) { + pvr_gem_object_put(pvr_obj); + return err; + } + + return 0; + + case DRM_PVR_VM_BIND_OP_TYPE_UNMAP: + if (uapi_op->handle || uapi_op->offset) + return -EINVAL; + + return pvr_vm_bind_op_unmap_init(bind_op, vm_ctx, NULL, + uapi_op->device_addr, + uapi_op->size); + + default: + return -EINVAL; + } +} + +/** + * pvr_vm_bind_ops_create_from_uapi() - Prepare bind operations from their + * userspace description. + * @vm_ctx: Target VM context. + * @pvr_file: PowerVR file used to resolve buffer object handles. + * @uapi_ops: Array of userspace operation descriptions. + * @op_count: Number of entries in @uapi_ops. + * + * Every allocation needed to apply the operations is performed here, so t= hat + * applying them later - possibly from inside the dma-fence signalling cri= tical + * path - cannot fail for want of memory. + * + * Return: The new array on success, or an ERR_PTR on failure. + */ +static struct pvr_vm_bind_op * +pvr_vm_bind_ops_create_from_uapi(struct pvr_vm_context *vm_ctx, + struct pvr_file *pvr_file, + const struct drm_pvr_vm_bind_op *uapi_ops, + u32 op_count) +{ + struct pvr_vm_bind_op *ops; + int err; + + ops =3D kvzalloc_objs(*ops, op_count, GFP_KERNEL); + if (!ops) + return ERR_PTR(-ENOMEM); + + for (u32 prepared =3D 0; prepared < op_count; prepared++) { + err =3D pvr_vm_bind_op_init_from_uapi(&ops[prepared], vm_ctx, + pvr_file, + &uapi_ops[prepared]); + if (err) { + pvr_vm_bind_ops_free(ops, prepared); + return ERR_PTR(err); + } + } + + return ops; +} + +/** + * pvr_vm_bind_exec_async() - Queue a batch of bind operations. + * @vm_ctx: Target VM context. + * @ops: Prepared bind operations. Consumed by this function. + * @op_count: Number of entries in @ops. + * @pvr_file: PowerVR file the request was issued on. + * @sync_ops: Sync operations to apply to the request. + * @sync_op_count: Number of entries in @sync_ops. + * + * Wraps @ops in a &pvr_vm_bind_job and hands it to the VM context schedul= er. + * The synchronous path needs no job at all; it applies @ops inline. + * + * Return: + * * 0 on success, or + * * Any error returned while resolving @sync_ops or arming the job. + */ +static int pvr_vm_bind_exec_async(struct pvr_vm_context *vm_ctx, + struct pvr_vm_bind_op *ops, u32 op_count, + struct pvr_file *pvr_file, + const struct drm_pvr_sync_op *sync_ops, + u32 sync_op_count) +{ + struct dma_fence *finished_fence; + struct pvr_vm_bind_job *job; + struct xarray signal_array; + int err; + + job =3D kzalloc_obj(*job); + if (!job) { + pvr_vm_bind_ops_free(ops, op_count); + return -ENOMEM; + } + + job->vm_ctx =3D pvr_vm_context_get(vm_ctx); + job->ops =3D ops; + job->op_count =3D op_count; + INIT_WORK(&job->cleanup_work, pvr_vm_bind_job_cleanup_work); + + xa_init_flags(&signal_array, XA_FLAGS_ALLOC); + + err =3D drm_sched_job_init(&job->base, &vm_ctx->entity, 1, pvr_file, + from_pvr_file(pvr_file)->client_id); + if (err) + goto err_cleanup_signal_array; + + err =3D pvr_sync_signal_array_collect_ops(&signal_array, + from_pvr_file(pvr_file), + sync_op_count, sync_ops); + if (err) + goto err_cleanup_job; + + err =3D pvr_sync_add_deps_to_job(pvr_file, &job->base, sync_op_count, + sync_ops, &signal_array); + if (err) + goto err_cleanup_job; + + drm_sched_job_arm(&job->base); + finished_fence =3D &job->base.s_fence->finished; + + /* + * Arming is the point of no return: the job has to be pushed now. The + * update below only touches entries the collect above created, so it + * cannot fail, and a driver bug that made it fail has already warned. + */ + pvr_sync_signal_array_update_fences(&signal_array, sync_op_count, + sync_ops, finished_fence); + + drm_sched_entity_push_job(&job->base); + pvr_sync_signal_array_push_fences(&signal_array); + + pvr_sync_signal_array_cleanup(&signal_array); + + return 0; + +err_cleanup_job: + drm_sched_job_cleanup(&job->base); + +err_cleanup_signal_array: + pvr_sync_signal_array_cleanup(&signal_array); + pvr_vm_bind_job_free(job); + + return err; +} + +/** + * pvr_vm_bind() - Apply a batch of bind operations to a VM context. + * @vm_ctx: Target VM context. + * @pvr_file: PowerVR file the request was issued on. + * @req: The request to apply. + * + * This is the single entry point for every userspace-initiated mapping ch= ange: + * %DRM_IOCTL_PVR_VM_BIND passes its whole operation array, while the lega= cy + * %DRM_IOCTL_PVR_VM_MAP and %DRM_IOCTL_PVR_VM_UNMAP build a one-element a= rray. + * + * Return: + * * 0 on success, or + * * A negative error code on failure. + */ +int pvr_vm_bind(struct pvr_vm_context *vm_ctx, struct pvr_file *pvr_file, + const struct pvr_vm_bind_req *req) +{ + struct pvr_vm_bind_op *ops; + int err =3D 0; + + if (pvr_vm_context_is_unusable(vm_ctx)) + return -ECANCELED; + + if (req->async && !vm_ctx->sched_initialised) + return -EINVAL; + + ops =3D pvr_vm_bind_ops_create_from_uapi(vm_ctx, pvr_file, req->ops, + req->op_count); + if (IS_ERR(ops)) + return PTR_ERR(ops); + + if (req->async) + return pvr_vm_bind_exec_async(vm_ctx, ops, req->op_count, + pvr_file, req->sync_ops, + req->sync_op_count); + + mutex_lock(&vm_ctx->lock); + + if (pvr_vm_context_is_unusable(vm_ctx)) + err =3D -ECANCELED; + + for (u32 i =3D 0; !err && i < req->op_count; i++) + err =3D pvr_vm_bind_op_exec(&ops[i]); + + mutex_unlock(&vm_ctx->lock); + + pvr_vm_bind_ops_free(ops, req->op_count); + drm_gpuvm_bo_deferred_cleanup(&vm_ctx->gpuvm_mgr); + + return err; +} diff --git a/drivers/gpu/drm/imagination/pvr_vm.h b/drivers/gpu/drm/imagina= tion/pvr_vm.h index b0528dffa7f..76762133c64 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.h +++ b/drivers/gpu/drm/imagination/pvr_vm.h @@ -22,6 +22,8 @@ struct pvr_vm_context; =20 /* Forward declaration from */ struct drm_pvr_ioctl_get_heap_info_args; +struct drm_pvr_sync_op; +struct drm_pvr_vm_bind_op; =20 /* Forward declaration from */ struct drm_exec; @@ -44,6 +46,31 @@ int pvr_vm_unmap_obj(struct pvr_vm_context *vm_ctx, int pvr_vm_unmap(struct pvr_vm_context *vm_ctx, u64 device_addr, u64 size); void pvr_vm_unmap_all(struct pvr_vm_context *vm_ctx); =20 +bool pvr_vm_context_is_unusable(struct pvr_vm_context *vm_ctx); + +/** + * struct pvr_vm_bind_req - A VM bind request, as passed to pvr_vm_bind(). + */ +struct pvr_vm_bind_req { + /** @ops: Array of userspace operation descriptions. */ + const struct drm_pvr_vm_bind_op *ops; + + /** @op_count: Number of entries in @ops. */ + u32 op_count; + + /** @sync_ops: Array of sync operations, or %NULL if there are none. */ + const struct drm_pvr_sync_op *sync_ops; + + /** @sync_op_count: Number of entries in @sync_ops. */ + u32 sync_op_count; + + /** @async: Queue the request instead of applying it inline. */ + bool async; +}; + +int pvr_vm_bind(struct pvr_vm_context *vm_ctx, struct pvr_file *pvr_file, + const struct pvr_vm_bind_req *req); + dma_addr_t pvr_vm_get_page_table_root_addr(struct pvr_vm_context *vm_ctx); struct dma_resv *pvr_vm_get_dma_resv(struct pvr_vm_context *vm_ctx); =20 diff --git a/include/uapi/drm/pvr_drm.h b/include/uapi/drm/pvr_drm.h index ccf6c211246..eb1535cd513 100644 --- a/include/uapi/drm/pvr_drm.h +++ b/include/uapi/drm/pvr_drm.h @@ -108,6 +108,7 @@ struct drm_pvr_obj_array { #define DRM_IOCTL_PVR_CREATE_HWRT_DATASET PVR_IOCTL(0x0b, DRM_IOWR, create= _hwrt_dataset) #define DRM_IOCTL_PVR_DESTROY_HWRT_DATASET PVR_IOCTL(0x0c, DRM_IOW, destro= y_hwrt_dataset) #define DRM_IOCTL_PVR_SUBMIT_JOBS PVR_IOCTL(0x0d, DRM_IOW, submit_jobs) +#define DRM_IOCTL_PVR_VM_BIND PVR_IOCTL(0x0e, DRM_IOW, vm_bind) =20 /** * DOC: PowerVR IOCTL DEV_QUERY interface @@ -1288,6 +1289,120 @@ struct drm_pvr_ioctl_submit_jobs_args { struct drm_pvr_obj_array jobs; }; =20 +/** + * DOC: PowerVR IOCTL VM_BIND interface + * + * %DRM_IOCTL_PVR_VM_BIND applies a batch of map and/or unmap operations t= o a + * single VM context, either before the IOCTL returns or, with + * %DRM_PVR_VM_BIND_ASYNC, from a queue. + * + * Operations within a request are applied in array order, and queued requ= ests + * targeting one VM context in submission order. A synchronous request doe= s not + * wait for the queued ones; a caller mixing the two on one VM context has= to + * order them itself. + * + * A request that fails part way through may leave the address space in an + * undefined state; how much of it was applied is not reported. + */ + +/** + * DOC: Flags for VM_BIND operations. + * + * The type of a VM bind operation is stored in the top four bits of + * &drm_pvr_vm_bind_op.flags. + * + * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_MAP + * + * Create a new mapping. &drm_pvr_vm_bind_op.handle must be a valid buf= fer + * object handle. + * + * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_UNMAP + * + * Remove existing mappings. &drm_pvr_vm_bind_op.handle and + * &drm_pvr_vm_bind_op.offset must both be zero. + * + * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_MASK + * + * Mask used to extract the operation type. + */ +#define DRM_PVR_VM_BIND_OP_TYPE_MAP (0u << 28) +#define DRM_PVR_VM_BIND_OP_TYPE_UNMAP (1u << 28) +#define DRM_PVR_VM_BIND_OP_TYPE_MASK (0xfu << 28) + +#define DRM_PVR_VM_BIND_OP_FLAGS_MASK DRM_PVR_VM_BIND_OP_TYPE_MASK + +/** + * struct drm_pvr_vm_bind_op - A single VM bind operation. + */ +struct drm_pvr_vm_bind_op { + /** @flags: [IN] Combination of ``DRM_PVR_VM_BIND_OP_`` flags. */ + __u32 flags; + + /** + * @handle: [IN] Handle of the target buffer object. + * + * Must be a valid handle returned by %DRM_IOCTL_PVR_CREATE_BO for map + * operations. MBZ for unmap operations. + */ + __u32 handle; + + /** + * @offset: [IN] Offset into the target buffer object from which to + * begin the mapping. MBZ for unmap operations. + */ + __u64 offset; + + /** + * @device_addr: [IN] Device-virtual address at the start of the target + * range. This must be non-zero and must obey the same alignment and + * heap containment rules as %DRM_IOCTL_PVR_VM_MAP. + */ + __u64 device_addr; + + /** @size: [IN] Size in bytes of the target range. Must be non-zero. */ + __u64 size; +}; + +/** + * DOC: Flags for the VM_BIND ioctl. + * + * .. c:macro:: DRM_PVR_VM_BIND_ASYNC + * + * Queue the request instead of applying it synchronously. Completion is + * reported through &drm_pvr_ioctl_vm_bind_args.sync_ops. + */ +#define DRM_PVR_VM_BIND_ASYNC _BITUL(0) + +#define DRM_PVR_VM_BIND_FLAGS_MASK DRM_PVR_VM_BIND_ASYNC + +/** + * struct drm_pvr_ioctl_vm_bind_args - Arguments for %DRM_IOCTL_PVR_VM_BIN= D. + */ +struct drm_pvr_ioctl_vm_bind_args { + /** + * @vm_context_handle: [IN] Handle for the VM context these operations + * apply to. + */ + __u32 vm_context_handle; + + /** @flags: [IN] Combination of ``DRM_PVR_VM_BIND_`` flags. */ + __u32 flags; + + /** @ops: [IN] Array of &struct drm_pvr_vm_bind_op to apply. */ + struct drm_pvr_obj_array ops; + + /** + * @sync_ops: [IN] Sync operations applied to the request as a whole. + * Waits are honoured before any of @ops is applied, signals fire once + * all of them have been. Must be empty unless %DRM_PVR_VM_BIND_ASYNC is + * set in @flags. + * + * A request with no operations but a non-empty @sync_ops is valid, and + * places a bare synchronisation point on the VM bind queue. + */ + struct drm_pvr_obj_array sync_ops; +}; + #if defined(__cplusplus) } #endif --=20 2.43.0 From nobody Mon Aug 24 04:17:54 2026 Received: from mail-pj1-f43.google.com (mail-pj1-f43.google.com [209.85.216.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A66A73D090E for ; Sun, 16 Aug 2026 19:43:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909414; cv=none; b=f4dneEoo5rusD1lrBc+1fZ/FGe5ON83oWuhLQeU0CbtYYrz7sLtDQPmzMHX2MDSlVKNkqCyCPYjvSg/LHUEsnWVawadlvrl2X6vUWP+l2KiyXHcod37qqsFREKpFdmFu2dNAn2LRRcjqlviTUhIZUdM1ZoSZBataS3V7XF2ujK8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786909414; c=relaxed/simple; bh=jclIzQuM3bzXgFYxw+/3FbMjl+ULDlFfzy81qHBP2ow=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fFFC2xHNVghVd2a8obRB+a2Fg8sUMhZKO9ykT/zU4bvekiF3Sm8mW+AOFDxBjyq7ewM48vweT8DgpqStM+9EpWWA/XgadUkug4pETO+d0tEQUmjY5gg24tO4aeFuecKddqm6loRm9bK2xRXDE4/c3gDLvEsV8z4jDmnmLt55Is0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sgC9Urf0; arc=none smtp.client-ip=209.85.216.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sgC9Urf0" Received: by mail-pj1-f43.google.com with SMTP id 98e67ed59e1d1-38101f85591so3369573a91.1 for ; Sun, 16 Aug 2026 12:43:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786909412; x=1787514212; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TmS/O1rRWa79hgNz7Dg/kJ8vBv2DQsi5CP/JVSYodyI=; b=sgC9Urf02XKbLoho/Q8QCD4j83vGA8FWRsJDg2hf06ejPOxVm3ObEKsUg9OoJkQKPE EQEYiChmPSVo2U+5XOB2I9b0Uq4H2FdcUdS8pVQaJWmUl/D+xLh5PprtjVItSozAwc0B A4PRFMc3WgebL/4v+5HVyVFfS02Drl/cGKGoD8WLtEFq+rIKtGORVXWWmWHNZX81PMT4 Om05pPLmxSJ/8ixuuwWIdQ43ckuapbm/9OEOa0sICsU0nMAtulZaIxxZp8ZjmbJTluhR 3xZ8lkApPKYKMdwilVEWB7H4GCqPnvdPSrb/ZH3tnGL5VQPRElBohmlgLZ/U1Q6g6G+Q HQ4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786909412; x=1787514212; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=TmS/O1rRWa79hgNz7Dg/kJ8vBv2DQsi5CP/JVSYodyI=; b=OgmuLd1fX5D0uvz8ErP0pr7vSaB9aIrlZpPQhoSdWZaMqmbWJ1vNI8THm3VksfKrMX aRrFEk2uU09C+5FOc5PP5HZvPhtwLtGQzE2/lbh3SEhB5qxPnOAJ/Vey3lAhm/fJrwf4 nUSB4aKWDZKMnufUNjRs4r8cTKpoA4fEWlygp15JEyXCpMAYFSHQD6v1DOnIPFFurmzF 2wEUe22tNRoHp2dxdDECoRAsrrd5jazM2XYKzHwWT1qsVZnoBjFfafbvqMhrwZANri4i v1zBJX9dNinEY+B0Nw9KduuC6xwVgGE6ZTQwSgjiRvZM4sFPIu7TcIfT6QGCTRCIgi88 basQ== X-Forwarded-Encrypted: i=1; AHgh+RoAiYTy+ZFyxIbRP/nYREQbJ+fzQh94ZKFvF9CdAoTV3U+ERqQcBHWGYMv2gjvX5OamCI7PSc3Fg/j2OI0=@vger.kernel.org X-Gm-Message-State: AOJu0YxX1P0y/yDWflOgRBXsrAElZgb+bGFtMY9n9imq9Wqpl4/+Zwld bmte4EwtAQ9agAIUSO8Nl249ylLOKyNFuIwSqy5IwZ1i5RBB7WeXO0A1 X-Gm-Gg: AR+sD13wd67Yf6A8OZ1h3AiWqp5zfXeG+tsBUIK4Q/yJ2mO8aikFy4QB9c4pJY/gdQH 85eqHq1Mfgwp62cat85TjrKQ8Jek9gu/vsgdOsd/ZsVLL8Cg3JCGgYCgMLvNbwEpDVs/2+50IR+ rGXDVCvItDIxpVIJp/ccDNGI0mKJU5a/jYSdAwD3dTmkn7w5CQ+kUZvFBwAWIFBoi/V6PR7Ot0v 0UwcwF1RTU84tlSntOIy2n1ZhWDT0gyDNw1JUnNzBoP83YRZ1aWRvfMssc74T4jWYRzEYFDwFmt xWwH+SSw2abia1CBZkbnLLpnTyQrclmuZuSHwopj5UwTY1gcT560GKTZSPx2YhACD+DzEyZ+myV 1eT3sa7JX8Idt+x/VlaJ+SGhJZAix3eXX3s9E0P5Yfd82LATb2a2DhTpdDh4+5nDa7jxTrHQQ/a 9iesulw2wqc1CA/hIHYuXjHsoAaQbKDssvlfInfZiv31vUpsP0xVBuLQ== X-Received: by 2002:a17:90b:1dcb:b0:38e:5ce1:ae30 with SMTP id 98e67ed59e1d1-3933b0d37ebmr14980176a91.14.1786909411881; Sun, 16 Aug 2026 12:43:31 -0700 (PDT) Received: from [127.0.1.1] ([1.243.227.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-395320229f4sm2141310a91.15.2026.08.16.12.43.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Aug 2026 12:43:31 -0700 (PDT) From: Gyeyoung Baek Date: Mon, 17 Aug 2026 04:42:11 +0900 Subject: [PATCH 4/4] drm/imagination: Add sparse mappings to VM_BIND Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-pvr-vm-bind-v1-4-0a0f21be7d38@gmail.com> References: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> In-Reply-To: <20260817-pvr-vm-bind-v1-0-0a0f21be7d38@gmail.com> To: Alessio Belle , Luigi Santivetti , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Matt Coster , Donald Robson , Sarah Walker , Sumit Semwal , =?utf-8?q?Christian_K=C3=B6nig?= Cc: imagination@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-media@vger.kernel.org, linaro-mm-sig@lists.linaro.org, Gyeyoung Baek X-Mailer: b4 0.15.1 Vulkan sparse residency requires that an access to an unbound part of a sparse resource does not fault, so a range has to be mappable without naming any buffer object. Add DRM_PVR_VM_BIND_OP_MAP_SPARSE: every page table entry of the range points at the same dummy page, so the mapping costs one physical page however wide it is. The dummy is created per file rather than per device - writes to a sparse range stay in this page, and one shared device-wide would let clients see each other's writes. Signed-off-by: Gyeyoung Baek --- drivers/gpu/drm/imagination/pvr_device.h | 9 ++++ drivers/gpu/drm/imagination/pvr_drv.c | 14 ++++++- drivers/gpu/drm/imagination/pvr_drv.h | 1 + drivers/gpu/drm/imagination/pvr_mmu.c | 71 ++++++++++++++++++++++++++++= ++++ drivers/gpu/drm/imagination/pvr_mmu.h | 2 + drivers/gpu/drm/imagination/pvr_vm.c | 52 ++++++++++++++++++----- include/uapi/drm/pvr_drm.h | 32 +++++++++++--- 7 files changed, 164 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/ima= gination/pvr_device.h index d51c57cf933..133b5c789bf 100644 --- a/drivers/gpu/drm/imagination/pvr_device.h +++ b/drivers/gpu/drm/imagination/pvr_device.h @@ -380,6 +380,15 @@ struct pvr_file { */ struct xarray hwrt_handles; =20 + /** + * @sparse_dummy_bo: Single page every sparse mapping made through this + * file points at. + * + * Writes to a sparse range land here, so it is kept per file rather + * than per device to keep them out of other clients' view. + */ + struct pvr_gem_object *sparse_dummy_bo; + /** * @vm_ctx_handles: Array of VM contexts belonging to this file. Array * members are of type "struct pvr_vm_context *". diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagin= ation/pvr_drv.c index ec6ed610e29..ae11fbf7528 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1402,11 +1402,21 @@ static int pvr_drm_driver_open(struct drm_device *drm_dev, struct drm_file *file) { struct pvr_device *pvr_dev =3D to_pvr_device(drm_dev); + struct pvr_gem_object *dummy_bo; struct pvr_file *pvr_file; =20 + dummy_bo =3D pvr_gem_object_create(pvr_dev, PVR_DEVICE_PAGE_SIZE, + DRM_PVR_BO_BYPASS_DEVICE_CACHE); + if (IS_ERR(dummy_bo)) + return PTR_ERR(dummy_bo); + pvr_file =3D kzalloc_obj(*pvr_file); - if (!pvr_file) + if (!pvr_file) { + pvr_gem_object_put(dummy_bo); return -ENOMEM; + } + + pvr_file->sparse_dummy_bo =3D dummy_bo; =20 /* * Store reference to base DRM file private data for use by @@ -1460,6 +1470,8 @@ pvr_drm_driver_postclose(__always_unused struct drm_d= evice *drm_dev, pvr_destroy_hwrt_datasets_for_file(pvr_file); pvr_destroy_vm_contexts_for_file(pvr_file); =20 + pvr_gem_object_put(pvr_file->sparse_dummy_bo); + kfree(pvr_file); file->driver_priv =3D NULL; } diff --git a/drivers/gpu/drm/imagination/pvr_drv.h b/drivers/gpu/drm/imagin= ation/pvr_drv.h index 9ca8f8780a3..109b99ea062 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.h +++ b/drivers/gpu/drm/imagination/pvr_drv.h @@ -14,6 +14,7 @@ * Driver interface version: * - 1.0: Initial interface * - 1.1: adds DRM_IOCTL_PVR_VM_BIND + * adds DRM_PVR_VM_BIND_OP_MAP_SPARSE flag */ #define PVR_DRIVER_MAJOR 1 #define PVR_DRIVER_MINOR 1 diff --git a/drivers/gpu/drm/imagination/pvr_mmu.c b/drivers/gpu/drm/imagin= ation/pvr_mmu.c index 3cac482e103..fc1347d6f23 100644 --- a/drivers/gpu/drm/imagination/pvr_mmu.c +++ b/drivers/gpu/drm/imagination/pvr_mmu.c @@ -2558,6 +2558,77 @@ pvr_mmu_map_sgl(struct pvr_mmu_op_context *op_ctx, s= truct scatterlist *sgl, return err; } =20 +/** + * pvr_mmu_map_dummy() - Point a range of device-virtual memory at a single + * repeated physical page. + * @op_ctx: Target MMU op context. + * @size: Size of memory to be mapped in bytes. Must be a non-zero multiple + * of the device page size. + * @flags: Flags from pvr_gem_object associated with the mapping. + * @device_addr: Virtual device address to map to. Must be device page-ali= gned. + * + * Every entry of the range is pointed at the first page of + * &pvr_mmu_op_context.map.sgt, so the mapping costs one page whatever @si= ze is. + * + * Return: + * * 0 on success, + * * -%EINVAL if @size or @device_addr is misaligned, or + * * Any error encountered while creating a page with pvr_page_create(), = or + * * Any error encountered while advancing @op_ctx.curr_page. + */ +int pvr_mmu_map_dummy(struct pvr_mmu_op_context *op_ctx, u64 size, u64 fla= gs, + u64 device_addr) +{ + const u64 pages =3D size >> PVR_DEVICE_PAGE_SHIFT; + struct pvr_page_table_ptr ptr_copy; + struct pvr_page_flags_raw flags_raw; + dma_addr_t dma_addr; + u64 page; + int err; + + if (!size) + return 0; + + if (size & ~PVR_DEVICE_PAGE_MASK) + return -EINVAL; + + dma_addr =3D sg_dma_address(op_ctx->map.sgt->sgl); + + err =3D pvr_mmu_op_context_set_curr_page(op_ctx, device_addr, true); + if (err) + return -EINVAL; + + memcpy(&ptr_copy, &op_ctx->curr_page, sizeof(ptr_copy)); + + flags_raw =3D pvr_page_flags_raw_create(false, false, + flags & DRM_PVR_BO_BYPASS_DEVICE_CACHE, + flags & DRM_PVR_BO_PM_FW_PROTECT); + + err =3D pvr_page_create(op_ctx, dma_addr, flags_raw); + if (err) + return err; + + for (page =3D 1; page < pages; ++page) { + err =3D pvr_mmu_op_context_next_page(op_ctx, true); + if (err) + goto err_destroy_pages; + + err =3D pvr_page_create(op_ctx, dma_addr, flags_raw); + if (err) + goto err_destroy_pages; + } + + pvr_mmu_op_context_require_sync(op_ctx, PVR_MMU_SYNC_LEVEL_0); + + return 0; + +err_destroy_pages: + memcpy(&op_ctx->curr_page, &ptr_copy, sizeof(op_ctx->curr_page)); + pvr_mmu_op_context_unmap_curr_page(op_ctx, page); + + return err; +} + /** * pvr_mmu_map() - Map an object's virtual memory to physical memory. * @op_ctx: Target MMU op context. diff --git a/drivers/gpu/drm/imagination/pvr_mmu.h b/drivers/gpu/drm/imagin= ation/pvr_mmu.h index a8ecd460168..b014ea8efb0 100644 --- a/drivers/gpu/drm/imagination/pvr_mmu.h +++ b/drivers/gpu/drm/imagination/pvr_mmu.h @@ -101,6 +101,8 @@ struct pvr_mmu_op_context * pvr_mmu_op_context_create(struct pvr_mmu_context *ctx, struct sg_table *sgt, u64 sgt_offset, u64 size); =20 +int pvr_mmu_map_dummy(struct pvr_mmu_op_context *op_ctx, u64 size, u64 fla= gs, + u64 device_addr); int pvr_mmu_map(struct pvr_mmu_op_context *op_ctx, u64 size, u64 flags, u64 device_addr); int pvr_mmu_unmap(struct pvr_mmu_op_context *op_ctx, u64 device_addr, u64 = size); diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagina= tion/pvr_vm.c index f92bfeacd7a..d11e192c772 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -200,6 +200,12 @@ struct pvr_vm_bind_op { */ struct pvr_vm_gpuva *next_va; =20 + /** + * @sparse: The mapping repeats the file's dummy page rather than + * covering real pages of @pvr_obj. + */ + bool sparse; + /** @offset: Offset into @pvr_obj to begin mapping from. */ u64 offset; =20 @@ -267,7 +273,7 @@ static int pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_op, struct pvr_vm_context *vm_ctx, struct pvr_gem_object *pvr_obj, u64 offset, - u64 device_addr, u64 size) + u64 device_addr, u64 size, bool sparse) { struct drm_gem_object *obj =3D gem_from_pvr_gem(pvr_obj); const bool is_user =3D vm_ctx !=3D vm_ctx->pvr_dev->kernel_vm_ctx; @@ -285,11 +291,19 @@ pvr_vm_bind_op_map_init(struct pvr_vm_bind_op *bind_o= p, } =20 if (!pvr_device_addr_and_size_are_valid(vm_ctx, device_addr, size) || - offset & ~PAGE_MASK || size & ~PAGE_MASK || - offset >=3D pvr_obj_size || offset_plus_size > pvr_obj_size) + offset & ~PAGE_MASK || size & ~PAGE_MASK) + return -EINVAL; + + /* + * A sparse mapping repeats one page over a deliberately wider range, + * so the containment check only applies to ordinary mappings. + */ + if (!sparse && + (offset >=3D pvr_obj_size || offset_plus_size > pvr_obj_size)) return -EINVAL; =20 bind_op->type =3D PVR_VM_BIND_TYPE_MAP; + bind_op->sparse =3D sparse; =20 bind_op->gpuvm_bo =3D drm_gpuvm_bo_create(&vm_ctx->gpuvm_mgr, obj); if (!bind_op->gpuvm_bo) @@ -396,8 +410,12 @@ pvr_vm_gpuva_map(struct drm_gpuva_op *op, void *op_ctx) if ((op->map.gem.offset | op->map.va.range) & ~PVR_DEVICE_PAGE_MASK) return -EINVAL; =20 - err =3D pvr_mmu_map(ctx->mmu_op_ctx, op->map.va.range, pvr_gem->flags, - op->map.va.addr); + if (ctx->sparse) + err =3D pvr_mmu_map_dummy(ctx->mmu_op_ctx, op->map.va.range, + pvr_gem->flags, op->map.va.addr); + else + err =3D pvr_mmu_map(ctx->mmu_op_ctx, op->map.va.range, + pvr_gem->flags, op->map.va.addr); if (err) return err; =20 @@ -790,7 +808,7 @@ pvr_vm_map(struct pvr_vm_context *vm_ctx, struct pvr_ge= m_object *pvr_obj, =20 int err =3D pvr_vm_bind_op_map_init(&bind_op, vm_ctx, pvr_obj, pvr_obj_offset, device_addr, - size); + size, false); =20 if (err) return err; @@ -1475,14 +1493,25 @@ pvr_vm_bind_op_init_from_uapi(struct pvr_vm_bind_op= *bind_op, =20 switch (uapi_op->flags & DRM_PVR_VM_BIND_OP_TYPE_MASK) { case DRM_PVR_VM_BIND_OP_TYPE_MAP: - pvr_obj =3D pvr_gem_object_from_handle(pvr_file, uapi_op->handle); - if (!pvr_obj) - return -ENOENT; + if (uapi_op->flags & DRM_PVR_VM_BIND_OP_MAP_SPARSE) { + if (uapi_op->handle || uapi_op->offset) + return -EINVAL; + + pvr_obj =3D pvr_file->sparse_dummy_bo; + pvr_gem_object_get(pvr_obj); + } else { + pvr_obj =3D pvr_gem_object_from_handle(pvr_file, + uapi_op->handle); + if (!pvr_obj) + return -ENOENT; + } =20 err =3D pvr_vm_bind_op_map_init(bind_op, vm_ctx, pvr_obj, uapi_op->offset, uapi_op->device_addr, - uapi_op->size); + uapi_op->size, + uapi_op->flags & + DRM_PVR_VM_BIND_OP_MAP_SPARSE); if (err) { pvr_gem_object_put(pvr_obj); return err; @@ -1494,6 +1523,9 @@ pvr_vm_bind_op_init_from_uapi(struct pvr_vm_bind_op *= bind_op, if (uapi_op->handle || uapi_op->offset) return -EINVAL; =20 + if (uapi_op->flags & DRM_PVR_VM_BIND_OP_MAP_SPARSE) + return -EINVAL; + return pvr_vm_bind_op_unmap_init(bind_op, vm_ctx, NULL, uapi_op->device_addr, uapi_op->size); diff --git a/include/uapi/drm/pvr_drm.h b/include/uapi/drm/pvr_drm.h index eb1535cd513..5cb63eb8182 100644 --- a/include/uapi/drm/pvr_drm.h +++ b/include/uapi/drm/pvr_drm.h @@ -1309,27 +1309,45 @@ struct drm_pvr_ioctl_submit_jobs_args { * DOC: Flags for VM_BIND operations. * * The type of a VM bind operation is stored in the top four bits of - * &drm_pvr_vm_bind_op.flags. + * &drm_pvr_vm_bind_op.flags. The remaining bits carry modifiers, each of = which + * is only valid with one type. + * + * .. c:macro:: DRM_PVR_VM_BIND_OP_MAP_SPARSE + * + * Sparsely map a range of virtual addresses, without naming a buffer + * object. Only valid with %DRM_PVR_VM_BIND_OP_TYPE_MAP; + * &drm_pvr_vm_bind_op.handle and &drm_pvr_vm_bind_op.offset must both = be + * zero. + * + * GPU accesses to the range do not fault. Reads return undefined value= s, + * and writes may become visible through other sparse mappings. Nothing + * more is guaranteed; in particular + * %VkPhysicalDeviceSparseProperties.residencyNonResidentStrict is not + * provided. * * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_MAP * * Create a new mapping. &drm_pvr_vm_bind_op.handle must be a valid buf= fer - * object handle. + * object handle, unless %DRM_PVR_VM_BIND_OP_MAP_SPARSE is also set. * * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_UNMAP * * Remove existing mappings. &drm_pvr_vm_bind_op.handle and - * &drm_pvr_vm_bind_op.offset must both be zero. + * &drm_pvr_vm_bind_op.offset must both be zero, as must + * %DRM_PVR_VM_BIND_OP_MAP_SPARSE. * * .. c:macro:: DRM_PVR_VM_BIND_OP_TYPE_MASK * * Mask used to extract the operation type. */ +#define DRM_PVR_VM_BIND_OP_MAP_SPARSE _BITUL(0) + #define DRM_PVR_VM_BIND_OP_TYPE_MAP (0u << 28) #define DRM_PVR_VM_BIND_OP_TYPE_UNMAP (1u << 28) #define DRM_PVR_VM_BIND_OP_TYPE_MASK (0xfu << 28) =20 -#define DRM_PVR_VM_BIND_OP_FLAGS_MASK DRM_PVR_VM_BIND_OP_TYPE_MASK +#define DRM_PVR_VM_BIND_OP_FLAGS_MASK \ + (DRM_PVR_VM_BIND_OP_MAP_SPARSE | DRM_PVR_VM_BIND_OP_TYPE_MASK) =20 /** * struct drm_pvr_vm_bind_op - A single VM bind operation. @@ -1342,13 +1360,15 @@ struct drm_pvr_vm_bind_op { * @handle: [IN] Handle of the target buffer object. * * Must be a valid handle returned by %DRM_IOCTL_PVR_CREATE_BO for map - * operations. MBZ for unmap operations. + * operations. MBZ for unmap operations, and for sparse map operations, + * which have no buffer object to name. */ __u32 handle; =20 /** * @offset: [IN] Offset into the target buffer object from which to - * begin the mapping. MBZ for unmap operations. + * begin the mapping. MBZ for unmap operations and for sparse map + * operations. */ __u64 offset; =20 --=20 2.43.0