Skip to content

Commit

Permalink
mei: add support for mei extended header.
Browse files Browse the repository at this point in the history
Add an extend header beyond existing 4 bytes of the mei message header.
The extension is of variable length, starting with meta header
that contains the number of headers and the overall size of
the extended headers excluding meta header itself followed by
TLV list of extended headers. Currently only supported extension is
the vtag. From the HW perspective the extended headers is already
part of the payload.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Alexander Usyskin <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
tomasbw authored and gregkh committed Aug 18, 2020
1 parent 2dd1e5a commit 0cd7c01
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 86 deletions.
189 changes: 133 additions & 56 deletions drivers/misc/mei/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,8 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
cb->cl = cl;
cb->buf_idx = 0;
cb->fop_type = type;
cb->vtag = 0;

return cb;
}

Expand Down Expand Up @@ -1518,21 +1520,67 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
return rets;
}

static inline u8 mei_ext_hdr_set_vtag(struct mei_ext_hdr *ext, u8 vtag)
{
ext->type = MEI_EXT_HDR_VTAG;
ext->ext_payload[0] = vtag;
ext->length = mei_data2slots(sizeof(*ext));
return ext->length;
}

/**
* mei_msg_hdr_init - initialize mei message header
* mei_msg_hdr_init - allocate and initialize mei message header
*
* @mei_hdr: mei message header
* @cb: message callback structure
*
* Return: a pointer to initialized header
*/
static void mei_msg_hdr_init(struct mei_msg_hdr *mei_hdr, struct mei_cl_cb *cb)
static struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb)
{
size_t hdr_len;
struct mei_ext_meta_hdr *meta;
struct mei_ext_hdr *ext;
struct mei_msg_hdr *mei_hdr;
bool is_ext, is_vtag;

if (!cb)
return ERR_PTR(-EINVAL);

/* Extended header for vtag is attached only on the first fragment */
is_vtag = (cb->vtag && cb->buf_idx == 0);
is_ext = is_vtag;

/* Compute extended header size */
hdr_len = sizeof(*mei_hdr);

if (!is_ext)
goto setup_hdr;

hdr_len += sizeof(*meta);
if (is_vtag)
hdr_len += sizeof(*ext);

setup_hdr:
mei_hdr = kzalloc(hdr_len, GFP_KERNEL);
if (!mei_hdr)
return ERR_PTR(-ENOMEM);

mei_hdr->host_addr = mei_cl_host_addr(cb->cl);
mei_hdr->me_addr = mei_cl_me_id(cb->cl);
mei_hdr->length = 0;
mei_hdr->reserved = 0;
mei_hdr->msg_complete = 0;
mei_hdr->dma_ring = 0;
mei_hdr->internal = cb->internal;
mei_hdr->extended = is_ext;

if (!is_ext)
goto out;

meta = (struct mei_ext_meta_hdr *)mei_hdr->extension;
if (is_vtag) {
meta->count++;
meta->size += mei_ext_hdr_set_vtag(meta->hdrs, cb->vtag);
}
out:
mei_hdr->length = hdr_len - sizeof(*mei_hdr);
return mei_hdr;
}

/**
Expand All @@ -1550,10 +1598,11 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
{
struct mei_device *dev;
struct mei_msg_data *buf;
struct mei_msg_hdr mei_hdr;
size_t hdr_len = sizeof(mei_hdr);
size_t len;
struct mei_msg_hdr *mei_hdr = NULL;
size_t hdr_len;
size_t hbuf_len, dr_len;
size_t buf_len;
size_t data_len;
int hbuf_slots;
u32 dr_slots;
u32 dma_len;
Expand All @@ -1579,7 +1628,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
return 0;
}

len = buf->size - cb->buf_idx;
buf_len = buf->size - cb->buf_idx;
data = buf->data + cb->buf_idx;
hbuf_slots = mei_hbuf_empty_slots(dev);
if (hbuf_slots < 0) {
Expand All @@ -1591,42 +1640,54 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
dr_slots = mei_dma_ring_empty_slots(dev);
dr_len = mei_slots2data(dr_slots);

mei_msg_hdr_init(&mei_hdr, cb);
mei_hdr = mei_msg_hdr_init(cb);
if (IS_ERR(mei_hdr)) {
rets = PTR_ERR(mei_hdr);
mei_hdr = NULL;
goto err;
}

cl_dbg(dev, cl, "Extended Header %d vtag = %d\n",
mei_hdr->extended, cb->vtag);

hdr_len = sizeof(*mei_hdr) + mei_hdr->length;

/**
* Split the message only if we can write the whole host buffer
* otherwise wait for next time the host buffer is empty.
*/
if (len + hdr_len <= hbuf_len) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
if (hdr_len + buf_len <= hbuf_len) {
data_len = buf_len;
mei_hdr->msg_complete = 1;
} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
mei_hdr.dma_ring = 1;
if (len > dr_len)
len = dr_len;
mei_hdr->dma_ring = 1;
if (buf_len > dr_len)
buf_len = dr_len;
else
mei_hdr.msg_complete = 1;
mei_hdr->msg_complete = 1;

mei_hdr.length = sizeof(dma_len);
dma_len = len;
data_len = sizeof(dma_len);
dma_len = buf_len;
data = &dma_len;
} else if ((u32)hbuf_slots == mei_hbuf_depth(dev)) {
len = hbuf_len - hdr_len;
mei_hdr.length = len;
buf_len = hbuf_len - hdr_len;
data_len = buf_len;
} else {
kfree(mei_hdr);
return 0;
}
mei_hdr->length += data_len;

