Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/amdkfd/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 68 kB image not shown  

Quelle  kfd_topology.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
 * Copyright 2014-2022 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */


#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/hash.h>
#include <linux/cpufreq.h>
#include <linux/log2.h>
#include <linux/dmi.h>
#include <linux/atomic.h>
#include <linux/crc16.h>

#include "kfd_priv.h"
#include "kfd_crat.h"
#include "kfd_topology.h"
#include "kfd_device_queue_manager.h"
#include "kfd_svm.h"
#include "kfd_debug.h"
#include "amdgpu_amdkfd.h"
#include "amdgpu_ras.h"
#include "amdgpu.h"

/* topology_device_list - Master list of all topology devices */
static struct list_head topology_device_list;
static struct kfd_system_properties sys_props;

static DECLARE_RWSEM(topology_lock);
static uint32_t topology_crat_proximity_domain;

struct kfd_topology_device *kfd_topology_device_by_proximity_domain_no_lock(
      uint32_t proximity_domain)
{
 struct kfd_topology_device *top_dev;
 struct kfd_topology_device *device = NULL;

 list_for_each_entry(top_dev, &topology_device_list, list)
  if (top_dev->proximity_domain == proximity_domain) {
   device = top_dev;
   break;
  }

 return device;
}

struct kfd_topology_device *kfd_topology_device_by_proximity_domain(
      uint32_t proximity_domain)
{
 struct kfd_topology_device *device = NULL;

 down_read(&topology_lock);

 device = kfd_topology_device_by_proximity_domain_no_lock(
       proximity_domain);
 up_read(&topology_lock);

 return device;
}

struct kfd_topology_device *kfd_topology_device_by_id(uint32_t gpu_id)
{
 struct kfd_topology_device *top_dev = NULL;
 struct kfd_topology_device *ret = NULL;

 down_read(&topology_lock);

 list_for_each_entry(top_dev, &topology_device_list, list)
  if (top_dev->gpu_id == gpu_id) {
   ret = top_dev;
   break;
  }

 up_read(&topology_lock);

 return ret;
}

struct kfd_node *kfd_device_by_id(uint32_t gpu_id)
{
 struct kfd_topology_device *top_dev;

 top_dev = kfd_topology_device_by_id(gpu_id);
 if (!top_dev)
  return NULL;

 return top_dev->gpu;
}

/* Called with write topology_lock acquired */
static void kfd_release_topology_device(struct kfd_topology_device *dev)
{
 struct kfd_mem_properties *mem;
 struct kfd_cache_properties *cache;
 struct kfd_iolink_properties *iolink;
 struct kfd_iolink_properties *p2plink;
 struct kfd_perf_properties *perf;

 list_del(&dev->list);

 while (dev->mem_props.next != &dev->mem_props) {
  mem = container_of(dev->mem_props.next,
    struct kfd_mem_properties, list);
  list_del(&mem->list);
  kfree(mem);
 }

 while (dev->cache_props.next != &dev->cache_props) {
  cache = container_of(dev->cache_props.next,
    struct kfd_cache_properties, list);
  list_del(&cache->list);
  kfree(cache);
 }

 while (dev->io_link_props.next != &dev->io_link_props) {
  iolink = container_of(dev->io_link_props.next,
    struct kfd_iolink_properties, list);
  list_del(&iolink->list);
  kfree(iolink);
 }

 while (dev->p2p_link_props.next != &dev->p2p_link_props) {
  p2plink = container_of(dev->p2p_link_props.next,
    struct kfd_iolink_properties, list);
  list_del(&p2plink->list);
  kfree(p2plink);
 }

 while (dev->perf_props.next != &dev->perf_props) {
  perf = container_of(dev->perf_props.next,
    struct kfd_perf_properties, list);
  list_del(&perf->list);
  kfree(perf);
 }

 kfree(dev);
}

void kfd_release_topology_device_list(struct list_head *device_list)
{
 struct kfd_topology_device *dev;

 while (!list_empty(device_list)) {
  dev = list_first_entry(device_list,
           struct kfd_topology_device, list);
  kfd_release_topology_device(dev);
 }
}

static void kfd_release_live_view(void)
{
 kfd_release_topology_device_list(&topology_device_list);
 memset(&sys_props, 0, sizeof(sys_props));
}

struct kfd_topology_device *kfd_create_topology_device(
    struct list_head *device_list)
{
 struct kfd_topology_device *dev;

 dev = kfd_alloc_struct(dev);
 if (!dev) {
  pr_err("No memory to allocate a topology device");
  return NULL;
 }

 INIT_LIST_HEAD(&dev->mem_props);
 INIT_LIST_HEAD(&dev->cache_props);
 INIT_LIST_HEAD(&dev->io_link_props);
 INIT_LIST_HEAD(&dev->p2p_link_props);
 INIT_LIST_HEAD(&dev->perf_props);

 list_add_tail(&dev->list, device_list);

 return dev;
}


#define sysfs_show_gen_prop(buffer, offs, fmt, ...)  \
  (offs += snprintf(buffer+offs, PAGE_SIZE-offs, \
      fmt, __VA_ARGS__))
#define sysfs_show_32bit_prop(buffer, offs, name, value) \
  sysfs_show_gen_prop(buffer, offs, "%s %u\n", name, value)
#define sysfs_show_64bit_prop(buffer, offs, name, value) \
  sysfs_show_gen_prop(buffer, offs, "%s %llu\n", name, value)
#define sysfs_show_32bit_val(buffer, offs, value) \
  sysfs_show_gen_prop(buffer, offs, "%u\n", value)
#define sysfs_show_str_val(buffer, offs, value) \
  sysfs_show_gen_prop(buffer, offs, "%s\n", value)

static ssize_t sysprops_show(struct kobject *kobj, struct attribute *attr,
  char *buffer)
{
 int offs = 0;

 /* Making sure that the buffer is an empty string */
 buffer[0] = 0;

 if (attr == &sys_props.attr_genid) {
  sysfs_show_32bit_val(buffer, offs,
         sys_props.generation_count);
 } else if (attr == &sys_props.attr_props) {
  sysfs_show_64bit_prop(buffer, offs, "platform_oem",
          sys_props.platform_oem);
  sysfs_show_64bit_prop(buffer, offs, "platform_id",
          sys_props.platform_id);
  sysfs_show_64bit_prop(buffer, offs, "platform_rev",
          sys_props.platform_rev);
 } else {
  offs = -EINVAL;
 }

 return offs;
}

static void kfd_topology_kobj_release(struct kobject *kobj)
{
 kfree(kobj);
}

static const struct sysfs_ops sysprops_ops = {
 .show = sysprops_show,
};

static const struct kobj_type sysprops_type = {
 .release = kfd_topology_kobj_release,
 .sysfs_ops = &sysprops_ops,
};

static ssize_t iolink_show(struct kobject *kobj, struct attribute *attr,
  char *buffer)
{
 int offs = 0;
 struct kfd_iolink_properties *iolink;

 /* Making sure that the buffer is an empty string */
 buffer[0] = 0;

 iolink = container_of(attr, struct kfd_iolink_properties, attr);
 if (iolink->gpu && kfd_devcgroup_check_permission(iolink->gpu))
  return -EPERM;
 sysfs_show_32bit_prop(buffer, offs, "type", iolink->iolink_type);
 sysfs_show_32bit_prop(buffer, offs, "version_major", iolink->ver_maj);
 sysfs_show_32bit_prop(buffer, offs, "version_minor", iolink->ver_min);
 sysfs_show_32bit_prop(buffer, offs, "node_from", iolink->node_from);
 sysfs_show_32bit_prop(buffer, offs, "node_to", iolink->node_to);
 sysfs_show_32bit_prop(buffer, offs, "weight", iolink->weight);
 sysfs_show_32bit_prop(buffer, offs, "min_latency", iolink->min_latency);
 sysfs_show_32bit_prop(buffer, offs, "max_latency", iolink->max_latency);
 sysfs_show_32bit_prop(buffer, offs, "min_bandwidth",
         iolink->min_bandwidth);
 sysfs_show_32bit_prop(buffer, offs, "max_bandwidth",
         iolink->max_bandwidth);
 sysfs_show_32bit_prop(buffer, offs, "recommended_transfer_size",
         iolink->rec_transfer_size);
 sysfs_show_32bit_prop(buffer, offs, "recommended_sdma_engine_id_mask",
         iolink->rec_sdma_eng_id_mask);
 sysfs_show_32bit_prop(buffer, offs, "flags", iolink->flags);

 return offs;
}

static const struct sysfs_ops iolink_ops = {
 .show = iolink_show,
};

static const struct kobj_type iolink_type = {
 .release = kfd_topology_kobj_release,
 .sysfs_ops = &iolink_ops,
};

static ssize_t mem_show(struct kobject *kobj, struct attribute *attr,
  char *buffer)
{
 int offs = 0;
 struct kfd_mem_properties *mem;

 /* Making sure that the buffer is an empty string */
 buffer[0] = 0;

 mem = container_of(attr, struct kfd_mem_properties, attr);
 if (mem->gpu && kfd_devcgroup_check_permission(mem->gpu))
  return -EPERM;
 sysfs_show_32bit_prop(buffer, offs, "heap_type", mem->heap_type);
 sysfs_show_64bit_prop(buffer, offs, "size_in_bytes",
         mem->size_in_bytes);
 sysfs_show_32bit_prop(buffer, offs, "flags", mem->flags);
 sysfs_show_32bit_prop(buffer, offs, "width", mem->width);
 sysfs_show_32bit_prop(buffer, offs, "mem_clk_max",
         mem->mem_clk_max);

 return offs;
}

static const struct sysfs_ops mem_ops = {
 .show = mem_show,
};

static const struct kobj_type mem_type = {
 .release = kfd_topology_kobj_release,
 .sysfs_ops = &mem_ops,
};

