Merge "msm: synx: Porting synx driver to 5.x kernel"

This commit is contained in:
qctecmdr 2019-10-29 12:49:44 -07:00 • committed by Gerrit - the friendly Code Review server
commit c1640b59aa
13 changed files with 4041 additions and 0 deletions

View file

@ -686,3 +686,5 @@ config VIDEO_RCAR_DRIF
will be called rcar_drif.
endif # SDR_PLATFORM_DRIVERS
source "drivers/media/platform/msm/Kconfig"

View file

@ -101,3 +101,5 @@ obj-y += meson/
obj-y += cros-ec-cec/
obj-y += sunxi/
obj-y += msm/

View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/media/platform/msm/synx/Kconfig"

View file

@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx/

View file

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

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MSM_GLOBAL_SYNX) += synx-driver.o
synx-driver-y := synx.o synx_util.o synx_debugfs.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,344 @@
/* 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);
/**
* 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
* @enable_signaling : Function to enable the signaling on the external
* sync object (optional)
* @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 (*enable_signaling)(s32 sync_obj);
int (*signal)(s32 sync_obj, u32 status);
};
/**
* struct synx_register_params - External registration parameters
*
* @ops : Bind operations struct
* @name : External client name
* Only first 64 bytes are accepted, rest will be ignored
* @type : Synx external client type
*/
struct synx_register_params {
struct bind_operations ops;
char *name;
u32 type;
};
/**
* struct synx_session - Client session handle
*
* @client_id : Client id
*/
struct synx_session {
u32 client_id;
};
/**
* struct synx_initialization_params - Session params
*
* @name : Client session name
* Only first 64 bytes are accepted, rest will be ignored
*/
struct synx_initialization_params {
const char *name;
};
/**
* struct synx_create_params - Synx creation parameters
*
* @h_synx : Pointer to synx object handle (filled by function)
* @name : Optional parameter associating a name with the synx
* object for debug purposes
* Only first 64 bytes are accepted,
* rest will be ignored
*/
struct synx_create_params {
s32 *h_synx;
const char *name;
};
/**
* struct synx_export_params - Synx export parameters
*
* @h_synx : Synx object handle to export
* @secure_key : Pointer to Key generated for authentication
* (filled by the function)
*/
struct synx_export_params {
s32 h_synx;
u32 *secure_key;
};
/**
* struct synx_import_params - Synx import parameters
*
* @h_synx : Synx object handle to import
* @secure_key : Key to verify authenticity
* @new_h_synx : Pointer to newly created synx object
* (filled by the function)
* This handle should be used by importing
* process for all synx api operations
*/
struct synx_import_params {
s32 h_synx;
u32 secure_key;
s32 *new_h_synx;
};
/* Kernel APIs */
/* @brief: Register operations for external synchronization
*
* Register with synx for enabling external synchronization through bind
*
* @param params : Pointer to register params
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if params are invalid.
* -ENOMEM will be returned if bind ops cannot be registered due to
* insufficient memory.
* -EALREADY will be returned if type already in use
*/
int synx_register_ops(const struct synx_register_params *params);
/**
* @brief: De-register external synchronization operations
*
* @param params : Pointer to register params
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if record not found.
*/
int synx_deregister_ops(const struct synx_register_params *params);
/**
* @brief: Initializes a new client session
*
* Create a new client and provides access to the synx framework
*
* @param session_id : Pointer to client session id (filled by function)
* @param params : Pointer to session init params
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if params is not valid.
* -ENOMEM will be returned if the kernel can't allocate space for
* new client
*/
int synx_initialize(struct synx_session *session_id,
struct synx_initialization_params *params);
/**
* @brief: Destroys the client session
*
* The client session is destroyed and all synx objects owned
* by the client are cleaned up.
*
* @param session_id : Client session id
*
* @return Status of operation. Zero in case of success.
*/
int synx_uninitialize(struct synx_session session_id);
/**
* @brief: Creates a synx object
*
* Creates a new synx obj and returns the handle to client
*
* @param session_id : Client session id
* @param params : Pointer to create params
*
* @return Status of operation. Zero in case of success.
* -EINVAL will be returned if params were invalid.
* -ENOMEM will be returned if the kernel can't allocate space for
* synx object.
*/
int synx_create(struct synx_session session_id,
struct synx_create_params *params);
/**
* @brief: Registers a callback with a synx object
*
* @param session_id : Client session id
* @param h_synx : Synx object handle
* @param cb_func : Pointer to callback to be registered
* @param userdata : Opaque pointer passed back with callback
*
* @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(struct synx_session session_id,
s32 h_synx, synx_callback cb_func, void *userdata);
/**
* @brief: De-registers a callback with a synx object
*
* @param session_id : Client session id
* @param h_synx : Synx object handle
* @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(struct synx_session session_id,
s32 h_synx,
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 h_synx
* 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 session_id : Client session id
* @param h_synx : 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(struct synx_session session_id, s32 h_synx, u32 status);
/**
* @brief: Merges multiple synx objects
*
* This function will merge multiple synx objects into a synx group.
*
* @param session_id : Client session id
* @param h_synxs : Pointer to a block of synx handles to be merged
* @param num_objs : Number of synx objs in the block
* @param h_merged_obj : Merged synx object handle (filled by function)
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_merge(struct synx_session session_id,
s32 *h_synxs, u32 num_objs, s32 *h_merged_obj);
/**
* @brief: Waits for a synx object synchronously
*
* Does a wait on the synx object identified by h_synx 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 session_id : Client session id
* @param h_synx : Synx object handle to be waited upon
* @param timeout_ms : 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(struct synx_session session_id, s32 h_synx, u64 timeout_ms);
/**
* @brief: Binds synx object with external sync
*
* Binding synx objects with supported external sync object will unify them
* in a way that if one of them signals, the other ones is signaled as well
*
* @param session_id : Client session id
* @param h_synx : 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.
*/
int synx_bind(struct synx_session session_id, s32 h_synx,
struct synx_external_desc external_sync);
/**
* @brief: Returns the status of the synx object
*
* @param session_id : Client session id
* @param h_synx : Synx object handle
*
* @return Status of the synx object
*/
int synx_get_status(struct synx_session session_id, s32 h_synx);
/**
* @brief: Adds to the reference count of a synx object
*
* When a synx object is created, the refcount will be 1. Every
* additional refcount requires separate release of object handle.
*
* @param session_id : Client session id
* @param h_synx : 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(struct synx_session session_id,
s32 h_synx, s32 count);
/**
* @brief: Imports (looks up) synx object from given handle and key
*
* The given ID should have been exported by another client
* and provided with key.
*
* @param session_id : Client session id
* @param params : Pointer to import params
*
* @return 0 upon success, -EINVAL if synx object is bad state
*/
int synx_import(struct synx_session session_id,
struct synx_import_params *params);
/**
* @brief: Exports a synx object and returns a key
*
* The synx object handle and given key may be passed to other
* clients to be imported.
*
* @param session_id : Client session id
* @param params : Pointer to export params
*
* @return 0 upon success, -EINVAL if the ID is bad
*/
int synx_export(struct synx_session session_id,
struct synx_export_params *params);
/**
* @brief: Release the synx object
* Decrements refcount of a synx object by 1, and destroys it
* if becomes 0
*
* @param session_id : Client session id
* @param h_synx : Synx object handle to be destroyed
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_release(struct synx_session session_id, s32 h_synx);
#endif /* __SYNX_API_H__ */

