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

Commit 79563db9 authored by Tomas Winkler's avatar Tomas Winkler Committed by Greg Kroah-Hartman
Browse files

mei: add reference counting for me clients



To support dynamic addition and removal of
me clients we add reference counter.

Update kdoc with locking requirements.

Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3542f6b1
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -97,23 +97,25 @@ int mei_amthif_host_init(struct mei_device *dev)
	/* allocate storage for ME message buffer */
	msg_buf = kcalloc(dev->iamthif_mtu,
			sizeof(unsigned char), GFP_KERNEL);
	if (!msg_buf)
		return -ENOMEM;
	if (!msg_buf) {
		ret = -ENOMEM;
		goto out;
	}

	dev->iamthif_msg_buf = msg_buf;

	ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);

	if (ret < 0) {
		dev_err(dev->dev,
			"amthif: failed link client %d\n", ret);
		return ret;
		dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
		goto out;
	}

	ret = mei_cl_connect(cl, NULL);

	dev->iamthif_state = MEI_IAMTHIF_IDLE;

out:
	mei_me_cl_put(me_cl);
	return ret;
}

+23 −16
Original line number Diff line number Diff line
@@ -228,8 +228,8 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
			bool blocking)
{
	struct mei_device *dev;
	struct mei_me_client *me_cl;
	struct mei_cl_cb *cb;
	struct mei_me_client *me_cl = NULL;
	struct mei_cl_cb *cb = NULL;
	ssize_t rets;

	if (WARN_ON(!cl || !cl->dev))
@@ -237,33 +237,40 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,

	dev = cl->dev;

	if (cl->state != MEI_FILE_CONNECTED)
		return -ENODEV;
	mutex_lock(&dev->device_lock);
	if (cl->state != MEI_FILE_CONNECTED) {
		rets = -ENODEV;
		goto out;
	}

	/* Check if we have an ME client device */
	me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
	if (!me_cl)
		return -ENOTTY;
	if (!me_cl) {
		rets = -ENOTTY;
		goto out;
	}

	if (length > me_cl->props.max_msg_length)
		return -EFBIG;
	if (length > me_cl->props.max_msg_length) {
		rets = -EFBIG;
		goto out;
	}

	cb = mei_io_cb_init(cl, NULL);
	if (!cb)
		return -ENOMEM;
	if (!cb) {
		rets = -ENOMEM;
		goto out;
	}

	rets = mei_io_cb_alloc_req_buf(cb, length);
	if (rets < 0) {
		mei_io_cb_free(cb);
		return rets;
	}
	if (rets < 0)
		goto out;

	memcpy(cb->request_buffer.data, buf, length);

	mutex_lock(&dev->device_lock);

	rets = mei_cl_write(cl, cb, blocking);

out:
	mei_me_cl_put(me_cl);
	mutex_unlock(&dev->device_lock);
	if (rets < 0)
		mei_io_cb_free(cb);
+129 −23
Original line number Diff line number Diff line
@@ -26,8 +26,64 @@
#include "hbm.h"
#include "client.h"

/**
 * mei_me_cl_init - initialize me client
 *
 * @me_cl: me client
 */
void mei_me_cl_init(struct mei_me_client *me_cl)
{
	INIT_LIST_HEAD(&me_cl->list);
	kref_init(&me_cl->refcnt);
}

/**
 * mei_me_cl_get - increases me client refcount
 *
 * @me_cl: me client
 *
 * Locking: called under "dev->device_lock" lock
 *
 * Return: me client or NULL
 */
struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
{
	if (me_cl)
		kref_get(&me_cl->refcnt);

	return me_cl;
}

/**
 * mei_me_cl_release - unlink and free me client
 *
 * Locking: called under "dev->device_lock" lock
 *
 * @ref: me_client refcount
 */
static void mei_me_cl_release(struct kref *ref)
{
	struct mei_me_client *me_cl =
		container_of(ref, struct mei_me_client, refcnt);
	list_del(&me_cl->list);
	kfree(me_cl);
}
/**
 * mei_me_cl_put - decrease me client refcount and free client if necessary
 *
 * Locking: called under "dev->device_lock" lock
 *
 * @me_cl: me client
 */
void mei_me_cl_put(struct mei_me_client *me_cl)
{
	if (me_cl)
		kref_put(&me_cl->refcnt, mei_me_cl_release);
}

/**
 * mei_me_cl_by_uuid - locate me client by uuid
 *	increases ref count
 *
 * @dev: mei device
 * @uuid: me client uuid
@@ -43,13 +99,14 @@ struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,

	list_for_each_entry(me_cl, &dev->me_clients, list)
		if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
			return me_cl;
			return mei_me_cl_get(me_cl);

	return NULL;
}

/**
 * mei_me_cl_by_id - locate me client by client id
 *	increases ref count
 *
 * @dev: the device structure
 * @client_id: me client id
@@ -65,12 +122,14 @@ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)

	list_for_each_entry(me_cl, &dev->me_clients, list)
		if (me_cl->client_id == client_id)
			return me_cl;
			return mei_me_cl_get(me_cl);

	return NULL;
}

/**
 * mei_me_cl_by_uuid_id - locate me client by client id and uuid
 *	increases ref count
 *
 * @dev: the device structure
 * @uuid: me client uuid
@@ -88,32 +147,68 @@ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
	list_for_each_entry(me_cl, &dev->me_clients, list)
		if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 &&
		    me_cl->client_id == client_id)
			return me_cl;
			return mei_me_cl_get(me_cl);

	return NULL;
}

/**
 * mei_me_cl_remove - remove me client matching uuid and client_id
 * mei_me_cl_rm_by_uuid - remove all me clients matching uuid
 *
 * @dev: the device structure
 * @uuid: me client uuid
 * @client_id: me client address
 *
 * Locking: called under "dev->device_lock" lock
 */
void mei_me_cl_remove(struct mei_device *dev, const uuid_le *uuid, u8 client_id)
void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
{
	struct mei_me_client *me_cl, *next;

	dev_dbg(dev->dev, "remove %pUl\n", uuid);
	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
		if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
			mei_me_cl_put(me_cl);
}

/**
 * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id
 *
 * @dev: the device structure
 * @uuid: me client uuid
 * @id: me client id
 *
 * Locking: called under "dev->device_lock" lock
 */
void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
{
	struct mei_me_client *me_cl, *next;
	const uuid_le *pn;

	dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
		if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 &&
		    me_cl->client_id == client_id) {
			list_del(&me_cl->list);
			kfree(me_cl);
			break;
		pn =  &me_cl->props.protocol_name;
		if (me_cl->client_id == id && uuid_le_cmp(*uuid, *pn) == 0)
			mei_me_cl_put(me_cl);
	}
}