static ssize_t kfd_cache_show(struct kobject *kobj, struct attribute *attr,
  char *buffer)
{
 int offs = 0;
 uint32_t i, j;
 struct kfd_cache_properties *cache;

 /* Making sure that the buffer is an empty string */
 buffer[0] = 0;
 cache = container_of(attr, struct kfd_cache_properties, attr);
 if (cache->gpu && kfd_devcgroup_check_permission(cache->gpu))
  return -EPERM;
 sysfs_show_32bit_prop(buffer, offs, "processor_id_low",
   cache->processor_id_low);
 sysfs_show_32bit_prop(buffer, offs, "level", cache->cache_level);
 sysfs_show_32bit_prop(buffer, offs, "size", cache->cache_size);
 sysfs_show_32bit_prop(buffer, offs, "cache_line_size",
         cache->cacheline_size);
 sysfs_show_32bit_prop(buffer, offs, "cache_lines_per_tag",
         cache->cachelines_per_tag);
 sysfs_show_32bit_prop(buffer, offs, "association", cache->cache_assoc);
 sysfs_show_32bit_prop(buffer, offs, "latency", cache->cache_latency);
 sysfs_show_32bit_prop(buffer, offs, "type", cache->cache_type);

 offs += snprintf(buffer+offs, PAGE_SIZE-offs, "sibling_map ");
 for (i = 0; i < cache->sibling_map_size; i++)
  for (j = 0; j < sizeof(cache->sibling_map[0])*8; j++)
   /* Check each bit */
   offs += snprintf(buffer+offs, PAGE_SIZE-offs, "%d,",
      (cache->sibling_map[i] >> j) & 1);

 /* Replace the last "," with end of line */
 buffer[offs-1] = '\n';
 return offs;
}

static const struct sysfs_ops cache_ops = {
 .show = kfd_cache_show,
};

static const struct kobj_type cache_type = {
 .release = kfd_topology_kobj_release,
 .sysfs_ops = &cache_ops,
};

/****** Sysfs of Performance Counters ******/

struct kfd_perf_attr {
 struct kobj_attribute attr;
 uint32_t data;
};

static ssize_t perf_show(struct kobject *kobj, struct kobj_attribute *attrs,
   char *buf)
{
 int offs = 0;
 struct kfd_perf_attr *attr;

 buf[0] = 0;
 attr = container_of(attrs, struct kfd_perf_attr, attr);
 if (!attr->data) /* invalid data for PMC */
  return 0;
 else
  return sysfs_show_32bit_val(buf, offs, attr->data);
}

#define KFD_PERF_DESC(_name, _data)   \
{       \
 .attr  = __ATTR(_name, 0444, perf_show, NULL), \
 .data = _data,     \
}

static struct kfd_perf_attr perf_attr_iommu[] = {
 KFD_PERF_DESC(max_concurrent, 0),
 KFD_PERF_DESC(num_counters, 0),
 KFD_PERF_DESC(counter_ids, 0),
};
/****************************************/

static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
  char *buffer)
{
 int offs = 0;
 struct kfd_topology_device *dev;
 uint32_t log_max_watch_addr;

 /* Making sure that the buffer is an empty string */
 buffer[0] = 0;

 if (strcmp(attr->name, "gpu_id") == 0) {
  dev = container_of(attr, struct kfd_topology_device,
    attr_gpuid);
  if (dev->gpu && kfd_devcgroup_check_permission(dev->gpu))
   return -EPERM;
  return sysfs_show_32bit_val(buffer, offs, dev->gpu_id);
 }

 if (strcmp(attr->name, "name") == 0) {
  dev = container_of(attr, struct kfd_topology_device,
    attr_name);

  if (dev->gpu && kfd_devcgroup_check_permission(dev->gpu))
   return -EPERM;
  return sysfs_show_str_val(buffer, offs, dev->node_props.name);
 }

 dev = container_of(attr, struct kfd_topology_device,
   attr_props);
 if (dev->gpu && kfd_devcgroup_check_permission(dev->gpu))
  return -EPERM;
 sysfs_show_32bit_prop(buffer, offs, "cpu_cores_count",
         dev->node_props.cpu_cores_count);
 sysfs_show_32bit_prop(buffer, offs, "simd_count",
         dev->gpu ? dev->node_props.simd_count : 0);
 sysfs_show_32bit_prop(buffer, offs, "mem_banks_count",
         dev->node_props.mem_banks_count);
 sysfs_show_32bit_prop(buffer, offs, "caches_count",
         dev->node_props.caches_count);
 sysfs_show_32bit_prop(buffer, offs, "io_links_count",
         dev->node_props.io_links_count);
 sysfs_show_32bit_prop(buffer, offs, "p2p_links_count",
         dev->node_props.p2p_links_count);
 sysfs_show_32bit_prop(buffer, offs, "cpu_core_id_base",
         dev->node_props.cpu_core_id_base);
 sysfs_show_32bit_prop(buffer, offs, "simd_id_base",
         dev->node_props.simd_id_base);
 sysfs_show_32bit_prop(buffer, offs, "max_waves_per_simd",
         dev->node_props.max_waves_per_simd);
 sysfs_show_32bit_prop(buffer, offs, "lds_size_in_kb",
         dev->node_props.lds_size_in_kb);
 sysfs_show_32bit_prop(buffer, offs, "gds_size_in_kb",
         dev->node_props.gds_size_in_kb);
 sysfs_show_32bit_prop(buffer, offs, "num_gws",
         dev->node_props.num_gws);
 sysfs_show_32bit_prop(buffer, offs, "wave_front_size",
         dev->node_props.wave_front_size);
 sysfs_show_32bit_prop(buffer, offs, "array_count",
         dev->gpu ? (dev->node_props.array_count *
       NUM_XCC(dev->gpu->xcc_mask)) : 0);
 sysfs_show_32bit_prop(buffer, offs, "simd_arrays_per_engine",
         dev->node_props.simd_arrays_per_engine);
 sysfs_show_32bit_prop(buffer, offs, "cu_per_simd_array",
         dev->node_props.cu_per_simd_array);
 sysfs_show_32bit_prop(buffer, offs, "simd_per_cu",
         dev->node_props.simd_per_cu);
 sysfs_show_32bit_prop(buffer, offs, "max_slots_scratch_cu",
         dev->node_props.max_slots_scratch_cu);
 sysfs_show_32bit_prop(buffer, offs, "gfx_target_version",
         dev->node_props.gfx_target_version);
 sysfs_show_32bit_prop(buffer, offs, "vendor_id",
         dev->node_props.vendor_id);
 sysfs_show_32bit_prop(buffer, offs, "device_id",
         dev->node_props.device_id);
 sysfs_show_32bit_prop(buffer, offs, "location_id",
         dev->node_props.location_id);
 sysfs_show_32bit_prop(buffer, offs, "domain",
         dev->node_props.domain);
 sysfs_show_32bit_prop(buffer, offs, "drm_render_minor",
         dev->node_props.drm_render_minor);
 sysfs_show_64bit_prop(buffer, offs, "hive_id",
         dev->node_props.hive_id);
 sysfs_show_32bit_prop(buffer, offs, "num_sdma_engines",
         dev->node_props.num_sdma_engines);
 sysfs_show_32bit_prop(buffer, offs, "num_sdma_xgmi_engines",
         dev->node_props.num_sdma_xgmi_engines);
 sysfs_show_32bit_prop(buffer, offs, "num_sdma_queues_per_engine",
         dev->node_props.num_sdma_queues_per_engine);
 sysfs_show_32bit_prop(buffer, offs, "num_cp_queues",
         dev->node_props.num_cp_queues);

 if (dev->gpu) {
  log_max_watch_addr =
   __ilog2_u32(dev->gpu->kfd->device_info.num_of_watch_points);

  if (log_max_watch_addr) {
   dev->node_props.capability |=
     HSA_CAP_WATCH_POINTS_SUPPORTED;

   dev->node_props.capability |=
    ((log_max_watch_addr <<
     HSA_CAP_WATCH_POINTS_TOTALBITS_SHIFT) &
    HSA_CAP_WATCH_POINTS_TOTALBITS_MASK);
  }

  if (dev->gpu->adev->asic_type == CHIP_TONGA)
   dev->node_props.capability |=
     HSA_CAP_AQL_QUEUE_DOUBLE_MAP;

  if (KFD_GC_VERSION(dev->gpu) < IP_VERSION(10, 0, 0) &&
   (dev->gpu->adev->sdma.supported_reset & AMDGPU_RESET_TYPE_PER_QUEUE))
    dev->node_props.capability2 |= HSA_CAP2_PER_SDMA_QUEUE_RESET_SUPPORTED;

  sysfs_show_32bit_prop(buffer, offs, "max_engine_clk_fcompute",
   dev->node_props.max_engine_clk_fcompute);

  sysfs_show_64bit_prop(buffer, offs, "local_mem_size", 0ULL);

  sysfs_show_32bit_prop(buffer, offs, "fw_version",
          dev->gpu->kfd->mec_fw_version);
  sysfs_show_32bit_prop(buffer, offs, "capability",
          dev->node_props.capability);
  sysfs_show_32bit_prop(buffer, offs, "capability2",
          dev->node_props.capability2);
  sysfs_show_64bit_prop(buffer, offs, "debug_prop",
          dev->node_props.debug_prop);
  sysfs_show_32bit_prop(buffer, offs, "sdma_fw_version",
          dev->gpu->kfd->sdma_fw_version);
  sysfs_show_64bit_prop(buffer, offs, "unique_id",
          dev->gpu->adev->unique_id);
  sysfs_show_32bit_prop(buffer, offs, "num_xcc",
          NUM_XCC(dev->gpu->xcc_mask));
 }

 return sysfs_show_32bit_prop(buffer, offs, "max_engine_clk_ccompute",
         cpufreq_quick_get_max(0)/1000);
}

static const struct sysfs_ops node_ops = {
 .show = node_show,
};

static const struct kobj_type node_type = {
 .release = kfd_topology_kobj_release,
 .sysfs_ops = &node_ops,
};

static void kfd_remove_sysfs_file(struct kobject *kobj, struct attribute *attr)
{
 sysfs_remove_file(kobj, attr);
 kobject_del(kobj);
 kobject_put(kobj);
}