View file

@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "synx_api.h"
#include "synx_debugfs.h"
#include "synx_util.h"
#define MAX_DBG_BUF_SIZE (36 * SYNX_MAX_OBJS)
struct dentry *my_direc;
static const char delim[] = ",";
int columns = NAME_COLUMN | ID_COLUMN |
BOUND_COLUMN | STATE_COLUMN | ERROR_CODES;
void populate_bound_rows(
struct synx_coredata *row,
char *cur,
char *end)
{
int j;
for (j = 0; j < row->num_bound_synxs;
j++) {
cur += scnprintf(cur, end - cur,
"\n\tID: %d",
row->bound_synxs[j].external_desc.id[0]);
}
}
static ssize_t synx_table_read(struct file *file,
char *buf,
size_t count,
loff_t *ppos)
{
struct synx_device *dev = file->private_data;
struct error_node *err_node, *err_node_tmp;
struct synx_client *client;
struct synx_coredata *row;
char *dbuf, *cur, *end;
int i = 0;
int j = 0;
int status = SYNX_STATE_INVALID;
ssize_t len = 0;
dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL);
if (!dbuf)
return -ENOMEM;
cur = dbuf;
end = cur + MAX_DBG_BUF_SIZE;
if (columns & NAME_COLUMN)
cur += scnprintf(cur, end - cur, "| Name |");
if (columns & ID_COLUMN)
cur += scnprintf(cur, end - cur, "| ID |");
if (columns & BOUND_COLUMN)
cur += scnprintf(cur, end - cur, "| Bound |");
if (columns & STATE_COLUMN)
cur += scnprintf(cur, end - cur, "| Status |");
cur += scnprintf(cur, end - cur, "\n");
for (j = 0; j < SYNX_MAX_CLIENTS; j++) {
struct synx_session session;
session.client_id = j;
client = synx_get_client(session);
if (!client)
continue;
cur += scnprintf(cur, end - cur,
"=============== session %08u ===============\n",
client->id);
for (i = 0; i < SYNX_MAX_OBJS; i++) {
row = synx_util_acquire_object(client, i);
if (!row)
continue;
spin_lock_bh(&row->lock);
if (columns & NAME_COLUMN)
cur += scnprintf(cur, end - cur,
"|%10s|", row->name);
if (columns & ID_COLUMN)
cur += scnprintf(cur, end - cur,
"|%10d|", i);
if (columns & BOUND_COLUMN)
cur += scnprintf(cur, end - cur,
"|%11d|", row->num_bound_synxs);
if (columns & STATE_COLUMN) {
status =
synx_util_get_object_status_locked(row);
cur += scnprintf(cur, end - cur,
"|%10d|", status);
}
if ((columns & BOUND_COLUMN) &&
(row->num_bound_synxs > 0)) {
cur += scnprintf(
cur, end - cur, "\nBound synx: ");
populate_bound_rows(row,
cur,
end);
}
spin_unlock_bh(&row->lock);
cur += scnprintf(cur, end - cur, "\n");
synx_util_release_object(client, i);
}
cur += scnprintf(cur, end - cur,
"\n================================================\n\n");
synx_put_client(client);
}
if (columns & ERROR_CODES && !list_empty(
&dev->error_list)) {
cur += scnprintf(
cur, end - cur, "\nError(s): ");
mutex_lock(&dev->error_lock);
list_for_each_entry_safe(
err_node, err_node_tmp,
&dev->error_list,
node) {
if (err_node->timestamp != NULL) {
cur += scnprintf(cur, end - cur,
"\n\tTime: %s - ID: %d - Code: %d",
err_node->timestamp,
err_node->h_synx,
err_node->error_code);
}
list_del(&err_node->node);
kfree(err_node);
}
mutex_unlock(&dev->error_lock);
}
len = simple_read_from_buffer(buf, count, ppos,
dbuf, cur - dbuf);
kfree(dbuf);
return len;
}
static ssize_t synx_table_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
char *ptr;
char *kbuffer = kzalloc(48, GFP_KERNEL);
int stat = -1;
if (!kbuffer)
return -ENOMEM;
stat = copy_from_user(kbuffer, buf, 48);
if (stat != 0) {
kfree(kbuffer);
return -EFAULT;
}
while ((ptr = strsep(&kbuffer, delim)) != NULL) {
ptr += '\0';
if (strcmp(ptr, "bound\n") == 0)
columns = columns ^ BOUND_COLUMN;
else if (strcmp(ptr, "name\n") == 0)
columns = columns ^ NAME_COLUMN;
else if (strcmp(ptr, "synxid\n") == 0)
columns = columns ^ ID_COLUMN;
else if (strcmp(ptr, "status\n") == 0)
columns = columns ^ STATE_COLUMN;
else if (strcmp(ptr, "errors\n") == 0)
columns = columns ^ ERROR_CODES;
}
kfree(kbuffer);
return count;
}
static const struct file_operations synx_table_fops = {
.owner = THIS_MODULE,
.read = synx_table_read,
.write = synx_table_write,
.open = simple_open,
};
struct dentry *synx_init_debugfs_dir(struct synx_device *dev)
{
struct dentry *dir = NULL;
dir = debugfs_create_dir("synx_debug", NULL);
if (!dir) {
pr_debug("Failed to create debugfs for synx\n");
return NULL;
}
if (!debugfs_create_file("synx_table",
0644, dir, dev, &synx_table_fops)) {
pr_debug("Failed to create debugfs file for synx\n");
return NULL;
}
mutex_init(&dev->error_lock);
INIT_LIST_HEAD(&dev->error_list);
return dir;
}
void synx_remove_debugfs_dir(struct synx_device *dev)
{
debugfs_remove_recursive(dev->debugfs_root);
}

View file

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include "synx_private.h"
/**
* @brief: Initializes debugfs
*
* @param dev : Pointer to synx device structure
*/
struct dentry *synx_init_debugfs_dir(struct synx_device *dev);
/**
* @brief: Removes debugfs
*
* @param dev : Pointer to synx device structure
*/
void synx_remove_debugfs_dir(struct synx_device *dev);
#define NAME_COLUMN 0x0001
#define ID_COLUMN 0x0002
#define BOUND_COLUMN 0x0004
#define STATE_COLUMN 0x0008
#define ERROR_CODES 0x8000

View file

