dpdk/drivers/bus/vmbus/linux/vmbus_uio.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 <fcntl.h>
   9#include <dirent.h>
  10#include <inttypes.h>
  11#include <sys/stat.h>
  12#include <sys/mman.h>
  13
  14#include <rte_eal.h>
  15#include <rte_log.h>
  16#include <rte_memory.h>
  17#include <rte_common.h>
  18#include <rte_malloc.h>
  19#include <rte_bus_vmbus.h>
  20#include <rte_string_fns.h>
  21
  22#include "private.h"
  23
  24/** Pathname of VMBUS devices directory. */
  25#define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
  26
  27static void *vmbus_map_addr;
  28
  29/* Control interrupts */
  30void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
  31{
  32        if ((rte_intr_fd_get(dev->intr_handle) < 0) ||
  33            write(rte_intr_fd_get(dev->intr_handle), &onoff,
  34                  sizeof(onoff)) < 0) {
  35                VMBUS_LOG(ERR, "cannot write to %d:%s",
  36                          rte_intr_fd_get(dev->intr_handle),
  37                          strerror(errno));
  38        }
  39}
  40
  41int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
  42{
  43        int32_t count;
  44        int cc;
  45
  46        if (rte_intr_fd_get(dev->intr_handle) < 0)
  47                return -1;
  48
  49        cc = read(rte_intr_fd_get(dev->intr_handle), &count,
  50                  sizeof(count));
  51        if (cc < (int)sizeof(count)) {
  52                if (cc < 0) {
  53                        VMBUS_LOG(ERR, "IRQ read failed %s",
  54                                  strerror(errno));
  55                        return -errno;
  56                }
  57                VMBUS_LOG(ERR, "can't read IRQ count");
  58                return -EINVAL;
  59        }
  60
  61        return count;
  62}
  63
  64void
  65vmbus_uio_free_resource(struct rte_vmbus_device *dev,
  66                struct mapped_vmbus_resource *uio_res)
  67{
  68        rte_free(uio_res);
  69
  70        if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
  71                close(rte_intr_dev_fd_get(dev->intr_handle));
  72                rte_intr_dev_fd_set(dev->intr_handle, -1);
  73        }
  74
  75        if (rte_intr_fd_get(dev->intr_handle) >= 0) {
  76                close(rte_intr_fd_get(dev->intr_handle));
  77                rte_intr_fd_set(dev->intr_handle, -1);
  78                rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
  79        }
  80}
  81
  82int
  83vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
  84                         struct mapped_vmbus_resource **uio_res)
  85{
  86        char devname[PATH_MAX]; /* contains the /dev/uioX */
  87        int fd;
  88
  89        /* save fd if in primary process */
  90        snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
  91        fd = open(devname, O_RDWR);
  92        if (fd < 0) {
  93                VMBUS_LOG(ERR, "Cannot open %s: %s",
  94                        devname, strerror(errno));
  95                goto error;
  96        }
  97
  98        if (rte_intr_fd_set(dev->intr_handle, fd))
  99                goto error;
 100
 101        if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UIO_INTX))
 102                goto error;
 103
 104        /* allocate the mapping details for secondary processes*/
 105        *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
 106        if (*uio_res == NULL) {
 107                VMBUS_LOG(ERR, "cannot store uio mmap details");
 108                goto error;
 109        }
 110
 111        strlcpy((*uio_res)->path, devname, PATH_MAX);
 112        rte_uuid_copy((*uio_res)->id, dev->device_id);
 113
 114        return 0;
 115
 116error:
 117        vmbus_uio_free_resource(dev, *uio_res);
 118        return -1;
 119}
 120
 121static int
 122find_max_end_va(const struct rte_memseg_list *msl, void *arg)
 123{
 124        size_t sz = msl->memseg_arr.len * msl->page_sz;
 125        void *end_va = RTE_PTR_ADD(msl->base_va, sz);
 126        void **max_va = arg;
 127
 128        if (*max_va < end_va)
 129                *max_va = end_va;
 130        return 0;
 131}
 132
 133/*
 134 * TODO: this should be part of memseg api.
 135 *       code is duplicated from PCI.
 136 */
 137static void *
 138vmbus_find_max_end_va(void)
 139{
 140        void *va = NULL;
 141
 142        rte_memseg_list_walk(find_max_end_va, &va);
 143        return va;
 144}
 145
 146int
 147vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
 148                                struct mapped_vmbus_resource *uio_res,
 149                                int flags)
 150{
 151        size_t size = dev->resource[idx].len;
 152        struct vmbus_map *maps = uio_res->maps;
 153        void *mapaddr;
 154        off_t offset;
 155        int fd;
 156
 157        /* devname for mmap  */
 158        fd = open(uio_res->path, O_RDWR);
 159        if (fd < 0) {
 160                VMBUS_LOG(ERR, "Cannot open %s: %s",
 161                          uio_res->path, strerror(errno));
 162                return -1;
 163        }
 164
 165        /* try mapping somewhere close to the end of hugepages */
 166        if (vmbus_map_addr == NULL)
 167                vmbus_map_addr = vmbus_find_max_end_va();
 168
 169        /* offset is special in uio it indicates which resource */
 170        offset = idx * rte_mem_page_size();
 171
 172        mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
 173        close(fd);
 174
 175        if (mapaddr == MAP_FAILED)
 176                return -1;
 177
 178        dev->resource[idx].addr = mapaddr;
 179        vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
 180
 181        /* Record result of successful mapping for use by secondary */
 182        maps[idx].addr = mapaddr;
 183        maps[idx].size = size;
 184
 185        return 0;
 186}
 187
 188static int vmbus_uio_map_primary(struct vmbus_channel *chan,
 189                                 void **ring_buf, uint32_t *ring_size)
 190{
 191        struct mapped_vmbus_resource *uio_res;
 192
 193        uio_res = vmbus_uio_find_resource(chan->device);
 194        if (!uio_res) {
 195                VMBUS_LOG(ERR, "can not find resources!");
 196                return -ENOMEM;
 197        }
 198
 199        if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
 200                VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
 201                          uio_res->nb_maps);
 202                return -EINVAL;
 203        }
 204
 205        *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
 206        *ring_buf  = uio_res->maps[HV_TXRX_RING_MAP].addr;
 207        return 0;
 208}
 209
 210static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
 211                                 const struct vmbus_channel *chan,
 212                                 void **ring_buf, uint32_t *ring_size)
 213{
 214        char ring_path[PATH_MAX];
 215        size_t file_size;
 216        struct stat sb;
 217        void *mapaddr;
 218        int fd;
 219        struct mapped_vmbus_resource *uio_res;
 220        int channel_idx;
 221
 222        uio_res = vmbus_uio_find_resource(dev);
 223        if (!uio_res) {
 224                VMBUS_LOG(ERR, "can not find resources for mapping subchan");
 225                return -ENOMEM;
 226        }
 227
 228        if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
 229                if (uio_res->nb_subchannels >= UIO_MAX_SUBCHANNEL) {
 230                        VMBUS_LOG(ERR,
 231                                "exceeding max subchannels UIO_MAX_SUBCHANNEL(%d)",
 232                                UIO_MAX_SUBCHANNEL);
 233                        VMBUS_LOG(ERR, "Change UIO_MAX_SUBCHANNEL and recompile");
 234                        return -ENOMEM;
 235                }
 236        } else {
 237                for (channel_idx = 0; channel_idx < uio_res->nb_subchannels;
 238                     channel_idx++)
 239                        if (uio_res->subchannel_maps[channel_idx].relid ==
 240                                        chan->relid)
 241                                break;
 242                if (channel_idx == uio_res->nb_subchannels) {
 243                        VMBUS_LOG(ERR,
 244                                "couldn't find sub channel %d from shared mapping in primary",
 245                                chan->relid);
 246                        return -ENOMEM;
 247                }
 248                vmbus_map_addr = uio_res->subchannel_maps[channel_idx].addr;
 249        }
 250
 251        snprintf(ring_path, sizeof(ring_path),
 252                 "%s/%s/channels/%u/ring",
 253                 SYSFS_VMBUS_DEVICES, dev->device.name,
 254                 chan->relid);
 255
 256        fd = open(ring_path, O_RDWR);
 257        if (fd < 0) {
 258                VMBUS_LOG(ERR, "Cannot open %s: %s",
 259                          ring_path, strerror(errno));
 260                return -errno;
 261        }
 262
 263        if (fstat(fd, &sb) < 0) {
 264                VMBUS_LOG(ERR, "Cannot state %s: %s",
 265                          ring_path, strerror(errno));
 266                close(fd);
 267                return -errno;
 268        }
 269        file_size = sb.st_size;
 270
 271        if (file_size == 0 || (file_size & (rte_mem_page_size() - 1))) {
 272                VMBUS_LOG(ERR, "incorrect size %s: %zu",
 273                          ring_path, file_size);
 274
 275                close(fd);
 276                return -EINVAL;
 277        }
 278
 279        mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
 280                                     0, file_size, 0);
 281        close(fd);
 282
 283        if (mapaddr == MAP_FAILED)
 284                return -EIO;
 285
 286        if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
 287
 288                /* Add this mapping to uio_res for use by secondary */
 289                uio_res->subchannel_maps[uio_res->nb_subchannels].relid =
 290                        chan->relid;
 291                uio_res->subchannel_maps[uio_res->nb_subchannels].addr =
 292                        mapaddr;
 293                uio_res->subchannel_maps[uio_res->nb_subchannels].size =
 294                        file_size;
 295                uio_res->nb_subchannels++;
 296
 297                vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
 298        } else {
 299                if (mapaddr != vmbus_map_addr) {
 300                        VMBUS_LOG(ERR, "failed to map channel %d to addr %p",
 301                                        chan->relid, mapaddr);
 302                        vmbus_unmap_resource(mapaddr, file_size);
 303                        return -EIO;
 304                }
 305        }
 306
 307        *ring_size = file_size / 2;
 308        *ring_buf = mapaddr;
 309
 310        return 0;
 311}
 312
 313int vmbus_uio_map_rings(struct vmbus_channel *chan)
 314{
 315        const struct rte_vmbus_device *dev = chan->device;
 316        uint32_t ring_size;
 317        void *ring_buf;
 318        int ret;
 319
 320        /* Primary channel */
 321        if (chan->subchannel_id == 0)
 322                ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
 323        else
 324                ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
 325
 326        if (ret)
 327                return ret;
 328
 329        vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
 330        vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
 331        return 0;
 332}
 333
 334static int vmbus_uio_sysfs_read(const char *dir, const char *name,
 335                                unsigned long *val, unsigned long max_range)
 336{
 337        char path[PATH_MAX];
 338        FILE *f;
 339        int ret;
 340
 341        snprintf(path, sizeof(path), "%s/%s", dir, name);
 342        f = fopen(path, "r");
 343        if (!f) {
 344                VMBUS_LOG(ERR, "can't open %s:%s",
 345                          path, strerror(errno));
 346                return -errno;
 347        }
 348
 349        if (fscanf(f, "%lu", val) != 1)
 350                ret = -EIO;
 351        else if (*val > max_range)
 352                ret = -ERANGE;
 353        else
 354                ret = 0;
 355        fclose(f);
 356
 357        return ret;
 358}
 359
 360static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
 361                                   uint32_t relid)
 362{
 363        char ring_path[PATH_MAX];
 364
 365        /* Check if kernel has subchannel sysfs files */
 366        snprintf(ring_path, sizeof(ring_path),
 367                 "%s/%s/channels/%u/ring",
 368                 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
 369
 370        return access(ring_path, R_OK|W_OK) == 0;
 371}
 372
 373bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
 374                                     const struct vmbus_channel *chan)
 375{
 376        return vmbus_uio_ring_present(dev, chan->relid);
 377}
 378
 379static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
 380                                   unsigned long id)
 381{
 382        const struct vmbus_channel *c;
 383
 384        STAILQ_FOREACH(c, &primary->subchannel_list, next) {
 385                if (c->relid == id)
 386                        return false;
 387        }
 388        return true;
 389}
 390
 391int vmbus_uio_get_subchan(struct vmbus_channel *primary,
 392                          struct vmbus_channel **subchan)
 393{
 394        const struct rte_vmbus_device *dev = primary->device;
 395        char chan_path[PATH_MAX], subchan_path[PATH_MAX];
 396        struct dirent *ent;
 397        DIR *chan_dir;
 398        int err;
 399
 400        snprintf(chan_path, sizeof(chan_path),
 401                 "%s/%s/channels",
 402                 SYSFS_VMBUS_DEVICES, dev->device.name);
 403
 404        chan_dir = opendir(chan_path);
 405        if (!chan_dir) {
 406                VMBUS_LOG(ERR, "cannot open %s: %s",
 407                          chan_path, strerror(errno));
 408                return -errno;
 409        }
 410
 411        while ((ent = readdir(chan_dir))) {
 412                unsigned long relid, subid, monid;
 413                char *endp;
 414
 415                if (ent->d_name[0] == '.')
 416                        continue;
 417
 418                errno = 0;
 419                relid = strtoul(ent->d_name, &endp, 0);
 420                if (*endp || errno != 0 || relid > UINT16_MAX) {
 421                        VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
 422                                  ent->d_name);
 423                        continue;
 424                }
 425
 426                if (!vmbus_isnew_subchannel(primary, relid)) {
 427                        VMBUS_LOG(DEBUG, "skip already found channel: %lu",
 428                                  relid);
 429                        continue;
 430                }
 431
 432                if (!vmbus_uio_ring_present(dev, relid)) {
 433                        VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
 434                                  relid);
 435                        continue;
 436                }
 437
 438                snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
 439                         chan_path, relid);
 440                err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
 441                                           &subid, UINT16_MAX);
 442                if (err) {
 443                        VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
 444                                  subchan_path, strerror(-err));
 445                        goto fail;
 446                }
 447
 448                if (subid == 0)
 449                        continue;       /* skip primary channel */
 450
 451                err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
 452                                           &monid, UINT8_MAX);
 453                if (err) {
 454                        VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
 455                                  subchan_path, strerror(-err));
 456                        goto fail;
 457                }
 458
 459                err = vmbus_chan_create(dev, relid, subid, monid, subchan);
 460                if (err) {
 461                        VMBUS_LOG(ERR, "subchannel setup failed");
 462                        goto fail;
 463                }
 464                break;
 465        }
 466        closedir(chan_dir);
 467
 468        return (ent == NULL) ? -ENOENT : 0;
 469fail:
 470        closedir(chan_dir);
 471        return err;
 472}
 473