[PATCH 09/83] hsa/radeon: Add code base of hsa driver for AMD's GPUs

Jerome Glisse j.glisse at gmail.com
Fri Jul 11 10:04:12 PDT 2014


On Fri, Jul 11, 2014 at 12:50:09AM +0300, Oded Gabbay wrote:
> This patch adds the code base of the hsa driver for
> AMD's GPUs.
> 
> This driver is called kfd.
> 
> This initial version supports the first HSA chip, Kaveri.
> 
> This driver is located in a new directory structure under drivers/gpu.
> 
> Signed-off-by: Oded Gabbay <oded.gabbay at amd.com>

There is too coding style issues. While we have been lax on the enforcing the
scripts/checkpatch.pl rules i think there is a limit to that. I am not strict
on the 80chars per line but others things needs fixing so we stay inline.

Also i am a bit worried about the license, given top comment in each of the
files i am not sure this is GPL2 compatible. I would need to ask lawyer to
review that.

Others comment inline.


> ---
>  drivers/Kconfig                        |    2 +
>  drivers/gpu/Makefile                   |    1 +
>  drivers/gpu/hsa/Kconfig                |   20 +
>  drivers/gpu/hsa/Makefile               |    1 +
>  drivers/gpu/hsa/radeon/Makefile        |    8 +
>  drivers/gpu/hsa/radeon/kfd_chardev.c   |  133 ++++
>  drivers/gpu/hsa/radeon/kfd_crat.h      |  292 ++++++++
>  drivers/gpu/hsa/radeon/kfd_device.c    |  162 +++++
>  drivers/gpu/hsa/radeon/kfd_module.c    |  117 ++++
>  drivers/gpu/hsa/radeon/kfd_pasid.c     |   92 +++
>  drivers/gpu/hsa/radeon/kfd_priv.h      |  232 ++++++
>  drivers/gpu/hsa/radeon/kfd_process.c   |  400 +++++++++++
>  drivers/gpu/hsa/radeon/kfd_scheduler.h |   62 ++
>  drivers/gpu/hsa/radeon/kfd_topology.c  | 1201 ++++++++++++++++++++++++++++++++
>  drivers/gpu/hsa/radeon/kfd_topology.h  |  168 +++++
>  15 files changed, 2891 insertions(+)
>  create mode 100644 drivers/gpu/hsa/Kconfig
>  create mode 100644 drivers/gpu/hsa/Makefile
>  create mode 100644 drivers/gpu/hsa/radeon/Makefile
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_chardev.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_crat.h
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_device.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_module.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_pasid.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_priv.h
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_process.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_scheduler.h
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_topology.c
>  create mode 100644 drivers/gpu/hsa/radeon/kfd_topology.h
> 
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 9b2dcc2..c1ac8f8 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -178,4 +178,6 @@ source "drivers/mcb/Kconfig"
>  
>  source "drivers/thunderbolt/Kconfig"
>  
> +source "drivers/gpu/hsa/Kconfig"
> +
>  endmenu
> diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile
> index 70da9eb..749a7ea 100644
> --- a/drivers/gpu/Makefile
> +++ b/drivers/gpu/Makefile
> @@ -1,3 +1,4 @@
>  obj-y			+= drm/ vga/
>  obj-$(CONFIG_TEGRA_HOST1X)	+= host1x/
>  obj-$(CONFIG_IMX_IPUV3_CORE)	+= ipu-v3/
> +obj-$(CONFIG_HSA)	+= hsa/
> \ No newline at end of file
> diff --git a/drivers/gpu/hsa/Kconfig b/drivers/gpu/hsa/Kconfig
> new file mode 100644
> index 0000000..ee7bb28
> --- /dev/null
> +++ b/drivers/gpu/hsa/Kconfig
> @@ -0,0 +1,20 @@
> +#
> +# Heterogenous system architecture configuration
> +#
> +
> +menuconfig HSA
> +	bool "Heterogenous System Architecture"
> +	default y
> +	help
> +	  Say Y here if you want Heterogenous System Architecture support.

Maybe a bit more chatty here, there is already enough kernel option that
are cryptic even to kernel developer. Not everyone is well aware of all
the fence 3 letter accronym GPU uses :)

> +
> +if HSA
> +
> +config HSA_RADEON
> +	tristate "HSA kernel driver for AMD Radeon devices"
> +	depends on HSA && AMD_IOMMU_V2 && X86_64
> +	default m
> +	help
> +	  Enable this if you want to support HSA on AMD Radeon devices.
> +
> +endif # HSA
> diff --git a/drivers/gpu/hsa/Makefile b/drivers/gpu/hsa/Makefile
> new file mode 100644
> index 0000000..0951584
> --- /dev/null
> +++ b/drivers/gpu/hsa/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HSA_RADEON)	+= radeon/
> diff --git a/drivers/gpu/hsa/radeon/Makefile b/drivers/gpu/hsa/radeon/Makefile
> new file mode 100644
> index 0000000..ba16a09
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for Heterogenous System Architecture support for AMD Radeon devices
> +#
> +
> +radeon_kfd-y	:= kfd_module.o kfd_device.o kfd_chardev.o \
> +		kfd_pasid.o kfd_topology.o kfd_process.o
> +
> +obj-$(CONFIG_HSA_RADEON)	+= radeon_kfd.o
> diff --git a/drivers/gpu/hsa/radeon/kfd_chardev.c b/drivers/gpu/hsa/radeon/kfd_chardev.c
> new file mode 100644
> index 0000000..7a56a8f
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_chardev.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright 2014 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/device.h>
> +#include <linux/export.h>
> +#include <linux/err.h>
> +#include <linux/fs.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include "kfd_priv.h"
> +#include "kfd_scheduler.h"
> +
> +static long kfd_ioctl(struct file *, unsigned int, unsigned long);

Nitpick, avoid unsigned int just use unsigned.

