From 01f202069f90169f8b0e164c911fb26d0618cff3 Mon Sep 17 00:00:00 2001 From: Sumukh Hallymysore Ravindra Date: Wed, 27 Nov 2019 13:57:41 -0800 Subject: [PATCH] msm: synx: support for external dma fence Changes to support synx framework with both internally created dma fences and as a wrapper to externally managed dma fences. Accepts the external dma fence and binds to existing synx object through the synx_export api. A new api added to return the dma fence backing the synx object. Modifications to object creation, management and cleanup due to the support for external dma fences. Each synx object holds single reference to the underlying dma fence and multiple client references to the synx object is managed through the reference counts maintained per client handle. Added mutex lock to handle the critical section with synx objects. Non interruptible spin locks will be used when required to update backing fence. Change-Id: I94a77436e0c2379e3be5227dfb1ce835dc109ea5 Signed-off-by: Sumukh Hallymysore Ravindra --- drivers/media/platform/msm/synx/synx.c | 447 ++++++------ drivers/media/platform/msm/synx/synx_api.h | 31 + .../media/platform/msm/synx/synx_debugfs.c | 30 +- .../media/platform/msm/synx/synx_private.h | 39 +- drivers/media/platform/msm/synx/synx_util.c | 635 ++++++++++-------- drivers/media/platform/msm/synx/synx_util.h | 134 +++- include/uapi/media/synx.h | 1 + 7 files changed, 790 insertions(+), 527 deletions(-) diff --git a/drivers/media/platform/msm/synx/synx.c b/drivers/media/platform/msm/synx/synx.c index 81cad23368c2..41acc8ad270f 100644 --- a/drivers/media/platform/msm/synx/synx.c +++ b/drivers/media/platform/msm/synx/synx.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -17,42 +18,10 @@ struct synx_device *synx_dev; -static int synx_validate_callback( - struct synx_coredata *synx_obj, - s32 sync_id, - void *data) -{ - u32 i; - int rc = -EINVAL; - struct synx_bind_desc *bind_desc = NULL; - - if (!synx_obj) - return -EINVAL; - - /* need to validate the callback - * as it could be dispatched and/or - * scheduled late, after the handle - * has been released and re-allocated. - */ - spin_lock_bh(&synx_obj->lock); - for (i = 0; i < synx_obj->num_bound_synxs; i++) { - bind_desc = &synx_obj->bound_synxs[i]; - if ((sync_id == - bind_desc->external_desc.id[0]) && - (data == bind_desc->external_data)) { - rc = 0; - pr_debug("callback validation success %d\n", - sync_id); - break; - } - } - spin_unlock_bh(&synx_obj->lock); - - return rc; -} - void synx_external_callback(s32 sync_obj, int status, void *data) { + int rc; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; struct synx_client *client = NULL; struct synx_external_data *bind_data = data; @@ -70,28 +39,28 @@ void synx_external_callback(s32 sync_obj, int status, void *data) goto free; } - synx_obj = synx_util_acquire_object(client, bind_data->h_synx); - if (!synx_obj) { + synx_data = synx_util_acquire_handle(client, bind_data->h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { pr_err("[sess: %u] invalid callback from external obj %d handle %d\n", client->id, sync_obj, bind_data->h_synx); goto fail; } - if (synx_validate_callback(synx_obj, sync_obj, data)) { - pr_err("[sess: %u] stale callback from external obj %d handle %d\n", - client->id, sync_obj, bind_data->h_synx); - goto release; - } - pr_debug("[sess: %u] external callback from %d on handle %d\n", client->id, sync_obj, bind_data->h_synx); - if (synx_signal_core(synx_obj, status, true, sync_obj)) - pr_err("[sess: %u] signal callback failed for handle %d\n", - client->id, bind_data->h_synx); -release: - synx_util_release_object(client, bind_data->h_synx); + mutex_lock(&synx_obj->obj_lock); + rc = synx_signal_fence(synx_obj, status); + if (rc) + pr_err("[sess: %u] signaling failed for handle %d with err: %d\n", + client->id, bind_data->h_synx, rc); + else + synx_signal_core(synx_obj, status, true, sync_obj); + mutex_unlock(&synx_obj->obj_lock); + fail: + synx_util_release_handle(synx_data); synx_put_client(client); free: kfree(bind_data); @@ -110,10 +79,10 @@ const char *synx_fence_driver_name(struct dma_fence *fence) void synx_fence_release(struct dma_fence *fence) { - struct synx_coredata *synx_obj = - container_of(fence, struct synx_coredata, fence); - - synx_util_object_destroy(synx_obj); + /* release the memory allocated during create */ + kfree(fence->lock); + kfree(fence); + pr_debug("released synx backing fence %pK\n", fence); } EXPORT_SYMBOL(synx_fence_release); @@ -125,6 +94,44 @@ static struct dma_fence_ops synx_fence_ops = { .release = synx_fence_release, }; +struct dma_fence *synx_get_fence(struct synx_session session_id, + s32 h_synx) +{ + struct synx_client *client; + struct synx_handle_coredata *synx_data; + struct synx_coredata *synx_obj; + struct dma_fence *fence = NULL; + + pr_debug("[sess: %u] Enter from pid %d\n", + session_id.client_id, current->pid); + + client = synx_get_client(session_id); + if (!client) + return NULL; + + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { + pr_err("%s: [sess: %u] invalid handle access %d\n", + __func__, client->id, h_synx); + goto fail; + } + + mutex_lock(&synx_obj->obj_lock); + fence = synx_obj->fence; + /* obtain an additional reference to the fence */ + dma_fence_get(fence); + mutex_unlock(&synx_obj->obj_lock); + +fail: + synx_util_release_handle(synx_data); + synx_put_client(client); + pr_debug("[sess: %u] Exit from pid %d\n", + session_id.client_id, current->pid); + return fence; +} +EXPORT_SYMBOL(synx_get_fence); + int synx_create(struct synx_session session_id, struct synx_create_params *params) { @@ -166,23 +173,14 @@ int synx_create(struct synx_session session_id, goto clean_up; } - rc = synx_util_activate(synx_obj); - if (rc) { - pr_err("[sess: %u] unable to activate handle %ld\n", - client->id, h_synx); - goto clear_bit; - return -EINVAL; - } - *params->h_synx = h_synx; - pr_debug("[sess: %u] new synx obj with handle %ld\n", - client->id, h_synx); + pr_debug("[sess: %u] new synx obj with handle %ld, fence %pK\n", + client->id, h_synx, synx_obj); synx_put_client(client); return 0; -clear_bit: - clear_bit(h_synx, client->bitmap); clean_up: + dma_fence_put(synx_obj->fence); kfree(synx_obj); fail: synx_put_client(client); @@ -195,12 +193,11 @@ int synx_signal_core(struct synx_coredata *synx_obj, bool cb_signal, s32 ext_sync_id) { - int rc, ret; + int rc = 0, ret; u32 i = 0; u32 idx = 0; s32 sync_id; u32 type; - struct dma_fence *fence; struct synx_external_data *data = NULL; struct synx_bind_desc bind_descs[SYNX_MAX_NUM_BINDINGS]; struct bind_operations *bind_ops = NULL; @@ -208,37 +205,6 @@ int synx_signal_core(struct synx_coredata *synx_obj, if (!synx_obj) return -EINVAL; - if (status < SYNX_STATE_SIGNALED_SUCCESS) { - pr_err("signaling with undefined status = %d\n", status); - return -EINVAL; - } - - if (synx_util_is_merged_object(synx_obj)) { - pr_err("signaling a composite synx object\n"); - return -EINVAL; - } - - spin_lock_bh(&synx_obj->lock); - - if (synx_util_get_object_status_locked(synx_obj) != SYNX_STATE_ACTIVE) { - spin_unlock_bh(&synx_obj->lock); - return -EALREADY; - } - - fence = synx_util_get_fence(synx_obj); - /* set fence error to model {signal w/ error} */ - if (status != SYNX_STATE_SIGNALED_SUCCESS) - dma_fence_set_error(fence, -status); - - rc = dma_fence_signal_locked(fence); - if (rc) { - pr_err("signaling object failed with err: %d\n", rc); - if (status == SYNX_STATE_SIGNALED_SUCCESS) { - status = SYNX_STATE_SIGNALED_ERROR; - dma_fence_set_error(fence, -status); - } - } - synx_util_callback_dispatch(synx_obj, status); /* @@ -268,7 +234,6 @@ int synx_signal_core(struct synx_coredata *synx_obj, } synx_obj->num_bound_synxs = 0; } - spin_unlock_bh(&synx_obj->lock); for (i = 0; i < idx; i++) { sync_id = bind_descs[i].external_desc.id[0]; @@ -294,11 +259,9 @@ int synx_signal_core(struct synx_coredata *synx_obj, /* optional function to enable external signaling */ if (bind_ops->enable_signaling) { ret = bind_ops->enable_signaling(sync_id); - if (ret < 0) { + if (ret < 0) pr_err("enabling fail on %d, type: %u, err: %d\n", sync_id, type, ret); - continue; - } } ret = bind_ops->signal(sync_id, status); if (ret < 0) @@ -315,10 +278,75 @@ int synx_signal_core(struct synx_coredata *synx_obj, return rc; } +static int synx_signal_global(struct synx_coredata *synx_obj) +{ + return 0; +} + +void synx_fence_callback(struct dma_fence *fence, + struct dma_fence_cb *cb) +{ + struct synx_coredata *synx_obj = + container_of(cb, struct synx_coredata, fence_cb); + + synx_signal_global(synx_obj); +} +EXPORT_SYMBOL(synx_fence_callback); + +int synx_signal_fence(struct synx_coredata *synx_obj, + u32 status) +{ + int rc = 0; + unsigned long flags; + + if (!synx_obj || !synx_obj->fence) + return -EINVAL; + + if (status < SYNX_STATE_SIGNALED_SUCCESS) { + pr_err("signaling with wrong status = %u\n", + status); + return -EINVAL; + } + + if (synx_util_is_merged_object(synx_obj)) { + pr_err("signaling a composite object\n"); + return -EINVAL; + } + + /* + * remove registered callback for the fence + * so it does not invoke the signal through callback again + */ + if (!dma_fence_remove_callback(synx_obj->fence, + &synx_obj->fence_cb)) { + pr_err("synx callback could not be removed\n"); + return -EINVAL; + } + + spin_lock_irqsave(synx_obj->fence->lock, flags); + if (synx_util_get_object_status_locked(synx_obj) != + SYNX_STATE_ACTIVE) { + spin_unlock_irqrestore(synx_obj->fence->lock, flags); + return -EALREADY; + } + + /* set fence error to model {signal w/ error} */ + if (status != SYNX_STATE_SIGNALED_SUCCESS) + dma_fence_set_error(synx_obj->fence, -status); + + rc = dma_fence_signal_locked(synx_obj->fence); + if (rc) + pr_err("signaling object failed with err: %d\n", rc); + spin_unlock_irqrestore(synx_obj->fence->lock, flags); + + return rc; +} + int synx_signal(struct synx_session session_id, s32 h_synx, u32 status) { int rc = 0; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; pr_debug("[sess: %u] Enter signal from pid %d\n", @@ -328,21 +356,26 @@ int synx_signal(struct synx_session session_id, s32 h_synx, u32 status) if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); - if (!synx_obj) { + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); rc = -EINVAL; goto fail; } - rc = synx_signal_core(synx_obj, status, false, 0); + mutex_lock(&synx_obj->obj_lock); + rc = synx_signal_fence(synx_obj, status); if (rc) pr_err("[sess: %u] signaling failed for handle %d with err: %d\n", client->id, h_synx, rc); - synx_util_release_object(client, h_synx); + else + rc = synx_signal_core(synx_obj, status, false, 0); + mutex_unlock(&synx_obj->obj_lock); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit signal with status %d\n", session_id.client_id, rc); @@ -382,6 +415,7 @@ int synx_register_callback(struct synx_session session_id, u32 idx; u32 status; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; struct synx_cb_data *synx_cb; struct synx_kernel_payload payload; @@ -393,7 +427,8 @@ int synx_register_callback(struct synx_session session_id, if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); if (!synx_obj) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); @@ -401,15 +436,18 @@ int synx_register_callback(struct synx_session session_id, goto fail; } - if (synx_util_is_merged_object(synx_obj)) { + mutex_lock(&synx_obj->obj_lock); + if (synx_util_is_merged_object(synx_obj) || + synx_util_is_external_object(synx_obj)) { pr_err("cannot register cb with composite synx object\n"); - goto clear; + rc = -EINVAL; + goto release; } synx_cb = kzalloc(sizeof(*synx_cb), GFP_ATOMIC); if (!synx_cb) { rc = -ENOMEM; - goto clear; + goto release; } payload.h_synx = h_synx; @@ -422,11 +460,10 @@ int synx_register_callback(struct synx_session session_id, pr_err("[sess :%u] error allocating cb entry\n", client->id); kfree(synx_cb); - goto clear; + goto release; } - spin_lock_bh(&synx_obj->lock); - status = synx_util_get_object_status_locked(synx_obj); + status = synx_util_get_object_status(synx_obj); synx_cb->session_id = session_id; synx_cb->idx = idx; INIT_WORK(&synx_cb->cb_dispatch, synx_util_cb_dispatch); @@ -441,11 +478,11 @@ int synx_register_callback(struct synx_session session_id, queue_work(synx_dev->work_queue, &synx_cb->cb_dispatch); } - spin_unlock_bh(&synx_obj->lock); -clear: - synx_util_release_object(client, h_synx); +release: + mutex_unlock(&synx_obj->obj_lock); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit register cb with status %d\n", session_id.client_id, rc); @@ -463,6 +500,7 @@ int synx_deregister_callback(struct synx_session session_id, u32 status; bool match_found = false; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; struct synx_kernel_payload payload; struct synx_cb_data *synx_cb, *synx_cb_temp; @@ -475,7 +513,8 @@ int synx_deregister_callback(struct synx_session session_id, if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); if (!synx_obj) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); @@ -483,9 +522,11 @@ int synx_deregister_callback(struct synx_session session_id, goto fail; } - if (synx_util_is_merged_object(synx_obj)) { + mutex_lock(&synx_obj->obj_lock); + if (synx_util_is_merged_object(synx_obj) || + synx_util_is_external_object(synx_obj)) { pr_err("cannot deregister cb with composite synx object\n"); - goto clear; + goto release; } payload.h_synx = h_synx; @@ -493,14 +534,12 @@ int synx_deregister_callback(struct synx_session session_id, payload.data = userdata; payload.cancel_cb_func = cancel_cb_func; - spin_lock_bh(&synx_obj->lock); - status = synx_util_get_object_status_locked(synx_obj); + status = synx_util_get_object_status(synx_obj); if (status != SYNX_STATE_ACTIVE) { pr_err("handle %d already signaled. cannot deregister cb/s\n", h_synx); rc = -EINVAL; - spin_unlock_bh(&synx_obj->lock); - goto clear; + goto release; } status = SYNX_CALLBACK_RESULT_CANCELED; @@ -546,16 +585,15 @@ int synx_deregister_callback(struct synx_session session_id, break; } } - spin_unlock_bh(&synx_obj->lock); if (!match_found) rc = -EINVAL; -clear: - synx_util_release_object(client, h_synx); +release: + mutex_unlock(&synx_obj->obj_lock); fail: + synx_util_release_handle(synx_data); synx_put_client(client); - return rc; pr_debug("[sess: %u] exit deregister cb with status %d\n", session_id.client_id, rc); @@ -614,29 +652,21 @@ int synx_merge(struct synx_session session_id, if (rc) { pr_err("[sess: %u] unable to init merge handle %ld\n", client->id, h_synx); + dma_fence_put(synx_obj->fence); goto clean_up; } - rc = synx_util_activate(synx_obj); - if (rc) { - pr_err("[sess: %u] unable to activate merge handle %ld\n", - client->id, h_synx); - goto clear_bit; - } - *h_synx_merged = h_synx; synx_put_client(client); pr_debug("[sess: %u] exit merge with status %d\n", session_id.client_id, rc); return 0; -clear_bit: - clear_bit(h_synx, client->bitmap); clean_up: kfree(synx_obj); fail: synx_util_merge_error(client, h_synxs, count); - if (num_objs <= count) + if (num_objs && num_objs <= count) kfree(fences); synx_put_client(client); return rc; @@ -647,6 +677,7 @@ int synx_release(struct synx_session session_id, s32 h_synx) { int rc = 0; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; pr_debug("[sess: %u] Enter release from pid %d\n", @@ -656,7 +687,9 @@ int synx_release(struct synx_session session_id, s32 h_synx) if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + /* no need to check for fence here */ if (!synx_obj) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); @@ -665,10 +698,10 @@ int synx_release(struct synx_session session_id, s32 h_synx) } /* release the reference obtained at synx creation */ - synx_util_release_object(client, h_synx); - synx_util_release_object(client, h_synx); + synx_util_release_handle(synx_data); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit release with status %d\n", session_id.client_id, rc); @@ -682,8 +715,8 @@ int synx_wait(struct synx_session session_id, s32 h_synx, u64 timeout_ms) int rc = 0; unsigned long timeleft; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; - struct dma_fence *fence; pr_debug("[sess: %u] Enter wait from pid %d\n", session_id.client_id, current->pid); @@ -692,32 +725,33 @@ int synx_wait(struct synx_session session_id, s32 h_synx, u64 timeout_ms) if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); - if (!synx_obj) { + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); rc = -EINVAL; goto fail; } - fence = synx_util_get_fence(synx_obj); - timeleft = dma_fence_wait_timeout(fence, (bool) 0, + timeleft = dma_fence_wait_timeout(synx_obj->fence, (bool) 0, msecs_to_jiffies(timeout_ms)); if (timeleft <= 0) { pr_err("[sess: %u] wait timeout for handle %d\n", client->id, h_synx); rc = -ETIMEDOUT; - goto clear; + goto fail; } + mutex_lock(&synx_obj->obj_lock); rc = synx_util_get_object_status(synx_obj); + mutex_unlock(&synx_obj->obj_lock); /* remap the state if signaled successfully */ if (rc == SYNX_STATE_SIGNALED_SUCCESS) rc = 0; -clear: - synx_util_release_object(client, h_synx); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit wait with status %d\n", session_id.client_id, rc); @@ -731,7 +765,9 @@ int synx_bind(struct synx_session session_id, { int rc = 0; u32 i; + u32 bound_idx; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; struct synx_external_data *data = NULL; struct bind_operations *bind_ops = NULL; @@ -743,7 +779,8 @@ int synx_bind(struct synx_session session_id, if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); if (!synx_obj) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); @@ -751,29 +788,30 @@ int synx_bind(struct synx_session session_id, goto fail; } - if (synx_util_is_merged_object(synx_obj)) { - pr_err("[sess: %u] cannot bind to merged handle %d\n", - client->id, h_synx); - rc = -EINVAL; - goto clear; - } - bind_ops = synx_util_get_bind_ops(external_sync.type); if (!bind_ops) { pr_err("[sess: %u] invalid bind ops for %u\n", client->id, external_sync.type); rc = -EINVAL; - goto clear; + goto fail; + } + + mutex_lock(&synx_obj->obj_lock); + if (synx_util_is_merged_object(synx_obj) || + synx_util_is_external_object(synx_obj)) { + pr_err("[sess: %u] cannot bind to merged handle %d\n", + client->id, h_synx); + rc = -EINVAL; + goto release; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { rc = -ENOMEM; - goto clear; + goto release; } - spin_lock_bh(&synx_obj->lock); - if (synx_util_get_object_status_locked(synx_obj) != SYNX_STATE_ACTIVE) { + if (synx_util_get_object_status(synx_obj) != SYNX_STATE_ACTIVE) { pr_err("[sess: %u] bind prohibited to inactive handle %d\n", client->id, h_synx); rc = -EINVAL; @@ -801,32 +839,38 @@ int synx_bind(struct synx_session session_id, /* data passed to external callback */ data->h_synx = h_synx; data->session_id = session_id; + + bound_idx = synx_obj->num_bound_synxs; + memcpy(&synx_obj->bound_synxs[bound_idx], + &external_sync, sizeof(struct synx_external_desc)); + synx_obj->bound_synxs[bound_idx].external_data = data; + synx_obj->num_bound_synxs++; + mutex_unlock(&synx_obj->obj_lock); + rc = bind_ops->register_callback(synx_external_callback, data, external_sync.id[0]); if (rc) { pr_err("[sess: %u] callback registration failed for %d\n", client->id, external_sync.id[0]); + mutex_lock(&synx_obj->obj_lock); + memset(&synx_obj->bound_synxs[bound_idx], 0, + sizeof(struct synx_external_desc)); + synx_obj->num_bound_synxs--; goto free; } - memcpy(&synx_obj->bound_synxs[synx_obj->num_bound_synxs], - &external_sync, sizeof(struct synx_external_desc)); - synx_obj->bound_synxs[synx_obj->num_bound_synxs].external_data = data; - synx_obj->num_bound_synxs++; - spin_unlock_bh(&synx_obj->lock); - - synx_util_release_object(client, h_synx); + synx_util_release_handle(synx_data); synx_put_client(client); - pr_debug("[sess: %u] exit bind\n", - session_id.client_id); + pr_debug("[sess: %u] bind of handle %d with id %d successful\n", + session_id.client_id, h_synx, external_sync.id[0]); return 0; free: - spin_unlock_bh(&synx_obj->lock); kfree(data); -clear: - synx_util_release_object(client, h_synx); +release: + mutex_unlock(&synx_obj->obj_lock); fail: + synx_util_release_handle(synx_data); synx_put_client(client); return rc; } @@ -836,6 +880,7 @@ int synx_get_status(struct synx_session session_id, s32 h_synx) { int rc = 0; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; pr_debug("[sess: %u] Enter get_status from pid %d\n", @@ -845,20 +890,23 @@ int synx_get_status(struct synx_session session_id, s32 h_synx) if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); - if (!synx_obj) { + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); rc = SYNX_STATE_INVALID; goto fail; } + mutex_lock(&synx_obj->obj_lock); rc = synx_util_get_object_status(synx_obj); + mutex_unlock(&synx_obj->obj_lock); pr_debug("[sess: %u] synx object handle %d status %d\n", client->id, h_synx, rc); - synx_util_release_object(client, h_synx); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit get_status with status %d\n", session_id.client_id, rc); @@ -869,7 +917,9 @@ EXPORT_SYMBOL(synx_get_status); int synx_addrefcount(struct synx_session session_id, s32 h_synx, s32 count) { int rc = 0; + u32 idx; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *synx_obj; pr_debug("[sess: %u] Enter addrefcount from pid %d\n", @@ -879,8 +929,9 @@ int synx_addrefcount(struct synx_session session_id, s32 h_synx, s32 count) if (!client) return -EINVAL; - synx_obj = synx_util_acquire_object(client, h_synx); - if (!synx_obj) { + synx_data = synx_util_acquire_handle(client, h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { pr_err("%s: [sess: %u] invalid handle access %d\n", __func__, client->id, h_synx); rc = -EINVAL; @@ -891,15 +942,18 @@ int synx_addrefcount(struct synx_session session_id, s32 h_synx, s32 count) pr_err("[sess: %u] invalid addrefcount for handle %d\n", client->id, h_synx); rc = -EINVAL; - goto clear; + goto fail; } + idx = synx_util_handle_index(h_synx); + mutex_lock(&client->synx_table_lock[idx]); + /* acquire additional references to handle */ while (count--) - synx_util_acquire_object(client, h_synx); + kref_get(&synx_data->internal_refcount); + mutex_unlock(&client->synx_table_lock[idx]); -clear: - synx_util_release_object(client, h_synx); fail: + synx_util_release_handle(synx_data); synx_put_client(client); pr_debug("[sess: %u] exit addrefcount with status %d\n", session_id.client_id, rc); @@ -948,9 +1002,10 @@ int synx_import(struct synx_session session_id, } *params->new_h_synx = h_synx; + pr_debug("[sess: %u] new import obj with handle %ld, fence %pK\n", + client->id, h_synx, synx_obj); synx_put_client(client); - pr_debug("[sess: %u] exit import\n", session_id.client_id); return 0; clean_up: @@ -1006,6 +1061,7 @@ static int synx_handle_create(struct synx_private_ioctl_arg *k_ioctl, params.h_synx = &synx_create_info.synx_obj; params.name = synx_create_info.name; + params.type = 0; result = synx_create(session_id, ¶ms); if (!result) @@ -1085,6 +1141,7 @@ static int synx_handle_export(struct synx_private_ioctl_arg *k_ioctl, params.h_synx = id_info.synx_obj; params.secure_key = &id_info.secure_key; + params.fence = NULL; if (synx_export(session_id, ¶ms)) return -EINVAL; @@ -1463,22 +1520,19 @@ int synx_initialize(struct synx_session *session_id, struct synx_initialization_params *params) { u32 i; + u16 unique_id; long idx; - bool bit; struct synx_client *client; struct synx_client_metadata *client_metadata; if (!session_id || !params) return -EINVAL; - do { - idx = find_first_zero_bit(synx_dev->bitmap, SYNX_MAX_CLIENTS); - if (idx >= SYNX_MAX_CLIENTS) { - pr_err("maximum client limit reached\n"); - return -ENOMEM; - } - bit = test_and_set_bit(idx, synx_dev->bitmap); - } while (bit); + idx = synx_util_get_free_handle(synx_dev->bitmap, SYNX_MAX_CLIENTS); + if (idx >= SYNX_MAX_CLIENTS) { + pr_err("maximum client limit reached\n"); + return -ENOMEM; + } client = vzalloc(sizeof(*client)); if (!client) { @@ -1488,8 +1542,16 @@ int synx_initialize(struct synx_session *session_id, if (params->name) strlcpy(client->name, params->name, sizeof(client->name)); + + do { + get_random_bytes(&unique_id, sizeof(unique_id)); + } while (!unique_id); + client->device = synx_dev; - client->id = idx; + client->id = unique_id; + client->id <<= SYNX_CLIENT_HANDLE_SHIFT; + client->id |= (idx & SYNX_CLIENT_HANDLE_MASK); + mutex_init(&client->event_q_lock); for (i = 0; i < SYNX_MAX_OBJS; i++) mutex_init(&client->synx_table_lock[i]); @@ -1503,12 +1565,11 @@ int synx_initialize(struct synx_session *session_id, client_metadata = &synx_dev->client_table[idx]; client_metadata->client = client; kref_init(&client_metadata->refcount); + session_id->client_id = client->id; mutex_unlock(&synx_dev->dev_table_lock); - session_id->client_id = idx; pr_info("[sess: %u] session created %s\n", - idx, params->name); - + session_id->client_id, params->name); return 0; } EXPORT_SYMBOL(synx_initialize); diff --git a/drivers/media/platform/msm/synx/synx_api.h b/drivers/media/platform/msm/synx/synx_api.h index 4a27da0b932e..7cc4333ee9be 100644 --- a/drivers/media/platform/msm/synx/synx_api.h +++ b/drivers/media/platform/msm/synx/synx_api.h @@ -9,6 +9,18 @@ #include #include +/** + * SYNX_FLAG_GLOBAL_FENCE : Creates a global synx object + * If flag not set, creates local synx object + * SYNX_FLAG_EXTERNAL_FENCE : Creates an synx object with external fence + */ +enum synx_flags { + SYNX_FLAG_GLOBAL_FENCE = 0x1, + SYNX_FLAG_MERGED_FENCE = 0x2, + SYNX_FLAG_EXTERNAL_FENCE = 0x4, + SYNX_FLAG_MAX = 0x8, +}; + typedef void (*synx_callback)(s32 sync_obj, int status, void *data); /** @@ -67,6 +79,7 @@ struct synx_initialization_params { * struct synx_create_params - Synx creation parameters * * @h_synx : Pointer to synx object handle (filled by function) + * @type : Synx flags for customization * @name : Optional parameter associating a name with the synx * object for debug purposes * Only first 64 bytes are accepted, @@ -74,6 +87,7 @@ struct synx_initialization_params { */ struct synx_create_params { s32 *h_synx; + u32 type; const char *name; }; @@ -83,10 +97,12 @@ struct synx_create_params { * @h_synx : Synx object handle to export * @secure_key : Pointer to Key generated for authentication * (filled by the function) + * @fence : Pointer to dma fence for external synx object */ struct synx_export_params { s32 h_synx; u32 *secure_key; + struct dma_fence *fence; }; /** @@ -328,6 +344,21 @@ int synx_import(struct synx_session session_id, int synx_export(struct synx_session session_id, struct synx_export_params *params); +/** + * @brief: Get the dma fence backing the synx object + * + * Function obtains an additional reference to the fence. + * This reference needs to be released by the client + * through dma_fence_put explicitly. + * + * @param session_id : Client session id + * @param h_synx : Synx object handle + * + * @return Dma fence pointer for a valid synx handle. NULL otherwise. + */ +struct dma_fence *synx_get_fence(struct synx_session session_id, + s32 h_synx); + /** * @brief: Release the synx object diff --git a/drivers/media/platform/msm/synx/synx_debugfs.c b/drivers/media/platform/msm/synx/synx_debugfs.c index c4cc3a96c719..4fff061c34b4 100644 --- a/drivers/media/platform/msm/synx/synx_debugfs.c +++ b/drivers/media/platform/msm/synx/synx_debugfs.c @@ -43,6 +43,7 @@ static ssize_t synx_table_read(struct file *file, struct synx_device *dev = file->private_data; struct error_node *err_node, *err_node_tmp; struct synx_client *client; + struct synx_handle_coredata *synx_data; struct synx_coredata *row; char *dbuf, *cur, *end; @@ -68,22 +69,31 @@ static ssize_t synx_table_read(struct file *file, cur += scnprintf(cur, end - cur, "\n"); for (j = 0; j < SYNX_MAX_CLIENTS; j++) { - struct synx_session session; + struct synx_client_metadata *client_meta; - session.client_id = j; - client = synx_get_client(session); - if (!client) + mutex_lock(&synx_dev->dev_table_lock); + client_meta = &synx_dev->client_table[j]; + if (!client_meta->client) { + mutex_unlock(&synx_dev->dev_table_lock); continue; + } + kref_get(&client_meta->refcount); + client = client_meta->client; + mutex_unlock(&synx_dev->dev_table_lock); cur += scnprintf(cur, end - cur, "=============== session %08u ===============\n", client->id); + cur += scnprintf(cur, end - cur, + "session name ::: %s\n", + client->name); for (i = 0; i < SYNX_MAX_OBJS; i++) { - row = synx_util_acquire_object(client, i); - if (!row) + synx_data = synx_util_acquire_handle(client, i); + row = synx_util_obtain_object(synx_data); + if (!row || !row->fence) continue; - spin_lock_bh(&row->lock); + mutex_lock(&row->obj_lock); if (columns & NAME_COLUMN) cur += scnprintf(cur, end - cur, "|%10s|", row->name); @@ -95,7 +105,7 @@ static ssize_t synx_table_read(struct file *file, "|%11d|", row->num_bound_synxs); if (columns & STATE_COLUMN) { status = - synx_util_get_object_status_locked(row); + synx_util_get_object_status(row); cur += scnprintf(cur, end - cur, "|%10d|", status); } @@ -107,9 +117,9 @@ static ssize_t synx_table_read(struct file *file, cur, end); } - spin_unlock_bh(&row->lock); + mutex_unlock(&row->obj_lock); cur += scnprintf(cur, end - cur, "\n"); - synx_util_release_object(client, i); + synx_util_release_handle(synx_data); } cur += scnprintf(cur, end - cur, "\n================================================\n\n"); diff --git a/drivers/media/platform/msm/synx/synx_private.h b/drivers/media/platform/msm/synx/synx_private.h index 4642e938fed4..e8f5d8e3ba41 100644 --- a/drivers/media/platform/msm/synx/synx_private.h +++ b/drivers/media/platform/msm/synx/synx_private.h @@ -19,23 +19,17 @@ #define SYNX_DEVICE_NAME "synx_device" #define SYNX_CLIENT_HANDLE_SHIFT 8 -#define SYNX_SECURE_KEY_SHIFT 16 +#define SYNX_OBJ_HANDLE_SHIFT 10 #define MAX_TIMESTAMP_SIZE 32 #define SYNX_OBJ_NAME_LEN 64 #define SYNX_MAX_CLIENTS (1UL<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"); + spinlock_t *fence_lock; + struct dma_fence *fence; + + if (!synx_obj || !params || !ops || + params->type >= SYNX_FLAG_MAX) { + pr_err("%s: invalid arguments\n", __func__); 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; + synx_obj->type = params->type; + synx_obj->num_bound_synxs = 0; + kref_init(&synx_obj->refcount); + mutex_init(&synx_obj->obj_lock); INIT_LIST_HEAD(&synx_obj->reg_cbs_list); if (params->name) strlcpy(synx_obj->name, params->name, sizeof(synx_obj->name)); + if (!synx_util_is_external_object(synx_obj)) { + /* + * lock and fence memory will be released in fence + * release function + */ + fence_lock = kzalloc(sizeof(*fence_lock), GFP_KERNEL); + if (!fence_lock) + return -ENOMEM; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (!fence) { + kfree(fence_lock); + return -ENOMEM; + } + + spin_lock_init(fence_lock); + dma_fence_init(fence, ops, fence_lock, + synx_dev->dma_context, 1); + + /* + * adding callback enables the fence to be + * shared with clients, who can signal fence + * through dma signaling functions, and still + * get notified to update the synx coredata. + */ + if (dma_fence_add_callback(fence, + &synx_obj->fence_cb, synx_fence_callback)) { + pr_err("error adding fence callback for %pK\n", + fence); + dma_fence_put(fence); + return -EINVAL; + } + + synx_obj->fence = fence; + pr_debug("allocated synx backing fence %pK\n", fence); + } + + synx_util_activate(synx_obj); return 0; } @@ -105,13 +89,47 @@ int synx_util_init_group_coredata(struct synx_coredata *synx_obj, if (!array) return -EINVAL; - synx_obj->merged_fence = &array->base; - synx_obj->type = SYNX_OBJ_TYPE_LOCAL | SYNX_OBJ_TYPE_MERGED; + synx_obj->fence = &array->base; + synx_obj->type = SYNX_FLAG_MERGED_FENCE; + synx_obj->num_bound_synxs = 0; + kref_init(&synx_obj->refcount); + mutex_init(&synx_obj->obj_lock); INIT_LIST_HEAD(&synx_obj->reg_cbs_list); + synx_util_activate(synx_obj); return 0; } +static void synx_util_destroy_coredata(struct kref *kref) +{ + struct synx_coredata *synx_obj = + container_of(kref, struct synx_coredata, refcount); + + if (synx_obj->fence) { + /* need to release callback if unsignaled */ + if (synx_util_get_object_status(synx_obj) == + SYNX_STATE_ACTIVE) + if (!dma_fence_remove_callback(synx_obj->fence, + &synx_obj->fence_cb)) + /* nothing much but logging the error */ + pr_err("synx callback could not be removed %pK\n", + synx_obj->fence); + dma_fence_put(synx_obj->fence); + } + + synx_util_object_destroy(synx_obj); +} + +void synx_util_get_object(struct synx_coredata *synx_obj) +{ + kref_get(&synx_obj->refcount); +} + +void synx_util_put_object(struct synx_coredata *synx_obj) +{ + kref_put(&synx_obj->refcount, synx_util_destroy_coredata); +} + void synx_util_object_destroy(struct synx_coredata *synx_obj) { int rc; @@ -138,6 +156,12 @@ void synx_util_object_destroy(struct synx_coredata *synx_obj) type = bind_desc->external_desc.type; data = bind_desc->external_data; bind_ops = synx_util_get_bind_ops(type); + if (!bind_ops) { + pr_err("bind ops fail id: %d, type: %u, err: %d\n", + sync_id, type, rc); + continue; + } + rc = bind_ops->deregister_callback( synx_external_callback, data, sync_id); if (rc < 0) { @@ -154,51 +178,75 @@ void synx_util_object_destroy(struct synx_coredata *synx_obj) kfree(data); } + mutex_destroy(&synx_obj->obj_lock); kfree(synx_obj); - pr_debug("cleaned up synx object fence\n"); + pr_debug("released synx object %pK\n", synx_obj); +} + +long synx_util_get_free_handle(unsigned long *bitmap, unsigned int size) +{ + bool bit; + long idx; + + do { + idx = find_first_zero_bit(bitmap, size); + if (idx >= size) + break; + bit = test_and_set_bit(idx, bitmap); + } while (bit); + + return idx; } int synx_util_init_handle(struct synx_client *client, struct synx_coredata *synx_obj, - long *h_synx) + long *new_synx) { - bool bit; - long idx; + long idx = 0; + s32 h_synx = 0; + u16 unique_id; 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); + if (!synx_util_is_global_object(synx_obj)) { + idx = synx_util_get_free_handle(client->bitmap, SYNX_MAX_OBJS); + if (idx >= SYNX_MAX_OBJS) return -ENOMEM; - } - bit = test_and_set_bit(idx, client->bitmap); - } while (bit); + do { + get_random_bytes(&unique_id, sizeof(unique_id)); + } while (!unique_id); + h_synx = unique_id; + h_synx <<= SYNX_OBJ_HANDLE_SHIFT; + } + h_synx |= (idx & SYNX_OBJ_HANDLE_MASK); + + mutex_lock(&client->synx_table_lock[idx]); synx_data = &client->synx_table[idx]; + memset(synx_data, 0, sizeof(*synx_data)); synx_data->client = client; - synx_data->id = idx; + synx_data->handle = h_synx; synx_data->synx_obj = synx_obj; kref_init(&synx_data->internal_refcount); + mutex_unlock(&client->synx_table_lock[idx]); - *h_synx = idx; + *new_synx = h_synx; 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); + /* external fence activation is managed by client */ + if (synx_util_is_external_object(synx_obj)) + return 0; + /* move synx to ACTIVE state and register cb for merged object */ - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_sw_signaling(synx_obj->fence); return 0; } @@ -206,22 +254,19 @@ 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 (dma_fence_is_array(synx_obj->fence)) { + array = to_dma_fence_array(synx_obj->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); + dma_fence_get(synx_obj->fence); count = 1; } @@ -231,19 +276,17 @@ static u32 synx_util_get_references(struct synx_coredata *synx_obj) 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 (dma_fence_is_array(synx_obj->fence)) { + array = to_dma_fence_array(synx_obj->fence); if (!array) return; for (i = 0; i < array->num_fences; i++) dma_fence_put(array->fences[i]); } else { - dma_fence_put(fence); + dma_fence_put(synx_obj->fence); } } @@ -251,13 +294,11 @@ 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 (dma_fence_is_array(synx_obj->fence)) { + array = to_dma_fence_array(synx_obj->fence); if (!array) return 0; @@ -267,7 +308,7 @@ static u32 synx_util_add_fence(struct synx_coredata *synx_obj, return array->num_fences; } - fences[idx] = fence; + fences[idx] = synx_obj->fence; return 1; } @@ -302,21 +343,23 @@ s32 synx_util_merge_error(struct synx_client *client, u32 num_objs) { u32 i = 0; + struct synx_handle_coredata *synx_data; 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) { + synx_data = synx_util_acquire_handle(client, h_synxs[i]); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj || !synx_obj->fence) { 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]); + synx_util_release_handle(synx_data); } return 0; @@ -330,7 +373,8 @@ int synx_util_validate_merge(struct synx_client *client, { u32 count = 0; u32 i = 0; - struct synx_coredata **synx_objs = NULL; + struct synx_handle_coredata **synx_datas; + struct synx_coredata **synx_objs; struct dma_fence **fences = NULL; if (num_objs <= 1) { @@ -338,17 +382,22 @@ int synx_util_validate_merge(struct synx_client *client, return -EINVAL; } + synx_datas = kcalloc(num_objs, sizeof(*synx_datas), GFP_KERNEL); + if (!synx_datas) + return -ENOMEM; + 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) { + synx_datas[i] = synx_util_acquire_handle(client, h_synxs[i]); + synx_objs[i] = synx_util_obtain_object(synx_datas[i]); + if (!synx_objs[i] || !synx_objs[i]->fence) { pr_err("[sess: %u] invalid handle %d in merge list\n", client->id, h_synxs[i]); *fence_cnt = i; - return -EINVAL; + goto error; } count += synx_util_get_references(synx_objs[i]); } @@ -356,7 +405,7 @@ int synx_util_validate_merge(struct synx_client *client, fences = kcalloc(count, sizeof(*fences), GFP_KERNEL); if (!fences) { *fence_cnt = num_objs; - return -ENOMEM; + goto error; } /* memory will be released later in the invoking function */ @@ -366,12 +415,24 @@ int synx_util_validate_merge(struct synx_client *client, 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]); + synx_util_release_handle(synx_datas[i]); } *fence_cnt = synx_util_remove_duplicates(fences, count); kfree(synx_objs); + kfree(synx_datas); return 0; + +error: + /* release the reference/s obtained earlier in the function */ + for (i = 0; i < *fence_cnt; i++) { + synx_util_put_references(synx_objs[i]); + synx_util_release_handle(synx_datas[i]); + } + *fence_cnt = 0; + kfree(synx_objs); + kfree(synx_datas); + return -EINVAL; } static u32 __fence_state(struct dma_fence *fence, bool locked) @@ -400,8 +461,8 @@ static u32 __fence_state(struct dma_fence *fence, bool locked) case -SYNX_STATE_SIGNALED_CANCEL: state = SYNX_STATE_SIGNALED_CANCEL; break; - case -SYNX_STATE_FORCED_RELEASE: - state = SYNX_STATE_FORCED_RELEASE; + case -SYNX_STATE_SIGNALED_EXTERNAL: + state = SYNX_STATE_SIGNALED_EXTERNAL; break; case -SYNX_STATE_SIGNALED_ERROR: default: @@ -456,22 +517,20 @@ static u32 __fence_group_state(struct dma_fence *fence, bool locked) } /* - * WARN: Should not hold the synx spinlock when invoking + * WARN: Should not hold the fence 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); + state = __fence_group_state(synx_obj->fence, false); else - state = __fence_state(fence, false); + state = __fence_state(synx_obj->fence, false); return state; } @@ -480,75 +539,59 @@ u32 synx_util_get_object_status(struct synx_coredata *synx_obj) 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); + state = __fence_group_state(synx_obj->fence, true); else - state = __fence_state(fence, true); + state = __fence_state(synx_obj->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_handle_coredata *synx_util_acquire_handle( 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); + u32 idx = synx_util_handle_index(h_synx); + struct synx_handle_coredata *synx_data = NULL; + struct synx_handle_coredata *synx_handle = NULL; - if (!synx_data) + if (!client) 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", + mutex_lock(&client->synx_table_lock[idx]); + synx_data = &client->synx_table[idx]; + if (!synx_data->synx_obj) { + pr_err("[sess: %u] invalid object handle %d\n", client->id, h_synx); + } else if (synx_data->handle != h_synx) { + pr_err("[sess: %u] stale object handle %d\n", + client->id, h_synx); + } else if (!kref_read(&synx_data->internal_refcount)) { + pr_err("[sess: %u] destroyed object handle %d\n", + client->id, h_synx); + } else { + kref_get(&synx_data->internal_refcount); + synx_handle = synx_data; } - mutex_unlock(&client->synx_table_lock[h_synx]); + mutex_unlock(&client->synx_table_lock[idx]); - return synx_obj; + return synx_handle; } static void synx_util_destroy_handle(struct synx_handle_coredata *synx_data) { - long idx = synx_data->id; + long idx = synx_util_handle_index(synx_data->handle); 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); - } + struct synx_coredata *synx_obj = 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); + synx_util_put_object(synx_obj); + pr_debug("[sess: %u] handle %d destroyed %pK\n", + client->id, idx, synx_obj); } void synx_util_destroy_import_handle(struct kref *kref) @@ -558,7 +601,7 @@ void synx_util_destroy_import_handle(struct kref *kref) import_refcount); pr_debug("[sess: %u] import handle cleanup for %d\n", - synx_data->client->id, synx_data->id); + synx_data->client->id, synx_data->handle); /* in case of pending internal references, abort clean up */ if (kref_read(&synx_data->internal_refcount)) @@ -574,7 +617,7 @@ void synx_util_destroy_internal_handle(struct kref *kref) internal_refcount); pr_debug("[sess: %u] internal handle cleanup for %d\n", - synx_data->client->id, synx_data->id); + synx_data->client->id, synx_data->handle); /* in case of pending imports, abort clean up */ if (kref_read(&synx_data->import_refcount)) @@ -583,25 +626,24 @@ void synx_util_destroy_internal_handle(struct kref *kref) synx_util_destroy_handle(synx_data); } -void synx_util_release_object(struct synx_client *client, s32 h_synx) +void synx_util_release_handle(struct synx_handle_coredata *synx_data) { - struct synx_handle_coredata *synx_data = - synx_util_obtain_handle(client, h_synx); - struct synx_coredata *synx_obj; - struct dma_fence *fence; + u32 idx; + struct synx_client *client; 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); + idx = synx_util_handle_index(synx_data->handle); + client = synx_data->client; + mutex_lock(&client->synx_table_lock[idx]); + if (synx_data->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]); + else + pr_err("%s: invalid handle %d\n", + __func__, synx_data->handle); + mutex_unlock(&client->synx_table_lock[idx]); } struct bind_operations *synx_util_get_bind_ops(u32 type) @@ -626,22 +668,18 @@ int synx_util_alloc_cb_entry(struct synx_client *client, struct synx_kernel_payload *data, u32 *cb_idx) { - bool bit; long idx; struct synx_client_cb *cb; if (!client || !data || !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); + idx = synx_util_get_free_handle(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; + } cb = &client->cb_table[idx]; memset(cb, 0, sizeof(*cb)); @@ -773,15 +811,12 @@ void synx_util_cb_dispatch(struct work_struct *cb_dispatch) 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; + u32 idx; struct synx_session ex_session_id; struct synx_client *ex_client; struct synx_handle_coredata *synx_data; @@ -790,119 +825,157 @@ struct synx_coredata *synx_util_import_object(struct synx_import_params *params) 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; + ex_session_id.client_id = params->secure_key; /* 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); + pr_err("sess: %u invalid import handle %d\n", + ex_session_id.client_id, params->h_synx); return NULL; } - synx_data = synx_util_obtain_handle(ex_client, h_synx); - if (!synx_data) { + idx = synx_util_handle_index(params->h_synx); + /* + * need to access directly instead of acquire_handle + * as internal refcount might be released completely. + */ + mutex_lock(&ex_client->synx_table_lock[idx]); + synx_data = &ex_client->synx_table[idx]; + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj) { pr_err("[sess: %u] invalid import handle %d\n", - ex_client->id, h_synx); + ex_client->id, params->h_synx); + goto fail; + } + + if (synx_data->handle != params->h_synx) { + pr_err("[sess: %u] stale import handle %d\n", + ex_client->id, params->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; - } + if (kref_read(&synx_data->import_refcount)) { + /* get additional reference for client */ + synx_util_get_object(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); + ex_client->id, params->h_synx); + } else { + synx_obj = NULL; } - mutex_unlock(&ex_client->synx_table_lock[h_synx]); fail: + mutex_unlock(&ex_client->synx_table_lock[idx]); synx_put_client(ex_client); return synx_obj; } +static int synx_util_export_internal(struct synx_coredata *synx_obj, + struct synx_export_params *params) +{ + if (!synx_obj || !synx_obj->fence) + return -EINVAL; + + return 0; +} + +static int synx_util_export_external(struct synx_coredata *synx_obj, + struct synx_export_params *params) +{ + int rc; + + if (!synx_obj || !params || !params->fence) + return -EINVAL; + + if (synx_obj->fence) { + /* + * remove the previous dma fence (if any). + * should not call synx_util_put_object here, + * as we will reuse the synx obj memory. so, + * release just the fence reference. + * note: before releasing the reference, need + * to ensure registered callback is removed + * for unsignaled object. + */ + if (synx_util_get_object_status(synx_obj) == + SYNX_STATE_ACTIVE) + if (!dma_fence_remove_callback(synx_obj->fence, + &synx_obj->fence_cb)) + /* continue after logging the error */ + pr_err("synx callback could not be removed %pK\n", + synx_obj->fence); + dma_fence_put(synx_obj->fence); + pr_info("%s: released fence reference %pK, new fence %pK\n", + __func__, synx_obj->fence, params->fence); + } + + synx_obj->fence = params->fence; + /* get lone synx framework reference on the fence */ + dma_fence_get(synx_obj->fence); + rc = dma_fence_add_callback(synx_obj->fence, + &synx_obj->fence_cb, synx_fence_callback); + if (rc && rc != -ENOENT) { + pr_err("error registering fence callback on handle %d\n", + params->h_synx); + return rc; + } + + /* if fence is not active, invoke synx signaling */ + if (rc == -ENOENT) + synx_signal_core(synx_obj, SYNX_STATE_SIGNALED_EXTERNAL, + false, 0); + return 0; +} + int synx_util_export_object(struct synx_client *client, struct synx_export_params *params) { int rc = 0; - u16 key = 0; - s32 h_synx; + u32 idx; struct synx_handle_coredata *synx_data; + struct synx_coredata *synx_obj; if (!params || !params->secure_key) return -EINVAL; - h_synx = params->h_synx; - synx_data = synx_util_obtain_handle(client, h_synx); - if (!synx_data) { + synx_data = synx_util_acquire_handle(client, params->h_synx); + synx_obj = synx_util_obtain_object(synx_data); + if (!synx_obj) { pr_err("[sess: %u] invalid export handle %d\n", - client->id, h_synx); + client->id, params->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); + idx = synx_util_handle_index(params->h_synx); + mutex_lock(&client->synx_table_lock[idx]); + if (!kref_read(&synx_data->import_refcount)) + kref_init(&synx_data->import_refcount); + else + kref_get(&synx_data->import_refcount); - /* 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; + *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]); + if (synx_util_is_external_object(synx_data->synx_obj)) + rc = synx_util_export_external(synx_data->synx_obj, params); + else + rc = synx_util_export_internal(synx_data->synx_obj, params); + + /* release the import reference acquired above on failure */ + if (rc) + kref_put(&synx_data->import_refcount, + synx_util_destroy_import_handle); + else + pr_debug("[sess: %u] handle %d export successful\n", + client->id, params->h_synx); + mutex_unlock(&client->synx_table_lock[idx]); fail: + synx_util_release_handle(synx_data); return rc; } @@ -910,7 +983,7 @@ 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; + u32 id = synx_util_client_index(session_id.client_id); if (id >= SYNX_MAX_CLIENTS) { pr_err("%s: invalid session handle %u from pid: %d\n", @@ -921,11 +994,18 @@ struct synx_client *synx_get_client(struct synx_session session_id) 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 + if (client) { + if (client->id == session_id.client_id) { + kref_get(&client_metadata->refcount); + } else { + pr_err("session %u mismatch pid: %d\n", + session_id.client_id, current->pid); + client = NULL; + } + } else { pr_err("session %u not available, pid: %d\n", - id, current->pid); + session_id.client_id, current->pid); + } mutex_unlock(&synx_dev->dev_table_lock); return client; @@ -938,15 +1018,10 @@ static void synx_client_destroy(struct kref *kref) 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 @@ -954,27 +1029,22 @@ static void synx_client_destroy(struct kref *kref) * 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)) { + if (synx_data->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)) { + 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); + clear_bit(synx_util_client_index(client->id), synx_dev->bitmap); - pr_info("[sess: %u] session destroyed %s\n", - client->id, client->name); + pr_info("[sess: %u] session destroyed %s, uid: %u\n", + client->id, client->name, client->id); vfree(client); } @@ -987,14 +1057,15 @@ void synx_put_client(struct synx_client *client) return; } - if (client->id >= SYNX_MAX_CLIENTS) { + if (synx_util_client_index(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]; + client_metadata = + &synx_dev->client_table[synx_util_client_index(client->id)]; if (client_metadata->client == client) /* should not reference client after this call */ kref_put(&client_metadata->refcount, synx_client_destroy); diff --git a/drivers/media/platform/msm/synx/synx_util.h b/drivers/media/platform/msm/synx/synx_util.h index ccdf6b3fc79d..385c25c13648 100644 --- a/drivers/media/platform/msm/synx/synx_util.h +++ b/drivers/media/platform/msm/synx/synx_util.h @@ -10,6 +10,9 @@ extern struct synx_device *synx_dev; +extern void synx_fence_callback(struct dma_fence *fence, + struct dma_fence_cb *cb); + /** * @brief: Function to check if the external obj type is valid * @@ -17,7 +20,13 @@ extern struct synx_device *synx_dev; * * @return True if valid. False otherwise */ -bool synx_util_is_valid_bind_type(u32 type); +static inline bool synx_util_is_valid_bind_type(u32 type) +{ + if (type < SYNX_MAX_BIND_TYPES) + return true; + + return false; +} /** * @brief: Function to get the synx object type @@ -26,7 +35,35 @@ bool synx_util_is_valid_bind_type(u32 type); * * @return Synx object type. Zero if invalid */ -u32 synx_util_get_object_type(struct synx_coredata *synx_obj); +static inline u32 synx_util_get_object_type( + struct synx_coredata *synx_obj) +{ + return synx_obj->type; +} + +/** + * @brief: Function to get the client metadata index from session id + * + * @param client_id : Client session id + * + * @return Client metadata index. + */ +static inline u32 synx_util_client_index(u32 client_id) +{ + return (client_id & SYNX_CLIENT_HANDLE_MASK); +} + +/** + * @brief: Function to get the client synx table index from handle + * + * @param h_synx : Synx object handle + * + * @return Synx object index. + */ +static inline u32 synx_util_handle_index(s32 h_synx) +{ + return (h_synx & SYNX_OBJ_HANDLE_MASK); +} /** * @brief: Function to check if the synx object is a composite (merged) @@ -35,17 +72,49 @@ u32 synx_util_get_object_type(struct synx_coredata *synx_obj); * * @return True if merged object. False otherwise */ -bool synx_util_is_merged_object(struct synx_coredata *synx_obj); +static inline bool synx_util_is_merged_object( + struct synx_coredata *synx_obj) +{ + if (synx_obj->type & SYNX_FLAG_MERGED_FENCE) + return true; + + return false; +} /** - * @brief: Function to get the dma fence backing the synx object + * @brief: Function to check if object is of global type * * @param synx_obj : Pointer to synx object * - * @return Dma fence. NULL if synx_obj is invalid + * @return True if global object. False otherwise. */ -struct dma_fence *synx_util_get_fence( - struct synx_coredata *synx_obj); +static inline bool synx_util_is_global_object( + struct synx_coredata *synx_obj) +{ + if (synx_obj->type & SYNX_FLAG_GLOBAL_FENCE) + return true; + + return false; +} + +/** + * @brief: Function to check if object is of external type + * + * The dma fence backing the external synx object is created and + * managed outside the synx framework. + * + * @param synx_obj : Pointer to synx object + * + * @return True if external object. False otherwise. + */ +static inline bool synx_util_is_external_object( + struct synx_coredata *synx_obj) +{ + if (synx_obj->type & SYNX_FLAG_EXTERNAL_FENCE) + return true; + + return false; +} /** * @brief: Function to acquire reference to synx object @@ -108,6 +177,16 @@ int synx_util_init_group_coredata(struct synx_coredata *synx_obj, */ void synx_util_object_destroy(struct synx_coredata *synx_obj); +/** + * @brief: Function to find a free index from the bitmap + * + * @param bitmap : Pointer to bitmap + * @param size : Max index + * + * @return Free index if available. + */ +long synx_util_get_free_handle(unsigned long *bitmap, unsigned int size); + /** * @brief: Function to allocate synx_client_cb entry from cb table * @@ -245,42 +324,45 @@ u32 synx_util_get_object_status(struct synx_coredata *synx_obj); u32 synx_util_get_object_status_locked(struct synx_coredata *synx_obj); /** - * @brief: Function to obtain the synx object metadata for the client + * @brief: Function to acquire the synx object handle + * + * This function increments the reference count of synx handle data. + * The reference should be released by calling synx_util_release_handle * * @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. + * @return Pointer to synx handle data on success. NULL otherwise. */ -struct synx_handle_coredata *synx_util_obtain_handle( +struct synx_handle_coredata *synx_util_acquire_handle( struct synx_client *client, s32 h_synx); /** - * @brief: Function to acquire the synx object + * @brief: Function to obtain the synx object * - * This function increments the reference count of the dma fence. + * @param synx_data : Pointer to synx object handle data * - * @param client : Pointer to client session - * @param h_synx : Synx object handle - * - * @return Pointer to synx object. NULL if invalid synx handle. + * @return Pointer to synx object on success. NULL otherwise. */ -struct synx_coredata *synx_util_acquire_object( - struct synx_client *client, s32 h_synx); +static inline struct synx_coredata *synx_util_obtain_object( + struct synx_handle_coredata *synx_data) { + if (!synx_data) + return NULL; + + return synx_data->synx_obj; +} /** - * @brief: Function to release the synx object + * @brief: Function to release the synx object handle * - * 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 + * This function decrements the reference count of the synx handle and + * implicitly calls up the synx object release if count reaches 0. + * Synx handle data should not be derefrenced in the function after * calling this. * - * @param client : Pointer to client session - * @param h_synx : Synx object handle + * @param synx_data : Pointer to synx object handle data */ -void synx_util_release_object(struct synx_client *client, s32 h_synx); +void synx_util_release_handle(struct synx_handle_coredata *synx_data); /** * @brief: Function called implicitly when import refcount reaches zero diff --git a/include/uapi/media/synx.h b/include/uapi/media/synx.h index ea74d0a00bf7..dfdb3164ae8a 100644 --- a/include/uapi/media/synx.h +++ b/include/uapi/media/synx.h @@ -17,6 +17,7 @@ #define SYNX_STATE_SIGNALED_SUCCESS 2 #define SYNX_STATE_SIGNALED_ERROR 3 #define SYNX_STATE_SIGNALED_CANCEL 4 +#define SYNX_STATE_SIGNALED_EXTERNAL 5 #define SYNX_MAX_WAITING_SYNX 16