github.com/google/cadvisor@v0.49.1/nvm/machine_libipmctl.go (about)

     1  //go:build libipmctl && cgo
     2  // +build libipmctl,cgo
     3  
     4  // Copyright 2020 Google Inc. All Rights Reserved.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package nvm
    19  
    20  // #cgo pkg-config: libipmctl
    21  // #include <nvm_management.h>
    22  import "C"
    23  import (
    24  	"fmt"
    25  	"sync"
    26  
    27  	info "github.com/google/cadvisor/info/v1"
    28  
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  var (
    33  	isNVMLibInitialized = false
    34  	nvmLibMutex         = sync.Mutex{}
    35  )
    36  
    37  func init() {
    38  	nvmLibMutex.Lock()
    39  	defer nvmLibMutex.Unlock()
    40  	cErr := C.nvm_init()
    41  	if cErr != C.NVM_SUCCESS {
    42  		// Unfortunately klog does not seem to work here. I believe it's better to
    43  		// output information using fmt rather then let it disappear silently.
    44  		fmt.Printf("libipmctl initialization failed with status %d", cErr)
    45  		return
    46  	}
    47  	isNVMLibInitialized = true
    48  }
    49  
    50  // getAvgPowerBudget retrieves configured power budget
    51  // (in watts) for NVM devices. When libipmctl is not available
    52  // zero is returned.
    53  func getAvgPowerBudget() (uint, error) {
    54  	// Get number of devices on the platform
    55  	// see: https://github.com/intel/ipmctl/blob/v01.00.00.3497/src/os/nvm_api/nvm_management.h#L1478
    56  	count := C.uint(0)
    57  	err := C.nvm_get_number_of_devices(&count)
    58  	if err != C.NVM_SUCCESS {
    59  		klog.Warningf("Unable to get number of NVM devices. Status code: %d", err)
    60  		return uint(0), fmt.Errorf("Unable to get number of NVM devices. Status code: %d", err)
    61  	}
    62  
    63  	if count == 0 {
    64  		klog.V(4).Infof("There are no NVM devices.")
    65  		return uint(0), nil
    66  	}
    67  
    68  	// Load basic device information for all the devices
    69  	// to obtain UID of the first one.
    70  	devices := make([]C.struct_device_discovery, count)
    71  	err = C.nvm_get_devices(&devices[0], C.uchar(count))
    72  	if err != C.NVM_SUCCESS {
    73  		klog.Warningf("Unable to get all NVM devices. Status code: %d", err)
    74  		return uint(0), fmt.Errorf("Unable to get all NVM devices. Status code: %d", err)
    75  	}
    76  
    77  	// Power budget is same for all the devices
    78  	// so we can rely on any of them.
    79  	device := C.struct_device_details{}
    80  	err = C.nvm_get_device_details(&devices[0].uid[0], &device)
    81  	if err != C.NVM_SUCCESS {
    82  		uid := C.GoString(&devices[0].uid[0])
    83  		klog.Warningf("Unable to get details of NVM device %q. Status code: %d", uid, err)
    84  		return uint(0), fmt.Errorf("Unable to get details of NVM device %q. Status code: %d", uid, err)
    85  	}
    86  
    87  	return uint(device.avg_power_budget / 1000), nil
    88  }
    89  
    90  // getCapacities retrieves the total NVM capacity in bytes for memory mode and app direct mode
    91  func getCapacities() (uint64, uint64, error) {
    92  	caps := C.struct_device_capacities{}
    93  	err := C.nvm_get_nvm_capacities(&caps)
    94  	if err != C.NVM_SUCCESS {
    95  		klog.Warningf("Unable to get NVM capacity. Status code: %d", err)
    96  		return uint64(0), uint64(0), fmt.Errorf("Unable to get NVM capacity. Status code: %d", err)
    97  	}
    98  	return uint64(caps.memory_capacity), uint64(caps.app_direct_capacity), nil
    99  }
   100  
   101  // GetInfo returns information specific for non-volatile memory modules
   102  func GetInfo() (info.NVMInfo, error) {
   103  	nvmLibMutex.Lock()
   104  	defer nvmLibMutex.Unlock()
   105  
   106  	nvmInfo := info.NVMInfo{}
   107  	if !isNVMLibInitialized {
   108  		klog.V(1).Info("libipmctl has not been initialized. NVM information will not be available")
   109  		return nvmInfo, nil
   110  	}
   111  
   112  	var err error
   113  	nvmInfo.MemoryModeCapacity, nvmInfo.AppDirectModeCapacity, err = getCapacities()
   114  	if err != nil {
   115  		return info.NVMInfo{}, fmt.Errorf("Unable to get NVM capacities, err: %s", err)
   116  	}
   117  
   118  	nvmInfo.AvgPowerBudget, err = getAvgPowerBudget()
   119  	if err != nil {
   120  		return info.NVMInfo{}, fmt.Errorf("Unable to get NVM average power budget, err: %s", err)
   121  	}
   122  	return nvmInfo, nil
   123  }
   124  
   125  // Finalize un-initializes libipmctl. See https://github.com/google/cadvisor/issues/2457.
   126  func Finalize() {
   127  	nvmLibMutex.Lock()
   128  	defer nvmLibMutex.Unlock()
   129  
   130  	klog.V(1).Info("Attempting to un-initialize libipmctl")
   131  	if !isNVMLibInitialized {
   132  		klog.V(1).Info("libipmctl has not been initialized; not un-initializing.")
   133  		return
   134  	}
   135  
   136  	C.nvm_uninit()
   137  	isNVMLibInitialized = false
   138  }