msm: global synchronization driver

Synx driver manages synchronization and dependency
handling across heterogeneous cores.

Synx framework provides basic APIs to create, wait
(blocking nad non-blocking), signal and release
synx objects.
These objects can be used across UMD and KMD or
between KMD and FW. Each synx object is recognized
through a global identifier unique across the system.

Framework supports importing and exporting synx
object across process boundaries.
Also it is possible to merge multiple synx objects
to create a consolidated object, and can wait on
it till all synx objects composing the merged
object are signaled.

Synx framework also allows a synx object to bind
with an existing fence object. Today graphics and
camera systems already has signaling support.
For such systems, it is possible to use bind() api
to bind synx object with the already existing sync
object. Once bound, the synx object will also get
signaled whenever the other fence object is signaled
and vice-versa (through callback registration and
signaling).
This is driver version 1.0, bind operation is only
supported for camera csl fence objects.

Change-Id: I6e9f3ba4c45d42cce72467d1493a6abd5d98500b
Signed-off-by: Sumukh Hallymysore Ravindra <shallymy@codeaurora.org>
This commit is contained in:
Sumukh Hallymysore Ravindra 2019-01-14 14:07:57 -08:00
parent 863ea3f15c
commit fdf150a68d
11 changed files with 2833 additions and 0 deletions

View file

@ -13,3 +13,4 @@ source "drivers/media/platform/msm/cvp/Kconfig"
source "drivers/media/platform/msm/sde/Kconfig"
source "drivers/media/platform/msm/vidc/Kconfig"
source "drivers/media/platform/msm/npu/Kconfig"
source "drivers/media/platform/msm/synx/Kconfig"

View file

@ -9,3 +9,4 @@ obj-$(CONFIG_SPECTRA_CAMERA) += camera/
obj-$(CONFIG_MSM_CVP_V4L2) += cvp/
obj-$(CONFIG_MSM_VIDC_V4L2) += vidc/
obj-$(CONFIG_MSM_NPU) += npu/
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx/

View file

@ -0,0 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig MSM_GLOBAL_SYNX
bool "Qualcomm Technologies, Inc. Global Synchronization Framework"
depends on ARCH_QCOM
help
Say Y here to enable global synchronization framework for
Qualcomm Technologies, Inc. chipsets.
Enabling this adds support for global synchronization across
heterogeneous cores.

