dm: mei: implement rx flow.

Rx is triggered from epoll event (mevent), a host client
is retrieved from associated event data. P proper mei message header is
attached to the packet and sent to the virtio FE device.

Tracked-On: #1536
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Acked-by: Wang, Yu1 <yu1.wang@intel.com>
This commit is contained in:
Tomas Winkler 2018-10-19 11:41:04 +03:00 committed by wenlingz
parent 50ecd93b24
commit 6a1f824229

View File

@ -980,6 +980,8 @@ static int vmei_add_reset_event(struct virtio_mei *vmei)
return 0;
}
static void vmei_rx_callback(int fd, enum ev_type type, void *param);
static inline uint8_t
errno_to_hbm_conn_status(int err)
{
@ -1656,6 +1658,233 @@ vmei_host_client_native_read(struct vmei_host_client *hclient)
return hclient->recv_offset;
}
static void
vmei_rx_callback(int fd, enum ev_type type, void *param)
{
struct vmei_host_client *hclient = param;
struct virtio_mei *vmei = vmei_host_client_to_vmei(hclient);
ssize_t ret;
if (!vmei)
return;
if (!vmei_host_client_get(hclient)) {
DPRINTF("RX: client has been released, ignore data.\n");
return;
}
pthread_mutex_lock(&vmei->rx_mutex);
if (vmei->status != VMEI_STS_READY) {
HCL_DBG(hclient, "vmei is not ready %d\n", vmei->status);
goto out;
}
if (hclient->recv_offset) {
/* still has data in recv_buf, wait guest reading */
HCL_DBG(hclient, "data in recv_buf, wait for UOS reading.\n");
goto out;
}
/* read data from mei driver */
ret = vmei_host_client_native_read(hclient);
if (ret <= 0) {
HCL_DBG(hclient, "RX: no data %zd\n", ret);
goto out;
}
vmei->rx_need_sched = true;
HCL_DBG(hclient, "RX: read %zd bytes from the FW\n", ret);
/* wake up rx thread. */
pthread_cond_signal(&vmei->rx_cond);
out:
pthread_mutex_unlock(&vmei->rx_mutex);
vmei_host_client_put(hclient);
if (vmei->status == VMEI_STS_PENDING_RESET) {
vmei_virtual_fw_reset(vmei);
/* Let's wait 100ms for HBM enumeration done */
usleep(100000);
virtio_config_changed(&vmei->base);
}
}
/*
* Process the data received from native mei cdev and hbm emulation
* handler, assemable related mei header then copy to rx virtqueue.
*/
static void
vmei_proc_vclient_rx(struct vmei_host_client *hclient,
struct virtio_vq_info *vq)
{
struct iovec iov[VMEI_RX_SEGS + 1];
struct mei_msg_hdr *hdr;
uint16_t idx = 0;
int n, len;
int buf_len;
uint8_t *buf;
bool complete = true;
n = vq_getchain(vq, &idx, iov, VMEI_RX_SEGS, NULL);
assert(n == VMEI_RX_SEGS);
if (n != VMEI_RX_SEGS)
return;
len = hclient->recv_offset - hclient->recv_handled;
HCL_DBG(hclient, "RX: DM->UOS: off=%d len=%d\n",
hclient->recv_handled, len);
buf_len = VMEI_BUF_SZ - sizeof(*hdr);
hdr = (struct mei_msg_hdr *)iov[0].iov_base;
buf = (uint8_t *)iov[0].iov_base + sizeof(*hdr);
if (len > buf_len) {
len = buf_len;
complete = false;
}
mei_set_msg_hdr(hclient, hdr, len, complete);
memcpy(buf, hclient->recv_buf + hclient->recv_handled, len);
hclient->recv_handled += len;
HCL_DBG(hclient, "RX: complete = %d DM->UOS:off=%d len=%d\n",
complete, hclient->recv_handled, len);
len += sizeof(struct mei_msg_hdr);
if (complete) {
hclient->recv_offset = 0;
hclient->recv_handled = 0;
if (hclient->host_addr) {
hclient->recv_creds--;
mevent_disable(hclient->rx_mevp);
}
}
vq_relchain(vq, idx, len);
}
/**
* vmei_proc_rx() process rx UOS
* @vmei: virtio mei device
* @vq: virtio queue
*
* Function looks for client with pending buffer and sends
* it to the UOS.
*
* Locking: Must run under rx mutex
* Return:
* true - need reschedule
*/
static bool
vmei_proc_rx(struct virtio_mei *vmei, struct virtio_vq_info *vq)
{
struct vmei_me_client *me;
struct vmei_host_client *e, *hclient = NULL;
/* Find a client with data */
pthread_mutex_lock(&vmei->list_mutex);
LIST_FOREACH(me, &vmei->active_clients, list) {
pthread_mutex_lock(&me->list_mutex);
LIST_FOREACH(e, &me->connections, list) {
if (e->recv_offset - e->recv_handled > 0) {
if (e->recv_creds) {
hclient = vmei_host_client_get(e);
pthread_mutex_unlock(&me->list_mutex);
goto found;
}
}
}
pthread_mutex_unlock(&me->list_mutex);
}
found:
pthread_mutex_unlock(&vmei->list_mutex);
/* no client has data to be processed */
if (!hclient) {
DPRINTF("RX: No client with data\n");
return false;
}
vmei_proc_vclient_rx(hclient, vq);
pthread_mutex_lock(&vmei->tx_mutex);
if (vmei_host_ready_send_buffers(hclient))
pthread_cond_signal(&vmei->tx_cond);
pthread_mutex_unlock(&vmei->tx_mutex);
vmei_host_client_put(hclient);
return true;
}
/*
* Thread which will handle processing of RX desc
*/
static void *vmei_rx_thread(void *param)
{
struct virtio_mei *vmei = param;
struct virtio_vq_info *vq;
int err;
vq = &vmei->vqs[VMEI_RXQ];
/*
* Let us wait till the rx queue pointers get initialised &
* first tx signaled
*/
pthread_mutex_lock(&vmei->rx_mutex);
err = pthread_cond_wait(&vmei->rx_cond, &vmei->rx_mutex);
assert(err == 0);
if (err)
goto out;
while (vmei->status != VMEI_STST_DEINIT) {
/* note - rx mutex is locked here */
while (vq_ring_ready(vq)) {
vq->used->flags &= ~ACRN_VRING_USED_F_NO_NOTIFY;
mb();
if (vq_has_descs(vq) &&
vmei->rx_need_sched &&
vmei->status != VMEI_STS_RESET)
break;
err = pthread_cond_wait(&vmei->rx_cond,
&vmei->rx_mutex);
assert(err == 0);
if (err || vmei->status == VMEI_STST_DEINIT)
goto out;
}
vq->used->flags |= ACRN_VRING_USED_F_NO_NOTIFY;
do {
vmei->rx_need_sched = vmei_proc_rx(vmei, vq);
} while (vq_has_descs(vq) && vmei->rx_need_sched);
if (!vq_has_descs(vq))
vq_endchains(vq, 1);
}
out:
pthread_mutex_unlock(&vmei->rx_mutex);
pthread_exit(NULL);
}
static void
vmei_notify_rx(void *data, struct virtio_vq_info *vq)
{
struct virtio_mei *vmei = data;
/*
* Any ring entries to process?
*/
if (!vq_has_descs(vq))
return;
/* Signal the rx thread for processing */
pthread_mutex_lock(&vmei->rx_mutex);
DPRINTF("RX: New IN buffer available!\n");
vq->used->flags |= ACRN_VRING_USED_F_NO_NOTIFY;
pthread_cond_signal(&vmei->rx_cond);
pthread_mutex_unlock(&vmei->rx_mutex);
}
static int
vmei_cfgread(void *vsc, int offset, int size, uint32_t *retval)
{