diff --git a/drivers/media/platform/msm/synx/synx.c b/drivers/media/platform/msm/synx/synx.c index 19e49c91de75..7d742601ffad 100644 --- a/drivers/media/platform/msm/synx/synx.c +++ b/drivers/media/platform/msm/synx/synx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "synx: " fmt @@ -305,70 +305,29 @@ fail: } EXPORT_SYMBOL(synx_signal); -static int synx_match_payload(struct synx_client_cb *cb, - struct synx_kernel_payload *payload, - bool is_kernel_cb) +static int synx_match_payload(struct synx_kernel_payload *cb_payload, + struct synx_kernel_payload *payload) { int rc = 0; - if (!cb || !payload) + if (!cb_payload || !payload) return -EINVAL; - if (is_kernel_cb) { - struct synx_kernel_payload *cb_payload = &cb->kernel_cb; - - if ((cb_payload->cb_func == payload->cb_func) && - (cb_payload->data == payload->data)) { - if (payload->cancel_cb_func) { - cb_payload->cb_func = - payload->cancel_cb_func; - rc = 1; - } else { - rc = 2; - pr_debug("kernel cb deregistration success\n"); - } - } - } else { - struct synx_user_payload *cb_payload = &cb->user_cb; - struct synx_user_payload *user_payload = payload->data; - - if ((cb_payload->data[0] == user_payload->data[0]) && - (cb_payload->data[1] == user_payload->data[1])) { - if (user_payload->data[2]) { - memcpy(cb_payload->data, - user_payload->data, - SYNX_PAYLOAD_WORDS * sizeof(__u64)); - rc = 1; - } else { - rc = 2; - pr_debug("user cb deregistration success\n"); - } + if ((cb_payload->cb_func == payload->cb_func) && + (cb_payload->data == payload->data)) { + if (payload->cancel_cb_func) { + cb_payload->cb_func = + payload->cancel_cb_func; + rc = 1; + } else { + rc = 2; + pr_debug("kernel cb de-registration success\n"); } } return rc; } -void synx_default_user_callback(s32 h_synx, - int status, void *data) -{ - struct synx_user_payload *payload = data; - struct synx_client *client = payload->client; - struct synx_client_cb *cb = - container_of(payload, struct synx_client_cb, user_cb); - - if (client) { - payload->status = status; - pr_debug("user cb queued for handle %d\n", h_synx); - mutex_lock(&client->event_q_lock); - list_add_tail(&cb->node, &client->event_q); - mutex_unlock(&client->event_q_lock); - wake_up_all(&client->event_wq); - } else { - pr_err("%s: invalid client session\n", __func__); - } -} - int synx_register_callback(struct synx_session session_id, s32 h_synx, synx_callback cb_func, @@ -408,40 +367,16 @@ int synx_register_callback(struct synx_session session_id, goto clear; } - if (cb_func == synx_default_user_callback) { - /* userspace callback index allocated already */ - idx = *((u32 *)userdata); - if (idx < SYNX_MAX_OBJS) { - /* data used in default callback func */ - userdata = &client->cb_table[idx].user_cb; - } else { - pr_err("%s: [sess: %u] invalid cb index\n", - __func__, client->id); - kfree(synx_cb); - rc = -EINVAL; - goto clear; - } - } else { - /* obtain a free index from client cb table */ - rc = synx_util_alloc_cb_entry(client, &idx); - if (rc) { - pr_err("[sess :%u] error allocating cb entry\n", - client->id); - kfree(synx_cb); - goto clear; - } - } - payload.h_synx = h_synx; payload.cb_func = cb_func; payload.data = userdata; - /* update the cb entry with kernel cb data */ - rc = synx_util_update_cb_entry(client, &payload, idx); + + /* allocate a free index from client cb table */ + rc = synx_util_alloc_cb_entry(client, &payload, &idx); if (rc) { pr_err("[sess :%u] error allocating cb entry\n", client->id); kfree(synx_cb); - clear_bit(idx, client->cb_bitmap); goto clear; } @@ -454,7 +389,7 @@ int synx_register_callback(struct synx_session session_id, /* add callback if object still ACTIVE, dispatch if SIGNALED */ if (status == SYNX_STATE_ACTIVE) { pr_debug("[sess: %u] callback added\n", client->id); - list_add_tail(&synx_cb->node, &synx_obj->reg_cbs_list); + list_add(&synx_cb->node, &synx_obj->reg_cbs_list); } else { synx_cb->status = status; pr_debug("[sess: %u] callback queued\n", client->id); @@ -481,7 +416,6 @@ int synx_deregister_callback(struct synx_session session_id, { int rc = 0, ret = 0; u32 status; - bool is_kernel_cb = true; bool match_found = false; struct synx_client *client; struct synx_coredata *synx_obj; @@ -509,9 +443,6 @@ int synx_deregister_callback(struct synx_session session_id, goto clear; } - if (cb_func == synx_default_user_callback) - is_kernel_cb = false; - payload.h_synx = h_synx; payload.cb_func = cb_func; payload.data = userdata; @@ -533,7 +464,8 @@ int synx_deregister_callback(struct synx_session session_id, &synx_obj->reg_cbs_list, node) { if (synx_cb->session_id.client_id != client->id) { continue; - } else if (synx_cb->idx >= SYNX_MAX_OBJS) { + } else if (synx_cb->idx == 0 || + synx_cb->idx >= SYNX_MAX_OBJS) { /* * this should not happen. Even if it does, * the allocated memory will be cleaned up @@ -546,7 +478,7 @@ int synx_deregister_callback(struct synx_session session_id, } cb_payload = &client->cb_table[synx_cb->idx]; - ret = synx_match_payload(cb_payload, &payload, is_kernel_cb); + ret = synx_match_payload(&cb_payload->kernel_cb, &payload); switch (ret) { case 1: /* queue the cancel cb work */ @@ -558,6 +490,9 @@ int synx_deregister_callback(struct synx_session session_id, break; case 2: /* no cancellation cb */ + if (synx_util_clear_cb_entry(client, cb_payload)) + pr_err("%s: [sess: %u] error clearing cb %d\n", + __func__, client->id, h_synx); list_del_init(&synx_cb->node); kfree(synx_cb); match_found = true; @@ -1206,10 +1141,7 @@ static int synx_handle_register_user_payload( struct synx_session session_id) { int rc = 0; - u32 idx; struct synx_userpayload_info user_data; - struct synx_client *client; - struct synx_client_cb *cb_data; if (k_ioctl->size != sizeof(user_data)) return -EINVAL; @@ -1219,34 +1151,14 @@ static int synx_handle_register_user_payload( k_ioctl->size)) return -EFAULT; - client = synx_get_client(session_id); - if (!client) - return -EINVAL; - - /* obtain a free index from the cb table */ - rc = synx_util_alloc_cb_entry(client, &idx); - if (rc) { - pr_err("%s: [sess :%u] error allocating cb entry\n", - __func__, client->id); - goto fail; - } - - cb_data = &client->cb_table[idx]; - cb_data->user_cb.client = client; - cb_data->user_cb.h_synx = user_data.synx_obj; - memcpy(&cb_data->user_cb.data, user_data.payload, - SYNX_USER_PAYLOAD_SIZE * sizeof(__u64)); - + pr_debug("user cb registration with payload %x\n", + user_data.payload[0]); rc = synx_register_callback(session_id, user_data.synx_obj, - synx_default_user_callback, &idx); - if (rc) { + synx_util_default_user_callback, (void *)user_data.payload[0]); + if (rc) pr_err("[sess: %u] user cb registration failed for handle %d\n", session_id.client_id, user_data.synx_obj); - clear_bit(idx, client->cb_bitmap); - } -fail: - synx_put_client(client); return rc; } @@ -1256,7 +1168,6 @@ static int synx_handle_deregister_user_payload( { int rc = 0; struct synx_userpayload_info user_data; - struct synx_user_payload payload; if (k_ioctl->size != sizeof(user_data)) return -EINVAL; @@ -1266,16 +1177,12 @@ static int synx_handle_deregister_user_payload( k_ioctl->size)) return -EFAULT; - payload.h_synx = user_data.synx_obj; - memcpy(&payload.data, user_data.payload, - SYNX_USER_PAYLOAD_SIZE * sizeof(__u64)); - rc = synx_deregister_callback(session_id, - payload.h_synx, synx_default_user_callback, - &payload, NULL); + user_data.synx_obj, synx_util_default_user_callback, + (void *)user_data.payload[0], NULL); if (rc) pr_err("[sess: %u] callback deregistration failed for handle %d\n", - session_id.client_id, payload.h_synx); + session_id.client_id, user_data.synx_obj); return rc; } @@ -1420,7 +1327,6 @@ static ssize_t synx_read(struct file *filep, char __user *buf, size_t size, loff_t *f_pos) { ssize_t rc = 0; - u32 idx; struct synx_client *client = NULL; struct synx_client_cb *cb; struct synx_session *session = filep->private_data; @@ -1442,18 +1348,25 @@ static ssize_t synx_read(struct file *filep, cb = list_first_entry_or_null(&client->event_q, struct synx_client_cb, node); if (!cb) { + mutex_unlock(&client->event_q_lock); + rc = 0; + goto fail; + } + + if (cb->idx == 0 || cb->idx >= SYNX_MAX_OBJS) { + pr_err("%s invalid index\n", __func__); mutex_unlock(&client->event_q_lock); rc = -EINVAL; goto fail; } + list_del_init(&cb->node); mutex_unlock(&client->event_q_lock); rc = size; - data.synx_obj = cb->user_cb.h_synx; - data.reserved = cb->user_cb.status; - memcpy(data.payload, &cb->user_cb.data, - SYNX_USER_PAYLOAD_SIZE * sizeof(__u64)); + data.synx_obj = cb->kernel_cb.h_synx; + data.reserved = cb->kernel_cb.status; + data.payload[0] = (u64)cb->kernel_cb.data; if (copy_to_user(buf, &data, sizeof(struct synx_userpayload_info))) { @@ -1461,9 +1374,9 @@ static ssize_t synx_read(struct file *filep, rc = -EFAULT; } - idx = cb->idx; - memset(cb, 0, sizeof(*cb)); - clear_bit(idx, client->cb_bitmap); + if (synx_util_clear_cb_entry(client, cb)) + pr_err("%s: [sess: %u] error clearing cb for handle %d\n", + __func__, client->id, data.synx_obj); fail: synx_put_client(client); pr_debug("[sess: %u] exit with status %d\n", @@ -1539,6 +1452,7 @@ int synx_initialize(struct synx_session *session_id, init_waitqueue_head(&client->event_wq); /* zero handle not allowed */ set_bit(0, client->bitmap); + set_bit(0, client->cb_bitmap); mutex_lock(&synx_dev->dev_table_lock); client_metadata = &synx_dev->client_table[idx]; @@ -1547,7 +1461,7 @@ int synx_initialize(struct synx_session *session_id, mutex_unlock(&synx_dev->dev_table_lock); session_id->client_id = idx; - pr_debug("index location %ld allocated for client %s\n", + pr_info("[sess: %u] session created %s\n", idx, params->name); return 0; diff --git a/drivers/media/platform/msm/synx/synx_private.h b/drivers/media/platform/msm/synx/synx_private.h index 283227016e06..4642e938fed4 100644 --- a/drivers/media/platform/msm/synx/synx_private.h +++ b/drivers/media/platform/msm/synx/synx_private.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #ifndef __SYNX_PRIVATE_H__ @@ -82,33 +82,19 @@ struct error_node { * callback registered on a synx object * * @h_synx : Synx object handle + * @status : Synx obj status or callback failure * @data : Callback data, passed by client driver * @cb_func : Callback function, registered by client driver * @cancel_cb_func : Cancellation callback function */ struct synx_kernel_payload { s32 h_synx; + u32 status; void *data; synx_callback cb_func; synx_callback cancel_cb_func; }; -/** - * struct synx_user_payload - Single node of information about a callback - * registered from user space on a synx object - * - * @client : Synx client structure - * @h_synx : Synx object handle - * @status : Synx obj status or callback failure - * @data : Payload data, opaque to kernel - */ -struct synx_user_payload { - struct synx_client *client; - s32 h_synx; - u32 status; - u64 data[SYNX_PAYLOAD_WORDS]; -}; - /** * struct synx_cb_data - Single node of information about callback * registered by client for the synx object @@ -132,17 +118,16 @@ struct synx_cb_data { * registered by client * * @is_valid : True if this is a valid entry - * @is_kernel_cb : True for kernel callback, false for user callback * @idx : Index of the client callback table + * @client : Client session * @kernel_cb : Kernel callback payload - * @user_cb : Userspace callback payload * @node : List member used to append this node to event list */ struct synx_client_cb { bool is_valid; u32 idx; + struct synx_client *client; struct synx_kernel_payload kernel_cb; - struct synx_user_payload user_cb; struct list_head node; }; diff --git a/drivers/media/platform/msm/synx/synx_util.c b/drivers/media/platform/msm/synx/synx_util.c index 5e11efcb44a5..2b532429086a 100644 --- a/drivers/media/platform/msm/synx/synx_util.c +++ b/drivers/media/platform/msm/synx/synx_util.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "synx: " fmt @@ -623,13 +623,14 @@ struct bind_operations *synx_util_get_bind_ops(u32 type) } 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 || !cb_idx) + if (!client || !data || !cb_idx) return -EINVAL; do { @@ -644,30 +645,55 @@ int synx_util_alloc_cb_entry(struct synx_client *client, cb = &client->cb_table[idx]; memset(cb, 0, sizeof(*cb)); - cb->idx = idx; - *cb_idx = idx; - pr_debug("[sess: %u] allocated cb index %u\n", client->id, idx); - return 0; -} - -int synx_util_update_cb_entry(struct synx_client *client, - void *data, - u32 cb_idx) -{ - struct synx_client_cb *cb; - - if (!client || !data || (cb_idx >= SYNX_MAX_OBJS)) - return -EINVAL; - - cb = &client->cb_table[cb_idx]; cb->is_valid = true; + cb->client = client; + cb->idx = idx; memcpy(&cb->kernel_cb, data, sizeof(cb->kernel_cb)); - pr_debug("[sess: %u] updated cb index %u\n", client->id, cb_idx); + *cb_idx = idx; + pr_debug("[sess: %u] allocated cb index %u\n", client->id, *cb_idx); return 0; } +int synx_util_clear_cb_entry(struct synx_client *client, + struct synx_client_cb *cb) +{ + int rc = 0; + u32 idx; + + if (!cb) + return -EINVAL; + + idx = cb->idx; + memset(cb, 0, sizeof(*cb)); + if (idx && idx < SYNX_MAX_OBJS) { + clear_bit(idx, client->cb_bitmap); + } else { + pr_err("%s: found invalid index\n", __func__); + rc = -EINVAL; + } + + return rc; +} + +void synx_util_default_user_callback(s32 h_synx, + int status, void *data) +{ + struct synx_client_cb *cb = data; + + if (cb && cb->client) { + pr_debug("user cb queued for handle %d\n", h_synx); + cb->kernel_cb.status = status; + mutex_lock(&cb->client->event_q_lock); + list_add_tail(&cb->node, &cb->client->event_q); + mutex_unlock(&cb->client->event_q_lock); + wake_up_all(&cb->client->event_wq); + } else { + pr_err("%s: invalid params\n", __func__); + } +} + void synx_util_callback_dispatch(struct synx_coredata *synx_obj, u32 status) { struct synx_cb_data *synx_cb, *synx_cb_temp; @@ -693,7 +719,8 @@ void synx_util_cb_dispatch(struct work_struct *cb_dispatch) container_of(cb_dispatch, struct synx_cb_data, cb_dispatch); struct synx_client *client; struct synx_client_cb *cb; - struct synx_kernel_payload *payload; + struct synx_kernel_payload payload; + u32 status; client = synx_get_client(synx_cb->session_id); if (!client) { @@ -702,26 +729,46 @@ void synx_util_cb_dispatch(struct work_struct *cb_dispatch) goto free; } - if (synx_cb->idx >= SYNX_MAX_OBJS) { - pr_err("[sess: %u] invalid cb index\n", - client->id); + if (synx_cb->idx == 0 || + synx_cb->idx >= SYNX_MAX_OBJS) { + pr_err("[sess: %u] invalid cb index %u\n", + client->id, synx_cb->idx); goto fail; } + status = synx_cb->status; cb = &client->cb_table[synx_cb->idx]; if (!cb->is_valid) { pr_err("invalid cb payload\n"); goto fail; } - payload = &cb->kernel_cb; + memcpy(&payload, &cb->kernel_cb, sizeof(cb->kernel_cb)); + payload.status = status; + + if (payload.cb_func == synx_util_default_user_callback) { + /* + * need to send client cb data for default + * user cb (userspace cb) + */ + payload.data = cb; + } else { + /* + * clear the cb entry. userspace cb entry + * will be cleared after data read by the + * polling thread or when client is destroyed + */ + if (synx_util_clear_cb_entry(client, cb)) + pr_err("%s: [sess: %u] error clearing cb entry\n", + __func__, client->id); + } + pr_debug("[sess: %u] kernel cb dispatch for handle %d\n", - client->id, payload->h_synx); + client->id, payload.h_synx); /* dispatch kernel callback */ - payload->cb_func(payload->h_synx, - synx_cb->status, - payload->data); + payload.cb_func(payload.h_synx, + payload.status, payload.data); fail: synx_put_client(client); @@ -926,7 +973,7 @@ static void synx_client_destroy(struct kref *kref) memset(client_metadata, 0, sizeof(*client_metadata)); clear_bit(client->id, synx_dev->bitmap); - pr_debug("[sess: %u] session destroyed %s\n", + pr_info("[sess: %u] session destroyed %s\n", client->id, client->name); kfree(client); } diff --git a/drivers/media/platform/msm/synx/synx_util.h b/drivers/media/platform/msm/synx/synx_util.h index 39706dd5de98..ccdf6b3fc79d 100644 --- a/drivers/media/platform/msm/synx/synx_util.h +++ b/drivers/media/platform/msm/synx/synx_util.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #ifndef __SYNX_UTIL_H__ @@ -112,25 +112,25 @@ void synx_util_object_destroy(struct synx_coredata *synx_obj); * @brief: Function to allocate synx_client_cb entry from cb table * * @param client : Pointer to client session info + * @param data : Kernel payload data * @param cb_idx : Allocated index (filled by function) * * @return Status of operation. Negative in case of error. Zero otherwise. */ int synx_util_alloc_cb_entry(struct synx_client *client, + struct synx_kernel_payload *data, u32 *cb_idx); /** - * @brief: Function to allocate synx_client_cb entry from cb table + * @brief: Function to clean up synx_client_cb entry from cb table * * @param client : Pointer to client session info - * @param data : Opaque payload data - * @param cb_idx : Callback table index + * @param cb : Pointer to client cb entry * * @return Status of operation. Negative in case of error. Zero otherwise. */ -int synx_util_update_cb_entry(struct synx_client *client, - void *data, - u32 cb_idx); +int synx_util_clear_cb_entry(struct synx_client *client, + struct synx_client_cb *cb); /** * @brief: Function to initialize synx object handle for the client @@ -155,6 +155,15 @@ int synx_util_init_handle(struct synx_client *client, */ int synx_util_activate(struct synx_coredata *synx_obj); +/** + * @brief: Default kernel callback function to handle userspace callbacks + * + * @param h_synx : Synx object handle + * @param status : Synx object state + * @param data : Opaque pointer + */ +void synx_util_default_user_callback(s32 h_synx, int status, void *data); + /** * @brief: Function to queue all the registered callbacks by clients for * the synx object