View file

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
ccflags-$(CONFIG_SPECTRA_CAMERA) += -Idrivers/media/platform/msm/camera/cam_sync
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx.o synx_util.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#ifndef __SYNX_API_H__
#define __SYNX_API_H__
#include <linux/list.h>
#include <uapi/media/synx.h>
typedef void (*synx_callback)(s32 sync_obj, int status, void *data);
/* Kernel APIs */
/**
* @brief: Creates a synx object
*
* The newly created synx obj is assigned to synx_obj.
*
* @param synx_obj : Pointer to synx object handle (filled by the function)
* @param name : Optional parameter associating a name with the synx
* object for debug purposes.
* Only first SYNC_DEBUG_NAME_LEN bytes are accepted,
* rest will be ignored.
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if synx_obj is an invalid pointer.
* -ENOMEM will be returned if the kernel can't allocate space for
* synx object.
*/
int synx_create(s32 *synx_obj, const char *name);
/**
* @brief: Registers a callback with a synx object
*
* @param synx_obj : int referencing the synx object.
* @param userdata : Opaque pointer passed back with callback.
* @param cb_func : Pointer to callback to be registered
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if userdata is invalid.
* -ENOMEM will be returned if cb_func is invalid.
*
*/
int synx_register_callback(s32 synx_obj,
void *userdata, synx_callback cb_func);
/**
* @brief: De-registers a callback with a synx object
*
* @param synx_obj : int referencing the synx object.
* @param cb_func : Pointer to callback to be de-registered
* @param userdata : Opaque pointer passed back with callback.
* @param cancel_cb_func : Pointer to callback to ack de-registration
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if userdata is invalid.
* -ENOMEM will be returned if cb_func is invalid.
*/
int synx_deregister_callback(s32 synx_obj,
synx_callback cb_func,
void *userdata,
synx_callback cancel_cb_func);
/**
* @brief: Signals a synx object with the status argument.
*
* This function will signal the synx object referenced by the synx_obj
* param and invoke any external binding synx objs.
* The status parameter will indicate whether the entity
* performing the signaling wants to convey an error case or a success case.
*
* @param synx_obj : Synx object handle
* @param status : Status of signaling. Value : SYNX_STATE_SIGNALED_SUCCESS or
* SYNX_STATE_SIGNALED_ERROR.
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_signal(s32 synx_obj, u32 status);
/**
* @brief: Merges multiple synx objects
*
* This function will merge multiple synx objects into a synx group.
*
* @param synxs : Pointer to a block of synx handles to be merged
* @param num_objs : Number of synx objs in the block
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_merge(s32 *synx_obj, u32 num_objs, s32 *merged_obj);
/**
* @brief: Waits for a synx object synchronously
*
* Does a wait on the synx object identified by synx_obj for a maximum
* of timeout_ms milliseconds. Must not be called from interrupt context as
* this API can sleep. Should be called from process context only.
*
* @param synx_obj : Synx object handle to be waited upon
* @timeout_ms synx_obj : Timeout in ms.
*
* @return 0 upon success, -EINVAL if synx object is in bad state or arguments
* are invalid, -ETIMEDOUT if wait times out.
*/
int synx_wait(s32 synx_obj, u64 timeout_ms);
/**
* @brief: Binds two synx objects
*
* Binding two synx objects will unify them in a way that if one
* of the signals, the other ones is signaled as well.
*
* @param synx_obj : Synx object handle
* @param external_sync : External synx descriptor to bind to object
*
* @return 0 upon success, -EINVAL if synx object is in bad state or arguments
* are invalid, -ETIMEDOUT if wait times out.
*/
int synx_bind(s32 synx_obj,
struct synx_external_desc external_sync);
/**
* @brief: return the status of the synx object
*
* @param synx_obj : Synx object handle
*
* @return status of the synx object
*/
int synx_get_status(s32 synx_obj);
/**
* @brief: Adds to the reference count of a synx object
*
* When a synx object is created, the refcount will be 1.
*
* @param synx_obj : Synx object handle
* @param count : Count to add to the refcount
*
* @return 0 upon success, -EINVAL if synx object is in bad
* state
*/
int synx_addrefcount(s32 synx_obj, s32 count);
/**
* @brief: Imports (looks up) a synx object from a given ID
*
* The given ID should have been exported by another client
* and provided.
*
* @param synx_obj : Synx object handle to import
* @param secure_key : Key to verify authenticity
* @param new_synx_obj : Pointer to newly created synx object
*
* @return 0 upon success, -EINVAL if synx object is bad
*/
int synx_import(s32 synx_obj, u32 secure_key, s32 *new_synx_obj);
/**
* @brief: Exports a synx object and returns an ID
*
* The given ID may be passed to other clients to be
* imported.
*
* @param synx_obj : Synx object handle to export
* @param secure_key : POinter to gnerated secure key
*
* @return 0 upon success, -EINVAL if the ID is bad
*/
int synx_export(s32 synx_obj, u32 *secure_key);
/**
* @brief: Decrements refcount of a synx object, and destroys it
* if becomes 0
*
* @param synx_obj: Synx object handle to be destroyed
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_release(s32 synx_obj);
#endif /* __SYNX_API_H__ */

View file

