Skip to content

Conversation

@dhsrivas
Copy link
Contributor

@dhsrivas dhsrivas commented Oct 15, 2025

This patch introduces a set of VFIO (Virtual Function I/O) helper APIs to simplify container, IOMMU group, and device management. The new functions provide a clean abstraction over common VFIO operations:

  • get_vfio_container_fd() – open and validate a VFIO container
  • check_vfio_container() – verify API version and IOMMU extension support
  • get_iommu_group_fd() – obtain an IOMMU group file descriptor
  • attach_group_to_container()/detach_group_from_container() – manage group association with a VFIO container
  • get_device_fd() – retrieve a VFIO device file descriptor
  • vfio_device_supports_irq() – check if a device supports the requested number of MSI-X interrupts

Summary by CodeRabbit

  • New Features

    • Adds VFIO utilities for PCI device passthrough: container and IOMMU group management, device access, and MSI‑X IRQ capability checks with clearer error reporting.
  • Tests

    • Adds comprehensive unit tests covering VFIO success and failure paths to validate behavior and prevent regressions.
  • Chores

    • Increases self-test size metric to account for added coverage.

@coderabbitai
Copy link

coderabbitai bot commented Oct 15, 2025

📝 Walkthrough

Walkthrough

Adds a new VFIO utilities module providing functions to manage VFIO containers, IOMMU groups, device FDs, and IRQ queries; introduces unit tests exercising success/failure paths; updates selftests metadata to increase the unit test count.

Changes

Cohort / File(s) Summary of changes
VFIO utilities module
avocado/utils/vfio.py
New module implementing VFIO I/O management functions: get_vfio_container_fd, check_vfio_container, get_iommu_group_fd, attach_group_to_container, detach_group_from_container, get_device_fd, vfio_device_supports_irq. Uses ioctl/open interactions and raises ValueError on failures.
Unit tests for VFIO utilities
selftests/unit/utils/vfio.py
New unittest module VfioUtilsTests that mocks system calls and VFIO helpers to exercise success and failure paths for all added VFIO utility functions.
Selftests metadata
selftests/check.py
Updated TEST_SIZE dictionary: "unit" count changed from 874 to 889.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.82% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly identifies the main change by referencing the utils/vfio module and clearly stating that new APIs for VFIO container and device management are being added, which accurately reflects the content and scope of the pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.0)
avocado/utils/vfio.py
selftests/unit/utils/vfio.py
selftests/check.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mr-avocado mr-avocado bot moved this to Review Requested in Default project Oct 15, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
avocado/utils/vfio.py (3)

40-70: Consider removing the unnecessary return statement.

Line 70 returns True, but since the function raises ValueError on failure, the caller can assume success if no exception is raised. The return value adds no semantic value.

Apply this diff to simplify:

     if not ioctl(container_fd, vfio_check_extension, vfio_type_iommu):
         raise ValueError("does not support type 1 iommu")
-
-    return True

37-157: Optional: Consider custom exception classes for better error categorization.

Static analysis flags TRY003 warnings about long error messages in ValueError exceptions. While the current approach is acceptable, you could optionally define custom exception types (e.g., VfioContainerError, VfioGroupError, VfioDeviceError) to better categorize failures and allow callers to handle different error types separately.

This is a low-priority style improvement and can be deferred to maintain consistency with the rest of the codebase.


27-159: Consider documenting resource cleanup responsibilities.

The functions return file descriptors that need to be closed by the caller. Consider adding notes in the docstrings about cleanup responsibilities, especially for:

  • get_vfio_container_fd() - caller must close the container FD
  • get_iommu_group_fd() - caller must close the group FD and handle detachment
  • get_device_fd() - caller must close the device FD

Example for get_vfio_container_fd:

 def get_vfio_container_fd():
     """get vfio container file descriptor
 
+    Note: The caller is responsible for closing the returned file descriptor
+    when done.
+
     :return: file descriptor of the vfio container.
     :rtype: int
     :raises ValueError: if the vfio container cannot be opened.
     """
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6ac559 and 700ab6f.

📒 Files selected for processing (3)
  • avocado/utils/vfio.py (1 hunks)
  • selftests/check.py (1 hunks)
  • selftests/unit/utils/vfio.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
selftests/unit/utils/vfio.py (1)
avocado/utils/vfio.py (7)
  • get_vfio_container_fd (27-37)
  • check_vfio_container (40-70)
  • get_iommu_group_fd (73-100)
  • attach_group_to_container (103-119)
  • detach_group_from_container (122-138)
  • get_device_fd (141-159)
  • vfio_device_supports_irq (162-185)
avocado/utils/vfio.py (1)
avocado/utils/pci.py (1)
  • get_iommu_group (455-468)
🪛 Ruff (0.14.0)
avocado/utils/vfio.py

37-37: Avoid specifying long messages outside the exception class

(TRY003)


65-65: Avoid specifying long messages outside the exception class

(TRY003)


68-68: Avoid specifying long messages outside the exception class

(TRY003)


90-90: Avoid specifying long messages outside the exception class

(TRY003)


98-98: Avoid specifying long messages outside the exception class

(TRY003)


117-119: Avoid specifying long messages outside the exception class

(TRY003)


136-138: Avoid specifying long messages outside the exception class

(TRY003)


157-157: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (45)
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: Podman Egg task
  • GitHub Check: Podman spawner with 3rd party runner plugin
  • GitHub Check: Egg task ubuntu:24.04
  • GitHub Check: Egg task debian:12.4
  • GitHub Check: Version task debian:11.0
  • GitHub Check: Fedora selftests
  • GitHub Check: Egg task ubi:9.2
  • GitHub Check: Egg task fedora:40
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: Windows with Python 3.13
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Windows with Python 3.9
  • GitHub Check: Smokecheck on Linux with Python 3.11
  • GitHub Check: Smokecheck on Linux with Python 3.10
  • GitHub Check: Static checks
  • GitHub Check: macOS with Python 3.11
  • GitHub Check: Code Coverage (3.11)
🔇 Additional comments (5)
selftests/check.py (1)

