isolated_device.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package isolated_device
  15. import (
  16. "fmt"
  17. "regexp"
  18. "strings"
  19. "time"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/log"
  22. "yunion.io/x/pkg/errors"
  23. api "yunion.io/x/onecloud/pkg/apis/compute"
  24. "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  28. "yunion.io/x/onecloud/pkg/util/fileutils2"
  29. "yunion.io/x/onecloud/pkg/util/procutils"
  30. )
  31. const (
  32. RESOURCE = "isolated_devices"
  33. )
  34. type CloudDeviceInfo struct {
  35. Id string `json:"id"`
  36. GuestId string `json:"guest_id"`
  37. HostId string `json:"host_id"`
  38. DevType string `json:"dev_type"`
  39. VendorDeviceId string `json:"vendor_device_id"`
  40. Addr string `json:"addr"`
  41. DetectedOnHost bool `json:"detected_on_host"`
  42. MdevId string `json:"mdev_id"`
  43. Model string `json:"model"`
  44. WireId string `json:"wire_id"`
  45. OvsOffloadInterface string `json:"ovs_offload_interface"`
  46. IsInfinibandNic bool `json:"is_infiniband_nic"`
  47. NvmeSizeMB int `json:"nvme_size_mb"`
  48. DevicePath string `json:"device_path"`
  49. CardPath string `json:"card_path"`
  50. RenderPath string `json:"render_path"`
  51. Index int `json:"index"`
  52. DeviceMinor int `json:"device_minor"`
  53. MpsMemoryLimit int `json:"mps_memory_limit"`
  54. MpsMemoryTotal int `json:"mps_memory_total"`
  55. MpsThreadPercentage int `json:"mps_thread_percentage"`
  56. NumaNode int `json:"numa_node"`
  57. PcieInfo *api.IsolatedDevicePCIEInfo `json:"pcie_info"`
  58. // The frame rate limiter (FRL) configuration in frames per second
  59. FRL string `json:"frl"`
  60. // The frame buffer size in Mbytes
  61. Framebuffer string `json:"framebuffer"`
  62. // The maximum resolution per display head, eg: 5120x2880
  63. MaxResolution string `json:"max_resolution"`
  64. // The maximum number of virtual display heads that the vGPU type supports
  65. // In computer graphics and display technology, the term "head" is commonly used to
  66. // describe the physical interface of a display device or display output.
  67. // It refers to a connection point on the monitor, such as HDMI, DisplayPort, or VGA interface.
  68. NumHeads string `json:"num_heads"`
  69. // The maximum number of vGPU instances per physical GPU
  70. MaxInstance string `json:"max_instance"`
  71. }
  72. type IHost interface {
  73. GetHostId() string
  74. GetSession() *mcclient.ClientSession
  75. IsContainerHost() bool
  76. AppendHostError(content string)
  77. AppendError(content, objType, id, name string)
  78. GetContainerDeviceConfigurationFilePath() string
  79. }
  80. type HotPlugOption struct {
  81. Device string
  82. Options map[string]string
  83. }
  84. type HotUnplugOption struct {
  85. Id string
  86. }
  87. type IDevice interface {
  88. String() string
  89. GetCloudId() string
  90. GetHostId() string
  91. SetHostId(hId string)
  92. GetGuestId() string
  93. GetWireId() string
  94. IsInfinibandNic() bool
  95. GetOvsOffloadInterfaceName() string
  96. GetVendorDeviceId() string
  97. GetAddr() string
  98. GetDeviceType() string
  99. GetModelName() string
  100. CustomProbe(idx int) error
  101. SetDeviceInfo(info CloudDeviceInfo)
  102. DetectByAddr() error
  103. GetPassthroughOptions() map[string]string
  104. GetPassthroughCmd(index int) string
  105. GetIOMMUGroupDeviceCmd() string
  106. GetIOMMUGroupRestAddrs() []string
  107. GetVGACmd() string
  108. GetCPUCmd() string
  109. GetQemuId() string
  110. GetNumaNode() (int, error)
  111. // sriov nic
  112. GetPfName() string
  113. GetVirtfn() int
  114. // NVMe disk
  115. GetNVMESizeMB() int
  116. // legacy nvidia vgpu
  117. GetMdevId() string
  118. GetNVIDIAVgpuProfile() map[string]string
  119. GetHotPlugOptions(isolatedDev *desc.SGuestIsolatedDevice, guestDesc *desc.SGuestDesc) ([]*HotPlugOption, error)
  120. GetHotUnplugOptions(isolatedDev *desc.SGuestIsolatedDevice) ([]*HotUnplugOption, error)
  121. // Get extra PCIE information
  122. GetPCIEInfo() *api.IsolatedDevicePCIEInfo
  123. GetDevicePath() string
  124. GetCardPath() string
  125. GetRenderPath() string
  126. GetIndex() int
  127. GetDeviceMinor() int
  128. // mps infos
  129. GetNvidiaMpsMemoryLimit() int
  130. GetNvidiaMpsMemoryTotal() int
  131. GetNvidiaMpsThreadPercentage() int
  132. }
  133. type IsolatedDeviceManager interface {
  134. GetDevices() []IDevice
  135. GetDeviceByIdent(vendorDevId, addr, mdevId string) IDevice
  136. GetDeviceByAddr(addr string) IDevice
  137. ProbePCIDevices(skipGPUs, skipUSBs, skipCustomDevs bool, sriovNics, ovsOffloadNics []HostNic, nvmePciDisks, amdVgpuPFs, nvidiaVgpuPFs []string, enableCudaMps, enableContainerNPU, enableWhitelist bool)
  138. StartDetachTask()
  139. BatchCustomProbe()
  140. AppendDetachedDevice(dev *CloudDeviceInfo)
  141. GetQemuParams(devAddrs []string) *QemuParams
  142. CheckDevIsNeedUpdate(dev IDevice, devInfo *CloudDeviceInfo) bool
  143. }
  144. type isolatedDeviceManager struct {
  145. host IHost
  146. devices []IDevice
  147. DetachedDevices []*CloudDeviceInfo
  148. }
  149. func NewManager(host IHost) IsolatedDeviceManager {
  150. man := &isolatedDeviceManager{
  151. host: host,
  152. devices: make([]IDevice, 0),
  153. DetachedDevices: make([]*CloudDeviceInfo, 0),
  154. }
  155. // Do probe later - Qiu Jian
  156. return man
  157. }
  158. func (man *isolatedDeviceManager) GetDevices() []IDevice {
  159. return man.devices
  160. }
  161. func (man *isolatedDeviceManager) getContainerDeviceConfiguration() (*ContainerDeviceConfiguration, error) {
  162. fp := man.host.GetContainerDeviceConfigurationFilePath()
  163. if fp == "" {
  164. return nil, nil
  165. }
  166. content, err := procutils.NewRemoteCommandAsFarAsPossible("cat", fp).Output()
  167. if err != nil {
  168. return nil, errors.Wrapf(err, "Read container device configuration file %s", fp)
  169. }
  170. obj, err := jsonutils.ParseYAML(string(content))
  171. if err != nil {
  172. return nil, errors.Wrapf(err, "parse YAML content: %s", content)
  173. }
  174. cfg := new(ContainerDeviceConfiguration)
  175. if err := obj.Unmarshal(cfg); err != nil {
  176. return nil, errors.Wrapf(err, "unmarshal object to ContainerDeviceConfiguration")
  177. }
  178. return cfg, nil
  179. }
  180. func (man *isolatedDeviceManager) probeContainerDevices() {
  181. cfg, err := man.getContainerDeviceConfiguration()
  182. panicFatal := func(err error) {
  183. panic(err.Error())
  184. }
  185. if err != nil {
  186. panicFatal(errors.Wrap(err, "get container device configuration"))
  187. }
  188. if cfg == nil {
  189. return
  190. }
  191. for _, dev := range cfg.Devices {
  192. devMan, err := GetContainerDeviceManager(dev.Type)
  193. if err != nil {
  194. panicFatal(errors.Wrapf(err, "GetContainerDeviceManager by type %q", dev.Type))
  195. }
  196. iDevs, err := devMan.NewDevices(dev)
  197. if err != nil {
  198. panicFatal(errors.Wrapf(err, "NewDevices %#v", dev))
  199. }
  200. man.devices = append(man.devices, iDevs...)
  201. }
  202. }
  203. func (man *isolatedDeviceManager) probeContainerNvidiaGPUs(enableCudaMps bool) {
  204. devType := ContainerDeviceTypeNvidiaGpu
  205. if enableCudaMps {
  206. devType = ContainerDeviceTypeNvidiaMps
  207. }
  208. devman, err := GetContainerDeviceManager(devType)
  209. if err != nil {
  210. log.Errorf("no container device manager %s found", devType)
  211. return
  212. }
  213. devs, err := devman.ProbeDevices()
  214. if err != nil {
  215. log.Warningf("Probe container nvidia gpu devices: %v", err)
  216. return
  217. } else {
  218. for idx, dev := range devs {
  219. man.devices = append(man.devices, dev)
  220. log.Infof("Add Container nvidia GPU device: %d => %#v", idx, dev)
  221. }
  222. }
  223. }
  224. func (man *isolatedDeviceManager) probeContainerAscendNPUs(enable bool) {
  225. if !enable {
  226. return
  227. }
  228. devman, err := GetContainerDeviceManager(ContainerDeviceTypeAscendNpu)
  229. if err != nil {
  230. log.Errorf("no container device manager %s found", ContainerDeviceTypeAscendNpu)
  231. return
  232. }
  233. devs, err := devman.ProbeDevices()
  234. if err != nil {
  235. log.Warningf("Probe container Ascend npu devices: %v", err)
  236. return
  237. } else {
  238. for idx, dev := range devs {
  239. man.devices = append(man.devices, dev)
  240. log.Infof("Add Container Ascend npu device: %d => %#v", idx, dev)
  241. }
  242. }
  243. }
  244. func (man *isolatedDeviceManager) probeGPUS(skipGPUs bool, amdVgpuPFs, nvidiaVgpuPFs []string, enableWhitelist bool, whitelistModels []IsolatedDeviceModel) {
  245. if skipGPUs {
  246. return
  247. }
  248. filteredAddrs := []string{}
  249. filteredAddrs = append(filteredAddrs, amdVgpuPFs...)
  250. filteredAddrs = append(filteredAddrs, nvidiaVgpuPFs...)
  251. for i := 0; i < len(man.devices); i++ {
  252. filteredAddrs = append(filteredAddrs, man.devices[i].GetAddr())
  253. }
  254. gpus, err, warns := getPassthroughGPUs(filteredAddrs, enableWhitelist, whitelistModels)
  255. if err != nil {
  256. // ignore getPassthroughGPUS error on old machines without VGA devices
  257. log.Errorf("getPassthroughGPUS error: %v", err)
  258. man.host.AppendError(fmt.Sprintf("get passhtrough gpus %s", err.Error()), "isolated_devices", "", " ")
  259. } else {
  260. if len(warns) > 0 {
  261. for i := 0; i < len(warns); i++ {
  262. man.host.AppendError(warns[i].Error(), "isolated_devices", "", " ")
  263. }
  264. }
  265. for idx, gpu := range gpus {
  266. man.devices = append(man.devices, NewGPUHPCDevice(gpu))
  267. log.Infof("Add GPU device: %d => %#v", idx, gpu)
  268. }
  269. }
  270. }
  271. func (man *isolatedDeviceManager) probeCustomPCIDevs(skipCustomDevs bool, devModels []IsolatedDeviceModel, filterClassCodes []string) {
  272. if skipCustomDevs {
  273. return
  274. }
  275. for _, devModel := range devModels {
  276. devs, err := getPassthroughPCIDevs(devModel, filterClassCodes)
  277. if err != nil {
  278. log.Errorf("getPassthroughPCIDevs %v: %s", devModel, err)
  279. man.host.AppendError(fmt.Sprintf("get custom passthrough pci devices %s", err.Error()), "isolated_devices", "", "")
  280. continue
  281. }
  282. for i, dev := range devs {
  283. man.devices = append(man.devices, dev)
  284. log.Infof("Add general pci device: %d => %#v", i, dev)
  285. }
  286. }
  287. }
  288. func (man *isolatedDeviceManager) probeUSBs(skipUSBs bool) {
  289. if skipUSBs {
  290. return
  291. }
  292. usbs, err := getPassthroughUSBs()
  293. if err != nil {
  294. log.Errorf("getPassthroughUSBs: %v", err)
  295. man.host.AppendError(fmt.Sprintf("get passthrough usb devices %s", err.Error()), "isolated_devices", "", "")
  296. } else {
  297. for idx, usb := range usbs {
  298. man.devices = append(man.devices, usb)
  299. log.Infof("Add USB device: %d => %#v", idx, usb)
  300. }
  301. }
  302. }
  303. type HostNic struct {
  304. Bridge string
  305. Interface string
  306. Wire string
  307. }
  308. func (man *isolatedDeviceManager) probeSRIOVNics(sriovNics []HostNic) {
  309. if len(sriovNics) > 0 {
  310. nics, err := getSRIOVNics(sriovNics)
  311. if err != nil {
  312. log.Errorf("getSRIOVNics: %v", err)
  313. man.host.AppendError(fmt.Sprintf("get sriov nic devices %s", err.Error()), "isolated_devices", "", "")
  314. } else {
  315. for idx, nic := range nics {
  316. man.devices = append(man.devices, nic)
  317. log.Infof("Add sriov nic: %d => %#v", idx, nic)
  318. }
  319. }
  320. }
  321. }
  322. func (man *isolatedDeviceManager) probeOffloadNICS(ovsOffloadNics []HostNic) {
  323. if len(ovsOffloadNics) > 0 {
  324. nics, err := getOvsOffloadNics(ovsOffloadNics)
  325. if err != nil {
  326. log.Errorf("getOvsOffloadNics: %v", err)
  327. man.host.AppendError(fmt.Sprintf("get ovs offload nic devices %s", err.Error()), "isolated_devices", "", "")
  328. } else {
  329. for idx, nic := range nics {
  330. man.devices = append(man.devices, nic)
  331. log.Infof("Add sriov nic: %d => %#v", idx, nic)
  332. }
  333. }
  334. }
  335. }
  336. func (man *isolatedDeviceManager) probeNVMEDisks(nvmePciDisks []string) {
  337. if len(nvmePciDisks) > 0 {
  338. nvmeDisks, err := getPassthroughNVMEDisks(nvmePciDisks)
  339. if err != nil {
  340. log.Errorf("getPassthroughNVMEDisks: %v", err)
  341. man.host.AppendError(fmt.Sprintf("get nvme passthrough disks %s", err.Error()), "isolated_devices", "", "")
  342. } else {
  343. for i := range nvmeDisks {
  344. man.devices = append(man.devices, nvmeDisks[i])
  345. }
  346. }
  347. }
  348. }
  349. func (man *isolatedDeviceManager) probeAMDVgpus(amdVgpuPFs []string) {
  350. if len(amdVgpuPFs) > 0 {
  351. pattern := `^([0-9a-f]{2}):([0-9a-f]{2})\.([0-9a-f])$`
  352. for idx := range amdVgpuPFs {
  353. matched, _ := regexp.MatchString(pattern, amdVgpuPFs[idx])
  354. if !matched {
  355. err := errors.Errorf("probeAMDVgpus invaild input pci address %s", amdVgpuPFs[idx])
  356. log.Errorln(err)
  357. man.host.AppendError(err.Error(), "isolated_devices", "", "")
  358. continue
  359. }
  360. vgpus, err := getSRIOVGpus(amdVgpuPFs[idx])
  361. if err != nil {
  362. log.Errorf("getSRIOVGpus: %s", err)
  363. man.host.AppendError(fmt.Sprintf("get amd sriov vgpus %s", err.Error()), "isolated_devices", "", "")
  364. } else {
  365. for i := range vgpus {
  366. man.devices = append(man.devices, vgpus[i])
  367. }
  368. }
  369. }
  370. }
  371. }
  372. func (man *isolatedDeviceManager) probeNVIDIAVgpus(nvidiaVgpuPFs []string) {
  373. if len(nvidiaVgpuPFs) > 0 {
  374. pattern := `^([0-9a-f]{2}):([0-9a-f]{2})\.([0-9a-f])$`
  375. for idx := range nvidiaVgpuPFs {
  376. matched, _ := regexp.MatchString(pattern, nvidiaVgpuPFs[idx])
  377. if !matched {
  378. err := errors.Errorf("probeNVIDIAVgpus invaild input pci address %s", nvidiaVgpuPFs[idx])
  379. log.Errorln(err)
  380. man.host.AppendError(err.Error(), "isolated_devices", "", "")
  381. continue
  382. }
  383. vgpus, err := getNvidiaVGpus(nvidiaVgpuPFs[idx])
  384. if err != nil {
  385. log.Errorf("getNvidiaVGpus: %s", err)
  386. man.host.AppendError(fmt.Sprintf("get nvidia vgpus %s", err.Error()), "isolated_devices", "", "")
  387. } else {
  388. for i := range vgpus {
  389. man.devices = append(man.devices, vgpus[i])
  390. }
  391. }
  392. }
  393. }
  394. }
  395. func (man *isolatedDeviceManager) ProbePCIDevices(skipGPUs, skipUSBs, skipCustomDevs bool, sriovNics, ovsOffloadNics []HostNic, nvmePciDisks, amdVgpuPFs, nvidiaVgpuPFs []string, enableCudaMps, enableContainerNPU, enableWhitelist bool) {
  396. man.devices = make([]IDevice, 0)
  397. if man.host.IsContainerHost() {
  398. man.probeContainerDevices()
  399. man.probeContainerNvidiaGPUs(enableCudaMps)
  400. man.probeContainerAscendNPUs(enableContainerNPU)
  401. } else {
  402. devModels, err := man.getCustomIsolatedDeviceModels()
  403. if err != nil {
  404. log.Errorf("get isolated device devModels %s", err.Error())
  405. man.host.AppendError(fmt.Sprintf("get custom isolated device devModels %s", err.Error()), "isolated_devices", "", "")
  406. return
  407. }
  408. man.probeUSBs(skipUSBs)
  409. man.probeCustomPCIDevs(skipCustomDevs, devModels, GpuClassCodes)
  410. man.probeSRIOVNics(sriovNics)
  411. man.probeOffloadNICS(ovsOffloadNics)
  412. man.probeAMDVgpus(amdVgpuPFs)
  413. man.probeNVIDIAVgpus(nvidiaVgpuPFs)
  414. man.probeGPUS(skipGPUs, amdVgpuPFs, nvidiaVgpuPFs, enableWhitelist, devModels)
  415. }
  416. }
  417. type IsolatedDeviceModel struct {
  418. DevType string `json:"dev_type"`
  419. VendorId string `json:"vendor_id"`
  420. DeviceId string `json:"device_id"`
  421. Model string `json:"model"`
  422. }
  423. func (man *isolatedDeviceManager) getCustomIsolatedDeviceModels() ([]IsolatedDeviceModel, error) {
  424. //man.getSession().
  425. params := jsonutils.NewDict()
  426. params.Set("limit", jsonutils.NewInt(0))
  427. params.Set("scope", jsonutils.NewString("system"))
  428. params.Set("host_id", jsonutils.NewString(man.host.GetHostId()))
  429. res, err := modules.IsolatedDeviceModels.List(man.getSession(), jsonutils.NewDict())
  430. if err != nil {
  431. return nil, errors.Wrap(err, "list isolated_device_models from compute service")
  432. }
  433. devModels := make([]IsolatedDeviceModel, len(res.Data))
  434. for i, obj := range res.Data {
  435. if err := obj.Unmarshal(&devModels[i]); err != nil {
  436. return nil, errors.Wrap(err, "unmarshal isolated device model failed")
  437. }
  438. }
  439. return devModels, nil
  440. }
  441. func (man *isolatedDeviceManager) getSession() *mcclient.ClientSession {
  442. return man.host.GetSession()
  443. }
  444. func (man *isolatedDeviceManager) CheckDevIsNeedUpdate(dev IDevice, devInfo *CloudDeviceInfo) bool {
  445. if dev.GetDeviceType() != devInfo.DevType {
  446. return true
  447. }
  448. if dev.GetDevicePath() != devInfo.DevicePath {
  449. return true
  450. }
  451. if dev.GetCardPath() != devInfo.CardPath {
  452. return true
  453. }
  454. if dev.GetRenderPath() != devInfo.RenderPath {
  455. return true
  456. }
  457. if dev.GetIndex() != devInfo.Index {
  458. return true
  459. }
  460. if dev.GetDeviceMinor() != devInfo.DeviceMinor {
  461. return true
  462. }
  463. if dev.GetModelName() != devInfo.Model {
  464. return true
  465. }
  466. if dev.GetWireId() != devInfo.WireId {
  467. return true
  468. }
  469. if dev.IsInfinibandNic() != devInfo.IsInfinibandNic {
  470. return true
  471. }
  472. if dev.GetOvsOffloadInterfaceName() != devInfo.OvsOffloadInterface {
  473. return true
  474. }
  475. if dev.GetNVMESizeMB() > 0 && devInfo.NvmeSizeMB > 0 && dev.GetNVMESizeMB() != devInfo.NvmeSizeMB {
  476. return true
  477. }
  478. if numaNode, _ := dev.GetNumaNode(); numaNode != devInfo.NumaNode {
  479. return true
  480. }
  481. if dev.GetMdevId() != devInfo.MdevId {
  482. return true
  483. }
  484. if info := dev.GetPCIEInfo(); info != nil && devInfo.PcieInfo == nil {
  485. return true
  486. }
  487. if dev.GetNvidiaMpsMemoryLimit() != devInfo.MpsMemoryLimit {
  488. return true
  489. }
  490. if dev.GetNvidiaMpsMemoryTotal() != devInfo.MpsMemoryTotal {
  491. return true
  492. }
  493. if dev.GetNvidiaMpsThreadPercentage() != devInfo.MpsThreadPercentage {
  494. return true
  495. }
  496. if profile := dev.GetNVIDIAVgpuProfile(); profile != nil {
  497. if val, _ := profile["frl"]; val != devInfo.FRL {
  498. return true
  499. }
  500. if val, _ := profile["framebuffer"]; val != devInfo.Framebuffer {
  501. return true
  502. }
  503. if val, _ := profile["max_resolution"]; val != devInfo.MaxResolution {
  504. return true
  505. }
  506. if val, _ := profile["num_heads"]; val != devInfo.NumHeads {
  507. return true
  508. }
  509. if val, _ := profile["max_instance"]; val != devInfo.MaxInstance {
  510. return true
  511. }
  512. }
  513. return false
  514. }
  515. func (man *isolatedDeviceManager) GetDeviceByIdent(vendorDevId, addr, mdevId string) IDevice {
  516. for _, dev := range man.devices {
  517. if dev.GetVendorDeviceId() == vendorDevId && dev.GetAddr() == addr && dev.GetMdevId() == mdevId {
  518. return dev
  519. }
  520. }
  521. return nil
  522. }
  523. func (man *isolatedDeviceManager) GetDeviceByVendorDevId(vendorDevId string) IDevice {
  524. for _, dev := range man.devices {
  525. if dev.GetVendorDeviceId() == vendorDevId {
  526. return dev
  527. }
  528. }
  529. return nil
  530. }
  531. func (man *isolatedDeviceManager) GetDeviceByAddr(addr string) IDevice {
  532. for _, dev := range man.devices {
  533. if dev.GetAddr() == addr {
  534. return dev
  535. }
  536. }
  537. return nil
  538. }
  539. func (man *isolatedDeviceManager) BatchCustomProbe() {
  540. for i, dev := range man.devices {
  541. if err := dev.CustomProbe(i); err != nil {
  542. man.host.AppendError(
  543. fmt.Sprintf("CustomProbe failed %s", err.Error()),
  544. "isolated_devices", dev.GetAddr(), dev.GetModelName())
  545. }
  546. }
  547. }
  548. func (man *isolatedDeviceManager) AppendDetachedDevice(dev *CloudDeviceInfo) {
  549. dev.DetectedOnHost = false
  550. man.DetachedDevices = append(man.DetachedDevices, dev)
  551. }
  552. func (man *isolatedDeviceManager) StartDetachTask() {
  553. if len(man.DetachedDevices) == 0 {
  554. return
  555. }
  556. go func() {
  557. for _, dev := range man.DetachedDevices {
  558. for {
  559. log.Infof("Start delete cloud device %s", jsonutils.Marshal(dev))
  560. if _, err := modules.IsolatedDevices.PerformAction(man.getSession(), dev.Id, "purge",
  561. jsonutils.Marshal(map[string]interface{}{
  562. //"purge": true,
  563. })); err != nil {
  564. if errors.Cause(err) == httperrors.ErrResourceNotFound {
  565. break
  566. }
  567. if strings.Contains(err.Error(), api.ErrMsgIsolatedDeviceUsedByServer) {
  568. log.Warningf("Purge isolated device %s failed: %v", jsonutils.Marshal(dev), err)
  569. break
  570. }
  571. log.Errorf("Detach device %s failed: %v, try again later", dev.Id, err)
  572. time.Sleep(30 * time.Second)
  573. continue
  574. }
  575. break
  576. }
  577. }
  578. man.DetachedDevices = nil
  579. }()
  580. }
  581. func (man *isolatedDeviceManager) GetQemuParams(devAddrs []string) *QemuParams {
  582. return getQemuParams(man, devAddrs)
  583. }
  584. type SBaseDevice struct {
  585. dev *PCIDevice
  586. originAddr string
  587. cloudId string
  588. hostId string
  589. guestId string
  590. devType string
  591. detectedOnHost bool
  592. }
  593. func NewBaseDevice(dev *PCIDevice, devType string) *SBaseDevice {
  594. return &SBaseDevice{
  595. dev: dev,
  596. devType: devType,
  597. }
  598. }
  599. func (dev *SBaseDevice) GetDevicePath() string {
  600. return ""
  601. }
  602. func (dev *SBaseDevice) GetHostId() string {
  603. return dev.hostId
  604. }
  605. func (dev *SBaseDevice) SetHostId(hId string) {
  606. dev.hostId = hId
  607. }
  608. func (dev *SBaseDevice) String() string {
  609. return dev.dev.String()
  610. }
  611. func (dev *SBaseDevice) GetWireId() string {
  612. return ""
  613. }
  614. func (dev *SBaseDevice) SetDeviceInfo(info CloudDeviceInfo) {
  615. if len(info.Id) != 0 {
  616. dev.cloudId = info.Id
  617. }
  618. if len(info.GuestId) != 0 {
  619. dev.guestId = info.GuestId
  620. }
  621. if len(info.HostId) != 0 {
  622. dev.hostId = info.HostId
  623. }
  624. if len(info.DevType) != 0 {
  625. dev.devType = info.DevType
  626. }
  627. }
  628. func SyncDeviceInfo(session *mcclient.ClientSession, hostId string, dev IDevice, needUpdate bool) (jsonutils.JSONObject, error) {
  629. if len(dev.GetHostId()) == 0 {
  630. dev.SetHostId(hostId)
  631. }
  632. data := GetApiResourceData(dev)
  633. if len(dev.GetCloudId()) != 0 {
  634. if !needUpdate {
  635. log.Infof("Update %s isolated_device: do nothing", dev.GetCloudId())
  636. return nil, nil
  637. }
  638. log.Infof("Update %s isolated_device: %s", dev.GetCloudId(), data.String())
  639. return modules.IsolatedDevices.Update(session, dev.GetCloudId(), data)
  640. }
  641. log.Infof("Create new isolated_device: %s", data.String())
  642. return modules.IsolatedDevices.Create(session, data)
  643. }
  644. func (dev *SBaseDevice) GetCloudId() string {
  645. return dev.cloudId
  646. }
  647. func (dev *SBaseDevice) GetVendorDeviceId() string {
  648. return dev.dev.GetVendorDeviceId()
  649. }
  650. func (dev *SBaseDevice) GetAddr() string {
  651. return dev.dev.Addr
  652. }
  653. func (dev *SBaseDevice) GetOriginAddr() string {
  654. if dev.originAddr != "" {
  655. return dev.originAddr
  656. }
  657. return dev.dev.Addr
  658. }
  659. func (dev *SBaseDevice) SetAddr(addr, originAddr string) {
  660. dev.originAddr = originAddr
  661. dev.dev.Addr = addr
  662. }
  663. func (dev *SBaseDevice) GetDeviceType() string {
  664. return dev.devType
  665. }
  666. func (dev *SBaseDevice) GetPfName() string {
  667. return ""
  668. }
  669. func (dev *SBaseDevice) GetVirtfn() int {
  670. return -1
  671. }
  672. func (dev *SBaseDevice) GetNumaNode() (int, error) {
  673. numaNodePath := fmt.Sprintf("/sys/bus/pci/devices/0000:%s/numa_node", dev.GetAddr())
  674. numaNode, err := fileutils2.FileGetIntContent(numaNodePath)
  675. if err != nil {
  676. return -1, errors.Wrap(err, "get device numa node")
  677. }
  678. return numaNode, nil
  679. }
  680. func (dev *SBaseDevice) GetOvsOffloadInterfaceName() string {
  681. return ""
  682. }
  683. func (dev *SBaseDevice) IsInfinibandNic() bool {
  684. return false
  685. }
  686. func (dev *SBaseDevice) GetNVMESizeMB() int {
  687. return -1
  688. }
  689. func (dev *SBaseDevice) GetNVIDIAVgpuProfile() map[string]string {
  690. return nil
  691. }
  692. func (dev *SBaseDevice) GetMdevId() string {
  693. return ""
  694. }
  695. func (dev *SBaseDevice) GetModelName() string {
  696. if dev.dev.ModelName != "" {
  697. return dev.dev.ModelName
  698. } else {
  699. return dev.dev.DeviceName
  700. }
  701. }
  702. func (dev *SBaseDevice) SetModelName(modelName string) {
  703. if dev.dev.ModelName == "" {
  704. dev.dev.ModelName = modelName
  705. }
  706. }
  707. func (dev *SBaseDevice) GetGuestId() string {
  708. return dev.guestId
  709. }
  710. func (dev *SBaseDevice) GetNvidiaMpsMemoryLimit() int {
  711. return -1
  712. }
  713. func (dev *SBaseDevice) GetNvidiaMpsMemoryTotal() int {
  714. return -1
  715. }
  716. func (dev *SBaseDevice) GetNvidiaMpsThreadPercentage() int {
  717. return -1
  718. }
  719. func (dev *SBaseDevice) GetCardPath() string {
  720. return ""
  721. }
  722. func (dev *SBaseDevice) GetRenderPath() string {
  723. return ""
  724. }
  725. func (dev *SBaseDevice) GetIndex() int {
  726. return -1
  727. }
  728. func (dev *SBaseDevice) GetDeviceMinor() int {
  729. return -1
  730. }
  731. func GetApiResourceData(dev IDevice) *jsonutils.JSONDict {
  732. data := map[string]interface{}{
  733. "dev_type": dev.GetDeviceType(),
  734. "addr": dev.GetAddr(),
  735. "model": dev.GetModelName(),
  736. "vendor_device_id": dev.GetVendorDeviceId(),
  737. }
  738. detected := false
  739. if err := dev.DetectByAddr(); err == nil {
  740. detected = true
  741. }
  742. data["detected_on_host"] = detected
  743. if len(dev.GetCloudId()) != 0 {
  744. data["id"] = dev.GetCloudId()
  745. }
  746. if len(dev.GetHostId()) != 0 {
  747. data["host_id"] = dev.GetHostId()
  748. }
  749. if len(dev.GetGuestId()) != 0 {
  750. data["guest_id"] = dev.GetGuestId()
  751. }
  752. if len(dev.GetWireId()) != 0 {
  753. data["wire_id"] = dev.GetWireId()
  754. }
  755. if dev.IsInfinibandNic() {
  756. data["is_infiniband_nic"] = true
  757. }
  758. if len(dev.GetOvsOffloadInterfaceName()) != 0 {
  759. data["ovs_offload_interface"] = dev.GetOvsOffloadInterfaceName()
  760. }
  761. if dev.GetNVMESizeMB() > 0 {
  762. data["nvme_size_mb"] = dev.GetNVMESizeMB()
  763. }
  764. if numaNode, err := dev.GetNumaNode(); err == nil {
  765. data["numa_node"] = numaNode
  766. } else {
  767. log.Debugf("failed get dev %s numa node %s", dev.GetAddr(), err)
  768. }
  769. if dev.GetMdevId() != "" {
  770. data["mdev_id"] = dev.GetMdevId()
  771. }
  772. if profile := dev.GetNVIDIAVgpuProfile(); profile != nil {
  773. for k, v := range profile {
  774. data[k] = v
  775. }
  776. }
  777. if info := dev.GetPCIEInfo(); info != nil {
  778. data["pcie_info"] = info
  779. }
  780. devPath := dev.GetDevicePath()
  781. if devPath != "" {
  782. data["device_path"] = devPath
  783. }
  784. cardPath := dev.GetCardPath()
  785. if cardPath != "" {
  786. data["card_path"] = cardPath
  787. }
  788. renderPath := dev.GetRenderPath()
  789. if renderPath != "" {
  790. data["render_path"] = renderPath
  791. }
  792. if index := dev.GetIndex(); index != -1 {
  793. data["index"] = index
  794. }
  795. if deviceMinor := dev.GetDeviceMinor(); deviceMinor != -1 {
  796. data["device_minor"] = deviceMinor
  797. }
  798. if mpsMemTotal := dev.GetNvidiaMpsMemoryTotal(); mpsMemTotal > 0 {
  799. data["mps_memory_total"] = mpsMemTotal
  800. }
  801. if mpsMemLimit := dev.GetNvidiaMpsMemoryLimit(); mpsMemLimit > 0 {
  802. data["mps_memory_limit"] = mpsMemLimit
  803. }
  804. if mpsThreadPercentage := dev.GetNvidiaMpsThreadPercentage(); mpsThreadPercentage > 0 {
  805. data["mps_thread_percentage"] = mpsThreadPercentage
  806. }
  807. return jsonutils.Marshal(data).(*jsonutils.JSONDict)
  808. }
  809. func (dev *SBaseDevice) GetKernelDriver() (string, error) {
  810. return dev.dev.getKernelDriver()
  811. }
  812. func (dev *SBaseDevice) getVFIODeviceCmd(addr string) string {
  813. return fmt.Sprintf(" -device vfio-pci,host=%s", addr)
  814. }
  815. func (dev *SBaseDevice) GetPassthroughOptions() map[string]string {
  816. return nil
  817. }
  818. func (dev *SBaseDevice) GetPassthroughCmd(_ int) string {
  819. return dev.getVFIODeviceCmd(dev.GetAddr())
  820. }
  821. func (dev *SBaseDevice) GetIOMMUGroupRestAddrs() []string {
  822. addrs := []string{}
  823. for _, d := range dev.dev.RestIOMMUGroupDevs {
  824. addrs = append(addrs, d.Addr)
  825. }
  826. return addrs
  827. }
  828. func (dev *SBaseDevice) GetIOMMUGroupDeviceCmd() string {
  829. restAddrs := dev.GetIOMMUGroupRestAddrs()
  830. cmds := []string{}
  831. for _, addr := range restAddrs {
  832. cmds = append(cmds, dev.getVFIODeviceCmd(addr))
  833. }
  834. return strings.Join(cmds, "")
  835. }
  836. func (dev *SBaseDevice) DetectByAddr() error {
  837. return nil
  838. }
  839. func (dev *SBaseDevice) CustomProbe(idx int) error {
  840. // check environments on first probe
  841. if idx == 0 {
  842. for _, driver := range []string{"vfio", "vfio_iommu_type1", "vfio-pci"} {
  843. if err := procutils.NewRemoteCommandAsFarAsPossible("modprobe", driver).Run(); err != nil {
  844. return fmt.Errorf("modprobe %s: %v", driver, err)
  845. }
  846. }
  847. }
  848. driver, err := dev.GetKernelDriver()
  849. if err != nil {
  850. return fmt.Errorf("Nic %s is occupied by another driver: %s", dev.GetAddr(), driver)
  851. }
  852. if driver != VFIO_PCI_KERNEL_DRIVER {
  853. if driver != "" {
  854. if err = dev.dev.unbindDriver(); err != nil {
  855. return errors.Wrap(err, "unbind driver")
  856. }
  857. }
  858. if err = dev.dev.bindDriver(); err != nil {
  859. return errors.Wrap(err, "bind driver")
  860. }
  861. }
  862. return nil
  863. }
  864. func (dev *SBaseDevice) GetHotPlugOptions(isolatedDev *desc.SGuestIsolatedDevice, guestDesc *desc.SGuestDesc) ([]*HotPlugOption, error) {
  865. ret := make([]*HotPlugOption, 0)
  866. var masterDevOpt *HotPlugOption
  867. for i := 0; i < len(isolatedDev.VfioDevs); i++ {
  868. opts := map[string]string{
  869. "host": isolatedDev.VfioDevs[i].HostAddr,
  870. "bus": isolatedDev.VfioDevs[i].BusStr(),
  871. "addr": isolatedDev.VfioDevs[i].SlotFunc(),
  872. "id": isolatedDev.VfioDevs[i].Id,
  873. }
  874. if isolatedDev.VfioDevs[i].Multi != nil {
  875. if *isolatedDev.VfioDevs[i].Multi {
  876. opts["multifunction"] = "on"
  877. } else {
  878. opts["multifunction"] = "off"
  879. }
  880. }
  881. if isolatedDev.VfioDevs[i].XVga {
  882. opts["x-vga"] = "on"
  883. }
  884. devOpt := &HotPlugOption{
  885. Device: isolatedDev.VfioDevs[i].DevType,
  886. Options: opts,
  887. }
  888. if isolatedDev.VfioDevs[i].Function == 0 {
  889. masterDevOpt = devOpt
  890. } else {
  891. ret = append(ret, devOpt)
  892. }
  893. }
  894. // if PCI slot function 0 already assigned, qemu will reject hotplug function
  895. // so put function 0 at the enda
  896. if masterDevOpt == nil {
  897. return nil, errors.Errorf("GPU Device no function 0 found")
  898. }
  899. ret = append(ret, masterDevOpt)
  900. return ret, nil
  901. }
  902. func (dev *SBaseDevice) GetHotUnplugOptions(isolatedDev *desc.SGuestIsolatedDevice) ([]*HotUnplugOption, error) {
  903. if len(isolatedDev.VfioDevs) == 0 {
  904. return nil, errors.Errorf("device %s no pci ids", isolatedDev.Id)
  905. }
  906. return []*HotUnplugOption{
  907. {
  908. Id: isolatedDev.VfioDevs[0].Id,
  909. },
  910. }, nil
  911. }
  912. func (dev *SBaseDevice) GetPCIEInfo() *api.IsolatedDevicePCIEInfo {
  913. return dev.dev.PCIEInfo
  914. }
  915. func ParseOutput(output []byte, doTrim bool) []string {
  916. lines := make([]string, 0)
  917. for _, line := range strings.Split(string(output), "\n") {
  918. if doTrim {
  919. lines = append(lines, strings.TrimSpace(line))
  920. } else {
  921. lines = append(lines, line)
  922. }
  923. }
  924. return lines
  925. }
  926. func bashCmdOutput(cmd string, doTrim bool) ([]string, error) {
  927. args := []string{"-c", cmd}
  928. output, err := procutils.NewRemoteCommandAsFarAsPossible("bash", args...).Output()
  929. if err != nil {
  930. return nil, err
  931. } else {
  932. return ParseOutput(output, doTrim), nil
  933. }
  934. }
  935. func bashOutput(cmd string) ([]string, error) {
  936. return bashCmdOutput(cmd, true)
  937. }
  938. func bashRawOutput(cmd string) ([]string, error) {
  939. return bashCmdOutput(cmd, false)
  940. }
  941. type QemuParams struct {
  942. Cpu string
  943. Vga string
  944. Devices []string
  945. }
  946. func getQemuParams(man *isolatedDeviceManager, devAddrs []string) *QemuParams {
  947. if len(devAddrs) == 0 {
  948. return nil
  949. }
  950. devCmds := []string{}
  951. cpuCmd := DEFAULT_CPU_CMD
  952. vgaCmd := DEFAULT_VGA_CMD
  953. // group by device type firstly
  954. devices := make(map[string][]IDevice, 0)
  955. for _, addr := range devAddrs {
  956. dev := man.GetDeviceByAddr(addr)
  957. if dev == nil {
  958. log.Warningf("IsolatedDeviceManager not found dev %#v, ignore it!", addr)
  959. continue
  960. }
  961. devType := dev.GetDeviceType()
  962. if _, ok := devices[devType]; !ok {
  963. devices[devType] = []IDevice{dev}
  964. } else {
  965. devices[devType] = append(devices[devType], dev)
  966. }
  967. }
  968. for devType, devs := range devices {
  969. log.Debugf("get devices %s command", devType)
  970. for idx, dev := range devs {
  971. devCmds = append(devCmds, getDeviceCmd(dev, idx))
  972. if dev.GetVGACmd() != vgaCmd && dev.GetDeviceType() == api.GPU_VGA_TYPE {
  973. vgaCmd = dev.GetVGACmd()
  974. }
  975. if dev.GetCPUCmd() != cpuCmd {
  976. cpuCmd = dev.GetCPUCmd()
  977. }
  978. }
  979. }
  980. return &QemuParams{
  981. Cpu: cpuCmd,
  982. Vga: vgaCmd,
  983. Devices: devCmds,
  984. }
  985. }