@ -0,0 +1,195 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#ifndef __SYNX_PRIVATE_H__
#define __SYNX_PRIVATE_H__
#include <linux/bitmap.h>
#include <linux/cdev.h>
#include <linux/dma-fence.h>
#include <linux/dma-fence-array.h>
#include <linux/idr.h>
#include <linux/workqueue.h>
#define SYNX_OBJ_NAME_LEN 64
#define SYNX_MAX_OBJS 1024
#define SYNX_MAX_REF_COUNTS 2048
#define SYNX_PAYLOAD_WORDS 4
#define SYNX_NAME "synx"
#define SYNX_WORKQUEUE_NAME "hiprio_synx_work_queue"
#define SYNX_MAX_NUM_BINDINGS 8
#define SYNX_DEVICE_NAME "synx_device"
/**
* struct synx_external_data - data passed over to external sync objects
* to pass on callback
*
* @synx_obj : synx obj id
* @secure_key : secure key for authentication
*/
struct synx_external_data {
s32 synx_obj;
u32 secure_key;
};
/**
* struct synx_bind_desc - bind payload descriptor
*
* @external_desc : external bind information
* @bind_data : pointer to data passed over
*/
struct synx_bind_desc {
struct synx_external_desc external_desc;
struct synx_external_data *external_data;
};
/**
* struct synx_callback_info - Single node of information about a kernel
* callback registered on a sync object
*
* @callback_func : Callback function, registered by client driver
* @cb_data : Callback data, registered by client driver
* @status : Status with which callback will be invoked in client
* @synx_obj : Sync id of the object for which callback is registered
* @cb_dispatch_work : Work representing the call dispatch
* @list : List member used to append this node to a linked list
*/
struct synx_callback_info {
synx_callback callback_func;
void *cb_data;
int status;
s32 synx_obj;
struct work_struct cb_dispatch_work;
struct list_head list;
};
struct synx_client;
/**
* struct synx_user_payload - Single node of information about a callback
* registered from user space
*
* @synx_obj : Global id
* @status : synx obj status or callback failure
* @payload_data : Payload data, opaque to kernel
*/
struct synx_user_payload {
s32 synx_obj;
int status;
u64 payload_data[SYNX_PAYLOAD_WORDS];
};
/**
* struct synx_cb_data - Single node of information about a user space
* payload registered from user space
*
* @client : Synx client
* @data : Payload data, opaque to kernel
* @list : List member used to append this node to user cb list
*/
struct synx_cb_data {
struct synx_client *client;
struct synx_user_payload data;
struct list_head list;
};
/**
* struct synx_table_row - Single row of information about a synx object, used
* for internal book keeping in the synx driver
*
* @name : Optional string representation of the synx object
* @fence : dma fence backing the synx object
* @synx_obj : Integer id representing this synx object
* @index : Index of the spin lock table associated with synx obj
* @num_bound_synxs : Number of external bound synx objects
* @signaling_id : ID of the external sync object invoking the callback
* @secure_key : Secure key generated for authentication
* @bound_synxs : Array of bound synx objects
* @callback_list : Linked list of kernel callbacks registered
* @user_payload_list : Linked list of user space payloads registered
*/
struct synx_table_row {
char name[SYNX_OBJ_NAME_LEN];
struct dma_fence *fence;
s32 synx_obj;
s32 index;
u32 num_bound_synxs;
s32 signaling_id;
u32 secure_key;
struct synx_bind_desc bound_synxs[SYNX_MAX_NUM_BINDINGS];
struct list_head callback_list;
struct list_head user_payload_list;
};
/**
* struct bind_operations - Function pointers that need to be defined
* to achieve bind functionality for external fence with synx obj
*
* @register_callback : Function to register with external sync object
* @deregister_callback : Function to deregister with external sync object
* @signal : Function to signal the external sync object
*/
struct bind_operations {
int (*register_callback)(synx_callback cb_func,
void *userdata, s32 sync_obj);
int (*deregister_callback)(synx_callback cb_func,
void *userdata, s32 sync_obj);
int (*signal)(s32 sync_obj, u32 status);
};
/**
* struct synx_device - Internal struct to book keep synx driver details
*
* @cdev : Character device
* @dev : Device type
* @class : Device class
* @synx_table : Table of all synx objects
* @row_spinlocks : Spinlock array, one for each row in the table
* @table_lock : Mutex used to lock the table
* @open_cnt : Count of file open calls made on the synx driver
* @work_queue : Work queue used for dispatching kernel callbacks
* @bitmap : Bitmap representation of all synx objects
* synx_ids : Global unique ids
* dma_context : dma context id
* bind_vtbl : Table with bind ops for supported external sync objects
* client_list : All the synx clients
*/
struct synx_device {
struct cdev cdev;
dev_t dev;
struct class *class;
struct synx_table_row synx_table[SYNX_MAX_OBJS];
spinlock_t row_spinlocks[SYNX_MAX_OBJS];
struct mutex table_lock;
int open_cnt;
struct workqueue_struct *work_queue;
DECLARE_BITMAP(bitmap, SYNX_MAX_OBJS);
struct idr synx_ids;
u64 dma_context;
struct bind_operations bind_vtbl[SYNX_MAX_BIND_TYPES];
struct list_head client_list;
};
/**
* struct synx_client - Internal struct to book keep each client
* specific details
*
* @device : Pointer to synx device structure
* @pid : Process id
* @eventq_lock : Spinlock for the event queue
* @wq : Queue for the polling process
* @eventq : All the user callback payloads
* @list : List member used to append this node to client_list
*/
struct synx_client {
struct synx_device *device;
int pid;
spinlock_t eventq_lock;
wait_queue_head_t wq;
struct list_head eventq;
struct list_head list;
};
#endif /* __SYNX_PRIVATE_H__ */

View file