@ -0,0 +1,303 @@
/* 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/workqueue.h>
#define SYNX_NAME "synx"
#define SYNX_WORKQUEUE_NAME "hiprio_synx_work_queue"
#define SYNX_WORKQUEUE_THREADS 1
#define SYNX_MAX_NUM_BINDINGS 8
#define SYNX_DEVICE_NAME "synx_device"
#define SYNX_CLIENT_HANDLE_SHIFT 8
#define SYNX_SECURE_KEY_SHIFT 16
#define MAX_TIMESTAMP_SIZE 32
#define SYNX_OBJ_NAME_LEN 64
#define SYNX_MAX_CLIENTS (1UL<<SYNX_CLIENT_HANDLE_SHIFT)
#define SYNX_MAX_OBJS 1024
#define SYNX_MAX_REF_COUNTS 2048
#define SYNX_PAYLOAD_WORDS 4
#define SYNX_SECURE_KEY_SIZE (1UL<<SYNX_SECURE_KEY_SHIFT)
#define SYNX_SECURE_KEY_MASK (SYNX_SECURE_KEY_SIZE-1)
#define SYNX_CLIENT_HANDLE_MASK (SYNX_MAX_CLIENTS-1)
#define SYNX_OBJ_TYPE_LOCAL 0x1
#define SYNX_OBJ_TYPE_MERGED 0x2
#define SYNX_STATE_FORCED_RELEASE 5
/**
* struct synx_external_data - data passed over to external sync objects
* to pass on callback
*
* @session_id : Synx client id
* @h_synx : Synx object handle
*/
struct synx_external_data {
struct synx_session session_id;
s32 h_synx;
};
/**
* struct synx_bind_desc - bind payload descriptor
*
* @external_desc : External bind information
* @external_data : Pointer to data passed over
*/
struct synx_bind_desc {
struct synx_external_desc external_desc;
struct synx_external_data *external_data;
};
/**
* struct error_node - Single error node related to a table_row
*
* @timestamp : Time that the error occurred
* @client_id : Synx client id
* @h_synx : Synx object handle
* @error_code : Code related to the error
* @node : List member used to append this node to error list
*/
struct error_node {
char timestamp[MAX_TIMESTAMP_SIZE];
u32 client_id;
s32 h_synx;
s32 error_code;
struct list_head node;
};
/**
* struct synx_kernel_payload - Single node of information about a kernel
* callback registered on a synx object
*
* @h_synx : Synx object handle
* @data : Callback data, passed by client driver
* @cb_func : Callback function, registered by client driver
* @cancel_cb_func : Cancellation callback function
*/
struct synx_kernel_payload {
s32 h_synx;
void *data;
synx_callback cb_func;
synx_callback cancel_cb_func;
};
/**
* struct synx_user_payload - Single node of information about a callback
* registered from user space on a synx object
*
* @client : Synx client structure
* @h_synx : Synx object handle
* @status : Synx obj status or callback failure
* @data : Payload data, opaque to kernel
*/
struct synx_user_payload {
struct synx_client *client;
s32 h_synx;
u32 status;
u64 data[SYNX_PAYLOAD_WORDS];
};
/**
* struct synx_cb_data - Single node of information about callback
* registered by client for the synx object
*
* @session_id : Synx client id
* @idx : Client payload table index
* @status : Synx obj status or callback failure
* @cb_dispatch : Work representing the callback dispatch
* @node : List member used to append this node to reg cb list
*/
struct synx_cb_data {
struct synx_session session_id;
u32 idx;
u32 status;
struct work_struct cb_dispatch;
struct list_head node;
};
/**
* struct synx_client_cb - Single node of information about cb payload
* registered by client
*
* @is_valid : True if this is a valid entry
* @is_kernel_cb : True for kernel callback, false for user callback
* @idx : Index of the client callback table
* @kernel_cb : Kernel callback payload
* @user_cb : Userspace callback payload
* @node : List member used to append this node to event list
*/
struct synx_client_cb {
bool is_valid;
u32 idx;
struct synx_kernel_payload kernel_cb;
struct synx_user_payload user_cb;
struct list_head node;
};
/**
* struct synx_registered_ops - External sync clients registered for bind
* operations with synx driver
*
* @name : Name of the external sync client
* @ops : Bind operations struct for the client
* @type : External client type
* @valid : Validity of the client registered bind ops
*/
struct synx_registered_ops {
char name[SYNX_OBJ_NAME_LEN];
struct bind_operations ops;
u32 type;
bool valid;
};
/**
* struct synx_coredata - Synx object, used for internal book keeping
* of all metadata associated with each individual synx object
*
* @name : Optional string representation of the synx object
* @fence : Dma fence backing the synx object
* @merged_fence : Pointer to dma fence backing merged synx object
* @lock : Spinlock
* @type : Synx object type
* @num_bound_synxs : Number of external bound synx objects
* @bound_synxs : Array of bound external sync objects
* @reg_cbs_list : List of all registered callbacks
*/
struct synx_coredata {
char name[SYNX_OBJ_NAME_LEN];
struct dma_fence fence;
struct dma_fence *merged_fence;
spinlock_t lock;
u32 type;
u32 num_bound_synxs;
struct synx_bind_desc bound_synxs[SYNX_MAX_NUM_BINDINGS];
struct list_head reg_cbs_list;
};
struct synx_client;
struct synx_device;
/**
* struct synx_handle_coredata - Internal struct to manage synx handle to
* synx object mapping along with tracking synx object usage by the client
* through reference counting
*
* @client : Pointer to client owning the synx object
* @synx_obj : Pointer to synx object
* @internal_refcount : References by the client
* @import_refcount : References for external clients for import
* @id : Synx object handle for the client
* @key : Key for import authentication
*/
struct synx_handle_coredata {
struct synx_client *client;
struct synx_coredata *synx_obj;
struct kref internal_refcount;
struct kref import_refcount;
u32 id;
u16 key;
};
/**
* struct synx_client - Internal struct to book keep each client
* details
*
* @device : Pointer to synx device structure
* @name : Optional string representation of the client
* @id : Unique client session id
* @event_q_lock : Mutex for the event queue
* @event_q : All the user callback payloads
* @event_wq : Wait queue for the polling process
* @cb_bitmap : Bitmap representation of all cb table entries
* @cb_table : Table of all registered callbacks by client
* @bitmap : Bitmap representation of all synx object handles
* @synx_table : Table of all synx objects
* @synx_table_lock : Mutex array, one for each row in the synx table
*/
struct synx_client {
struct synx_device *device;
char name[SYNX_OBJ_NAME_LEN];
u32 id;
struct mutex event_q_lock;
struct list_head event_q;
wait_queue_head_t event_wq;
DECLARE_BITMAP(cb_bitmap, SYNX_MAX_OBJS);
struct synx_client_cb cb_table[SYNX_MAX_OBJS];
DECLARE_BITMAP(bitmap, SYNX_MAX_OBJS);
struct synx_handle_coredata synx_table[SYNX_MAX_OBJS];
struct mutex synx_table_lock[SYNX_MAX_OBJS];
};
/**
* struct synx_client_metadata - Internal struct to map client id with
* client data along with tracking usage through reference counting
*
* @client : Pointer to client data
* @refcount : Outstanding references to client data
*/
struct synx_client_metadata {
struct synx_client *client;
struct kref refcount;
};
/**
* struct synx_device - Internal struct to book keep synx driver details
*
* @cdev : Character device
* @dev : Device type
* @class : Device class
* @table_lock : Mutex used to lock the table
* @bitmap : Bitmap representation of all synx clients
* @client_table : Table of all synx clients
* @work_queue : Work queue used for dispatching callbacks
* @dma_context : dma context id
* @vtbl_lock : Mutex used to lock the bind table
* @bind_vtbl : Table with registered bind ops for external sync
* @debugfs_root : Root directory for debugfs
* @error_list : List of all errors occurred
* @error_lock : Mutex used to modify the error list
*/
struct synx_device {
struct cdev cdev;
dev_t dev;
struct class *class;
struct mutex dev_table_lock;
DECLARE_BITMAP(bitmap, SYNX_MAX_CLIENTS);
struct synx_client_metadata client_table[SYNX_MAX_CLIENTS];
struct workqueue_struct *work_queue;
u64 dma_context;
struct mutex vtbl_lock;
struct synx_registered_ops bind_vtbl[SYNX_MAX_BIND_TYPES];
struct dentry *debugfs_root;
struct list_head error_list;
struct mutex error_lock;
};
/**
* @brief: Internal function to signal the synx object
*
* @param synx_obj : Pointer to the synx object to signal
* @param status : Signaling status
* @param cb_signal : If signaling invoked from external cb
* @param ext_sync_id : External sync id
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_signal_core(struct synx_coredata *synx_obj,
u32 status,
bool cb_signal,
s32 ext_sync_id);
#endif /* __SYNX_PRIVATE_H__ */