/**
 * mei_me_cl_rm_all - remove all me clients
 *
 * @dev: the device structure
 *
 * Locking: called under "dev->device_lock" lock
 */
void mei_me_cl_rm_all(struct mei_device *dev)
{
	struct mei_me_client *me_cl, *next;

	list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
			mei_me_cl_put(me_cl);
}



/**
 * mei_cl_cmp_id - tells if the clients are the same
 *
@@ -695,6 +790,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
{
	struct mei_device *dev;
	struct mei_me_client *me_cl;
	int rets = 0;

	if (WARN_ON(!cl || !cl->dev))
		return -EINVAL;
@@ -710,12 +806,13 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
		return -ENOENT;
	}

	if (me_cl->mei_flow_ctrl_creds) {
	if (me_cl->mei_flow_ctrl_creds > 0) {
		rets = 1;
		if (WARN_ON(me_cl->props.single_recv_buf == 0))
			return -EINVAL;
		return 1;
			rets = -EINVAL;
	}
	return 0;
	mei_me_cl_put(me_cl);
	return rets;
}

/**
@@ -732,6 +829,7 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
{
	struct mei_device *dev;
	struct mei_me_client *me_cl;
	int rets;

	if (WARN_ON(!cl || !cl->dev))
		return -EINVAL;
@@ -745,15 +843,22 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
	}

	if (me_cl->props.single_recv_buf) {
		if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
			return -EINVAL;
		if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) {
			rets = -EINVAL;
			goto out;
		}
		me_cl->mei_flow_ctrl_creds--;
	} else {
		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
			return -EINVAL;
		if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) {
			rets = -EINVAL;
			goto out;
		}
		cl->mei_flow_ctrl_creds--;
	}
	return 0;
	rets = 0;
out:
	mei_me_cl_put(me_cl);
	return rets;
}

/**
@@ -788,6 +893,9 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
		cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
		return  -ENOTTY;
	}
	/* always allocate at least client max message */
	length = max_t(size_t, length, me_cl->props.max_msg_length);
	mei_me_cl_put(me_cl);

	rets = pm_runtime_get(dev->dev);
	if (rets < 0 && rets != -EINPROGRESS) {
@@ -802,8 +910,6 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
		goto out;
	}

	/* always allocate at least client max message */
	length = max_t(size_t, length, me_cl->props.max_msg_length);
	rets = mei_io_cb_alloc_resp_buf(cb, length);
	if (rets)
		goto out;
