diff --git a/include/systematic/linux/dma_buf.hpp b/include/systematic/linux/dma_buf.hpp index 5154a4b..445a9ff 100644 --- a/include/systematic/linux/dma_buf.hpp +++ b/include/systematic/linux/dma_buf.hpp @@ -4,7 +4,9 @@ #include +#include #include +#include #include @@ -24,6 +26,15 @@ namespace systematic :: linux_ :: dma_buf } + /// @brief How SyncGuard treats an fd that is not a DMA-BUF. + /// + enum class SyncPolicy + { + Strict, ///< Any ioctl failure throws. + BestEffort, ///< A non-DMA-BUF fd (`ENOTTY`) is a no-op; other failures throw. + }; + + /// @brief RAII guard bracketing CPU access to a DMA-BUF. /// /// Calls sync() with `DMA_BUF_SYNC_START | flags` on construction; on @@ -38,24 +49,43 @@ namespace systematic :: linux_ :: dma_buf /// @param flags Access mode (`DMA_BUF_SYNC_READ`, `DMA_BUF_SYNC_WRITE`, or /// `DMA_BUF_SYNC_RW`). Do not pass `DMA_BUF_SYNC_START` / `DMA_BUF_SYNC_END`. /// - /// @throws std::system_error if `DMA_BUF_IOCTL_SYNC` fails. + /// @param policy Whether a non-DMA-BUF fd is an error (`Strict`) or a no-op + /// (`BestEffort`). Use `BestEffort` when the fd may reference plain, + /// already-CPU-coherent memory (e.g. a memfd) that needs no syncing. + /// + /// @throws std::system_error if `DMA_BUF_IOCTL_SYNC` fails, except `ENOTTY` + /// under `SyncPolicy::BestEffort`. /// - explicit SyncGuard(int fd, std::uint64_t flags) + explicit SyncGuard(int fd, std::uint64_t flags, SyncPolicy policy = SyncPolicy::Strict) : fd_ {fd} , flags_ {flags} { - sync(fd_, DMA_BUF_SYNC_START | flags_); + ::dma_buf_sync arg { .flags = DMA_BUF_SYNC_START | flags_ }; + + if (::ioctl(fd_, DMA_BUF_IOCTL_SYNC, &arg) != 0) + { + // A non-DMA-BUF fd (e.g. a plain memfd) rejects the ioctl with ENOTTY. + // Such memory is already CPU-coherent, so BestEffort treats it as a no-op. + if (policy == SyncPolicy::BestEffort && errno == ENOTTY) + active_ = false; + else + throw std::system_error {errno, std::system_category(), "DMA_BUF_IOCTL_SYNC failed"}; + } } /// @brief Ends the CPU-access window. /// - /// ioctl() errors are ignored (return value not checked). + /// ioctl() errors are ignored (return value not checked). Does nothing when + /// construction found the fd was not a DMA-BUF. /// ~SyncGuard() { - ::dma_buf_sync arg { .flags = DMA_BUF_SYNC_END | flags_ }; - ::ioctl(fd_, DMA_BUF_IOCTL_SYNC, &arg); + if (active_) + { + ::dma_buf_sync arg { .flags = DMA_BUF_SYNC_END | flags_ }; + ::ioctl(fd_, DMA_BUF_IOCTL_SYNC, &arg); + } } @@ -65,5 +95,6 @@ namespace systematic :: linux_ :: dma_buf private: int const fd_; std::uint64_t const flags_; + bool active_ = true; }; }