clear-pkgs-linux-iot-lts2018/0145-mei-add-connect-with-v...

592 lines
16 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alexander Usyskin <alexander.usyskin@intel.com>
Date: Thu, 2 Aug 2018 14:54:47 +0300
Subject: [PATCH] mei: add connect with vtag ioctl
Change-Id: I3d68af4fd71bacac1180b80ace6d3b843142069c
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
---
drivers/misc/mei/client.c | 82 +++++++++++++-
drivers/misc/mei/client.h | 10 +-
drivers/misc/mei/main.c | 227 ++++++++++++++++++++++++++++++++++---
drivers/misc/mei/mei_dev.h | 9 ++
include/uapi/linux/mei.h | 19 ++++
5 files changed, 320 insertions(+), 27 deletions(-)
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 989fc73d241c..11749f1e90c7 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -364,6 +364,19 @@ static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb)
mei_io_cb_free(cb);
}
+static void mei_cl_set_read_by_fp(const struct mei_cl *cl,
+ const struct file *fp)
+{
+ struct mei_cl_vtag *cl_vtag;
+
+ list_for_each_entry(cl_vtag, &cl->vtag_map, list) {
+ if (cl_vtag->fp == fp) {
+ cl_vtag->pending_read = true;
+ return;
+ }
+ }
+}
+
/**
* mei_io_cb_init - allocate and initialize io callback
*
@@ -389,6 +402,7 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
cb->buf_idx = 0;
cb->fop_type = type;
cb->vtag = 0;
+
return cb;
}
@@ -571,6 +585,7 @@ static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
init_waitqueue_head(&cl->rx_wait);
init_waitqueue_head(&cl->tx_wait);
init_waitqueue_head(&cl->ev_wait);
+ INIT_LIST_HEAD(&cl->vtag_map);
spin_lock_init(&cl->rd_completed_lock);
INIT_LIST_HEAD(&cl->rd_completed);
INIT_LIST_HEAD(&cl->rd_pending);
@@ -1243,6 +1258,61 @@ static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
return 0;
}
+const struct file *mei_cl_fp_by_vtag(const struct mei_cl *cl, u8 vtag)
+{
+ struct mei_cl_vtag *vtag_l;
+
+ list_for_each_entry(vtag_l, &cl->vtag_map, list)
+ if (vtag_l->vtag == vtag)
+ return vtag_l->fp;
+
+ return NULL;
+}
+
+static void mei_cl_reset_read_by_vtag(const struct mei_cl *cl, u8 vtag)
+{
+ struct mei_cl_vtag *vtag_l;
+
+ list_for_each_entry(vtag_l, &cl->vtag_map, list) {
+ if (vtag_l->vtag == vtag) {
+ vtag_l->pending_read = false;
+ break;
+ }
+ }
+}
+
+static void mei_cl_read_vtag_add_fc(struct mei_cl *cl)
+{
+ struct mei_cl_vtag *cl_vtag;
+
+ list_for_each_entry(cl_vtag, &cl->vtag_map, list) {
+ if (cl_vtag->pending_read) {
+ if (mei_cl_enqueue_ctrl_wr_cb(cl,
+ mei_cl_mtu(cl),
+ MEI_FOP_READ,
+ cl_vtag->fp))
+ cl->rx_flow_ctrl_creds++;
+ break;
+ }
+ }
+}
+
+void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+ const struct file *fp;
+
+ fp = mei_cl_fp_by_vtag(cl, cb->vtag);
+ if (fp)
+ cb->fp = fp;
+ mei_cl_reset_read_by_vtag(cl, cb->vtag);
+
+ spin_lock(&cl->rd_completed_lock);
+ list_add_tail(&cb->list, &cl->rd_completed);
+ spin_unlock(&cl->rd_completed_lock);
+
+ mei_cl_read_vtag_add_fc(cl);
+}
+
/**
* mei_cl_notify_fop2req - convert fop to proper request
*
@@ -1498,13 +1568,17 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
return 0;
/* HW currently supports only one pending read */
- if (cl->rx_flow_ctrl_creds)
+ if (cl->rx_flow_ctrl_creds) {
+ mei_cl_set_read_by_fp(cl, fp);
return -EBUSY;
+ }
cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
if (!cb)
return -ENOMEM;
+ mei_cl_set_read_by_fp(cl, fp);
+
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
@@ -1622,6 +1696,9 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
hdr_len = mei_msg_hdr_init(mei_hdr, cb);
+ cl_dbg(dev, cl, "Extend Header %d vtag = %d\n",
+ mei_hdr->extended, cb->vtag);
+
/**
* Split the message only if we can write the whole host buffer
* otherwise wait for next time the host buffer is empty.
@@ -1732,6 +1809,9 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
hdr_len = mei_msg_hdr_init(mei_hdr, cb);
+ cl_dbg(dev, cl, "Extend Header %d vtag = %d\n",
+ mei_hdr->extended, cb->vtag);
+
if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
rets = len;
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index bb74eea3dba5..656eadea32a4 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -96,14 +96,7 @@ int mei_cl_unlink(struct mei_cl *cl);
struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev);
struct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp);
-
-static inline void mei_cl_add_rd_completed(struct mei_cl *cl,
- struct mei_cl_cb *cb)
-{
- spin_lock(&cl->rd_completed_lock);
- list_add_tail(&cb->list, &cl->rd_completed);
- spin_unlock(&cl->rd_completed_lock);
-}
+void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb);
static inline void mei_cl_del_rd_completed(struct mei_cl *cl,
struct mei_cl_cb *cb)
@@ -121,6 +114,7 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
const struct file *fp);
int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
+const struct file *mei_cl_fp_by_vtag(const struct mei_cl *cl, u8 vtag);
/*
* MEI input output function prototype
*/
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 6227b401ce3e..697f18d6b55a 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -83,6 +83,20 @@ static int mei_open(struct inode *inode, struct file *file)
return err;
}
+static void mei_cl_vtag_remove_by_fp(const struct mei_cl *cl,
+ const struct file *fp)
+{
+ struct mei_cl_vtag *vtag_l, *next;
+
+ list_for_each_entry_safe(vtag_l, next, &cl->vtag_map, list) {
+ if (vtag_l->fp == fp) {
+ list_del(&vtag_l->list);
+ kfree(vtag_l);
+ return;
+ }
+ }
+}
+
/**
* mei_release - the release function
*
@@ -104,17 +118,26 @@ static int mei_release(struct inode *inode, struct file *file)
mutex_lock(&dev->device_lock);
+ mei_cl_vtag_remove_by_fp(cl, file);
+
+ if (!list_empty(&cl->vtag_map)) {
+ cl_dbg(dev, cl, "not the last vtag\n");
+ mei_cl_flush_queues(cl, file);
+ rets = 0;
+ goto out;
+ }
+
rets = mei_cl_disconnect(cl);
mei_cl_flush_queues(cl, file);
cl_dbg(dev, cl, "removing\n");
mei_cl_unlink(cl);
+ kfree(cl);
+out:
file->private_data = NULL;
- kfree(cl);
-
mutex_unlock(&dev->device_lock);
return rets;
}
@@ -240,6 +263,20 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_unlock(&dev->device_lock);
return rets;
}
+
+static u8 mei_cl_vtag_by_fp(const struct mei_cl *cl, const struct file *fp)
+{
+ struct mei_cl_vtag *cl_vtag;
+
+ if (!fp)
+ return 0;
+
+ list_for_each_entry(cl_vtag, &cl->vtag_map, list)
+ if (cl_vtag->fp == fp)
+ return cl_vtag->vtag;
+ return 0;
+}
+
/**
* mei_write - the write function.
*
@@ -317,6 +354,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
rets = -ENOMEM;
goto out;
}
+ cb->vtag = mei_cl_vtag_by_fp(cl, file);
rets = copy_from_user(cb->buf.data, ubuf, length);
if (rets) {
@@ -336,17 +374,18 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
* mei_ioctl_connect_client - the connect to fw client IOCTL function
*
* @file: private data of the file object
- * @data: IOCTL connect data, input and output parameters
+ * @in_client_uuid: requested UUID for connection
+ * @client: IOCTL connect data, output parameters
*
* Locking: called under "dev->device_lock" lock
*
* Return: 0 on success, <0 on failure.
*/
static int mei_ioctl_connect_client(struct file *file,
- struct mei_connect_client_data *data)
+ const uuid_le *in_client_uuid,
+ struct mei_client *client)
{
struct mei_device *dev;
- struct mei_client *client;
struct mei_me_client *me_cl;
struct mei_cl *cl;
int rets;
@@ -354,18 +393,15 @@ static int mei_ioctl_connect_client(struct file *file,
cl = file->private_data;
dev = cl->dev;
- if (dev->dev_state != MEI_DEV_ENABLED)
- return -ENODEV;
-
if (cl->state != MEI_FILE_INITIALIZING &&
cl->state != MEI_FILE_DISCONNECTED)
return -EBUSY;
/* find ME client we're trying to connect to */
- me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
+ me_cl = mei_me_cl_by_uuid(dev, in_client_uuid);
if (!me_cl) {
dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
- &data->in_client_uuid);
+ in_client_uuid);
rets = -ENOTTY;
goto end;
}
@@ -375,7 +411,7 @@ static int mei_ioctl_connect_client(struct file *file,
!dev->allow_fixed_address : !dev->hbm_f_fa_supported;
if (forbidden) {
dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n",
- &data->in_client_uuid);
+ in_client_uuid);
rets = -ENOTTY;
goto end;
}
@@ -389,7 +425,6 @@ static int mei_ioctl_connect_client(struct file *file,
me_cl->props.max_msg_length);
/* prepare the output buffer */
- client = &data->out_client_properties;
client->max_msg_length = me_cl->props.max_msg_length;
client->protocol_version = me_cl->props.protocol_version;
dev_dbg(dev->dev, "Can connect?\n");
@@ -401,6 +436,113 @@ static int mei_ioctl_connect_client(struct file *file,
return rets;
}
+static int mei_cl_vm_support_check(struct mei_device *dev, const uuid_le *uuid)
+{
+ struct mei_me_client *me_cl;
+ int ret;
+
+ if (!dev->hbm_f_vm_supported) {
+ dev_dbg(dev->dev, "VTag not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ me_cl = mei_me_cl_by_uuid(dev, uuid);
+ if (!me_cl) {
+ dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n",
+ uuid);
+ return -ENOTTY;
+ }
+ ret = me_cl->props.vm_supported ? 0 : -EOPNOTSUPP;
+ mei_me_cl_put(me_cl);
+
+ return ret;
+}
+
+static struct mei_cl_vtag *mei_cl_vtag_alloc(struct file *fp, u8 vtag)
+{
+ struct mei_cl_vtag *cl_vtag;
+
+ cl_vtag = kzalloc(sizeof(*cl_vtag), GFP_KERNEL);
+ if (!cl_vtag)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cl_vtag->list);
+ cl_vtag->vtag = vtag;
+ cl_vtag->fp = fp;
+
+ return cl_vtag;
+}
+
+static int mei_ioctl_connect_vtag(struct file *file,
+ const uuid_le *in_client_uuid,
+ struct mei_client *client,
+ u8 vtag)
+{
+ struct mei_device *dev;
+ struct mei_cl *cl;
+ struct mei_cl *pos;
+ struct mei_cl_vtag *cl_vtag;
+
+ cl = file->private_data;
+ dev = cl->dev;
+
+ dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag);
+
+ if (cl->state != MEI_FILE_INITIALIZING &&
+ cl->state != MEI_FILE_DISCONNECTED)
+ return -EBUSY;
+
+ list_for_each_entry(pos, &dev->file_list, link) {
+ if (pos == cl)
+ continue;
+ if (!pos->me_cl)
+ continue;
+
+ /* FIXME: just compare me_cl addr */
+ if (uuid_le_cmp(*mei_cl_uuid(pos), *in_client_uuid))
+ continue;
+
+ /* if tag already exist try another fp */
+ if (mei_cl_fp_by_vtag(pos, vtag))
+ continue;
+
+ /* replace cl with acquired one */
+ dev_dbg(dev->dev, "replacing with existing cl\n");
+ mei_cl_unlink(cl);
+ kfree(cl);
+ file->private_data = pos;
+ cl = pos;
+ break;
+ }
+
+ cl_vtag = mei_cl_vtag_alloc(file, vtag);
+ if (IS_ERR(cl_vtag))
+ return -ENOMEM;
+
+ list_add_tail(&cl_vtag->list, &cl->vtag_map);
+
+ while (cl->state != MEI_FILE_INITIALIZING &&
+ cl->state != MEI_FILE_DISCONNECTED &&
+ cl->state != MEI_FILE_CONNECTED) {
+ mutex_unlock(&dev->device_lock);
+ wait_event_timeout(cl->wait,
+ (cl->state == MEI_FILE_CONNECTED ||
+ cl->state == MEI_FILE_DISCONNECTED ||
+ cl->state == MEI_FILE_DISCONNECT_REQUIRED ||
+ cl->state == MEI_FILE_DISCONNECT_REPLY),
+ mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
+ mutex_lock(&dev->device_lock);
+ }
+
+ if (!mei_cl_is_connected(cl))
+ return mei_ioctl_connect_client(file, in_client_uuid, client);
+
+ client->max_msg_length = cl->me_cl->props.max_msg_length;
+ client->protocol_version = cl->me_cl->props.protocol_version;
+
+ return 0;
+}
+
/**
* mei_ioctl_client_notify_request -
* propagate event notification request to client
@@ -457,7 +599,11 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
{
struct mei_device *dev;
struct mei_cl *cl = file->private_data;
- struct mei_connect_client_data connect_data;
+ struct mei_connect_client_data conn;
+ struct mei_connect_client_data_vtag conn_vtag;
+ const uuid_le *cl_uuid;
+ struct mei_client *props;
+ u8 vtag;
u32 notify_get, notify_req;
int rets;
@@ -478,20 +624,65 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
switch (cmd) {
case IOCTL_MEI_CONNECT_CLIENT:
dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
- if (copy_from_user(&connect_data, (char __user *)data,
- sizeof(struct mei_connect_client_data))) {
+ if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) {
dev_dbg(dev->dev, "failed to copy data from userland\n");
rets = -EFAULT;
goto out;
}
+ cl_uuid = &conn.in_client_uuid;
+ props = &conn.out_client_properties;
+ vtag = 0;
+
+ if (!mei_cl_vm_support_check(dev, cl_uuid))
+ rets = mei_ioctl_connect_vtag(file, cl_uuid, props,
+ vtag);
+ else
+ rets = mei_ioctl_connect_client(file, cl_uuid, props);
+ if (rets)
+ goto out;
+
+ /* if all is ok, copying the data back to user. */
+ if (copy_to_user((char __user *)data, &conn, sizeof(conn))) {
+ dev_dbg(dev->dev, "failed to copy data to userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+
+ break;
+
+ case IOCTL_MEI_CONNECT_CLIENT_VTAG:
+ dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n");
+ if (copy_from_user(&conn_vtag, (char __user *)data,
+ sizeof(conn_vtag))) {
+ dev_dbg(dev->dev, "failed to copy data from userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+
+ cl_uuid = &conn_vtag.connect.in_client_uuid;
+ props = &conn_vtag.out_client_properties;
+ vtag = conn_vtag.connect.vtag;
+
+ if (mei_cl_vm_support_check(dev, cl_uuid)) {
+ dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n",
+ cl_uuid);
+ rets = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!vtag) {
+ dev_dbg(dev->dev, "vtag can't be zero\n");
+ rets = -EINVAL;
+ goto out;
+ }
- rets = mei_ioctl_connect_client(file, &connect_data);
+ rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag);
if (rets)
goto out;
/* if all is ok, copying the data back to user. */
- if (copy_to_user((char __user *)data, &connect_data,
- sizeof(struct mei_connect_client_data))) {
+ if (copy_to_user((char __user *)data, &conn_vtag,
+ sizeof(conn_vtag))) {
dev_dbg(dev->dev, "failed to copy data to userland\n");
rets = -EFAULT;
goto out;
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 0467d0529667..f385c27de023 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -203,6 +203,13 @@ struct mei_cl_cb {
u32 blocking:1;
};
+struct mei_cl_vtag {
+ struct list_head list;
+ const struct file *fp;
+ u8 vtag;
+ u8 pending_read:1;
+};
+
/**
* struct mei_cl - me client host representation
* carried in file->private_data
@@ -219,6 +226,7 @@ struct mei_cl_cb {
* @me_cl: fw client connected
* @fp: file associated with client
* @host_client_id: host id
+ * @vtag_map: vm tag map
* @tx_flow_ctrl_creds: transmit flow credentials
* @rx_flow_ctrl_creds: receive flow credentials
* @timer_count: watchdog timer for operation completion
@@ -245,6 +253,7 @@ struct mei_cl {
struct mei_me_client *me_cl;
const struct file *fp;
u8 host_client_id;
+ struct list_head vtag_map;
u8 tx_flow_ctrl_creds;
u8 rx_flow_ctrl_creds;
u8 timer_count;
diff --git a/include/uapi/linux/mei.h b/include/uapi/linux/mei.h
index 0f681cbd38d3..0bc184bc0f46 100644
--- a/include/uapi/linux/mei.h
+++ b/include/uapi/linux/mei.h
@@ -127,4 +127,23 @@ struct mei_connect_client_data {
*/
#define IOCTL_MEI_NOTIFY_GET _IOR('H', 0x03, __u32)
+/*
+ * IOCTL Connect Client Data structure with vtag
+ */
+struct mei_connect_client_vtag {
+ uuid_le in_client_uuid;
+ __u8 vtag;
+ __u8 reserved[3];
+};
+
+struct mei_connect_client_data_vtag {
+ union {
+ struct mei_connect_client_vtag connect;
+ struct mei_client out_client_properties;
+ };
+};
+
+#define IOCTL_MEI_CONNECT_CLIENT_VTAG \
+ _IOWR('H', 0x04, struct mei_connect_client_data_vtag)
+
#endif /* _LINUX_MEI_H */
--
https://clearlinux.org