dm: mei: add client management infrastructure

1. virtio_mei structure represents an instance of mei device.
2. vmei_me_client represents an ME application in the MEI FW.
3. vmei_host_client represent a host application talking to the
ME application, ME application can support multiple connections.
4. Add debug helpers

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-16 01:09:44 +03:00 committed by wenlingz
parent 445f419304
commit a632ac3dae

View File

@ -153,6 +153,57 @@ refcnt_put(const struct refcnt *ref)
ref->destroy(ref);
}
static int vmei_debug;
static FILE *vmei_dbg_file;
#define DPRINTF(format, arg...) do { \
if (vmei_debug && vmei_dbg_file) { \
fprintf(vmei_dbg_file, "vmei: %s: " format, \
__func__, ##arg); \
fflush(vmei_dbg_file); \
} \
} while (0)
#define WPRINTF(format, arg...) do { \
fprintf(stderr, "vmei: %s: " format, __func__, ##arg); \
if (vmei_dbg_file) { \
fprintf(vmei_dbg_file, "vmei: %s: " format, \
__func__, ##arg); \
fflush(vmei_dbg_file); \
} \
} while (0)
#define MEI_HCL_FMT(format) "CL:[%02d:%02d](%04d): " format
#define MEI_HCL_PRM(_hcl) (_hcl)->host_addr, (_hcl)->me_addr, (_hcl)->client_fd
#define HCL_DBG(_hcl, format, arg...) \
DPRINTF(MEI_HCL_FMT(format), MEI_HCL_PRM(_hcl), ##arg)
#define HCL_WARN(_hcl, format, arg...) \
WPRINTF(MEI_HCL_FMT(format), MEI_HCL_PRM(_hcl), ##arg)
static void vmei_dbg_print_hex(const char *title,
const void *data, size_t length)
{
const unsigned char *bytes = data;
FILE *dbg_file;
size_t i;
if (vmei_debug < 2)
return;
dbg_file = (vmei_dbg_file) ? vmei_dbg_file : stdout;
if (title)
fprintf(dbg_file, "%s ", title);
for (i = 0; i < length; i++) {
if (i % 16 == 0 && i != 0)
fprintf(dbg_file, "\n");
fprintf(dbg_file, "%02x ", bytes[i]);
}
fprintf(dbg_file, "\n");
}
#define MEI_FW_STATUS_MAX 6
#define VMEI_VQ_NUM 2
@ -179,6 +230,369 @@ struct mei_virtio_cfg {
uint32_t fw_status[MEI_FW_STATUS_MAX];
} __attribute__((packed));
#define VMEI_IOBUFS_MAX 8
struct vmei_circular_iobufs {
struct iovec bufs[VMEI_IOBUFS_MAX];
uint8_t complete[VMEI_IOBUFS_MAX];
size_t buf_sz;
uint8_t i_idx; /* insert index */
uint8_t r_idx; /* remove index */
};
struct virtio_mei;
struct vmei_host_client {
struct refcnt ref;
struct vmei_me_client *mclient;
LIST_ENTRY(vmei_host_client) list;
uint8_t me_addr;
uint8_t host_addr;
uint8_t reserved[2];
int client_fd;
struct mevent *rx_mevp;
uint8_t *recv_buf;
size_t recv_buf_sz;
int recv_offset;
int recv_handled;
int recv_creds;
struct vmei_circular_iobufs send_bufs;
};
struct vmei_me_client {
struct refcnt ref;
struct virtio_mei *vmei;
LIST_ENTRY(vmei_me_client) list;
uint8_t client_id;
struct mei_client_properties props;
pthread_mutex_t list_mutex;
LIST_HEAD(connhead, vmei_host_client) connections;
};
enum vmei_status {
VMEI_STS_READY,
VMEI_STS_PENDING_RESET,
VMEI_STS_RESET,
VMEI_STST_DEINIT
};
struct virtio_mei {
struct virtio_base base;
char name[16];
char devnode[32];
struct mei_enumerate_me_clients me_clients_map;
struct virtio_vq_info vqs[VMEI_VQ_NUM];
volatile enum vmei_status status;
uint8_t vtag;
pthread_mutex_t mutex;
struct mevent *reset_mevp;
struct mei_virtio_cfg *config;
pthread_t tx_thread;
pthread_mutex_t tx_mutex;
pthread_cond_t tx_cond;
pthread_t rx_thread;
pthread_mutex_t rx_mutex;
pthread_cond_t rx_cond;
bool rx_need_sched;
pthread_mutex_t list_mutex;
LIST_HEAD(clhead, vmei_me_client) active_clients;
struct vmei_me_client *hbm_client;
int hbm_fd;
};
static inline void
vmei_set_status(struct virtio_mei *vmei, enum vmei_status status)
{
if (status == VMEI_STST_DEINIT ||
status == VMEI_STS_READY ||
status > vmei->status)
vmei->status = status;
}
static void
vmei_host_client_destroy(const struct refcnt *ref)
{
struct vmei_host_client *hclient;
struct vmei_me_client *mclient;
unsigned int i;
hclient = container_of(ref, struct vmei_host_client, ref);
mclient = hclient->mclient;
pthread_mutex_lock(&mclient->list_mutex);
LIST_REMOVE(hclient, list);
pthread_mutex_unlock(&mclient->list_mutex);
if (hclient->rx_mevp)
mevent_delete(hclient->rx_mevp);
if (hclient->client_fd > -1)
close(hclient->client_fd);
for (i = 0; i < VMEI_IOBUFS_MAX; i++)
free(hclient->send_bufs.bufs[i].iov_base);
free(hclient->recv_buf);
free(hclient);
}
static struct vmei_host_client *
vmei_host_client_create(struct vmei_me_client *mclient, uint8_t host_addr)
{
struct vmei_host_client *hclient;
size_t size = mclient->props.max_msg_length;
unsigned int i;
hclient = calloc(1, sizeof(*hclient));
if (!hclient)
return NULL;
hclient->ref = (struct refcnt){vmei_host_client_destroy, 1};
hclient->me_addr = mclient->client_id;
hclient->host_addr = host_addr;
hclient->mclient = mclient;
hclient->client_fd = -1;
hclient->rx_mevp = NULL;
/* HBM and fixed address doesn't provide flow control
* make the receiving part always available.
*/
if (host_addr == 0)
hclient->recv_creds = 1;
/* setup send_buf and recv_buf for the client */
for (i = 0; i < VMEI_IOBUFS_MAX; i++) {
hclient->send_bufs.bufs[i].iov_base = calloc(1, size);
if (!hclient->send_bufs.bufs[i].iov_base) {
HCL_WARN(hclient, "allocation failed\n");
goto fail;
}
}
hclient->recv_buf = calloc(1, size);
if (!hclient->recv_buf) {
HCL_WARN(hclient, "allocation failed\n");
goto fail;
}
hclient->send_bufs.buf_sz = size;
hclient->recv_buf_sz = size;
pthread_mutex_lock(&mclient->list_mutex);
LIST_INSERT_HEAD(&mclient->connections, hclient, list);
pthread_mutex_unlock(&mclient->list_mutex);
return hclient;
fail:
for (i = 0; i < VMEI_IOBUFS_MAX; i++)
free(hclient->send_bufs.bufs[i].iov_base);
memset(&hclient->send_bufs, 0, sizeof(hclient->send_bufs));
free(hclient->recv_buf);
free(hclient);
return NULL;
}
static struct vmei_host_client*
vmei_host_client_get(struct vmei_host_client *hclient)
{
refcnt_get(&hclient->ref);
return hclient;
}
static void
vmei_host_client_put(struct vmei_host_client *hclient)
{
refcnt_put(&hclient->ref);
}
struct virtio_mei *
vmei_host_client_to_vmei(struct vmei_host_client *hclient)
{
if (hclient && hclient->mclient)
return hclient->mclient->vmei;
return NULL;
}
static void
vmei_del_me_client(struct vmei_me_client *mclient)
{
struct virtio_mei *vmei = mclient->vmei;
pthread_mutex_lock(&vmei->list_mutex);
LIST_REMOVE(mclient, list);
pthread_mutex_unlock(&vmei->list_mutex);
}
static void
vmei_me_client_destroy_host_clients(struct vmei_me_client *mclient)
{
struct vmei_host_client *e, *n;
pthread_mutex_lock(&mclient->list_mutex);
e = LIST_FIRST(&mclient->connections);
while (e) {
n = LIST_NEXT(e, list);
vmei_host_client_put(e);
e = n;
}
pthread_mutex_unlock(&mclient->list_mutex);
LIST_INIT(&mclient->connections);
}
static void
vmei_me_client_destroy(const struct refcnt *ref)
{
struct vmei_me_client *mclient;
mclient = container_of(ref, struct vmei_me_client, ref);
vmei_del_me_client(mclient);
vmei_me_client_destroy_host_clients(mclient);
pthread_mutex_destroy(&mclient->list_mutex);
free(mclient);
}
static struct vmei_me_client *
vmei_me_client_create(struct virtio_mei *vmei, uint8_t client_id,
const struct mei_client_properties *props)
{
struct vmei_me_client *mclient;
pthread_mutexattr_t attr;
if (!props)
return NULL;
mclient = calloc(1, sizeof(*mclient));
if (!mclient)
return mclient;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mclient->list_mutex, &attr);
pthread_mutexattr_destroy(&attr);
mclient->vmei = vmei;
mclient->client_id = client_id;
mclient->ref = (struct refcnt){vmei_me_client_destroy, 1};
memcpy(&mclient->props, props, sizeof(*props));
vmei_dbg_client_properties(&mclient->props);
DPRINTF("ME client created %d\n", client_id);
return mclient;
}
static struct vmei_me_client*
vmei_me_client_get(struct vmei_me_client *mclient)
{
refcnt_get(&mclient->ref);
return mclient;
}
static void
vmei_me_client_put(struct vmei_me_client *mclient)
{
refcnt_put(&mclient->ref);
}
static struct vmei_host_client*
vmei_me_client_get_host_client(struct vmei_me_client *mclient,
uint8_t host_addr)
{
struct vmei_host_client *e, *hclient = NULL;
pthread_mutex_lock(&mclient->list_mutex);
LIST_FOREACH(e, &mclient->connections, list) {
if (e->host_addr == host_addr) {
hclient = vmei_host_client_get(e);
break;
}
}
pthread_mutex_unlock(&mclient->list_mutex);
return hclient;
}
static void vmei_add_me_client(struct vmei_me_client *mclient)
{
struct virtio_mei *vmei = mclient->vmei;
pthread_mutex_lock(&vmei->list_mutex);
if (mclient->client_id == 0)
/* make sure hbm client at the head of the list */
LIST_INSERT_HEAD(&vmei->active_clients, mclient, list);
else
LIST_INSERT_AFTER(LIST_FIRST(&vmei->active_clients),
mclient, list);
pthread_mutex_unlock(&vmei->list_mutex);
}
static struct vmei_me_client *
vmei_find_me_client(struct virtio_mei *vmei, uint8_t client_id)
{
struct vmei_me_client *e, *mclient = NULL;
pthread_mutex_lock(&vmei->list_mutex);
LIST_FOREACH(e, &vmei->active_clients, list) {
if (e->client_id == client_id) {
mclient = vmei_me_client_get(e);
break;
}
}
pthread_mutex_unlock(&vmei->list_mutex);
return mclient;
}
static struct vmei_host_client*
vmei_find_host_client(struct virtio_mei *vmei,
uint8_t me_addr, uint8_t host_addr)
{
struct vmei_me_client *mclient;
struct vmei_host_client *hclient;
mclient = vmei_find_me_client(vmei, me_addr);
if (!mclient) {
DPRINTF("client with me address %d was not found\n", me_addr);
return NULL;
}
hclient = vmei_me_client_get_host_client(mclient, host_addr);
if (!hclient)
DPRINTF("host client %d does not exists!\n", host_addr);
vmei_me_client_put(mclient);
return hclient;
}
static void vmei_free_me_clients(struct virtio_mei *vmei)
{
struct vmei_me_client *e, *n;
pthread_mutex_lock(&vmei->list_mutex);
e = LIST_FIRST(&vmei->active_clients);
while (e) {
n = LIST_NEXT(e, list);
vmei_me_client_put(e);
e = n;
}
LIST_INIT(&vmei->active_clients);
pthread_mutex_unlock(&vmei->list_mutex);
}
static int mei_sysfs_read_property_file(const char *fname, char *buf, size_t sz)
{
int fd;