static void kfd_remove_sysfs_node_entry(struct kfd_topology_device *dev)
{
 struct kfd_iolink_properties *p2plink;
 struct kfd_iolink_properties *iolink;
 struct kfd_cache_properties *cache;
 struct kfd_mem_properties *mem;
 struct kfd_perf_properties *perf;

 if (dev->kobj_iolink) {
  list_for_each_entry(iolink, &dev->io_link_props, list)
   if (iolink->kobj) {
    kfd_remove_sysfs_file(iolink->kobj,
       &iolink->attr);
    iolink->kobj = NULL;
   }
  kobject_del(dev->kobj_iolink);
  kobject_put(dev->kobj_iolink);
  dev->kobj_iolink = NULL;
 }

 if (dev->kobj_p2plink) {
  list_for_each_entry(p2plink, &dev->p2p_link_props, list)
   if (p2plink->kobj) {
    kfd_remove_sysfs_file(p2plink->kobj,
       &p2plink->attr);
    p2plink->kobj = NULL;
   }
  kobject_del(dev->kobj_p2plink);
  kobject_put(dev->kobj_p2plink);
  dev->kobj_p2plink = NULL;
 }

 if (dev->kobj_cache) {
  list_for_each_entry(cache, &dev->cache_props, list)
   if (cache->kobj) {
    kfd_remove_sysfs_file(cache->kobj,
       &cache->attr);
    cache->kobj = NULL;
   }
  kobject_del(dev->kobj_cache);
  kobject_put(dev->kobj_cache);
  dev->kobj_cache = NULL;
 }

 if (dev->kobj_mem) {
  list_for_each_entry(mem, &dev->mem_props, list)
   if (mem->kobj) {
    kfd_remove_sysfs_file(mem->kobj, &mem->attr);
    mem->kobj = NULL;
   }
  kobject_del(dev->kobj_mem);
  kobject_put(dev->kobj_mem);
  dev->kobj_mem = NULL;
 }

 if (dev->kobj_perf) {
  list_for_each_entry(perf, &dev->perf_props, list) {
   kfree(perf->attr_group);
   perf->attr_group = NULL;
  }
  kobject_del(dev->kobj_perf);
  kobject_put(dev->kobj_perf);
  dev->kobj_perf = NULL;
 }

 if (dev->kobj_node) {
  sysfs_remove_file(dev->kobj_node, &dev->attr_gpuid);
  sysfs_remove_file(dev->kobj_node, &dev->attr_name);
  sysfs_remove_file(dev->kobj_node, &dev->attr_props);
  kobject_del(dev->kobj_node);
  kobject_put(dev->kobj_node);
  dev->kobj_node = NULL;
 }
}

static int kfd_build_sysfs_node_entry(struct kfd_topology_device *dev,
  uint32_t id)
{
 struct kfd_iolink_properties *p2plink;
 struct kfd_iolink_properties *iolink;
 struct kfd_cache_properties *cache;
 struct kfd_mem_properties *mem;
 struct kfd_perf_properties *perf;
 int ret;
 uint32_t i, num_attrs;
 struct attribute **attrs;

 if (WARN_ON(dev->kobj_node))
  return -EEXIST;

 /*
 * Creating the sysfs folders
 */

 dev->kobj_node = kfd_alloc_struct(dev->kobj_node);
 if (!dev->kobj_node)
  return -ENOMEM;

 ret = kobject_init_and_add(dev->kobj_node, &node_type,
   sys_props.kobj_nodes, "%d", id);
 if (ret < 0) {
  kobject_put(dev->kobj_node);
  return ret;
 }

 dev->kobj_mem = kobject_create_and_add("mem_banks", dev->kobj_node);
 if (!dev->kobj_mem)
  return -ENOMEM;

 dev->kobj_cache = kobject_create_and_add("caches", dev->kobj_node);
 if (!dev->kobj_cache)
  return -ENOMEM;

 dev->kobj_iolink = kobject_create_and_add("io_links", dev->kobj_node);
 if (!dev->kobj_iolink)
  return -ENOMEM;

 dev->kobj_p2plink = kobject_create_and_add("p2p_links", dev->kobj_node);
 if (!dev->kobj_p2plink)
  return -ENOMEM;

 dev->kobj_perf = kobject_create_and_add("perf", dev->kobj_node);
 if (!dev->kobj_perf)
  return -ENOMEM;

 /*
 * Creating sysfs files for node properties
 */

 dev->attr_gpuid.name = "gpu_id";
 dev->attr_gpuid.mode = KFD_SYSFS_FILE_MODE;
 sysfs_attr_init(&dev->attr_gpuid);
 dev->attr_name.name = "name";
 dev->attr_name.mode = KFD_SYSFS_FILE_MODE;
 sysfs_attr_init(&dev->attr_name);
 dev->attr_props.name = "properties";
 dev->attr_props.mode = KFD_SYSFS_FILE_MODE;
 sysfs_attr_init(&dev->attr_props);
 ret = sysfs_create_file(dev->kobj_node, &dev->attr_gpuid);
 if (ret < 0)
  return ret;
 ret = sysfs_create_file(dev->kobj_node, &dev->attr_name);
 if (ret < 0)
  return ret;
 ret = sysfs_create_file(dev->kobj_node, &dev->attr_props);
 if (ret < 0)
  return ret;

 i = 0;
 list_for_each_entry(mem, &dev->mem_props, list) {
  mem->kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
  if (!mem->kobj)
   return -ENOMEM;
  ret = kobject_init_and_add(mem->kobj, &mem_type,
    dev->kobj_mem, "%d", i);
  if (ret < 0) {
   kobject_put(mem->kobj);
   return ret;
  }

  mem->attr.name = "properties";
  mem->attr.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&mem->attr);
  ret = sysfs_create_file(mem->kobj, &mem->attr);
  if (ret < 0)
   return ret;
  i++;
 }

 i = 0;
 list_for_each_entry(cache, &dev->cache_props, list) {
  cache->kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
  if (!cache->kobj)
   return -ENOMEM;
  ret = kobject_init_and_add(cache->kobj, &cache_type,
    dev->kobj_cache, "%d", i);
  if (ret < 0) {
   kobject_put(cache->kobj);
   return ret;
  }

  cache->attr.name = "properties";
  cache->attr.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&cache->attr);
  ret = sysfs_create_file(cache->kobj, &cache->attr);
  if (ret < 0)
   return ret;
  i++;
 }

 i = 0;
 list_for_each_entry(iolink, &dev->io_link_props, list) {
  iolink->kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
  if (!iolink->kobj)
   return -ENOMEM;
  ret = kobject_init_and_add(iolink->kobj, &iolink_type,
    dev->kobj_iolink, "%d", i);
  if (ret < 0) {
   kobject_put(iolink->kobj);
   return ret;
  }

  iolink->attr.name = "properties";
  iolink->attr.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&iolink->attr);
  ret = sysfs_create_file(iolink->kobj, &iolink->attr);
  if (ret < 0)
   return ret;
  i++;
 }

 i = 0;
 list_for_each_entry(p2plink, &dev->p2p_link_props, list) {
  p2plink->kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
  if (!p2plink->kobj)
   return -ENOMEM;
  ret = kobject_init_and_add(p2plink->kobj, &iolink_type,
    dev->kobj_p2plink, "%d", i);
  if (ret < 0) {
   kobject_put(p2plink->kobj);
   return ret;
  }

  p2plink->attr.name = "properties";
  p2plink->attr.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&p2plink->attr);
  ret = sysfs_create_file(p2plink->kobj, &p2plink->attr);
  if (ret < 0)
   return ret;
  i++;
 }

 /* All hardware blocks have the same number of attributes. */
 num_attrs = ARRAY_SIZE(perf_attr_iommu);
 list_for_each_entry(perf, &dev->perf_props, list) {
  perf->attr_group = kzalloc(sizeof(struct kfd_perf_attr)
   * num_attrs + sizeof(struct attribute_group),
   GFP_KERNEL);
  if (!perf->attr_group)
   return -ENOMEM;

  attrs = (struct attribute **)(perf->attr_group + 1);
  if (!strcmp(perf->block_name, "iommu")) {
  /* Information of IOMMU's num_counters and counter_ids is shown
 * under /sys/bus/event_source/devices/amd_iommu. We don't
 * duplicate here.
 */

   perf_attr_iommu[0].data = perf->max_concurrent;
   for (i = 0; i < num_attrs; i++)
    attrs[i] = &perf_attr_iommu[i].attr.attr;
  }
  perf->attr_group->name = perf->block_name;
  perf->attr_group->attrs = attrs;
  ret = sysfs_create_group(dev->kobj_perf, perf->attr_group);
  if (ret < 0)
   return ret;
 }

 return 0;
}

/* Called with write topology lock acquired */
static int kfd_build_sysfs_node_tree(void)
{
 struct kfd_topology_device *dev;
 int ret;
 uint32_t i = 0;

 list_for_each_entry(dev, &topology_device_list, list) {
  ret = kfd_build_sysfs_node_entry(dev, i);
  if (ret < 0)
   return ret;
  i++;
 }

 return 0;
}

/* Called with write topology lock acquired */
static void kfd_remove_sysfs_node_tree(void)
{
 struct kfd_topology_device *dev;

 list_for_each_entry(dev, &topology_device_list, list)
  kfd_remove_sysfs_node_entry(dev);
}

static int kfd_topology_update_sysfs(void)
{
 int ret;

 if (!sys_props.kobj_topology) {
  sys_props.kobj_topology =
    kfd_alloc_struct(sys_props.kobj_topology);
  if (!sys_props.kobj_topology)
   return -ENOMEM;

  ret = kobject_init_and_add(sys_props.kobj_topology,
    &sysprops_type,  &kfd_device->kobj,
    "topology");
  if (ret < 0) {
   kobject_put(sys_props.kobj_topology);
   return ret;
  }

  sys_props.kobj_nodes = kobject_create_and_add("nodes",
    sys_props.kobj_topology);
  if (!sys_props.kobj_nodes)
   return -ENOMEM;

  sys_props.attr_genid.name = "generation_id";
  sys_props.attr_genid.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&sys_props.attr_genid);
  ret = sysfs_create_file(sys_props.kobj_topology,
    &sys_props.attr_genid);
  if (ret < 0)
   return ret;

  sys_props.attr_props.name = "system_properties";
  sys_props.attr_props.mode = KFD_SYSFS_FILE_MODE;
  sysfs_attr_init(&sys_props.attr_props);
  ret = sysfs_create_file(sys_props.kobj_topology,
    &sys_props.attr_props);
  if (ret < 0)
   return ret;
 }

 kfd_remove_sysfs_node_tree();

 return kfd_build_sysfs_node_tree();
}