+12 −5
Original line number Diff line number Diff line
@@ -24,15 +24,22 @@

#include "mei_dev.h"

/*
 * reference counting base function
 */
void mei_me_cl_init(struct mei_me_client *me_cl);
void mei_me_cl_put(struct mei_me_client *me_cl);
struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl);

struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
					const uuid_le *cuuid);
					const uuid_le *uuid);
struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id);

struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
					   const uuid_le *uuid, u8 client_id);

void mei_me_cl_remove(struct mei_device *dev,
		      const uuid_le *uuid, u8 client_id);
void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid);
void mei_me_cl_rm_by_uuid_id(struct mei_device *dev,
			     const uuid_le *uuid, u8 id);
void mei_me_cl_rm_all(struct mei_device *dev);

/*
 * MEI IO Functions
+20 −12
Original line number Diff line number Diff line
@@ -21,20 +21,22 @@
#include <linux/mei.h>

#include "mei_dev.h"
#include "client.h"
#include "hw.h"

static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
					size_t cnt, loff_t *ppos)
{
	struct mei_device *dev = fp->private_data;
	struct mei_me_client *me_cl;
	struct mei_me_client *me_cl, *n;
	size_t bufsz = 1;
	char *buf;
	int i = 0;
	int pos = 0;
	int ret;

#define HDR "  |id|fix|         UUID                       |con|msg len|sb|\n"
#define HDR \
"  |id|fix|         UUID                       |con|msg len|sb|refc|\n"

	mutex_lock(&dev->device_lock);

@@ -54,16 +56,22 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
	if (dev->dev_state != MEI_DEV_ENABLED)
		goto out;

	list_for_each_entry(me_cl, &dev->me_clients, list) {
	list_for_each_entry_safe(me_cl, n, &dev->me_clients, list) {

		me_cl = mei_me_cl_get(me_cl);
		if (me_cl) {
			pos += scnprintf(buf + pos, bufsz - pos,
			"%2d|%2d|%3d|%pUl|%3d|%7d|%2d|\n",
				"%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n",
				i++, me_cl->client_id,
				me_cl->props.fixed_address,
				&me_cl->props.protocol_name,
				me_cl->props.max_number_of_connections,
				me_cl->props.max_msg_length,
			me_cl->props.single_recv_buf);
				me_cl->props.single_recv_buf,
				atomic_read(&me_cl->refcnt.refcount));
		}

		mei_me_cl_put(me_cl);
	}
out:
	mutex_unlock(&dev->device_lock);
Loading