@ -0,0 +1,558 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "synx: " fmt
#include <linux/slab.h>
#include <linux/random.h>
#include "synx_api.h"
#include "synx_util.h"
bool is_valid_type(u32 type)
{
if (type < SYNX_MAX_BIND_TYPES)
return true;
return false;
}
int synx_init_object(struct synx_table_row *table,
u32 idx,
s32 id,
const char *name,
struct dma_fence_ops *ops)
{
struct dma_fence *fence = NULL;
struct synx_table_row *row = table + idx;
if (!table || idx <= 0 || idx >= SYNX_MAX_OBJS)
return -EINVAL;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (!fence)
return -ENOMEM;
dma_fence_init(fence, ops, &synx_dev->row_spinlocks[idx],
synx_dev->dma_context, 1);
row->fence = fence;
row->synx_obj = id;
row->index = idx;
INIT_LIST_HEAD(&row->callback_list);
INIT_LIST_HEAD(&row->user_payload_list);
if (name)
strlcpy(row->name, name, sizeof(row->name));
pr_debug("synx obj init: id:0x%x state:%u fence: 0x%pK\n",
row->synx_obj, synx_status_locked(row), fence);
return 0;
}
int synx_init_group_object(struct synx_table_row *table,
u32 idx,
s32 id,
struct dma_fence **fences,
u32 num_objs)
{
struct synx_table_row *row = table + idx;
struct dma_fence_array *array;
array = dma_fence_array_create(num_objs,
fences, synx_dev->dma_context, 1, false);
if (!array)
return -EINVAL;
row->fence = &array->base;
row->synx_obj = id;
row->index = idx;
INIT_LIST_HEAD(&row->callback_list);
INIT_LIST_HEAD(&row->user_payload_list);
pr_debug("synx group obj init: id:0x%x state:%u fence: 0x%pK\n",
row->synx_obj, synx_status_locked(row), row->fence);
return 0;
}
void synx_callback_dispatch(struct synx_table_row *row)
{
u32 state = SYNX_STATE_INVALID;
struct synx_client *client = NULL;
struct synx_callback_info *synx_cb, *temp_synx_cb;
struct synx_cb_data *payload_info, *temp_payload_info;
if (!row)
return;
state = synx_status_locked(row);
/* dispatch the kernel callbacks registered (if any) */
list_for_each_entry_safe(synx_cb,
temp_synx_cb, &row->callback_list, list) {
synx_cb->status = state;
list_del_init(&synx_cb->list);
queue_work(synx_dev->work_queue,
&synx_cb->cb_dispatch_work);
pr_debug("dispatched kernel cb\n");
}
/* add user payloads to eventq */
list_for_each_entry_safe(payload_info, temp_payload_info,
&row->user_payload_list, list) {
payload_info->data.status = state;
client = payload_info->client;
if (!client) {
pr_err("invalid client member in cb list\n");
continue;
}
spin_lock_bh(&client->eventq_lock);
list_move_tail(&payload_info->list, &client->eventq);
spin_unlock_bh(&client->eventq_lock);
/*
* since cb can be registered by multiple clients,
* wake the process right away
*/
wake_up_all(&client->wq);
pr_debug("dispatched user cb\n");
}
}
int synx_activate(struct synx_table_row *row)
{
if (!row)
return -EINVAL;
/* move synx to ACTIVE state and register cb */
dma_fence_enable_sw_signaling(row->fence);
return 0;
}
int synx_deinit_object(struct synx_table_row *row)
{
s32 synx_obj;
struct synx_callback_info *synx_cb, *temp_cb;
struct synx_cb_data *upayload_info, *temp_upayload;
if (!row)
return -EINVAL;
synx_obj = row->synx_obj;
if ((struct synx_table_row *)idr_replace(&synx_dev->synx_ids,
NULL, row->synx_obj) != row)
pr_err("replacing data in idr table failed\n");
/*
* release the fence memory only for individual obj.
* dma fence array will release all the allocated mem
* in its registered release function.
*/
if (!is_merged_synx(row))
kfree(row->fence);
list_for_each_entry_safe(upayload_info, temp_upayload,
&row->user_payload_list, list) {
pr_err("pending user callback payload\n");
list_del_init(&upayload_info->list);
kfree(upayload_info);
}
list_for_each_entry_safe(synx_cb, temp_cb,
&row->callback_list, list) {
pr_err("pending kernel callback payload\n");
list_del_init(&synx_cb->list);
kfree(synx_cb);
}
clear_bit(row->index, synx_dev->bitmap);
memset(row, 0, sizeof(*row));
pr_debug("destroying synx obj: 0x%x successful\n", synx_obj);
return 0;
}
u32 synx_add_reference(struct dma_fence *fence)
{
u32 count = 0;
u32 i = 0;
struct dma_fence_array *array = NULL;
/* obtain dma fence reference */
if (dma_fence_is_array(fence)) {
array = to_dma_fence_array(fence);
if (!array)
return 0;
for (i = 0; i < array->num_fences; i++)
dma_fence_get(array->fences[i]);
count = array->num_fences;
} else {
dma_fence_get(fence);
count = 1;
}
return count;
}
void synx_release_reference(struct dma_fence *fence)
{
struct dma_fence_array *array = NULL;
u32 i = 0;
if (dma_fence_is_array(fence)) {
array = to_dma_fence_array(fence);
if (!array)
return;
for (i = 0; i < array->num_fences; i++)
dma_fence_put(array->fences[i]);
} else {
dma_fence_put(fence);
}
}
u32 synx_fence_add(struct dma_fence *fence,
struct dma_fence **fences,
u32 idx)
{
struct dma_fence_array *array = NULL;
u32 i = 0;
if (dma_fence_is_array(fence)) {
array = to_dma_fence_array(fence);
if (!array)
return 0;
for (i = 0; i < array->num_fences; i++)
fences[idx+i] = array->fences[i];
return array->num_fences;
}
fences[idx] = fence;
return 1;
}
u32 synx_remove_duplicates(struct dma_fence **arr, u32 num)
{
int i, j;
u32 wr_idx = 1;
if (!arr) {
pr_err("invalid input array\n");
return 0;
}
for (i = 1; i < num; i++) {
for (j = 0; j < wr_idx ; j++) {
if (arr[i] == arr[j]) {
/* release reference obtained for duplicate */
dma_fence_put(arr[i]);
break;
}
}
if (j == wr_idx)
arr[wr_idx++] = arr[i];
}
return wr_idx;
}
s32 synx_merge_error(s32 *synx_objs, u32 num_objs)
{
struct synx_table_row *row = NULL;
u32 i = 0;
if (!synx_objs)
return -EINVAL;
for (i = 0; i < num_objs; i++) {
row = (struct synx_table_row *)synx_from_handle(synx_objs[i]);
if (!row) {
pr_err("invalid handle 0x%x\n", synx_objs[i]);
return -EINVAL;
}
spin_lock_bh(&synx_dev->row_spinlocks[row->index]);
synx_release_reference(row->fence);
spin_unlock_bh(&synx_dev->row_spinlocks[row->index]);
}
return 0;
}
int synx_util_validate_merge(s32 *synx_objs,
u32 num_objs,
struct dma_fence ***fence_list,
u32 *fence_cnt)
{
u32 count = 0;
u32 i = 0;
struct synx_table_row *row = NULL;
struct dma_fence **fences = NULL;
if (num_objs <= 1) {
pr_err("single object merge is not allowed\n");
return -EINVAL;
}
for (i = 0; i < num_objs; i++) {
row = (struct synx_table_row *)synx_from_handle(synx_objs[i]);
if (!row) {
pr_err("invalid handle 0x%x\n", synx_objs[i]);
*fence_cnt = i;
return -EINVAL;
}
spin_lock_bh(&synx_dev->row_spinlocks[row->index]);
count += synx_add_reference(row->fence);
spin_unlock_bh(&synx_dev->row_spinlocks[row->index]);
}
fences = kcalloc(count, sizeof(*fences), GFP_KERNEL);
if (!fences) {
*fence_cnt = num_objs;
return -ENOMEM;
}
*fence_list = fences;
count = 0;
for (i = 0; i < num_objs; i++) {
row = (struct synx_table_row *)synx_from_handle(synx_objs[i]);
if (!row) {
*fence_cnt = num_objs;
return -EINVAL;
}
spin_lock_bh(&synx_dev->row_spinlocks[row->index]);
count += synx_fence_add(row->fence, fences, count);
spin_unlock_bh(&synx_dev->row_spinlocks[row->index]);
}
/* eliminate duplicates */
*fence_cnt = synx_remove_duplicates(fences, count);
return 0;
}
void synx_util_cb_dispatch(struct work_struct *cb_dispatch_work)
{
struct synx_callback_info *cb_info = container_of(cb_dispatch_work,
struct synx_callback_info,
cb_dispatch_work);
cb_info->callback_func(cb_info->synx_obj,
cb_info->status,
cb_info->cb_data);
kfree(cb_info);
}
bool is_merged_synx(struct synx_table_row *row)
{
if (!row)
return false;
if (dma_fence_is_array(row->fence))
return true;
return false;
}
u32 __fence_state(struct dma_fence *fence, bool locked)
{
s32 status;
u32 state = SYNX_STATE_INVALID;
if (locked)
status = dma_fence_get_status_locked(fence);
else
status = dma_fence_get_status(fence);
/* convert fence status to synx state */
switch (status) {
case 0:
state = SYNX_STATE_ACTIVE;
break;
case 1:
state = SYNX_STATE_SIGNALED_SUCCESS;
break;
default:
state = SYNX_STATE_SIGNALED_ERROR;
}
return state;
}
u32 __fence_group_state(struct dma_fence *fence, bool locked)
{
u32 i = 0;
u32 state = SYNX_STATE_INVALID;
struct dma_fence_array *array = to_dma_fence_array(fence);
u32 intr, actv_cnt, sig_cnt, err_cnt;
actv_cnt = sig_cnt = err_cnt = 0;
if (!array)
return SYNX_STATE_INVALID;
for (i = 0; i < array->num_fences; i++) {
intr = __fence_state(array->fences[i], locked);
switch (intr) {
case SYNX_STATE_ACTIVE:
actv_cnt++;
break;
case SYNX_STATE_SIGNALED_SUCCESS:
sig_cnt++;
break;
default:
err_cnt++;
}
}
pr_debug("group cnt stats act:%u, sig: %u, err: %u\n",
actv_cnt, sig_cnt, err_cnt);
if (err_cnt)
state = SYNX_STATE_SIGNALED_ERROR;
else if (actv_cnt)
state = SYNX_STATE_ACTIVE;
else if (sig_cnt == array->num_fences)
state = SYNX_STATE_SIGNALED_SUCCESS;
return state;
}
/*
* WARN: Should not hold the synx spinlock when invoking
* this function. Use synx_fence_state_locked instead
*/
u32 synx_status(struct synx_table_row *row)
{
u32 state;
if (!row)
return SYNX_STATE_INVALID;
if (is_merged_synx(row))
state = __fence_group_state(row->fence, false);
else
state = __fence_state(row->fence, false);
return state;
}
/* use this for status check when holding on to metadata spinlock */
u32 synx_status_locked(struct synx_table_row *row)
{
u32 state;
if (!row)
return SYNX_STATE_INVALID;
if (is_merged_synx(row))
state = __fence_group_state(row->fence, true);
else
state = __fence_state(row->fence, true);
return state;
}
void *synx_from_handle(s32 synx_obj)
{
s32 base;
struct synx_table_row *row =
(struct synx_table_row *) idr_find(&synx_dev->synx_ids,
synx_obj);
if (!row) {
pr_err(
"synx handle does not exist 0x%x\n", synx_obj);
return NULL;
}
base = current->tgid << 16;
if ((base >> 16) != (synx_obj >> 16)) {
pr_err("current client: %d, base: %d, synx_obj: 0x%x\n",
current->tgid, base, synx_obj);
return NULL;
}
return row;
}
s32 synx_create_handle(void *pObj)
{
s32 base = current->tgid << 16;
s32 id = idr_alloc(&synx_dev->synx_ids, pObj,
base, base + 0x10000, GFP_ATOMIC);
pr_debug("generated Id: 0x%x, base: 0x%x, client: 0x%x\n",
id, base, current->tgid);
return id;
}
struct synx_client *get_current_client(void)
{
struct synx_client *client = NULL;
list_for_each_entry(client, &synx_dev->client_list, list) {
if (current->tgid == client->pid)
break;
}
return client;
}
int synx_generate_secure_key(struct synx_table_row *row)
{
if (!row)
return -EINVAL;
get_random_bytes(&row->secure_key, sizeof(row->secure_key));
return row->secure_key;
}
struct synx_table_row *synx_from_fence(struct dma_fence *fence)
{
s32 idx = 0;
struct synx_table_row *row = NULL;
struct synx_table_row *table = synx_dev->synx_table;
if (!fence)
return NULL;
for (idx = 0; idx < SYNX_MAX_OBJS; idx++) {
if (table[idx].fence == fence) {
row = table + idx;
pr_debug("synx global data found for 0x%x\n",
row->synx_obj);
break;
}
}
return row;
}
void *synx_from_key(s32 id, u32 secure_key)
{
struct synx_table_row *row = NULL;
row = (struct synx_table_row *) idr_find(&synx_dev->synx_ids, id);
if (!row) {
pr_err("invalid synx obj 0x%x\n", id);
return NULL;
}
if (row->secure_key != secure_key)
row = NULL;
return row;
}