static void kfd_topology_release_sysfs(void)
{
 kfd_remove_sysfs_node_tree();
 if (sys_props.kobj_topology) {
  sysfs_remove_file(sys_props.kobj_topology,
    &sys_props.attr_genid);
  sysfs_remove_file(sys_props.kobj_topology,
    &sys_props.attr_props);
  if (sys_props.kobj_nodes) {
   kobject_del(sys_props.kobj_nodes);
   kobject_put(sys_props.kobj_nodes);
   sys_props.kobj_nodes = NULL;
  }
  kobject_del(sys_props.kobj_topology);
  kobject_put(sys_props.kobj_topology);
  sys_props.kobj_topology = NULL;
 }
}

/* Called with write topology_lock acquired */
static void kfd_topology_update_device_list(struct list_head *temp_list,
     struct list_head *master_list)
{
 while (!list_empty(temp_list)) {
  list_move_tail(temp_list->next, master_list);
  sys_props.num_devices++;
 }
}

static void kfd_debug_print_topology(void)
{
 struct kfd_topology_device *dev;

 down_read(&topology_lock);

 dev = list_last_entry(&topology_device_list,
   struct kfd_topology_device, list);
 if (dev) {
  if (dev->node_props.cpu_cores_count &&
    dev->node_props.simd_count) {
   pr_info("Topology: Add APU node [0x%0x:0x%0x]\n",
    dev->node_props.device_id,
    dev->node_props.vendor_id);
  } else if (dev->node_props.cpu_cores_count)
   pr_info("Topology: Add CPU node\n");
  else if (dev->node_props.simd_count)
   pr_info("Topology: Add dGPU node [0x%0x:0x%0x]\n",
    dev->node_props.device_id,
    dev->node_props.vendor_id);
 }
 up_read(&topology_lock);
}

/* Helper function for intializing platform_xx members of
 * kfd_system_properties. Uses OEM info from the last CPU/APU node.
 */

static void kfd_update_system_properties(void)
{
 struct kfd_topology_device *dev;

 down_read(&topology_lock);
 dev = list_last_entry(&topology_device_list,
   struct kfd_topology_device, list);
 if (dev) {
  sys_props.platform_id = dev->oem_id64;
  sys_props.platform_oem = *((uint64_t *)dev->oem_table_id);
  sys_props.platform_rev = dev->oem_revision;
 }
 up_read(&topology_lock);
}

static void find_system_memory(const struct dmi_header *dm, void *private)
{
 struct dmi_mem_device *memdev = container_of(dm, struct dmi_mem_device, header);
 struct kfd_mem_properties *mem;
 struct kfd_topology_device *kdev =
  (struct kfd_topology_device *)private;

 if (memdev->header.type != DMI_ENTRY_MEM_DEVICE)
  return;
 if (memdev->header.length < sizeof(struct dmi_mem_device))
  return;

 list_for_each_entry(mem, &kdev->mem_props, list) {
  if (memdev->total_width != 0xFFFF && memdev->total_width != 0)
   mem->width = memdev->total_width;
  if (memdev->speed != 0)
   mem->mem_clk_max = memdev->speed;
 }
}

/* kfd_add_non_crat_information - Add information that is not currently
 * defined in CRAT but is necessary for KFD topology
 * @dev - topology device to which addition info is added
 */

static void kfd_add_non_crat_information(struct kfd_topology_device *kdev)
{
 /* Check if CPU only node. */
 if (!kdev->gpu) {
  /* Add system memory information */
  dmi_walk(find_system_memory, kdev);
 }
 /* TODO: For GPU node, rearrange code from kfd_topology_add_device */
}

int kfd_topology_init(void)
{
 void *crat_image = NULL;
 size_t image_size = 0;
 int ret;
 struct list_head temp_topology_device_list;
 int cpu_only_node = 0;
 struct kfd_topology_device *kdev;
 int proximity_domain;

 /* topology_device_list - Master list of all topology devices
 * temp_topology_device_list - temporary list created while parsing CRAT
 * or VCRAT. Once parsing is complete the contents of list is moved to
 * topology_device_list
 */


 /* Initialize the head for the both the lists */
 INIT_LIST_HEAD(&topology_device_list);
 INIT_LIST_HEAD(&temp_topology_device_list);
 init_rwsem(&topology_lock);

 memset(&sys_props, 0, sizeof(sys_props));

 /* Proximity domains in ACPI CRAT tables start counting at
 * 0. The same should be true for virtual CRAT tables created
 * at this stage. GPUs added later in kfd_topology_add_device
 * use a counter.
 */

 proximity_domain = 0;

 ret = kfd_create_crat_image_virtual(&crat_image, &image_size,
         COMPUTE_UNIT_CPU, NULL,
         proximity_domain);
 cpu_only_node = 1;
 if (ret) {
  pr_err("Error creating VCRAT table for CPU\n");
  return ret;
 }

 ret = kfd_parse_crat_table(crat_image,
       &temp_topology_device_list,
       proximity_domain);
 if (ret) {
  pr_err("Error parsing VCRAT table for CPU\n");
  goto err;
 }

 kdev = list_first_entry(&temp_topology_device_list,
    struct kfd_topology_device, list);

 down_write(&topology_lock);
 kfd_topology_update_device_list(&temp_topology_device_list,
     &topology_device_list);
 topology_crat_proximity_domain = sys_props.num_devices-1;
 ret = kfd_topology_update_sysfs();
 up_write(&topology_lock);

 if (!ret) {
  sys_props.generation_count++;
  kfd_update_system_properties();
  kfd_debug_print_topology();
 } else
  pr_err("Failed to update topology in sysfs ret=%d\n", ret);

 /* For nodes with GPU, this information gets added
 * when GPU is detected (kfd_topology_add_device).
 */

 if (cpu_only_node) {
  /* Add additional information to CPU only node created above */
  down_write(&topology_lock);
  kdev = list_first_entry(&topology_device_list,
    struct kfd_topology_device, list);
  up_write(&topology_lock);
  kfd_add_non_crat_information(kdev);
 }

err:
 kfd_destroy_crat_image(crat_image);
 return ret;
}

void kfd_topology_shutdown(void)
{
 down_write(&topology_lock);
 kfd_topology_release_sysfs();
 kfd_release_live_view();
 up_write(&topology_lock);
}

static uint32_t kfd_generate_gpu_id(struct kfd_node *gpu)
{
 uint32_t gpu_id;
 uint32_t buf[8];
 uint64_t local_mem_size;
 struct kfd_topology_device *dev;
 bool is_unique;
 uint8_t *crc_buf;

 if (!gpu)
  return 0;

 crc_buf = (uint8_t *)&buf;
 local_mem_size = gpu->local_mem_info.local_mem_size_private +
   gpu->local_mem_info.local_mem_size_public;
 buf[0] = gpu->adev->pdev->devfn;
 buf[1] = gpu->adev->pdev->subsystem_vendor |
  (gpu->adev->pdev->subsystem_device << 16);
 buf[2] = pci_domain_nr(gpu->adev->pdev->bus);
 buf[3] = gpu->adev->pdev->device;
 buf[4] = gpu->adev->pdev->bus->number;
 buf[5] = lower_32_bits(local_mem_size);
 buf[6] = upper_32_bits(local_mem_size);
 buf[7] = (ffs(gpu->xcc_mask) - 1) | (NUM_XCC(gpu->xcc_mask) << 16);

 gpu_id = crc16(0, crc_buf, sizeof(buf)) &
   ((1 << KFD_GPU_ID_HASH_WIDTH) - 1);

 /* There is a very small possibility when generating a
 * 16 (KFD_GPU_ID_HASH_WIDTH) bit value from 8 word buffer
 * that the value could be 0 or non-unique. So, check if
 * it is unique and non-zero. If not unique increment till
 * unique one is found. In case of overflow, restart from 1
 */


 down_read(&topology_lock);
 do {
  is_unique = true;
  if (!gpu_id)
   gpu_id = 1;
  list_for_each_entry(dev, &topology_device_list, list) {
   if (dev->gpu && dev->gpu_id == gpu_id) {
    is_unique = false;
    break;
   }
  }
  if (unlikely(!is_unique))
   gpu_id = (gpu_id + 1) &
      ((1 << KFD_GPU_ID_HASH_WIDTH) - 1);
 } while (!is_unique);
 up_read(&topology_lock);

 return gpu_id;
}
/* kfd_assign_gpu - Attach @gpu to the correct kfd topology device. If
 * the GPU device is not already present in the topology device
 * list then return NULL. This means a new topology device has to
 * be created for this GPU.
 */

static struct kfd_topology_device *kfd_assign_gpu(struct kfd_node *gpu)
{
 struct kfd_topology_device *dev;
 struct kfd_topology_device *out_dev = NULL;
 struct kfd_mem_properties *mem;
 struct kfd_cache_properties *cache;
 struct kfd_iolink_properties *iolink;
 struct kfd_iolink_properties *p2plink;

 list_for_each_entry(dev, &topology_device_list, list) {
  /* Discrete GPUs need their own topology device list
 * entries. Don't assign them to CPU/APU nodes.
 */

  if (dev->node_props.cpu_cores_count)
   continue;

  if (!dev->gpu && (dev->node_props.simd_count > 0)) {
   dev->gpu = gpu;
   out_dev = dev;

   list_for_each_entry(mem, &dev->mem_props, list)
    mem->gpu = dev->gpu;
   list_for_each_entry(cache, &dev->cache_props, list)
    cache->gpu = dev->gpu;
   list_for_each_entry(iolink, &dev->io_link_props, list)
    iolink->gpu = dev->gpu;
   list_for_each_entry(p2plink, &dev->p2p_link_props, list)
    p2plink->gpu = dev->gpu;
   break;
  }
 }
 return out_dev;
}