30-30: LGTM! Test count correctly reflects new VFIO tests.

The increment of 15 tests accurately matches the 15 test methods added in selftests/unit/utils/vfio.py (VfioUtilsTests class).

selftests/unit/utils/vfio.py (1)

80-176: LGTM! Comprehensive test coverage for VFIO utilities.

The remaining test methods properly cover:

  • Successful IOMMU group FD retrieval with viable group status
  • Group attach/detach operations with both success and failure paths
  • Device FD retrieval with appropriate error handling
  • IRQ support checks with varying interrupt counts

The mocking strategy correctly simulates ioctl responses and verifies expected behavior.

avocado/utils/vfio.py (3)

27-37: LGTM! Proper error handling for container FD retrieval.

The function correctly:

  • Uses os.O_CLOEXEC to ensure the FD is closed on exec
  • Wraps OSError in a ValueError with context
  • Includes clear error messaging

103-138: LGTM! Proper IOMMU group container management.

Both attach_group_to_container and detach_group_from_container correctly:

  • Use ctypes.c_void_p() to pass the container FD as a pointer argument
  • Check ioctl return values (non-zero indicates failure)
  • Provide clear error messages

162-185: LGTM! Proper IRQ support verification.

The function correctly:

  • Packs the IRQ info request with appropriate struct format
  • Calls ioctl to retrieve interrupt information
  • Unpacks the response and extracts the interrupt count (4th field)
  • Returns a boolean indicating whether the device supports the requested count

@codecov
Copy link

codecov bot commented Oct 15, 2025

Codecov Report

❌ Patch coverage is 98.14815% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 73.53%. Comparing base (b6ac559) to head (32dfd3d).

Files with missing lines Patch % Lines
avocado/utils/vfio.py 98.14% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6226      +/-   ##
==========================================
+ Coverage   73.46%   73.53%   +0.06%     
==========================================
  Files         206      207       +1     
  Lines       22487    22541      +54     
==========================================
+ Hits        16521    16576      +55     
+ Misses       5966     5965       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dhsrivas dhsrivas force-pushed the utilies_vfio branch 2 times, most recently from 3936e5a to 5acdb5a Compare October 15, 2025 23:53
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
avocado/utils/vfio.py (2)

91-93: Fix incorrect error handling - os.open() raises exceptions, doesn't return -1.

Line 92 checks if group_fd < 0, but in Python, os.open() raises OSError on failure rather than returning a negative value. This error check is unreachable and ineffective.

Apply this diff to handle errors properly:

     vfio_group = f"/dev/vfio/{pci.get_iommu_group(device)}"
-    group_fd = os.open(vfio_group, os.O_RDWR)
-    if group_fd < 0:
-        raise ValueError(f"Failed to open {vfio_group}, {group_fd}")
+    try:
+        group_fd = os.open(vfio_group, os.O_RDWR)
+    except OSError as e:
+        raise ValueError(f"Failed to open {vfio_group}: {e}") from e

158-160: Replace return-value error check with exception handling for ioctl.

fcntl.ioctl raises OSError on failure rather than returning negative values. Remove the if device_fd < 0 block and wrap the ioctl call in a try/except OSError to handle errors.

Apply this diff:

     buf = ctypes.create_string_buffer(device.encode("utf-8") + b"\x00")
-    device_fd = ioctl(group_fd, vfio_group_get_device_fd, buf)
-    if device_fd < 0:
-        raise ValueError("failed to get vfio device fd")
+    try:
+        device_fd = ioctl(group_fd, vfio_group_get_device_fd, buf)
+    except OSError as e:
+        raise ValueError(f"Failed to get vfio device fd: {e}") from e
 
     return device_fd
selftests/unit/utils/vfio.py (1)

49-60: Fix the mock to raise OSError instead of returning -1.

Line 52 sets mock_open.return_value = -1, but os.open() raises OSError on failure rather than returning -1. This test doesn't properly simulate the real failure scenario. The past review comment indicated this was addressed in commit 3936e5a, but the issue remains.

Apply this diff:

     @mock.patch("avocado.utils.vfio.os.open")
     @mock.patch("avocado.utils.vfio.pci.get_iommu_group")
     def test_get_group_fd_fail_1(self, mock_get_group, mock_open):
