From 732b0e4115cf882d5c17479d36b9b37fa8fcdce1 Mon Sep 17 00:00:00 2001
From: psychocrypt <psychocryptHPC@gmail.com>
Date: Thu, 11 Oct 2018 10:30:51 +0200
Subject: [PATCH] NVIDIA: support for multiple CUDA libs

Allow to ship the miner with multiple cuda backends those depends on different driver versions.
This will allow to support Turing/Volta and old Fermi GPU within one release.

- add support to search for the first working CUDA backend
- add some more messages to support better debugging (if a user has some issues)
---
 xmrstak/backend/backendConnector.cpp | 46 +++++++++++++++++++++++-----
 xmrstak/backend/nvidia/minethd.cpp   |  4 +++
 xmrstak/backend/plugin.hpp           | 31 +++++++++++--------
 3 files changed, 62 insertions(+), 19 deletions(-)

diff --git a/xmrstak/backend/backendConnector.cpp b/xmrstak/backend/backendConnector.cpp
index 525413f..92bb015 100644
--- a/xmrstak/backend/backendConnector.cpp
+++ b/xmrstak/backend/backendConnector.cpp
@@ -63,10 +63,35 @@ std::vector<iBackend*>* BackendConnector::thread_starter(miner_work& pWork)
 #ifndef CONF_NO_CUDA
 	if(params::inst().useNVIDIA)
 	{
-		plugin nvidiaplugin("NVIDIA", "xmrstak_cuda_backend");
-		std::vector<iBackend*>* nvidiaThreads = nvidiaplugin.startBackend(static_cast<uint32_t>(pvThreads->size()), pWork, environment::inst());
-		pvThreads->insert(std::end(*pvThreads), std::begin(*nvidiaThreads), std::end(*nvidiaThreads));
-		if(nvidiaThreads->size() == 0)
+		plugin nvidiaplugin;
+		std::vector<iBackend*>* nvidiaThreads;
+		std::vector<std::string> libNames = {"xmrstak_cuda_backend_cuda10_0", "xmrstak_cuda_backend_cuda9_2", "xmrstak_cuda_backend"};
+		size_t numWorkers = 0u;
+
+		for( const auto & name : libNames)
+		{
+			printer::inst()->print_msg(L0, "NVIDIA: try to load library '%s'", name.c_str());
+			nvidiaplugin.load("NVIDIA", name);
+			std::vector<iBackend*>* nvidiaThreads = nvidiaplugin.startBackend(static_cast<uint32_t>(pvThreads->size()), pWork, environment::inst());
+			if(nvidiaThreads != nullptr)
+			{
+				pvThreads->insert(std::end(*pvThreads), std::begin(*nvidiaThreads), std::end(*nvidiaThreads));
+				numWorkers = nvidiaThreads->size();
+				delete nvidiaThreads;
+			}
+			else
+			{
+				// remove the plugin if we have found no GPUs
+				nvidiaplugin.unload();
+			}
+			// we found at leat one working GPU
+			if(numWorkers != 0)
+			{
+				printer::inst()->print_msg(L0, "NVIDIA: use library '%s'", name.c_str());
+				break;
+			}
+		}
+		if(numWorkers == 0)
 			printer::inst()->print_msg(L0, "WARNING: backend NVIDIA disabled.");
 	}
 #endif
@@ -75,10 +100,17 @@ std::vector<iBackend*>* BackendConnector::thread_starter(miner_work& pWork)
 	if(params::inst().useAMD)
 	{
 		const std::string backendName = xmrstak::params::inst().openCLVendor;
-		plugin amdplugin(backendName, "xmrstak_opencl_backend");
+		plugin amdplugin;
+		amdplugin.load(backendName, "xmrstak_opencl_backend");
 		std::vector<iBackend*>* amdThreads = amdplugin.startBackend(static_cast<uint32_t>(pvThreads->size()), pWork, environment::inst());
-		pvThreads->insert(std::end(*pvThreads), std::begin(*amdThreads), std::end(*amdThreads));
-		if(amdThreads->size() == 0)
+		size_t numWorkers = 0u;
+		if(amdThreads != nullptr)
+		{
+			pvThreads->insert(std::end(*pvThreads), std::begin(*amdThreads), std::end(*amdThreads));
+			numWorkers = amdThreads->size();
+			delete amdThreads;
+		}
+		if(numWorkers == 0)
 			printer::inst()->print_msg(L0, "WARNING: backend %s (OpenCL) disabled.", backendName.c_str());
 	}
 #endif
diff --git a/xmrstak/backend/nvidia/minethd.cpp b/xmrstak/backend/nvidia/minethd.cpp
index e82ec91..6460628 100644
--- a/xmrstak/backend/nvidia/minethd.cpp
+++ b/xmrstak/backend/nvidia/minethd.cpp
@@ -165,6 +165,10 @@ std::vector<iBackend*>* minethd::thread_starter(uint32_t threadOffset, miner_wor
 		std::cout<<"WARNING: NVIDIA no device found"<<std::endl;
 		return pvThreads;
 	}
+	else
+	{
+		std::cout<<"NVIDIA: found "<< deviceCount <<" potential device's"<<std::endl;
+	}
 
 	size_t i, n = jconf::inst()->GetGPUThreadCount();
 	pvThreads->reserve(n);
diff --git a/xmrstak/backend/plugin.hpp b/xmrstak/backend/plugin.hpp
index 1811af2..5c7dfe1 100644
--- a/xmrstak/backend/plugin.hpp
+++ b/xmrstak/backend/plugin.hpp
@@ -27,8 +27,11 @@ namespace xmrstak
 struct plugin
 {
 
-	plugin(const std::string backendName, const std::string libName) : fn_startBackend(nullptr), m_backendName(backendName)
+	plugin() = default;
+
+	void load(const std::string backendName, const std::string libName)
 	{
+		m_backendName = backendName;
 #ifdef WIN32
 		libBackend = LoadLibrary(TEXT((libName + ".dll").c_str()));
 		if(!libBackend)
@@ -81,32 +84,36 @@ struct plugin
 		if(fn_startBackend == nullptr)
 		{
 			std::vector<iBackend*>* pvThreads = new std::vector<iBackend*>();
-			std::cerr << "WARNING: " << m_backendName << " Backend disabled"<< std::endl;
 			return pvThreads;
 		}
 
 		return fn_startBackend(threadOffset, pWork, env);
 	}
 
+	void unload()
+	{
+		if(libBackend)
+		{
+#ifdef WIN32
+			FreeLibrary(libBackend);
+#else
+			dlclose(libBackend);
+#endif
+		}
+		fn_startBackend = nullptr;
+	}
+
 	std::string m_backendName;
 
 	typedef std::vector<iBackend*>* (*startBackend_t)(uint32_t threadOffset, miner_work& pWork, environment& env);
 
-	startBackend_t fn_startBackend;
+	startBackend_t fn_startBackend = nullptr;
 
 #ifdef WIN32
 	HINSTANCE libBackend;
 #else
-	void *libBackend;
-#endif
-
-/* \todo add unload to destructor and change usage of plugin that libs kept open until the miner ends
-#ifdef WIN32
-	FreeLibrary(libBackend);
-#else
-	dlclose(libBackend);
+	void *libBackend = nullptr;
 #endif
- * */
 };
 
 } // namespace xmrstak
-- 
GitLab