static void kfd_notify_gpu_change(uint32_t gpu_id, int arrival)
{
 /*
 * TODO: Generate an event for thunk about the arrival/removal
 * of the GPU
 */

}

/* kfd_fill_mem_clk_max_info - Since CRAT doesn't have memory clock info,
 * patch this after CRAT parsing.
 */

static void kfd_fill_mem_clk_max_info(struct kfd_topology_device *dev)
{
 struct kfd_mem_properties *mem;
 struct kfd_local_mem_info local_mem_info;

 if (!dev)
  return;

 /* Currently, amdgpu driver (amdgpu_mc) deals only with GPUs with
 * single bank of VRAM local memory.
 * for dGPUs - VCRAT reports only one bank of Local Memory
 * for APUs - If CRAT from ACPI reports more than one bank, then
 * all the banks will report the same mem_clk_max information
 */

 amdgpu_amdkfd_get_local_mem_info(dev->gpu->adev, &local_mem_info,
      dev->gpu->xcp);

 list_for_each_entry(mem, &dev->mem_props, list)
  mem->mem_clk_max = local_mem_info.mem_clk_max;
}

static void kfd_set_iolink_no_atomics(struct kfd_topology_device *dev,
     struct kfd_topology_device *target_gpu_dev,
     struct kfd_iolink_properties *link)
{
 /* xgmi always supports atomics between links. */
 if (link->iolink_type == CRAT_IOLINK_TYPE_XGMI)
  return;

 /* check pcie support to set cpu(dev) flags for target_gpu_dev link. */
 if (target_gpu_dev) {
  uint32_t cap;

  pcie_capability_read_dword(target_gpu_dev->gpu->adev->pdev,
    PCI_EXP_DEVCAP2, &cap);

  if (!(cap & (PCI_EXP_DEVCAP2_ATOMIC_COMP32 |
        PCI_EXP_DEVCAP2_ATOMIC_COMP64)))
   link->flags |= CRAT_IOLINK_FLAGS_NO_ATOMICS_32_BIT |
    CRAT_IOLINK_FLAGS_NO_ATOMICS_64_BIT;
 /* set gpu (dev) flags. */
 } else {
  if (!dev->gpu->kfd->pci_atomic_requested ||
    dev->gpu->adev->asic_type == CHIP_HAWAII)
   link->flags |= CRAT_IOLINK_FLAGS_NO_ATOMICS_32_BIT |
    CRAT_IOLINK_FLAGS_NO_ATOMICS_64_BIT;
 }
}

static void kfd_set_iolink_non_coherent(struct kfd_topology_device *to_dev,
  struct kfd_iolink_properties *outbound_link,
  struct kfd_iolink_properties *inbound_link)
{
 /* CPU -> GPU with PCIe */
 if (!to_dev->gpu &&
     inbound_link->iolink_type == CRAT_IOLINK_TYPE_PCIEXPRESS)
  inbound_link->flags |= CRAT_IOLINK_FLAGS_NON_COHERENT;

 if (to_dev->gpu) {
  /* GPU <-> GPU with PCIe and
 * Vega20 with XGMI
 */

  if (inbound_link->iolink_type == CRAT_IOLINK_TYPE_PCIEXPRESS ||
      (inbound_link->iolink_type == CRAT_IOLINK_TYPE_XGMI &&
      KFD_GC_VERSION(to_dev->gpu) == IP_VERSION(9, 4, 0))) {
   outbound_link->flags |= CRAT_IOLINK_FLAGS_NON_COHERENT;
   inbound_link->flags |= CRAT_IOLINK_FLAGS_NON_COHERENT;
  }
 }
}

#define REC_SDMA_NUM_GPU 8
static const int rec_sdma_eng_map[REC_SDMA_NUM_GPU][REC_SDMA_NUM_GPU] = {
       { -1, 14, 12, 2, 4, 8, 10, 6 },
       { 14, -1, 2, 10, 8, 4, 6, 12 },
       { 10, 2, -1, 12, 14, 6, 4, 8 },
       { 2, 12, 10, -1, 6, 14, 8, 4 },
       { 4, 8, 14, 6, -1, 10, 12, 2 },
       { 8, 4, 6, 14, 12, -1, 2, 10 },
       { 10, 6, 4, 8, 12, 2, -1, 14 },
       { 6, 12, 8, 4, 2, 10, 14, -1 }};

static void kfd_set_recommended_sdma_engines(struct kfd_topology_device *to_dev,
          struct kfd_iolink_properties *outbound_link,
          struct kfd_iolink_properties *inbound_link)
{
 struct kfd_node *gpu = outbound_link->gpu;
 struct amdgpu_device *adev = gpu->adev;
 unsigned int num_xgmi_nodes = adev->gmc.xgmi.num_physical_nodes;
 unsigned int num_xgmi_sdma_engines = kfd_get_num_xgmi_sdma_engines(gpu);
 unsigned int num_sdma_engines = kfd_get_num_sdma_engines(gpu);
 uint32_t sdma_eng_id_mask = (1 << num_sdma_engines) - 1;
 uint32_t xgmi_sdma_eng_id_mask =
   ((1 << num_xgmi_sdma_engines) - 1) << num_sdma_engines;

 bool support_rec_eng = !amdgpu_sriov_vf(adev) && to_dev->gpu &&
  adev->aid_mask && num_xgmi_nodes && gpu->kfd->num_nodes == 1 &&
  num_xgmi_sdma_engines >= 6 && (!(adev->flags & AMD_IS_APU) &&
  num_xgmi_nodes == 8);

 if (support_rec_eng) {
  int src_socket_id = adev->gmc.xgmi.physical_node_id;
  int dst_socket_id = to_dev->gpu->adev->gmc.xgmi.physical_node_id;
  unsigned int reshift = num_xgmi_sdma_engines == 6 ? 1 : 0;

  outbound_link->rec_sdma_eng_id_mask =
   1 << (rec_sdma_eng_map[src_socket_id][dst_socket_id] >> reshift);
  inbound_link->rec_sdma_eng_id_mask =
   1 << (rec_sdma_eng_map[dst_socket_id][src_socket_id] >> reshift);

  /* If recommended engine is out of range, need to reset the mask */
  if (outbound_link->rec_sdma_eng_id_mask & sdma_eng_id_mask)
   outbound_link->rec_sdma_eng_id_mask = xgmi_sdma_eng_id_mask;
  if (inbound_link->rec_sdma_eng_id_mask & sdma_eng_id_mask)
   inbound_link->rec_sdma_eng_id_mask = xgmi_sdma_eng_id_mask;

 } else {
  uint32_t engine_mask = (outbound_link->iolink_type == CRAT_IOLINK_TYPE_XGMI &&
    num_xgmi_sdma_engines && to_dev->gpu) ? xgmi_sdma_eng_id_mask :
    sdma_eng_id_mask;

  outbound_link->rec_sdma_eng_id_mask = engine_mask;
  inbound_link->rec_sdma_eng_id_mask = engine_mask;
 }
}

static void kfd_fill_iolink_non_crat_info(struct kfd_topology_device *dev)
{
 struct kfd_iolink_properties *link, *inbound_link;
 struct kfd_topology_device *peer_dev;

 if (!dev || !dev->gpu)
  return;

 /* GPU only creates direct links so apply flags setting to all */
 list_for_each_entry(link, &dev->io_link_props, list) {
  link->flags = CRAT_IOLINK_FLAGS_ENABLED;
  kfd_set_iolink_no_atomics(dev, NULL, link);
  peer_dev = kfd_topology_device_by_proximity_domain(
    link->node_to);

  if (!peer_dev)
   continue;

  /* Include the CPU peer in GPU hive if connected over xGMI. */
  if (!peer_dev->gpu &&
      link->iolink_type == CRAT_IOLINK_TYPE_XGMI) {
   /*
 * If the GPU is not part of a GPU hive, use its pci
 * device location as the hive ID to bind with the CPU.
 */

   if (!dev->node_props.hive_id)
    dev->node_props.hive_id = pci_dev_id(dev->gpu->adev->pdev);
   peer_dev->node_props.hive_id = dev->node_props.hive_id;
  }

  list_for_each_entry(inbound_link, &peer_dev->io_link_props,
         list) {
   if (inbound_link->node_to != link->node_from)
    continue;

   inbound_link->flags = CRAT_IOLINK_FLAGS_ENABLED;
   kfd_set_iolink_no_atomics(peer_dev, dev, inbound_link);
   kfd_set_iolink_non_coherent(peer_dev, link, inbound_link);
   kfd_set_recommended_sdma_engines(peer_dev, link, inbound_link);
  }
 }

 /* Create indirect links so apply flags setting to all */
 list_for_each_entry(link, &dev->p2p_link_props, list) {
  link->flags = CRAT_IOLINK_FLAGS_ENABLED;
  kfd_set_iolink_no_atomics(dev, NULL, link);
  peer_dev = kfd_topology_device_by_proximity_domain(
    link->node_to);

  if (!peer_dev)
   continue;

  list_for_each_entry(inbound_link, &peer_dev->p2p_link_props,
         list) {
   if (inbound_link->node_to != link->node_from)
    continue;

   inbound_link->flags = CRAT_IOLINK_FLAGS_ENABLED;
   kfd_set_iolink_no_atomics(peer_dev, dev, inbound_link);
   kfd_set_iolink_non_coherent(peer_dev, link, inbound_link);
  }
 }
}

static int kfd_build_p2p_node_entry(struct kfd_topology_device *dev,
    struct kfd_iolink_properties *p2plink)
{
 int ret;

