mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
gpu: host1x: Add dma_fence_chain support
Enhance IOCTL handler to identify and handle dma_fence_chain objects that might contain host1x dma fences. This fixes issues when userspace passes a dma_fence_chain (created by DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT operations) to HOST1X_IOCTL_FENCE_EXTRACT. The updated code iteratively unwraps fence chains until it finds a host1x_syncpt_fence or reaches a fence it can't process. This ensures proper operation with DRM-based applications that use timeline syncobj features which internally use dma_fence_chain. Bug 4983872 Change-Id: I3eef9d54e2c42180cb5c74236cd64f42a863b7ea Signed-off-by: Mainak Sen <msen@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3364940 GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: svcacv <svcacv@nvidia.com> Reviewed-by: Leslin Varghese <lvarghese@nvidia.com> Tested-by: Arunmozhikannan Soundarapandian <asoundarapan@nvidia.com> Reviewed-by: Sourab Gupta <sourabg@nvidia.com>
This commit is contained in:
@@ -15,6 +15,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sync_file.h>
|
#include <linux/sync_file.h>
|
||||||
#include <linux/host1x-dispatch.h>
|
#include <linux/host1x-dispatch.h>
|
||||||
|
#include <linux/dma-fence.h>
|
||||||
|
#include <linux/dma-fence-chain.h>
|
||||||
|
|
||||||
#include "include/uapi/linux/host1x-fence.h"
|
#include "include/uapi/linux/host1x-fence.h"
|
||||||
|
|
||||||
@@ -141,14 +143,169 @@ static int dev_file_ioctl_create_fence(struct host1x **host1xp, void __user *dat
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fence_extract - Extract fence information from a single dma_fence
|
||||||
|
* @fence: The input dma_fence to extract from
|
||||||
|
* @max_fences: Maximum number of fences to copy to userspace (0 or 1)
|
||||||
|
* @fences_user_ptr: Userspace pointer to copy extracted fence information to
|
||||||
|
* @num_extracted: Output parameter to store the number of valid fences extracted
|
||||||
|
*
|
||||||
|
* This function extracts syncpoint information from a single dma_fence
|
||||||
|
* and copies it to userspace if max_fences > 0. For single fences,
|
||||||
|
* num_extracted will be either 0 (for signaled stub fences) or 1.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int fence_extract(struct dma_fence *fence, uint32_t max_fences,
|
||||||
|
struct host1x_fence_extract_fence __user *fences_user_ptr,
|
||||||
|
uint32_t *num_extracted)
|
||||||
|
{
|
||||||
|
struct host1x_fence_extract_fence f;
|
||||||
|
int instance, err;
|
||||||
|
unsigned long copy_err;
|
||||||
|
|
||||||
|
err = host1x_fence_extract(fence, &f.id, &f.threshold);
|
||||||
|
if (err == -EINVAL && dma_fence_is_signaled(fence)) {
|
||||||
|
/* Likely stub fence */
|
||||||
|
*num_extracted = 0;
|
||||||
|
return 0;
|
||||||
|
} else if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert to global id before giving to userspace */
|
||||||
|
instance = host1x_fence_get_node(fence);
|
||||||
|
if (instance < HOST1X_INSTANCE_MAX && instance >= 0)
|
||||||
|
f.id = HOST1X_LOCAL_TO_GLOBAL_SYNCPOINT(f.id, instance);
|
||||||
|
|
||||||
|
if (max_fences > 0) {
|
||||||
|
copy_err = copy_to_user(fences_user_ptr, &f, sizeof(f));
|
||||||
|
if (copy_err)
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_extracted = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fence_array_extract - Extract fence information from a dma_fence_array
|
||||||
|
* @array: The input dma_fence_array to extract from
|
||||||
|
* @max_fences: Maximum number of fences to copy to userspace
|
||||||
|
* @fences_user_ptr: Userspace pointer to copy extracted fence information to
|
||||||
|
* @num_extracted: Output parameter to store the total number of valid fences extracted
|
||||||
|
*
|
||||||
|
* This function traverses a dma_fence_array, extracts syncpoint information from
|
||||||
|
* each fence in the array, and copies it to userspace. The function will
|
||||||
|
* count all valid fences in the array but only copy up to @max_fences to userspace.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int fence_array_extract(struct dma_fence_array *array, uint32_t max_fences,
|
||||||
|
struct host1x_fence_extract_fence __user *fences_user_ptr,
|
||||||
|
uint32_t *num_extracted)
|
||||||
|
{
|
||||||
|
unsigned int i, j = 0;
|
||||||
|
int err;
|
||||||
|
struct host1x_fence_extract_fence f;
|
||||||
|
int instance;
|
||||||
|
unsigned long copy_err;
|
||||||
|
|
||||||
|
for (i = 0; i < array->num_fences; i++) {
|
||||||
|
err = host1x_fence_extract(array->fences[i], &f.id, &f.threshold);
|
||||||
|
if (err == -EINVAL && dma_fence_is_signaled(array->fences[i])) {
|
||||||
|
/* Likely stub fence */
|
||||||
|
continue;
|
||||||
|
} else if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j < max_fences) {
|
||||||
|
/* Convert to global id before giving to userspace */
|
||||||
|
instance = host1x_fence_get_node(array->fences[i]);
|
||||||
|
if (instance < HOST1X_INSTANCE_MAX && instance >= 0)
|
||||||
|
f.id = HOST1X_LOCAL_TO_GLOBAL_SYNCPOINT(f.id, instance);
|
||||||
|
copy_err = copy_to_user(fences_user_ptr + j, &f, sizeof(f));
|
||||||
|
if (copy_err)
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_extracted = j;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fence_chain_extract - Extract fence information from a dma_fence chain
|
||||||
|
* @fence: The input dma_fence chain to extract from
|
||||||
|
* @max_fences: Maximum number of fences to copy to userspace
|
||||||
|
* @fences_user_ptr: Userspace pointer to copy extracted fence information to
|
||||||
|
* @num_extracted: Output parameter to store the total number of valid fences extracted
|
||||||
|
*
|
||||||
|
* This function traverses a dma_fence chain, extracts syncpoint information from
|
||||||
|
* each child fence, and copies it to userspace. It optimizes memory usage by
|
||||||
|
* processing fences directly without intermediate allocation. The function will
|
||||||
|
* count all valid fences in the chain but only copy up to @max_fences to userspace.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int fence_chain_extract(struct dma_fence *fence, uint32_t max_fences,
|
||||||
|
struct host1x_fence_extract_fence __user *fences_user_ptr,
|
||||||
|
uint32_t *num_extracted)
|
||||||
|
{
|
||||||
|
struct dma_fence *iter, *child;
|
||||||
|
unsigned int count = 0;
|
||||||
|
int err = 0;
|
||||||
|
struct host1x_fence_extract_fence f;
|
||||||
|
int instance;
|
||||||
|
unsigned long copy_err;
|
||||||
|
|
||||||
|
/* Traverse chain and extract fences directly */
|
||||||
|
dma_fence_chain_for_each(iter, fence) {
|
||||||
|
child = dma_fence_chain_contained(iter);
|
||||||
|
if (!child) {
|
||||||
|
dma_fence_put(iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = host1x_fence_extract(child, &f.id, &f.threshold);
|
||||||
|
if (err == -EINVAL && dma_fence_is_signaled(child)) {
|
||||||
|
/* Likely stub fence */
|
||||||
|
dma_fence_put(iter);
|
||||||
|
continue;
|
||||||
|
} else if (err) {
|
||||||
|
dma_fence_put(iter);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < max_fences) {
|
||||||
|
/* Convert to global id before giving to userspace */
|
||||||
|
instance = host1x_fence_get_node(child);
|
||||||
|
if (instance < HOST1X_INSTANCE_MAX && instance >= 0)
|
||||||
|
f.id = HOST1X_LOCAL_TO_GLOBAL_SYNCPOINT(f.id, instance);
|
||||||
|
copy_err = copy_to_user(fences_user_ptr + count, &f, sizeof(f));
|
||||||
|
if (copy_err) {
|
||||||
|
dma_fence_put(iter);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
dma_fence_put(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_extracted = count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int dev_file_ioctl_fence_extract(struct host1x **host1xp, void __user *data)
|
static int dev_file_ioctl_fence_extract(struct host1x **host1xp, void __user *data)
|
||||||
{
|
{
|
||||||
struct host1x_fence_extract_fence __user *fences_user_ptr;
|
struct host1x_fence_extract_fence __user *fences_user_ptr;
|
||||||
struct dma_fence *fence, **fences;
|
struct dma_fence *fence;
|
||||||
struct host1x_fence_extract args;
|
struct host1x_fence_extract args;
|
||||||
struct dma_fence_array *array;
|
struct dma_fence_array *array;
|
||||||
unsigned int num_fences, i, j;
|
|
||||||
unsigned long copy_err;
|
unsigned long copy_err;
|
||||||
|
uint32_t num_extracted = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
copy_err = copy_from_user(&args, data, sizeof(args));
|
copy_err = copy_from_user(&args, data, sizeof(args));
|
||||||
@@ -164,58 +321,27 @@ static int dev_file_ioctl_fence_extract(struct host1x **host1xp, void __user *da
|
|||||||
if (!fence)
|
if (!fence)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
array = to_dma_fence_array(fence);
|
if (dma_fence_is_array(fence)) {
|
||||||
if (array) {
|
array = to_dma_fence_array(fence);
|
||||||
fences = array->fences;
|
err = fence_array_extract(array, args.num_fences, fences_user_ptr, &num_extracted);
|
||||||
num_fences = array->num_fences;
|
} else if (dma_fence_is_chain(fence)) {
|
||||||
|
err = fence_chain_extract(fence, args.num_fences, fences_user_ptr, &num_extracted);
|
||||||
} else {
|
} else {
|
||||||
fences = &fence;
|
err = fence_extract(fence, args.num_fences, fences_user_ptr, &num_extracted);
|
||||||
num_fences = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0, j = 0; i < num_fences; i++) {
|
if (err == 0) {
|
||||||
struct host1x_fence_extract_fence f;
|
args.num_fences = num_extracted;
|
||||||
int instance;
|
copy_err = copy_to_user(data, &args, sizeof(args));
|
||||||
|
if (copy_err)
|
||||||
err = host1x_fence_extract(fences[i], &f.id, &f.threshold);
|
err = -EFAULT;
|
||||||
if (err == -EINVAL && dma_fence_is_signaled(fences[i])) {
|
|
||||||
/* Likely stub fence */
|
|
||||||
continue;
|
|
||||||
} else if (err) {
|
|
||||||
goto put_fence;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j < args.num_fences) {
|
|
||||||
/* Convert to global id before giving to userspace */
|
|
||||||
instance = host1x_fence_get_node(fences[i]);
|
|
||||||
if (instance < HOST1X_INSTANCE_MAX && instance >= 0)
|
|
||||||
f.id = HOST1X_LOCAL_TO_GLOBAL_SYNCPOINT(f.id, instance);
|
|
||||||
|
|
||||||
copy_err = copy_to_user(fences_user_ptr + j, &f, sizeof(f));
|
|
||||||
if (copy_err) {
|
|
||||||
err = -EFAULT;
|
|
||||||
goto put_fence;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
j++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args.num_fences = j;
|
/* If the fence is a chain, we have not taken the reference to the chain */
|
||||||
|
if (dma_fence_is_chain(fence))
|
||||||
copy_err = copy_to_user(data, &args, sizeof(args));
|
return err;
|
||||||
if (copy_err) {
|
|
||||||
err = -EFAULT;
|
|
||||||
goto put_fence;
|
|
||||||
}
|
|
||||||
|
|
||||||
dma_fence_put(fence);
|
dma_fence_put(fence);
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
put_fence:
|
|
||||||
dma_fence_put(fence);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user