-        mock_open.return_value = -1
+        mock_open.side_effect = OSError("No such file or directory")
         mock_get_group.return_value = "42"
         with self.assertRaises(ValueError) as e:
             vfio.get_iommu_group_fd(
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3936e5a and 5acdb5a.

📒 Files selected for processing (3)
  • avocado/utils/vfio.py (1 hunks)
  • selftests/check.py (1 hunks)
  • selftests/unit/utils/vfio.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
selftests/unit/utils/vfio.py (1)
avocado/utils/vfio.py (7)
  • get_vfio_container_fd (28-38)
  • check_vfio_container (41-73)
  • get_iommu_group_fd (76-103)
  • attach_group_to_container (106-122)
  • detach_group_from_container (125-141)
  • get_device_fd (144-162)
  • vfio_device_supports_irq (165-188)
avocado/utils/vfio.py (1)
avocado/utils/pci.py (1)
  • get_iommu_group (455-468)
🪛 Ruff (0.14.0)
avocado/utils/vfio.py

38-38: Avoid specifying long messages outside the exception class

(TRY003)


68-68: Avoid specifying long messages outside the exception class

(TRY003)


71-71: Avoid specifying long messages outside the exception class

(TRY003)


93-93: Avoid specifying long messages outside the exception class

(TRY003)


101-101: Avoid specifying long messages outside the exception class

(TRY003)


120-122: Avoid specifying long messages outside the exception class

(TRY003)


139-141: Avoid specifying long messages outside the exception class

(TRY003)


160-160: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (51)
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: Egg task ubuntu:22.04
  • GitHub Check: Podman spawner with 3rd party runner plugin
  • GitHub Check: Egg task debian:11.0
  • GitHub Check: Egg task ubi:8.8
  • GitHub Check: Fedora selftests
  • GitHub Check: Egg task fedora:41
  • GitHub Check: Egg task ubi:9.2
  • GitHub Check: Fedora develop install/uninstall task
  • GitHub Check: Windows with Python 3.13
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Windows with Python 3.12
  • GitHub Check: Smokecheck on Linux with Python 3.11
  • GitHub Check: Smokecheck on Linux with Python 3.10
  • GitHub Check: macOS with Python 3.11
  • GitHub Check: Static checks
  • GitHub Check: Code Coverage (3.11)
🔇 Additional comments (13)
selftests/check.py (1)

30-30: LGTM!

The unit test count has been correctly updated from 874 to 889, reflecting the addition of 15 new VFIO utility tests in selftests/unit/utils/vfio.py.

avocado/utils/vfio.py (6)

28-38: LGTM!

The function properly handles os.open() failures by catching OSError and re-raising as ValueError with context. Exception chaining using from e preserves the original error details.


67-71: Verify error handling for ioctl calls.

The ioctl() calls on lines 67 and 70 do not wrap exception handling. While ioctl() typically raises OSError on failure, the current implementation assumes success or that exceptions will propagate naturally. Confirm whether callers are expected to handle OSError, or consider wrapping these calls in try/except blocks for more explicit error messages.


97-101: Verify error handling for ioctl call.

The ioctl() call on line 97 does not wrap exception handling. If the ioctl fails, OSError will propagate. Consider whether you want to catch and convert this to a ValueError with a more specific message, or allow the OSError to propagate naturally.


118-122: Verify error handling for ioctl call.

The ioctl() call on line 118 may raise OSError on failure. The current code checks the return value, but if ioctl() raises an exception instead of returning a non-zero value, the check on line 119 becomes unreachable. Verify the expected behavior and consider wrapping in try/except for explicit error handling.


137-141: Verify error handling for ioctl call.

The ioctl() call on line 137 may raise OSError on failure. The current code checks the return value, but if ioctl() raises an exception instead of returning a non-zero value, the check on line 138 becomes unreachable. Verify the expected behavior and consider wrapping in try/except for explicit error handling.


184-185: Verify error handling for ioctl call.

The ioctl() call on line 184 does not wrap exception handling. If the ioctl fails, OSError will propagate. Confirm whether you want this behavior or prefer to catch and convert to a more specific exception.

selftests/unit/utils/vfio.py (6)

11-21: LGTM!

Both success and failure test cases for get_vfio_container_fd() are correct. The failure case properly mocks os.open() to raise OSError, which aligns with the actual behavior of the function.


23-47: LGTM!

The tests for check_vfio_container() properly exercise both failure paths (invalid API version and missing IOMMU extension) using appropriate mock return values.


62-98: LGTM!

The tests for get_iommu_group_fd() success and non-viable group failure cases are well-structured. The use of struct.pack() to simulate ioctl responses correctly tests the binary protocol handling.


100-131: LGTM!

The tests for attach_group_to_container() and detach_group_from_container() properly cover both success and failure paths by mocking ioctl return values.


133-139: LGTM!

The success test for get_device_fd() correctly mocks the ioctl return value and validates the returned file descriptor.


150-176: LGTM!

Both tests for vfio_device_supports_irq() properly exercise the success and failure paths using structured ioctl responses. The tests correctly validate IRQ count comparisons.

This patch introduces a set of VFIO (Virtual Function I/O) helper APIs
to simplify container, IOMMU group, and device management. The new
functions provide a clean abstraction over common VFIO operations:

* get_vfio_container_fd() – open and validate a VFIO container
* check_vfio_container() – verify API version and IOMMU extension support
* get_iommu_group_fd() – obtain an IOMMU group file descriptor
* attach_group_to_container()/detach_group_from_container() – manage group
  association with a VFIO container
* get_device_fd() – retrieve a VFIO device file descriptor
* vfio_device_supports_irq() – check if a device supports the requested
  number of MSI-X interrupts

Signed-off-by: Dheeraj Kumar Srivastava <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5acdb5a and 32dfd3d.

📒 Files selected for processing (3)
  • avocado/utils/vfio.py (1 hunks)
  • selftests/check.py (1 hunks)
  • selftests/unit/utils/vfio.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • selftests/check.py
🧰 Additional context used
🧬 Code graph analysis (2)
selftests/unit/utils/vfio.py (1)
avocado/utils/vfio.py (7)
  • get_vfio_container_fd (28-38)
  • check_vfio_container (41-73)
  • get_iommu_group_fd (76-104)
  • attach_group_to_container (107-123)
  • detach_group_from_container (126-142)
  • get_device_fd (145-164)
  • vfio_device_supports_irq (167-190)
avocado/utils/vfio.py (1)
avocado/utils/pci.py (1)
  • get_iommu_group (455-468)
🪛 Ruff (0.14.0)
avocado/utils/vfio.py

38-38: Avoid specifying long messages outside the exception class

(TRY003)


68-68: Avoid specifying long messages outside the exception class

(TRY003)


71-71: Avoid specifying long messages outside the exception class

(TRY003)


94-94: Avoid specifying long messages outside the exception class

(TRY003)


102-102: Avoid specifying long messages outside the exception class

(TRY003)


121-123: Avoid specifying long messages outside the exception class

(TRY003)


140-142: Avoid specifying long messages outside the exception class

(TRY003)


162-162: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (36)
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:fedora-41-ppc64le
  • GitHub Check: rpm-build:fedora-43-x86_64
  • GitHub Check: rpm-build:centos-stream-9-x86_64
  • GitHub Check: rpm-build:fedora-41-s390x
  • GitHub Check: rpm-build:fedora-41-aarch64
  • GitHub Check: rpm-build:fedora-41-x86_64
  • GitHub Check: rpm-build:fedora-42-x86_64
  • GitHub Check: rpm-build:epel-9-x86_64
  • GitHub Check: rpm-build:fedora-rawhide-x86_64
  • GitHub Check: Egg task ubi:9.2
  • GitHub Check: Egg task ubuntu:22.04
  • GitHub Check: Version task ubuntu:22.04
  • GitHub Check: Egg task debian:12.4
  • GitHub Check: Fedora develop install/uninstall task
  • GitHub Check: Podman Egg task
  • GitHub Check: Egg task debian:11.0
  • GitHub Check: Egg task fedora:41
  • GitHub Check: Egg task ubuntu:24.04
  • GitHub Check: Egg task ubi:8.8
  • GitHub Check: Fedora selftests
  • GitHub Check: Podman spawner with 3rd party runner plugin
  • GitHub Check: Version task debian:12.4
  • GitHub Check: Version task ubi:8.8
  • GitHub Check: Smokecheck on Linux with Python 3.10
  • GitHub Check: Static checks
  • GitHub Check: macOS with Python 3.11
  • GitHub Check: Code Coverage (3.11)
🔇 Additional comments (10)
avocado/utils/vfio.py (3)

28-38: LGTM! Proper exception handling for container FD.

The function correctly wraps os.open() in a try/except block and raises a descriptive ValueError with exception chaining. This properly handles the OSError that os.open() raises on failure.


126-142: Verify ioctl error handling - return value check may be ineffective.

Line 138 checks if ret: after calling ioctl(). Similar to attach_group_to_container(), this return value check may be ineffective if fcntl.ioctl() raises OSError on failure rather than returning error codes.

If ioctl raises OSError on failure (as verified by the script in the previous comment), apply this diff:

 def detach_group_from_container(group_fd, container_fd, vfio_group_unset_container):
     """detach the iommu group of pci device from vfio container

     :param group_fd: iommu group file descriptor
     :type group_fd: int
     :param container_fd: vfio container file descriptor
     :type container_fd: int
     :param vfio_group_unset_container: vfio ioctl to detach iommu group from the
                                        container fd
     :type vfio_group_unset_container: int
     :raises ValueError: if detaching the group to the container fails.
     """
-    ret = ioctl(group_fd, vfio_group_unset_container, ctypes.c_void_p(container_fd))
-    if ret:
-        raise ValueError(
-            "failed to detach pci device's iommu group from vfio container"
-        )
+    try:
+        ioctl(group_fd, vfio_group_unset_container, ctypes.c_void_p(container_fd))
+    except OSError as e:
+        raise ValueError(
+            f"failed to detach pci device's iommu group from vfio container: {e}"
+        ) from e

145-164: LGTM! Proper exception handling for device FD retrieval.

The function correctly wraps ioctl() in a try/except block and raises a descriptive ValueError with exception chaining. This is the correct pattern for handling ioctl failures.

selftests/unit/utils/vfio.py (7)

10-21: LGTM! Container FD tests properly verify exception handling.

Both success and failure test cases are well-structured:

  • Success case mocks os.open() to return a valid FD
  • Failure case properly mocks os.open() to raise OSError and verifies the ValueError is raised with the correct message

23-47: Add tests for ioctl exception path.

While these tests verify the validation logic (API version mismatch and missing IOMMU extension), they don't test the case where ioctl() itself raises an OSError. If the implementation is updated to wrap ioctl in try/except (as suggested in the review of avocado/utils/vfio.py), tests should verify that OSError is properly caught and converted to ValueError.

Consider adding tests like:

@mock.patch("avocado.utils.vfio.ioctl")
def test_check_vfio_container_ioctl_failure(self, mock_ioctl):
    mock_ioctl.side_effect = OSError("ioctl failed")
    with self.assertRaises(ValueError) as e:
        vfio.check_vfio_container(
            container_fd=3,
            vfio_get_api_version=15204,
            vfio_api_version=0,
            vfio_check_extension=15205,
            vfio_type_iommu=1,
        )
    self.assertIn("Failed to get API version", str(e.exception))

49-98: Add test for ioctl exception path and verify FD cleanup.

The tests cover open failure and non-viable group, but don't test the case where the ioctl() call itself raises an OSError. Additionally, if the implementation is updated to close the FD on error (as suggested in the review of avocado/utils/vfio.py), tests should verify that cleanup occurs.

Consider adding a test like:

@mock.patch("avocado.utils.vfio.os.close")
@mock.patch("avocado.utils.vfio.ioctl")
@mock.patch("avocado.utils.vfio.os.open")
@mock.patch("avocado.utils.vfio.pci.get_iommu_group")
def test_get_group_fd_ioctl_failure(self, mock_get_group, mock_open, mock_ioctl, mock_close):
    mock_open.return_value = 3
    mock_get_group.return_value = "42"
    mock_ioctl.side_effect = OSError("ioctl failed")
    
    with self.assertRaises(ValueError) as e:
        vfio.get_iommu_group_fd(
            device="0000:03:00.0",
            vfio_group_get_status=100,
            vfio_group_flags_viable=0x1,
        )
    
    self.assertIn("Failed to get group status", str(e.exception))
    mock_close.assert_called_once_with(3)  # Verify FD was closed

100-131: Tests may not match implementation behavior.

These tests mock ioctl() to return 0 or 1, but in Python, fcntl.ioctl() typically raises OSError on failure rather than returning error codes. If the implementation is corrected to use exception handling (as suggested in the review of avocado/utils/vfio.py), these tests will need updates:

If the implementation is updated to catch OSError, modify the failure tests:

@mock.patch("avocado.utils.vfio.ioctl")
def test_attach_group_to_container_failure(self, mock_ioctl):
    mock_ioctl.side_effect = OSError("Attach failed")
    with self.assertRaises(ValueError) as ctx:
        vfio.attach_group_to_container(
            group_fd=10, container_fd=20, vfio_group_set_container=100
        )
    self.assertIn("failed to attach pci device", str(ctx.exception))

@mock.patch("avocado.utils.vfio.ioctl")
def test_detach_group_from_container_failure(self, mock_ioctl):
    mock_ioctl.side_effect = OSError("Detach failed")
    with self.assertRaises(ValueError) as ctx:
        vfio.detach_group_from_container(
            group_fd=10, container_fd=20, vfio_group_unset_container=200
        )
    self.assertIn("failed to detach pci device", str(ctx.exception))

Keep the success tests as-is since successful ioctl calls typically return normally.


133-148: LGTM! Device FD tests properly verify exception handling.

Both success and failure test cases are well-structured:

  • Success case mocks ioctl() to return a valid device FD
  • Failure case properly mocks ioctl() to raise OSError and verifies the ValueError is raised with the correct message

This aligns with the implementation which already has proper exception handling.


150-176: Add test for ioctl exception path.

While these tests verify the success and insufficient IRQ count scenarios, they don't test the case where ioctl() itself raises an OSError. If the implementation is updated to wrap ioctl in try/except (as suggested in the review of avocado/utils/vfio.py), tests should verify that OSError is properly caught and converted to ValueError.

Consider adding a test like:

@mock.patch("avocado.utils.vfio.ioctl")
def test_vfio_device_supports_irq_ioctl_failure(self, mock_ioctl):
    mock_ioctl.side_effect = OSError("ioctl failed")
    with self.assertRaises(ValueError) as e:
        vfio.vfio_device_supports_irq(
            device_fd=30,
            vfio_pci_msix_irq_index=100,
            vfio_device_get_irq_info=300,
            count=4,
        )
    self.assertIn("Failed to get device IRQ info", str(e.exception))

179-180: LGTM! Standard unittest entry point.

The standard unittest.main() entry point is appropriate for running the tests as a standalone module.

Comment on lines +41 to +73
def check_vfio_container(
container_fd,
vfio_get_api_version,
vfio_api_version,
vfio_check_extension,
vfio_type_iommu,
):
"""validate the vfio container by verifying the api version and ensuring the required
iommu extension is supported. if either validation fails, the test is canceled with
an appropriate message.

:param container_fd: vfio container file descriptor
:type container_fd: int
:param vfio_get_api_version: ioctl to retrieve the vfio container api version
:type vfio_get_api_version: int
:param vfio_api_version: expected vfio api version
:type vfio_api_version: int
:param vfio_check_extension: ioctl to check iommu extension support
:type vfio_check_extension: int
:param vfio_type_iommu: expected vfio iommu type
:type vfio_type_iommu: int
:raises ValueError: if the vfio api version is invalid or if the required
iommu extension is not supported.
:return: True if vfio container fd check passes.
:rtype: bool
"""
if ioctl(container_fd, vfio_get_api_version) != vfio_api_version:
raise ValueError("Failed to get right API version")

if not ioctl(container_fd, vfio_check_extension, vfio_type_iommu):
raise ValueError("does not support type 1 iommu")

return True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add exception handling for ioctl calls.

The two ioctl() calls (lines 67 and 70) are not wrapped in try/except blocks. In Python, fcntl.ioctl() raises OSError on failure rather than returning error codes. If either ioctl fails, an unhandled OSError will propagate instead of the intended ValueError.

Apply this diff to add proper exception handling:

 def check_vfio_container(
     container_fd,
     vfio_get_api_version,
     vfio_api_version,
     vfio_check_extension,
     vfio_type_iommu,
 ):
     """validate the vfio container by verifying the api version and ensuring the required
     iommu extension is supported. if either validation fails, the test is canceled with
     an appropriate message.

     :param container_fd: vfio container file descriptor
     :type container_fd: int
     :param vfio_get_api_version: ioctl to retrieve the vfio container api version
     :type vfio_get_api_version: int
     :param vfio_api_version: expected vfio api version
     :type vfio_api_version: int
     :param vfio_check_extension:  ioctl to check iommu extension support
     :type vfio_check_extension: int
     :param vfio_type_iommu: expected vfio iommu type
     :type vfio_type_iommu: int
     :raises ValueError: if the vfio api version is invalid or if the required
                         iommu extension is not supported.
     :return: True if vfio container fd check passes.
     :rtype: bool
     """
-    if ioctl(container_fd, vfio_get_api_version) != vfio_api_version:
-        raise ValueError("Failed to get right API version")
+    try:
+        api_version = ioctl(container_fd, vfio_get_api_version)
+    except OSError as e:
+        raise ValueError(f"Failed to get API version: {e}") from e
+    
+    if api_version != vfio_api_version:
+        raise ValueError("Failed to get right API version")

-    if not ioctl(container_fd, vfio_check_extension, vfio_type_iommu):
-        raise ValueError("does not support type 1 iommu")
+    try:
+        has_extension = ioctl(container_fd, vfio_check_extension, vfio_type_iommu)
+    except OSError as e:
+        raise ValueError(f"Failed to check IOMMU extension: {e}") from e
+    
+    if not has_extension:
+        raise ValueError("does not support type 1 iommu")

     return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def check_vfio_container(
container_fd,
vfio_get_api_version,
vfio_api_version,
vfio_check_extension,
vfio_type_iommu,
):
"""validate the vfio container by verifying the api version and ensuring the required
iommu extension is supported. if either validation fails, the test is canceled with
an appropriate message.
:param container_fd: vfio container file descriptor
:type container_fd: int
:param vfio_get_api_version: ioctl to retrieve the vfio container api version
:type vfio_get_api_version: int
:param vfio_api_version: expected vfio api version
:type vfio_api_version: int
:param vfio_check_extension: ioctl to check iommu extension support
:type vfio_check_extension: int
:param vfio_type_iommu: expected vfio iommu type
:type vfio_type_iommu: int
:raises ValueError: if the vfio api version is invalid or if the required
iommu extension is not supported.
:return: True if vfio container fd check passes.
:rtype: bool
"""
if ioctl(container_fd, vfio_get_api_version) != vfio_api_version:
raise ValueError("Failed to get right API version")
if not ioctl(container_fd, vfio_check_extension, vfio_type_iommu):
raise ValueError("does not support type 1 iommu")
return True
def check_vfio_container(
container_fd,
vfio_get_api_version,
vfio_api_version,
vfio_check_extension,
vfio_type_iommu,
):
"""validate the vfio container by verifying the api version and ensuring the required
iommu extension is supported. if either validation fails, the test is canceled with
an appropriate message.
:param container_fd: vfio container file descriptor
:type container_fd: int
:param vfio_get_api_version: ioctl to retrieve the vfio container api version
:type vfio_get_api_version: int
:param vfio_api_version: expected vfio api version
:type vfio_api_version: int
:param vfio_check_extension: ioctl to check iommu extension support
:type vfio_check_extension: int
:param vfio_type_iommu: expected vfio iommu type
:type vfio_type_iommu: int
:raises ValueError: if the vfio api version is invalid or if the required
iommu extension is not supported.
:return: True if vfio container fd check passes.
:rtype: bool
"""
try:
api_version = ioctl(container_fd, vfio_get_api_version)
except OSError as e:
raise ValueError(f"Failed to get API version: {e}") from e
if api_version != vfio_api_version:
raise ValueError("Failed to get right API version")
try:
has_extension = ioctl(container_fd, vfio_check_extension, vfio_type_iommu)
except OSError as e:
raise ValueError(f"Failed to check IOMMU extension: {e}") from e
if not has_extension:
raise ValueError("does not support type 1 iommu")
return True
🧰 Tools
🪛 Ruff (0.14.0)

68-68: Avoid specifying long messages outside the exception class

(TRY003)


71-71: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In avocado/utils/vfio.py around lines 41 to 73, the two ioctl() calls are not
exception-handled and will raise OSError instead of the intended ValueError on
failure; wrap each ioctl call in a try/except OSError block, catch the OSError,
and re-raise a ValueError with a clear message that includes the original
exception information (e.g., using f-strings or chaining) so failures produce
the intended ValueError rather than an unhandled OSError.

Comment on lines +76 to +104
def get_iommu_group_fd(device, vfio_group_get_status, vfio_group_flags_viable):
"""get iommu group fd for the pci device

:param device: full pci address including domain (0000:03:00.0)
:type device: str
:param vfio_group_get_status: ioctl to get iommu group status
:type vfio_group_get_status: int
:param vfio_group_flags_viable: ioctl to check if iommu group is viable
:type vfio_group_flags_viable: int
:raises ValueError: if the vfio group device cannot be opened or the group
is not viable.
:return: file descriptor for the iommu group.
:rtype: int
"""
vfio_group = f"/dev/vfio/{pci.get_iommu_group(device)}"
try:
group_fd = os.open(vfio_group, os.O_RDWR)
except OSError as e:
raise ValueError(f"Failed to open {vfio_group}: {e}") from e

argsz = struct.calcsize("II")
group_status_request = struct.pack("II", argsz, 2)
group_status_response = ioctl(group_fd, vfio_group_get_status, group_status_request)
group_status = struct.unpack("II", group_status_response)

if not group_status[1] & vfio_group_flags_viable:
raise ValueError("Group not viable, are all devices attached to vfio?")

return group_fd
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add exception handling for ioctl call.

The ioctl() call at line 98 is not wrapped in a try/except block. If the ioctl fails, an unhandled OSError will propagate instead of a descriptive ValueError. This is inconsistent with other functions in this module (e.g., get_device_fd() which properly wraps ioctl).

Apply this diff to add proper exception handling:

     vfio_group = f"/dev/vfio/{pci.get_iommu_group(device)}"
     try:
         group_fd = os.open(vfio_group, os.O_RDWR)
     except OSError as e:
         raise ValueError(f"Failed to open {vfio_group}: {e}") from e

     argsz = struct.calcsize("II")
     group_status_request = struct.pack("II", argsz, 2)
-    group_status_response = ioctl(group_fd, vfio_group_get_status, group_status_request)
+    try:
+        group_status_response = ioctl(group_fd, vfio_group_get_status, group_status_request)
+    except OSError as e:
+        os.close(group_fd)
+        raise ValueError(f"Failed to get group status: {e}") from e
+    
     group_status = struct.unpack("II", group_status_response)

     if not group_status[1] & vfio_group_flags_viable:
+        os.close(group_fd)
         raise ValueError("Group not viable, are all devices attached to vfio?")

     return group_fd

Note: Added os.close(group_fd) in error paths to prevent file descriptor leaks.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_iommu_group_fd(device, vfio_group_get_status, vfio_group_flags_viable):
"""get iommu group fd for the pci device
:param device: full pci address including domain (0000:03:00.0)
:type device: str
:param vfio_group_get_status: ioctl to get iommu group status
:type vfio_group_get_status: int
:param vfio_group_flags_viable: ioctl to check if iommu group is viable
:type vfio_group_flags_viable: int
:raises ValueError: if the vfio group device cannot be opened or the group
is not viable.
:return: file descriptor for the iommu group.
:rtype: int
"""
vfio_group = f"/dev/vfio/{pci.get_iommu_group(device)}"
try:
group_fd = os.open(vfio_group, os.O_RDWR)
except OSError as e:
raise ValueError(f"Failed to open {vfio_group}: {e}") from e
argsz = struct.calcsize("II")
group_status_request = struct.pack("II", argsz, 2)
group_status_response = ioctl(group_fd, vfio_group_get_status, group_status_request)
group_status = struct.unpack("II", group_status_response)
if not group_status[1] & vfio_group_flags_viable:
raise ValueError("Group not viable, are all devices attached to vfio?")
return group_fd
def get_iommu_group_fd(device, vfio_group_get_status, vfio_group_flags_viable):
"""get iommu group fd for the pci device
:param device: full pci address including domain (0000:03:00.0)
:type device: str
:param vfio_group_get_status: ioctl to get iommu group status
:type vfio_group_get_status: int
:param vfio_group_flags_viable: ioctl to check if iommu group is viable
:type vfio_group_flags_viable: int
:raises ValueError: if the vfio group device cannot be opened or the group
is not viable.
:return: file descriptor for the iommu group.
:rtype: int
"""
vfio_group = f"/dev/vfio/{pci.get_iommu_group(device)}"
try:
group_fd = os.open(vfio_group, os.O_RDWR)
except OSError as e:
raise ValueError(f"Failed to open {vfio_group}: {e}") from e
argsz = struct.calcsize("II")
group_status_request = struct.pack("II", argsz, 2)
try:
group_status_response = ioctl(group_fd, vfio_group_get_status, group_status_request)
except OSError as e:
os.close(group_fd)
raise ValueError(f"Failed to get group status: {e}") from e
group_status = struct.unpack("II", group_status_response)
if not group_status[1] & vfio_group_flags_viable:
os.close(group_fd)
raise ValueError("Group not viable, are all devices attached to vfio?")
return group_fd
🧰 Tools
🪛 Ruff (0.14.0)

94-94: Avoid specifying long messages outside the exception class

(TRY003)


102-102: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In avocado/utils/vfio.py around lines 76 to 104, the ioctl() call that checks
group status is not exception-handled so an OSError would propagate and leak the
opened group_fd; wrap the ioctl and subsequent unpack in a try/except OSError
block, on exception close group_fd and raise a ValueError with a descriptive
message (including the original error), and likewise ensure that if the group is
not viable you close group_fd before raising the ValueError to avoid file
descriptor leaks.

Comment on lines +107 to +123
def attach_group_to_container(group_fd, container_fd, vfio_group_set_container):
"""attach the iommu group of pci device to the vfio container.

:param group_fd: iommu group file descriptor
:type group_fd: int
:param container_fd: vfio container file descriptor
:type container_fd: int
:param vfio_group_set_container: vfio ioctl to add iommu group to the container fd
:type vfio_group_set_container: int
:raises ValueError: if attaching the group to the container fails.
"""

ret = ioctl(group_fd, vfio_group_set_container, ctypes.c_void_p(container_fd))
if ret:
raise ValueError(
"failed to attached pci device's iommu group to the vfio container"
)
Copy link

@coderabbitai coderabbitai bot Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify ioctl error handling - return value check may be ineffective.

Line 119 checks if ret: after calling ioctl(). In Python, fcntl.ioctl() typically raises OSError on failure rather than returning non-zero error codes. This return value check may never trigger as intended.

Run the following script to examine ioctl usage patterns in similar code:

If ioctl raises OSError on failure, apply this diff:

 def attach_group_to_container(group_fd, container_fd, vfio_group_set_container):
     """attach the iommu group of pci device to the vfio container.

     :param group_fd: iommu group file descriptor
     :type group_fd: int
     :param container_fd: vfio container file descriptor
     :type container_fd: int
     :param vfio_group_set_container: vfio ioctl to add iommu group to the container fd
     :type vfio_group_set_container: int
     :raises ValueError: if attaching the group to the container fails.
     """

-    ret = ioctl(group_fd, vfio_group_set_container, ctypes.c_void_p(container_fd))
-    if ret:
-        raise ValueError(
-            "failed to attached pci device's iommu group to the vfio container"
-        )
+    try:
+        ioctl(group_fd, vfio_group_set_container, ctypes.c_void_p(container_fd))
+    except OSError as e:
+        raise ValueError(
+            f"failed to attach pci device's iommu group to the vfio container: {e}"
+        ) from e

🏁 Script executed:

#!/bin/bash
# Search for ioctl error handling patterns in the codebase
echo "=== Searching for ioctl usage patterns ==="
rg -n -C3 'ioctl\(' --type=py -g 'avocado/utils/*.py' | head -50

echo "=== Checking if ioctl is imported ==="
rg -n 'from fcntl import ioctl' --type=py

echo "=== Looking for ioctl exception handling ==="
rg -n -B2 -A5 'try:.*ioctl' --type=py -g 'avocado/utils/*.py'

Length of output: 3161


Refactor attach_group_to_container to use exception-based error handling
In avocado/utils/vfio.py (lines 119–122), replace the ret = ioctl…; if ret: check with a try/except OSError that raises a ValueError including the caught error. For example:

try:
    ioctl(group_fd, vfio_group_set_container, ctypes.c_void_p(container_fd))
except OSError as e:
    raise ValueError(f"failed to attach pci device's iommu group to the vfio container: {e}") from e
🧰 Tools
🪛 Ruff (0.14.0)

121-123: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In avocado/utils/vfio.py around lines 107 to 123, the function
attach_group_to_container currently checks ioctl's integer return and raises a
ValueError; change it to call ioctl inside a try/except OSError block and on
exception raise a ValueError that includes the caught error message and uses
"from e" to preserve the original traceback, so failures surface as a clear
ValueError with underlying OSError details.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhsrivas this comment from CodeRabbit seems very appropriate.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +167 to +190
def vfio_device_supports_irq(
device_fd, vfio_pci_msix_irq_index, vfio_device_get_irq_info, count
):
"""Check if device supports at least count number of interrupts

:param device_fd: device file descriptor
:type device_fd: int
:param vfio_pci_msix_irq_index: vfio ioctl to get irq index for msix
:type vfio_pci_msix_irq_index: int
:param vfio_device_get_irq_info: vfio ioctl to get vfio device irq information
:type vfio_device_get_irq_info: int
:param count: number of irqs the device should support
:type count: int
:return: true if supported, false otherwise
:rtype: bool
"""
argsz = struct.calcsize("IIII")
index = vfio_pci_msix_irq_index
irq_info_request = struct.pack("IIII", argsz, 1, index, 1)
irq_info_response = ioctl(device_fd, vfio_device_get_irq_info, irq_info_request)
nirq = (struct.unpack("IIII", irq_info_response))[3]
if nirq < count:
return False
return True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add exception handling for ioctl call.

The ioctl() call at line 186 is not wrapped in a try/except block. If the ioctl fails, an unhandled OSError will propagate. For consistency with get_device_fd() and proper error handling, this should be wrapped in try/except.

Apply this diff to add proper exception handling:

 def vfio_device_supports_irq(
     device_fd, vfio_pci_msix_irq_index, vfio_device_get_irq_info, count
 ):
     """Check if device supports at least count number of interrupts

     :param device_fd: device file descriptor
     :type device_fd: int
     :param vfio_pci_msix_irq_index: vfio ioctl to get irq index for msix
     :type vfio_pci_msix_irq_index: int
     :param vfio_device_get_irq_info: vfio ioctl to get vfio device irq information
     :type vfio_device_get_irq_info: int
     :param count: number of irqs the device should support
     :type count: int
+    :raises ValueError: if unable to query device IRQ information
     :return: true if supported, false otherwise
     :rtype: bool
     """
     argsz = struct.calcsize("IIII")
     index = vfio_pci_msix_irq_index
     irq_info_request = struct.pack("IIII", argsz, 1, index, 1)
-    irq_info_response = ioctl(device_fd, vfio_device_get_irq_info, irq_info_request)
+    try:
+        irq_info_response = ioctl(device_fd, vfio_device_get_irq_info, irq_info_request)
+    except OSError as e:
+        raise ValueError(f"Failed to get device IRQ info: {e}") from e
+    
     nirq = (struct.unpack("IIII", irq_info_response))[3]
     if nirq < count:
         return False
     return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def vfio_device_supports_irq(
device_fd, vfio_pci_msix_irq_index, vfio_device_get_irq_info, count
):
"""Check if device supports at least count number of interrupts
:param device_fd: device file descriptor
:type device_fd: int
:param vfio_pci_msix_irq_index: vfio ioctl to get irq index for msix
:type vfio_pci_msix_irq_index: int
:param vfio_device_get_irq_info: vfio ioctl to get vfio device irq information
:type vfio_device_get_irq_info: int
:param count: number of irqs the device should support
:type count: int
:return: true if supported, false otherwise
:rtype: bool
"""
argsz = struct.calcsize("IIII")
index = vfio_pci_msix_irq_index
irq_info_request = struct.pack("IIII", argsz, 1, index, 1)
irq_info_response = ioctl(device_fd, vfio_device_get_irq_info, irq_info_request)
nirq = (struct.unpack("IIII", irq_info_response))[3]
if nirq < count:
return False
return True
def vfio_device_supports_irq(
device_fd, vfio_pci_msix_irq_index, vfio_device_get_irq_info, count
):
"""Check if device supports at least count number of interrupts
:param device_fd: device file descriptor
:type device_fd: int
:param vfio_pci_msix_irq_index: vfio ioctl to get irq index for msix
:type vfio_pci_msix_irq_index: int
:param vfio_device_get_irq_info: vfio ioctl to get vfio device irq information
:type vfio_device_get_irq_info: int
:param count: number of irqs the device should support
:type count: int
:raises ValueError: if unable to query device IRQ information
:return: true if supported, false otherwise
:rtype: bool
"""
argsz = struct.calcsize("IIII")
index = vfio_pci_msix_irq_index
irq_info_request = struct.pack("IIII", argsz, 1, index, 1)
try:
irq_info_response = ioctl(device_fd, vfio_device_get_irq_info, irq_info_request)
except OSError as e:
raise ValueError(f"Failed to get device IRQ info: {e}") from e
nirq = (struct.unpack("IIII", irq_info_response))[3]
if nirq < count:
return False
return True
🤖 Prompt for AI Agents
In avocado/utils/vfio.py around lines 167 to 190, the ioctl() call that fetches
IRQ info is not protected and will let OSError propagate; wrap the
ioctl(device_fd, vfio_device_get_irq_info, irq_info_request) call in a
try/except OSError block and on failure raise a RuntimeError (or rethrow a
contextual exception) that includes a clear message and the original exception
details so callers get consistent, descriptive error handling similar to
get_device_fd().

dhsrivas added a commit to dhsrivas/avocado-misc-tests that referenced this pull request Oct 16, 2025
Adds functionality to test interrupt remapping support for a PCI device
using VFIO ioctls.

- Verifies if the PCI device supports "count" MSI-X interrupts.
- Binds the input PCI device to the vfio-pci driver.
- Attempts to allocate "count" number of IRQs with IOMMU Interrupt
  remapping enabled; the test fails if allocation is unsuccessful.

This patch is depended on
1. avocado-framework/avocado#6225
2. avocado-framework/avocado#6226

Signed-off-by: Dheeraj Kumar Srivastava <[email protected]>
@dhsrivas
Copy link
Contributor Author

@richtja In continuation to #6205. This PR contains new utility vfio.py file added in avocado. I have addressed all your previous review comments for utilities in this PR. Please review and let me know if you have any further review comments.

Q. vfio.py depends on few pci.py avocado utils. Have we moved pci.py file utilities to AAutils.

Copy link
Contributor

@clebergnu clebergnu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @dhsrivas,

Thanks for this contribution, it looks like a nice addition to vfio users. But, there are a few comments from me I'd like for you to respond to.

Comment on lines +107 to +123
def attach_group_to_container(group_fd, container_fd, vfio_group_set_container):
"""attach the iommu group of pci device to the vfio container.

:param group_fd: iommu group file descriptor
:type group_fd: int
:param container_fd: vfio container file descriptor
:type container_fd: int
:param vfio_group_set_container: vfio ioctl to add iommu group to the container fd
:type vfio_group_set_container: int
:raises ValueError: if attaching the group to the container fails.
"""

ret = ioctl(group_fd, vfio_group_set_container, ctypes.c_void_p(container_fd))
if ret:
raise ValueError(
"failed to attached pci device's iommu group to the vfio container"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhsrivas this comment from CodeRabbit seems very appropriate.

from avocado.utils import pci


def get_vfio_container_fd():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see this function being used publicly or only by the vfio module itself? If this is probably not going to be used by end users, maybe making it a private function can give more API flexiblity. And, TBH, it seems to provide little value other than standardization.

vfio_type_iommu,
):
"""validate the vfio container by verifying the api version and ensuring the required
iommu extension is supported. if either validation fails, the test is canceled with
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a generic utility, it's better to talk about the behavior of this function without strictly talking about "the test".

@mock.patch("os.open")
def test_get_vfio_container_fd_success(self, mock_open):
mock_open.return_value = 10
vfio.get_vfio_container_fd()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the simple logic on get_vfio_container_fd(), the use of mocking and lack of checks here, I can't see actual value in this particular test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Review Requested

Development

Successfully merging this pull request may close these issues.

2 participants