mirror of https://github.com/F-Stack/f-stack.git
359 lines
7.4 KiB
C
359 lines
7.4 KiB
C
|
/* SPDX-License-Identifier: BSD-3-Clause
|
||
|
* Copyright (c) 2018, Microsoft Corporation.
|
||
|
* All Rights Reserved.
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <dirent.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
#include <rte_eal.h>
|
||
|
#include <rte_uuid.h>
|
||
|
#include <rte_tailq.h>
|
||
|
#include <rte_log.h>
|
||
|
#include <rte_devargs.h>
|
||
|
#include <rte_memory.h>
|
||
|
#include <rte_malloc.h>
|
||
|
#include <rte_bus_vmbus.h>
|
||
|
|
||
|
#include "eal_filesystem.h"
|
||
|
#include "private.h"
|
||
|
|
||
|
/** Pathname of VMBUS devices directory. */
|
||
|
#define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
|
||
|
|
||
|
extern struct rte_vmbus_bus rte_vmbus_bus;
|
||
|
|
||
|
/* Read sysfs file to get UUID */
|
||
|
static int
|
||
|
parse_sysfs_uuid(const char *filename, rte_uuid_t uu)
|
||
|
{
|
||
|
char buf[BUFSIZ];
|
||
|
char *cp, *in = buf;
|
||
|
FILE *f;
|
||
|
|
||
|
f = fopen(filename, "r");
|
||
|
if (f == NULL) {
|
||
|
VMBUS_LOG(ERR, "cannot open sysfs value %s: %s",
|
||
|
filename, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (fgets(buf, sizeof(buf), f) == NULL) {
|
||
|
VMBUS_LOG(ERR, "cannot read sysfs value %s",
|
||
|
filename);
|
||
|
fclose(f);
|
||
|
return -1;
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
cp = strchr(buf, '\n');
|
||
|
if (cp)
|
||
|
*cp = '\0';
|
||
|
|
||
|
/* strip { } notation */
|
||
|
if (buf[0] == '{') {
|
||
|
in = buf + 1;
|
||
|
cp = strchr(in, '}');
|
||
|
if (cp)
|
||
|
*cp = '\0';
|
||
|
}
|
||
|
|
||
|
if (rte_uuid_parse(in, uu) < 0) {
|
||
|
VMBUS_LOG(ERR, "%s %s not a valid UUID",
|
||
|
filename, buf);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
get_sysfs_string(const char *filename, char *buf, size_t buflen)
|
||
|
{
|
||
|
char *cp;
|
||
|
FILE *f;
|
||
|
|
||
|
f = fopen(filename, "r");
|
||
|
if (f == NULL) {
|
||
|
VMBUS_LOG(ERR, "cannot open sysfs value %s:%s",
|
||
|
filename, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (fgets(buf, buflen, f) == NULL) {
|
||
|
VMBUS_LOG(ERR, "cannot read sysfs value %s",
|
||
|
filename);
|
||
|
fclose(f);
|
||
|
return -1;
|
||
|
}
|
||
|
fclose(f);
|
||
|
|
||
|
/* remove trailing newline */
|
||
|
cp = memchr(buf, '\n', buflen);
|
||
|
if (cp)
|
||
|
*cp = '\0';
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
vmbus_get_uio_dev(const struct rte_vmbus_device *dev,
|
||
|
char *dstbuf, size_t buflen)
|
||
|
{
|
||
|
char dirname[PATH_MAX];
|
||
|
unsigned int uio_num;
|
||
|
struct dirent *e;
|
||
|
DIR *dir;
|
||
|
|
||
|
/* Assume recent kernel where uio is in uio/uioX */
|
||
|
snprintf(dirname, sizeof(dirname),
|
||
|
SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name);
|
||
|
|
||
|
dir = opendir(dirname);
|
||
|
if (dir == NULL)
|
||
|
return -1; /* Not a UIO device */
|
||
|
|
||
|
/* take the first file starting with "uio" */
|
||
|
while ((e = readdir(dir)) != NULL) {
|
||
|
const int prefix_len = 3;
|
||
|
char *endptr;
|
||
|
|
||
|
if (strncmp(e->d_name, "uio", prefix_len) != 0)
|
||
|
continue;
|
||
|
|
||
|
/* try uio%d */
|
||
|
errno = 0;
|
||
|
uio_num = strtoull(e->d_name + prefix_len, &endptr, 10);
|
||
|
if (errno == 0 && endptr != (e->d_name + prefix_len)) {
|
||
|
snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
closedir(dir);
|
||
|
|
||
|
if (e == NULL)
|
||
|
return -1;
|
||
|
|
||
|
return uio_num;
|
||
|
}
|
||
|
|
||
|
/* Check map names with kernel names */
|
||
|
static const char *map_names[VMBUS_MAX_RESOURCE] = {
|
||
|
[HV_TXRX_RING_MAP] = "txrx_rings",
|
||
|
[HV_INT_PAGE_MAP] = "int_page",
|
||
|
[HV_MON_PAGE_MAP] = "monitor_page",
|
||
|
[HV_RECV_BUF_MAP] = "recv:",
|
||
|
[HV_SEND_BUF_MAP] = "send:",
|
||
|
};
|
||
|
|
||
|
|
||
|
/* map the resources of a vmbus device in virtual memory */
|
||
|
int
|
||
|
rte_vmbus_map_device(struct rte_vmbus_device *dev)
|
||
|
{
|
||
|
char uioname[PATH_MAX], filename[PATH_MAX];
|
||
|
char dirname[PATH_MAX], mapname[64];
|
||
|
int i;
|
||
|
|
||
|
dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
|
||
|
if (dev->uio_num < 0) {
|
||
|
VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Extract resource value */
|
||
|
for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
|
||
|
struct rte_mem_resource *res = &dev->resource[i];
|
||
|
unsigned long len, gpad = 0;
|
||
|
char *cp;
|
||
|
|
||
|
snprintf(dirname, sizeof(dirname),
|
||
|
"%s/maps/map%d", uioname, i);
|
||
|
|
||
|
snprintf(filename, sizeof(filename),
|
||
|
"%s/name", dirname);
|
||
|
|
||
|
if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
|
||
|
VMBUS_LOG(ERR, "could not read %s", filename);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
|
||
|
VMBUS_LOG(ERR,
|
||
|
"unexpected resource %s (expected %s)",
|
||
|
mapname, map_names[i]);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
snprintf(filename, sizeof(filename),
|
||
|
"%s/size", dirname);
|
||
|
if (eal_parse_sysfs_value(filename, &len) < 0) {
|
||
|
VMBUS_LOG(ERR,
|
||
|
"could not read %s", filename);
|
||
|
return -1;
|
||
|
}
|
||
|
res->len = len;
|
||
|
|
||
|
/* both send and receive buffers have gpad in name */
|
||
|
cp = memchr(mapname, ':', sizeof(mapname));
|
||
|
if (cp)
|
||
|
gpad = strtoul(cp+1, NULL, 0);
|
||
|
|
||
|
/* put the GPAD value in physical address */
|
||
|
res->phys_addr = gpad;
|
||
|
}
|
||
|
|
||
|
return vmbus_uio_map_resource(dev);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
|
||
|
{
|
||
|
vmbus_uio_unmap_resource(dev);
|
||
|
}
|
||
|
|
||
|
/* Scan one vmbus sysfs entry, and fill the devices list from it. */
|
||
|
static int
|
||
|
vmbus_scan_one(const char *name)
|
||
|
{
|
||
|
struct rte_vmbus_device *dev, *dev2;
|
||
|
char filename[PATH_MAX];
|
||
|
char dirname[PATH_MAX];
|
||
|
unsigned long tmp;
|
||
|
|
||
|
dev = calloc(1, sizeof(*dev));
|
||
|
if (dev == NULL)
|
||
|
return -1;
|
||
|
|
||
|
dev->device.bus = &rte_vmbus_bus.bus;
|
||
|
dev->device.name = strdup(name);
|
||
|
if (!dev->device.name)
|
||
|
goto error;
|
||
|
|
||
|
/* sysfs base directory
|
||
|
* /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
|
||
|
* or on older kernel
|
||
|
* /sys/bus/vmbus/devices/vmbus_1
|
||
|
*/
|
||
|
snprintf(dirname, sizeof(dirname), "%s/%s",
|
||
|
SYSFS_VMBUS_DEVICES, name);
|
||
|
|
||
|
/* get device id */
|
||
|
snprintf(filename, sizeof(filename), "%s/device_id", dirname);
|
||
|
if (parse_sysfs_uuid(filename, dev->device_id) < 0)
|
||
|
goto error;
|
||
|
|
||
|
/* get device class */
|
||
|
snprintf(filename, sizeof(filename), "%s/class_id", dirname);
|
||
|
if (parse_sysfs_uuid(filename, dev->class_id) < 0)
|
||
|
goto error;
|
||
|
|
||
|
/* get relid */
|
||
|
snprintf(filename, sizeof(filename), "%s/id", dirname);
|
||
|
if (eal_parse_sysfs_value(filename, &tmp) < 0)
|
||
|
goto error;
|
||
|
dev->relid = tmp;
|
||
|
|
||
|
/* get monitor id */
|
||
|
snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
|
||
|
if (eal_parse_sysfs_value(filename, &tmp) < 0)
|
||
|
goto error;
|
||
|
dev->monitor_id = tmp;
|
||
|
|
||
|
/* get numa node (if present) */
|
||
|
snprintf(filename, sizeof(filename), "%s/numa_node",
|
||
|
dirname);
|
||
|
|
||
|
if (access(filename, R_OK) == 0) {
|
||
|
if (eal_parse_sysfs_value(filename, &tmp) < 0)
|
||
|
goto error;
|
||
|
dev->device.numa_node = tmp;
|
||
|
} else {
|
||
|
/* if no NUMA support, set default to 0 */
|
||
|
dev->device.numa_node = SOCKET_ID_ANY;
|
||
|
}
|
||
|
|
||
|
dev->device.devargs = vmbus_devargs_lookup(dev);
|
||
|
|
||
|
/* device is valid, add in list (sorted) */
|
||
|
VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
|
||
|
|
||
|
TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
|
||
|
int ret;
|
||
|
|
||
|
ret = rte_uuid_compare(dev->device_id, dev2->device_id);
|
||
|
if (ret > 0)
|
||
|
continue;
|
||
|
|
||
|
if (ret < 0) {
|
||
|
vmbus_insert_device(dev2, dev);
|
||
|
} else { /* already registered */
|
||
|
VMBUS_LOG(NOTICE,
|
||
|
"%s already registered", name);
|
||
|
free(dev);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
vmbus_add_device(dev);
|
||
|
return 0;
|
||
|
error:
|
||
|
VMBUS_LOG(DEBUG, "failed");
|
||
|
|
||
|
free(dev);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Scan the content of the vmbus, and the devices in the devices list
|
||
|
*/
|
||
|
int
|
||
|
rte_vmbus_scan(void)
|
||
|
{
|
||
|
struct dirent *e;
|
||
|
DIR *dir;
|
||
|
|
||
|
dir = opendir(SYSFS_VMBUS_DEVICES);
|
||
|
if (dir == NULL) {
|
||
|
if (errno == ENOENT)
|
||
|
return 0;
|
||
|
|
||
|
VMBUS_LOG(ERR, "opendir %s failed: %s",
|
||
|
SYSFS_VMBUS_DEVICES, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
while ((e = readdir(dir)) != NULL) {
|
||
|
if (e->d_name[0] == '.')
|
||
|
continue;
|
||
|
|
||
|
if (vmbus_scan_one(e->d_name) < 0)
|
||
|
goto error;
|
||
|
}
|
||
|
closedir(dir);
|
||
|
return 0;
|
||
|
|
||
|
error:
|
||
|
closedir(dir);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
|
||
|
{
|
||
|
vmbus_uio_irq_control(device, 1);
|
||
|
}
|
||
|
|
||
|
void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
|
||
|
{
|
||
|
vmbus_uio_irq_control(device, 0);
|
||
|
}
|
||
|
|
||
|
int rte_vmbus_irq_read(struct rte_vmbus_device *device)
|
||
|
{
|
||
|
return vmbus_uio_irq_read(device);
|
||
|
}
|