 p2plink->kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL);
 if (!p2plink->kobj)
  return -ENOMEM;

 ret = kobject_init_and_add(p2plink->kobj, &iolink_type,
   dev->kobj_p2plink, "%d", dev->node_props.p2p_links_count - 1);
 if (ret < 0) {
  kobject_put(p2plink->kobj);
  return ret;
 }

 p2plink->attr.name = "properties";
 p2plink->attr.mode = KFD_SYSFS_FILE_MODE;
 sysfs_attr_init(&p2plink->attr);
 ret = sysfs_create_file(p2plink->kobj, &p2plink->attr);
 if (ret < 0)
  return ret;

 return 0;
}

static int kfd_create_indirect_link_prop(struct kfd_topology_device *kdev, int gpu_node)
{
 struct kfd_iolink_properties *gpu_link, *tmp_link, *cpu_link;
 struct kfd_iolink_properties *props = NULL, *props2 = NULL;
 struct kfd_topology_device *cpu_dev;
 int ret = 0;
 int i, num_cpu;

 num_cpu = 0;
 list_for_each_entry(cpu_dev, &topology_device_list, list) {
  if (cpu_dev->gpu)
   break;
  num_cpu++;
 }

 if (list_empty(&kdev->io_link_props))
  return -ENODATA;

 gpu_link = list_first_entry(&kdev->io_link_props,
        struct kfd_iolink_properties, list);

 for (i = 0; i < num_cpu; i++) {
  /* CPU <--> GPU */
  if (gpu_link->node_to == i)
   continue;

  /* find CPU <-->  CPU links */
  cpu_link = NULL;
  cpu_dev = kfd_topology_device_by_proximity_domain(i);
  if (cpu_dev) {
   list_for_each_entry(tmp_link,
     &cpu_dev->io_link_props, list) {
    if (tmp_link->node_to == gpu_link->node_to) {
     cpu_link = tmp_link;
     break;
    }
   }
  }

  if (!cpu_link)
   return -ENOMEM;

  /* CPU <--> CPU <--> GPU, GPU node*/
  props = kfd_alloc_struct(props);
  if (!props)
   return -ENOMEM;

  memcpy(props, gpu_link, sizeof(struct kfd_iolink_properties));
  props->weight = gpu_link->weight + cpu_link->weight;
  props->min_latency = gpu_link->min_latency + cpu_link->min_latency;
  props->max_latency = gpu_link->max_latency + cpu_link->max_latency;
  props->min_bandwidth = min(gpu_link->min_bandwidth, cpu_link->min_bandwidth);
  props->max_bandwidth = min(gpu_link->max_bandwidth, cpu_link->max_bandwidth);

  props->node_from = gpu_node;
  props->node_to = i;
  kdev->node_props.p2p_links_count++;
  list_add_tail(&props->list, &kdev->p2p_link_props);
  ret = kfd_build_p2p_node_entry(kdev, props);
  if (ret < 0)
   return ret;

  /* for small Bar, no CPU --> GPU in-direct links */
  if (kfd_dev_is_large_bar(kdev->gpu)) {
   /* CPU <--> CPU <--> GPU, CPU node*/
   props2 = kfd_alloc_struct(props2);
   if (!props2)
    return -ENOMEM;

   memcpy(props2, props, sizeof(struct kfd_iolink_properties));
   props2->node_from = i;
   props2->node_to = gpu_node;
   props2->kobj = NULL;
   cpu_dev->node_props.p2p_links_count++;
   list_add_tail(&props2->list, &cpu_dev->p2p_link_props);
   ret = kfd_build_p2p_node_entry(cpu_dev, props2);
   if (ret < 0)
    return ret;
  }
 }
 return ret;
}

#if defined(CONFIG_HSA_AMD_P2P)
static int kfd_add_peer_prop(struct kfd_topology_device *kdev,
  struct kfd_topology_device *peer, int from, int to)
{
 struct kfd_iolink_properties *props = NULL;
 struct kfd_iolink_properties *iolink1, *iolink2, *iolink3;
 struct kfd_topology_device *cpu_dev;
 int ret = 0;

 if (!amdgpu_device_is_peer_accessible(
    kdev->gpu->adev,
    peer->gpu->adev))
  return ret;

 if (list_empty(&kdev->io_link_props))
  return -ENODATA;

 iolink1 = list_first_entry(&kdev->io_link_props,
       struct kfd_iolink_properties, list);

 if (list_empty(&peer->io_link_props))
  return -ENODATA;

 iolink2 = list_first_entry(&peer->io_link_props,
       struct kfd_iolink_properties, list);

 props = kfd_alloc_struct(props);
 if (!props)
  return -ENOMEM;

 memcpy(props, iolink1, sizeof(struct kfd_iolink_properties));

 props->weight = iolink1->weight + iolink2->weight;
 props->min_latency = iolink1->min_latency + iolink2->min_latency;
 props->max_latency = iolink1->max_latency + iolink2->max_latency;
 props->min_bandwidth = min(iolink1->min_bandwidth, iolink2->min_bandwidth);
 props->max_bandwidth = min(iolink2->max_bandwidth, iolink2->max_bandwidth);

 if (iolink1->node_to != iolink2->node_to) {
  /* CPU->CPU  link*/
  cpu_dev = kfd_topology_device_by_proximity_domain(iolink1->node_to);
  if (cpu_dev) {
   list_for_each_entry(iolink3, &cpu_dev->io_link_props, list) {
    if (iolink3->node_to != iolink2->node_to)
     continue;

    props->weight += iolink3->weight;
    props->min_latency += iolink3->min_latency;
    props->max_latency += iolink3->max_latency;
    props->min_bandwidth = min(props->min_bandwidth,
          iolink3->min_bandwidth);
    props->max_bandwidth = min(props->max_bandwidth,
          iolink3->max_bandwidth);
    break;
   }
  } else {
   WARN(1, "CPU node not found");
  }
 }

 props->node_from = from;
 props->node_to = to;
 peer->node_props.p2p_links_count++;
 list_add_tail(&props->list, &peer->p2p_link_props);
 ret = kfd_build_p2p_node_entry(peer, props);

 return ret;
}
#endif

static int kfd_dev_create_p2p_links(void)
{
 struct kfd_topology_device *dev;
 struct kfd_topology_device *new_dev;
#if defined(CONFIG_HSA_AMD_P2P)
 uint32_t i;
#endif
 uint32_t k;
 int ret = 0;

 k = 0;
 list_for_each_entry(dev, &topology_device_list, list)
  k++;
 if (k < 2)
  return 0;

 new_dev = list_last_entry(&topology_device_list, struct kfd_topology_device, list);
 if (WARN_ON(!new_dev->gpu))
  return 0;

 k--;

 /* create in-direct links */
 ret = kfd_create_indirect_link_prop(new_dev, k);
 if (ret < 0)
  goto out;

 /* create p2p links */
#if defined(CONFIG_HSA_AMD_P2P)
 i = 0;
 list_for_each_entry(dev, &topology_device_list, list) {
  if (dev == new_dev)
   break;
  if (!dev->gpu || !dev->gpu->adev ||
      (dev->gpu->kfd->hive_id &&
       dev->gpu->kfd->hive_id == new_dev->gpu->kfd->hive_id &&
       amdgpu_xgmi_get_is_sharing_enabled(dev->gpu->adev, new_dev->gpu->adev)))
   goto next;

  /* check if node(s) is/are peer accessible in one direction or bi-direction */
  ret = kfd_add_peer_prop(new_dev, dev, i, k);
  if (ret < 0)
   goto out;

  ret = kfd_add_peer_prop(dev, new_dev, k, i);
  if (ret < 0)
   goto out;
next:
  i++;
 }
#endif

out:
 return ret;
}

/* Helper function. See kfd_fill_gpu_cache_info for parameter description */
static int fill_in_l1_pcache(struct kfd_cache_properties **props_ext,
    struct kfd_gpu_cache_info *pcache_info,
    int cu_bitmask,
    int cache_type, unsigned int cu_processor_id,
    int cu_block)
{
 unsigned int cu_sibling_map_mask;
 int first_active_cu;
 struct kfd_cache_properties *pcache = NULL;

 cu_sibling_map_mask = cu_bitmask;
 cu_sibling_map_mask >>= cu_block;
 cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1);
 first_active_cu = ffs(cu_sibling_map_mask);

 /* CU could be inactive. In case of shared cache find the first active
 * CU. and incase of non-shared cache check if the CU is inactive. If
 * inactive active skip it
 */

 if (first_active_cu) {
  pcache = kfd_alloc_struct(pcache);
  if (!pcache)
   return -ENOMEM;

  memset(pcache, 0, sizeof(struct kfd_cache_properties));
  pcache->processor_id_low = cu_processor_id + (first_active_cu - 1);
  pcache->cache_level = pcache_info[cache_type].cache_level;
  pcache->cache_size = pcache_info[cache_type].cache_size;
  pcache->cacheline_size = pcache_info[cache_type].cache_line_size;

  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_DATA_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_DATA;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_INST_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_INSTRUCTION;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_CPU_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_CPU;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_SIMD_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_HSACU;

  /* Sibling map is w.r.t processor_id_low, so shift out
 * inactive CU
 */

  cu_sibling_map_mask =
   cu_sibling_map_mask >> (first_active_cu - 1);

  pcache->sibling_map[0] = (uint8_t)(cu_sibling_map_mask & 0xFF);
  pcache->sibling_map[1] =
    (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF);
  pcache->sibling_map[2] =
    (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF);
  pcache->sibling_map[3] =
    (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF);

  pcache->sibling_map_size = 4;
  *props_ext = pcache;

  return 0;
 }
 return 1;
}

/* Helper function. See kfd_fill_gpu_cache_info for parameter description */
static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext,
    struct kfd_gpu_cache_info *pcache_info,
    struct amdgpu_cu_info *cu_info,
    struct amdgpu_gfx_config *gfx_info,
    int cache_type, unsigned int cu_processor_id,
    struct kfd_node *knode)
{
 unsigned int cu_sibling_map_mask = 0;
 int first_active_cu;
 int i, j, k, xcc, start, end;
 int num_xcc = NUM_XCC(knode->xcc_mask);
 struct kfd_cache_properties *pcache = NULL;
 enum amdgpu_memory_partition mode;
 struct amdgpu_device *adev = knode->adev;
 bool found = false;

