vlib: fix coverity warning
[vpp.git] / src / vlib / linux / pci.c
index c6fa8a7..9f0629f 100644 (file)
@@ -55,6 +55,7 @@
 #include <linux/vfio.h>
 #include <sys/eventfd.h>
 
+#define SYSFS_DEVICES_PCI "/sys/devices/pci"
 static const char *sysfs_pci_dev_path = "/sys/bus/pci/devices";
 static const char *sysfs_pci_drv_path = "/sys/bus/pci/drivers";
 static char *sysfs_mod_vfio_noiommu =
@@ -97,6 +98,7 @@ typedef struct
   linux_pci_device_type_t type;
   vlib_pci_dev_handle_t handle;
   vlib_pci_addr_t addr;
+  u32 numa_node;
 
   /* Resource file descriptors. */
   linux_pci_region_t *regions;
@@ -165,6 +167,31 @@ vlib_pci_get_addr (vlib_main_t * vm, vlib_pci_dev_handle_t h)
   return &d->addr;
 }
 
+u32
+vlib_pci_get_numa_node (vlib_main_t * vm, vlib_pci_dev_handle_t h)
+{
+  linux_pci_device_t *d = linux_pci_get_device (h);
+  return d->numa_node;
+}
+
+u32
+vlib_pci_get_num_msix_interrupts (vlib_main_t * vm, vlib_pci_dev_handle_t h)
+{
+  linux_pci_device_t *d = linux_pci_get_device (h);
+
+  if (d->type == LINUX_PCI_DEVICE_TYPE_VFIO)
+    {
+      struct vfio_irq_info ii = { 0 };
+
+      ii.argsz = sizeof (struct vfio_irq_info);
+      ii.index = VFIO_PCI_MSIX_IRQ_INDEX;
+      if (ioctl (d->fd, VFIO_DEVICE_GET_IRQ_INFO, &ii) < 0)
+       return 0;
+      return ii.count;
+    }
+  return 0;
+}
+
 /* Call to allocate/initialize the pci subsystem.
    This is not an init function so that users can explicitly enable
    pci only when it's needed. */
