Before we begin lets make a few assumptions about what has already been configured. We assume that we have a working OpenShift 4.9 cluster, could be a full cluster, a compact cluster or in my case just a single node cluster (SNO). We also can assume that Containerized virtualization and Node Feature Discovery operator has been installed via OperatorHub.
Now that we have the basic assumptions out of the way lets begin the process of enabling virtual GPUs. The very first step is to label the nodes that have a GPU installed:
$ oc get nodes NAME STATUS ROLES AGE VERSION sno2 Ready master,worker 1d v1.22.3+e790d7f $ oc label nodes sno2 hasGpu=true node/sno2 labeled
With the labeling of the node which will be used later when we deploy the driver we can now create the MachineConfig to enable the IOMMU:
$ cat << EOF > ~/100-master-kernel-arg-iommu.yaml
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
machineconfiguration.openshift.io/role: master
name: 100-master-iommu
spec:
config:
ignition:
version: 3.2.0
kernelArguments:
- intel_iommu=on
EOF
With the MachineConfig created lets go ahead and apply it to the cluster:
$ oc create -f ~/100-master-kernel-arg-iommu.yaml machineconfig.machineconfiguration.openshift.io/100-master-iommu created
Wait for the nodes where the machine config is applied to reboot. Once the nodes have rebooted we can continue onto the next step.
Once the nodes have rebooted we can verify the MachineConfig was applied by running the following:
$ oc get MachineConfig 100-master-iommu NAME GENERATEDBYCONTROLLER IGNITIONVERSION AGE 100-master-iommu 3.2.0 6m25s
Now lets go ahead and build the driver container that will apply the NVIDIA driver to the worker nodes that have GPUs in them. I should note that in order to proceed the NVIDIA GRID drivers need to be obtained from NVIDIA here. I will be using the following driver in this example to build my container: NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run. The first step we need to do is determine the driver-toolkit release image our current cluster is using. We can find that by running the following command:
$ oc adm release info --image-for=driver-toolkit quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:ce897bc72101dacc82aa593974fa0d8a421a43227b540fbcf1e303ffb1d3f1ea
Next we will take that release image and place it into a Dockerfile in a directory called vgpu:
$ cat << EOF > ~/vgpu/Dockerfile
FROM quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:ce897bc72101dacc82aa593974fa0d8a421a43227b540fbcf1e303ffb1d3f1ea
ARG NVIDIA_INSTALLER_BINARY
ENV NVIDIA_INSTALLER_BINARY=${NVIDIA_INSTALLER_BINARY:-NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run}
RUN dnf -y install git make sudo gcc \
&& dnf clean all \
&& rm -rf /var/cache/dnf
RUN mkdir -p /root/nvidia
WORKDIR /root/nvidia
ADD ${NVIDIA_INSTALLER_BINARY} .
RUN chmod +x /root/nvidia/${NVIDIA_INSTALLER_BINARY}
ADD entrypoint.sh .
RUN chmod +x /root/nvidia/entrypoint.sh
RUN mkdir -p /root/tmp
EOF
$ cat << EOF > ~/vgpu/entrypoint.sh
#!/bin/sh
/usr/sbin/rmmod nvidia
/root/nvidia/${NVIDIA_INSTALLER_BINARY} --kernel-source-path=/usr/src/kernels/$(uname -r) --kernel-install-path=/lib/modules/$(uname -r)/kernel/drivers/video/ --silent --tmpdir /root/tmp/ --no-systemd
/usr/bin/nvidia-vgpud &
/usr/bin/nvidia-vgpu-mgr &
while true; do sleep 15 ; /usr/bin/pgrep nvidia-vgpu-mgr ; if [ $? -ne 0 ] ; then echo "nvidia-vgpu-mgr is not running" && exit 1; fi; done
EOF
Also place the NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run in to the vgpu directory. Then you should have the following:
$ ls Dockerfile entrypoint.sh NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run
At this point change directory into vgpu and then use the podman build command to build the driver container local:
$ cd ~/vgpu
$ podman build --build-arg NVIDIA_INSTALLER_BINARY=NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run -t ocp-nvidia-vgpu-installer .
STEP 1/11: FROM quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:ce897bc72101dacc82aa593974fa0d8a421a43227b540fbcf1e303ffb1d3f1ea
STEP 2/11: ARG NVIDIA_INSTALLER_BINARY
--> Using cache 13aa17a1fd44bb7afea0a1b884b7005aaa51091e47dfe14987b572db9efab1f2
--> 13aa17a1fd4
STEP 3/11: ENV NVIDIA_INSTALLER_BINARY=${NVIDIA_INSTALLER_BINARY:-NVIDIA-Linux-x86_64-470.63-vgpu-kvm.run}
--> Using cache e818b281ad40c0e78ef4c01a71d73b45b509392100262fddbf542c457d697255
--> e818b281ad4
STEP 4/11: RUN dnf -y install git make sudo gcc && dnf clean all && rm -rf /var/cache/dnf
--> Using cache d6f3687a545589cf096353ad792fb464a6961ff204c49234ced26d996da9f1c8
--> d6f3687a545
STEP 5/11: RUN mkdir -p /root/nvidia
--> Using cache 708b464de69de2443edb5609623478945af6f9498d73bf4d47c577e29811a414
--> 708b464de69
STEP 6/11: WORKDIR /root/nvidia
--> Using cache 6cb724eeb99d21a30f50a3c25954426d4719af84ef43bda7ab0aeab6e7da81a8
--> 6cb724eeb99
STEP 7/11: ADD ${NVIDIA_INSTALLER_BINARY} .
--> Using cache 71dd0491be7e3c20a742cd50efe26f54a5e2f61d4aa8846cd5d7ccd82f27ab45
--> 71dd0491be7
STEP 8/11: RUN chmod +x /root/nvidia/${NVIDIA_INSTALLER_BINARY}
--> Using cache 85d64dc8b702936412fa121aaab3733a60f880aa211e0197f1c8853ddbb617b5
--> 85d64dc8b70
STEP 9/11: ADD entrypoint.sh .
--> Using cache 9d49c87387f926ec39162c5e1c2a7866c1494c1ab8f3912c53ea6eaefe0be254
--> 9d49c87387f
STEP 10/11: RUN chmod +x /root/nvidia/entrypoint.sh
--> Using cache 79d682f8471fc97a60b6507d2cff164b3b9283a1e078d4ddb9f8138741c033b5
--> 79d682f8471
STEP 11/11: RUN mkdir -p /root/tmp
--> Using cache bcbb311e35999cb6c55987049033c5d278ee93d76a97fe9203ce68257a9f8ebd
COMMIT ocp-nvidia-vgpu-installer
--> bcbb311e359
Successfully tagged localhost/ocp-nvidia-vgpu-installer:latest
Successfully tagged localhost/ocp-nvidia-vgpu-nstaller:latest
Successfully tagged quay.io/bschmaus/ocp-nvidia-vgpu-nstaller:latest
bcbb311e35999cb6c55987049033c5d278ee93d76a97fe9203ce68257a9f8ebd
Once the container is build push it to a private repository that is only accessible by the organization that purchased the NVIDIA GRID license. It is not legal to freely distribute the driver image.
$ podman push quay.io/bschmaus/ocp-nvidia-vgpu-nstaller:latest Getting image source signatures Copying blob b0b1274fc88c done Copying blob 525ed45dbdb1 done Copying blob 8aa226ded434 done Copying blob d9ad9932e964 done Copying blob 5bc03dec6239 done Copying blob ab10e1e28fa3 done Copying blob 4eff86c961b3 done Copying blob e1790381e6f7 done Copying blob a8701ba769cc done Copying blob 38a3912b1d62 done Copying blob 257db9f06185 done Copying blob 09fd8acd3579 done Copying config bcbb311e35 done Writing manifest to image destination Copying config bcbb311e35 [--------------------------------------] 0.0b / 5.6KiB Writing manifest to image destination Storing signatures
Now that we have a driver image lets create a custom resource that will use and apply that driver image to the cluster nodes that have the label hasGPU via a deamonset. The file will look similar below but will need the container image path to be updated to fit ones environment.
$ cat << EOF > ~/1000-drivercontainer.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: simple-kmod-driver-container
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: simple-kmod-driver-container
rules:
- apiGroups:
- security.openshift.io
resources:
- securitycontextconstraints
verbs:
- use
resourceNames:
- privileged
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: simple-kmod-driver-container
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: simple-kmod-driver-container
subjects:
- kind: ServiceAccount
name: simple-kmod-driver-container
userNames:
- system:serviceaccount:simple-kmod-demo:simple-kmod-driver-container
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: simple-kmod-driver-container
spec:
selector:
matchLabels:
app: simple-kmod-driver-container
template:
metadata:
labels:
app: simple-kmod-driver-container
spec:
serviceAccount: simple-kmod-driver-container
serviceAccountName: simple-kmod-driver-container
hostPID: true
hostIPC: true
containers:
- image: quay.io/bschmaus/ocp-nvidia-vgpu-nstaller:latest
name: simple-kmod-driver-container
imagePullPolicy: Always
command: ["/root/nvidia/entrypoint.sh"]
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "systemctl stop kmods-via-containers@simple-kmod"]
securityContext:
privileged: true
allowedCapabilities:
- '*'
capabilities:
add: ["SYS_ADMIN"]
volumeMounts:
- mountPath: /dev/vfio/
name: vfio
- mountPath: /sys/fs/cgroup
name: cgroup
volumes:
- hostPath:
path: /sys/fs/cgroup
type: Directory
name: cgroup
- hostPath:
path: /dev/vfio/
type: Directory
name: vfio
nodeSelector:
hasGpu: "true"
EOF
Now that we have our custom resource driver yaml lets apply it to the cluster:
$ oc create -f 1000-drivercontainer.yaml serviceaccount/simple-kmod-driver-container created role.rbac.authorization.k8s.io/simple-kmod-driver-container created rolebinding.rbac.authorization.k8s.io/simple-kmod-driver-container created daemonset.apps/simple-kmod-driver-container created
We can validate the daemonset is running by looking at the daemonsets under openshift-nfd:
$ oc get daemonset simple-kmod-driver-container NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE simple-kmod-driver-container 1 1 1 1 1 hasGpu=true 9m23s
Now lets further validate by logging into the worker node as the core user, sudo up to root and then list out the loaded kernel modules. We should see the NVIDIA drivers loaded:
# sudo bash # lsmod| grep nvi nvidia_vgpu_vfio 65536 0 nvidia 35274752 10 nvidia_vgpu_vfio mdev 20480 2 vfio_mdev,nvidia_vgpu_vfio vfio 36864 3 vfio_mdev,nvidia_vgpu_vfio,vfio_iommu_type1 drm 569344 4 drm_kms_helper,nvidia,mgag200
Once we have confirmed the NVIDIA drivers are loaded lets enumerate through the possible mdev_type devices for our GPU card. Using the commands below we can show what the different options are for carving up the GPU card from a vGPU perspective. In our example below we have a variety of ways we could use this card. However it should be noted that only one nvidia-(n) device can be used. That is if we choose nvidia-22 and carve up each GPU into a single vGPU then we end up with one vGPU per physical GPU on the card. As another example if we chose nvidia-15 we would then end up with 8 vGPUs per physical GPU on the card.
# for device in /sys/class/mdev_bus/*; do for mdev_type in "$device"/mdev_supported_types/*; do MDEV_TYPE=$(basename $mdev_type); DESCRIPTION=$(cat $mdev_type/description); NAME=$(cat $mdev_type/name); echo "mdev_type: $MDEV_TYPE --- description: $DESCRIPTION --- name: $NAME"; done; done | sort | uniq mdev_type: nvidia-11 --- description: num_heads=2, frl_config=45, framebuffer=512M, max_resolution=2560x1600, max_instance=16 --- name: GRID M60-0B mdev_type: nvidia-12 --- description: num_heads=2, frl_config=60, framebuffer=512M, max_resolution=2560x1600, max_instance=16 --- name: GRID M60-0Q mdev_type: nvidia-13 --- description: num_heads=1, frl_config=60, framebuffer=1024M, max_resolution=1280x1024, max_instance=8 --- name: GRID M60-1A mdev_type: nvidia-14 --- description: num_heads=4, frl_config=45, framebuffer=1024M, max_resolution=5120x2880, max_instance=8 --- name: GRID M60-1B mdev_type: nvidia-15 --- description: num_heads=4, frl_config=60, framebuffer=1024M, max_resolution=5120x2880, max_instance=8 --- name: GRID M60-1Q mdev_type: nvidia-16 --- description: num_heads=1, frl_config=60, framebuffer=2048M, max_resolution=1280x1024, max_instance=4 --- name: GRID M60-2A mdev_type: nvidia-17 --- description: num_heads=4, frl_config=45, framebuffer=2048M, max_resolution=5120x2880, max_instance=4 --- name: GRID M60-2B mdev_type: nvidia-18 --- description: num_heads=4, frl_config=60, framebuffer=2048M, max_resolution=5120x2880, max_instance=4 --- name: GRID M60-2Q mdev_type: nvidia-19 --- description: num_heads=1, frl_config=60, framebuffer=4096M, max_resolution=1280x1024, max_instance=2 --- name: GRID M60-4A mdev_type: nvidia-20 --- description: num_heads=4, frl_config=60, framebuffer=4096M, max_resolution=5120x2880, max_instance=2 --- name: GRID M60-4Q mdev_type: nvidia-210 --- description: num_heads=4, frl_config=45, framebuffer=2048M, max_resolution=5120x2880, max_instance=4 --- name: GRID M60-2B4 mdev_type: nvidia-21 --- description: num_heads=1, frl_config=60, framebuffer=8192M, max_resolution=1280x1024, max_instance=1 --- name: GRID M60-8A mdev_type: nvidia-22 --- description: num_heads=4, frl_config=60, framebuffer=8192M, max_resolution=5120x2880, max_instance=1 --- name: GRID M60-8Q mdev_type: nvidia-238 --- description: num_heads=4, frl_config=45, framebuffer=1024M, max_resolution=5120x2880, max_instance=8 --- name: GRID M60-1B4
In my example I am going to go ahead and use nvidia-22 and only pass one vGPU per physical GPU. To do this we need to echo a unique uuid number into the following device path create file. I will do this twice once for each physical GPU device. Note that this can only be done once. If attempted more then once an IO error will result.
# echo `uuidgen` > /sys/class/mdev_bus/0000:3e:00.0/mdev_supported_types/nvidia-22/create # echo `uuidgen` > /sys/class/mdev_bus/0000:3d:00.0/mdev_supported_types/nvidia-22/create
Now that we have created our vGPU devices we next need to expose those devices to Containerized virtualization so they can then be consumed by a virtual machine. To do this we need to patch the kubevirt-hyperconverged configuration. So first lets create the patch file:
$ cat << EOF > ~/kubevirt-hyperconverged-patch.yaml
spec:
permittedHostDevices:
mediatedDevices:
- mdevNameSelector: "GRID M60-8Q"
resourceName: "nvidia.com/GRID_M60_8Q"
EOF
With the patch file created we next need to merge it with the existing kubevirt-hyperconverged configuration using the oc patch command:
$ oc patch hyperconverged kubevirt-hyperconverged -n openshift-cnv --patch "$(cat ~/kubevirt-hyperconverged-patch.yaml)" --type=merge hyperconverged.hco.kubevirt.io/kubevirt-hyperconverged patched
Once applied wait a few minutes for the configuration to be reloaded. Then to validate it run the oc describe node command against the node and look for the GPU devices under Capacity and Allocatable. In our example we see two devices because we had 2 physical GPUs and we created a vGPU using nvidia-22 which allows for one vGPU per physical GPU.
$ oc describe node| sed '/Capacity/,/System/!d;/System/d' Capacity: cpu: 24 devices.kubevirt.io/kvm: 1k devices.kubevirt.io/tun: 1k devices.kubevirt.io/vhost-net: 1k ephemeral-storage: 936104940Ki hugepages-1Gi: 0 hugepages-2Mi: 0 memory: 131561680Ki nvidia.com/GRID_M60_8Q: 2 pods: 250 Allocatable: cpu: 23500m devices.kubevirt.io/kvm: 1k devices.kubevirt.io/tun: 1k devices.kubevirt.io/vhost-net: 1k ephemeral-storage: 862714311276 hugepages-1Gi: 0 hugepages-2Mi: 0 memory: 130410704Ki nvidia.com/GRID_M60_8Q: 2 pods: 250
At this point VMs can now be deployed and consume the available vGPUs on the node. To do this we need to create a VM resource configuration file like the example below. Notice that we define in this file host devices and we pass in the NVIDIA host device of the vGPU name:
$ cat << EOF > ~/fedora-vm.yaml
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
annotations:
kubemacpool.io/transaction-timestamp: '2022-02-09T17:23:53.76596817Z'
kubevirt.io/latest-observed-api-version: v1
kubevirt.io/storage-observed-api-version: v1alpha3
name.os.template.kubevirt.io/fedora34: Fedora 33 or higher
vm.kubevirt.io/validations: |
[
{
"name": "minimal-required-memory",
"path": "jsonpath::.spec.domain.resources.requests.memory",
"rule": "integer",
"message": "This VM requires more memory.",
"min": 1073741824
}
]
resourceVersion: '19096098'
name: fedora
uid: 48bf787d-9240-444c-92fd-f0e5ce0ced23
creationTimestamp: '2022-02-09T16:48:54Z'
generation: 3
managedFields:
- apiVersion: kubevirt.io/v1
fieldsType: FieldsV1
fieldsV1:
'f:metadata':
'f:annotations':
.: {}
'f:name.os.template.kubevirt.io/fedora34': {}
'f:vm.kubevirt.io/validations': {}
'f:labels':
.: {}
'f:app': {}
'f:os.template.kubevirt.io/fedora34': {}
'f:vm.kubevirt.io/template': {}
'f:vm.kubevirt.io/template.namespace': {}
'f:vm.kubevirt.io/template.revision': {}
'f:vm.kubevirt.io/template.version': {}
'f:workload.template.kubevirt.io/server': {}
'f:spec':
.: {}
'f:dataVolumeTemplates': {}
'f:template':
.: {}
'f:metadata':
.: {}
'f:annotations': {}
'f:labels': {}
'f:spec':
.: {}
'f:domain':
.: {}
'f:cpu':
.: {}
'f:cores': {}
'f:sockets': {}
'f:threads': {}
'f:devices':
.: {}
'f:disks': {}
'f:interfaces': {}
'f:networkInterfaceMultiqueue': {}
'f:rng': {}
'f:machine':
.: {}
'f:type': {}
'f:resources':
.: {}
'f:requests':
.: {}
'f:memory': {}
'f:evictionStrategy': {}
'f:hostname': {}
'f:networks': {}
'f:terminationGracePeriodSeconds': {}
'f:volumes': {}
manager: Mozilla
operation: Update
time: '2022-02-09T16:48:54Z'
- apiVersion: kubevirt.io/v1alpha3
fieldsType: FieldsV1
fieldsV1:
'f:status':
'f:conditions': {}
'f:printableStatus': {}
manager: Go-http-client
operation: Update
subresource: status
time: '2022-02-09T17:23:53Z'
namespace: openshift-nfd
labels:
app: fedora
os.template.kubevirt.io/fedora34: 'true'
vm.kubevirt.io/template: fedora-server-large
vm.kubevirt.io/template.namespace: openshift
vm.kubevirt.io/template.revision: '1'
vm.kubevirt.io/template.version: v0.16.4
workload.template.kubevirt.io/server: 'true'
spec:
dataVolumeTemplates:
- metadata:
creationTimestamp: null
name: fedora-rootdisk-uqf5j
spec:
pvc:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 40Gi
storageClassName: hostpath-provisioner
volumeMode: Filesystem
source:
http:
url: >-
https://download-ib01.fedoraproject.org/pub/fedora/linux/releases/34/Cloud/x86_64/images/Fedora-Cloud-Base-34-1.2.x86_64.raw.xz
running: true
template:
metadata:
annotations:
vm.kubevirt.io/flavor: large
vm.kubevirt.io/os: fedora
vm.kubevirt.io/workload: server
creationTimestamp: null
labels:
kubevirt.io/domain: fedora
kubevirt.io/size: large
os.template.kubevirt.io/fedora34: 'true'
vm.kubevirt.io/name: fedora
workload.template.kubevirt.io/server: 'true'
spec:
domain:
cpu:
cores: 12
sockets: 1
threads: 1
devices:
disks:
- disk:
bus: virtio
name: cloudinitdisk
- bootOrder: 1
disk:
bus: virtio
name: rootdisk
hostDevices:
- deviceName: nvidia.com/GRID_M60_8Q
name: GRID_M60_8Q
interfaces:
- macAddress: '02:01:53:00:00:00'
masquerade: {}
model: virtio
name: default
networkInterfaceMultiqueue: true
rng: {}
machine:
type: pc-q35-rhel8.4.0
resources:
requests:
memory: 32Gi
evictionStrategy: LiveMigrate
hostname: fedora
networks:
- name: default
pod: {}
terminationGracePeriodSeconds: 180
volumes:
- cloudInitNoCloud:
userData: |
#cloud-config
user: fedora
password: password
chpasswd:
expire: false
ssh_authorized_keys:
- >-
ssh-rsa
SSH-KEY-HERE
name: cloudinitdisk
- dataVolume:
name: fedora-rootdisk-uqf5j
name: rootdisk
status:
conditions:
- lastProbeTime: '2022-02-09T17:24:09Z'
lastTransitionTime: '2022-02-09T17:24:09Z'
message: VMI does not exist
reason: VMINotExists
status: 'False'
type: Ready
printableStatus: Stopped
volumeSnapshotStatuses:
- enabled: false
name: cloudinitdisk
reason: 'Snapshot is not supported for this volumeSource type [cloudinitdisk]'
- enabled: false
name: rootdisk
reason: >-
No VolumeSnapshotClass: Volume snapshots are not configured for this
StorageClass [hostpath-provisioner] [rootdisk]
EOF
Lets go ahead and create the virtual machine:
$ oc create -f ~/fedora-vm.yaml virtualmachine.kubevirt.io/fedora created
Wait a few moments for the virtual machine to get to a running state. We can confirm its running by doing oc get vms:
$ oc get vms NAME AGE STATUS READY fedora 8m17s Running True
Now lets expose the running virtual machines ssh port so we can ssh into it by using the virtctl command:
$ virtctl expose vmi fedora --port=22 --name=fedora-ssh --type=NodePort Service fedora-ssh successfully exposed for vmi fedora
We can confirm the ssh port is exposed and get the port number it uses by running the oc get svc command:
$ oc get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE fedora-ssh NodePort 172.30.220.248 none 22:30106/TCP 7s
Now lets ssh into the fedora virtual machine and become root:
$ ssh fedora@10.11.176.230 -p 30106 The authenticity of host '[10.11.176.230]:30106 ([10.11.176.230]:30106)' can't be established. ECDSA key fingerprint is SHA256:Zmpcpm8vgQc3Oa72RFL0iKU/OPjHshAbHyGO7Smk8oE. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '[10.11.176.230]:30106' (ECDSA) to the list of known hosts. Last login: Wed Feb 9 18:41:14 2022 [fedora@fedora ~]$ sudo bash [root@fedora fedora]#
Once at a root prompt we can execute lspci and see the NVIDIA vGPU we passed to the virtual machine is listed as a device:
[root@fedora fedora]# lspci|grep NVIDIA 06:00.0 VGA compatible controller: NVIDIA Corporation GM204GL [Tesla M60] (rev a1)
At this point all that one would need to do is install the NVIDIA drivers on the virtual machine and then fire up their favorite application that would take advantage of the vGPU in the virtual machine!