| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "runtime_probe/functions/gpu.h" |
| |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/containers/fixed_flat_set.h> |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <minigbm/minigbm_helpers.h> |
| |
| #include "runtime_probe/system/context.h" |
| #include "runtime_probe/utils/file_utils.h" |
| |
| namespace runtime_probe { |
| namespace { |
| |
| constexpr char kPCIDevicesPath[] = "sys/bus/pci/devices"; |
| constexpr auto kGPUFields = base::MakeFixedFlatSet<std::string_view>( |
| {"vendor", "device", "subsystem_vendor", "subsystem_device"}); |
| |
| } // namespace |
| |
| int GpuFunction::GbmDetectDeviceInfoPath(unsigned int detect_flags, |
| const char* dev_node, |
| ::GbmDeviceInfo* info) const { |
| return ::gbm_detect_device_info_path(detect_flags, dev_node, info); |
| } |
| |
| // If fails to call gbm library, this function will return true because: |
| // * If it is not work due to driver issue, it should be more likely a dGPU, |
| // because we have better iGPU support. |
| // * Probing additional devices should be better than dropping devices |
| // silently. |
| bool GpuFunction::IsDGPUDeviceByGBMLibrary( |
| const base::FilePath& sysfs_node) const { |
| base::FilePath drm_node_pattern = sysfs_node.Append("drm/renderD*"); |
| std::vector<base::FilePath> drm_nodes = Glob(drm_node_pattern); |
| for (const auto& node : drm_nodes) { |
| VLOG(1) << "Found drm node: " << node; |
| } |
| LOG_IF(ERROR, drm_nodes.size() >= 2) |
| << "Found multiple drm nodes. Use the first one and ignore others."; |
| if (drm_nodes.size() == 0) { |
| LOG(ERROR) << "Cannot find any drm node from " << sysfs_node |
| << ". Is the driver ready?"; |
| return true; |
| } |
| |
| ::GbmDeviceInfo info; |
| int ret = GbmDetectDeviceInfoPath(0, drm_nodes[0].value().c_str(), &info); |
| if (ret) { |
| LOG(ERROR) << "Cannot get gbm info from drm node " << drm_nodes[0] |
| << ", return " << ret; |
| return true; |
| } |
| VLOG(1) << "Drm node " << sysfs_node << " is discrete: " |
| << (info.dev_type_flags & GBM_DEV_TYPE_FLAG_DISCRETE); |
| return info.dev_type_flags & GBM_DEV_TYPE_FLAG_DISCRETE; |
| } |
| |
| bool GpuFunction::IsDGPUDevice(const base::FilePath& sysfs_node) const { |
| base::FilePath class_file = sysfs_node.Append("class"); |
| std::string class_value; |
| if (!ReadFileToString(class_file, &class_value)) { |
| return false; |
| } |
| // 0x03 is the class code of PCI display controllers. |
| // - 0x00 is the subclass code of VGA compatible controller. |
| // - 0x02 is the subclass code of 3D controller (for GPU without display |
| // attached.). |
| if (!base::StartsWith(class_value, "0x0300") && |
| !base::StartsWith(class_value, "0x0302")) { |
| return false; |
| } |
| |
| return IsDGPUDeviceByGBMLibrary(sysfs_node); |
| } |
| |
| GpuFunction::DataType GpuFunction::EvalImpl() const { |
| DataType results{}; |
| |
| base::FileEnumerator it( |
| Context::Get()->root_dir().Append(kPCIDevicesPath), false, |
| base::FileEnumerator::SHOW_SYM_LINKS | base::FileEnumerator::FILES | |
| base::FileEnumerator::DIRECTORIES); |
| for (auto path = it.Next(); !path.empty(); path = it.Next()) { |
| if (!IsDGPUDevice(path)) { |
| continue; |
| } |
| std::optional<base::Value> res = MapFilesToDict(path, kGPUFields); |
| if (res.has_value()) { |
| results.Append(std::move(res).value()); |
| } |
| } |
| |
| return results; |
| } |
| |
| } // namespace runtime_probe |