> +static int kfd_open(struct inode *, struct file *);
> +
> +static const char kfd_dev_name[] = "kfd";
> +
> +static const struct file_operations kfd_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = kfd_ioctl,
> +	.open = kfd_open,
> +};
> +
> +static int kfd_char_dev_major = -1;
> +static struct class *kfd_class;
> +struct device *kfd_device;
> +
> +int
> +radeon_kfd_chardev_init(void)
> +{
> +	int err = 0;
> +
> +	kfd_char_dev_major = register_chrdev(0, kfd_dev_name, &kfd_fops);
> +	err = kfd_char_dev_major;
> +	if (err < 0)
> +		goto err_register_chrdev;
> +
> +	kfd_class = class_create(THIS_MODULE, kfd_dev_name);
> +	err = PTR_ERR(kfd_class);
> +	if (IS_ERR(kfd_class))
> +		goto err_class_create;
> +
> +	kfd_device = device_create(kfd_class, NULL, MKDEV(kfd_char_dev_major, 0), NULL, kfd_dev_name);
> +	err = PTR_ERR(kfd_device);
> +	if (IS_ERR(kfd_device))
> +		goto err_device_create;
> +
> +	return 0;
> +
> +err_device_create:
> +	class_destroy(kfd_class);
> +err_class_create:
> +	unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
> +err_register_chrdev:
> +	return err;
> +}
> +
> +void
> +radeon_kfd_chardev_exit(void)
> +{
> +	device_destroy(kfd_class, MKDEV(kfd_char_dev_major, 0));
> +	class_destroy(kfd_class);
> +	unregister_chrdev(kfd_char_dev_major, kfd_dev_name);
> +}
> +
> +struct device*
> +radeon_kfd_chardev(void)
> +{
> +	return kfd_device;
> +}
> +
> +
> +static int
> +kfd_open(struct inode *inode, struct file *filep)
> +{
> +	struct kfd_process *process;
> +
> +	if (iminor(inode) != 0)
> +		return -ENODEV;
> +
> +	process = radeon_kfd_create_process(current);
> +	if (IS_ERR(process))
> +		return PTR_ERR(process);
> +
> +	pr_debug("\nkfd: process %d opened dev/kfd", process->pasid);
> +
> +	return 0;
> +}
> +
> +
> +static long
> +kfd_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
> +{
> +	long err = -EINVAL;
> +
> +	dev_info(kfd_device,
> +		 "ioctl cmd 0x%x (#%d), arg 0x%lx\n",
> +		 cmd, _IOC_NR(cmd), arg);
> +
> +	switch (cmd) {
> +	default:
> +		dev_err(kfd_device,
> +			"unknown ioctl cmd 0x%x, arg 0x%lx)\n",
> +			cmd, arg);
> +		err = -EINVAL;
> +		break;
> +	}
> +
> +	if (err < 0)
> +		dev_err(kfd_device, "ioctl error %ld\n", err);
> +
> +	return err;
> +}
> diff --git a/drivers/gpu/hsa/radeon/kfd_crat.h b/drivers/gpu/hsa/radeon/kfd_crat.h
> new file mode 100644
> index 0000000..587455d
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_crat.h
> @@ -0,0 +1,292 @@
> +/*
> + * Copyright 2014 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.
> + */
> +
> +#ifndef KFD_CRAT_H_INCLUDED
> +#define KFD_CRAT_H_INCLUDED
> +
> +#include <linux/types.h>
> +
> +#pragma pack(1)
> +
> +/*
> + * 4CC signature values for the CRAT and CDIT ACPI tables
> + */
> +
> +#define CRAT_SIGNATURE	"CRAT"
> +#define CDIT_SIGNATURE	"CDIT"
> +
> +/*
> + * Component Resource Association Table (CRAT)
> + */
> +
> +#define CRAT_OEMID_LENGTH	6
> +#define CRAT_OEMTABLEID_LENGTH	8
> +#define CRAT_RESERVED_LENGTH	6
> +
> +struct crat_header {
> +	uint32_t	signature;
> +	uint32_t	length;
> +	uint8_t		revision;
> +	uint8_t		checksum;
> +	uint8_t		oem_id[CRAT_OEMID_LENGTH];
> +	uint8_t		oem_table_id[CRAT_OEMTABLEID_LENGTH];
> +	uint32_t	oem_revision;
> +	uint32_t	creator_id;
> +	uint32_t	creator_revision;
> +	uint32_t	total_entries;
> +	uint16_t	num_domains;
> +	uint8_t		reserved[CRAT_RESERVED_LENGTH];
> +};
> +
> +/*
> + * The header structure is immediately followed by total_entries of the
> + * data definitions
> + */
> +
> +/*
> + * The currently defined subtype entries in the CRAT
> + */
> +#define CRAT_SUBTYPE_COMPUTEUNIT_AFFINITY	0
> +#define CRAT_SUBTYPE_MEMORY_AFFINITY		1
> +#define CRAT_SUBTYPE_CACHE_AFFINITY		2
> +#define CRAT_SUBTYPE_TLB_AFFINITY		3
> +#define CRAT_SUBTYPE_CCOMPUTE_AFFINITY		4
> +#define CRAT_SUBTYPE_IOLINK_AFFINITY		5
> +#define CRAT_SUBTYPE_MAX			6
> +
> +#define CRAT_SIBLINGMAP_SIZE	32
> +
> +/*
> + * ComputeUnit Affinity structure and definitions
> + */
> +#define CRAT_CU_FLAGS_ENABLED		0x00000001
> +#define CRAT_CU_FLAGS_HOT_PLUGGABLE	0x00000002
> +#define CRAT_CU_FLAGS_CPU_PRESENT	0x00000004
> +#define CRAT_CU_FLAGS_GPU_PRESENT	0x00000008
> +#define CRAT_CU_FLAGS_IOMMU_PRESENT	0x00000010
> +#define CRAT_CU_FLAGS_RESERVED		0xffffffe0
> +
> +#define CRAT_COMPUTEUNIT_RESERVED_LENGTH 4
> +
> +struct crat_subtype_computeunit {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	proximity_domain;
> +	uint32_t	processor_id_low;
> +	uint16_t	num_cpu_cores;
> +	uint16_t	num_simd_cores;
> +	uint16_t	max_waves_simd;
> +	uint16_t	io_count;
> +	uint16_t	hsa_capability;
> +	uint16_t	lds_size_in_kb;
> +	uint8_t		wave_front_size;
> +	uint8_t		num_banks;
> +	uint16_t	micro_engine_id;
> +	uint8_t		num_arrays;
> +	uint8_t		num_cu_per_array;
> +	uint8_t		num_simd_per_cu;
> +	uint8_t		max_slots_scatch_cu;
> +	uint8_t		reserved2[CRAT_COMPUTEUNIT_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA Memory Affinity structure and definitions
> + */
> +#define CRAT_MEM_FLAGS_ENABLED		0x00000001
> +#define CRAT_MEM_FLAGS_HOT_PLUGGABLE	0x00000002
> +#define CRAT_MEM_FLAGS_NON_VOLATILE	0x00000004
> +#define CRAT_MEM_FLAGS_RESERVED		0xfffffff8
> +
> +#define CRAT_MEMORY_RESERVED_LENGTH 8
> +
> +struct crat_subtype_memory {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	promixity_domain;
> +	uint32_t	base_addr_low;
> +	uint32_t	base_addr_high;
> +	uint32_t	length_low;
> +	uint32_t	length_high;
> +	uint32_t	width;
> +	uint8_t		reserved2[CRAT_MEMORY_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA Cache Affinity structure and definitions
> + */
> +#define CRAT_CACHE_FLAGS_ENABLED	0x00000001
> +#define CRAT_CACHE_FLAGS_DATA_CACHE	0x00000002
> +#define CRAT_CACHE_FLAGS_INST_CACHE	0x00000004
> +#define CRAT_CACHE_FLAGS_CPU_CACHE	0x00000008
> +#define CRAT_CACHE_FLAGS_SIMD_CACHE	0x00000010
> +#define CRAT_CACHE_FLAGS_RESERVED	0xffffffe0
> +
> +#define CRAT_CACHE_RESERVED_LENGTH 8
> +
> +struct crat_subtype_cache {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	processor_id_low;
> +	uint8_t		sibling_map[CRAT_SIBLINGMAP_SIZE];
> +	uint32_t	cache_size;
> +	uint8_t		cache_level;
> +	uint8_t		lines_per_tag;
> +	uint16_t	cache_line_size;
> +	uint8_t		associativity;
> +	uint8_t		cache_properties;
> +	uint16_t	cache_latency;
> +	uint8_t		reserved2[CRAT_CACHE_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA TLB Affinity structure and definitions
> + */
> +#define CRAT_TLB_FLAGS_ENABLED	0x00000001
> +#define CRAT_TLB_FLAGS_DATA_TLB	0x00000002
> +#define CRAT_TLB_FLAGS_INST_TLB	0x00000004
> +#define CRAT_TLB_FLAGS_CPU_TLB	0x00000008
> +#define CRAT_TLB_FLAGS_SIMD_TLB	0x00000010
> +#define CRAT_TLB_FLAGS_RESERVED	0xffffffe0
> +
> +#define CRAT_TLB_RESERVED_LENGTH 4
> +
> +struct crat_subtype_tlb {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	processor_id_low;
> +	uint8_t		sibling_map[CRAT_SIBLINGMAP_SIZE];
> +	uint32_t	tlb_level;
> +	uint8_t		data_tlb_associativity_2mb;
> +	uint8_t		data_tlb_size_2mb;
> +	uint8_t		instruction_tlb_associativity_2mb;
> +	uint8_t		instruction_tlb_size_2mb;
> +	uint8_t		data_tlb_associativity_4k;
> +	uint8_t		data_tlb_size_4k;
> +	uint8_t		instruction_tlb_associativity_4k;
> +	uint8_t		instruction_tlb_size_4k;
> +	uint8_t		data_tlb_associativity_1gb;
> +	uint8_t		data_tlb_size_1gb;
> +	uint8_t		instruction_tlb_associativity_1gb;
> +	uint8_t		instruction_tlb_size_1gb;
> +	uint8_t		reserved2[CRAT_TLB_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA CCompute/APU Affinity structure and definitions
> + */
> +#define CRAT_CCOMPUTE_FLAGS_ENABLED	0x00000001
> +#define CRAT_CCOMPUTE_FLAGS_RESERVED	0xfffffffe
> +
> +#define CRAT_CCOMPUTE_RESERVED_LENGTH 16
> +
> +struct crat_subtype_ccompute {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	processor_id_low;
> +	uint8_t		sibling_map[CRAT_SIBLINGMAP_SIZE];
> +	uint32_t	apu_size;
> +	uint8_t		reserved2[CRAT_CCOMPUTE_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA IO Link Affinity structure and definitions
> + */
> +#define CRAT_IOLINK_FLAGS_ENABLED	0x00000001
> +#define CRAT_IOLINK_FLAGS_COHERENCY	0x00000002
> +#define CRAT_IOLINK_FLAGS_RESERVED	0xfffffffc
> +
> +/*
> + * IO interface types
> + */
> +#define CRAT_IOLINK_TYPE_UNDEFINED	0
> +#define CRAT_IOLINK_TYPE_HYPERTRANSPORT	1
> +#define CRAT_IOLINK_TYPE_PCIEXPRESS	2
> +#define CRAT_IOLINK_TYPE_OTHER		3
> +#define CRAT_IOLINK_TYPE_MAX		255
> +
> +#define CRAT_IOLINK_RESERVED_LENGTH 24
> +
> +struct crat_subtype_iolink {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +	uint32_t	proximity_domain_from;
> +	uint32_t	proximity_domain_to;
> +	uint8_t		io_interface_type;
> +	uint8_t		version_major;
> +	uint16_t	version_minor;
> +	uint32_t	minimum_latency;
> +	uint32_t	maximum_latency;
> +	uint32_t	minimum_bandwidth_mbs;
> +	uint32_t	maximum_bandwidth_mbs;
> +	uint32_t	recommended_transfer_size;
> +	uint8_t		reserved2[CRAT_IOLINK_RESERVED_LENGTH];
> +};
> +
> +/*
> + * HSA generic sub-type header
> + */
> +
> +#define CRAT_SUBTYPE_FLAGS_ENABLED 0x00000001
> +
> +struct crat_subtype_generic {
> +	uint8_t		type;
> +	uint8_t		length;
> +	uint16_t	reserved;
> +	uint32_t	flags;
> +};
> +
> +/*
> + * Component Locality Distance Information Table (CDIT)
> + */
> +#define CDIT_OEMID_LENGTH	6
> +#define CDIT_OEMTABLEID_LENGTH	8
> +
> +struct cdit_header {
> +	uint32_t	signature;
> +	uint32_t	length;
> +	uint8_t		revision;
> +	uint8_t		checksum;
> +	uint8_t		oem_id[CDIT_OEMID_LENGTH];
> +	uint8_t		oem_table_id[CDIT_OEMTABLEID_LENGTH];
> +	uint32_t	oem_revision;
> +	uint32_t	creator_id;
> +	uint32_t	creator_revision;
> +	uint32_t	total_entries;
> +	uint16_t	num_domains;
> +	uint8_t		entry[1];
> +};
> +
> +#pragma pack()
> +
> +#endif /* KFD_CRAT_H_INCLUDED */
> diff --git a/drivers/gpu/hsa/radeon/kfd_device.c b/drivers/gpu/hsa/radeon/kfd_device.c
> new file mode 100644
> index 0000000..d122920
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_device.c
> @@ -0,0 +1,162 @@
> +/*
> + * Copyright 2014 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/amd-iommu.h>
> +#include <linux/bsearch.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include "kfd_priv.h"
> +#include "kfd_scheduler.h"
> +
> +static const struct kfd_device_info bonaire_device_info = {
> +	.max_pasid_bits = 16,
> +};
> +
> +struct kfd_deviceid {
> +	unsigned short did;
> +	const struct kfd_device_info *device_info;
> +};
> +
> +/* Please keep this sorted by increasing device id. */
> +static const struct kfd_deviceid supported_devices[] = {
> +	{ 0x1305, &bonaire_device_info },	/* Kaveri */
> +	{ 0x1307, &bonaire_device_info },	/* Kaveri */
> +	{ 0x130F, &bonaire_device_info },	/* Kaveri */
> +	{ 0x665C, &bonaire_device_info },	/* Bonaire */
> +};
> +
> +static const struct kfd_device_info *
> +lookup_device_info(unsigned short did)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < ARRAY_SIZE(supported_devices); i++) {
> +		if (supported_devices[i].did == did) {
> +			BUG_ON(supported_devices[i].device_info == NULL);
> +			return supported_devices[i].device_info;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd, struct pci_dev *pdev)
> +{
> +	struct kfd_dev *kfd;
> +
> +	const struct kfd_device_info *device_info = lookup_device_info(pdev->device);
> +
> +	if (!device_info)
> +		return NULL;
> +
> +	kfd = kzalloc(sizeof(*kfd), GFP_KERNEL);
> +	kfd->kgd = kgd;
> +	kfd->device_info = device_info;
> +	kfd->pdev = pdev;
> +
> +	return kfd;
> +}
> +
> +static bool
> +device_iommu_pasid_init(struct kfd_dev *kfd)
> +{
> +	const u32 required_iommu_flags = AMD_IOMMU_DEVICE_FLAG_ATS_SUP | AMD_IOMMU_DEVICE_FLAG_PRI_SUP
> +					| AMD_IOMMU_DEVICE_FLAG_PASID_SUP;
> +
> +	struct amd_iommu_device_info iommu_info;
> +	pasid_t pasid_limit;
> +	int err;
> +
> +	err = amd_iommu_device_info(kfd->pdev, &iommu_info);
> +	if (err < 0)
> +		return false;
> +
> +	if ((iommu_info.flags & required_iommu_flags) != required_iommu_flags)
> +		return false;
> +
> +	pasid_limit = min_t(pasid_t, (pasid_t)1 << kfd->device_info->max_pasid_bits, iommu_info.max_pasids);
> +	pasid_limit = min_t(pasid_t, pasid_limit, kfd->doorbell_process_limit);
> +
> +	err = amd_iommu_init_device(kfd->pdev, pasid_limit);
> +	if (err < 0)
> +		return false;
> +
> +	if (!radeon_kfd_set_pasid_limit(pasid_limit)) {
> +		amd_iommu_free_device(kfd->pdev);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static void iommu_pasid_shutdown_callback(struct pci_dev *pdev, int pasid)
> +{
> +	struct kfd_dev *dev = radeon_kfd_device_by_pci_dev(pdev);
> +
> +	if (dev)
> +		radeon_kfd_unbind_process_from_device(dev, pasid);
> +}
> +
> +bool kgd2kfd_device_init(struct kfd_dev *kfd,
> +			 const struct kgd2kfd_shared_resources *gpu_resources)
> +{
> +	kfd->shared_resources = *gpu_resources;
> +
> +	kfd->regs = gpu_resources->mmio_registers;
> +
> +	if (!device_iommu_pasid_init(kfd))
> +		return false;
> +
> +	if (kfd_topology_add_device(kfd) != 0) {
> +		amd_iommu_free_device(kfd->pdev);
> +		return false;
> +	}
> +
> +	amd_iommu_set_invalidate_ctx_cb(kfd->pdev, iommu_pasid_shutdown_callback);
> +
> +	if (kfd->device_info->scheduler_class->create(kfd, &kfd->scheduler)) {
> +		amd_iommu_free_device(kfd->pdev);
> +		return false;
> +	}
> +
> +	kfd->device_info->scheduler_class->start(kfd->scheduler);
> +
> +	kfd->init_complete = true;
> +
> +	return true;
> +}
> +
> +void kgd2kfd_device_exit(struct kfd_dev *kfd)
> +{
> +	int err = kfd_topology_remove_device(kfd);
> +
> +	BUG_ON(err != 0);
> +
> +	if (kfd->init_complete) {
> +		kfd->device_info->scheduler_class->stop(kfd->scheduler);
> +		kfd->device_info->scheduler_class->destroy(kfd->scheduler);
> +
> +		amd_iommu_free_device(kfd->pdev);
> +	}
> +
> +	kfree(kfd);
> +}
> diff --git a/drivers/gpu/hsa/radeon/kfd_module.c b/drivers/gpu/hsa/radeon/kfd_module.c
> new file mode 100644
> index 0000000..6978bc0
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_module.c
> @@ -0,0 +1,117 @@
> +/*
> + * Copyright 2014 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/module.h>
> +#include <linux/sched.h>
> +#include <linux/notifier.h>
> +
> +#include "kfd_priv.h"
> +
> +#define DRIVER_AUTHOR		"Andrew Lewycky, Oded Gabbay, Evgeny Pinchuk, others."
> +
> +#define DRIVER_NAME		"kfd"
> +#define DRIVER_DESC		"AMD HSA Kernel Fusion Driver"
> +#define DRIVER_DATE		"20140127"
> +
> +const struct kfd2kgd_calls *kfd2kgd;
> +static const struct kgd2kfd_calls kgd2kfd = {
> +	.exit		= kgd2kfd_exit,
> +	.probe		= kgd2kfd_probe,
> +	.device_init	= kgd2kfd_device_init,
> +	.device_exit	= kgd2kfd_device_exit,
> +};
> +
> +bool kgd2kfd_init(unsigned interface_version,
> +		  const struct kfd2kgd_calls *f2g,
> +		  const struct kgd2kfd_calls **g2f)
> +{
> +	/* Only one interface version is supported, no kfd/kgd version skew allowed. */
> +	if (interface_version != KFD_INTERFACE_VERSION)
> +		return false;
> +
> +	kfd2kgd = f2g;
> +	*g2f = &kgd2kfd;
> +
> +	return true;
> +}
> +EXPORT_SYMBOL(kgd2kfd_init);
> +
> +void kgd2kfd_exit(void)
> +{
> +}
> +
> +extern int kfd_process_exit(struct notifier_block *nb,
> +				unsigned long action, void *data);
> +
> +static struct notifier_block kfd_mmput_nb = {
> +	.notifier_call		= kfd_process_exit,
> +	.priority		= 3,
> +};
> +
> +static int __init kfd_module_init(void)
> +{
> +	int err;
> +
> +	err = radeon_kfd_pasid_init();
> +	if (err < 0)
> +		goto err_pasid;
> +
> +	err = radeon_kfd_chardev_init();
> +	if (err < 0)
> +		goto err_ioctl;
> +
> +	err = mmput_register_notifier(&kfd_mmput_nb);
> +	if (err)
> +		goto err_mmu_notifier;
> +
> +	err = kfd_topology_init();
> +	if (err < 0)
> +		goto err_topology;
> +
> +	pr_info("[hsa] Initialized kfd module");
> +
> +	return 0;
> +err_topology:
> +	mmput_unregister_notifier(&kfd_mmput_nb);
> +err_mmu_notifier:
> +	radeon_kfd_chardev_exit();
> +err_ioctl:
> +	radeon_kfd_pasid_exit();
> +err_pasid:
> +	return err;
> +}
> +
> +static void __exit kfd_module_exit(void)
> +{
> +	kfd_topology_shutdown();
> +	mmput_unregister_notifier(&kfd_mmput_nb);
> +	radeon_kfd_chardev_exit();
> +	radeon_kfd_pasid_exit();
> +	pr_info("[hsa] Removed kfd module");
> +}
> +
> +module_init(kfd_module_init);
> +module_exit(kfd_module_exit);
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE("GPL");

If it is GPL then comment at the top of all files must reflect that
and not use some special worded license.

> diff --git a/drivers/gpu/hsa/radeon/kfd_pasid.c b/drivers/gpu/hsa/radeon/kfd_pasid.c
> new file mode 100644
> index 0000000..d78bd00
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_pasid.c
> @@ -0,0 +1,92 @@
> +/*
> + * Copyright 2014 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/slab.h>
> +#include <linux/types.h>
> +#include "kfd_priv.h"
> +
> +#define INITIAL_PASID_LIMIT (1<<20)
> +
> +static unsigned long *pasid_bitmap;
> +static pasid_t pasid_limit;
> +static DEFINE_MUTEX(pasid_mutex);
> +
> +int radeon_kfd_pasid_init(void)
> +{
> +	pasid_limit = INITIAL_PASID_LIMIT;
> +
> +	pasid_bitmap = kzalloc(DIV_ROUND_UP(INITIAL_PASID_LIMIT, BITS_PER_BYTE), GFP_KERNEL);
> +	if (!pasid_bitmap)
> +		return -ENOMEM;
> +
> +	set_bit(0, pasid_bitmap); /* PASID 0 is reserved. */
> +
> +	return 0;
> +}
> +
> +void radeon_kfd_pasid_exit(void)
> +{
> +	kfree(pasid_bitmap);
> +}
> +
> +bool radeon_kfd_set_pasid_limit(pasid_t new_limit)
> +{
> +	if (new_limit < pasid_limit) {
> +		bool ok;
> +
> +		mutex_lock(&pasid_mutex);
> +
> +		/* ensure that no pasids >= new_limit are in-use */
> +		ok = (find_next_bit(pasid_bitmap, pasid_limit, new_limit) == pasid_limit);
> +		if (ok)
> +			pasid_limit = new_limit;
> +
> +		mutex_unlock(&pasid_mutex);
> +
> +		return ok;
> +	}
> +
> +	return true;
> +}
> +
> +pasid_t radeon_kfd_pasid_alloc(void)
> +{
> +	pasid_t found;
> +
> +	mutex_lock(&pasid_mutex);
> +
> +	found = find_first_zero_bit(pasid_bitmap, pasid_limit);
> +	if (found == pasid_limit)
> +		found = 0;
> +	else
> +		set_bit(found, pasid_bitmap);
> +
> +	mutex_unlock(&pasid_mutex);
> +
> +	return found;
> +}
> +
> +void radeon_kfd_pasid_free(pasid_t pasid)
> +{
> +	BUG_ON(pasid == 0 || pasid >= pasid_limit);
> +	clear_bit(pasid, pasid_bitmap);
> +}
> diff --git a/drivers/gpu/hsa/radeon/kfd_priv.h b/drivers/gpu/hsa/radeon/kfd_priv.h
> new file mode 100644
> index 0000000..1d1dbcf
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_priv.h
> @@ -0,0 +1,232 @@
> +/*
> + * Copyright 2014 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.
> + */
> +
> +#ifndef KFD_PRIV_H_INCLUDED
> +#define KFD_PRIV_H_INCLUDED
> +
> +#include <linux/hashtable.h>
> +#include <linux/mmu_notifier.h>
> +#include <linux/mutex.h>
> +#include <linux/radeon_kfd.h>
> +#include <linux/types.h>
> +
> +struct kfd_scheduler_class;
> +
> +#define MAX_KFD_DEVICES 16	/* Global limit - only MAX_KFD_DEVICES will be supported by KFD. */
> +
> +/*
> + * Per-process limit. Each process can only
> + * create MAX_PROCESS_QUEUES across all devices
> + */
> +#define MAX_PROCESS_QUEUES 1024
> +
> +#define MAX_DOORBELL_INDEX MAX_PROCESS_QUEUES
> +#define KFD_SYSFS_FILE_MODE 0444
> +
> +/* We multiplex different sorts of mmap-able memory onto /dev/kfd.
> +** We figure out what type of memory the caller wanted by comparing the mmap page offset to known ranges. */
> +#define KFD_MMAP_DOORBELL_START	(((1ULL << 32)*1) >> PAGE_SHIFT)
> +#define KFD_MMAP_DOORBELL_END	(((1ULL << 32)*2) >> PAGE_SHIFT)
> +
> +/* GPU ID hash width in bits */
> +#define KFD_GPU_ID_HASH_WIDTH 16
> +
> +/* Macro for allocating structures */
> +#define kfd_alloc_struct(ptr_to_struct)	((typeof(ptr_to_struct)) kzalloc(sizeof(*ptr_to_struct), GFP_KERNEL))
> +
> +/* Large enough to hold the maximum usable pasid + 1.
> +** It must also be able to store the number of doorbells reported by a KFD device. */
> +typedef unsigned int pasid_t;

Same on unsigned int.

> +
> +/* Type that represents a HW doorbell slot. */
> +typedef u32 doorbell_t;
> +
> +struct kfd_device_info {
> +	const struct kfd_scheduler_class *scheduler_class;
> +	unsigned int max_pasid_bits;
> +};
> +
> +struct kfd_dev {
> +	struct kgd_dev *kgd;
> +
> +	const struct kfd_device_info *device_info;
> +	struct pci_dev *pdev;
> +
> +	void __iomem *regs;
> +
> +	bool init_complete;
> +
> +	unsigned int id;		/* topology stub index */
> +
> +	phys_addr_t doorbell_base;	/* Start of actual doorbells used by
> +					 * KFD. It is aligned for mapping
> +					 * into user mode
> +					 */
> +	size_t doorbell_id_offset;	/* Doorbell offset (from KFD doorbell
> +					 * to HW doorbell, GFX reserved some
> +					 * at the start)
> +					 */
> +	size_t doorbell_process_limit;	/* Number of processes we have doorbell space for. */
> +
> +	struct kgd2kfd_shared_resources shared_resources;
> +
> +	struct kfd_scheduler *scheduler;
> +};
> +
> +/* KGD2KFD callbacks */
> +void kgd2kfd_exit(void);
> +struct kfd_dev *kgd2kfd_probe(struct kgd_dev *kgd, struct pci_dev *pdev);
> +bool kgd2kfd_device_init(struct kfd_dev *kfd,
> +			 const struct kgd2kfd_shared_resources *gpu_resources);
> +void kgd2kfd_device_exit(struct kfd_dev *kfd);
> +
> +extern const struct kfd2kgd_calls *kfd2kgd;
> +
> +
> +/* KFD2KGD callback wrappers */
> +void radeon_kfd_lock_srbm_index(struct kfd_dev *kfd);
> +void radeon_kfd_unlock_srbm_index(struct kfd_dev *kfd);
> +
> +enum kfd_mempool {
> +	KFD_MEMPOOL_SYSTEM_CACHEABLE = 1,
> +	KFD_MEMPOOL_SYSTEM_WRITECOMBINE = 2,
> +	KFD_MEMPOOL_FRAMEBUFFER = 3,
> +};
> +
> +struct kfd_mem_obj_s; /* Dummy struct just to make kfd_mem_obj* a unique pointer type. */
> +typedef struct kfd_mem_obj_s *kfd_mem_obj;
> +
> +int radeon_kfd_vidmem_alloc(struct kfd_dev *kfd, size_t size, size_t alignment,
> +				enum kfd_mempool pool, kfd_mem_obj *mem_obj);
> +void radeon_kfd_vidmem_free(struct kfd_dev *kfd, kfd_mem_obj mem_obj);
> +int radeon_kfd_vidmem_gpumap(struct kfd_dev *kfd, kfd_mem_obj mem_obj, uint64_t *vmid0_address);
> +void radeon_kfd_vidmem_ungpumap(struct kfd_dev *kfd, kfd_mem_obj mem_obj);
> +int radeon_kfd_vidmem_kmap(struct kfd_dev *kfd, kfd_mem_obj mem_obj, void **ptr);
> +void radeon_kfd_vidmem_unkmap(struct kfd_dev *kfd, kfd_mem_obj mem_obj);
> +
> +/* Character device interface */
> +int radeon_kfd_chardev_init(void);
> +void radeon_kfd_chardev_exit(void);
> +struct device *radeon_kfd_chardev(void);
> +
> +/* Scheduler */
> +struct kfd_scheduler;
> +struct kfd_scheduler_process;
> +struct kfd_scheduler_queue {
> +	uint64_t dummy;
> +};
> +
> +struct kfd_queue {
> +	struct kfd_dev *dev;
> +
> +	/* scheduler_queue must be last. It is variable sized (dev->device_info->scheduler_class->queue_size) */
> +	struct kfd_scheduler_queue scheduler_queue;
> +};
> +
> +/* Data that is per-process-per device. */
> +struct kfd_process_device {
> +	/* List of all per-device data for a process. Starts from kfd_process.per_device_data. */
> +	struct list_head per_device_list;
> +
> +	/* The device that owns this data. */
> +	struct kfd_dev *dev;
> +
> +	/* The user-mode address of the doorbell mapping for this device. */
> +	doorbell_t __user *doorbell_mapping;
> +
> +	/* The number of queues created by this process for this device. */
> +	uint32_t queue_count;
> +
> +	/* Scheduler process data for this device. */
> +	struct kfd_scheduler_process *scheduler_process;
> +
> +	/* Is this process/pasid bound to this device? (amd_iommu_bind_pasid) */
> +	bool bound;
> +};
> +
> +/* Process data */
> +struct kfd_process {
> +	struct list_head processes_list;
> +
> +	struct mm_struct *mm;
> +
> +	struct mutex mutex;
> +
> +	/* In any process, the thread that started main() is the lead thread and outlives the rest.
> +	 * It is here because amd_iommu_bind_pasid wants a task_struct. */
> +	struct task_struct *lead_thread;
> +
> +	pasid_t pasid;
> +
> +	/* List of kfd_process_device structures, one for each device the process is using. */
> +	struct list_head per_device_data;
> +
> +	/* The process's queues. */
> +	size_t queue_array_size;
> +	struct kfd_queue **queues;	/* Size is queue_array_size, up to MAX_PROCESS_QUEUES. */
> +	unsigned long allocated_queue_bitmap[DIV_ROUND_UP(MAX_PROCESS_QUEUES, BITS_PER_LONG)];
> +};
> +
> +struct kfd_process *radeon_kfd_create_process(const struct task_struct *);
> +struct kfd_process *radeon_kfd_get_process(const struct task_struct *);
> +
> +struct kfd_process_device *radeon_kfd_bind_process_to_device(struct kfd_dev *dev, struct kfd_process *p);
> +void radeon_kfd_unbind_process_from_device(struct kfd_dev *dev, pasid_t pasid);
> +struct kfd_process_device *radeon_kfd_get_process_device_data(struct kfd_dev *dev, struct kfd_process *p);
> +
> +bool radeon_kfd_allocate_queue_id(struct kfd_process *p, unsigned int *queue_id);
> +void radeon_kfd_install_queue(struct kfd_process *p, unsigned int queue_id, struct kfd_queue *queue);
> +void radeon_kfd_remove_queue(struct kfd_process *p, unsigned int queue_id);
> +struct kfd_queue *radeon_kfd_get_queue(struct kfd_process *p, unsigned int queue_id);
> +
> +
> +/* PASIDs */
> +int radeon_kfd_pasid_init(void);
> +void radeon_kfd_pasid_exit(void);
> +bool radeon_kfd_set_pasid_limit(pasid_t new_limit);
> +pasid_t radeon_kfd_pasid_alloc(void);
> +void radeon_kfd_pasid_free(pasid_t pasid);
> +
> +/* Doorbells */
> +void radeon_kfd_doorbell_init(struct kfd_dev *kfd);
> +int radeon_kfd_doorbell_mmap(struct kfd_process *process, struct vm_area_struct *vma);
> +doorbell_t __user *radeon_kfd_get_doorbell(struct file *devkfd, struct kfd_process *process, struct kfd_dev *dev,
> +					   unsigned int doorbell_index);
> +unsigned int radeon_kfd_queue_id_to_doorbell(struct kfd_dev *kfd, struct kfd_process *process, unsigned int queue_id);
> +
> +extern struct device *kfd_device;
> +
> +/* Topology */
> +int kfd_topology_init(void);
> +void kfd_topology_shutdown(void);
> +int kfd_topology_add_device(struct kfd_dev *gpu);
> +int kfd_topology_remove_device(struct kfd_dev *gpu);
> +struct kfd_dev *radeon_kfd_device_by_id(uint32_t gpu_id);
> +struct kfd_dev *radeon_kfd_device_by_pci_dev(const struct pci_dev *pdev);
> +
> +/* MMIO registers */
> +#define WRITE_REG(dev, reg, value) radeon_kfd_write_reg((dev), (reg), (value))
> +#define READ_REG(dev, reg) radeon_kfd_read_reg((dev), (reg))
> +void radeon_kfd_write_reg(struct kfd_dev *dev, uint32_t reg, uint32_t value);
> +uint32_t radeon_kfd_read_reg(struct kfd_dev *dev, uint32_t reg);
> +
> +#endif
> diff --git a/drivers/gpu/hsa/radeon/kfd_process.c b/drivers/gpu/hsa/radeon/kfd_process.c
> new file mode 100644
> index 0000000..145ee38
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_process.c
> @@ -0,0 +1,400 @@
> +/*
> + * Copyright 2014 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/mutex.h>
> +#include <linux/log2.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/amd-iommu.h>
> +#include <linux/notifier.h>
> +struct mm_struct;
> +
> +#include "kfd_priv.h"
> +#include "kfd_scheduler.h"
> +
> +/* Initial size for the array of queues.
> + * The allocated size is doubled each time it is exceeded up to MAX_PROCESS_QUEUES. */
> +#define INITIAL_QUEUE_ARRAY_SIZE 16
> +
> +/* List of struct kfd_process */
> +static struct list_head kfd_processes_list = LIST_HEAD_INIT(kfd_processes_list);
> +
> +static DEFINE_MUTEX(kfd_processes_mutex);
> +
> +static struct kfd_process *create_process(const struct task_struct *thread);
> +
> +struct kfd_process*
> +radeon_kfd_create_process(const struct task_struct *thread)
> +{
> +	struct kfd_process *process;
> +
> +	if (thread->mm == NULL)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Only the pthreads threading model is supported. */
> +	if (thread->group_leader->mm != thread->mm)
> +		return ERR_PTR(-EINVAL);
> +
> +	/*
> +	 * take kfd processes mutex before starting of process creation
> +	 * so there won't be a case where two threads of the same process
> +	 * create two kfd_process structures
> +	 */
> +	mutex_lock(&kfd_processes_mutex);

Given that this is to protect mm->kfd_process i would rather that you
use some mm lock so that if another non kfd code ever need to check
this variable in a sensible way then it could protect itself with a
mm lock.

But again i believe that mm_struct should not have a new kfd field but
rather some generic iommu pasid field that can then forward through
generic iommu code things to kfd.

> +
> +	/* A prior open of /dev/kfd could have already created the process. */
> +	process = thread->mm->kfd_process;
> +	if (process)
> +		pr_debug("kfd: process already found\n");
> +
> +	if (!process)
> +		process = create_process(thread);
> +
> +	mutex_unlock(&kfd_processes_mutex);
> +
> +	return process;
> +}
> +
> +struct kfd_process*
> +radeon_kfd_get_process(const struct task_struct *thread)
> +{
> +	struct kfd_process *process;
> +
> +	if (thread->mm == NULL)
> +		return ERR_PTR(-EINVAL);
> +
> +	/* Only the pthreads threading model is supported. */
> +	if (thread->group_leader->mm != thread->mm)
> +		return ERR_PTR(-EINVAL);
> +
> +	process = thread->mm->kfd_process;
> +
> +	return process;
> +}
> +
> +/* Assumes that the kfd_process mutex is held.
> + * (Or that it doesn't need to be held because the process is exiting.)
> + *
> + * dev_filter can be set to only destroy queues for one device.
> + * Otherwise all queues for the process are destroyed.
> + */
> +static void
> +destroy_queues(struct kfd_process *p, struct kfd_dev *dev_filter)
> +{
> +	unsigned long queue_id;
> +
> +	for_each_set_bit(queue_id, p->allocated_queue_bitmap, MAX_PROCESS_QUEUES) {
> +
> +		struct kfd_queue *queue = radeon_kfd_get_queue(p, queue_id);
> +		struct kfd_dev *dev;
> +
> +		BUG_ON(queue == NULL);
> +
> +		dev = queue->dev;
> +
> +		if (!dev_filter || dev == dev_filter) {
> +			struct kfd_process_device *pdd = radeon_kfd_get_process_device_data(dev, p);
> +
> +			BUG_ON(pdd == NULL); /* A queue exists so pdd must. */
> +
> +			radeon_kfd_remove_queue(p, queue_id);
> +			dev->device_info->scheduler_class->destroy_queue(dev->scheduler, &queue->scheduler_queue);
> +
> +			kfree(queue);
> +
> +			BUG_ON(pdd->queue_count == 0);
> +			BUG_ON(pdd->scheduler_process == NULL);
> +
> +			if (--pdd->queue_count == 0) {
> +				dev->device_info->scheduler_class->deregister_process(dev->scheduler,
> +							pdd->scheduler_process);
> +				pdd->scheduler_process = NULL;
> +			}
> +		}
> +	}
> +}
> +
> +static void free_process(struct kfd_process *p)
> +{
> +	struct kfd_process_device *pdd, *temp;
> +
> +	BUG_ON(p == NULL);
> +
> +	destroy_queues(p, NULL);
> +
> +	/* doorbell mappings: automatic */
> +
> +	list_for_each_entry_safe(pdd, temp, &p->per_device_data, per_device_list) {
> +		amd_iommu_unbind_pasid(pdd->dev->pdev, p->pasid);
> +		list_del(&pdd->per_device_list);
> +		kfree(pdd);
> +	}
> +
> +	radeon_kfd_pasid_free(p->pasid);
> +
> +	mutex_destroy(&p->mutex);
> +
> +	kfree(p->queues);
> +
> +	list_del(&p->processes_list);
> +
> +	kfree(p);
> +}
> +
> +int kfd_process_exit(struct notifier_block *nb,
> +			unsigned long action, void *data)
> +{
> +	struct mm_struct *mm = data;
> +	struct kfd_process *p;
> +
> +	mutex_lock(&kfd_processes_mutex);
> +
> +	p = mm->kfd_process;
> +	if (p) {
> +		free_process(p);
> +		mm->kfd_process = NULL;
> +	}
> +
> +	mutex_unlock(&kfd_processes_mutex);
> +
> +	return 0;
> +}
> +
> +static struct kfd_process *create_process(const struct task_struct *thread)
> +{
> +	struct kfd_process *process;
> +	int err = -ENOMEM;
> +
> +	process = kzalloc(sizeof(*process), GFP_KERNEL);
> +
> +	if (!process)
> +		goto err_alloc;
> +
> +	process->queues = kmalloc_array(INITIAL_QUEUE_ARRAY_SIZE, sizeof(process->queues[0]), GFP_KERNEL);
> +	if (!process->queues)
> +		goto err_alloc;
> +
> +	process->pasid = radeon_kfd_pasid_alloc();
> +	if (process->pasid == 0)
> +		goto err_alloc;
> +
> +	mutex_init(&process->mutex);
> +
> +	process->mm = thread->mm;
> +	thread->mm->kfd_process = process;
> +	list_add_tail(&process->processes_list, &kfd_processes_list);
> +
> +	process->lead_thread = thread->group_leader;
> +
> +	process->queue_array_size = INITIAL_QUEUE_ARRAY_SIZE;
> +
> +	INIT_LIST_HEAD(&process->per_device_data);
> +
> +	return process;
> +
> +err_alloc:
> +	kfree(process->queues);
> +	kfree(process);
> +	return ERR_PTR(err);
> +}
> +
> +struct kfd_process_device *
> +radeon_kfd_get_process_device_data(struct kfd_dev *dev, struct kfd_process *p)
> +{
> +	struct kfd_process_device *pdd;
> +
> +	list_for_each_entry(pdd, &p->per_device_data, per_device_list)
> +		if (pdd->dev == dev)
> +			return pdd;
> +
> +	pdd = kzalloc(sizeof(*pdd), GFP_KERNEL);
> +	if (pdd != NULL) {
> +		pdd->dev = dev;
> +		list_add(&pdd->per_device_list, &p->per_device_data);
> +	}
> +
> +	return pdd;
> +}
> +
> +/* Direct the IOMMU to bind the process (specifically the pasid->mm) to the device.
> + * Unbinding occurs when the process dies or the device is removed.
> + *
> + * Assumes that the process lock is held.
> + */
> +struct kfd_process_device *radeon_kfd_bind_process_to_device(struct kfd_dev *dev, struct kfd_process *p)
> +{
> +	struct kfd_process_device *pdd = radeon_kfd_get_process_device_data(dev, p);
> +	int err;
> +
> +	if (pdd == NULL)
> +		return ERR_PTR(-ENOMEM);
> +
> +	if (pdd->bound)
> +		return pdd;
> +
> +	err = amd_iommu_bind_pasid(dev->pdev, p->pasid, p->lead_thread);

Are we to assume that for eternity this will not work on iommu that do support
PASID/ATS but are not from AMD ? If it was an APU specific function i would
understand but it seems that the IOMMU API needs to grow. I am pretty sure
Intel will have an ATS/PASID IOMMU.

> +	if (err < 0)
> +		return ERR_PTR(err);
> +
> +	pdd->bound = true;
> +
> +	return pdd;
> +}
> +
> +void radeon_kfd_unbind_process_from_device(struct kfd_dev *dev, pasid_t pasid)
> +{
> +	struct kfd_process *p;
> +	struct kfd_process_device *pdd;
> +
> +	BUG_ON(dev == NULL);
> +
> +	mutex_lock(&kfd_processes_mutex);
> +
> +	list_for_each_entry(p, &kfd_processes_list, processes_list)
> +		if (p->pasid == pasid)
> +			break;
> +
> +	mutex_unlock(&kfd_processes_mutex);
> +
> +	BUG_ON(p->pasid != pasid);
> +
> +	pdd = radeon_kfd_get_process_device_data(dev, p);
> +
> +	BUG_ON(pdd == NULL);
> +
> +	mutex_lock(&p->mutex);
> +
> +	destroy_queues(p, dev);
> +
> +	/* All queues just got destroyed so this should be gone. */
> +	BUG_ON(pdd->scheduler_process != NULL);
> +
> +	/*
> +	 * Just mark pdd as unbound, because we still need it to call
> +	 * amd_iommu_unbind_pasid() in when the process exits.
> +	 * We don't call amd_iommu_unbind_pasid() here
> +	 * because the IOMMU called us.
> +	 */
> +	pdd->bound = false;
> +
> +	mutex_unlock(&p->mutex);
> +}
> +
> +/* Ensure that the process's queue array is large enough to hold the queue at queue_id.
> + * Assumes that the process lock is held. */
> +static bool ensure_queue_array_size(struct kfd_process *p, unsigned int queue_id)
> +{
> +	size_t desired_size;
> +	struct kfd_queue **new_queues;
> +
> +	compiletime_assert(INITIAL_QUEUE_ARRAY_SIZE > 0, "INITIAL_QUEUE_ARRAY_SIZE must not be 0");
> +	compiletime_assert(INITIAL_QUEUE_ARRAY_SIZE <= MAX_PROCESS_QUEUES,
> +			   "INITIAL_QUEUE_ARRAY_SIZE must be less than MAX_PROCESS_QUEUES");
> +	/* Ensure that doubling the current size won't ever overflow. */
> +	compiletime_assert(MAX_PROCESS_QUEUES < SIZE_MAX / 2, "MAX_PROCESS_QUEUES must be less than SIZE_MAX/2");
> +
> +	/*
> +	 * These & queue_id < MAX_PROCESS_QUEUES guarantee that
> +	 * the desired_size calculation will end up <= MAX_PROCESS_QUEUES
> +	 */
> +	compiletime_assert(is_power_of_2(INITIAL_QUEUE_ARRAY_SIZE), "INITIAL_QUEUE_ARRAY_SIZE must be power of 2.");
> +	compiletime_assert(MAX_PROCESS_QUEUES % INITIAL_QUEUE_ARRAY_SIZE == 0,
> +			   "MAX_PROCESS_QUEUES must be multiple of INITIAL_QUEUE_ARRAY_SIZE.");
> +	compiletime_assert(is_power_of_2(MAX_PROCESS_QUEUES / INITIAL_QUEUE_ARRAY_SIZE),
> +			   "MAX_PROCESS_QUEUES must be a power-of-2 multiple of INITIAL_QUEUE_ARRAY_SIZE.");
> +
> +	if (queue_id < p->queue_array_size)
> +		return true;
> +
> +	if (queue_id >= MAX_PROCESS_QUEUES)
> +		return false;
> +
> +	desired_size = p->queue_array_size;
> +	while (desired_size <= queue_id)
> +		desired_size *= 2;
> +
> +	BUG_ON(desired_size < queue_id || desired_size > MAX_PROCESS_QUEUES);
> +	BUG_ON(desired_size % INITIAL_QUEUE_ARRAY_SIZE != 0 || !is_power_of_2(desired_size / INITIAL_QUEUE_ARRAY_SIZE));
> +
> +	new_queues = kmalloc_array(desired_size, sizeof(p->queues[0]), GFP_KERNEL);
> +	if (!new_queues)
> +		return false;
> +
> +	memcpy(new_queues, p->queues, p->queue_array_size * sizeof(p->queues[0]));
> +
> +	kfree(p->queues);
> +	p->queues = new_queues;
> +	p->queue_array_size = desired_size;
> +
> +	return true;
> +}
> +
> +/* Assumes that the process lock is held. */
> +bool radeon_kfd_allocate_queue_id(struct kfd_process *p, unsigned int *queue_id)
> +{
> +	unsigned int qid = find_first_zero_bit(p->allocated_queue_bitmap, MAX_PROCESS_QUEUES);
> +
> +	if (qid >= MAX_PROCESS_QUEUES)
> +		return false;
> +
> +	if (!ensure_queue_array_size(p, qid))
> +		return false;
> +
> +	__set_bit(qid, p->allocated_queue_bitmap);
> +
> +	p->queues[qid] = NULL;
> +	*queue_id = qid;
> +
> +	return true;
> +}
> +
> +/* Install a queue into a previously-allocated queue id.
> + *  Assumes that the process lock is held. */
> +void radeon_kfd_install_queue(struct kfd_process *p, unsigned int queue_id, struct kfd_queue *queue)
> +{
> +	BUG_ON(queue_id >= p->queue_array_size); /* Have to call allocate_queue_id before install_queue. */
> +	BUG_ON(queue == NULL);
> +
> +	p->queues[queue_id] = queue;
> +}
> +
> +/* Remove a queue from the open queue list and deallocate the queue id.
> + * This can be called whether or not a queue was installed.
> + * Assumes that the process lock is held. */
> +void radeon_kfd_remove_queue(struct kfd_process *p, unsigned int queue_id)
> +{
> +	BUG_ON(!test_bit(queue_id, p->allocated_queue_bitmap));
> +	BUG_ON(queue_id >= p->queue_array_size);
> +
> +	__clear_bit(queue_id, p->allocated_queue_bitmap);
> +}
> +
> +/* Assumes that the process lock is held. */
> +struct kfd_queue *radeon_kfd_get_queue(struct kfd_process *p, unsigned int queue_id)
> +{
> +	/* test_bit because the contents of unallocated queue slots are undefined.
> +	 * Otherwise ensure_queue_array_size would have to clear new entries and
> +	 * remove_queue would have to NULL removed queues. */
> +	return (queue_id < p->queue_array_size &&
> +		test_bit(queue_id, p->allocated_queue_bitmap)) ?
> +			p->queues[queue_id] : NULL;
> +}
> diff --git a/drivers/gpu/hsa/radeon/kfd_scheduler.h b/drivers/gpu/hsa/radeon/kfd_scheduler.h
> new file mode 100644
> index 0000000..48a032f
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_scheduler.h
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright 2014 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.
> + */
> +
> +#ifndef KFD_SCHEDULER_H_INCLUDED
> +#define KFD_SCHEDULER_H_INCLUDED
> +
> +#include <linux/types.h>
> +struct kfd_process;
> +
> +/* Opaque types for scheduler private data. */
> +struct kfd_scheduler;
> +struct kfd_scheduler_process;
> +struct kfd_scheduler_queue;
> +
> +struct kfd_scheduler_class {
> +	const char *name;
> +
> +	int (*create)(struct kfd_dev *, struct kfd_scheduler **);
> +	void (*destroy)(struct kfd_scheduler *);
> +
> +	void (*start)(struct kfd_scheduler *);
> +	void (*stop)(struct kfd_scheduler *);
> +
> +	int (*register_process)(struct kfd_scheduler *, struct kfd_process *, struct kfd_scheduler_process **);
> +	void (*deregister_process)(struct kfd_scheduler *, struct kfd_scheduler_process *);
> +
> +	size_t queue_size;
> +
> +	int (*create_queue)(struct kfd_scheduler *scheduler,
> +			    struct kfd_scheduler_process *process,
> +			    struct kfd_scheduler_queue *queue,
> +			    void __user *ring_address,
> +			    uint64_t ring_size,
> +			    void __user *rptr_address,
> +			    void __user *wptr_address,
> +			    unsigned int doorbell);
> +
> +	void (*destroy_queue)(struct kfd_scheduler *, struct kfd_scheduler_queue *);
> +};
> +
> +extern const struct kfd_scheduler_class radeon_kfd_cik_static_scheduler_class;
> +
> +#endif
> diff --git a/drivers/gpu/hsa/radeon/kfd_topology.c b/drivers/gpu/hsa/radeon/kfd_topology.c
> new file mode 100644
> index 0000000..6acac25
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_topology.c
> @@ -0,0 +1,1201 @@
> +/*
> + * Copyright 2014 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 "kfd_priv.h"
> +#include "kfd_crat.h"
> +#include "kfd_topology.h"
> +
> +static struct list_head topology_device_list;
> +static int topology_crat_parsed;
> +static struct kfd_system_properties sys_props;
> +
> +static DECLARE_RWSEM(topology_lock);
> +
> +
> +static uint8_t checksum_image(const void *buf, size_t len)
> +{
> +	uint8_t *p = (uint8_t *)buf;
> +	uint8_t sum = 0;
> +
> +	if (!buf)
> +		return 0;
> +
> +	while (len-- > 0)
> +		sum += *p++;
> +
> +	return sum;
> +		}
> +
> +struct kfd_dev *radeon_kfd_device_by_id(uint32_t gpu_id)
> +{
> +	struct kfd_topology_device *top_dev;
> +	struct kfd_dev *device = NULL;
> +
> +	down_read(&topology_lock);
> +
> +	list_for_each_entry(top_dev, &topology_device_list, list)
> +		if (top_dev->gpu_id == gpu_id) {
> +			device = top_dev->gpu;
> +			break;
> +		}
> +
> +	up_read(&topology_lock);
> +
> +	return device;
> +}
> +
> +struct kfd_dev *radeon_kfd_device_by_pci_dev(const struct pci_dev *pdev)
> +{
> +	struct kfd_topology_device *top_dev;
> +	struct kfd_dev *device = NULL;
> +
> +	down_read(&topology_lock);
> +
> +	list_for_each_entry(top_dev, &topology_device_list, list)
> +		if (top_dev->gpu->pdev == pdev) {
> +			device = top_dev->gpu;
> +			break;
> +		}
> +
> +	up_read(&topology_lock);
> +
> +	return device;
> +}
> +
> +static int kfd_topology_get_crat_acpi(void *crat_image, size_t *size)
> +{
> +	struct acpi_table_header *crat_table;
> +	acpi_status status;
> +
> +	if (!size)
> +		return -EINVAL;
> +
> +/*
> +	 * Fetch the CRAT table from ACPI
> + */
> +	status = acpi_get_table(CRAT_SIGNATURE, 0, &crat_table);
> +	if (status == AE_NOT_FOUND) {
> +		pr_warn("CRAT table not found\n");
> +		return -ENODATA;
> +	} else if (ACPI_FAILURE(status)) {
> +		const char *err = acpi_format_exception(status);
> +
> +		pr_err("CRAT table error: %s\n", err);
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * The checksum of the table should be verified
> +	 */
> +	if (checksum_image(crat_table, crat_table->length) ==
> +		crat_table->checksum) {
> +		pr_err("Bad checksum for the CRAT table\n");
> +		return -EINVAL;
> +}
> +
> +
> +	if (*size >= crat_table->length && crat_image != 0)
> +		memcpy(crat_image, crat_table, crat_table->length);
> +
> +	*size = crat_table->length;
> +
> +	return 0;
> +}
> +
> +static void kfd_populated_cu_info_cpu(struct kfd_topology_device *dev,
> +		struct crat_subtype_computeunit *cu)
> +{
> +	BUG_ON(!dev);
> +	BUG_ON(!cu);
> +
> +	dev->node_props.cpu_cores_count = cu->num_cpu_cores;
> +	dev->node_props.cpu_core_id_base = cu->processor_id_low;
> +	if (cu->hsa_capability & CRAT_CU_FLAGS_IOMMU_PRESENT)
> +		dev->node_props.capability |= HSA_CAP_ATS_PRESENT;
> +
> +	pr_info("CU CPU: cores=%d id_base=%d\n", cu->num_cpu_cores,
> +			cu->processor_id_low);
> +}
> +
> +static void kfd_populated_cu_info_gpu(struct kfd_topology_device *dev,
> +		struct crat_subtype_computeunit *cu)
> +{
> +	BUG_ON(!dev);
> +	BUG_ON(!cu);
> +
> +	dev->node_props.simd_id_base = cu->processor_id_low;
> +	dev->node_props.simd_count = cu->num_simd_cores;
> +	dev->node_props.lds_size_in_kb = cu->lds_size_in_kb;
> +	dev->node_props.max_waves_per_simd = cu->max_waves_simd;
> +	dev->node_props.wave_front_size = cu->wave_front_size;
> +	dev->node_props.mem_banks_count = cu->num_banks;
> +	dev->node_props.array_count = cu->num_arrays;
> +	dev->node_props.cu_per_simd_array = cu->num_cu_per_array;
> +	dev->node_props.simd_per_cu = cu->num_simd_per_cu;
> +	dev->node_props.max_slots_scratch_cu = cu->max_slots_scatch_cu;
> +	if (cu->hsa_capability & CRAT_CU_FLAGS_HOT_PLUGGABLE)
> +		dev->node_props.capability |= HSA_CAP_HOT_PLUGGABLE;
> +	pr_info("CU GPU: simds=%d id_base=%d\n", cu->num_simd_cores,
> +				cu->processor_id_low);
> +}
> +
> +/* kfd_parse_subtype_cu is called when the topology mutex is already acquired */
> +static int kfd_parse_subtype_cu(struct crat_subtype_computeunit *cu)
> +{
> +	struct kfd_topology_device *dev;
> +	int i = 0;
> +
> +	BUG_ON(!cu);
> +
> +	pr_info("Found CU entry in CRAT table with proximity_domain=%d caps=%x\n",
> +			cu->proximity_domain, cu->hsa_capability);
> +	list_for_each_entry(dev, &topology_device_list, list) {
> +		if (cu->proximity_domain == i) {
> +			if (cu->flags & CRAT_CU_FLAGS_CPU_PRESENT)
> +				kfd_populated_cu_info_cpu(dev, cu);
> +
> +			if (cu->flags & CRAT_CU_FLAGS_GPU_PRESENT)
> +				kfd_populated_cu_info_gpu(dev, cu);
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +/* kfd_parse_subtype_mem is called when the topology mutex is already acquired */
> +static int kfd_parse_subtype_mem(struct crat_subtype_memory *mem)
> +{
> +	struct kfd_mem_properties *props;
> +	struct kfd_topology_device *dev;
> +	int i = 0;
> +
> +	BUG_ON(!mem);
> +
> +	pr_info("Found memory entry in CRAT table with proximity_domain=%d\n",
> +			mem->promixity_domain);
> +	list_for_each_entry(dev, &topology_device_list, list) {
> +		if (mem->promixity_domain == i) {
> +			props = kfd_alloc_struct(props);
> +			if (props == 0)
> +				return -ENOMEM;
> +
> +			if (dev->node_props.cpu_cores_count == 0)
> +				props->heap_type = HSA_MEM_HEAP_TYPE_FB_PRIVATE;
> +			else
> +				props->heap_type = HSA_MEM_HEAP_TYPE_SYSTEM;
> +
> +			if (mem->flags & CRAT_MEM_FLAGS_HOT_PLUGGABLE)
> +				props->flags |= HSA_MEM_FLAGS_HOT_PLUGGABLE;
> +			if (mem->flags & CRAT_MEM_FLAGS_NON_VOLATILE)
> +				props->flags |= HSA_MEM_FLAGS_NON_VOLATILE;
> +
> +			props->size_in_bytes = ((uint64_t)mem->length_high << 32) +
> +						mem->length_low;
> +			props->width = mem->width;
> +
> +			dev->mem_bank_count++;
> +			list_add_tail(&props->list, &dev->mem_props);
> +
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +/* kfd_parse_subtype_cache is called when the topology mutex is already acquired */
> +static int kfd_parse_subtype_cache(struct crat_subtype_cache *cache)
> +{
> +	struct kfd_cache_properties *props;
> +	struct kfd_topology_device *dev;
> +	uint32_t id;
> +
> +	BUG_ON(!cache);
> +
> +	id = cache->processor_id_low;
> +
> +	pr_info("Found cache entry in CRAT table with processor_id=%d\n", id);
> +	list_for_each_entry(dev, &topology_device_list, list)
> +		if (id == dev->node_props.cpu_core_id_base ||
> +		    id == dev->node_props.simd_id_base) {
> +			props = kfd_alloc_struct(props);
> +			if (props == 0)
> +				return -ENOMEM;
> +
> +			props->processor_id_low = id;
> +			props->cache_level = cache->cache_level;
> +			props->cache_size = cache->cache_size;
> +			props->cacheline_size = cache->cache_line_size;
> +			props->cachelines_per_tag = cache->lines_per_tag;
> +			props->cache_assoc = cache->associativity;
> +			props->cache_latency = cache->cache_latency;
> +
> +			if (cache->flags & CRAT_CACHE_FLAGS_DATA_CACHE)
> +				props->cache_type |= HSA_CACHE_TYPE_DATA;
> +			if (cache->flags & CRAT_CACHE_FLAGS_INST_CACHE)
> +				props->cache_type |= HSA_CACHE_TYPE_INSTRUCTION;
> +			if (cache->flags & CRAT_CACHE_FLAGS_CPU_CACHE)
> +				props->cache_type |= HSA_CACHE_TYPE_CPU;
> +			if (cache->flags & CRAT_CACHE_FLAGS_SIMD_CACHE)
> +				props->cache_type |= HSA_CACHE_TYPE_HSACU;
> +
> +			dev->cache_count++;
> +			dev->node_props.caches_count++;
> +			list_add_tail(&props->list, &dev->cache_props);
> +
> +			break;
> +		}
> +
> +	return 0;
> +}
> +
> +/* kfd_parse_subtype_iolink is called when the topology mutex is already acquired */
> +static int kfd_parse_subtype_iolink(struct crat_subtype_iolink *iolink)
> +{
> +	struct kfd_iolink_properties *props;
> +	struct kfd_topology_device *dev;
> +	uint32_t i = 0;
> +	uint32_t id_from;
> +	uint32_t id_to;
> +
> +	BUG_ON(!iolink);
> +
> +	id_from = iolink->proximity_domain_from;
> +	id_to = iolink->proximity_domain_to;
> +
> +	pr_info("Found IO link entry in CRAT table with id_from=%d\n", id_from);
> +	list_for_each_entry(dev, &topology_device_list, list) {
> +		if (id_from == i) {
> +			props = kfd_alloc_struct(props);
> +			if (props == 0)
> +				return -ENOMEM;
> +
> +			props->node_from = id_from;
> +			props->node_to = id_to;
> +			props->ver_maj = iolink->version_major;
> +			props->ver_min = iolink->version_minor;
> +
> +			/*
> +			 * weight factor (derived from CDIR), currently always 1
> +			 */
> +			props->weight = 1;
> +
> +			props->min_latency = iolink->minimum_latency;
> +			props->max_latency = iolink->maximum_latency;
> +			props->min_bandwidth = iolink->minimum_bandwidth_mbs;
> +			props->max_bandwidth = iolink->maximum_bandwidth_mbs;
> +			props->rec_transfer_size =
> +					iolink->recommended_transfer_size;
> +
> +			dev->io_link_count++;
> +			dev->node_props.io_links_count++;
> +			list_add_tail(&props->list, &dev->io_link_props);
> +
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int kfd_parse_subtype(struct crat_subtype_generic *sub_type_hdr)
> +{
> +	struct crat_subtype_computeunit *cu;
> +	struct crat_subtype_memory *mem;
> +	struct crat_subtype_cache *cache;
> +	struct crat_subtype_iolink *iolink;
> +	int ret = 0;
> +
> +	BUG_ON(!sub_type_hdr);
> +
> +	switch (sub_type_hdr->type) {
> +	case CRAT_SUBTYPE_COMPUTEUNIT_AFFINITY:
> +		cu = (struct crat_subtype_computeunit *)sub_type_hdr;
> +		ret = kfd_parse_subtype_cu(cu);
> +		break;
> +	case CRAT_SUBTYPE_MEMORY_AFFINITY:
> +		mem = (struct crat_subtype_memory *)sub_type_hdr;
> +		ret = kfd_parse_subtype_mem(mem);
> +		break;
> +	case CRAT_SUBTYPE_CACHE_AFFINITY:
> +		cache = (struct crat_subtype_cache *)sub_type_hdr;
> +		ret = kfd_parse_subtype_cache(cache);
> +		break;
> +	case CRAT_SUBTYPE_TLB_AFFINITY:
> +		/*
> +		 * For now, nothing to do here
> +		 */
> +		pr_info("Found TLB entry in CRAT table (not processing)\n");
> +		break;
> +	case CRAT_SUBTYPE_CCOMPUTE_AFFINITY:
> +		/*
> +		 * For now, nothing to do here
> +		 */
> +		pr_info("Found CCOMPUTE entry in CRAT table (not processing)\n");
> +		break;
> +	case CRAT_SUBTYPE_IOLINK_AFFINITY:
> +		iolink = (struct crat_subtype_iolink *)sub_type_hdr;
> +		ret = kfd_parse_subtype_iolink(iolink);
> +		break;
> +	default:
> +		pr_warn("Unknown subtype (%d) in CRAT\n",
> +				sub_type_hdr->type);
> +	}
> +
> +	return ret;
> +}
> +
> +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;
> +
> +	BUG_ON(!dev);
> +
> +	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);
> +	}
> +
> +	kfree(dev);
> +
> +	sys_props.num_devices--;
> +}
> +
> +static void kfd_release_live_view(void)
> +{
> +	struct kfd_topology_device *dev;
> +
> +	while (topology_device_list.next != &topology_device_list) {
> +		dev = container_of(topology_device_list.next,
> +				 struct kfd_topology_device, list);
> +		kfd_release_topology_device(dev);
> +}
> +
> +	memset(&sys_props, 0, sizeof(sys_props));
> +}
> +
> +static struct kfd_topology_device *kfd_create_topology_device(void)
> +{
> +	struct kfd_topology_device *dev;
> +
> +	dev = kfd_alloc_struct(dev);
> +	if (dev == 0) {
> +		pr_err("No memory to allocate a topology device");
> +		return 0;
> +	}
> +
> +	INIT_LIST_HEAD(&dev->mem_props);
> +	INIT_LIST_HEAD(&dev->cache_props);
> +	INIT_LIST_HEAD(&dev->io_link_props);
> +
> +	list_add_tail(&dev->list, &topology_device_list);
> +	sys_props.num_devices++;
> +
> +	return dev;
> +	}
> +
> +static int kfd_parse_crat_table(void *crat_image)
> +{
> +	struct kfd_topology_device *top_dev;
> +	struct crat_subtype_generic *sub_type_hdr;
> +	uint16_t node_id;
> +	int ret;
> +	struct crat_header *crat_table = (struct crat_header *)crat_image;
> +	uint16_t num_nodes;
> +	uint32_t image_len;
> +
> +	if (!crat_image)
> +		return -EINVAL;
> +
> +	num_nodes = crat_table->num_domains;
> +	image_len = crat_table->length;
> +
> +	pr_info("Parsing CRAT table with %d nodes\n", num_nodes);
> +
> +	for (node_id = 0; node_id < num_nodes; node_id++) {
> +		top_dev = kfd_create_topology_device();
> +		if (!top_dev) {
> +			kfd_release_live_view();
> +			return -ENOMEM;
> +	}
> +}
> +
> +	sys_props.platform_id = *((uint64_t *)crat_table->oem_id);
> +	sys_props.platform_oem = *((uint64_t *)crat_table->oem_table_id);
> +	sys_props.platform_rev = crat_table->revision;
> +
> +	sub_type_hdr = (struct crat_subtype_generic *)(crat_table+1);
> +	while ((char *)sub_type_hdr + sizeof(struct crat_subtype_generic) <
> +			((char *)crat_image) + image_len) {
> +		if (sub_type_hdr->flags & CRAT_SUBTYPE_FLAGS_ENABLED) {
> +			ret = kfd_parse_subtype(sub_type_hdr);
> +			if (ret != 0) {
> +				kfd_release_live_view();
> +				return ret;
> +			}
> +		}
> +
> +		sub_type_hdr = (typeof(sub_type_hdr))((char *)sub_type_hdr +
> +				sub_type_hdr->length);
> +	}
> +
> +	sys_props.generation_count++;
> +	topology_crat_parsed = 1;
> +
> +	return 0;
> +}
> +
> +
> +#define sysfs_show_gen_prop(buffer, fmt, ...) \
> +		snprintf(buffer, PAGE_SIZE, "%s"fmt, buffer, __VA_ARGS__)
> +#define sysfs_show_32bit_prop(buffer, name, value) \
> +		sysfs_show_gen_prop(buffer, "%s %u\n", name, value)
> +#define sysfs_show_64bit_prop(buffer, name, value) \
> +		sysfs_show_gen_prop(buffer, "%s %llu\n", name, value)
> +#define sysfs_show_32bit_val(buffer, value) \
> +		sysfs_show_gen_prop(buffer, "%u\n", value)
> +#define sysfs_show_str_val(buffer, value) \
> +		sysfs_show_gen_prop(buffer, "%s\n", value)
> +
> +static ssize_t sysprops_show(struct kobject *kobj, struct attribute *attr,
> +		char *buffer)
> +{
> +	ssize_t ret;
> +
> +	/* Making sure that the buffer is an empty string */
> +	buffer[0] = 0;
> +
> +	if (attr == &sys_props.attr_genid) {
> +		ret = sysfs_show_32bit_val(buffer, sys_props.generation_count);
> +	} else if (attr == &sys_props.attr_props) {
> +		sysfs_show_64bit_prop(buffer, "platform_oem",
> +				sys_props.platform_oem);
> +		sysfs_show_64bit_prop(buffer, "platform_id",
> +				sys_props.platform_id);
> +		ret = sysfs_show_64bit_prop(buffer, "platform_rev",
> +				sys_props.platform_rev);
> +	} else {
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct sysfs_ops sysprops_ops = {
> +	.show = sysprops_show,
> +};
> +
> +static struct kobj_type sysprops_type = {
> +	.sysfs_ops = &sysprops_ops,
> +};
> +
> +static ssize_t iolink_show(struct kobject *kobj, struct attribute *attr,
> +		char *buffer)
> +{
> +	ssize_t ret;
> +	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);
> +	sysfs_show_32bit_prop(buffer, "type", iolink->iolink_type);
> +	sysfs_show_32bit_prop(buffer, "version_major", iolink->ver_maj);
> +	sysfs_show_32bit_prop(buffer, "version_minor", iolink->ver_min);
> +	sysfs_show_32bit_prop(buffer, "node_from", iolink->node_from);
> +	sysfs_show_32bit_prop(buffer, "node_to", iolink->node_to);
> +	sysfs_show_32bit_prop(buffer, "weight", iolink->weight);
> +	sysfs_show_32bit_prop(buffer, "min_latency", iolink->min_latency);
> +	sysfs_show_32bit_prop(buffer, "max_latency", iolink->max_latency);
> +	sysfs_show_32bit_prop(buffer, "min_bandwidth", iolink->min_bandwidth);
> +	sysfs_show_32bit_prop(buffer, "max_bandwidth", iolink->max_bandwidth);
> +	sysfs_show_32bit_prop(buffer, "recommended_transfer_size",
> +			iolink->rec_transfer_size);
> +	ret = sysfs_show_32bit_prop(buffer, "flags", iolink->flags);
> +
> +	return ret;
> +}
> +
> +static const struct sysfs_ops iolink_ops = {
> +	.show = iolink_show,
> +};
> +
> +static struct kobj_type iolink_type = {
> +	.sysfs_ops = &iolink_ops,
> +};
> +
> +static ssize_t mem_show(struct kobject *kobj, struct attribute *attr,
> +		char *buffer)
> +{
> +	ssize_t ret;
> +	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);
> +	sysfs_show_32bit_prop(buffer, "heap_type", mem->heap_type);
> +	sysfs_show_64bit_prop(buffer, "size_in_bytes", mem->size_in_bytes);
> +	sysfs_show_32bit_prop(buffer, "flags", mem->flags);
> +	sysfs_show_32bit_prop(buffer, "width", mem->width);
> +	ret = sysfs_show_32bit_prop(buffer, "mem_clk_max", mem->mem_clk_max);
> +
> +	return ret;
> +}
> +
> +static const struct sysfs_ops mem_ops = {
> +	.show = mem_show,
> +};
> +
> +static struct kobj_type mem_type = {
> +	.sysfs_ops = &mem_ops,
> +};
> +
> +static ssize_t kfd_cache_show(struct kobject *kobj, struct attribute *attr,
> +		char *buffer)
> +{
> +	ssize_t ret;
> +	uint32_t i;
> +	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);
> +	sysfs_show_32bit_prop(buffer, "processor_id_low",
> +			cache->processor_id_low);
> +	sysfs_show_32bit_prop(buffer, "level", cache->cache_level);
> +	sysfs_show_32bit_prop(buffer, "size", cache->cache_size);
> +	sysfs_show_32bit_prop(buffer, "cache_line_size", cache->cacheline_size);
> +	sysfs_show_32bit_prop(buffer, "cache_lines_per_tag",
> +			cache->cachelines_per_tag);
> +	sysfs_show_32bit_prop(buffer, "association", cache->cache_assoc);
> +	sysfs_show_32bit_prop(buffer, "latency", cache->cache_latency);
> +	sysfs_show_32bit_prop(buffer, "type", cache->cache_type);
> +	snprintf(buffer, PAGE_SIZE, "%ssibling_map ", buffer);
> +	for (i = 0; i < KFD_TOPOLOGY_CPU_SIBLINGS; i++)
> +		ret = snprintf(buffer, PAGE_SIZE, "%s%d%s",
> +				buffer, cache->sibling_map[i],
> +				(i == KFD_TOPOLOGY_CPU_SIBLINGS-1) ?
> +						"\n" : ",");
> +
> +	return ret;
> +}
> +
> +static const struct sysfs_ops cache_ops = {
> +	.show = kfd_cache_show,
> +};
> +
> +static struct kobj_type cache_type = {
> +	.sysfs_ops = &cache_ops,
> +};
> +
> +static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
> +		char *buffer)
> +{
> +	ssize_t ret;
> +	struct kfd_topology_device *dev;
> +	char public_name[KFD_TOPOLOGY_PUBLIC_NAME_SIZE];
> +	uint32_t i;
> +
> +	/* 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);
> +		ret = sysfs_show_32bit_val(buffer, dev->gpu_id);
> +	} else if (strcmp(attr->name, "name") == 0) {
> +		dev = container_of(attr, struct kfd_topology_device,
> +				attr_name);
> +		for (i = 0; i < KFD_TOPOLOGY_PUBLIC_NAME_SIZE; i++) {
> +			public_name[i] =
> +					(char)dev->node_props.marketing_name[i];
> +			if (dev->node_props.marketing_name[i] == 0)
> +				break;
> +		}
> +		public_name[KFD_TOPOLOGY_PUBLIC_NAME_SIZE-1] = 0x0;
> +		ret = sysfs_show_str_val(buffer, public_name);
> +	} else {
> +		dev = container_of(attr, struct kfd_topology_device,
> +				attr_props);
> +		sysfs_show_32bit_prop(buffer, "cpu_cores_count",
> +				dev->node_props.cpu_cores_count);
> +		sysfs_show_32bit_prop(buffer, "simd_count",
> +				dev->node_props.simd_count);
> +		sysfs_show_32bit_prop(buffer, "mem_banks_count",
> +				dev->node_props.mem_banks_count);
> +		sysfs_show_32bit_prop(buffer, "caches_count",
> +				dev->node_props.caches_count);
> +		sysfs_show_32bit_prop(buffer, "io_links_count",
> +				dev->node_props.io_links_count);
> +		sysfs_show_32bit_prop(buffer, "cpu_core_id_base",
> +				dev->node_props.cpu_core_id_base);
> +		sysfs_show_32bit_prop(buffer, "simd_id_base",
> +				dev->node_props.simd_id_base);
> +		sysfs_show_32bit_prop(buffer, "capability",
> +				dev->node_props.capability);
> +		sysfs_show_32bit_prop(buffer, "max_waves_per_simd",
> +				dev->node_props.max_waves_per_simd);
> +		sysfs_show_32bit_prop(buffer, "lds_size_in_kb",
> +				dev->node_props.lds_size_in_kb);
> +		sysfs_show_32bit_prop(buffer, "gds_size_in_kb",
> +				dev->node_props.gds_size_in_kb);
> +		sysfs_show_32bit_prop(buffer, "wave_front_size",
> +				dev->node_props.wave_front_size);
> +		sysfs_show_32bit_prop(buffer, "array_count",
> +				dev->node_props.array_count);
> +		sysfs_show_32bit_prop(buffer, "simd_arrays_per_engine",
> +				dev->node_props.simd_arrays_per_engine);
> +		sysfs_show_32bit_prop(buffer, "cu_per_simd_array",
> +				dev->node_props.cu_per_simd_array);
> +		sysfs_show_32bit_prop(buffer, "simd_per_cu",
> +				dev->node_props.simd_per_cu);
> +		sysfs_show_32bit_prop(buffer, "max_slots_scratch_cu",
> +				dev->node_props.max_slots_scratch_cu);
> +		sysfs_show_32bit_prop(buffer, "engine_id",
> +				dev->node_props.engine_id);
> +		sysfs_show_32bit_prop(buffer, "vendor_id",
> +				dev->node_props.vendor_id);
> +		sysfs_show_32bit_prop(buffer, "device_id",
> +				dev->node_props.device_id);
> +		sysfs_show_32bit_prop(buffer, "location_id",
> +				dev->node_props.location_id);
> +		sysfs_show_32bit_prop(buffer, "max_engine_clk_fcompute",
> +				dev->node_props.max_engine_clk_fcompute);
> +		ret = sysfs_show_32bit_prop(buffer, "max_engine_clk_ccompute",
> +				dev->node_props.max_engine_clk_ccompute);
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct sysfs_ops node_ops = {
> +	.show = node_show,
> +};
> +
> +static struct kobj_type node_type = {
> +	.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 *iolink;
> +	struct kfd_cache_properties *cache;
> +	struct kfd_mem_properties *mem;
> +
> +	BUG_ON(!dev);
> +
> +	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 = 0;
> +			}
> +		kobject_del(dev->kobj_iolink);
> +		kobject_put(dev->kobj_iolink);
> +		dev->kobj_iolink = 0;
> +	}
> +
> +	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 = 0;
> +			}
> +		kobject_del(dev->kobj_cache);
> +		kobject_put(dev->kobj_cache);
> +		dev->kobj_cache = 0;
> +	}
> +
> +	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 = 0;
> +			}
> +		kobject_del(dev->kobj_mem);
> +		kobject_put(dev->kobj_mem);
> +		dev->kobj_mem = 0;
> +	}
> +
> +	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 = 0;
> +	}
> +}
> +
> +static int kfd_build_sysfs_node_entry(struct kfd_topology_device *dev,
> +		uint32_t id)
> +{
> +	struct kfd_iolink_properties *iolink;
> +	struct kfd_cache_properties *cache;
> +	struct kfd_mem_properties *mem;
> +	int ret;
> +	uint32_t i;
> +
> +	BUG_ON(!dev);
> +
> +	/*
> +	 * Creating the sysfs folders
> +	 */
> +	BUG_ON(dev->kobj_node);
> +	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)
> +		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;
> +
> +	/*
> +	 * 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)
> +			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)
> +			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)
> +			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++;
> +}
> +
> +	return 0;
> +}
> +
> +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, 0);
> +		if (ret < 0)
> +			return ret;
> +		i++;
> +	}
> +
> +	return 0;
> +}
> +
> +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;
> +
> +	pr_info("Creating topology SYSFS entries\n");
> +	if (sys_props.kobj_topology == 0) {
> +		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)
> +			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 = 0;
> +		}
> +		kobject_del(sys_props.kobj_topology);
> +		kobject_put(sys_props.kobj_topology);
> +		sys_props.kobj_topology = 0;
> +	}
> +}
> +
> +int kfd_topology_init(void)
> +{
> +	void *crat_image = 0;
> +	size_t image_size = 0;
> +	int ret;
> +
> +	/*
> +	 * Initialize the head for the topology device list
> +	 */
> +	INIT_LIST_HEAD(&topology_device_list);
> +	init_rwsem(&topology_lock);
> +	topology_crat_parsed = 0;
> +
> +	memset(&sys_props, 0, sizeof(sys_props));
> +
> +	/*
> +	 * Get the CRAT image from the ACPI
> +	 */
> +	ret = kfd_topology_get_crat_acpi(crat_image, &image_size);
> +	if (ret == 0 && image_size > 0) {
> +		pr_info("Found CRAT image with size=%zd\n", image_size);
> +		crat_image = kmalloc(image_size, GFP_KERNEL);
> +		if (!crat_image) {
> +			ret = -ENOMEM;
> +			pr_err("No memory for allocating CRAT image\n");
> +			goto err;
> +		}
> +		ret = kfd_topology_get_crat_acpi(crat_image, &image_size);
> +
> +		if (ret == 0) {
> +			down_write(&topology_lock);
> +			ret = kfd_parse_crat_table(crat_image);
> +			if (ret == 0)
> +				ret = kfd_topology_update_sysfs();
> +			up_write(&topology_lock);
> +		} else {
> +			pr_err("Couldn't get CRAT table size from ACPI\n");
> +		}
> +		kfree(crat_image);
> +	} else if (ret == -ENODATA) {
> +		ret = 0;
> +	} else {
> +		pr_err("Couldn't get CRAT table size from ACPI\n");
> +	}
> +
> +err:
> +	pr_info("Finished initializing topology ret=%d\n", ret);
> +	return ret;
> +}
> +
> +void kfd_topology_shutdown(void)
> +{
> +	kfd_topology_release_sysfs();
> +	kfd_release_live_view();
> +}
> +
> +static void kfd_debug_print_topology(void)
> +{
> +	struct kfd_topology_device *dev;
> +	uint32_t i = 0;
> +
> +	pr_info("DEBUG PRINT OF TOPOLOGY:");
> +	list_for_each_entry(dev, &topology_device_list, list) {
> +		pr_info("Node: %d\n", i);
> +		pr_info("\tGPU assigned: %s\n", (dev->gpu ? "yes" : "no"));
> +		pr_info("\tCPU count: %d\n", dev->node_props.cpu_cores_count);
> +		pr_info("\tSIMD count: %d", dev->node_props.simd_count);
> +		i++;
> +	}
> +}
> +
> +static uint32_t kfd_generate_gpu_id(struct kfd_dev *gpu)
> +{
> +	uint32_t hashout;
> +	uint32_t buf[7];
> +	int i;
> +
> +	if (!gpu)
> +		return 0;
> +
> +	buf[0] = gpu->pdev->devfn;
> +	buf[1] = gpu->pdev->subsystem_vendor;
> +	buf[2] = gpu->pdev->subsystem_device;
> +	buf[3] = gpu->pdev->device;
> +	buf[4] = gpu->pdev->bus->number;
> +	buf[5] = (uint32_t)(kfd2kgd->get_vmem_size(gpu->kgd) & 0xffffffff);
> +	buf[6] = (uint32_t)(kfd2kgd->get_vmem_size(gpu->kgd) >> 32);
> +
> +	for (i = 0, hashout = 0; i < 7; i++)
> +		hashout ^= hash_32(buf[i], KFD_GPU_ID_HASH_WIDTH);
> +
> +	return hashout;
> +}
> +
> +static struct kfd_topology_device *kfd_assign_gpu(struct kfd_dev *gpu)
> +{
> +	struct kfd_topology_device *dev;
> +	struct kfd_topology_device *out_dev = 0;
> +
> +	BUG_ON(!gpu);
> +
> +	list_for_each_entry(dev, &topology_device_list, list)
> +		if (dev->gpu == 0 && dev->node_props.simd_count > 0) {
> +			dev->gpu = gpu;
> +			out_dev = dev;
> +			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
> +	 */
> +}
> +
> +int kfd_topology_add_device(struct kfd_dev *gpu)
> +{
> +	uint32_t gpu_id;
> +	struct kfd_topology_device *dev;
> +	int res;
> +
> +	BUG_ON(!gpu);
> +
> +	gpu_id = kfd_generate_gpu_id(gpu);
> +
> +	pr_info("Adding new GPU (ID: 0x%x) to topology\n", gpu_id);
> +
> +	down_write(&topology_lock);
> +	/*
> +	 * Try to assign the GPU to existing topology device (generated from
> +	 * CRAT table
> +	 */
> +	dev = kfd_assign_gpu(gpu);
> +	if (!dev) {
> +		pr_info("GPU was not found in the current topology. Extending.\n");
> +		kfd_debug_print_topology();
> +		dev = kfd_create_topology_device();
> +		if (!dev) {
> +			res = -ENOMEM;
> +			goto err;
> +		}
> +		dev->gpu = gpu;
> +
> +		/*
> +		 * TODO: Make a call to retrieve topology information from the
> +		 * GPU vBIOS
> +		 */
> +
> +		/*
> +		 * Update the SYSFS tree, since we added another topology device
> +		 */
> +		if (kfd_topology_update_sysfs() < 0)
> +			kfd_topology_release_sysfs();
> +
> +	}
> +
> +	dev->gpu_id = gpu_id;
> +	gpu->id = gpu_id;
> +	dev->node_props.vendor_id = gpu->pdev->vendor;
> +	dev->node_props.device_id = gpu->pdev->device;
> +	dev->node_props.location_id = (gpu->pdev->bus->number << 24) +
> +			(gpu->pdev->devfn & 0xffffff);
> +	/*
> +	 * TODO: Retrieve max engine clock values from KGD
> +	 */
> +
> +	res = 0;
> +
> +err:
> +	up_write(&topology_lock);
> +
> +	if (res == 0)
> +		kfd_notify_gpu_change(gpu_id, 1);
> +
> +	return res;
> +}
> +
> +int kfd_topology_remove_device(struct kfd_dev *gpu)
> +{
> +	struct kfd_topology_device *dev;
> +	uint32_t gpu_id;
> +	int res = -ENODEV;
> +
> +	BUG_ON(!gpu);
> +
> +	down_write(&topology_lock);
> +
> +	list_for_each_entry(dev, &topology_device_list, list)
> +		if (dev->gpu == gpu) {
> +			gpu_id = dev->gpu_id;
> +			kfd_remove_sysfs_node_entry(dev);
> +			kfd_release_topology_device(dev);
> +			res = 0;
> +			if (kfd_topology_update_sysfs() < 0)
> +				kfd_topology_release_sysfs();
> +			break;
> +		}
> +
> +	up_write(&topology_lock);
> +
> +	if (res == 0)
> +		kfd_notify_gpu_change(gpu_id, 0);
> +
> +	return res;
> +}

I am not convince that sysfs is the right place to expose this.
I need to think on that a bit.

> diff --git a/drivers/gpu/hsa/radeon/kfd_topology.h b/drivers/gpu/hsa/radeon/kfd_topology.h
> new file mode 100644
> index 0000000..989624b
> --- /dev/null
> +++ b/drivers/gpu/hsa/radeon/kfd_topology.h
> @@ -0,0 +1,168 @@
> +/*
> + * Copyright 2014 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.
> + */
> +
> +#ifndef __KFD_TOPOLOGY_H__
> +#define __KFD_TOPOLOGY_H__
> +
> +#include <linux/types.h>
> +#include <linux/list.h>
> +#include "kfd_priv.h"
> +
> +#define KFD_TOPOLOGY_PUBLIC_NAME_SIZE 128
> +
> +#define HSA_CAP_HOT_PLUGGABLE			0x00000001
> +#define HSA_CAP_ATS_PRESENT			0x00000002
> +#define HSA_CAP_SHARED_WITH_GRAPHICS		0x00000004
> +#define HSA_CAP_QUEUE_SIZE_POW2			0x00000008
> +#define HSA_CAP_QUEUE_SIZE_32BIT		0x00000010
> +#define HSA_CAP_QUEUE_IDLE_EVENT		0x00000020
> +#define HSA_CAP_VA_LIMIT			0x00000040
> +#define HSA_CAP_WATCH_POINTS_SUPPORTED		0x00000080
> +#define HSA_CAP_WATCH_POINTS_TOTALBITS_MASK	0x00000f00
> +#define HSA_CAP_WATCH_POINTS_TOTALBITS_SHIFT	8
> +#define HSA_CAP_RESERVED			0xfffff000
> +
> +struct kfd_node_properties {
> +	uint32_t cpu_cores_count;
> +	uint32_t simd_count;
> +	uint32_t mem_banks_count;
> +	uint32_t caches_count;
> +	uint32_t io_links_count;
> +	uint32_t cpu_core_id_base;
> +	uint32_t simd_id_base;
> +	uint32_t capability;
> +	uint32_t max_waves_per_simd;
> +	uint32_t lds_size_in_kb;
> +	uint32_t gds_size_in_kb;
> +	uint32_t wave_front_size;
> +	uint32_t array_count;
> +	uint32_t simd_arrays_per_engine;
> +	uint32_t cu_per_simd_array;
> +	uint32_t simd_per_cu;
> +	uint32_t max_slots_scratch_cu;
> +	uint32_t engine_id;
> +	uint32_t vendor_id;
> +	uint32_t device_id;
> +	uint32_t location_id;
> +	uint32_t max_engine_clk_fcompute;
> +	uint32_t max_engine_clk_ccompute;
> +	uint16_t marketing_name[KFD_TOPOLOGY_PUBLIC_NAME_SIZE];
> +};
> +
> +#define HSA_MEM_HEAP_TYPE_SYSTEM	0
> +#define HSA_MEM_HEAP_TYPE_FB_PUBLIC	1
> +#define HSA_MEM_HEAP_TYPE_FB_PRIVATE	2
> +#define HSA_MEM_HEAP_TYPE_GPU_GDS	3
> +#define HSA_MEM_HEAP_TYPE_GPU_LDS	4
> +#define HSA_MEM_HEAP_TYPE_GPU_SCRATCH	5
> +
> +#define HSA_MEM_FLAGS_HOT_PLUGGABLE	0x00000001
> +#define HSA_MEM_FLAGS_NON_VOLATILE	0x00000002
> +#define HSA_MEM_FLAGS_RESERVED		0xfffffffc
> +
> +struct kfd_mem_properties {
> +	struct list_head	list;
> +	uint32_t		heap_type;
> +	uint64_t		size_in_bytes;
> +	uint32_t		flags;
> +	uint32_t		width;
> +	uint32_t		mem_clk_max;
> +	struct kobject		*kobj;
> +	struct attribute	attr;
> +};
> +
> +#define KFD_TOPOLOGY_CPU_SIBLINGS 256
> +
> +#define HSA_CACHE_TYPE_DATA		0x00000001
> +#define HSA_CACHE_TYPE_INSTRUCTION	0x00000002
> +#define HSA_CACHE_TYPE_CPU		0x00000004
> +#define HSA_CACHE_TYPE_HSACU		0x00000008
> +#define HSA_CACHE_TYPE_RESERVED		0xfffffff0
> +
> +struct kfd_cache_properties {
> +	struct list_head	list;
> +	uint32_t		processor_id_low;
> +	uint32_t		cache_level;
> +	uint32_t		cache_size;
> +	uint32_t		cacheline_size;
> +	uint32_t		cachelines_per_tag;
> +	uint32_t		cache_assoc;
> +	uint32_t		cache_latency;
> +	uint32_t		cache_type;
> +	uint8_t			sibling_map[KFD_TOPOLOGY_CPU_SIBLINGS];
> +	struct kobject		*kobj;
> +	struct attribute	attr;
> +};
> +
> +struct kfd_iolink_properties {
> +	struct list_head	list;
> +	uint32_t		iolink_type;
> +	uint32_t		ver_maj;
> +	uint32_t		ver_min;
> +	uint32_t		node_from;
> +	uint32_t		node_to;
> +	uint32_t		weight;
> +	uint32_t		min_latency;
> +	uint32_t		max_latency;
> +	uint32_t		min_bandwidth;
> +	uint32_t		max_bandwidth;
> +	uint32_t		rec_transfer_size;
> +	uint32_t		flags;
> +	struct kobject		*kobj;
> +	struct attribute	attr;
> +};
> +
> +struct kfd_topology_device {
> +	struct list_head		list;
> +	uint32_t			gpu_id;
> +	struct kfd_node_properties	node_props;
> +	uint32_t			mem_bank_count;
> +	struct list_head		mem_props;
> +	uint32_t			cache_count;
> +	struct list_head		cache_props;
> +	uint32_t			io_link_count;
> +	struct list_head		io_link_props;
> +	struct kfd_dev			*gpu;
> +	struct kobject			*kobj_node;
> +	struct kobject			*kobj_mem;
> +	struct kobject			*kobj_cache;
> +	struct kobject			*kobj_iolink;
> +	struct attribute		attr_gpuid;
> +	struct attribute		attr_name;
> +	struct attribute		attr_props;
> +};
> +
> +struct kfd_system_properties {
> +	uint32_t		num_devices;     /* Number of H-NUMA nodes */
> +	uint32_t		generation_count;
> +	uint64_t		platform_oem;
> +	uint64_t		platform_id;
> +	uint64_t		platform_rev;
> +	struct kobject		*kobj_topology;
> +	struct kobject		*kobj_nodes;
> +	struct attribute	attr_genid;
> +	struct attribute	attr_props;
> +};
> +
> +
> +
> +#endif /* __KFD_TOPOLOGY_H__ */
> -- 
> 1.9.1
> 


More information about the dri-devel mailing list