@@ -176,12 +203,13 @@ vlib_pci_device_info_t *
 vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
                          clib_error_t ** error)
 {
-  linux_vfio_main_t *lvm = &vfio_main;
   clib_error_t *err;
   vlib_pci_device_info_t *di;
   u8 *f = 0;
   u32 tmp;
   int fd;
+  u8 *tmpstr;
+  clib_bitmap_t *bmp = 0;
 
   di = clib_mem_alloc (sizeof (vlib_pci_device_info_t));
   clib_memset (di, 0, sizeof (vlib_pci_device_info_t));
@@ -234,12 +262,22 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
   di->numa_node = -1;
   vec_reset_length (f);
   f = format (f, "%v/numa_node%c", dev_dir_name, 0);
-  err = clib_sysfs_read ((char *) f, "%u", &di->numa_node);
+  err = clib_sysfs_read ((char *) f, "%d", &di->numa_node);
   if (err)
     {
       di->numa_node = -1;
       clib_error_free (err);
     }
+  if (di->numa_node == -1)
+    {
+      /* if '/sys/bus/pci/devices/<device id>/numa_node' returns -1 and
+         it is a SMP system, set numa_node to 0. */
+      if ((err = clib_sysfs_read ("/sys/devices/system/node/online", "%U",
+                                 unformat_bitmap_list, &bmp)))
+       clib_error_free (err);
+      if (clib_bitmap_count_set_bits (bmp) == 1)
+       di->numa_node = 0;
+    }
 
   vec_reset_length (f);
   f = format (f, "%v/class%c", dev_dir_name, 0);
@@ -265,31 +303,30 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
   vec_reset_length (f);
   f = format (f, "%v/driver%c", dev_dir_name, 0);
   di->driver_name = clib_sysfs_link_to_name ((char *) f);
+  if (!di->driver_name)
+    di->driver_name = format (0, "<NONE>%c", 0);
 
   di->iommu_group = -1;
-  if (lvm->container_fd != -1)
+  vec_reset_length (f);
+  f = format (f, "%v/iommu_group%c", dev_dir_name, 0);
+  tmpstr = clib_sysfs_link_to_name ((char *) f);
+  if (tmpstr)
     {
-      u8 *tmpstr;
-      vec_reset_length (f);
-      f = format (f, "%v/iommu_group%c", dev_dir_name, 0);
-      tmpstr = clib_sysfs_link_to_name ((char *) f);
-      if (tmpstr)
-       {
-         di->iommu_group = atoi ((char *) tmpstr);
-         vec_free (tmpstr);
-       }
-      vec_reset_length (f);
-      f = format (f, "%v/iommu_group/name%c", dev_dir_name, 0);
-      err = clib_sysfs_read ((char *) f, "%s", &tmpstr);
-      if (err == 0)
-       {
-         if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
-           di->flags |= VLIB_PCI_DEVICE_INFO_F_NOIOMMU;
-         vec_free (tmpstr);
-       }
-      else
-       clib_error_free (err);
+      di->iommu_group = atoi ((char *) tmpstr);
+      vec_free (tmpstr);
+    }
+
+  vec_reset_length (f);
+  f = format (f, "%v/iommu_group/name%c", dev_dir_name, 0);
+  err = clib_sysfs_read ((char *) f, "%s", &tmpstr);
+  if (err == 0)
+    {
+      if (strncmp ((char *) tmpstr, "vfio-noiommu", 12) == 0)
+       di->flags |= VLIB_PCI_DEVICE_INFO_F_NOIOMMU;
+      vec_free (tmpstr);
     }
+  else
+    clib_error_free (err);
 
   close (fd);
 
@@ -311,7 +348,7 @@ vlib_pci_get_device_info (vlib_main_t * vm, vlib_pci_addr_t * addr,
            break;
 
          len = (tag[2] << 8) | tag[1];
-         vec_validate (data, len);
+         vec_validate (data, len - 1);
 
          if (read (fd, data, len) != len)
            {
@@ -337,6 +374,7 @@ error:
   di = 0;
 
 done:
+  vec_free (bmp);
   vec_free (f);
   vec_free (dev_dir_name);
   if (error)
@@ -346,6 +384,64 @@ done:
   return di;
 }
 
+clib_error_t *__attribute__ ((weak))
+vlib_pci_get_device_root_bus (vlib_pci_addr_t *addr, vlib_pci_addr_t *root_bus)
+{
+  u8 *rel_path = 0, *abs_path = 0, *link_path = 0;
+  unformat_input_t input;
+  int fd = open (sysfs_pci_dev_path, O_RDONLY);
+  ssize_t size = 0;
+  u32 domain = 0, bus;
+  clib_error_t *err = NULL;
+
+  if (fd < 0)
+    return clib_error_return_unix (0, "failed to open %s", sysfs_pci_dev_path);
+
+  vec_alloc (rel_path, PATH_MAX);
+  vec_alloc (abs_path, PATH_MAX);
+
+  link_path =
+    format (0, "%s/%U", sysfs_pci_dev_path, format_vlib_pci_addr, addr);
+  size = readlinkat (fd, (char *) link_path, (char *) rel_path, PATH_MAX);
+  if (size < 0)
+    {
+      err = clib_error_return_unix (0, "failed to read %s", rel_path);
+      goto done;
+    }
+
+  rel_path[size] = '\0';
+  vec_free (link_path);
+
+  link_path = format (0, "%s/%s", sysfs_pci_dev_path, rel_path);
+  if (!realpath ((char *) link_path, (char *) abs_path))
+    {
+      err = clib_error_return_unix (0, "failed to resolve %s", link_path);
+      goto done;
+    }
+
+  unformat_init_string (&input, (char *) abs_path,
+                       clib_strnlen ((char *) abs_path, PATH_MAX));
+
+  if (!unformat (&input, SYSFS_DEVICES_PCI "%x:%x/%s", &domain, &bus,
+                link_path))
+    {
+      err = clib_error_return (0, "unknown input '%U'", format_unformat_error,
+                              input);
+      goto done;
+    }
+
+  root_bus->domain = domain;
+  root_bus->bus = bus;
+
+done:
+  vec_free (abs_path);
+  vec_free (link_path);
+  vec_free (rel_path);
+  close (fd);
+
+  return err;
+}
+
 static int
 directory_exists (char *path)
 {
@@ -458,8 +554,8 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr,
       clib_memset (&ifr, 0, sizeof ifr);
       clib_memset (&drvinfo, 0, sizeof drvinfo);
       ifr.ifr_data = (char *) &drvinfo;
-      strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name));
-      ifr.ifr_name[ARRAY_LEN (ifr.ifr_name) - 1] = '\0';
+      clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1);
+
       drvinfo.cmd = ETHTOOL_GDRVINFO;
       if (ioctl (fd, SIOCETHTOOL, &ifr) < 0)
        {
@@ -474,8 +570,8 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr,
        continue;
 
       clib_memset (&ifr, 0, sizeof (ifr));
-      strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name));
-      ifr.ifr_name[ARRAY_LEN (ifr.ifr_name) - 1] = '\0';
+      clib_strncpy (ifr.ifr_name, e->d_name, sizeof (ifr.ifr_name) - 1);
+
       if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
        {
          error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
@@ -486,9 +582,10 @@ vlib_pci_bind_to_uio (vlib_main_t * vm, vlib_pci_addr_t * addr,
 
       if (ifr.ifr_flags & IFF_UP)
        {
-         error = clib_error_return (0, "Skipping PCI device %U as host "
-                                    "interface %s is up",
-                                    format_vlib_pci_addr, addr, e->d_name);
+         vlib_log (VLIB_LOG_LEVEL_WARNING, pci_main.log_default,
+                   "Skipping PCI device %U as host "
+                   "interface %s is up", format_vlib_pci_addr, addr,
+                   e->d_name);
          close (fd);
          goto done;
        }
@@ -584,7 +681,7 @@ vfio_set_irqs (vlib_main_t * vm, linux_pci_device_t * p, u32 index, u32 start,
   if (efds)
     {
       flags |= VFIO_IRQ_SET_DATA_EVENTFD;
-      clib_memcpy (&irq_set->data, efds, data_len);
+      clib_memcpy_fast (&irq_set->data, efds, data_len);
     }
   else
     flags |= VFIO_IRQ_SET_DATA_NONE;
@@ -713,8 +810,8 @@ add_device_uio (vlib_main_t * vm, linux_pci_device_t * p,
       goto error;
     }
 
-  s = format (0, "%s/%U/uio", sysfs_pci_dev_path,
-             format_vlib_pci_addr, &di->addr);
+  s = format (0, "%s/%U/uio%c", sysfs_pci_dev_path,
+             format_vlib_pci_addr, &di->addr, 0);
   foreach_directory_file ((char *) s, scan_uio_dir, p, /* scan_dirs */
                          1);
   vec_reset_length (s);
@@ -875,6 +972,17 @@ vlib_pci_enable_msix_irq (vlib_main_t * vm, vlib_pci_dev_handle_t h,
                        VFIO_IRQ_SET_ACTION_TRIGGER, fds);
 }
 
+uword
+vlib_pci_get_msix_file_index (vlib_main_t * vm, vlib_pci_dev_handle_t h,
+                             u16 index)
+{
+  linux_pci_device_t *p = linux_pci_get_device (h);
+  linux_pci_irq_t *irq = vec_elt_at_index (p->msix_irqs, index);
+  if (irq->fd == -1)
+    return ~0;
+  return irq->clib_file_index;
+}
+
 clib_error_t *
 vlib_pci_disable_msix_irq (vlib_main_t * vm, vlib_pci_dev_handle_t h,
                           u16 start, u16 count)
@@ -929,13 +1037,7 @@ add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p,
       goto error;
     }
 
-  pci_log_debug (vm, p, "%s region_info index:%u size:0x%lx offset:0x%lx "
-                "flags: %s%s%s(0x%x)", __func__,
-                reg.index, reg.size, reg.offset,
-                reg.flags & VFIO_REGION_INFO_FLAG_READ ? "rd " : "",
-                reg.flags & VFIO_REGION_INFO_FLAG_WRITE ? "wr " : "",
-                reg.flags & VFIO_REGION_INFO_FLAG_MMAP ? "mmap " : "",
-                reg.flags);
+  pci_log_debug (vm, p, "%s %U", __func__, format_vfio_region_info, &reg);
 
   p->config_offset = reg.offset;
   p->config_fd = p->fd;
@@ -959,7 +1061,7 @@ add_device_vfio (vlib_main_t * vm, linux_pci_device_t * p,
     {
       vlib_buffer_pool_t *bp;
       /* *INDENT-OFF* */
-      vec_foreach (bp, buffer_main.buffer_pools)
+      vec_foreach (bp, vm->buffer_main->buffer_pools)
        {
          u32 i;
          vlib_physmem_map_t *pm;
@@ -1050,23 +1152,28 @@ vlib_pci_region (vlib_main_t * vm, vlib_pci_dev_handle_t h, u32 bar, int *fd,
     }
   else if (p->type == LINUX_PCI_DEVICE_TYPE_VFIO)
     {
-      struct vfio_region_info reg = { 0 };
-      reg.argsz = sizeof (struct vfio_region_info);
-      reg.index = bar;
-      if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, &reg) < 0)
+      struct vfio_region_info *r;
+      u32 sz = sizeof (struct vfio_region_info);
+    again:
+      r = clib_mem_alloc (sz);
+      clib_memset (r, 0, sz);
+      r->argsz = sz;
+      r->index = bar;
+      if (ioctl (p->fd, VFIO_DEVICE_GET_REGION_INFO, r) < 0)
        return clib_error_return_unix (0, "ioctl(VFIO_DEVICE_GET_INFO) "
                                       "'%U'", format_vlib_pci_addr,
                                       &p->addr);
+      if (sz != r->argsz)
+       {
+         sz = r->argsz;
+         clib_mem_free (r);
+         goto again;
+       }
       _fd = p->fd;
-      _size = reg.size;
-      _offset = reg.offset;
-      pci_log_debug (vm, p, "%s region_info index:%u size:0x%lx offset:0x%lx "
-                    "flags: %s%s%s(0x%x)", __func__,
-                    reg.index, reg.size, reg.offset,
-                    reg.flags & VFIO_REGION_INFO_FLAG_READ ? "rd " : "",
-                    reg.flags & VFIO_REGION_INFO_FLAG_WRITE ? "wr " : "",
-                    reg.flags & VFIO_REGION_INFO_FLAG_MMAP ? "mmap " : "",
-                    reg.flags);
+      _size = r->size;
+      _offset = r->offset;
+      pci_log_debug (vm, p, "%s %U", __func__, format_vfio_region_info, r);
+      clib_mem_free (r);
     }
   else
     ASSERT (0);
@@ -1085,22 +1192,32 @@ vlib_pci_map_region_int (vlib_main_t * vm, vlib_pci_dev_handle_t h,
   linux_pci_device_t *p = linux_pci_get_device (h);
   int fd = -1;
   clib_error_t *error;
-  int flags = MAP_SHARED;
   u64 size = 0, offset = 0;
+  u16 command;
 
   pci_log_debug (vm, p, "map region %u to va %p", bar, addr);
 
-  if ((error = vlib_pci_region (vm, h, bar, &fd, &size, &offset)))
+  if ((error = vlib_pci_read_config_u16 (vm, h, 4, &command)))
     return error;
 
-  if (p->type == LINUX_PCI_DEVICE_TYPE_UIO && addr != 0)
-    flags |= MAP_FIXED;
+  if (!(command & PCI_COMMAND_MEMORY))
+    {
+      pci_log_debug (vm, p, "setting memory enable bit");
+      command |= PCI_COMMAND_MEMORY;
+      if ((error = vlib_pci_write_config_u16 (vm, h, 4, &command)))
+       return error;
+    }
+
+  if ((error = vlib_pci_region (vm, h, bar, &fd, &size, &offset)))
+    return error;
 
-  *result = mmap (addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
-  if (*result == (void *) -1)
+  *result = clib_mem_vm_map_shared (addr, size, fd, offset,
+                                   "PCIe %U region %u", format_vlib_pci_addr,
+                                   vlib_pci_get_addr (vm, h), bar);
+  if (*result == CLIB_MEM_VM_MAP_FAILED)
     {
       error = clib_error_return_unix (0, "mmap `BAR%u'", bar);
-      if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
+      if (p->type == LINUX_PCI_DEVICE_TYPE_UIO && (fd != -1))
        close (fd);
       return error;
     }
@@ -1210,6 +1327,8 @@ vlib_pci_device_open (vlib_main_t * vm, vlib_pci_addr_t * addr,
   p->handle = p - lpm->linux_pci_devices;
   p->addr.as_u32 = di->addr.as_u32;
   p->intx_irq.fd = -1;
+  p->intx_irq.clib_file_index = -1;
+  p->numa_node = di->numa_node;
   /*
    * pci io bar read/write fd
    */
@@ -1219,9 +1338,9 @@ vlib_pci_device_open (vlib_main_t * vm, vlib_pci_addr_t * addr,
                 di->vendor_id, di->device_id, di->driver_name,
                 di->iommu_group);
 
-  if (strncmp ("vfio-pci", (char *) di->driver_name, 8) == 0)
+  if (clib_strncmp ("vfio-pci", (char *) di->driver_name, 8) == 0)
     err = add_device_vfio (vm, p, di, 0);
-  else if (strncmp ("uio_pci_generic", (char *) di->driver_name, 8) == 0)
+  else if (clib_strncmp ("uio_pci_generic", (char *) di->driver_name, 8) == 0)
     err = add_device_uio (vm, p, di, 0);
   else
     err = clib_error_create ("device not bound to 'vfio-pci' or "
@@ -1255,7 +1374,8 @@ vlib_pci_device_close (vlib_main_t * vm, vlib_pci_dev_handle_t h)
   if (p->type == LINUX_PCI_DEVICE_TYPE_UIO)
     {
       irq = &p->intx_irq;
-      clib_file_del_by_index (&file_main, irq->clib_file_index);
+      if (irq->clib_file_index != -1)
+       clib_file_del_by_index (&file_main, irq->clib_file_index);
       close (p->config_fd);
       if (p->io_fd != -1)
        close (p->io_fd);
@@ -1269,7 +1389,8 @@ vlib_pci_device_close (vlib_main_t * vm, vlib_pci_dev_handle_t h)
          err = vfio_set_irqs (vm, p, VFIO_PCI_INTX_IRQ_INDEX, 0, 0,
                               VFIO_IRQ_SET_ACTION_TRIGGER, 0);
          clib_error_free (err);
-         clib_file_del_by_index (&file_main, irq->clib_file_index);
+         if (irq->clib_file_index != -1)
+           clib_file_del_by_index (&file_main, irq->clib_file_index);
          close (irq->fd);
        }
 
@@ -1297,7 +1418,7 @@ vlib_pci_device_close (vlib_main_t * vm, vlib_pci_dev_handle_t h)
     {
       if (res->size == 0)
        continue;
-      munmap (res->addr, res->size);
+      clib_mem_vm_unmap (res->addr);
       if (res->fd != -1)
         close (res->fd);
     }
@@ -1419,13 +1540,9 @@ linux_pci_init (vlib_main_t * vm)
 {
   vlib_pci_main_t *pm = &pci_main;
   vlib_pci_addr_t *addr = 0, *addrs;
-  clib_error_t *error;
 
   pm->vlib_main = vm;
 
-  if ((error = vlib_call_init_function (vm, unix_input_init)))
-    return error;
-
   ASSERT (sizeof (vlib_pci_addr_t) == sizeof (u32));
 
   addrs = vlib_pci_get_all_dev_addrs ();
@@ -1441,10 +1558,15 @@ linux_pci_init (vlib_main_t * vm)
     }
   /* *INDENT-ON* */
 
-  return error;
+  return 0;
 }
 
-VLIB_INIT_FUNCTION (linux_pci_init);
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (linux_pci_init) =
+{
+  .runs_after = VLIB_INITS("unix_input_init"),
+};
+/* *INDENT-ON* */
 
 /*
  * fd.io coding-style-patch-verification: ON