 start = ffs(knode->xcc_mask) - 1;
 end = start + num_xcc;

 /* To find the bitmap in the first active cu in the first
 * xcc, it is based on the assumption that evrey xcc must
 * have at least one active cu.
 */

 for (i = 0; i < gfx_info->max_shader_engines && !found; i++) {
  for (j = 0; j < gfx_info->max_sh_per_se && !found; j++) {
   if (cu_info->bitmap[start][i % 4][j % 4]) {
    cu_sibling_map_mask =
     cu_info->bitmap[start][i % 4][j % 4];
    found = true;
   }
  }
 }

 cu_sibling_map_mask &=
  ((1 << pcache_info[cache_type].num_cu_shared) - 1);
 first_active_cu = ffs(cu_sibling_map_mask);

 /* CU could be inactive. In case of shared cache find the first active
 * CU. and incase of non-shared cache check if the CU is inactive. If
 * inactive active skip it
 */

 if (first_active_cu) {
  pcache = kfd_alloc_struct(pcache);
  if (!pcache)
   return -ENOMEM;

  memset(pcache, 0, sizeof(struct kfd_cache_properties));
  pcache->processor_id_low = cu_processor_id
     + (first_active_cu - 1);
  pcache->cache_level = pcache_info[cache_type].cache_level;
  pcache->cacheline_size = pcache_info[cache_type].cache_line_size;

  if (KFD_GC_VERSION(knode) == IP_VERSION(9, 4, 3) ||
      KFD_GC_VERSION(knode) == IP_VERSION(9, 4, 4) ||
      KFD_GC_VERSION(knode) == IP_VERSION(9, 5, 0))
   mode = adev->gmc.gmc_funcs->query_mem_partition_mode(adev);
  else
   mode = UNKNOWN_MEMORY_PARTITION_MODE;

  pcache->cache_size = pcache_info[cache_type].cache_size;
  /* Partition mode only affects L3 cache size */
  if (mode && pcache->cache_level == 3)
   pcache->cache_size /= mode;

  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_DATA_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_DATA;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_INST_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_INSTRUCTION;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_CPU_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_CPU;
  if (pcache_info[cache_type].flags & CRAT_CACHE_FLAGS_SIMD_CACHE)
   pcache->cache_type |= HSA_CACHE_TYPE_HSACU;

  /* Sibling map is w.r.t processor_id_low, so shift out
 * inactive CU
 */

  cu_sibling_map_mask = cu_sibling_map_mask >> (first_active_cu - 1);
  k = 0;

  for (xcc = start; xcc < end; xcc++) {
   for (i = 0; i < gfx_info->max_shader_engines; i++) {
    for (j = 0; j < gfx_info->max_sh_per_se; j++) {
     pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF);
     pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF);
     pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF);
     pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF);
     k += 4;

     cu_sibling_map_mask = cu_info->bitmap[xcc][i % 4][j + i / 4];
     cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1);
    }
   }
  }
  pcache->sibling_map_size = k;
  *props_ext = pcache;
  return 0;
 }
 return 1;
}

#define KFD_MAX_CACHE_TYPES 6

/* kfd_fill_cache_non_crat_info - Fill GPU cache info using kfd_gpu_cache_info
 * tables
 */

static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct kfd_node *kdev)
{
 struct kfd_gpu_cache_info *pcache_info = NULL;
 int i, j, k, xcc, start, end;
 int ct = 0;
 unsigned int cu_processor_id;
 int ret;
 unsigned int num_cu_shared;
 struct amdgpu_cu_info *cu_info = &kdev->adev->gfx.cu_info;
 struct amdgpu_gfx_config *gfx_info = &kdev->adev->gfx.config;
 int gpu_processor_id;
 struct kfd_cache_properties *props_ext = NULL;
 int num_of_entries = 0;
 int num_of_cache_types = 0;
 struct kfd_gpu_cache_info cache_info[KFD_MAX_CACHE_TYPES];


 gpu_processor_id = dev->node_props.simd_id_base;

 memset(cache_info, 0, sizeof(cache_info));
 pcache_info = cache_info;
 num_of_cache_types = kfd_get_gpu_cache_info(kdev, &pcache_info);
 if (!num_of_cache_types) {
  pr_warn("no cache info found\n");
  return;
 }

 /* For each type of cache listed in the kfd_gpu_cache_info table,
 * go through all available Compute Units.
 * The [i,j,k] loop will
 * if kfd_gpu_cache_info.num_cu_shared = 1
 * will parse through all available CU
 * If (kfd_gpu_cache_info.num_cu_shared != 1)
 * then it will consider only one CU from
 * the shared unit
 */

 start = ffs(kdev->xcc_mask) - 1;
 end = start + NUM_XCC(kdev->xcc_mask);

 for (ct = 0; ct < num_of_cache_types; ct++) {
  cu_processor_id = gpu_processor_id;
  if (pcache_info[ct].cache_level == 1) {
   for (xcc = start; xcc < end; xcc++) {
    for (i = 0; i < gfx_info->max_shader_engines; i++) {
     for (j = 0; j < gfx_info->max_sh_per_se; j++) {
      for (k = 0; k < gfx_info->max_cu_per_sh; k += pcache_info[ct].num_cu_shared) {

       ret = fill_in_l1_pcache(&props_ext, pcache_info,
          cu_info->bitmap[xcc][i % 4][j + i / 4], ct,
          cu_processor_id, k);

       if (ret < 0)
        break;

       if (!ret) {
        num_of_entries++;
        list_add_tail(&props_ext->list, &dev->cache_props);
       }

       /* Move to next CU block */
       num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <=
        gfx_info->max_cu_per_sh) ?
        pcache_info[ct].num_cu_shared :
        (gfx_info->max_cu_per_sh - k);
       cu_processor_id += num_cu_shared;
      }
     }
    }
   }
  } else {
   ret = fill_in_l2_l3_pcache(&props_ext, pcache_info,
         cu_info, gfx_info, ct, cu_processor_id, kdev);

   if (ret < 0)
    break;

   if (!ret) {
    num_of_entries++;
    list_add_tail(&props_ext->list, &dev->cache_props);
   }
  }
 }
 dev->node_props.caches_count += num_of_entries;
 pr_debug("Added [%d] GPU cache entries\n", num_of_entries);
}

static int kfd_topology_add_device_locked(struct kfd_node *gpu,
       struct kfd_topology_device **dev)
{
 int proximity_domain = ++topology_crat_proximity_domain;
 struct list_head temp_topology_device_list;
 void *crat_image = NULL;
 size_t image_size = 0;
 int res;

 res = kfd_create_crat_image_virtual(&crat_image, &image_size,
         COMPUTE_UNIT_GPU, gpu,
         proximity_domain);
 if (res) {
  dev_err(gpu->adev->dev, "Error creating VCRAT\n");
  topology_crat_proximity_domain--;
  goto err;
 }

 INIT_LIST_HEAD(&temp_topology_device_list);

 res = kfd_parse_crat_table(crat_image,
       &temp_topology_device_list,
       proximity_domain);
 if (res) {
  dev_err(gpu->adev->dev, "Error parsing VCRAT\n");
  topology_crat_proximity_domain--;
  goto err;
 }

 kfd_topology_update_device_list(&temp_topology_device_list,
     &topology_device_list);

 *dev = kfd_assign_gpu(gpu);
 if (WARN_ON(!*dev)) {
  res = -ENODEV;
  goto err;
 }

 /* Fill the cache affinity information here for the GPUs
 * using VCRAT
 */

 kfd_fill_cache_non_crat_info(*dev, gpu);

 /* Update the SYSFS tree, since we added another topology
 * device
 */

 res = kfd_topology_update_sysfs();
 if (!res)
  sys_props.generation_count++;
 else
  dev_err(gpu->adev->dev, "Failed to update GPU to sysfs topology. res=%d\n",
   res);

err:
 kfd_destroy_crat_image(crat_image);
 return res;
}

static void kfd_topology_set_dbg_firmware_support(struct kfd_topology_device *dev)
{
 bool firmware_supported = true;

 if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(11, 0, 0) &&
   KFD_GC_VERSION(dev->gpu) < IP_VERSION(12, 0, 0)) {
  uint32_t mes_api_rev = (dev->gpu->adev->mes.sched_version &
      AMDGPU_MES_API_VERSION_MASK) >>
      AMDGPU_MES_API_VERSION_SHIFT;
  uint32_t mes_rev = dev->gpu->adev->mes.sched_version &
      AMDGPU_MES_VERSION_MASK;

  firmware_supported = (mes_api_rev >= 14) && (mes_rev >= 64);
  goto out;
 }

 /*
 * Note: Any unlisted devices here are assumed to support exception handling.
 * Add additional checks here as needed.
 */

 switch (KFD_GC_VERSION(dev->gpu)) {
 case IP_VERSION(9, 0, 1):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 459 + 32768;
  break;
 case IP_VERSION(9, 1, 0):
 case IP_VERSION(9, 2, 1):
 case IP_VERSION(9, 2, 2):
 case IP_VERSION(9, 3, 0):
 case IP_VERSION(9, 4, 0):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 459;
  break;
 case IP_VERSION(9, 4, 1):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 60;
  break;
 case IP_VERSION(9, 4, 2):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 51;
  break;
 case IP_VERSION(10, 1, 10):
 case IP_VERSION(10, 1, 2):
 case IP_VERSION(10, 1, 1):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 144;
  break;
 case IP_VERSION(10, 3, 0):
 case IP_VERSION(10, 3, 2):
 case IP_VERSION(10, 3, 1):
 case IP_VERSION(10, 3, 4):
 case IP_VERSION(10, 3, 5):
  firmware_supported = dev->gpu->kfd->mec_fw_version >= 89;
  break;
 case IP_VERSION(10, 1, 3):
 case IP_VERSION(10, 3, 3):
  firmware_supported = false;
  break;
 default:
  break;
 }

