From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin 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 Signed-off-by: Tomas Winkler --- 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