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

Commit b0a7db06 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: synx: redesign callback registration and dispatch"

parents 5e3eb7b6 8e24c5cc
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