View file

@ -0,0 +1,992 @@
// 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"
extern void synx_external_callback(s32 sync_obj, int status, void *data);
bool synx_util_is_valid_bind_type(u32 type)
{
if (type < SYNX_MAX_BIND_TYPES)
return true;
return false;
}
u32 synx_util_get_object_type(struct synx_coredata *synx_obj)
{
if (!synx_obj)
return 0;
return synx_obj->type;
}
bool synx_util_is_merged_object(struct synx_coredata *synx_obj)
{
if (synx_util_get_object_type(synx_obj)
& SYNX_OBJ_TYPE_MERGED)
return true;
return false;
}
struct dma_fence *synx_util_get_fence(struct synx_coredata *synx_obj)
{
if (!synx_obj)
return NULL;
if (synx_util_is_merged_object(synx_obj))
return synx_obj->merged_fence;
return &synx_obj->fence;
}
void synx_util_get_object(struct synx_coredata *synx_obj)
{
struct dma_fence *fence;
if (!synx_obj)
return;
fence = synx_util_get_fence(synx_obj);
dma_fence_get(fence);
}
void synx_util_put_object(struct synx_coredata *synx_obj)
{
struct dma_fence *fence;
if (!synx_obj)
return;
fence = synx_util_get_fence(synx_obj);
dma_fence_put(fence);
}
int synx_util_init_coredata(struct synx_coredata *synx_obj,
struct synx_create_params *params,
struct dma_fence_ops *ops)
{
if (!synx_obj || !params || !ops) {
pr_err("invalid arguments\n");
return -EINVAL;
}
spin_lock_init(&synx_obj->lock);
dma_fence_init(&synx_obj->fence, ops,
&synx_obj->lock, synx_dev->dma_context, 1);
synx_obj->type = SYNX_OBJ_TYPE_LOCAL;
INIT_LIST_HEAD(&synx_obj->reg_cbs_list);
if (params->name)
strlcpy(synx_obj->name, params->name, sizeof(synx_obj->name));
return 0;
}
int synx_util_init_group_coredata(struct synx_coredata *synx_obj,
struct dma_fence **fences,
u32 num_objs)
{
struct dma_fence_array *array;
if (!synx_obj)
return -EINVAL;
array = dma_fence_array_create(num_objs, fences,
synx_dev->dma_context, 1, false);
if (!array)
return -EINVAL;
synx_obj->merged_fence = &array->base;
synx_obj->type = SYNX_OBJ_TYPE_LOCAL | SYNX_OBJ_TYPE_MERGED;
INIT_LIST_HEAD(&synx_obj->reg_cbs_list);
return 0;
}
void synx_util_object_destroy(struct synx_coredata *synx_obj)
{
int rc;
u32 i;
s32 sync_id;
u32 type;
struct synx_cb_data *synx_cb, *synx_cb_temp;
struct synx_bind_desc *bind_desc;
struct bind_operations *bind_ops;
struct synx_external_data *data;
/* clear all the undispatched callbacks */
list_for_each_entry_safe(synx_cb,
synx_cb_temp, &synx_obj->reg_cbs_list, node) {
pr_err("[sess: %u] cleaning up callback\n",
synx_cb->session_id.client_id);
list_del_init(&synx_cb->node);
kfree(synx_cb);
}
for (i = 0; i < synx_obj->num_bound_synxs; i++) {
bind_desc = &synx_obj->bound_synxs[i];
sync_id = bind_desc->external_desc.id[0];
type = bind_desc->external_desc.type;
data = bind_desc->external_data;
bind_ops = synx_util_get_bind_ops(type);
rc = bind_ops->deregister_callback(
synx_external_callback, data, sync_id);
if (rc < 0)
pr_err("de-registration fail id: %d, type: %u, err: %d\n",
sync_id, type, rc);
/*
* release the memory allocated for external data.
* It is safe to release this memory as external cb
* has been already deregistered before this.
* even if deregistration fails, it implies the
* external fence might have been cleaned up already
* or in some bad state. Have to release cb data
* at this point or else will lead to memory leak.
*/
kfree(data);
}
kfree(synx_obj);
pr_debug("cleaned up synx object fence\n");
}
int synx_util_init_handle(struct synx_client *client,
struct synx_coredata *synx_obj,
long *h_synx)
{
bool bit;
long idx;
struct synx_handle_coredata *synx_data;
if (!client || !synx_obj)
return -EINVAL;
do {
idx = find_first_zero_bit(client->bitmap, SYNX_MAX_OBJS);
if (idx >= SYNX_MAX_OBJS) {
pr_err("[sess: %u] free index not available\n",
client->id);
return -ENOMEM;
}
bit = test_and_set_bit(idx, client->bitmap);
} while (bit);
synx_data = &client->synx_table[idx];
synx_data->client = client;
synx_data->id = idx;
synx_data->synx_obj = synx_obj;
kref_init(&synx_data->internal_refcount);
*h_synx = idx;
return 0;
}
int synx_util_activate(struct synx_coredata *synx_obj)
{
struct dma_fence *fence;
if (!synx_obj)
return -EINVAL;
fence = synx_util_get_fence(synx_obj);
/* move synx to ACTIVE state and register cb for merged object */
dma_fence_enable_sw_signaling(fence);
return 0;
}
static u32 synx_util_get_references(struct synx_coredata *synx_obj)
{
u32 count = 0;
u32 i = 0;
struct dma_fence *fence;
struct dma_fence_array *array = NULL;
fence = synx_util_get_fence(synx_obj);
/* 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;
}
static void synx_util_put_references(struct synx_coredata *synx_obj)
{
u32 i = 0;
struct dma_fence *fence;
struct dma_fence_array *array = NULL;
fence = synx_util_get_fence(synx_obj);
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);
}
}
static u32 synx_util_add_fence(struct synx_coredata *synx_obj,
struct dma_fence **fences,
u32 idx)
{
struct dma_fence *fence;
struct dma_fence_array *array = NULL;
u32 i = 0;
fence = synx_util_get_fence(synx_obj);
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;
}
static u32 synx_util_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 */
pr_debug("releasing duplicate reference\n");
dma_fence_put(arr[i]);
break;
}
}
if (j == wr_idx)
arr[wr_idx++] = arr[i];
}
return wr_idx;
}
s32 synx_util_merge_error(struct synx_client *client,
s32 *h_synxs,
u32 num_objs)
{
u32 i = 0;
struct synx_coredata *synx_obj;
if (!client || !h_synxs)
return -EINVAL;
for (i = 0; i < num_objs; i++) {
synx_obj = synx_util_acquire_object(client, h_synxs[i]);
if (!synx_obj) {
pr_err("[sess: %u] invalid handle %d in merge cleanup\n",
client->id, h_synxs[i]);
continue;
}
/* release all references obtained during merge validatation */
synx_util_put_references(synx_obj);
synx_util_release_object(client, h_synxs[i]);
}
return 0;
}
int synx_util_validate_merge(struct synx_client *client,
s32 *h_synxs,
u32 num_objs,
struct dma_fence ***fence_list,
u32 *fence_cnt)
{
u32 count = 0;
u32 i = 0;
struct synx_coredata **synx_objs = NULL;
struct dma_fence **fences = NULL;
if (num_objs <= 1) {
pr_err("single object merge is not allowed\n");
return -EINVAL;
}
synx_objs = kcalloc(num_objs, sizeof(*synx_objs), GFP_KERNEL);
if (!synx_objs)
return -ENOMEM;
for (i = 0; i < num_objs; i++) {
synx_objs[i] = synx_util_acquire_object(client, h_synxs[i]);
if (!synx_objs) {
pr_err("[sess: %u] invalid handle %d in merge list\n",
client->id, h_synxs[i]);
*fence_cnt = i;
return -EINVAL;
}
count += synx_util_get_references(synx_objs[i]);
}
fences = kcalloc(count, sizeof(*fences), GFP_KERNEL);
if (!fences) {
*fence_cnt = num_objs;
return -ENOMEM;
}
/* memory will be released later in the invoking function */
*fence_list = fences;
count = 0;
for (i = 0; i < num_objs; i++) {
count += synx_util_add_fence(synx_objs[i], fences, count);
/* release the reference obtained earlier in the function */
synx_util_release_object(client, h_synxs[i]);
}
*fence_cnt = synx_util_remove_duplicates(fences, count);
kfree(synx_objs);
return 0;
}
static u32 __fence_state(struct dma_fence *fence, bool locked)
{
s32 status;
u32 state = SYNX_STATE_INVALID;
if (!fence) {
pr_err("invalid dma fence addr\n");
return 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;
case -SYNX_STATE_SIGNALED_CANCEL:
state = SYNX_STATE_SIGNALED_CANCEL;
break;
case -SYNX_STATE_FORCED_RELEASE:
state = SYNX_STATE_FORCED_RELEASE;
break;
case -SYNX_STATE_SIGNALED_ERROR:
default:
state = SYNX_STATE_SIGNALED_ERROR;
}
return state;
}
static u32 __fence_group_state(struct dma_fence *fence, bool locked)
{
u32 i = 0;
u32 state = SYNX_STATE_INVALID;
struct dma_fence_array *array = NULL;
u32 intr, actv_cnt, sig_cnt, err_cnt;
if (!fence) {
pr_err("invalid dma fence addr\n");
return SYNX_STATE_INVALID;
}
actv_cnt = sig_cnt = err_cnt = 0;
array = to_dma_fence_array(fence);
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_util_get_object_status(struct synx_coredata *synx_obj)
{
u32 state;
struct dma_fence *fence;
if (!synx_obj)
return SYNX_STATE_INVALID;
fence = synx_util_get_fence(synx_obj);
if (synx_util_is_merged_object(synx_obj))
state = __fence_group_state(fence, false);
else
state = __fence_state(fence, false);
return state;
}
/* use this for status check when holding on to metadata spinlock */
u32 synx_util_get_object_status_locked(struct synx_coredata *synx_obj)
{
u32 state;
struct dma_fence *fence;
if (!synx_obj)
return SYNX_STATE_INVALID;
fence = synx_util_get_fence(synx_obj);
if (synx_util_is_merged_object(synx_obj))
state = __fence_group_state(fence, true);
else
state = __fence_state(fence, true);
return state;
}
struct synx_handle_coredata *synx_util_obtain_handle(
struct synx_client *client,
s32 h_synx)
{
if (!client) {
pr_err("invalid session argument\n");
return NULL;
}
if (h_synx < 0 || h_synx >= SYNX_MAX_OBJS) {
pr_err("[sess: %u] invalid handle %d access\n",
client->id, h_synx);
return NULL;
}
return &client->synx_table[h_synx];
}
struct synx_coredata *synx_util_acquire_object(
struct synx_client *client, s32 h_synx)
{
struct synx_coredata *synx_obj = NULL;
struct synx_handle_coredata *synx_data =
synx_util_obtain_handle(client, h_synx);
if (!synx_data)
return NULL;
mutex_lock(&client->synx_table_lock[h_synx]);
synx_obj = synx_data->synx_obj;
if (synx_obj) {
synx_util_get_object(synx_obj);
kref_get(&synx_data->internal_refcount);
pr_debug("[sess: %u] acquired synx object for handle %d\n",
client->id, h_synx);
}
mutex_unlock(&client->synx_table_lock[h_synx]);
return synx_obj;
}
static void synx_util_destroy_handle(struct synx_handle_coredata *synx_data)
{
long idx = synx_data->id;
struct synx_client *client = synx_data->client;
if (synx_util_is_merged_object(synx_data->synx_obj)) {
pr_debug("cleaned up synx object fence\n");
kfree(synx_data->synx_obj);
}
memset(synx_data, 0, sizeof(*synx_data));
clear_bit(idx, client->bitmap);
pr_debug("[sess: %u] handle %d destroyed\n",
client->id, idx);
}
void synx_util_destroy_import_handle(struct kref *kref)
{
struct synx_handle_coredata *synx_data =
container_of(kref, struct synx_handle_coredata,
import_refcount);
pr_debug("[sess: %u] import handle cleanup for %d\n",
synx_data->client->id, synx_data->id);
/* in case of pending internal references, abort clean up */
if (kref_read(&synx_data->internal_refcount))
return;
synx_util_destroy_handle(synx_data);
}
void synx_util_destroy_internal_handle(struct kref *kref)
{
struct synx_handle_coredata *synx_data =
container_of(kref, struct synx_handle_coredata,
internal_refcount);
pr_debug("[sess: %u] internal handle cleanup for %d\n",
synx_data->client->id, synx_data->id);
/* in case of pending imports, abort clean up */
if (kref_read(&synx_data->import_refcount))
return;
synx_util_destroy_handle(synx_data);
}
void synx_util_release_object(struct synx_client *client, s32 h_synx)
{
struct synx_handle_coredata *synx_data =
synx_util_obtain_handle(client, h_synx);
struct synx_coredata *synx_obj;
struct dma_fence *fence;
if (!synx_data)
return;
mutex_lock(&client->synx_table_lock[h_synx]);
synx_obj = synx_data->synx_obj;
if (synx_obj) {
fence = synx_util_get_fence(synx_obj);
kref_put(&synx_data->internal_refcount,
synx_util_destroy_internal_handle);
dma_fence_put(fence);
}
mutex_unlock(&client->synx_table_lock[h_synx]);
}
struct bind_operations *synx_util_get_bind_ops(u32 type)
{
struct synx_registered_ops *client_ops;
if (!synx_util_is_valid_bind_type(type))
return NULL;
mutex_lock(&synx_dev->vtbl_lock);
client_ops = &synx_dev->bind_vtbl[type];
if (!client_ops->valid) {
mutex_unlock(&synx_dev->vtbl_lock);
return NULL;
}
mutex_unlock(&synx_dev->vtbl_lock);
return &client_ops->ops;
}
int synx_util_alloc_cb_entry(struct synx_client *client,
u32 *cb_idx)
{
bool bit;
long idx;
struct synx_client_cb *cb;
if (!client || !cb_idx)
return -EINVAL;
do {
idx = find_first_zero_bit(client->cb_bitmap, SYNX_MAX_OBJS);
if (idx >= SYNX_MAX_OBJS) {
pr_err("[sess: %u] free cb index not available\n",
client->id);
return -ENOMEM;
}
bit = test_and_set_bit(idx, client->cb_bitmap);
} while (bit);
cb = &client->cb_table[idx];
memset(cb, 0, sizeof(*cb));
cb->idx = idx;
*cb_idx = idx;
pr_debug("[sess: %u] allocated cb index %u\n", client->id, idx);
return 0;
}
int synx_util_update_cb_entry(struct synx_client *client,
void *data,
u32 cb_idx)
{
struct synx_client_cb *cb;
if (!client || !data || (cb_idx >= SYNX_MAX_OBJS))
return -EINVAL;
cb = &client->cb_table[cb_idx];
cb->is_valid = true;
memcpy(&cb->kernel_cb, data,
sizeof(cb->kernel_cb));
pr_debug("[sess: %u] updated cb index %u\n", client->id, cb_idx);
return 0;
}
void synx_util_callback_dispatch(struct synx_coredata *synx_obj, u32 status)
{
struct synx_cb_data *synx_cb, *synx_cb_temp;
if (!synx_obj) {
pr_err("invalid arguments\n");
return;
}
list_for_each_entry_safe(synx_cb,
synx_cb_temp, &synx_obj->reg_cbs_list, node) {
synx_cb->status = status;
list_del_init(&synx_cb->node);
queue_work(synx_dev->work_queue,
&synx_cb->cb_dispatch);
pr_debug("dispatched callback\n");
}
}
void synx_util_cb_dispatch(struct work_struct *cb_dispatch)
{
struct synx_cb_data *synx_cb =
container_of(cb_dispatch, struct synx_cb_data, cb_dispatch);
struct synx_client *client;
struct synx_client_cb *cb;
struct synx_kernel_payload *payload;
client = synx_get_client(synx_cb->session_id);
if (!client) {
pr_err("invalid session data %u in cb payload\n",
synx_cb->session_id);
goto free;
}
if (synx_cb->idx >= SYNX_MAX_OBJS) {
pr_err("[sess: %u] invalid cb index\n",
client->id);
goto fail;
}
cb = &client->cb_table[synx_cb->idx];
if (!cb->is_valid) {
pr_err("invalid cb payload\n");
goto fail;
}
payload = &cb->kernel_cb;
pr_debug("[sess: %u] kernel cb dispatch for handle %d\n",
client->id, payload->h_synx);
/* dispatch kernel callback */
payload->cb_func(payload->h_synx,
synx_cb->status,
payload->data);
fail:
synx_put_client(client);
free:
pr_debug("released synx_cb_data memory\n");
kfree(synx_cb);
}
struct synx_coredata *synx_util_import_object(struct synx_import_params *params)
{
u16 key;
s32 h_synx;
u32 secure_key;
struct synx_session ex_session_id;
struct synx_client *ex_client;
struct synx_handle_coredata *synx_data;
struct synx_coredata *synx_obj = NULL;
if (!params)
return NULL;
h_synx = params->h_synx;
secure_key = params->secure_key;
ex_session_id.client_id = secure_key & SYNX_CLIENT_HANDLE_MASK;
key = (secure_key >> SYNX_CLIENT_HANDLE_SHIFT) & SYNX_SECURE_KEY_MASK;
/* get the client exporting the synx handle */
ex_client = synx_get_client(ex_session_id);
if (!ex_client) {
pr_err("sess: %u invalid import handle %d and/or key %u\n",
ex_session_id.client_id, h_synx, secure_key);
return NULL;
}
synx_data = synx_util_obtain_handle(ex_client, h_synx);
if (!synx_data) {
pr_err("[sess: %u] invalid import handle %d\n",
ex_client->id, h_synx);
goto fail;
}
mutex_lock(&ex_client->synx_table_lock[h_synx]);
/* need to check whether import is accounted for in import_refcount */
if (synx_data->synx_obj &&
key && (key == synx_data->key) &&
kref_read(&synx_data->import_refcount)) {
if (synx_util_is_merged_object(synx_data->synx_obj)) {
/*
* need to copy the synx coredata for merged object
* as we need to clean up the coredata on release
* since fence array cannot be registered with
* synx_fence_release function.
*/
synx_obj = kzalloc(sizeof(*synx_obj), GFP_KERNEL);
if (synx_obj)
memcpy(synx_obj, synx_data->synx_obj,
sizeof(*synx_obj));
} else {
synx_obj = synx_data->synx_obj;
}
/* release the reference obtained during export */
kref_put(&synx_data->import_refcount,
synx_util_destroy_import_handle);
pr_debug("sess: %u handle %d import successful\n",
ex_client->id, h_synx);
}
mutex_unlock(&ex_client->synx_table_lock[h_synx]);
fail:
synx_put_client(ex_client);
return synx_obj;
}
int synx_util_export_object(struct synx_client *client,
struct synx_export_params *params)
{
int rc = 0;
u16 key = 0;
s32 h_synx;
struct synx_handle_coredata *synx_data;
if (!params || !params->secure_key)
return -EINVAL;
h_synx = params->h_synx;
synx_data = synx_util_obtain_handle(client, h_synx);
if (!synx_data) {
pr_err("[sess: %u] invalid export handle %d\n",
client->id, h_synx);
rc = -EINVAL;
goto fail;
}
mutex_lock(&client->synx_table_lock[h_synx]);
/* need to check whether import is accounted for in import_refcount */
if (synx_data->synx_obj) {
/* generate the key on the first export of the handle */
if (!synx_data->key) {
kref_init(&synx_data->import_refcount);
while (!key)
get_random_bytes(&key, sizeof(key));
synx_data->key = key;
pr_debug("[sess: %u] import refcount initialized\n",
client->id);
} else {
kref_get(&synx_data->import_refcount);
key = synx_data->key;
}
/*
* to make sure the synx is not lost if the process
* dies or synx is released before any other process
* gets a chance to import it.
* The assumption is that an import will match this
* and account for the extra reference. Otherwise,
* this will be released upon client uninit.
*/
synx_util_get_object(synx_data->synx_obj);
/* encode the key and the client id to user provided variable */
*params->secure_key = key;
*params->secure_key <<= SYNX_CLIENT_HANDLE_SHIFT;
*params->secure_key |= client->id;
pr_debug("[sess: %u] handle %d export successful with key %u\n",
client->id, params->h_synx, *params->secure_key);
} else {
pr_err("[sess: %u] invalid export object %d\n",
client->id, h_synx);
rc = -EINVAL;
}
mutex_unlock(&client->synx_table_lock[h_synx]);
fail:
return rc;
}
struct synx_client *synx_get_client(struct synx_session session_id)
{
struct synx_client_metadata *client_metadata;
struct synx_client *client;
u32 id = session_id.client_id;
if (id >= SYNX_MAX_CLIENTS) {
pr_err("%s: invalid session handle %u from pid: %d\n",
__func__, id, current->pid);
return NULL;
}
mutex_lock(&synx_dev->dev_table_lock);
client_metadata = &synx_dev->client_table[id];
client = client_metadata->client;
if (client)
kref_get(&client_metadata->refcount);
else
pr_err("session %u not available, pid: %d\n",
id, current->pid);
mutex_unlock(&synx_dev->dev_table_lock);
return client;
}
static void synx_client_destroy(struct kref *kref)
{
u32 i;
struct synx_client_metadata *client_metadata =
container_of(kref, struct synx_client_metadata, refcount);
struct synx_client *client = client_metadata->client;
struct synx_handle_coredata *synx_data;
struct synx_coredata *synx_obj;
struct dma_fence *fence;
/* TODO:can create a thread and handle it */
/* go over all the remaining synx obj handles and clear them */
for (i = 0; i < SYNX_MAX_OBJS; i++) {
synx_data = &client->synx_table[i];
synx_obj = synx_data->synx_obj;
/*
* cleanup unreleased references by the client
* Note: it is only safe to access synx_obj if
* there are refcounts (internal, import)
* remaining in the current handle, as it
* gurantees corresponding reference to fence.
*/
if (synx_obj) {
fence = synx_util_get_fence(synx_obj);
while (kref_read(&synx_data->internal_refcount)) {
kref_put(&synx_data->internal_refcount,
synx_util_destroy_internal_handle);
dma_fence_put(fence);
}
while (kref_read(&synx_data->import_refcount)) {
kref_put(&synx_data->import_refcount,
synx_util_destroy_import_handle);
dma_fence_put(fence);
}
}
mutex_destroy(&client->synx_table_lock[i]);
}
mutex_destroy(&client->event_q_lock);
memset(client_metadata, 0, sizeof(*client_metadata));
clear_bit(client->id, synx_dev->bitmap);
pr_debug("[sess: %u] session destroyed %s\n",
client->id, client->name);
kfree(client);
}
void synx_put_client(struct synx_client *client)
{
struct synx_client_metadata *client_metadata;
if (!client) {
pr_err("%s: invalid client ptr\n", __func__);
return;
}
if (client->id >= SYNX_MAX_CLIENTS) {
pr_err("%s: session id %u invalid from pid: %d\n",
__func__, client->id, current->pid);
return;
}
mutex_lock(&synx_dev->dev_table_lock);
client_metadata = &synx_dev->client_table[client->id];
if (client_metadata->client == client)
/* should not reference client after this call */
kref_put(&client_metadata->refcount, synx_client_destroy);
else
pr_err("%s: invalid session %u from pid: %d\n",
__func__, client->id, current->pid);
mutex_unlock(&synx_dev->dev_table_lock);
}
void synx_util_generate_timestamp(char *timestamp, size_t size)
{
struct timespec64 tv;
struct tm tm;
ktime_get_real_ts64(&tv);
time64_to_tm(tv.tv_sec, 0, &tm);
snprintf(timestamp, size, "%02d-%02d %02d:%02d:%02d",
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
}
void synx_util_log_error(u32 client_id, s32 h_synx, s32 err)
{
struct error_node *err_node;
if (!synx_dev->debugfs_root)
return;
err_node = kzalloc(sizeof(*err_node), GFP_KERNEL);
if (!err_node)
return;
err_node->client_id = client_id;
err_node->error_code = err;
err_node->h_synx = h_synx;
synx_util_generate_timestamp(err_node->timestamp,
sizeof(err_node->timestamp));
mutex_lock(&synx_dev->error_lock);
list_add(&err_node->node,
&synx_dev->error_list);
mutex_unlock(&synx_dev->error_lock);
}

View file

@ -0,0 +1,365 @@
/* 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 obj type is valid
*
* @param type : External sync object type
*
* @return True if valid. False otherwise
*/
bool synx_util_is_valid_bind_type(u32 type);
/**
* @brief: Function to get the synx object type
*
* @param synx_obj : Pointer to synx object
*
* @return Synx object type. Zero if invalid
*/
u32 synx_util_get_object_type(struct synx_coredata *synx_obj);
/**
* @brief: Function to check if the synx object is a composite (merged)
*
* @param synx_obj : Pointer to synx object
*
* @return True if merged object. False otherwise
*/
bool synx_util_is_merged_object(struct synx_coredata *synx_obj);
/**
* @brief: Function to get the dma fence backing the synx object
*
* @param synx_obj : Pointer to synx object
*
* @return Dma fence. NULL if synx_obj is invalid
*/
struct dma_fence *synx_util_get_fence(
struct synx_coredata *synx_obj);
/**
* @brief: Function to acquire reference to synx object
*
* This acquires additional reference to the synx object and should be
* released using synx_util_put_object
*
* @param synx_obj : Pointer to synx object
*/
void synx_util_get_object(struct synx_coredata *synx_obj);
/**
* @brief: Function to release reference to synx object
*
* This releases reference to the synx object and should be
* matched with synx_util_get_object
*
* @param synx_obj : Pointer to synx object
*/
void synx_util_put_object(struct synx_coredata *synx_obj);
/**
* @brief: Function to initialize synx object
*
* It also initializes the dma fence. This should be called only
* for individual objects.
*
* @param synx_obj : Pointer to synx object
* @param params : Pointer to create params
* @param ops : Dma fence ops required for fence initialization
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_init_coredata(struct synx_coredata *synx_obj,
struct synx_create_params *params,
struct dma_fence_ops *ops);
/**
* @brief: Function to initialize a merged synx object.
*
* It also initializes dma fence array.
*
* @param synx_obj : Pointer to synx 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_util_init_group_coredata(struct synx_coredata *synx_obj,
struct dma_fence **fences,
u32 num_objs);
/**
* @brief: Function to cleanup the synx object
*
* This function will be invoked when the dma fence ref count reaches 0.
*
* @param synx_obj : Pointer to synx object
*/
void synx_util_object_destroy(struct synx_coredata *synx_obj);
/**
* @brief: Function to allocate synx_client_cb entry from cb table
*
* @param client : Pointer to client session info
* @param cb_idx : Allocated index (filled by function)
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_alloc_cb_entry(struct synx_client *client,
u32 *cb_idx);
/**
* @brief: Function to allocate synx_client_cb entry from cb table
*
* @param client : Pointer to client session info
* @param data : Opaque payload data
* @param cb_idx : Callback table index
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_update_cb_entry(struct synx_client *client,
void *data,
u32 cb_idx);
/**
* @brief: Function to initialize synx object handle for the client
*
* @param client : Pointer to client session info
* @param synx_obj : Pointer to synx object
* @param h_synx : Pointer to synx object handle (filled by function)
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_init_handle(struct synx_client *client,
struct synx_coredata *synx_obj,
long *h_synx);
/**
* @brief: Function to activate the synx object. Moves the synx from INVALID
* state to ACTIVE state.
*
* @param synx_obj : Pointer to synx object
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_activate(struct synx_coredata *synx_obj);
/**
* @brief: Function to queue all the registered callbacks by clients for
* the synx object
*
* @param synx_obj : Pointer to synx object
* @param state : State of the object
*/
void synx_util_callback_dispatch(struct synx_coredata *synx_obj, u32 state);
/**
* @brief: Function to dispatch client registered callback
*
* @param cb_dispatch : Pointer to work_struct that needs to be dispatched
*/
void synx_util_cb_dispatch(struct work_struct *cb_dispatch);
/**
* @brief: Function to handle error during merging of synx objects
*
* Removes the references added on the dma fence objects
*
* @param client : Pointer to client session
* @param h_synxs : Array of synx object handles 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_util_merge_error(struct synx_client *client,
s32 *h_synxs,
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 client : Pointer to client session
* @param h_synxs : Array of synx object handles 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
* (filled by the function)
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_validate_merge(struct synx_client *client,
s32 *h_synxs,
u32 num_objs,
struct dma_fence ***fences,
u32 *fence_cnt);
/**
* @brief: Function to obtain the state of synx object
*
* The lock associated with the synx obj should not be
* held when invoking this function.
* Use synx_util_get_object_status_locked for state enquiry
* when holding lock
*
* @param synx_obj : Pointer to synx object
*
* @return Status of the synx object.
*/
u32 synx_util_get_object_status(struct synx_coredata *synx_obj);
/**
* @brief: Function to obtain the state of synx object
*
* The lock associated with the synx obj should be held
* when invoking this function.
* Use synx_util_get_object_status for state enquiry
* when not holding lock
*
* @param synx_obj : Pointer to synx object
*
* @return Status of the synx object.
*/
u32 synx_util_get_object_status_locked(struct synx_coredata *synx_obj);
/**
* @brief: Function to obtain the synx object metadata for the client
*
* @param client : Pointer to client session
* @param h_synx : Synx object handle
*
* @return Pointer to synx object metadata for the client.
* NULL if invalid synx handle.
*/
struct synx_handle_coredata *synx_util_obtain_handle(
struct synx_client *client, s32 h_synx);
/**
* @brief: Function to acquire the synx object
*
* This function increments the reference count of the dma fence.
*
* @param client : Pointer to client session
* @param h_synx : Synx object handle
*
* @return Pointer to synx object. NULL if invalid synx handle.
*/
struct synx_coredata *synx_util_acquire_object(
struct synx_client *client, s32 h_synx);
/**
* @brief: Function to release the synx object
*
* This function decrements the reference count of the dma fence and
* implicitly calls up the synx object cleanup if count reaches 0.
* Synx object should not be derefrenced in the function after
* calling this.
*
* @param client : Pointer to client session
* @param h_synx : Synx object handle
*/
void synx_util_release_object(struct synx_client *client, s32 h_synx);
/**
* @brief: Function called implicitly when import refcount reaches zero
*
* @param kref : &synx_handle_coredata.import_refcount
*/
void synx_util_destroy_import_handle(struct kref *kref);
/**
* @brief: Function called implicitly when internal refcount reaches zero
*
* @param kref : &synx_handle_coredata.internal_refcount
*/
void synx_util_destroy_internal_handle(struct kref *kref);
/**
* @brief: Function to get the bind operations registered by external driver
*
* @param type : External sync type
*
* @return Bind operations. NULL if type is invalid
*/
struct bind_operations *synx_util_get_bind_ops(u32 type);
/**
* @brief: Function to share the synx object to use by other clients
*
* Shares the synx object by acquiring additional reference to dma fence
*
* @param client : Pointer to client session
* @param params : Pointer to export params struct
*
* @return Status of operation. Negative in case of error. Zero otherwise.
*/
int synx_util_export_object(struct synx_client *client,
struct synx_export_params *params);
/**
* @brief: Function to import the synx object for use by the client
*
* Obtains the synx object corresponding to the importing synx handle
*
* @param params : Pointer to import params struct
*
* @return Synx object pointer on success. NULL if import failed or invalid
*/
struct synx_coredata *synx_util_import_object(
struct synx_import_params *params);
/**
* @brief: Function to acquire the synx client session
*
* This increments the reference count of the client in device client_table
*
* @param session_id : Client session id
*
* @return Pointer to synx client. NULL if invalid session id.
*/
struct synx_client *synx_get_client(struct synx_session session_id);
/**
* @brief: Function to release the synx client session
*
* This decrements the reference count of the client in device client_table and
* invokes the client session cleanup if reference reaches zero.
*
* @param client : Pointer to client session structure
*/
void synx_put_client(struct synx_client *client);
/**
* @brief: Function to generate timestamp
*
* @param timestamp : Timestamp string (filled by function)
* @param size : Timestamp string size
*/
void synx_util_generate_timestamp(char *timestamp, size_t size);
/**
* @brief: Function to log framework errors
*
* This saves the error in a global list and can be accessed using debugfs.
* Needs to be added where we wish to trace the error.
*
* @param id : Client session id
* @param h_synx : Synx object handle
* @param err : Error code
*/
void synx_util_log_error(u32 id, s32 h_synx, s32 err);
#endif /* __SYNX_UTIL_H__ */