From 04eb718f886008c8159f68c9598c9ecdf71615f5 Mon Sep 17 00:00:00 2001 From: congs Date: Thu, 11 Aug 2022 09:05:27 +1200 Subject: [PATCH] fix(gpu) EE-3191 fix gpu bugs (#7451) --- app/docker/react/views/gpu.tsx | 58 ++++++++++++++----- .../create/createContainerController.js | 20 +++---- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/app/docker/react/views/gpu.tsx b/app/docker/react/views/gpu.tsx index ebf544c1d..e26114097 100644 --- a/app/docker/react/views/gpu.tsx +++ b/app/docker/react/views/gpu.tsx @@ -1,6 +1,10 @@ import { useMemo } from 'react'; -import { components } from 'react-select'; -import { OnChangeValue } from 'react-select/dist/declarations/src/types'; +import { components, MultiValue } from 'react-select'; +import { MultiValueRemoveProps } from 'react-select/dist/declarations/src/components/MultiValue'; +import { + ActionMeta, + OnChangeValue, +} from 'react-select/dist/declarations/src/types'; import { OptionProps } from 'react-select/dist/declarations/src/components/Option'; import { Select } from '@@/form-components/ReactSelect'; @@ -9,6 +13,7 @@ import { Tooltip } from '@@/Tip/Tooltip'; interface Values { enabled: boolean; + useSpecific: boolean; selectedGPUs: string[]; capabilities: string[]; } @@ -81,6 +86,17 @@ function Option(props: OptionProps) { ); } +function MultiValueRemove(props: MultiValueRemoveProps) { + const { + selectProps: { value }, + } = props; + if (value && (value as MultiValue).length === 1) { + return null; + } + // eslint-disable-next-line react/jsx-props-no-spreading + return ; +} + export function Gpu({ values, onChange, @@ -97,6 +113,11 @@ export function Gpu({ : gpu.name, })); + options.unshift({ + value: 'all', + label: 'Use All GPUs', + }); + return options; }, [gpus, usedGpus, usedAllGpus]); @@ -112,11 +133,22 @@ export function Gpu({ onChangeValues('enabled', !values.enabled); } - function onChangeSelectedGpus(newValue: OnChangeValue) { - onChangeValues( - 'selectedGPUs', - newValue.map((option) => option.value) - ); + function onChangeSelectedGpus( + newValue: OnChangeValue, + actionMeta: ActionMeta + ) { + let { useSpecific } = values; + let selectedGPUs = newValue.map((option) => option.value); + + if (actionMeta.action === 'select-option') { + useSpecific = actionMeta.option?.value !== 'all'; + selectedGPUs = selectedGPUs.filter((value) => + useSpecific ? value !== 'all' : value === 'all' + ); + } + + const newValues = { ...values, selectedGPUs, useSpecific }; + onChange(newValues); } function onChangeSelectedCaps(newValue: OnChangeValue) { @@ -128,8 +160,9 @@ export function Gpu({ const gpuCmd = useMemo(() => { const devices = values.selectedGPUs.join(','); + const deviceStr = devices === 'all' ? 'all,' : `device=${devices},`; const caps = values.capabilities.join(','); - return `--gpus 'device=${devices},"capabilities=${caps}"`; + return `--gpus '${deviceStr}"capabilities=${caps}"'`; }, [values.selectedGPUs, values.capabilities]); const gpuValue = useMemo( @@ -164,9 +197,12 @@ export function Gpu({ isMulti closeMenuOnSelect value={gpuValue} + isClearable={false} + backspaceRemovesValue={false} isDisabled={!values.enabled} onChange={onChangeSelectedGpus} options={options} + components={{ MultiValueRemove }} /> @@ -176,11 +212,7 @@ export function Gpu({
Capabilities - +
diff --git a/app/docker/views/containers/create/createContainerController.js b/app/docker/views/containers/create/createContainerController.js index 1b2247944..38e043be1 100644 --- a/app/docker/views/containers/create/createContainerController.js +++ b/app/docker/views/containers/create/createContainerController.js @@ -72,7 +72,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ GPU: { enabled: false, useSpecific: false, - selectedGPUs: [], + selectedGPUs: ['all'], capabilities: ['compute', 'utility'], }, Console: 'none', @@ -469,28 +469,24 @@ angular.module('portainer.docker').controller('CreateContainerController', [ function prepareGPUOptions(config) { const driver = 'nvidia'; const gpuOptions = $scope.formValues.GPU; - - const existingDeviceRequest = _.find($scope.config.HostConfig.DeviceRequests, function (o) { - return o.Driver === driver || o.Capabilities[0][0] === 'gpu'; - }); + const existingDeviceRequest = _.find($scope.config.HostConfig.DeviceRequests, { Driver: driver }); if (existingDeviceRequest) { _.pullAllBy(config.HostConfig.DeviceRequests, [existingDeviceRequest], 'Driver'); } - if (!gpuOptions.enabled) { return; } - - const deviceRequest = existingDeviceRequest || { + const deviceRequest = { Driver: driver, Count: -1, DeviceIDs: [], // must be empty if Count != 0 https://github.com/moby/moby/blob/master/daemon/nvidia_linux.go#L50 Capabilities: [], // array of ORed arrays of ANDed capabilites = [ [c1 AND c2] OR [c1 AND c3] ] : https://github.com/moby/moby/blob/master/api/types/container/host_config.go#L272 // Options: { property1: "string", property2: "string" }, // seems to never be evaluated/used in docker API ? }; - - deviceRequest.DeviceIDs = gpuOptions.selectedGPUs; - deviceRequest.Count = 0; + if (gpuOptions.useSpecific) { + deviceRequest.DeviceIDs = gpuOptions.selectedGPUs; + deviceRequest.Count = 0; + } deviceRequest.Capabilities = [gpuOptions.capabilities]; config.HostConfig.DeviceRequests.push(deviceRequest); @@ -661,6 +657,8 @@ angular.module('portainer.docker').controller('CreateContainerController', [ $scope.formValues.GPU.selectedGPUs = deviceRequest.DeviceIDs || []; if ($scope.formValues.GPU.useSpecific) { $scope.formValues.GPU.selectedGPUs = deviceRequest.DeviceIDs; + } else { + $scope.formValues.GPU.selectedGPUs = ['all']; } // we only support a single set of capabilities for now // UI needs to be reworked in order to support OR combinations of AND capabilities