if (mei_hdr.dma_ring)
mei_dma_ring_write(dev, buf->data + cb->buf_idx, len);
if (mei_hdr->dma_ring)
mei_dma_ring_write(dev, buf->data + cb->buf_idx, buf_len);
rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len);

rets = mei_write_message(dev, &mei_hdr, hdr_len, data, mei_hdr.length);
if (rets)
goto err;

cl->status = 0;
cl->writing_state = MEI_WRITING;
cb->buf_idx += len;
cb->buf_idx += buf_len;

if (first_chunk) {
if (mei_cl_tx_flow_ctrl_creds_reduce(cl)) {
Expand All @@ -1635,12 +1696,14 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
}
}

if (mei_hdr.msg_complete)
if (mei_hdr->msg_complete)
list_move_tail(&cb->list, &dev->write_waiting_list);

kfree(mei_hdr);
return 0;

err:
kfree(mei_hdr);
cl->status = rets;
list_move_tail(&cb->list, cmpl_list);
return rets;
Expand All @@ -1659,9 +1722,11 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev;
struct mei_msg_data *buf;
struct mei_msg_hdr mei_hdr;
size_t hdr_len = sizeof(mei_hdr);
size_t len, hbuf_len, dr_len;
struct mei_msg_hdr *mei_hdr = NULL;
size_t hdr_len;
size_t hbuf_len, dr_len;
size_t buf_len;
size_t data_len;
int hbuf_slots;
u32 dr_slots;
u32 dma_len;
Expand All @@ -1678,9 +1743,9 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
dev = cl->dev;

buf = &cb->buf;
len = buf->size;
buf_len = buf->size;

cl_dbg(dev, cl, "len=%zd\n", len);
cl_dbg(dev, cl, "buf_len=%zd\n", buf_len);

blocking = cb->blocking;
data = buf->data;
Expand All @@ -1700,17 +1765,27 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
if (rets < 0)
goto err;

mei_msg_hdr_init(&mei_hdr, cb);
mei_hdr = mei_msg_hdr_init(cb);
if (IS_ERR(mei_hdr)) {
rets = -PTR_ERR(mei_hdr);
mei_hdr = NULL;
goto err;
}

cl_dbg(dev, cl, "Extended Header %d vtag = %d\n",
mei_hdr->extended, cb->vtag);

hdr_len = sizeof(*mei_hdr) + mei_hdr->length;

if (rets == 0) {
cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
rets = len;
rets = buf_len;
goto out;
}

if (!mei_hbuf_acquire(dev)) {
cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
rets = len;
rets = buf_len;
goto out;
}

Expand All @@ -1724,29 +1799,30 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
dr_slots = mei_dma_ring_empty_slots(dev);
dr_len = mei_slots2data(dr_slots);

if (len + hdr_len <= hbuf_len) {
mei_hdr.length = len;
mei_hdr.msg_complete = 1;
if (hdr_len + buf_len <= hbuf_len) {
data_len = buf_len;
mei_hdr->msg_complete = 1;
} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
mei_hdr.dma_ring = 1;
if (len > dr_len)
len = dr_len;
mei_hdr->dma_ring = 1;
if (buf_len > dr_len)
buf_len = dr_len;
else
mei_hdr.msg_complete = 1;
mei_hdr->msg_complete = 1;

mei_hdr.length = sizeof(dma_len);
dma_len = len;
data_len = sizeof(dma_len);
dma_len = buf_len;
data = &dma_len;
} else {
len = hbuf_len - hdr_len;
mei_hdr.length = len;
buf_len = hbuf_len - hdr_len;
data_len = buf_len;
}

if (mei_hdr.dma_ring)
mei_dma_ring_write(dev, buf->data, len);
mei_hdr->length += data_len;

if (mei_hdr->dma_ring)
mei_dma_ring_write(dev, buf->data, buf_len);
rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len);

rets = mei_write_message(dev, &mei_hdr, hdr_len,
data, mei_hdr.length);
if (rets)
goto err;

Expand All @@ -1755,12 +1831,12 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
goto err;

cl->writing_state = MEI_WRITING;
cb->buf_idx = len;
cb->buf_idx = buf_len;
/* restore return value */
len = buf->size;
buf_len = buf->size;

out:
if (mei_hdr.msg_complete)
if (mei_hdr->msg_complete)
mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
else
mei_tx_cb_enqueue(cb, &dev->write_list);
Expand All @@ -1785,18 +1861,19 @@ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
}
}

rets = len;
rets = buf_len;
err:
cl_dbg(dev, cl, "rpm: autosuspend\n");
pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
free:
mei_io_cb_free(cb);

kfree(mei_hdr);

return rets;
}


/**
* mei_cl_complete - processes completed operation for a client
*
Expand Down
14 changes: 5 additions & 9 deletions drivers/misc/mei/hbm.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,15 @@ void mei_hbm_reset(struct mei_device *dev)
/**
* mei_hbm_hdr - construct hbm header
*
* @hdr: hbm header
* @mei_hdr: hbm header
* @length: payload length
*/

static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
static inline void mei_hbm_hdr(struct mei_msg_hdr *mei_hdr, size_t length)
{
hdr->host_addr = 0;
hdr->me_addr = 0;
hdr->length = length;
hdr->msg_complete = 1;
hdr->dma_ring = 0;
hdr->reserved = 0;
hdr->internal = 0;
memset(mei_hdr, 0, sizeof(*mei_hdr));
mei_hdr->length = length;
mei_hdr->msg_complete = 1;
}

/**
Expand Down
Loading

0 comments on commit 0cd7c01

Please sign in to comment.