text: /*
   * Copyright © 2009 CNRS
   * Copyright © 2009-2011 inria.  All rights reserved.
   * Copyright © 2009-2010, 2012 Université Bordeaux 1
   * Copyright © 2011 Cisco Systems, Inc.  All rights reserved.
   * See COPYING in top-level directory.
   */
  
  #include <private/autogen/config.h>
  #include <hwloc.h>
  #include <private/private.h>
  #include <hwloc/helper.h>
  #ifdef HAVE_SYS_MMAN_H
  #  include <sys/mman.h>
  #endif
  #ifdef HAVE_MALLOC_H
  #  include <malloc.h>
  #endif
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #include <stdlib.h>
  #include <errno.h>
  
  /* TODO: HWLOC_GNU_SYS, HWLOC_IRIX_SYS,
   *
   * IRIX: see MP_MUSTRUN / _DSM_MUSTRUN, pthread_setrunon_np, /hw, procss_cpulink, numa_create
   *
   * We could use glibc's sched_setaffinity generically when it is available
   *
   * Darwin and OpenBSD don't seem to have binding facilities.
   */
  
  static hwloc_const_bitmap_t
  hwloc_fix_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t set)
  {
    hwloc_const_bitmap_t topology_set = hwloc_topology_get_topology_cpuset(topology);
    hwloc_const_bitmap_t complete_set = hwloc_topology_get_complete_cpuset(topology);
  
    if (!topology_set) {
      /* The topology is composed of several systems, the cpuset is ambiguous. */
      errno = EXDEV;
      return NULL;
    }
  
    if (hwloc_bitmap_iszero(set)) {
      errno = EINVAL;
      return NULL;
    }
  
    if (!hwloc_bitmap_isincluded(set, complete_set)) {
      errno = EINVAL;
      return NULL;
    }
  
    if (hwloc_bitmap_isincluded(topology_set, set))
      set = complete_set;
  
    return set;
  }
  
  int
  hwloc_set_cpubind(hwloc_topology_t topology, hwloc_const_bitmap_t set, int flags)
  {
    set = hwloc_fix_cpubind(topology, set);
    if (!set)
      return -1;
  
    if (flags & HWLOC_CPUBIND_PROCESS) {
      if (topology->set_thisproc_cpubind)
        return topology->set_thisproc_cpubind(topology, set, flags);
    } else if (flags & HWLOC_CPUBIND_THREAD) {
      if (topology->set_thisthread_cpubind)
        return topology->set_thisthread_cpubind(topology, set, flags);
    } else {
      if (topology->set_thisproc_cpubind)
        return topology->set_thisproc_cpubind(topology, set, flags);
      else if (topology->set_thisthread_cpubind)
        return topology->set_thisthread_cpubind(topology, set, flags);
    }
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_cpubind(hwloc_topology_t topology, hwloc_bitmap_t set, int flags)
  {
    if (flags & HWLOC_CPUBIND_PROCESS) {
      if (topology->get_thisproc_cpubind)
        return topology->get_thisproc_cpubind(topology, set, flags);
    } else if (flags & HWLOC_CPUBIND_THREAD) {
      if (topology->get_thisthread_cpubind)
        return topology->get_thisthread_cpubind(topology, set, flags);
    } else {
      if (topology->get_thisproc_cpubind)
        return topology->get_thisproc_cpubind(topology, set, flags);
      else if (topology->get_thisthread_cpubind)
        return topology->get_thisthread_cpubind(topology, set, flags);
    }
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_set_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_bitmap_t set, int flags)
  {
    set = hwloc_fix_cpubind(topology, set);
    if (!set)
      return -1;
  
    if (topology->set_proc_cpubind)
      return topology->set_proc_cpubind(topology, pid, set, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_proc_cpubind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, int flags)
  {
    if (topology->get_proc_cpubind)
      return topology->get_proc_cpubind(topology, pid, set, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  #ifdef hwloc_thread_t
  int
  hwloc_set_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_const_bitmap_t set, int flags)
  {
    set = hwloc_fix_cpubind(topology, set);
    if (!set)
      return -1;
  
    if (topology->set_thread_cpubind)
      return topology->set_thread_cpubind(topology, tid, set, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_thread_cpubind(hwloc_topology_t topology, hwloc_thread_t tid, hwloc_bitmap_t set, int flags)
  {
    if (topology->get_thread_cpubind)
      return topology->get_thread_cpubind(topology, tid, set, flags);
  
    errno = ENOSYS;
    return -1;
  }
  #endif
  
  int
  hwloc_get_last_cpu_location(hwloc_topology_t topology, hwloc_bitmap_t set, int flags)
  {
    if (flags & HWLOC_CPUBIND_PROCESS) {
      if (topology->get_thisproc_last_cpu_location)
        return topology->get_thisproc_last_cpu_location(topology, set, flags);
    } else if (flags & HWLOC_CPUBIND_THREAD) {
      if (topology->get_thisthread_last_cpu_location)
        return topology->get_thisthread_last_cpu_location(topology, set, flags);
    } else {
      if (topology->get_thisproc_last_cpu_location)
        return topology->get_thisproc_last_cpu_location(topology, set, flags);
      else if (topology->get_thisthread_last_cpu_location)
        return topology->get_thisthread_last_cpu_location(topology, set, flags);
    }
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_proc_last_cpu_location(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_bitmap_t set, int flags)
  {
    if (topology->get_proc_last_cpu_location)
      return topology->get_proc_last_cpu_location(topology, pid, set, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  static hwloc_const_nodeset_t
  hwloc_fix_membind(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset)
  {
    hwloc_const_bitmap_t topology_nodeset = hwloc_topology_get_topology_nodeset(topology);
    hwloc_const_bitmap_t complete_nodeset = hwloc_topology_get_complete_nodeset(topology);
  
    if (!hwloc_topology_get_topology_cpuset(topology)) {
      /* The topology is composed of several systems, the nodeset is thus
       * ambiguous. */
      errno = EXDEV;
      return NULL;
    }
  
    if (!complete_nodeset) {
      /* There is no NUMA node */
      errno = ENODEV;
      return NULL;
    }
  
    if (hwloc_bitmap_iszero(nodeset)) {
      errno = EINVAL;
      return NULL;
    }
  
    if (!hwloc_bitmap_isincluded(nodeset, complete_nodeset)) {
      errno = EINVAL;
      return NULL;
    }
  
    if (hwloc_bitmap_isincluded(topology_nodeset, nodeset))
      return complete_nodeset;
  
    return nodeset;
  }
  
  static int
  hwloc_fix_membind_cpuset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_const_cpuset_t cpuset)
  {
    hwloc_const_bitmap_t topology_set = hwloc_topology_get_topology_cpuset(topology);
    hwloc_const_bitmap_t complete_set = hwloc_topology_get_complete_cpuset(topology);
    hwloc_const_bitmap_t complete_nodeset = hwloc_topology_get_complete_nodeset(topology);
  
    if (!topology_set) {
      /* The topology is composed of several systems, the cpuset is thus
       * ambiguous. */
      errno = EXDEV;
      return -1;
    }
  
    if (!complete_nodeset) {
      /* There is no NUMA node */
      errno = ENODEV;
      return -1;
    }
  
    if (hwloc_bitmap_iszero(cpuset)) {
      errno = EINVAL;
      return -1;
    }
  
    if (!hwloc_bitmap_isincluded(cpuset, complete_set)) {
      errno = EINVAL;
      return -1;
    }
  
    if (hwloc_bitmap_isincluded(topology_set, cpuset)) {
      hwloc_bitmap_copy(nodeset, complete_nodeset);
      return 0;
    }
  
    hwloc_cpuset_to_nodeset(topology, cpuset, nodeset);
    return 0;
  }
  
  int
  hwloc_set_membind_nodeset(hwloc_topology_t topology, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags)
  {
    nodeset = hwloc_fix_membind(topology, nodeset);
    if (!nodeset)
      return -1;
  
    if (flags & HWLOC_MEMBIND_PROCESS) {
      if (topology->set_thisproc_membind)
        return topology->set_thisproc_membind(topology, nodeset, policy, flags);
    } else if (flags & HWLOC_MEMBIND_THREAD) {
      if (topology->set_thisthread_membind)
        return topology->set_thisthread_membind(topology, nodeset, policy, flags);
    } else {
      if (topology->set_thisproc_membind)
        return topology->set_thisproc_membind(topology, nodeset, policy, flags);
      else if (topology->set_thisthread_membind)
        return topology->set_thisthread_membind(topology, nodeset, policy, flags);
    }
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_set_membind(hwloc_topology_t topology, hwloc_const_cpuset_t set, hwloc_membind_policy_t policy, int flags)
  {
    hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();
    int ret;
  
    if (hwloc_fix_membind_cpuset(topology, nodeset, set))
      ret = -1;
    else
      ret = hwloc_set_membind_nodeset(topology, nodeset, policy, flags);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_get_membind_nodeset(hwloc_topology_t topology, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags)
  {
    if (flags & HWLOC_MEMBIND_PROCESS) {
      if (topology->get_thisproc_membind)
        return topology->get_thisproc_membind(topology, nodeset, policy, flags);
    } else if (flags & HWLOC_MEMBIND_THREAD) {
      if (topology->get_thisthread_membind)
        return topology->get_thisthread_membind(topology, nodeset, policy, flags);
    } else {
      if (topology->get_thisproc_membind)
        return topology->get_thisproc_membind(topology, nodeset, policy, flags);
      else if (topology->get_thisthread_membind)
        return topology->get_thisthread_membind(topology, nodeset, policy, flags);
    }
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_membind(hwloc_topology_t topology, hwloc_cpuset_t set, hwloc_membind_policy_t * policy, int flags)
  {
    hwloc_nodeset_t nodeset;
    int ret;
  
    nodeset = hwloc_bitmap_alloc();
    ret = hwloc_get_membind_nodeset(topology, nodeset, policy, flags);
  
    if (!ret)
      hwloc_cpuset_from_nodeset(topology, set, nodeset);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_set_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags)
  {
    nodeset = hwloc_fix_membind(topology, nodeset);
    if (!nodeset)
      return -1;
  
    if (topology->set_proc_membind)
      return topology->set_proc_membind(topology, pid, nodeset, policy, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  
  int
  hwloc_set_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_const_cpuset_t set, hwloc_membind_policy_t policy, int flags)
  {
    hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();
    int ret;
  
    if (hwloc_fix_membind_cpuset(topology, nodeset, set))
      ret = -1;
    else
      ret = hwloc_set_proc_membind_nodeset(topology, pid, nodeset, policy, flags);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_get_proc_membind_nodeset(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags)
  {
    if (topology->get_proc_membind)
      return topology->get_proc_membind(topology, pid, nodeset, policy, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_proc_membind(hwloc_topology_t topology, hwloc_pid_t pid, hwloc_cpuset_t set, hwloc_membind_policy_t * policy, int flags)
  {
    hwloc_nodeset_t nodeset;
    int ret;
  
    nodeset = hwloc_bitmap_alloc();
    ret = hwloc_get_proc_membind_nodeset(topology, pid, nodeset, policy, flags);
  
    if (!ret)
      hwloc_cpuset_from_nodeset(topology, set, nodeset);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_set_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags)
  {
    nodeset = hwloc_fix_membind(topology, nodeset);
    if (!nodeset)
      return -1;
  
    if (topology->set_area_membind)
      return topology->set_area_membind(topology, addr, len, nodeset, policy, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_set_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_const_cpuset_t set, hwloc_membind_policy_t policy, int flags)
  {
    hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();
    int ret;
  
    if (hwloc_fix_membind_cpuset(topology, nodeset, set))
      ret = -1;
    else
      ret = hwloc_set_area_membind_nodeset(topology, addr, len, nodeset, policy, flags);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_get_area_membind_nodeset(hwloc_topology_t topology, const void *addr, size_t len, hwloc_nodeset_t nodeset, hwloc_membind_policy_t * policy, int flags)
  {
    if (topology->get_area_membind)
      return topology->get_area_membind(topology, addr, len, nodeset, policy, flags);
  
    errno = ENOSYS;
    return -1;
  }
  
  int
  hwloc_get_area_membind(hwloc_topology_t topology, const void *addr, size_t len, hwloc_cpuset_t set, hwloc_membind_policy_t * policy, int flags)
  {
    hwloc_nodeset_t nodeset;
    int ret;
  
    nodeset = hwloc_bitmap_alloc();
    ret = hwloc_get_area_membind_nodeset(topology, addr, len, nodeset, policy, flags);
  
    if (!ret)
      hwloc_cpuset_from_nodeset(topology, set, nodeset);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  void *
  hwloc_alloc_heap(hwloc_topology_t topology __hwloc_attribute_unused, size_t len)
  {
    void *p;
  #if defined(HAVE_GETPAGESIZE) && defined(HAVE_POSIX_MEMALIGN)
    errno = posix_memalign(&p, getpagesize(), len);
    if (errno)
      p = NULL;
  #elif defined(HAVE_GETPAGESIZE) && defined(HAVE_MEMALIGN)
    p = memalign(getpagesize(), len);
  #else
    p = malloc(len);
  #endif
    return p;
  }
  
  #ifdef MAP_ANONYMOUS
  void *
  hwloc_alloc_mmap(hwloc_topology_t topology __hwloc_attribute_unused, size_t len)
  {
    return mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  }
  #endif
  
  int
  hwloc_free_heap(hwloc_topology_t topology __hwloc_attribute_unused, void *addr, size_t len __hwloc_attribute_unused)
  {
    free(addr);
    return 0;
  }
  
  #ifdef MAP_ANONYMOUS
  int
  hwloc_free_mmap(hwloc_topology_t topology __hwloc_attribute_unused, void *addr, size_t len)
  {
    if (!addr)
      return 0;
    return munmap(addr, len);
  }
  #endif
  
  void *
  hwloc_alloc(hwloc_topology_t topology, size_t len)
  {
    if (topology->alloc)
      return topology->alloc(topology, len);
    return hwloc_alloc_heap(topology, len);
  }
  
  void *
  hwloc_alloc_membind_nodeset(hwloc_topology_t topology, size_t len, hwloc_const_nodeset_t nodeset, hwloc_membind_policy_t policy, int flags)
  {
    void *p;
    nodeset = hwloc_fix_membind(topology, nodeset);
    if (!nodeset)
      goto fallback;
    if (flags & HWLOC_MEMBIND_MIGRATE) {
      errno = EINVAL;
      goto fallback;
    }
  
    if (topology->alloc_membind)
      return topology->alloc_membind(topology, len, nodeset, policy, flags);
    else if (topology->set_area_membind) {
      p = hwloc_alloc(topology, len);
      if (!p)
        return NULL;
      if (topology->set_area_membind(topology, p, len, nodeset, policy, flags) && flags & HWLOC_MEMBIND_STRICT) {
        int error = errno;
        free(p);
        errno = error;
        return NULL;
      }
      return p;
    } else {
      errno = ENOSYS;
    }
  
  fallback:
    if (flags & HWLOC_MEMBIND_STRICT)
      /* Report error */
      return NULL;
    /* Never mind, allocate anyway */
    return hwloc_alloc(topology, len);
  }
  
  void *
  hwloc_alloc_membind(hwloc_topology_t topology, size_t len, hwloc_const_cpuset_t set, hwloc_membind_policy_t policy, int flags)
  {
    hwloc_nodeset_t nodeset = hwloc_bitmap_alloc();
    void *ret;
  
    if (hwloc_fix_membind_cpuset(topology, nodeset, set)) {
      if (flags & HWLOC_MEMBIND_STRICT)
        ret = NULL;
      else
        ret = hwloc_alloc(topology, len);
    } else
      ret = hwloc_alloc_membind_nodeset(topology, len, nodeset, policy, flags);
  
    hwloc_bitmap_free(nodeset);
    return ret;
  }
  
  int
  hwloc_free(hwloc_topology_t topology, void *addr, size_t len)
  {
    if (topology->free_membind)
      return topology->free_membind(topology, addr, len);
    return hwloc_free_heap(topology, addr, len);
  }
  