View file

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#ifndef __SYNX_UTIL_H__
#define __SYNX_UTIL_H__
#include "synx_private.h"
extern struct synx_device *synx_dev;
/**
* @brief: Function to check if the external sync obj is valid
*
* @param type : External sync object type
*
* @return True if valid. False otherwise
*/
bool is_valid_type(u32 type);
/**
* @brief: Function to initialize an empty row in the synx table.
* It also initializes dma fence.
* This should be called only for individual objects.
*
* @param table : Pointer to the synx objects table
* @param idx : Index of row to initialize
* @param id : Id associated with the object
* @param name : Optional string representation of the synx object. Should be
* 63 characters or less
* @param ops : dma fence ops required for fence initialization
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_init_object(struct synx_table_row *table,
u32 idx,
s32 id,
const char *name,
struct dma_fence_ops *ops);
/**
* @brief: Function to uninitialize a row in the synx table.
*
* @param row : Pointer to the synx object row
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_deinit_object(struct synx_table_row *row);
/**
* @brief: Function to initialize a row in the synx table when the object is a
* a merged synx object. It also initializes dma fence array.
*
* @param table : Pointer to the synx objects table
* @param idx : Index of row to initialize
* @param id : Id associated with the object
* @param fences : Array of fence objects which will merged
* or grouped together to a fence array
* @param num_objs : Number of fence objects in the array
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_init_group_object(struct synx_table_row *table,
u32 idx,
s32 id,
struct dma_fence **fences,
u32 num_objs);
/**
* @brief: Function to activate the synx object. Moves the synx from INVALID
* state to ACTIVE state.
*
* @param row : Pointer to the synx object row
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_activate(struct synx_table_row *row);
/**
* @brief: Function to dispatch callbacks registered with
* the synx object.
*
* @param row : Pointer to the synx object row
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
void synx_callback_dispatch(struct synx_table_row *row);
/**
* @brief: Function to handle error during group synx obj initialization.
* Removes the references added on the fence objects.
*
* @param synx_objs : Array of synx objects to merge
* @param num_objs : Number of synx obects in the array
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
s32 synx_merge_error(s32 *synx_objs, u32 num_objs);
/**
* @brief: Function to validate synx merge arguments. It obtains the necessary
* references to the fence objects and also removes duplicates (if any).
*
* @param synx_objs : Array of synx objects to merge
* @param num_objs : Number of synx objects in the array
* @param fences : Address to a list of dma fence* array
* @param fence_cnt : Number of fence objects in the fences array
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_validate_merge(s32 *synx_objs,
u32 num_objs,
struct dma_fence ***fences,
u32 *fence_cnt);
/**
* @brief: Function to dispatch a kernel callback for a sync callback
*
* @param cb_dispatch_work : Pointer to the work_struct that needs to be
* dispatched
*
* @return None
*/
void synx_util_cb_dispatch(struct work_struct *cb_dispatch_work);
/**
* @brief: Function to check if the synx is a merged (grouped) object
*
* @param row : Pointer to the synx object row
*
* @return True if merged object. Otherwise false.
*/
bool is_merged_synx(struct synx_table_row *row);
/**
* @brief: Function to check the state of synx object.
* The row lock associated with the synx obj should not be
* held when invoking this function.
* Use synx_status_locked for state enquiry when holding lock.
*
* @param row : Pointer to the synx object row
*
* @return Status of the synx object.
*/
u32 synx_status(struct synx_table_row *row);
/**
* @brief: Function to check the state of synx object.
* The row lock associated with the synx obj should be
* held when invoking this function.
* Use synx_status for state enquiry when not holding lock.
*
* @param row : Pointer to the synx object row
*
* @return Status of the synx object.
*/
u32 synx_status_locked(struct synx_table_row *row);
/**
* @brief: Function to return the current client (active process)
*
* @return The current client
*/
struct synx_client *get_current_client(void);
/**
* @brief: Function to look up a synx handle
* It also verifies the authenticity of the request through
* the key provided.
*
* @param synx_id : Synx handle
* @param secure_key : Key to verify authenticity
* @return The synx table entry corresponding to the given synx ID
*/
void *synx_from_key(s32 synx_id, u32 secure_key);
/**
* @brief: Function to look up a synx handle using the backed dma fence
*
* @param fence : dma fence backing the synx object
* @return The synx table entry corresponding to the given dma fence.
* NULL otherwise.
*/
struct synx_table_row *synx_from_fence(struct dma_fence *fence);
/**
* @brief: Function to look up a synx handle
*
* @param synx_id : Synx handle
*
* @return The synx corresponding to the given handle or NULL if
* handle is invalid (or not permitted).
*/
void *synx_from_handle(s32 synx_id);
/**
* @brief: Function to create a new synx handle
*
* @param pObj : Object to be associated with the created handle
*
* @return The created handle
*/
s32 synx_create_handle(void *pObj);
/**
* @brief: Function to generate a secure key for authentication
* Used to verify the requests generated on synx objects
* not owned by the process.
*
* @param row : Pointer to the synx object row
*
* @return The created handle
*/
int synx_generate_secure_key(struct synx_table_row *row);
#endif /* __SYNX_UTIL_H__ */

