Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e24c5cc authored by Sumukh Hallymysore Ravindra's avatar Sumukh Hallymysore Ravindra Committed by Gerrit - the friendly Code Review server
Browse files

msm: synx: redesign callback registration and dispatch



The changes modify the callback registration and dispatch
on a synx object for non-blocking wait.
The userspace callback payloads are maintained in UMD driver
and unique id passed by client is saved in callback entry
to manage the callback. These callbacks are distinguished
with the callbacks registered by kernel clients through the
default callback function registered.
This provides uniform handling (in most cases) for callbacks
registered by userspace and kernel clients.
The change also allows signaling with CANCELLED state.

Change-Id: Ic084374c63c3c09c5deccb404e81da69f1dcb70e
Signed-off-by: default avatarSumukh Hallymysore Ravindra <shallymy@codeaurora.org>
parent 41c8b9e2
Loading
Loading
Loading
Loading
+46 −132
Original line number Diff line number Diff line
// 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,18 +305,14 @@ int synx_signal(struct synx_session session_id, s32 h_synx, u32 status)
}
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) {
@@ -325,50 +321,13 @@ static int synx_match_payload(struct synx_client_cb *cb,
			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");
			}
			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;
+5 −20
Original line number Diff line number Diff line
/* 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;
};

+71 −24
Original line number Diff line number Diff line
// 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,28 +645,53 @@ int synx_util_alloc_cb_entry(struct synx_client *client,

	cb = &client->cb_table[idx];
	memset(cb, 0, sizeof(*cb));
	cb->is_valid = true;
	cb->client = client;
	cb->idx = idx;
	memcpy(&cb->kernel_cb, data,
		sizeof(cb->kernel_cb));

	*cb_idx = idx;
	pr_debug("[sess: %u] allocated cb index %u\n", client->id, idx);
	pr_debug("[sess: %u] allocated cb index %u\n", client->id, *cb_idx);
	return 0;
}

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)
{
	struct synx_client_cb *cb;
	int rc = 0;
	u32 idx;

	if (!client || !data || (cb_idx >= SYNX_MAX_OBJS))
	if (!cb)
		return -EINVAL;

	cb = &client->cb_table[cb_idx];
	cb->is_valid = true;
	memcpy(&cb->kernel_cb, data,
		sizeof(cb->kernel_cb));
	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;
	}

	pr_debug("[sess: %u] updated cb index %u\n", client->id, cb_idx);
	return 0;
	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)
@@ -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);
}
+16 −7
Original line number Diff line number Diff line
/* 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