out:
 if (firmware_supported)
  dev->node_props.capability |= HSA_CAP_TRAP_DEBUG_FIRMWARE_SUPPORTED;
}

static void kfd_topology_set_capabilities(struct kfd_topology_device *dev)
{
 dev->node_props.capability |= ((HSA_CAP_DOORBELL_TYPE_2_0 <<
    HSA_CAP_DOORBELL_TYPE_TOTALBITS_SHIFT) &
    HSA_CAP_DOORBELL_TYPE_TOTALBITS_MASK);

 dev->node_props.capability |= HSA_CAP_TRAP_DEBUG_SUPPORT |
   HSA_CAP_TRAP_DEBUG_WAVE_LAUNCH_TRAP_OVERRIDE_SUPPORTED |
   HSA_CAP_TRAP_DEBUG_WAVE_LAUNCH_MODE_SUPPORTED;

 if (kfd_dbg_has_ttmps_always_setup(dev->gpu))
  dev->node_props.debug_prop |= HSA_DBG_DISPATCH_INFO_ALWAYS_VALID;

 if (KFD_GC_VERSION(dev->gpu) < IP_VERSION(10, 0, 0)) {
  if (KFD_GC_VERSION(dev->gpu) == IP_VERSION(9, 4, 3) ||
      KFD_GC_VERSION(dev->gpu) == IP_VERSION(9, 4, 4))
   dev->node_props.debug_prop |=
    HSA_DBG_WATCH_ADDR_MASK_LO_BIT_GFX9_4_3 |
    HSA_DBG_WATCH_ADDR_MASK_HI_BIT_GFX9_4_3;
  else
   dev->node_props.debug_prop |=
    HSA_DBG_WATCH_ADDR_MASK_LO_BIT_GFX9 |
    HSA_DBG_WATCH_ADDR_MASK_HI_BIT;

  if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(9, 4, 2))
   dev->node_props.capability |=
    HSA_CAP_TRAP_DEBUG_PRECISE_MEMORY_OPERATIONS_SUPPORTED;

  if (!amdgpu_sriov_vf(dev->gpu->adev))
   dev->node_props.capability |= HSA_CAP_PER_QUEUE_RESET_SUPPORTED;

 } else {
  dev->node_props.debug_prop |= HSA_DBG_WATCH_ADDR_MASK_LO_BIT_GFX10 |
     HSA_DBG_WATCH_ADDR_MASK_HI_BIT;

  if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(12, 0, 0))
   dev->node_props.capability |=
    HSA_CAP_TRAP_DEBUG_PRECISE_ALU_OPERATIONS_SUPPORTED;
 }

 kfd_topology_set_dbg_firmware_support(dev);
}

int kfd_topology_add_device(struct kfd_node *gpu)
{
 uint32_t gpu_id;
 struct kfd_topology_device *dev;
 int res = 0;
 int i;
 const char *asic_name = amdgpu_asic_name[gpu->adev->asic_type];
 struct amdgpu_gfx_config *gfx_info = &gpu->adev->gfx.config;
 struct amdgpu_cu_info *cu_info = &gpu->adev->gfx.cu_info;

 if (gpu->xcp && !gpu->xcp->ddev) {
  dev_warn(gpu->adev->dev,
    "Won't add GPU to topology since it has no drm node assigned.");
  return 0;
 } else {
  dev_dbg(gpu->adev->dev, "Adding new GPU to topology\n");
 }

 /* Check to see if this gpu device exists in the topology_device_list.
 * If so, assign the gpu to that device,
 * else create a Virtual CRAT for this gpu device and then parse that
 * CRAT to create a new topology device. Once created assign the gpu to
 * that topology device
 */

 down_write(&topology_lock);
 dev = kfd_assign_gpu(gpu);
 if (!dev)
  res = kfd_topology_add_device_locked(gpu, &dev);
 up_write(&topology_lock);
 if (res)
  return res;

 gpu_id = kfd_generate_gpu_id(gpu);
 dev->gpu_id = gpu_id;
 gpu->id = gpu_id;

 kfd_dev_create_p2p_links();

 /* TODO: Move the following lines to function
 * kfd_add_non_crat_information
 */


 /* Fill-in additional information that is not available in CRAT but
 * needed for the topology
 */

 for (i = 0; i < KFD_TOPOLOGY_PUBLIC_NAME_SIZE-1; i++) {
  dev->node_props.name[i] = __tolower(asic_name[i]);
  if (asic_name[i] == '\0')
   break;
 }
 dev->node_props.name[i] = '\0';

 dev->node_props.simd_arrays_per_engine =
  gfx_info->max_sh_per_se;

 dev->node_props.gfx_target_version =
    gpu->kfd->device_info.gfx_target_version;
 dev->node_props.vendor_id = gpu->adev->pdev->vendor;
 dev->node_props.device_id = gpu->adev->pdev->device;
 dev->node_props.capability |=
  ((dev->gpu->adev->rev_id << HSA_CAP_ASIC_REVISION_SHIFT) &
   HSA_CAP_ASIC_REVISION_MASK);

 dev->node_props.location_id = pci_dev_id(gpu->adev->pdev);
 if (gpu->kfd->num_nodes > 1)
  dev->node_props.location_id |= dev->gpu->node_id;

 dev->node_props.domain = pci_domain_nr(gpu->adev->pdev->bus);
 dev->node_props.max_engine_clk_fcompute =
  amdgpu_amdkfd_get_max_engine_clock_in_mhz(dev->gpu->adev);
 dev->node_props.max_engine_clk_ccompute =
  cpufreq_quick_get_max(0) / 1000;

 if (gpu->xcp)
  dev->node_props.drm_render_minor = gpu->xcp->ddev->render->index;
 else
  dev->node_props.drm_render_minor =
    gpu->kfd->shared_resources.drm_render_minor;

 dev->node_props.hive_id = gpu->kfd->hive_id;
 dev->node_props.num_sdma_engines = kfd_get_num_sdma_engines(gpu);
 dev->node_props.num_sdma_xgmi_engines =
     kfd_get_num_xgmi_sdma_engines(gpu);
 dev->node_props.num_sdma_queues_per_engine =
    gpu->kfd->device_info.num_sdma_queues_per_engine -
    gpu->kfd->device_info.num_reserved_sdma_queues_per_engine;
 dev->node_props.num_gws = (dev->gpu->gws &&
  dev->gpu->dqm->sched_policy != KFD_SCHED_POLICY_NO_HWS) ?
  dev->gpu->adev->gds.gws_size : 0;
 dev->node_props.num_cp_queues = get_cp_queues_num(dev->gpu->dqm);

 kfd_fill_mem_clk_max_info(dev);
 kfd_fill_iolink_non_crat_info(dev);

 switch (dev->gpu->adev->asic_type) {
 case CHIP_KAVERI:
 case CHIP_HAWAII:
 case CHIP_TONGA:
  dev->node_props.capability |= ((HSA_CAP_DOORBELL_TYPE_PRE_1_0 <<
   HSA_CAP_DOORBELL_TYPE_TOTALBITS_SHIFT) &
   HSA_CAP_DOORBELL_TYPE_TOTALBITS_MASK);
  break;
 case CHIP_CARRIZO:
 case CHIP_FIJI:
 case CHIP_POLARIS10:
 case CHIP_POLARIS11:
 case CHIP_POLARIS12:
 case CHIP_VEGAM:
  pr_debug("Adding doorbell packet type capability\n");
  dev->node_props.capability |= ((HSA_CAP_DOORBELL_TYPE_1_0 <<
   HSA_CAP_DOORBELL_TYPE_TOTALBITS_SHIFT) &
   HSA_CAP_DOORBELL_TYPE_TOTALBITS_MASK);
  break;
 default:
  if (KFD_GC_VERSION(dev->gpu) < IP_VERSION(9, 0, 1))
   WARN(1, "Unexpected ASIC family %u",
        dev->gpu->adev->asic_type);
  else
   kfd_topology_set_capabilities(dev);
 }

 /*
 * Overwrite ATS capability according to needs_iommu_device to fix
 * potential missing corresponding bit in CRAT of BIOS.
 */

 dev->node_props.capability &= ~HSA_CAP_ATS_PRESENT;

 /* Fix errors in CZ CRAT.
 * simd_count: Carrizo CRAT reports wrong simd_count, probably
 * because it doesn't consider masked out CUs
 * max_waves_per_simd: Carrizo reports wrong max_waves_per_simd
 */

 if (dev->gpu->adev->asic_type == CHIP_CARRIZO) {
  dev->node_props.simd_count =
   cu_info->simd_per_cu * cu_info->number;
  dev->node_props.max_waves_per_simd = 10;
 }

 /* kfd only concerns sram ecc on GFX and HBM ecc on UMC */
 dev->node_props.capability |=
  ((dev->gpu->adev->ras_enabled & BIT(AMDGPU_RAS_BLOCK__GFX)) != 0) ?
  HSA_CAP_SRAM_EDCSUPPORTED : 0;
 dev->node_props.capability |=
  ((dev->gpu->adev->ras_enabled & BIT(AMDGPU_RAS_BLOCK__UMC)) != 0) ?
  HSA_CAP_MEM_EDCSUPPORTED : 0;

 if (KFD_GC_VERSION(dev->gpu) != IP_VERSION(9, 0, 1))
  dev->node_props.capability |= (dev->gpu->adev->ras_enabled != 0) ?
   HSA_CAP_RASEVENTNOTIFY : 0;

 if (KFD_IS_SVM_API_SUPPORTED(dev->gpu->adev))
  dev->node_props.capability |= HSA_CAP_SVMAPI_SUPPORTED;

 if (dev->gpu->adev->gmc.is_app_apu ||
  dev->gpu->adev->gmc.xgmi.connected_to_cpu)
  dev->node_props.capability |= HSA_CAP_FLAGS_COHERENTHOSTACCESS;

 kfd_queue_ctx_save_restore_size(dev);

 kfd_debug_print_topology();

 kfd_notify_gpu_change(gpu_id, 1);

 return 0;
}

/**
 * kfd_topology_update_io_links() - Update IO links after device removal.
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=93 G=95

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.