2021-05-14 19:22:59 +02:00
|
|
|
package collectors
|
|
|
|
|
|
|
|
import (
|
2021-11-25 15:11:39 +01:00
|
|
|
"encoding/json"
|
2021-05-14 19:22:59 +02:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"time"
|
2022-02-11 14:20:06 +01:00
|
|
|
|
|
|
|
cclog "github.com/ClusterCockpit/cc-metric-collector/internal/ccLogger"
|
2022-01-25 15:37:43 +01:00
|
|
|
lp "github.com/ClusterCockpit/cc-metric-collector/internal/ccMetric"
|
2022-01-21 14:35:52 +01:00
|
|
|
"github.com/NVIDIA/go-nvml/pkg/nvml"
|
2021-05-14 19:22:59 +02:00
|
|
|
)
|
|
|
|
|
2021-11-25 14:04:03 +01:00
|
|
|
type NvidiaCollectorConfig struct {
|
2022-01-20 12:38:52 +01:00
|
|
|
ExcludeMetrics []string `json:"exclude_metrics,omitempty"`
|
|
|
|
ExcludeDevices []string `json:"exclude_devices,omitempty"`
|
2022-02-11 14:20:06 +01:00
|
|
|
AddPciInfoTag bool `json:"add_pci_info_tag,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type NvidiaCollectorDevice struct {
|
|
|
|
device nvml.Device
|
|
|
|
excludeMetrics map[string]bool
|
|
|
|
tags map[string]string
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
|
|
|
|
2021-05-14 19:22:59 +02:00
|
|
|
type NvidiaCollector struct {
|
2022-01-25 15:37:43 +01:00
|
|
|
metricCollector
|
2021-05-14 19:22:59 +02:00
|
|
|
num_gpus int
|
2021-11-25 15:11:39 +01:00
|
|
|
config NvidiaCollectorConfig
|
2022-02-11 14:20:06 +01:00
|
|
|
gpus []NvidiaCollectorDevice
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2021-11-26 19:01:47 +01:00
|
|
|
func (m *NvidiaCollector) CatchPanic() {
|
2021-10-04 15:23:43 +02:00
|
|
|
if rerr := recover(); rerr != nil {
|
2021-11-26 19:01:47 +01:00
|
|
|
log.Print(rerr)
|
|
|
|
m.init = false
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
func (m *NvidiaCollector) Init(config json.RawMessage) error {
|
2021-11-25 15:11:39 +01:00
|
|
|
var err error
|
2021-05-14 19:22:59 +02:00
|
|
|
m.name = "NvidiaCollector"
|
2022-02-11 14:20:06 +01:00
|
|
|
m.config.AddPciInfoTag = false
|
2021-05-14 19:22:59 +02:00
|
|
|
m.setup()
|
2021-11-25 14:04:03 +01:00
|
|
|
if len(config) > 0 {
|
2021-11-25 15:11:39 +01:00
|
|
|
err = json.Unmarshal(config, &m.config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-25 14:04:03 +01:00
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
m.meta = map[string]string{
|
|
|
|
"source": m.name,
|
|
|
|
"group": "Nvidia",
|
|
|
|
}
|
|
|
|
|
2021-05-14 19:22:59 +02:00
|
|
|
m.num_gpus = 0
|
2021-10-04 15:23:43 +02:00
|
|
|
defer m.CatchPanic()
|
2022-02-15 09:23:57 +01:00
|
|
|
|
|
|
|
// Initialize NVIDIA Management Library (NVML)
|
2021-05-14 19:22:59 +02:00
|
|
|
ret := nvml.Init()
|
|
|
|
if ret != nvml.SUCCESS {
|
2021-11-25 14:04:03 +01:00
|
|
|
err = errors.New(nvml.ErrorString(ret))
|
2022-02-11 14:20:06 +01:00
|
|
|
cclog.ComponentError(m.name, "Unable to initialize NVML", err.Error())
|
2021-05-14 19:22:59 +02:00
|
|
|
return err
|
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
|
|
|
|
// Number of NVIDIA GPUs
|
2022-02-11 14:20:06 +01:00
|
|
|
num_gpus, ret := nvml.DeviceGetCount()
|
2021-05-14 19:22:59 +02:00
|
|
|
if ret != nvml.SUCCESS {
|
2021-11-25 14:04:03 +01:00
|
|
|
err = errors.New(nvml.ErrorString(ret))
|
2022-02-11 14:20:06 +01:00
|
|
|
cclog.ComponentError(m.name, "Unable to get device count", err.Error())
|
2021-05-14 19:22:59 +02:00
|
|
|
return err
|
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
|
|
|
|
// For all GPUs
|
2022-02-11 14:20:06 +01:00
|
|
|
m.gpus = make([]NvidiaCollectorDevice, num_gpus)
|
2022-02-15 09:23:57 +01:00
|
|
|
for i := 0; i < num_gpus; i++ {
|
|
|
|
g := &m.gpus[i]
|
|
|
|
|
|
|
|
// Skip excluded devices
|
2022-02-11 14:20:06 +01:00
|
|
|
str_i := fmt.Sprintf("%d", i)
|
|
|
|
if _, skip := stringArrayContains(m.config.ExcludeDevices, str_i); skip {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-02-15 09:23:57 +01:00
|
|
|
// Get device handle
|
2022-02-11 14:20:06 +01:00
|
|
|
device, ret := nvml.DeviceGetHandleByIndex(i)
|
|
|
|
if ret != nvml.SUCCESS {
|
|
|
|
err = errors.New(nvml.ErrorString(ret))
|
|
|
|
cclog.ComponentError(m.name, "Unable to get device at index", i, ":", err.Error())
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
g.device = device
|
2022-02-15 09:23:57 +01:00
|
|
|
|
|
|
|
// Add tags
|
|
|
|
g.tags = map[string]string{
|
|
|
|
"type": "accelerator",
|
|
|
|
"type-id": str_i,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add excluded metrics
|
2022-02-11 14:20:06 +01:00
|
|
|
g.excludeMetrics = map[string]bool{}
|
|
|
|
for _, e := range m.config.ExcludeMetrics {
|
|
|
|
g.excludeMetrics[e] = true
|
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
|
|
|
|
// Add PCI info as tag
|
2022-02-11 14:20:06 +01:00
|
|
|
if m.config.AddPciInfoTag {
|
2022-02-15 09:23:57 +01:00
|
|
|
pciInfo, ret := nvml.DeviceGetPciInfo(g.device)
|
2022-02-11 14:20:06 +01:00
|
|
|
if ret != nvml.SUCCESS {
|
|
|
|
err = errors.New(nvml.ErrorString(ret))
|
2022-02-15 09:23:57 +01:00
|
|
|
cclog.ComponentError(m.name, "Unable to get PCI info for device at index", i, ":", err.Error())
|
2022-02-11 14:20:06 +01:00
|
|
|
return err
|
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
g.tags["pci_identifier"] = fmt.Sprintf(
|
|
|
|
"%08X:%02X:%02X.0",
|
|
|
|
pciInfo.Domain,
|
|
|
|
pciInfo.Bus,
|
|
|
|
pciInfo.Device)
|
2022-02-11 14:20:06 +01:00
|
|
|
}
|
|
|
|
}
|
2022-02-15 09:23:57 +01:00
|
|
|
|
2021-10-04 15:23:43 +02:00
|
|
|
m.init = true
|
2021-05-14 19:22:59 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-25 15:37:43 +01:00
|
|
|
func (m *NvidiaCollector) Read(interval time.Duration, output chan lp.CCMetric) {
|
2021-11-25 15:11:39 +01:00
|
|
|
if !m.init {
|
|
|
|
return
|
|
|
|
}
|
2022-02-11 14:20:06 +01:00
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
for i := range m.gpus {
|
|
|
|
device := &m.gpus[i]
|
2022-02-11 14:20:06 +01:00
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_util"] || !device.excludeMetrics["nv_mem_util"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
util, ret := nvml.DeviceGetUtilizationRates(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_util"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
y, err := lp.New("nv_util", device.tags, m.meta, map[string]interface{}{"value": float64(util.Gpu)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
|
|
|
}
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_mem_util"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
y, err := lp.New("nv_mem_util", device.tags, m.meta, map[string]interface{}{"value": float64(util.Memory)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_mem_total"] || !device.excludeMetrics["nv_fb_memory"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
meminfo, ret := nvml.DeviceGetMemoryInfo(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_mem_total"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
t := float64(meminfo.Total) / (1024 * 1024)
|
|
|
|
y, err := lp.New("nv_mem_total", device.tags, m.meta, map[string]interface{}{"value": t}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
y.AddMeta("unit", "MByte")
|
|
|
|
output <- y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_fb_memory"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
f := float64(meminfo.Used) / (1024 * 1024)
|
|
|
|
y, err := lp.New("nv_fb_memory", device.tags, m.meta, map[string]interface{}{"value": f}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
y.AddMeta("unit", "MByte")
|
|
|
|
output <- y
|
|
|
|
}
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_temp"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
temp, ret := nvml.DeviceGetTemperature(device.device, nvml.TEMPERATURE_GPU)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_temp", device.tags, m.meta, map[string]interface{}{"value": float64(temp)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
y.AddMeta("unit", "degC")
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_fan"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
fan, ret := nvml.DeviceGetFanSpeed(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_fan", device.tags, m.meta, map[string]interface{}{"value": float64(fan)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2022-02-11 14:20:06 +01:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_ecc_mode"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
_, ecc_pend, ret := nvml.DeviceGetEccMode(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
var y lp.CCMetric
|
|
|
|
var err error
|
|
|
|
switch ecc_pend {
|
|
|
|
case nvml.FEATURE_DISABLED:
|
|
|
|
y, err = lp.New("nv_ecc_mode", device.tags, m.meta, map[string]interface{}{"value": string("OFF")}, time.Now())
|
|
|
|
case nvml.FEATURE_ENABLED:
|
|
|
|
y, err = lp.New("nv_ecc_mode", device.tags, m.meta, map[string]interface{}{"value": string("ON")}, time.Now())
|
|
|
|
default:
|
|
|
|
y, err = lp.New("nv_ecc_mode", device.tags, m.meta, map[string]interface{}{"value": string("UNKNOWN")}, time.Now())
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
|
|
|
} else if ret == nvml.ERROR_NOT_SUPPORTED {
|
|
|
|
y, err := lp.New("nv_ecc_mode", device.tags, m.meta, map[string]interface{}{"value": string("N/A")}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_perf_state"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
pstate, ret := nvml.DeviceGetPerformanceState(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_perf_state", device.tags, m.meta, map[string]interface{}{"value": fmt.Sprintf("P%d", int(pstate))}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_power_usage_report"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
power, ret := nvml.DeviceGetPowerUsage(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_power_usage_report", device.tags, m.meta, map[string]interface{}{"value": float64(power) / 1000}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_graphics_clock_report"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
gclk, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_GRAPHICS)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_graphics_clock_report", device.tags, m.meta, map[string]interface{}{"value": float64(gclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_sm_clock_report"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
smclk, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_SM)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_sm_clock_report", device.tags, m.meta, map[string]interface{}{"value": float64(smclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_mem_clock_report"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
memclk, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_MEM)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_mem_clock_report", device.tags, m.meta, map[string]interface{}{"value": float64(memclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_max_graphics_clock"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
max_gclk, ret := nvml.DeviceGetMaxClockInfo(device.device, nvml.CLOCK_GRAPHICS)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_max_graphics_clock", device.tags, m.meta, map[string]interface{}{"value": float64(max_gclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_max_sm_clock"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
max_smclk, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_SM)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_max_sm_clock", device.tags, m.meta, map[string]interface{}{"value": float64(max_smclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_max_mem_clock"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
max_memclk, ret := nvml.DeviceGetClockInfo(device.device, nvml.CLOCK_MEM)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_max_mem_clock", device.tags, m.meta, map[string]interface{}{"value": float64(max_memclk)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_ecc_db_error"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
ecc_db, ret := nvml.DeviceGetTotalEccErrors(device.device, 1, 1)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_ecc_db_error", device.tags, m.meta, map[string]interface{}{"value": float64(ecc_db)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_ecc_sb_error"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
ecc_sb, ret := nvml.DeviceGetTotalEccErrors(device.device, 0, 1)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_ecc_sb_error", device.tags, m.meta, map[string]interface{}{"value": float64(ecc_sb)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_power_man_limit"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
pwr_limit, ret := nvml.DeviceGetPowerManagementLimit(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_power_man_limit", device.tags, m.meta, map[string]interface{}{"value": float64(pwr_limit)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_encoder_util"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
enc_util, _, ret := nvml.DeviceGetEncoderUtilization(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_encoder_util", device.tags, m.meta, map[string]interface{}{"value": float64(enc_util)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:47:24 +01:00
|
|
|
if !device.excludeMetrics["nv_decoder_util"] {
|
2022-02-11 14:20:06 +01:00
|
|
|
dec_util, _, ret := nvml.DeviceGetDecoderUtilization(device.device)
|
|
|
|
if ret == nvml.SUCCESS {
|
|
|
|
y, err := lp.New("nv_decoder_util", device.tags, m.meta, map[string]interface{}{"value": float64(dec_util)}, time.Now())
|
|
|
|
if err == nil {
|
|
|
|
output <- y
|
|
|
|
}
|
2021-10-04 15:23:43 +02:00
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *NvidiaCollector) Close() {
|
2021-10-04 15:23:43 +02:00
|
|
|
if m.init {
|
|
|
|
nvml.Shutdown()
|
|
|
|
m.init = false
|
|
|
|
}
|
2021-05-14 19:22:59 +02:00
|
|
|
}
|