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