dpdk/drivers/bus/vmbus/linux/vmbus_bus.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright (c) 2018, Microsoft Corporation.
   3 * All Rights Reserved.
   4 */
   5
   6#include <string.h>
   7#include <unistd.h>
   8#include <dirent.h>
   9#include <fcntl.h>
  10#include <sys/mman.h>
  11#include <sys/stat.h>
  12
  13#include <rte_eal.h>
  14#include <rte_uuid.h>
  15#include <rte_tailq.h>
  16#include <rte_log.h>
  17#include <rte_devargs.h>
  18#include <rte_memory.h>
  19#include <rte_malloc.h>
  20#include <rte_bus_vmbus.h>
  21
  22#include "eal_filesystem.h"
  23#include "private.h"
  24
  25/** Pathname of VMBUS devices directory. */
  26#define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
  27
  28/*
  29 * GUID associated with network devices
  30 * {f8615163-df3e-46c5-913f-f2d2f965ed0e}
  31 */
  32static const rte_uuid_t vmbus_nic_uuid = {
  33        0xf8, 0x61, 0x51, 0x63,
  34        0xdf, 0x3e,
  35        0x46, 0xc5,
  36        0x91, 0x3f,
  37        0xf2, 0xd2, 0xf9, 0x65, 0xed, 0xe
  38};
  39
  40extern struct rte_vmbus_bus rte_vmbus_bus;
  41
  42/* Read sysfs file to get UUID */
  43static int
  44parse_sysfs_uuid(const char *filename, rte_uuid_t uu)
  45{
  46        char buf[BUFSIZ];
  47        char *cp, *in = buf;
  48        FILE *f;
  49
  50        f = fopen(filename, "r");
  51        if (f == NULL) {
  52                VMBUS_LOG(ERR, "cannot open sysfs value %s: %s",
  53                          filename, strerror(errno));
  54                return -1;
  55        }
  56
  57        if (fgets(buf, sizeof(buf), f) == NULL) {
  58                VMBUS_LOG(ERR, "cannot read sysfs value %s",
  59                                filename);
  60                fclose(f);
  61                return -1;
  62        }
  63        fclose(f);
  64
  65        cp = strchr(buf, '\n');
  66        if (cp)
  67                *cp = '\0';
  68
  69        /* strip { } notation */
  70        if (buf[0] == '{') {
  71                in = buf + 1;
  72                cp = strchr(in, '}');
  73                if (cp)
  74                        *cp = '\0';
  75        }
  76
  77        if (rte_uuid_parse(in, uu) < 0) {
  78                VMBUS_LOG(ERR, "%s %s not a valid UUID",
  79                        filename, buf);
  80                return -1;
  81        }
  82
  83        return 0;
  84}
  85
  86static int
  87get_sysfs_string(const char *filename, char *buf, size_t buflen)
  88{
  89        char *cp;
  90        FILE *f;
  91
  92        f = fopen(filename, "r");
  93        if (f == NULL) {
  94                VMBUS_LOG(ERR, "cannot open sysfs value %s:%s",
  95                          filename, strerror(errno));
  96                return -1;
  97        }
  98
  99        if (fgets(buf, buflen, f) == NULL) {
 100                VMBUS_LOG(ERR, "cannot read sysfs value %s",
 101                                filename);
 102                fclose(f);
 103                return -1;
 104        }
 105        fclose(f);
 106
 107        /* remove trailing newline */
 108        cp = memchr(buf, '\n', buflen);
 109        if (cp)
 110                *cp = '\0';
 111
 112        return 0;
 113}
 114
 115static int
 116vmbus_get_uio_dev(const struct rte_vmbus_device *dev,
 117                  char *dstbuf, size_t buflen)
 118{
 119        char dirname[PATH_MAX];
 120        unsigned int uio_num;
 121        struct dirent *e;
 122        DIR *dir;
 123
 124        /* Assume recent kernel where uio is in uio/uioX */
 125        snprintf(dirname, sizeof(dirname),
 126                 SYSFS_VMBUS_DEVICES "/%s/uio", dev->device.name);
 127
 128        dir = opendir(dirname);
 129        if (dir == NULL)
 130                return -1; /* Not a UIO device */
 131
 132        /* take the first file starting with "uio" */
 133        while ((e = readdir(dir)) != NULL) {
 134                const int prefix_len = 3;
 135                char *endptr;
 136
 137                if (strncmp(e->d_name, "uio", prefix_len) != 0)
 138                        continue;
 139
 140                /* try uio%d */
 141                errno = 0;
 142                uio_num = strtoull(e->d_name + prefix_len, &endptr, 10);
 143                if (errno == 0 && endptr != (e->d_name + prefix_len)) {
 144                        snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
 145                        break;
 146                }
 147        }
 148        closedir(dir);
 149
 150        if (e == NULL)
 151                return -1;
 152
 153        return uio_num;
 154}
 155
 156/* Check map names with kernel names */
 157static const char *map_names[VMBUS_MAX_RESOURCE] = {
 158        [HV_TXRX_RING_MAP] = "txrx_rings",
 159        [HV_INT_PAGE_MAP]  = "int_page",
 160        [HV_MON_PAGE_MAP]  = "monitor_page",
 161        [HV_RECV_BUF_MAP]  = "recv:",
 162        [HV_SEND_BUF_MAP]  = "send:",
 163};
 164
 165
 166/* map the resources of a vmbus device in virtual memory */
 167int
 168rte_vmbus_map_device(struct rte_vmbus_device *dev)
 169{
 170        char uioname[PATH_MAX], filename[PATH_MAX];
 171        char dirname[PATH_MAX], mapname[64];
 172        int i;
 173
 174        dev->uio_num = vmbus_get_uio_dev(dev, uioname, sizeof(uioname));
 175        if (dev->uio_num < 0) {
 176                VMBUS_LOG(DEBUG, "Not managed by UIO driver, skipped");
 177                return 1;
 178        }
 179
 180        /* Extract resource value */
 181        for (i = 0; i < VMBUS_MAX_RESOURCE; i++) {
 182                struct rte_mem_resource *res = &dev->resource[i];
 183                unsigned long len, gpad = 0;
 184                char *cp;
 185
 186                snprintf(dirname, sizeof(dirname),
 187                         "%s/maps/map%d", uioname, i);
 188
 189                snprintf(filename, sizeof(filename),
 190                         "%s/name", dirname);
 191
 192                if (get_sysfs_string(filename, mapname, sizeof(mapname)) < 0) {
 193                        VMBUS_LOG(ERR, "could not read %s", filename);
 194                        return -1;
 195                }
 196
 197                if (strncmp(map_names[i], mapname, strlen(map_names[i])) != 0) {
 198                        VMBUS_LOG(ERR,
 199                                "unexpected resource %s (expected %s)",
 200                                mapname, map_names[i]);
 201                        return -1;
 202                }
 203
 204                snprintf(filename, sizeof(filename),
 205                         "%s/size", dirname);
 206                if (eal_parse_sysfs_value(filename, &len) < 0) {
 207                        VMBUS_LOG(ERR,
 208                                "could not read %s", filename);
 209                        return -1;
 210                }
 211                res->len = len;
 212
 213                /* both send and receive buffers have gpad in name */
 214                cp = memchr(mapname, ':', sizeof(mapname));
 215                if (cp)
 216                        gpad = strtoul(cp+1, NULL, 0);
 217
 218                /* put the GPAD value in physical address */
 219                res->phys_addr = gpad;
 220        }
 221
 222        return vmbus_uio_map_resource(dev);
 223}
 224
 225void
 226rte_vmbus_unmap_device(struct rte_vmbus_device *dev)
 227{
 228        vmbus_uio_unmap_resource(dev);
 229}
 230
 231/* Scan one vmbus sysfs entry, and fill the devices list from it. */
 232static int
 233vmbus_scan_one(const char *name)
 234{
 235        struct rte_vmbus_device *dev, *dev2;
 236        char filename[PATH_MAX];
 237        char dirname[PATH_MAX];
 238        unsigned long tmp;
 239        char *dev_name;
 240
 241        dev = calloc(1, sizeof(*dev));
 242        if (dev == NULL)
 243                return -1;
 244
 245        dev->device.bus = &rte_vmbus_bus.bus;
 246        dev->device.name = dev_name = strdup(name);
 247        if (!dev->device.name)
 248                goto error;
 249
 250        /* sysfs base directory
 251         *   /sys/bus/vmbus/devices/7a08391f-f5a0-4ac0-9802-d13fd964f8df
 252         * or on older kernel
 253         *   /sys/bus/vmbus/devices/vmbus_1
 254         */
 255        snprintf(dirname, sizeof(dirname), "%s/%s",
 256                 SYSFS_VMBUS_DEVICES, name);
 257
 258        /* get device class  */
 259        snprintf(filename, sizeof(filename), "%s/class_id", dirname);
 260        if (parse_sysfs_uuid(filename, dev->class_id) < 0)
 261                goto error;
 262
 263        /* skip non-network devices */
 264        if (rte_uuid_compare(dev->class_id, vmbus_nic_uuid) != 0) {
 265                free(dev_name);
 266                free(dev);
 267                return 0;
 268        }
 269
 270        /* get device id */
 271        snprintf(filename, sizeof(filename), "%s/device_id", dirname);
 272        if (parse_sysfs_uuid(filename, dev->device_id) < 0)
 273                goto error;
 274
 275        /* get relid */
 276        snprintf(filename, sizeof(filename), "%s/id", dirname);
 277        if (eal_parse_sysfs_value(filename, &tmp) < 0)
 278                goto error;
 279        dev->relid = tmp;
 280
 281        /* get monitor id */
 282        snprintf(filename, sizeof(filename), "%s/monitor_id", dirname);
 283        if (eal_parse_sysfs_value(filename, &tmp) < 0)
 284                goto error;
 285        dev->monitor_id = tmp;
 286
 287        /* get numa node (if present) */
 288        snprintf(filename, sizeof(filename), "%s/numa_node",
 289                 dirname);
 290
 291        if (access(filename, R_OK) == 0) {
 292                if (eal_parse_sysfs_value(filename, &tmp) < 0)
 293                        goto error;
 294                dev->device.numa_node = tmp;
 295        } else {
 296                /* if no NUMA support, set default to 0 */
 297                dev->device.numa_node = SOCKET_ID_ANY;
 298        }
 299
 300        dev->device.devargs = vmbus_devargs_lookup(dev);
 301
 302        /* Allocate interrupt handle instance */
 303        dev->intr_handle =
 304                rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
 305        if (dev->intr_handle == NULL)
 306                goto error;
 307
 308        /* device is valid, add in list (sorted) */
 309        VMBUS_LOG(DEBUG, "Adding vmbus device %s", name);
 310
 311        TAILQ_FOREACH(dev2, &rte_vmbus_bus.device_list, next) {
 312                int ret;
 313
 314                ret = rte_uuid_compare(dev->device_id, dev2->device_id);
 315                if (ret > 0)
 316                        continue;
 317
 318                if (ret < 0) {
 319                        vmbus_insert_device(dev2, dev);
 320                } else { /* already registered */
 321                        VMBUS_LOG(NOTICE,
 322                                "%s already registered", name);
 323                        free(dev_name);
 324                        free(dev);
 325                }
 326                return 0;
 327        }
 328
 329        vmbus_add_device(dev);
 330        return 0;
 331error:
 332        VMBUS_LOG(DEBUG, "failed");
 333
 334        free(dev_name);
 335        free(dev);
 336        return -1;
 337}
 338
 339/*
 340 * Scan the content of the vmbus, and the devices in the devices list
 341 */
 342int
 343rte_vmbus_scan(void)
 344{
 345        struct dirent *e;
 346        DIR *dir;
 347
 348        dir = opendir(SYSFS_VMBUS_DEVICES);
 349        if (dir == NULL) {
 350                if (errno == ENOENT)
 351                        return 0;
 352
 353                VMBUS_LOG(ERR, "opendir %s failed: %s",
 354                          SYSFS_VMBUS_DEVICES, strerror(errno));
 355                return -1;
 356        }
 357
 358        while ((e = readdir(dir)) != NULL) {
 359                if (e->d_name[0] == '.')
 360                        continue;
 361
 362                if (vmbus_scan_one(e->d_name) < 0)
 363                        goto error;
 364        }
 365        closedir(dir);
 366        return 0;
 367
 368error:
 369        closedir(dir);
 370        return -1;
 371}
 372
 373void rte_vmbus_irq_mask(struct rte_vmbus_device *device)
 374{
 375        vmbus_uio_irq_control(device, 1);
 376}
 377
 378void rte_vmbus_irq_unmask(struct rte_vmbus_device *device)
 379{
 380        vmbus_uio_irq_control(device, 0);
 381}
 382
 383int rte_vmbus_irq_read(struct rte_vmbus_device *device)
 384{
 385        return vmbus_uio_irq_read(device);
 386}
 387