mirror of
https://github.com/revyos/thead-kernel.git
synced 2026-09-03 16:42:01 +02:00
This version update involves a lot of content, so the previous version has been deleted and the new version has been re-merged into the kernel. The configuration file for the GPU driver originates from a previous version. Signed-off-by: Mingzheng Xing <xingmingzheng@iscas.ac.cn>
1095 lines
31 KiB
C
1095 lines
31 KiB
C
/*
|
|
* @File pvr_sync_file.c
|
|
* @Title Kernel driver for Android's sync mechanism
|
|
* @Codingstyle LinuxKernel
|
|
* @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
|
|
* @License Dual MIT/GPLv2
|
|
*
|
|
* The contents of this file are subject to the MIT license as set out below.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* the GNU General Public License Version 2 ("GPL") in which case the provisions
|
|
* of GPL are applicable instead of those above.
|
|
*
|
|
* If you wish to allow use of your version of this file only under the terms of
|
|
* GPL, and not to allow others to use your version of this file under the terms
|
|
* of the MIT license, indicate your decision by deleting the provisions above
|
|
* and replace them with the notice and other provisions required by GPL as set
|
|
* out in the file called "GPL-COPYING" included in this distribution. If you do
|
|
* not delete the provisions above, a recipient may use your version of this file
|
|
* under the terms of either the MIT license or GPL.
|
|
*
|
|
* This License is also included in this distribution in the file called
|
|
* "MIT-COPYING".
|
|
*
|
|
* EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS
|
|
* PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
|
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "services_kernel_client.h"
|
|
#include "pvr_drv.h"
|
|
#include "pvr_sync.h"
|
|
#include "pvr_fence.h"
|
|
#include "pvr_counting_timeline.h"
|
|
|
|
#include "linux_sw_sync.h"
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sync_file.h>
|
|
#include <linux/file.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/uaccess.h>
|
|
|
|
#include "pvr_sync_api.h"
|
|
|
|
/* This header must always be included last */
|
|
#include "kernel_compatibility.h"
|
|
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)) && !defined(CHROMIUMOS_KERNEL)
|
|
#define sync_file_user_name(s) ((s)->name)
|
|
#else
|
|
#define sync_file_user_name(s) ((s)->user_name)
|
|
#endif
|
|
|
|
#define PVR_DUMPDEBUG_LOG(pfnDumpDebugPrintf, pvDumpDebugFile, fmt, ...) \
|
|
do { \
|
|
if (pfnDumpDebugPrintf) \
|
|
pfnDumpDebugPrintf(pvDumpDebugFile, fmt, \
|
|
## __VA_ARGS__); \
|
|
else \
|
|
pr_err(fmt "\n", ## __VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define FILE_NAME "pvr_sync_file"
|
|
|
|
struct sw_sync_create_fence_data {
|
|
__u32 value;
|
|
char name[32];
|
|
__s32 fence;
|
|
};
|
|
#define SW_SYNC_IOC_MAGIC 'W'
|
|
#define SW_SYNC_IOC_CREATE_FENCE \
|
|
(_IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data))
|
|
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
|
|
|
|
/* Global data for the sync driver */
|
|
static struct {
|
|
struct pvr_fence_context *foreign_fence_context;
|
|
PFN_SYNC_CHECKPOINT_STRUCT sync_checkpoint_ops;
|
|
} pvr_sync_data;
|
|
|
|
#if defined(NO_HARDWARE)
|
|
static DEFINE_MUTEX(pvr_timeline_active_list_lock);
|
|
static struct list_head pvr_timeline_active_list;
|
|
#endif
|
|
|
|
/* This is the actual timeline metadata. We might keep this around after the
|
|
* base sync driver has destroyed the pvr_sync_timeline_wrapper object.
|
|
*/
|
|
struct pvr_sync_timeline {
|
|
char name[32];
|
|
void *file_handle;
|
|
bool is_sw;
|
|
/* Fence context used for hw fences */
|
|
struct pvr_fence_context *hw_fence_context;
|
|
/* Timeline and context for sw fences */
|
|
struct pvr_counting_fence_timeline *sw_fence_timeline;
|
|
#if defined(NO_HARDWARE)
|
|
/* List of all timelines (used to advance all timelines in nohw builds) */
|
|
struct list_head list;
|
|
#endif
|
|
};
|
|
|
|
static
|
|
void pvr_sync_free_checkpoint_list_mem(void *mem_ptr)
|
|
{
|
|
kfree(mem_ptr);
|
|
}
|
|
|
|
#if defined(NO_HARDWARE)
|
|
/* function used to signal pvr fence in nohw builds */
|
|
static
|
|
void pvr_sync_nohw_signal_fence(void *fence_data_to_signal)
|
|
{
|
|
struct pvr_sync_timeline *this_timeline;
|
|
|
|
mutex_lock(&pvr_timeline_active_list_lock);
|
|
list_for_each_entry(this_timeline, &pvr_timeline_active_list, list) {
|
|
pvr_fence_context_signal_fences_nohw(this_timeline->hw_fence_context);
|
|
}
|
|
mutex_unlock(&pvr_timeline_active_list_lock);
|
|
}
|
|
#endif
|
|
|
|
static struct pvr_sync_timeline *pvr_sync_timeline_fget(int fd)
|
|
{
|
|
struct file *file = fget(fd);
|
|
struct pvr_sync_timeline *timeline;
|
|
|
|
if (!file)
|
|
return NULL;
|
|
|
|
timeline = pvr_sync_get_api_priv(file);
|
|
if (!timeline)
|
|
fput(file);
|
|
|
|
return timeline;
|
|
}
|
|
|
|
static void pvr_sync_timeline_fput(struct pvr_sync_timeline *timeline)
|
|
{
|
|
struct file *file = pvr_sync_get_file_struct(timeline->file_handle);
|
|
|
|
if (file)
|
|
fput(file);
|
|
else
|
|
pr_err(FILE_NAME ": %s: Timeline incomplete\n", __func__);
|
|
}
|
|
|
|
/* ioctl and fops handling */
|
|
|
|
int pvr_sync_api_init(void *file_handle, void **api_priv)
|
|
{
|
|
struct pvr_sync_timeline *timeline;
|
|
char task_comm[TASK_COMM_LEN];
|
|
|
|
get_task_comm(task_comm, current);
|
|
|
|
timeline = kzalloc(sizeof(*timeline), GFP_KERNEL);
|
|
if (!timeline)
|
|
return -ENOMEM;
|
|
|
|
strlcpy(timeline->name, task_comm, sizeof(timeline->name));
|
|
timeline->file_handle = file_handle;
|
|
timeline->is_sw = false;
|
|
|
|
*api_priv = (void *)timeline;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pvr_sync_api_deinit(void *api_priv, bool is_sw)
|
|
{
|
|
struct pvr_sync_timeline *timeline = api_priv;
|
|
|
|
if (!timeline)
|
|
return 0;
|
|
|
|
if (timeline->sw_fence_timeline) {
|
|
/* This makes sure any outstanding SW syncs are marked as
|
|
* complete at timeline close time. Otherwise it'll leak the
|
|
* timeline (as outstanding fences hold a ref) and possibly
|
|
* wedge the system if something is waiting on one of those
|
|
* fences
|
|
*/
|
|
pvr_counting_fence_timeline_force_complete(
|
|
timeline->sw_fence_timeline);
|
|
pvr_counting_fence_timeline_put(timeline->sw_fence_timeline);
|
|
}
|
|
|
|
if (timeline->hw_fence_context) {
|
|
#if defined(NO_HARDWARE)
|
|
mutex_lock(&pvr_timeline_active_list_lock);
|
|
list_del(&timeline->list);
|
|
mutex_unlock(&pvr_timeline_active_list_lock);
|
|
#endif
|
|
pvr_fence_context_destroy(timeline->hw_fence_context);
|
|
}
|
|
|
|
kfree(timeline);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This is the function that kick code will call in order to 'finalise' a
|
|
* created output fence just prior to returning from the kick function.
|
|
* The OS native sync code needs to implement a function meeting this
|
|
* specification - the implementation may be a nop if the OS does not need
|
|
* to perform any actions at this point.
|
|
*
|
|
* Input: fence_fd The PVRSRV_FENCE to be 'finalised'. This value
|
|
* will have been returned by an earlier call to
|
|
* pvr_sync_create_fence().
|
|
* Input: finalise_data The finalise data returned by an earlier call
|
|
* to pvr_sync_create_fence().
|
|
*/
|
|
static enum PVRSRV_ERROR_TAG
|
|
pvr_sync_finalise_fence(PVRSRV_FENCE fence_fd, void *finalise_data)
|
|
{
|
|
struct sync_file *sync_file = finalise_data;
|
|
struct pvr_fence *pvr_fence;
|
|
|
|
if (!sync_file || (fence_fd < 0)) {
|
|
pr_err(FILE_NAME ": %s: Invalid input fence\n", __func__);
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
}
|
|
|
|
pvr_fence = to_pvr_fence(sync_file->fence);
|
|
|
|
if (!pvr_fence) {
|
|
pr_err(FILE_NAME ": %s: Fence not a pvr fence\n", __func__);
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
}
|
|
|
|
/* pvr fences can be signalled any time after creation */
|
|
dma_fence_enable_sw_signaling(&pvr_fence->base);
|
|
|
|
fd_install(fence_fd, sync_file->file);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
/*
|
|
* This is the function that kick code will call in order to obtain a new
|
|
* PVRSRV_FENCE from the OS native sync code and the PSYNC_CHECKPOINT used
|
|
* in that fence. The OS native sync code needs to implement a function
|
|
* meeting this specification.
|
|
*
|
|
* Input: device Device node to use in creating a hw_fence_ctx
|
|
* Input: fence_name A string to annotate the fence with (for
|
|
* debug).
|
|
* Input: timeline The timeline on which the new fence is to be
|
|
* created.
|
|
* Output: new_fence The new PVRSRV_FENCE to be returned by the
|
|
* kick call.
|
|
* Output: fence_uid Unique ID of the update fence.
|
|
* Output: fence_finalise_data Pointer to data needed to finalise the fence.
|
|
* Output: new_checkpoint_handle The PSYNC_CHECKPOINT used by the new fence.
|
|
*/
|
|
static enum PVRSRV_ERROR_TAG
|
|
pvr_sync_create_fence(
|
|
struct _PVRSRV_DEVICE_NODE_ *device,
|
|
const char *fence_name,
|
|
PVRSRV_TIMELINE new_fence_timeline,
|
|
PSYNC_CHECKPOINT_CONTEXT psSyncCheckpointContext,
|
|
PVRSRV_FENCE *new_fence, u64 *fence_uid,
|
|
void **fence_finalise_data,
|
|
PSYNC_CHECKPOINT *new_checkpoint_handle,
|
|
void **timeline_update_sync,
|
|
__u32 *timeline_update_value)
|
|
{
|
|
PVRSRV_ERROR err = PVRSRV_OK;
|
|
PVRSRV_FENCE new_fence_fd = -1;
|
|
struct pvr_sync_timeline *timeline;
|
|
struct pvr_fence *pvr_fence;
|
|
PSYNC_CHECKPOINT checkpoint;
|
|
struct sync_file *sync_file;
|
|
|
|
if (new_fence_timeline < 0 || !new_fence || !new_checkpoint_handle
|
|
|| !fence_finalise_data) {
|
|
pr_err(FILE_NAME ": %s: Invalid input params\n", __func__);
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_out;
|
|
}
|
|
|
|
/* We reserve the new fence FD before taking any operations
|
|
* as we do not want to fail (e.g. run out of FDs)
|
|
*/
|
|
new_fence_fd = get_unused_fd_flags(O_CLOEXEC);
|
|
if (new_fence_fd < 0) {
|
|
pr_err(FILE_NAME ": %s: Failed to get fd\n", __func__);
|
|
err = PVRSRV_ERROR_UNABLE_TO_ADD_HANDLE;
|
|
goto err_out;
|
|
}
|
|
|
|
timeline = pvr_sync_timeline_fget(new_fence_timeline);
|
|
if (!timeline) {
|
|
pr_err(FILE_NAME ": %s: Failed to open supplied timeline fd (%d)\n",
|
|
__func__, new_fence_timeline);
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
if (timeline->is_sw) {
|
|
/* This should never happen! */
|
|
pr_err(FILE_NAME ": %s: Request to create a pvr fence on sw timeline (%d)\n",
|
|
__func__, new_fence_timeline);
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_put_timeline;
|
|
}
|
|
|
|
if (!timeline->hw_fence_context) {
|
|
/* First time we use this timeline, so create a context. */
|
|
timeline->hw_fence_context =
|
|
pvr_fence_context_create(
|
|
device,
|
|
NativeSyncGetFenceStatusWq(),
|
|
timeline->name);
|
|
if (!timeline->hw_fence_context) {
|
|
pr_err(FILE_NAME ": %s: Failed to create fence context (%d)\n",
|
|
__func__, new_fence_timeline);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_timeline;
|
|
}
|
|
#if defined(NO_HARDWARE)
|
|
/* Add timeline to active list */
|
|
INIT_LIST_HEAD(&timeline->list);
|
|
mutex_lock(&pvr_timeline_active_list_lock);
|
|
list_add_tail(&timeline->list, &pvr_timeline_active_list);
|
|
mutex_unlock(&pvr_timeline_active_list_lock);
|
|
#endif
|
|
}
|
|
|
|
pvr_fence = pvr_fence_create(timeline->hw_fence_context,
|
|
psSyncCheckpointContext,
|
|
new_fence_timeline,
|
|
fence_name);
|
|
if (!pvr_fence) {
|
|
pr_err(FILE_NAME ": %s: Failed to create new pvr_fence\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_timeline;
|
|
}
|
|
|
|
checkpoint = pvr_fence_get_checkpoint(pvr_fence);
|
|
if (!checkpoint) {
|
|
pr_err(FILE_NAME ": %s: Failed to get fence checkpoint\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_destroy_fence;
|
|
}
|
|
|
|
sync_file = sync_file_create(&pvr_fence->base);
|
|
if (!sync_file) {
|
|
pr_err(FILE_NAME ": %s: Failed to create sync_file\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_destroy_fence;
|
|
}
|
|
strlcpy(sync_file_user_name(sync_file),
|
|
pvr_fence->name,
|
|
sizeof(sync_file_user_name(sync_file)));
|
|
dma_fence_put(&pvr_fence->base);
|
|
|
|
*new_fence = new_fence_fd;
|
|
*fence_finalise_data = sync_file;
|
|
*new_checkpoint_handle = checkpoint;
|
|
*fence_uid = OSGetCurrentClientProcessIDKM();
|
|
*fence_uid = (*fence_uid << 32) | (new_fence_fd & U32_MAX);
|
|
/* not used but don't want to return dangling pointers */
|
|
*timeline_update_sync = NULL;
|
|
*timeline_update_value = 0;
|
|
|
|
pvr_sync_timeline_fput(timeline);
|
|
err_out:
|
|
return err;
|
|
|
|
err_destroy_fence:
|
|
pvr_fence_destroy(pvr_fence);
|
|
err_put_timeline:
|
|
pvr_sync_timeline_fput(timeline);
|
|
err_put_fd:
|
|
put_unused_fd(new_fence_fd);
|
|
*fence_uid = PVRSRV_NO_FENCE;
|
|
goto err_out;
|
|
}
|
|
|
|
/*
|
|
* This is the function that kick code will call in order to 'rollback' a
|
|
* created output fence should an error occur when submitting the kick.
|
|
* The OS native sync code needs to implement a function meeting this
|
|
* specification.
|
|
*
|
|
* Input: fence_to_rollback The PVRSRV_FENCE to be 'rolled back'. The fence
|
|
* should be destroyed and any actions taken due to
|
|
* its creation that need to be undone should be
|
|
* reverted.
|
|
* Input: finalise_data The finalise data for the fence to be 'rolled back'.
|
|
*/
|
|
static enum PVRSRV_ERROR_TAG
|
|
pvr_sync_rollback_fence_data(PVRSRV_FENCE fence_to_rollback,
|
|
void *fence_data_to_rollback)
|
|
{
|
|
struct sync_file *sync_file = fence_data_to_rollback;
|
|
struct pvr_fence *pvr_fence;
|
|
|
|
if (!sync_file || fence_to_rollback < 0) {
|
|
pr_err(FILE_NAME ": %s: Invalid fence (%d)\n", __func__,
|
|
fence_to_rollback);
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
}
|
|
|
|
pvr_fence = to_pvr_fence(sync_file->fence);
|
|
if (!pvr_fence) {
|
|
pr_err(FILE_NAME
|
|
": %s: Non-PVR fence (%p)\n",
|
|
__func__, sync_file->fence);
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
}
|
|
|
|
fput(sync_file->file);
|
|
|
|
put_unused_fd(fence_to_rollback);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
/*
|
|
* This is the function that kick code will call in order to obtain a list of
|
|
* the PSYNC_CHECKPOINTs for a given PVRSRV_FENCE passed to a kick function.
|
|
* The OS native sync code will allocate the memory to hold the returned list
|
|
* of PSYNC_CHECKPOINT ptrs. The caller will free this memory once it has
|
|
* finished referencing it.
|
|
*
|
|
* Input: fence The input (check) fence
|
|
* Output: nr_checkpoints The number of PVRSRV_SYNC_CHECKPOINT ptrs
|
|
* returned in the checkpoint_handles
|
|
* parameter.
|
|
* Output: fence_uid Unique ID of the check fence
|
|
* Input/Output: checkpoint_handles The returned list of PVRSRV_SYNC_CHECKPOINTs.
|
|
*/
|
|
static enum PVRSRV_ERROR_TAG
|
|
pvr_sync_resolve_fence(PSYNC_CHECKPOINT_CONTEXT psSyncCheckpointContext,
|
|
PVRSRV_FENCE fence_to_resolve, u32 *nr_checkpoints,
|
|
PSYNC_CHECKPOINT **checkpoint_handles, u64 *fence_uid)
|
|
{
|
|
PSYNC_CHECKPOINT *checkpoints = NULL;
|
|
unsigned int i, num_fences = 0, num_used_fences = 0;
|
|
struct dma_fence **fences = NULL;
|
|
struct dma_fence *fence;
|
|
PVRSRV_ERROR err = PVRSRV_OK;
|
|
|
|
if (!nr_checkpoints || !checkpoint_handles || !fence_uid) {
|
|
pr_err(FILE_NAME ": %s: Invalid input checkpoint pointer\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_out;
|
|
}
|
|
|
|
*nr_checkpoints = 0;
|
|
*checkpoint_handles = NULL;
|
|
*fence_uid = 0;
|
|
|
|
if (fence_to_resolve < 0)
|
|
goto err_out;
|
|
|
|
fence = sync_file_get_fence(fence_to_resolve);
|
|
if (!fence) {
|
|
pr_err(FILE_NAME ": %s: Failed to read sync private data for fd %d\n",
|
|
__func__, fence_to_resolve);
|
|
err = PVRSRV_ERROR_HANDLE_NOT_FOUND;
|
|
goto err_out;
|
|
}
|
|
|
|
if (dma_fence_is_array(fence)) {
|
|
struct dma_fence_array *array = to_dma_fence_array(fence);
|
|
|
|
if (array) {
|
|
fences = array->fences;
|
|
num_fences = array->num_fences;
|
|
}
|
|
} else {
|
|
fences = &fence;
|
|
num_fences = 1;
|
|
}
|
|
|
|
checkpoints = kmalloc_array(num_fences, sizeof(PSYNC_CHECKPOINT),
|
|
GFP_KERNEL);
|
|
if (!checkpoints) {
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_fence;
|
|
}
|
|
for (i = 0; i < num_fences; i++) {
|
|
/*
|
|
* Only return the checkpoint if the fence is still active.
|
|
* Don't checked for signalled on PDUMP drivers as we need
|
|
* to make sure that all fences make it to the pdump.
|
|
*/
|
|
#if !defined(PDUMP)
|
|
if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
|
|
&fences[i]->flags))
|
|
#endif
|
|
{
|
|
struct pvr_fence *pvr_fence =
|
|
pvr_fence_create_from_fence(
|
|
pvr_sync_data.foreign_fence_context,
|
|
psSyncCheckpointContext,
|
|
fences[i],
|
|
fence_to_resolve,
|
|
"foreign");
|
|
if (!pvr_fence) {
|
|
pr_err(FILE_NAME ": %s: Failed to create fence\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_free_checkpoints;
|
|
}
|
|
checkpoints[num_used_fences] =
|
|
pvr_fence_get_checkpoint(pvr_fence);
|
|
SyncCheckpointTakeRef(checkpoints[num_used_fences]);
|
|
++num_used_fences;
|
|
dma_fence_put(&pvr_fence->base);
|
|
}
|
|
}
|
|
/* If we don't return any checkpoints, delete the array because
|
|
* the caller will not.
|
|
*/
|
|
if (num_used_fences == 0) {
|
|
kfree(checkpoints);
|
|
checkpoints = NULL;
|
|
}
|
|
|
|
*checkpoint_handles = checkpoints;
|
|
*nr_checkpoints = num_used_fences;
|
|
*fence_uid = OSGetCurrentClientProcessIDKM();
|
|
*fence_uid = (*fence_uid << 32) | (fence_to_resolve & U32_MAX);
|
|
|
|
err_put_fence:
|
|
dma_fence_put(fence);
|
|
err_out:
|
|
return err;
|
|
|
|
err_free_checkpoints:
|
|
for (i = 0; i < num_used_fences; i++) {
|
|
if (checkpoints[i])
|
|
SyncCheckpointDropRef(checkpoints[i]);
|
|
}
|
|
kfree(checkpoints);
|
|
goto err_put_fence;
|
|
}
|
|
|
|
/*
|
|
* This is the function that driver code will call in order to request the
|
|
* sync implementation to output debug information relating to any sync
|
|
* checkpoints it may have created which appear in the provided array of
|
|
* FW addresses of Unified Fence Objects (UFOs).
|
|
*
|
|
* Input: nr_ufos The number of FW addresses provided in the
|
|
* vaddrs parameter.
|
|
* Input: vaddrs The array of FW addresses of UFOs. The sync
|
|
* implementation should check each of these to
|
|
* see if any relate to sync checkpoints it has
|
|
* created and where they do output debug information
|
|
* pertaining to the native/fallback sync with
|
|
* which it is associated.
|
|
*/
|
|
static u32
|
|
pvr_sync_dump_info_on_stalled_ufos(u32 nr_ufos, u32 *vaddrs)
|
|
{
|
|
return pvr_fence_dump_info_on_stalled_ufos(pvr_sync_data.foreign_fence_context,
|
|
nr_ufos,
|
|
vaddrs);
|
|
}
|
|
|
|
#if defined(PDUMP)
|
|
static enum PVRSRV_ERROR_TAG
|
|
pvr_sync_fence_get_checkpoints(PVRSRV_FENCE fence_to_pdump, u32 *nr_checkpoints,
|
|
struct SYNC_CHECKPOINT_TAG ***checkpoint_handles)
|
|
{
|
|
struct dma_fence **fences = NULL;
|
|
struct dma_fence *fence;
|
|
struct pvr_fence *pvr_fence;
|
|
struct SYNC_CHECKPOINT_TAG **checkpoints = NULL;
|
|
unsigned int i, num_fences, num_used_fences = 0;
|
|
enum PVRSRV_ERROR_TAG err;
|
|
|
|
if (fence_to_pdump < 0) {
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_out;
|
|
}
|
|
|
|
if (!nr_checkpoints || !checkpoint_handles) {
|
|
pr_err(FILE_NAME ": %s: Invalid input checkpoint pointer\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_out;
|
|
}
|
|
|
|
fence = sync_file_get_fence(fence_to_pdump);
|
|
if (!fence) {
|
|
pr_err(FILE_NAME ": %s: Failed to read sync private data for fd %d\n",
|
|
__func__, fence_to_pdump);
|
|
err = PVRSRV_ERROR_HANDLE_NOT_FOUND;
|
|
goto err_out;
|
|
}
|
|
|
|
if (dma_fence_is_array(fence)) {
|
|
struct dma_fence_array *array = to_dma_fence_array(fence);
|
|
|
|
fences = array->fences;
|
|
num_fences = array->num_fences;
|
|
} else {
|
|
fences = &fence;
|
|
num_fences = 1;
|
|
}
|
|
|
|
checkpoints = kmalloc_array(num_fences, sizeof(*checkpoints),
|
|
GFP_KERNEL);
|
|
if (!checkpoints) {
|
|
pr_err("pvr_sync_file: %s: Failed to alloc memory for returned list of sync checkpoints\n",
|
|
__func__);
|
|
err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_fence;
|
|
}
|
|
|
|
for (i = 0; i < num_fences; i++) {
|
|
pvr_fence = to_pvr_fence(fences[i]);
|
|
if (!pvr_fence)
|
|
continue;
|
|
checkpoints[num_used_fences] = pvr_fence_get_checkpoint(pvr_fence);
|
|
++num_used_fences;
|
|
}
|
|
|
|
*checkpoint_handles = checkpoints;
|
|
*nr_checkpoints = num_used_fences;
|
|
err = PVRSRV_OK;
|
|
|
|
err_put_fence:
|
|
dma_fence_put(fence);
|
|
err_out:
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
int pvr_sync_api_rename(void *api_priv, void *user_data)
|
|
{
|
|
struct pvr_sync_timeline *timeline = api_priv;
|
|
struct pvr_sync_rename_ioctl_data *data = user_data;
|
|
|
|
data->szName[sizeof(data->szName) - 1] = '\0';
|
|
strlcpy(timeline->name, data->szName, sizeof(timeline->name));
|
|
if (timeline->hw_fence_context)
|
|
strlcpy(timeline->hw_fence_context->name, data->szName,
|
|
sizeof(timeline->hw_fence_context->name));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pvr_sync_api_force_sw_only(void *api_priv, void **api_priv_new)
|
|
{
|
|
struct pvr_sync_timeline *timeline = api_priv;
|
|
|
|
/* Already in SW mode? */
|
|
if (timeline->sw_fence_timeline)
|
|
return 0;
|
|
|
|
/* Create a sw_sync timeline with the old GPU timeline's name */
|
|
timeline->sw_fence_timeline = pvr_counting_fence_timeline_create(
|
|
timeline->name);
|
|
if (!timeline->sw_fence_timeline)
|
|
return -ENOMEM;
|
|
|
|
timeline->is_sw = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pvr_sync_api_sw_create_fence(void *api_priv, void *user_data)
|
|
{
|
|
struct pvr_sync_timeline *timeline = api_priv;
|
|
struct pvr_sw_sync_create_fence_data *data = user_data;
|
|
struct sync_file *sync_file;
|
|
int fd = get_unused_fd_flags(O_CLOEXEC);
|
|
struct dma_fence *fence;
|
|
int err;
|
|
|
|
if (fd < 0) {
|
|
pr_err(FILE_NAME ": %s: Failed to find unused fd (%d)\n",
|
|
__func__, fd);
|
|
err = -EMFILE;
|
|
goto err_out;
|
|
}
|
|
|
|
fence = pvr_counting_fence_create(timeline->sw_fence_timeline, &data->sync_pt_idx);
|
|
if (!fence) {
|
|
pr_err(FILE_NAME ": %s: Failed to create a sync point (%d)\n",
|
|
__func__, fd);
|
|
err = -ENOMEM;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
sync_file = sync_file_create(fence);
|
|
dma_fence_put(fence);
|
|
if (!sync_file) {
|
|
pr_err(FILE_NAME ": %s: Failed to create a sync point (%d)\n",
|
|
__func__, fd);
|
|
err = -ENOMEM;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
data->fence = fd;
|
|
|
|
fd_install(fd, sync_file->file);
|
|
|
|
return 0;
|
|
|
|
err_put_fd:
|
|
put_unused_fd(fd);
|
|
err_out:
|
|
return err;
|
|
}
|
|
|
|
int pvr_sync_api_sw_inc(void *api_priv, void *user_data)
|
|
{
|
|
struct pvr_sync_timeline *timeline = api_priv;
|
|
struct pvr_sw_timeline_advance_data *data = user_data;
|
|
bool res;
|
|
|
|
res = pvr_counting_fence_timeline_inc(timeline->sw_fence_timeline, &data->sync_pt_idx);
|
|
|
|
/* pvr_counting_fence_timeline_inc won't allow sw timeline to be
|
|
* advanced beyond the last defined point
|
|
*/
|
|
if (!res) {
|
|
pr_err("pvr_sync_file: attempt to advance SW timeline beyond last defined point\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pvr_sync_debug_request_heading(void *data, u32 verbosity,
|
|
DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf,
|
|
void *pvDumpDebugFile)
|
|
{
|
|
if (DD_VERB_LVL_ENABLED(verbosity, DEBUG_REQUEST_VERBOSITY_MEDIUM))
|
|
PVR_DUMPDEBUG_LOG(pfnDumpDebugPrintf, pvDumpDebugFile,
|
|
"------[ Native Fence Sync: timelines ]------");
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_register_functions(void)
|
|
{
|
|
/* Register the resolve fence and create fence functions with
|
|
* sync_checkpoint.c
|
|
* The pvr_fence context registers its own EventObject callback to
|
|
* update sync status
|
|
*/
|
|
/* Initialise struct and register with sync_checkpoint.c */
|
|
pvr_sync_data.sync_checkpoint_ops.pfnFenceResolve = pvr_sync_resolve_fence;
|
|
pvr_sync_data.sync_checkpoint_ops.pfnFenceCreate = pvr_sync_create_fence;
|
|
pvr_sync_data.sync_checkpoint_ops.pfnFenceDataRollback = pvr_sync_rollback_fence_data;
|
|
pvr_sync_data.sync_checkpoint_ops.pfnFenceFinalise = pvr_sync_finalise_fence;
|
|
#if defined(NO_HARDWARE)
|
|
pvr_sync_data.sync_checkpoint_ops.pfnNoHWUpdateTimelines = pvr_sync_nohw_signal_fence;
|
|
#else
|
|
pvr_sync_data.sync_checkpoint_ops.pfnNoHWUpdateTimelines = NULL;
|
|
#endif
|
|
pvr_sync_data.sync_checkpoint_ops.pfnFreeCheckpointListMem =
|
|
pvr_sync_free_checkpoint_list_mem;
|
|
pvr_sync_data.sync_checkpoint_ops.pfnDumpInfoOnStalledUFOs =
|
|
pvr_sync_dump_info_on_stalled_ufos;
|
|
strlcpy(pvr_sync_data.sync_checkpoint_ops.pszImplName, "pvr_sync_file",
|
|
SYNC_CHECKPOINT_IMPL_MAX_STRLEN);
|
|
#if defined(PDUMP)
|
|
pvr_sync_data.sync_checkpoint_ops.pfnSyncFenceGetCheckpoints =
|
|
pvr_sync_fence_get_checkpoints;
|
|
#endif
|
|
|
|
return SyncCheckpointRegisterFunctions(&pvr_sync_data.sync_checkpoint_ops);
|
|
}
|
|
|
|
int pvr_sync_init(void)
|
|
{
|
|
int err;
|
|
|
|
pvr_sync_data.foreign_fence_context =
|
|
pvr_fence_foreign_context_create(
|
|
NativeSyncGetFenceStatusWq(),
|
|
"foreign_sync");
|
|
if (!pvr_sync_data.foreign_fence_context) {
|
|
pr_err(FILE_NAME ": %s: Failed to create foreign sync context\n",
|
|
__func__);
|
|
err = -ENOMEM;
|
|
goto err_out;
|
|
}
|
|
|
|
#if defined(NO_HARDWARE)
|
|
INIT_LIST_HEAD(&pvr_timeline_active_list);
|
|
#endif
|
|
|
|
err = pvr_sync_ioctl_init();
|
|
if (err) {
|
|
pr_err(FILE_NAME ": %s: Failed to register pvr_sync device (%d)\n",
|
|
__func__, err);
|
|
goto err_ioctl_init;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_ioctl_init:
|
|
pvr_fence_context_destroy(pvr_sync_data.foreign_fence_context);
|
|
pvr_fence_cleanup();
|
|
err_out:
|
|
return err;
|
|
}
|
|
|
|
void pvr_sync_deinit(void)
|
|
{
|
|
pvr_sync_ioctl_deinit();
|
|
pvr_fence_context_destroy(pvr_sync_data.foreign_fence_context);
|
|
pvr_fence_cleanup();
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_device_init(struct device *dev)
|
|
{
|
|
struct drm_device *ddev = dev_get_drvdata(dev);
|
|
struct pvr_drm_private *priv = ddev->dev_private;
|
|
enum PVRSRV_ERROR_TAG error;
|
|
|
|
error = PVRSRVRegisterDeviceDbgRequestNotify(
|
|
&priv->sync_debug_notify_handle,
|
|
priv->dev_node,
|
|
pvr_sync_debug_request_heading,
|
|
DEBUG_REQUEST_LINUXFENCE,
|
|
NULL);
|
|
if (error != PVRSRV_OK) {
|
|
pr_err("%s: failed to register debug request callback (%s)\n",
|
|
__func__, PVRSRVGetErrorString(error));
|
|
goto err_out;
|
|
}
|
|
|
|
/* Register the foreign sync context debug notifier on each device */
|
|
error = pvr_fence_context_register_dbg(
|
|
&priv->sync_foreign_debug_notify_handle,
|
|
priv->dev_node,
|
|
pvr_sync_data.foreign_fence_context);
|
|
if (error != PVRSRV_OK) {
|
|
pr_err("%s: failed to register fence debug request callback (%s)\n",
|
|
__func__, PVRSRVGetErrorString(error));
|
|
goto err_context_regdbg;
|
|
}
|
|
|
|
#if defined(NO_HARDWARE)
|
|
INIT_LIST_HEAD(&pvr_timeline_active_list);
|
|
#endif
|
|
|
|
return PVRSRV_OK;
|
|
|
|
err_context_regdbg:
|
|
PVRSRVUnregisterDeviceDbgRequestNotify(priv->sync_debug_notify_handle);
|
|
err_out:
|
|
return error;
|
|
}
|
|
|
|
void pvr_sync_device_deinit(struct device *dev)
|
|
{
|
|
struct drm_device *ddev = dev_get_drvdata(dev);
|
|
struct pvr_drm_private *priv = ddev->dev_private;
|
|
|
|
PVRSRVUnregisterDeviceDbgRequestNotify(priv->sync_foreign_debug_notify_handle);
|
|
PVRSRVUnregisterDeviceDbgRequestNotify(priv->sync_debug_notify_handle);
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_fence_wait(void *fence, u32 timeout_in_ms)
|
|
{
|
|
long timeout = msecs_to_jiffies(timeout_in_ms);
|
|
int err;
|
|
|
|
err = dma_fence_wait_timeout(fence, true, timeout);
|
|
/*
|
|
* dma_fence_wait_timeout returns:
|
|
* - the remaining timeout on success
|
|
* - 0 on timeout
|
|
* - -ERESTARTSYS if interrupted
|
|
*/
|
|
if (err > 0)
|
|
return PVRSRV_OK;
|
|
else if (err == 0)
|
|
return PVRSRV_ERROR_TIMEOUT;
|
|
|
|
return PVRSRV_ERROR_FAILED_DEPENDENCIES;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_fence_release(void *fence)
|
|
{
|
|
dma_fence_put(fence);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_fence_get(int fence_fd, void **fence_out)
|
|
{
|
|
struct dma_fence *fence;
|
|
|
|
fence = sync_file_get_fence(fence_fd);
|
|
if (fence == NULL)
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
|
|
*fence_out = fence;
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG
|
|
pvr_sync_sw_timeline_fence_create(struct _PVRSRV_DEVICE_NODE_ *pvrsrv_dev_node,
|
|
int timeline_fd,
|
|
const char *fence_name,
|
|
int *fence_fd_out,
|
|
u64 *sync_pt_idx)
|
|
{
|
|
enum PVRSRV_ERROR_TAG srv_err;
|
|
struct pvr_sync_timeline *timeline;
|
|
struct dma_fence *fence = NULL;
|
|
struct sync_file *sync_file = NULL;
|
|
int fd;
|
|
|
|
(void)(pvrsrv_dev_node);
|
|
|
|
fd = get_unused_fd_flags(O_CLOEXEC);
|
|
if (fd < 0)
|
|
return PVRSRV_ERROR_UNABLE_TO_ADD_HANDLE;
|
|
|
|
timeline = pvr_sync_timeline_fget(timeline_fd);
|
|
if (!timeline) {
|
|
/* unrecognised timeline */
|
|
srv_err = PVRSRV_ERROR_RESOURCE_UNAVAILABLE;
|
|
goto err_put_fd;
|
|
}
|
|
if (!timeline->is_sw) {
|
|
pvr_sync_timeline_fput(timeline);
|
|
srv_err = PVRSRV_ERROR_INVALID_PARAMS;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
fence = pvr_counting_fence_create(timeline->sw_fence_timeline, sync_pt_idx);
|
|
pvr_sync_timeline_fput(timeline);
|
|
if (!fence) {
|
|
srv_err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
sync_file = sync_file_create(fence);
|
|
dma_fence_put(fence);
|
|
if (!sync_file) {
|
|
srv_err = PVRSRV_ERROR_OUT_OF_MEMORY;
|
|
goto err_put_fd;
|
|
}
|
|
|
|
fd_install(fd, sync_file->file);
|
|
|
|
*fence_fd_out = fd;
|
|
|
|
return PVRSRV_OK;
|
|
|
|
err_put_fd:
|
|
put_unused_fd(fd);
|
|
return srv_err;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_sw_timeline_advance(void *timeline, u64 *sync_pt_idx)
|
|
{
|
|
if (timeline == NULL)
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
|
|
pvr_counting_fence_timeline_inc(timeline, sync_pt_idx);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_sw_timeline_release(void *timeline)
|
|
{
|
|
if (timeline == NULL)
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
|
|
pvr_counting_fence_timeline_put(timeline);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG pvr_sync_sw_timeline_get(int timeline_fd,
|
|
void **timeline_out)
|
|
{
|
|
struct pvr_counting_fence_timeline *sw_timeline;
|
|
struct pvr_sync_timeline *timeline;
|
|
|
|
timeline = pvr_sync_timeline_fget(timeline_fd);
|
|
if (!timeline)
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
|
|
sw_timeline =
|
|
pvr_counting_fence_timeline_get(timeline->sw_fence_timeline);
|
|
pvr_sync_timeline_fput(timeline);
|
|
if (!sw_timeline)
|
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
|
|
|
*timeline_out = sw_timeline;
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
static void _dump_sync_point(struct dma_fence *fence,
|
|
DUMPDEBUG_PRINTF_FUNC *dump_debug_printf,
|
|
void *dump_debug_file)
|
|
{
|
|
const struct dma_fence_ops *fence_ops = fence->ops;
|
|
bool signaled = dma_fence_is_signaled(fence);
|
|
char time[16] = { '\0' };
|
|
|
|
fence_ops->timeline_value_str(fence, time, sizeof(time));
|
|
|
|
PVR_DUMPDEBUG_LOG(dump_debug_printf,
|
|
dump_debug_file,
|
|
"<%p> Seq#=%llu TS=%s State=%s TLN=%s",
|
|
fence,
|
|
(u64) fence->seqno,
|
|
time,
|
|
(signaled) ? "Signalled" : "Active",
|
|
fence_ops->get_timeline_name(fence));
|
|
}
|
|
|
|
static void _dump_fence(struct dma_fence *fence,
|
|
DUMPDEBUG_PRINTF_FUNC *dump_debug_printf,
|
|
void *dump_debug_file)
|
|
{
|
|
if (dma_fence_is_array(fence)) {
|
|
struct dma_fence_array *fence_array = to_dma_fence_array(fence);
|
|
int i;
|
|
|
|
if (fence_array) {
|
|
PVR_DUMPDEBUG_LOG(dump_debug_printf,
|
|
dump_debug_file,
|
|
"Fence: [%p] Sync Points:\n",
|
|
fence_array);
|
|
|
|
for (i = 0; i < fence_array->num_fences; i++)
|
|
_dump_sync_point(fence_array->fences[i],
|
|
dump_debug_printf,
|
|
dump_debug_file);
|
|
}
|
|
|
|
} else {
|
|
_dump_sync_point(fence, dump_debug_printf, dump_debug_file);
|
|
}
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG
|
|
sync_dump_fence(void *sw_fence_obj,
|
|
DUMPDEBUG_PRINTF_FUNC *dump_debug_printf,
|
|
void *dump_debug_file)
|
|
{
|
|
struct dma_fence *fence = (struct dma_fence *) sw_fence_obj;
|
|
|
|
_dump_fence(fence, dump_debug_printf, dump_debug_file);
|
|
|
|
return PVRSRV_OK;
|
|
}
|
|
|
|
enum PVRSRV_ERROR_TAG
|
|
sync_sw_dump_timeline(void *sw_timeline_obj,
|
|
DUMPDEBUG_PRINTF_FUNC *dump_debug_printf,
|
|
void *dump_debug_file)
|
|
{
|
|
pvr_counting_fence_timeline_dump_timeline(sw_timeline_obj,
|
|
dump_debug_printf,
|
|
dump_debug_file);
|
|
|
|
return PVRSRV_OK;
|
|
}
|