- name: Setup GPU passthrough host (Fedora + QEMU/KVM) hosts: virtualization become: true vars: grub_config_file: "/etc/default/grub" grub_cmdline_args: > rhgb quiet intel_iommu=on iommu=pt isolcpus={{ virt_vm_reserved_cpu }} nohz_full={{ virt_vm_reserved_cpu }} rcu_nocbs={{ virt_vm_reserved_cpu }} snd_hda_codec_hdmi.blacklist=1 rd.driver.pre=vfio-pci audit=off default_hugepagesz=1G hugepagesz=1G hugepages=8 mitigations=off loglevel=3 {{ virt_extra_grub_args | default("") | trim}} tasks: - name: Install required packages ansible.builtin.dnf: name: - qemu-kvm - libvirt - virt-install - virt-manager - bridge-utils - pciutils - tuned - python3-lxml - python3-libvirt state: present - name: Enable and start libvirtd ansible.builtin.systemd: name: libvirtd enabled: true state: started - name: Ensure GRUB_CMDLINE_LINUX is configured for vfio, isolcpus, nohz_full, rcu_nocbs ansible.builtin.lineinfile: path: "{{ grub_config_file }}" regexp: '^GRUB_CMDLINE_LINUX=' line: 'GRUB_CMDLINE_LINUX="{{ grub_cmdline_args | trim }}"' backrefs: true notify: Rebuild GRUB config - name: Add VFIO PCI device IDs to modprobe ansible.builtin.copy: dest: /etc/modprobe.d/vfio.conf content: | options vfio-pci ids={{ virt_vfio_ids | join(',') }} install snd_hda_codec_hdmi /bin/false mode: '0644' - name: Blacklist Nouveau and other drivers ansible.builtin.template: src: blacklist.conf.j2 dest: /etc/modprobe.d/ansible-blacklist.conf mode: '0644' when: virt_blacklist is defined - name: Add vfio modules to initramfs config ansible.builtin.copy: dest: /etc/dracut.conf.d/vfio.conf content: | add_drivers+=" vfio vfio_iommu_type1 vfio_pci " mode: '0644' notify: Rebuild initramfs - name: Configure hugepages ansible.posix.sysctl: name: vm.nr_hugepages value: "{{ hugepages_count | default('2048') }}" state: present reload: true - name: Enable KVM modules at boot ansible.builtin.copy: dest: /etc/modules-load.d/kvm.conf content: | kvmgt mdev vfio vfio-pci vfio_iommu_type1 mode: '0644' - name: Set I/O scheduler to 'none' for NVMes ansible.builtin.copy: dest: /etc/udev/rules.d/60-ioscheduler.rules content: | ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none" mode: '0644' notify: - Reload udev rules - Trigger udev to apply changes - name: Activate virtual-host tuned profile ansible.builtin.command: tuned-adm profile virtual-host changed_when: false - name: Set permissions for partition passthrough ansible.builtin.file: path: "/dev/{{ item }}" owner: qemu group: kvm mode: '0660' loop: "{{ virt_raw_block_devices }}" - name: Create udev rules for persistent access ansible.builtin.template: src: 99-qemu-nvme.rules.j2 dest: /etc/udev/rules.d/99-qemu-nvme.rules mode: '0644' notify: - Reload udev rules - Trigger udev to apply changes - name: Create or update virtual machine vars: vm: "{{ item }}" loop: "{{ virt_machines }}" community.libvirt.virt: autostart: "{{ vm.autostart | default(false) }}" name: "{{ vm.name }}" state: running command: define xml: "{{ lookup('template', 'domain.xml.j2') }}" - name: Ensure virtual machines are running vars: vm: "{{ item }}" loop: "{{ virt_machines }}" community.libvirt.virt: name: "{{ vm.name }}" state: running - name: Notify user to reboot for changes ansible.builtin.debug: msg: | Reboot your system to apply IOMMU, CPU isolation, and VFIO settings. After reboot, verify using: `dmesg | grep -i vfio` and `lspci -nnk` handlers: - name: Reload udev rules changed_when: false ansible.builtin.command: udevadm control --reload-rules - name: Trigger udev to apply changes changed_when: false ansible.builtin.command: udevadm trigger - name: Rebuild initramfs changed_when: false ansible.builtin.command: dracut -f - name: Rebuild GRUB config changed_when: false ansible.builtin.command: grub2-mkconfig -o /boot/grub2/grub.cfg when: ansible_facts['distribution'] == "Fedora"