From c68f4528a2e984d044a5f6ad1c5eca7bba9b3b4c Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 13 Feb 2016 10:14:07 +0200 Subject: [PATCH 1/3] drm/amdkfd: Track when module's init is complete Current dependencies between amdkfd and radeon/amdgpu force the loading of amdkfd _before_ radeon and/or amdgpu are loaded. When all these kernel drivers are built as modules, this ordering is enforced by the kernel built-in mechanism of loading dependent modules. However, there is no such mechanism in case where all these drivers are compiled inside the kernel image (not as modules). The current way to enforce loading of amdkfd before radeon/amdgpu, is to put amdkfd before radeon/amdgpu in the drm Makefile, but that method is way too fragile. In addition, there is no kernel mechanism to check whether a kernel driver that is built inside the kernel image, has already been loaded. To solve this, this patch adds to kfd_module.c a new static variable, amdkfd_init_completed, that is set to 1 only when amdkfd's module initialization function has been completed (successfully). kgd2kfd_init(), which is the initialization function of the kgd-->kfd interface, and which is the first function in amdkfd called by radeon/amdgpu, will return successfully only if amdkfd_init_completed is equal 1. If amdkfd_init_completed is not equal to 1, kgd2kfd_init() will return -EPROBE_DEFER to signal radeon/amdgpu they need to defer their loading until amdkfd is loaded. Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 4 ++-- drivers/gpu/drm/amd/amdkfd/kfd_module.c | 15 ++++++++++++--- drivers/gpu/drm/amd/include/kgd_kfd_interface.h | 2 +- drivers/gpu/drm/radeon/radeon_kfd.c | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 84d68d658f8a..44ba8bed1b29 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -70,7 +70,7 @@ bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) return false; } - if (!kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { + if (kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { symbol_put(kgd2kfd_init); kfd2kgd = NULL; kgd2kfd = NULL; @@ -80,7 +80,7 @@ bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) return true; #elif defined(CONFIG_HSA_AMD) - if (!kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { + if (kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { kfd2kgd = NULL; kgd2kfd = NULL; return false; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c index ca8410e8683d..850a5623661f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c @@ -59,18 +59,23 @@ module_param(send_sigterm, int, 0444); MODULE_PARM_DESC(send_sigterm, "Send sigterm to HSA process on unhandled exception (0 = disable, 1 = enable)"); -bool kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f) +static int amdkfd_init_completed; + +int kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f) { + if (!amdkfd_init_completed) + return -EPROBE_DEFER; + /* * Only one interface version is supported, * no kfd/kgd version skew allowed. */ if (interface_version != KFD_INTERFACE_VERSION) - return false; + return -EINVAL; *g2f = &kgd2kfd; - return true; + return 0; } EXPORT_SYMBOL(kgd2kfd_init); @@ -111,6 +116,8 @@ static int __init kfd_module_init(void) kfd_process_create_wq(); + amdkfd_init_completed = 1; + dev_info(kfd_device, "Initialized module\n"); return 0; @@ -125,6 +132,8 @@ err_pasid: static void __exit kfd_module_exit(void) { + amdkfd_init_completed = 0; + kfd_process_destroy_wq(); kfd_topology_shutdown(); kfd_chardev_exit(); diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 888250b33ea8..a09d9f352871 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -221,7 +221,7 @@ struct kgd2kfd_calls { int (*resume)(struct kfd_dev *kfd); }; -bool kgd2kfd_init(unsigned interface_version, +int kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f); #endif /* KGD_KFD_INTERFACE_H_INCLUDED */ diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 9a4d69e59401..83c2704a7291 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -142,7 +142,7 @@ bool radeon_kfd_init(void) if (kgd2kfd_init_p == NULL) return false; - if (!kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { + if (kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { symbol_put(kgd2kfd_init); kgd2kfd = NULL; @@ -151,7 +151,7 @@ bool radeon_kfd_init(void) return true; #elif defined(CONFIG_HSA_AMD) - if (!kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { + if (kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { kgd2kfd = NULL; return false; From 412c8f7de011cb4a16d8a983607263da58517723 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 9 Feb 2016 13:30:04 +0200 Subject: [PATCH 2/3] drm/radeon: Return -EPROBE_DEFER when amdkfd not loaded radeon must load only after amdkfd's loading has been completed. If that is not enforced, then radeon's call into amdkfd's functions will cause a kernel BUG. When radeon and amdkfd are built as kernel modules, that rule is enforced by the kernel's modules loading mechanism. When radeon and amdkfd are built inside the kernel image, that rule is enforced by ordering in the drm Makefile (amdkfd before radeon). Instead of using drm Makefile ordering, we can now use deferred loading as amdkfd now returns -EPROBE_DEFER in kgd2kfd_init() when it is not yet loaded. This patch defers radeon loading by propagating -EPROBE_DEFER to the kernel's drivers loading infrastructure. That will put radeon into the pending drivers list (see description in dd.c). Once amdkfd is loaded, a call to kgd2kfd_init() will return successfully and radeon will be able to load. Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_drv.c | 10 ++++++++-- drivers/gpu/drm/radeon/radeon_kfd.c | 25 ++++++++++++------------- drivers/gpu/drm/radeon/radeon_kfd.h | 2 +- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index cad25557650f..ccd4ad4ee592 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -321,6 +321,14 @@ static int radeon_pci_probe(struct pci_dev *pdev, { int ret; + /* + * Initialize amdkfd before starting radeon. If it was not loaded yet, + * defer radeon probing + */ + ret = radeon_kfd_init(); + if (ret == -EPROBE_DEFER) + return ret; + /* * apple-gmux is needed on dual GPU MacBook Pro * to probe the panel if we're the inactive GPU. @@ -581,8 +589,6 @@ static int __init radeon_init(void) return -EINVAL; } - radeon_kfd_init(); - /* let modprobe override vga console setting */ return drm_pci_init(driver, pdriver); } diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 83c2704a7291..87a9ebb5f58f 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -132,35 +132,34 @@ static const struct kfd2kgd_calls kfd2kgd = { static const struct kgd2kfd_calls *kgd2kfd; -bool radeon_kfd_init(void) +int radeon_kfd_init(void) { + int ret; + #if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); + int (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); kgd2kfd_init_p = symbol_request(kgd2kfd_init); if (kgd2kfd_init_p == NULL) - return false; + return -ENOENT; - if (kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { + ret = kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) { symbol_put(kgd2kfd_init); kgd2kfd = NULL; - - return false; } - return true; #elif defined(CONFIG_HSA_AMD) - if (kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { + ret = kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) kgd2kfd = NULL; - return false; - } - - return true; #else - return false; + ret = -ENOENT; #endif + + return ret; } void radeon_kfd_fini(void) diff --git a/drivers/gpu/drm/radeon/radeon_kfd.h b/drivers/gpu/drm/radeon/radeon_kfd.h index 1103f9082f6b..9df1fea8e971 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.h +++ b/drivers/gpu/drm/radeon/radeon_kfd.h @@ -33,7 +33,7 @@ struct radeon_device; -bool radeon_kfd_init(void); +int radeon_kfd_init(void); void radeon_kfd_fini(void); void radeon_kfd_suspend(struct radeon_device *rdev); From efb1c6582e5cd291a6b9e6dde55fd31ce6f606a1 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 9 Feb 2016 13:30:12 +0200 Subject: [PATCH 3/3] drm/amdgpu: Return -EPROBE_DEFER when amdkfd not loaded amdgpu must load only after amdkfd's loading has been completed. If that is not enforced, then amdgpu's call into amdkfd's functions will cause a kernel BUG. When amdgpu and amdkfd are built as kernel modules, that rule is enforced by the kernel's modules loading mechanism. When amdgpu and amdkfd are built inside the kernel image, that rule is enforced by ordering in the drm Makefile (amdkfd before amdgpu). Instead of using drm Makefile ordering, we can now use deferred loading as amdkfd now returns -EPROBE_DEFER in kgd2kfd_init() when it is not yet loaded. This patch defers amdgpu loading by propagating -EPROBE_DEFER to the kernel's drivers loading infrastructure. That will put amdgpu into the pending drivers list (see description in dd.c). Once amdkfd is loaded, a call to kgd2kfd_init() will return successfully and amdgpu will be able to load. Signed-off-by: Oded Gabbay Reviewed-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 57 ++++++++-------------- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 10 +++- 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 44ba8bed1b29..32809f749903 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -30,25 +30,38 @@ const struct kfd2kgd_calls *kfd2kgd; const struct kgd2kfd_calls *kgd2kfd; bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); -bool amdgpu_amdkfd_init(void) +int amdgpu_amdkfd_init(void) { + int ret; + #if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); + int (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); kgd2kfd_init_p = symbol_request(kgd2kfd_init); if (kgd2kfd_init_p == NULL) - return false; + return -ENOENT; + + ret = kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) { + symbol_put(kgd2kfd_init); + kgd2kfd = NULL; + } + +#elif defined(CONFIG_HSA_AMD) + ret = kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) + kgd2kfd = NULL; + +#else + ret = -ENOENT; #endif - return true; + + return ret; } bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) { -#if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); -#endif - switch (rdev->asic_type) { #ifdef CONFIG_DRM_AMDGPU_CIK case CHIP_KAVERI: @@ -62,35 +75,7 @@ bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) return false; } -#if defined(CONFIG_HSA_AMD_MODULE) - kgd2kfd_init_p = symbol_request(kgd2kfd_init); - - if (kgd2kfd_init_p == NULL) { - kfd2kgd = NULL; - return false; - } - - if (kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { - symbol_put(kgd2kfd_init); - kfd2kgd = NULL; - kgd2kfd = NULL; - - return false; - } - return true; -#elif defined(CONFIG_HSA_AMD) - if (kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { - kfd2kgd = NULL; - kgd2kfd = NULL; - return false; - } - - return true; -#else - kfd2kgd = NULL; - return false; -#endif } void amdgpu_amdkfd_fini(void) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index a8be765542e6..de530f68d4e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -36,7 +36,7 @@ struct kgd_mem { void *cpu_ptr; }; -bool amdgpu_amdkfd_init(void); +int amdgpu_amdkfd_init(void); void amdgpu_amdkfd_fini(void); bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index ce79a8b605a0..01b4fd6115c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -310,6 +310,14 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, return -ENODEV; } + /* + * Initialize amdkfd before starting radeon. If it was not loaded yet, + * defer radeon probing + */ + ret = amdgpu_amdkfd_init(); + if (ret == -EPROBE_DEFER) + return ret; + /* Get rid of things like offb */ ret = amdgpu_kick_out_firmware_fb(pdev); if (ret) @@ -552,8 +560,6 @@ static int __init amdgpu_init(void) driver->num_ioctls = amdgpu_max_kms_ioctl; amdgpu_register_atpx_handler(); - amdgpu_amdkfd_init(); - /* let modprobe override vga console setting */ return drm_pci_init(driver, pdriver); }