View file

@ -14,3 +14,4 @@ header-y += cam_sync.h
header-y += cam_lrme.h
header-y += radio-iris.h
header-y += radio-iris-commands.h
header-y += synx.h

184
include/uapi/media/synx.h Normal file
View file

@ -0,0 +1,184 @@
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#ifndef __UAPI_SYNX_H__
#define __UAPI_SYNX_H__
#include <linux/types.h>
#include <linux/ioctl.h>
/* Size of opaque payload sent to kernel for safekeeping until signal time */
#define SYNX_USER_PAYLOAD_SIZE 4
#define SYNX_STATE_INVALID 0
#define SYNX_STATE_ACTIVE 1
#define SYNX_STATE_SIGNALED_SUCCESS 2
#define SYNX_STATE_SIGNALED_ERROR 3
#define SYNX_MAX_WAITING_SYNX 16
#define SYNX_CALLBACK_RESULT_SUCCESS 2
#define SYNX_CALLBACK_RESULT_FAILED 3
#define SYNX_CALLBACK_RESULT_CANCELED 4
/**
* type of external sync object
*
* SYNX_TYPE_CSL : Object is a CSL sync object
*/
#define SYNX_TYPE_CSL 0
#define SYNX_MAX_BIND_TYPES 1
/**
* struct synx_info - Sync object creation information
*
* @name : Optional string representation of the synx object
* @synx_obj : Sync object returned after creation in kernel
*/
struct synx_info {
char name[64];
__s32 synx_obj;
};
/**
* struct synx_userpayload_info - Payload info from user space
*
* @synx_obj: Sync object for which payload has to be registered for
* @reserved: Reserved
* @payload: Pointer to user payload
*/
struct synx_userpayload_info {
__s32 synx_obj;
__u32 reserved;
__u64 payload[SYNX_USER_PAYLOAD_SIZE];
};
/**
* struct synx_signal - Sync object signaling struct
*
* @synx_obj : Sync object to be signaled
* @synx_state : State of the synx object to which it should be signaled
*/
struct synx_signal {
__s32 synx_obj;
__u32 synx_state;
};
/**
* struct synx_merge - Merge information for synx objects
*
* @synx_objs : Pointer to synx object array to merge
* @num_objs : Number of objects in the array
* @merged : Merged synx object
*/
struct synx_merge {
__u64 synx_objs;
__u32 num_objs;
__s32 merged;
};
/**
* struct synx_wait - Sync object wait information
*
* @synx_obj : Sync object to wait on
* @reserved : Reserved
* @timeout_ms : Timeout in milliseconds
*/
struct synx_wait {
__s32 synx_obj;
__u32 reserved;
__u64 timeout_ms;
};
/**
* struct synx_external_desc - info of external sync object
*
* @type : Synx type
* @reserved : Reserved
* @id : Sync object id
*
*/
struct synx_external_desc {
__u32 type;
__u32 reserved;
__s32 id[2];
};
/**
* struct synx_bind - info for binding two synx objects
*
* @synx_obj : Synx object
* @Reserved : Reserved
* @ext_sync_desc : External synx to bind to
*
*/
struct synx_bind {
__s32 synx_obj;
__u32 reserved;
struct synx_external_desc ext_sync_desc;
};
/**
* struct synx_addrefcount - info for refcount increment
*
* @synx_obj : Synx object
* @count : Count to increment
*
*/
struct synx_addrefcount {
__s32 synx_obj;
__u32 count;
};
/**
* struct synx_id_info - info for import and export of a synx object
*
* @synx_obj : Synx object to be exported
* @secure_key : Secure key created in export and used in import
* @new_synx_obj : Synx object created in import
*
*/
struct synx_id_info {
__s32 synx_obj;
__u32 secure_key;
__s32 new_synx_obj;
__u32 padding;
};
/**
* struct synx_private_ioctl_arg - Sync driver ioctl argument
*
* @id : IOCTL command id
* @size : Size of command payload
* @result : Result of command execution
* @reserved : Reserved
* @ioctl_ptr : Pointer to user data
*/
struct synx_private_ioctl_arg {
__u32 id;
__u32 size;
__u32 result;
__u32 reserved;
__u64 ioctl_ptr;
};
#define SYNX_PRIVATE_MAGIC_NUM 's'
#define SYNX_PRIVATE_IOCTL_CMD \
_IOWR(SYNX_PRIVATE_MAGIC_NUM, 130, struct synx_private_ioctl_arg)
#define SYNX_CREATE 0
#define SYNX_RELEASE 1
#define SYNX_SIGNAL 2
#define SYNX_MERGE 3
#define SYNX_REGISTER_PAYLOAD 4
#define SYNX_DEREGISTER_PAYLOAD 5
#define SYNX_WAIT 6
#define SYNX_BIND 7
#define SYNX_ADDREFCOUNT 8
#define SYNX_GETSTATUS 9
#define SYNX_IMPORT 10
#define SYNX_EXPORT 11
#endif /* __UAPI_SYNX_H__ */