| /* |
| * Copyright © 2021 The Fuchsia Authors |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| * |
| */ |
| |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| |
| #include "inflight_list.h" |
| #include "mos_bufmgr.h" |
| #include "mos_bufmgr_priv.h" |
| #include "simple_allocator.h" |
| #include <magma/magma.h> |
| #include <magma_intel_gen_defs.h> |
| #include <magma_fd.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <mutex> |
| #include <vector> |
| |
| #define LOG_VERBOSE(msg, ...) do { \ |
| if (false) {\ |
| fprintf(stderr, "mos_bufmgr_magma:%d " msg "\n", __LINE__, ##__VA_ARGS__); \ |
| fflush(stderr); \ |
| } \ |
| } while (0) |
| |
| static inline uint64_t page_size() { return sysconf(_SC_PAGESIZE); } |
| |
| class MagmaBo; |
| |
| class MagmaBufMgr : public mos_bufmgr { |
| public: |
| MagmaBufMgr(magma_connection_t connection, uint32_t device_id); |
| ~MagmaBufMgr(); |
| |
| magma_connection_t connection() const { return connection_; } |
| magma_handle_t notification_handle() const { return notification_handle_; } |
| |
| uint32_t device_id() const { return device_id_; } |
| |
| bool Alloc(size_t size, uint8_t align_pow2, uint64_t* addr_out) { |
| std::lock_guard<std::mutex> lock(allocator_mutex_); |
| return allocator_->Alloc(size, align_pow2, addr_out); |
| } |
| |
| bool Free(uint64_t addr) { |
| std::lock_guard<std::mutex> lock(allocator_mutex_); |
| return allocator_->Free(addr); |
| } |
| |
| MagmaBo* CreateBo(const char *name, unsigned long size); |
| MagmaBo* ImportBo(int zx_handle, unsigned long size); |
| |
| InflightList* inflight_list() { return &inflight_list_;} |
| |
| uint32_t next_vm_id() { |
| uint32_t id = next_pseudo_vm_id_.fetch_add(1); |
| // Don't support wrap around. |
| assert(id != 0); |
| return id; |
| } |
| |
| static constexpr uint32_t kInvalidVmId = 0; |
| |
| // Sets the pseudo VM ID if not already set. |
| uint32_t GetVmId(uint32_t vm_id) { |
| uint32_t invalid_id = kInvalidVmId; |
| vm_id_.compare_exchange_strong(invalid_id, vm_id); |
| return vm_id_.load(); |
| } |
| |
| private: |
| magma_connection_t connection_ {}; |
| magma_handle_t notification_handle_{}; |
| uint32_t device_id_; |
| std::mutex allocator_mutex_; |
| std::unique_ptr<SimpleAllocator> allocator_ __attribute__((__guarded_by__(allocator_mutex_))); |
| InflightList inflight_list_; |
| std::atomic_uint next_pseudo_vm_id_ {1}; |
| std::atomic_uint vm_id_ { kInvalidVmId }; |
| }; |
| |
| |
| class MagmaBo : public mos_linux_bo { |
| public: |
| // MagmaBufMgr must outlive MagmaBo. |
| MagmaBo(magma_buffer_t buffer, uint64_t size, magma_buffer_id_t id, MagmaBufMgr *bufmgr); |
| |
| private: |
| ~MagmaBo(); |
| |
| public: |
| void AddRef() { ref_count_ += 1; } |
| |
| void RemoveRef() { |
| ref_count_ -= 1; |
| int32_t count = ref_count_; |
| assert(count >= 0); |
| if (count == 0) { |
| delete this; |
| } |
| } |
| |
| MagmaBufMgr* magma_bufmgr() const { return static_cast<MagmaBufMgr*>(bufmgr); } |
| |
| uint64_t id() { return id_; } |
| |
| magma_buffer_t buffer() const { return buffer_; } |
| |
| bool is_mapped_gpu() { return is_mapped_gpu_; } |
| |
| std::vector<magma_exec_resource>& exec_resources() { return exec_resources_; } |
| |
| bool MapGpu(); |
| |
| void AddExecResource(MagmaBo* bo); |
| |
| void clear_exec_resources() { exec_resources_.clear(); } |
| |
| private: |
| std::atomic_int ref_count_{}; |
| magma_buffer_t buffer_ {}; |
| uint64_t id_ {}; |
| bool is_mapped_gpu_= false; |
| std::vector<magma_exec_resource> exec_resources_; |
| }; |
| |
| MagmaBo::MagmaBo(magma_buffer_t buffer, uint64_t size, magma_buffer_id_t id, MagmaBufMgr *bufmgr) |
| : buffer_(buffer), id_(id) { |
| mos_linux_bo::size = size; |
| assert(mos_linux_bo::size == size); |
| mos_linux_bo::align = 0; |
| mos_linux_bo::virt = nullptr; |
| mos_linux_bo::bufmgr = bufmgr; |
| mos_linux_bo::handle = 0; |
| mos_linux_bo::offset = 0; |
| mos_linux_bo::offset64 = 0; |
| mos_linux_bo::aux_mapped = false; |
| |
| AddRef(); |
| } |
| |
| MagmaBo::~MagmaBo() { |
| magma_connection_release_buffer(magma_bufmgr()->connection(), buffer()); |
| |
| if (is_mapped_gpu_) { |
| magma_bufmgr()->Free(offset64); |
| } |
| } |
| |
| void MagmaBo::AddExecResource(MagmaBo* bo) { |
| if (exec_resources_.empty()) { |
| // Add this as the first resource. |
| exec_resources_.push_back({ |
| .buffer_id = id(), |
| .offset = 0, |
| .length = size, |
| }); |
| } |
| exec_resources_.push_back({ .buffer_id = bo->id(), |
| .offset = 0, |
| .length = bo->size} |
| ); |
| } |
| |
| bool MagmaBo::MapGpu() { |
| if (is_mapped_gpu_) |
| return true; |
| |
| if (!magma_bufmgr()->Alloc(size, __builtin_ctz(PAGE_SIZE_64K), &offset64)) { |
| LOG_VERBOSE("Alloc failed: size %lu", size); |
| return false; |
| } |
| |
| uint64_t page_count = size / page_size(); |
| constexpr uint64_t kFlags = MAGMA_MAP_FLAG_READ | MAGMA_MAP_FLAG_EXECUTE | |
| MAGMA_MAP_FLAG_WRITE; |
| |
| LOG_VERBOSE("Map buffer %lu addr 0x%lx", id(), offset64); |
| |
| magma_status_t status = |
| magma_connection_map_buffer(magma_bufmgr()->connection(), offset64, buffer(), |
| /*page_offset=*/0, page_count * PAGE_SIZE, kFlags); |
| |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_map_buffer failed: %d", status); |
| return false; |
| } |
| |
| is_mapped_gpu_ = true; |
| |
| return true; |
| } |
| |
| class MagmaContext : public mos_linux_context { |
| public: |
| MagmaContext(struct mos_bufmgr* bufmgr, uint32_t context_id) { |
| this->ctx_id = context_id; |
| this->bufmgr = bufmgr; |
| this->pOsContext = nullptr; |
| this->vm = nullptr; |
| } |
| |
| // Should only be called once. |
| void set_target_engines(std::vector<uint32_t>&& engines) { |
| assert(!engines.empty()); |
| assert(target_engines_.empty()); |
| target_engines_ = std::move(engines); |
| } |
| |
| const std::vector<uint32_t>& target_engines() { return target_engines_; } |
| |
| // Should only be called once. |
| void set_vm_id(uint32_t vm_id) { |
| assert(vm_id != 0); |
| assert(vm_id_ == 0); |
| vm_id_ = vm_id; |
| } |
| |
| uint32_t vm_id() { return vm_id_; } |
| |
| // Should only be called once. |
| void set_ensure_ordering_across_engines() { |
| assert(!ensure_ordering_across_engines_); |
| ensure_ordering_across_engines_ = true; |
| } |
| |
| bool ensure_ordering_across_engines() { return ensure_ordering_across_engines_; } |
| |
| static constexpr uint64_t kInitialFlags = 0; |
| |
| // Sets the command buffer flags if not already set. |
| uint64_t GetCommandBufferFlags(uint64_t command_buffer_flags) { |
| uint64_t unset = kInitialFlags; |
| command_buffer_flags_.compare_exchange_strong(unset, command_buffer_flags); |
| return command_buffer_flags_.load(); |
| } |
| |
| private: |
| std::vector<uint32_t> target_engines_; |
| uint32_t vm_id_ = 0; |
| bool ensure_ordering_across_engines_ = false; |
| std::atomic_uint64_t command_buffer_flags_ {kInitialFlags}; |
| }; |
| |
| static void bufmgr_destroy(struct mos_bufmgr* mos_bufmgr) |
| { |
| LOG_VERBOSE("bufmgr_destroy"); |
| |
| delete static_cast<MagmaBufMgr*>(mos_bufmgr); |
| } |
| |
| static struct mos_linux_bo *bufmgr_bo_alloc_for_render(struct mos_bufmgr *bufmgr, |
| const char *name, |
| unsigned long size, |
| unsigned int alignment, |
| int mem_type) |
| { |
| LOG_VERBOSE("bo_alloc_for_render not implemented"); |
| return nullptr; |
| } |
| |
| static struct mos_linux_bo *bufmgr_bo_alloc(struct mos_bufmgr *mos_bufmgr, |
| const char *name, |
| unsigned long size, |
| unsigned int /*alignment*/, |
| int /*mem_type*/) |
| { |
| MagmaBo* bo = static_cast<MagmaBufMgr*>(mos_bufmgr)->CreateBo(name, size); |
| |
| if (bo) |
| LOG_VERBOSE("bo_alloc '%s' size %lu buffer %lu", name, size, bo->id()); |
| |
| return bo; |
| } |
| |
| struct mos_linux_bo *mos_bo_gem_create_from_prime(struct mos_bufmgr *mos_bufmgr, int prime_fd, int size) |
| { |
| MagmaBo* bo = static_cast<MagmaBufMgr*>(mos_bufmgr)->ImportBo(prime_fd, size); |
| if (bo) |
| LOG_VERBOSE("bo_create_from_prime size %d buffer %lu", size, bo->id()); |
| return bo; |
| } |
| |
| |
| static uint64_t get_pitch_with_tiling(uint64_t pitch, uint32_t tiling) |
| { |
| switch (tiling) { |
| case I915_TILING_NONE: |
| return ROUND_UP_TO(pitch, 64); |
| |
| case I915_TILING_X: |
| return ROUND_UP_TO(pitch, 512); |
| |
| case I915_TILING_Y: |
| return ROUND_UP_TO(pitch, 128); |
| } |
| assert(false); |
| return 0; |
| } |
| |
| static uint64_t get_height_with_tiling(uint64_t height, uint32_t tiling) |
| { |
| switch (tiling) { |
| case I915_TILING_NONE: |
| return ROUND_UP_TO(height, 2); |
| |
| case I915_TILING_X: |
| return ROUND_UP_TO(height, 8); |
| |
| case I915_TILING_Y: |
| return ROUND_UP_TO(height, 32); |
| } |
| assert(false); |
| return 0; |
| } |
| |
| static struct mos_linux_bo *bufmgr_bo_alloc_tiled(struct mos_bufmgr *bufmgr, const char *name, |
| int x, int y, int cpp, uint32_t *tiling_mode, |
| unsigned long *pitch, unsigned long flags, |
| int mem_type) |
| { |
| uint32_t tiling = *tiling_mode; |
| uint64_t stride = get_pitch_with_tiling(x * cpp, tiling); |
| uint64_t height = get_height_with_tiling(y, tiling); |
| uint64_t size = ROUND_UP_TO(stride * height, 4096); |
| |
| *pitch = stride; |
| |
| MagmaBo* bo = static_cast<MagmaBufMgr*>(bufmgr)->CreateBo(name, size); |
| |
| if (bo) |
| LOG_VERBOSE("bo_alloc_tiled '%s' x %d y %d cpp %d tiling %u size %lu buffer %lu", |
| name, x, y, cpp, tiling, size, bo->id()); |
| |
| return bo; |
| } |
| |
| static void bufmgr_bo_reference(struct mos_linux_bo *mos_linux_bo) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| LOG_VERBOSE("bo_reference %lu\n", bo->id()); |
| bo->AddRef(); |
| } |
| |
| static void bufmgr_bo_unreference(struct mos_linux_bo *mos_linux_bo) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| LOG_VERBOSE("bo_unreference %lu", bo->id()); |
| bo->RemoveRef(); |
| } |
| |
| static int bufmgr_bo_map(struct mos_linux_bo *mos_linux_bo, int write_enable) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| assert(!bo->virt); |
| |
| zx_handle_t vmo_handle; |
| { |
| magma_handle_t handle; |
| magma_status_t status = magma_buffer_get_handle(bo->buffer(), &handle); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_get_buffer_handle failed: status %d", status); |
| return -1; |
| } |
| vmo_handle = handle; |
| } |
| |
| // mos_bufmgr always maps RW on the CPU |
| zx_vaddr_t zx_vaddr; |
| zx_status_t zx_status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, |
| 0 /*vmar_offset*/, vmo_handle, 0 /*offset*/, bo->size, &zx_vaddr); |
| |
| zx_handle_close(vmo_handle); |
| |
| if (zx_status != ZX_OK) { |
| LOG_VERBOSE("zx_vmar_map failed: status %d", zx_status); |
| return -1; |
| } |
| |
| bo->virt = reinterpret_cast<void*>(zx_vaddr); |
| |
| LOG_VERBOSE("mapped buffer %lu address %p write_enable %d", bo->id(), bo->virt, write_enable); |
| return 0; |
| } |
| |
| static int bufmgr_bo_unmap(struct mos_linux_bo *mos_linux_bo) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| |
| zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(), reinterpret_cast<uintptr_t>(bo->virt), bo->size); |
| if (status != ZX_OK) { |
| LOG_VERBOSE("zx_vmar_unmap failed: %d", status); |
| return -1; |
| } |
| |
| bo->virt = nullptr; |
| |
| LOG_VERBOSE("unmapped buffer %lu", bo->id()); |
| return 0; |
| } |
| |
| static int bufmgr_bo_subdata(struct mos_linux_bo *mos_linux_bo, unsigned long offset, |
| unsigned long size, const void *data) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| |
| assert(offset + size >= offset); |
| assert(offset + size <= bo->size); |
| |
| if (!bo->virt) { |
| LOG_VERBOSE("bo_subdata: not mapped"); |
| return -1; |
| } |
| |
| LOG_VERBOSE("bo_subdata %lu offset %lu size %lu", bo->id(), offset, size); |
| |
| memcpy(reinterpret_cast<uint8_t*>(bo->virt) + offset, data, size); |
| |
| return 0; |
| } |
| |
| static int bufmgr_bo_set_softpin(struct mos_linux_bo *bo) |
| { |
| // Never called because buffers mapped at creation. |
| assert(false); |
| return 0; |
| } |
| |
| static int bufmgr_bo_add_softpin_target(struct mos_linux_bo *bo_base, struct mos_linux_bo *target_bo_base, bool /*write_flag*/) |
| { |
| auto bo = static_cast<MagmaBo*>(bo_base); |
| |
| auto target_bo = static_cast<MagmaBo*>(target_bo_base); |
| |
| bo->AddExecResource(target_bo); |
| |
| LOG_VERBOSE("bo_add_softpin_target bo %lu target_bo %lu", bo->id(), target_bo->id()); |
| |
| return 0; |
| } |
| |
| // Stubs |
| extern int bufmgr_bo_get_subdata(struct mos_linux_bo *bo, unsigned long offset, |
| unsigned long size, void *data); |
| extern void bufmgr_bo_wait_rendering(struct mos_linux_bo *bo); |
| extern int bufmgr_bo_pad_to_size(struct mos_linux_bo *bo, uint64_t pad_to_size); |
| extern int bufmgr_bo_emit_reloc(struct mos_linux_bo *bo, uint32_t offset, |
| struct mos_linux_bo *target_bo, uint32_t target_offset, |
| uint32_t read_domains, uint32_t write_domain); |
| extern int bufmgr_bo_emit_reloc2(struct mos_linux_bo *bo, uint32_t offset, |
| struct mos_linux_bo *target_bo, uint32_t target_offset, |
| uint32_t read_domains, uint32_t write_domain, |
| uint64_t presumed_offset); |
| extern int bufmgr_bo_emit_reloc_fence(struct mos_linux_bo *bo, uint32_t offset, |
| struct mos_linux_bo *target_bo, |
| uint32_t target_offset, |
| uint32_t read_domains, uint32_t write_domain); |
| extern int bufmgr_bo_pin(struct mos_linux_bo *bo, uint32_t alignment); |
| extern int bufmgr_bo_unpin(struct mos_linux_bo *bo); |
| extern int bufmgr_bo_get_tiling(struct mos_linux_bo *bo, uint32_t * tiling_mode, |
| uint32_t * swizzle_mode); |
| extern int bufmgr_bo_set_tiling(struct mos_linux_bo *bo, uint32_t * tiling_mode, |
| uint32_t stride); |
| extern int bufmgr_bo_flink(struct mos_linux_bo *bo, uint32_t * name); |
| extern int bufmgr_bo_exec2(struct mos_linux_bo *bo, int used, |
| drm_clip_rect_t *cliprects, int num_cliprects, |
| int DR4); |
| extern int bufmgr_bo_mrb_exec2(struct mos_linux_bo *bo, int used, |
| drm_clip_rect_t *cliprects, int num_cliprects, int DR4, |
| unsigned int flags); |
| extern int bufmgr_bo_busy(struct mos_linux_bo *bo); |
| extern int bufmgr_bo_madvise(struct mos_linux_bo *bo, int madv); |
| extern int bufmgr_check_aperture_space(struct mos_linux_bo **bo_array, int count); |
| extern int bufmgr_bo_disable_reuse(struct mos_linux_bo *bo); |
| extern int bufmgr_bo_is_reusable(struct mos_linux_bo *bo); |
| extern int bufmgr_get_pipe_from_crtc_id(struct mos_bufmgr *bufmgr, int crtc_id); |
| extern int bufmgr_bo_references(struct mos_linux_bo *bo, struct mos_linux_bo *target_bo); |
| |
| MagmaBufMgr::MagmaBufMgr(magma_connection_t connection, uint32_t device_id) : |
| connection_(connection), device_id_(device_id) { |
| notification_handle_ = magma_connection_get_notification_channel_handle(connection_); |
| |
| mos_bufmgr::bo_alloc = bufmgr_bo_alloc; |
| mos_bufmgr::bo_alloc_for_render = bufmgr_bo_alloc_for_render; |
| mos_bufmgr::bo_alloc_tiled = bufmgr_bo_alloc_tiled; |
| mos_bufmgr::bo_reference = bufmgr_bo_reference; |
| mos_bufmgr::bo_unreference = bufmgr_bo_unreference; |
| mos_bufmgr::bo_map = bufmgr_bo_map; |
| mos_bufmgr::bo_unmap = bufmgr_bo_unmap; |
| mos_bufmgr::bo_subdata = bufmgr_bo_subdata; |
| mos_bufmgr::bo_get_subdata = bufmgr_bo_get_subdata; |
| mos_bufmgr::bo_wait_rendering = bufmgr_bo_wait_rendering; |
| mos_bufmgr::bo_pad_to_size = bufmgr_bo_pad_to_size; |
| mos_bufmgr::bo_emit_reloc = bufmgr_bo_emit_reloc; |
| mos_bufmgr::bo_emit_reloc2 = bufmgr_bo_emit_reloc2; |
| mos_bufmgr::bo_emit_reloc_fence = bufmgr_bo_emit_reloc_fence; |
| mos_bufmgr::bo_pin = bufmgr_bo_pin; |
| mos_bufmgr::bo_unpin = bufmgr_bo_unpin; |
| mos_bufmgr::bo_get_tiling = bufmgr_bo_get_tiling; |
| mos_bufmgr::bo_set_tiling = bufmgr_bo_set_tiling; |
| mos_bufmgr::bo_flink = bufmgr_bo_flink; |
| mos_bufmgr::bo_exec = bufmgr_bo_exec2; |
| mos_bufmgr::bo_mrb_exec = bufmgr_bo_mrb_exec2; |
| mos_bufmgr::bo_busy = bufmgr_bo_busy; |
| mos_bufmgr::bo_madvise = bufmgr_bo_madvise; |
| mos_bufmgr::destroy = bufmgr_destroy; |
| mos_bufmgr::check_aperture_space = bufmgr_check_aperture_space; |
| mos_bufmgr::bo_disable_reuse = bufmgr_bo_disable_reuse; |
| mos_bufmgr::bo_is_reusable = bufmgr_bo_is_reusable; |
| mos_bufmgr::get_pipe_from_crtc_id = bufmgr_get_pipe_from_crtc_id; |
| mos_bufmgr::bo_references = bufmgr_bo_references; |
| mos_bufmgr::bo_set_softpin = bufmgr_bo_set_softpin; |
| mos_bufmgr::bo_add_softpin_target = bufmgr_bo_add_softpin_target; |
| |
| #if DEBUG |
| mos_bufmgr::debug = 1; |
| #else |
| mos_bufmgr::debug = 0; |
| #endif |
| |
| constexpr uint64_t kSize = MEMZONE_TOTAL - MEMZONE_SYS_START; |
| allocator_ = SimpleAllocator::Create(MEMZONE_SYS_START, kSize); |
| } |
| |
| MagmaBufMgr::~MagmaBufMgr() { |
| if (connection_) { |
| magma_connection_release(connection_); |
| } |
| } |
| |
| MagmaBo* MagmaBufMgr::CreateBo(const char *name, unsigned long size) |
| { |
| magma_buffer_t buffer; |
| uint64_t size_actual; |
| magma_buffer_id_t buffer_id; |
| magma_status_t status = |
| magma_connection_create_buffer2(connection(), size, &size_actual, &buffer, &buffer_id); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_create_buffer failed: %d", status); |
| return nullptr; |
| } |
| |
| magma_buffer_set_name(buffer, name); |
| |
| auto bo = new MagmaBo(buffer, size_actual, buffer_id, this); |
| |
| if (!bo->MapGpu()) { |
| bo->RemoveRef(); |
| return nullptr; |
| } |
| |
| return bo; |
| } |
| |
| MagmaBo* MagmaBufMgr::ImportBo(int handle, unsigned long size) |
| { |
| magma_buffer_t buffer; |
| size_t imported_size; |
| magma_buffer_id_t id; |
| magma_status_t status = |
| magma_connection_import_buffer2(connection(), handle, &imported_size, &buffer, &id); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_import failed: %d", status); |
| return nullptr; |
| } |
| |
| auto bo = new MagmaBo(buffer, imported_size, id, this); |
| |
| if (!bo->MapGpu()) { |
| bo->RemoveRef(); |
| return nullptr; |
| } |
| |
| return bo; |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| struct mos_bufmgr* mos_bufmgr_gem_init(int fd, int /*batch_size*/) |
| { |
| magma_device_t unowned_device = get_device_for_magma_fd(fd); |
| assert(unowned_device); |
| |
| uint32_t device_id; |
| { |
| uint64_t val; |
| magma_status_t status = |
| magma_device_query(unowned_device, MAGMA_QUERY_DEVICE_ID, nullptr, &val); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_query failed: %d", status); |
| return nullptr; |
| } |
| device_id = static_cast<uint32_t>(val); |
| } |
| |
| magma_connection_t connection; |
| magma_status_t status = magma_device_create_connection(unowned_device, &connection); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_create_connection2 failed: %d", status); |
| return nullptr; |
| } |
| |
| LOG_VERBOSE("mos_bufmgr_gem_init connected to magma"); |
| |
| return new MagmaBufMgr(connection, device_id); |
| } |
| |
| struct mos_linux_context* mos_gem_context_create(struct mos_bufmgr *mos_bufmgr) |
| { |
| auto bufmgr = static_cast<MagmaBufMgr*>(mos_bufmgr); |
| |
| uint32_t context_id; |
| magma_status_t status = magma_connection_create_context(bufmgr->connection(), &context_id); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_create_context failed: %d", status); |
| return nullptr; |
| } |
| assert(context_id); |
| |
| auto context = new MagmaContext(mos_bufmgr, context_id); |
| |
| LOG_VERBOSE("mos_gem_context_create context_id %u", context_id); |
| |
| return context; |
| } |
| |
| struct mos_linux_context *mos_gem_context_create_ext(struct mos_bufmgr *bufmgr, __u32 flags) |
| { |
| assert(flags == 0); |
| return mos_gem_context_create(bufmgr); |
| } |
| |
| // Creates a new context and associates with it the pseudo VM referenced by the given context. |
| // Note the reference context may not have been created with mos_gem_context_create_shared. |
| // The pseudo VMs are just IDs and are used to track which "VMs" are used to execute command |
| // buffers. We assume that only one VM is ever used for all executed contexts, but if that |
| // assumption proves false then we'll have to create additional connections for each VM. |
| struct mos_linux_context* mos_gem_context_create_shared(struct mos_bufmgr *bufmgr, |
| mos_linux_context* ref_context, __u32 flags) |
| { |
| LOG_VERBOSE("mos_gem_context_create_shared vm_id %u flags 0x%x", ref_context->vm->vm_id, flags); |
| |
| bool ensure_ordering_across_engines = flags & I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE; |
| |
| flags &= ~I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE; |
| assert(flags == 0); |
| |
| auto context = static_cast<MagmaContext*>(mos_gem_context_create(bufmgr)); |
| if (!context) |
| return nullptr; |
| |
| context->set_vm_id(ref_context->vm->vm_id); |
| |
| if (ensure_ordering_across_engines) { |
| context->set_ensure_ordering_across_engines(); |
| } |
| |
| return context; |
| } |
| |
| void mos_gem_context_destroy(struct mos_linux_context *mos_context) |
| { |
| auto context = static_cast<MagmaContext*>(mos_context); |
| |
| LOG_VERBOSE("mos_gem_context_destroy context_id %u", context->ctx_id); |
| |
| auto bufmgr = static_cast<MagmaBufMgr*>(context->bufmgr); |
| |
| magma_connection_release_context(bufmgr->connection(), context->ctx_id); |
| |
| delete context; |
| } |
| |
| int mos_set_context_param_load_balance(struct mos_linux_context *mos_context, |
| struct i915_engine_class_instance *ci, unsigned int count) |
| { |
| LOG_VERBOSE("mos_set_context_param_load_balance: context_id %u count %u [%u]", |
| mos_context->ctx_id, count, count == 0 ? 0 : ci->engine_class); |
| |
| // May be called with count 0 in the case mos_query_engines sets engine count to 0. |
| if (count == 0) |
| return 0; |
| |
| std::vector<uint32_t> target_engines; |
| |
| for (uint32_t i = 0; i < count; i++) { |
| if (ci->engine_class != I915_ENGINE_CLASS_RENDER && |
| ci->engine_class != I915_ENGINE_CLASS_VIDEO) { |
| LOG_VERBOSE("Unhandled engine class: %u", ci->engine_class); |
| return -1; |
| } |
| if (ci->engine_instance != 0) { |
| LOG_VERBOSE("Unhandled engine instance: %u", ci->engine_instance); |
| return -1; |
| } |
| target_engines.push_back(ci->engine_class); |
| } |
| |
| // Magma doesn't support load balancing so just check that we're only targetting a single |
| // video engine; however, enabling this on Linux uses I915_CONTEXT_PARAM_ENGINES which changes |
| // the meaning of the exec ring to an array index (see mos_gem_bo_context_exec2). |
| assert(target_engines.size() == 1); |
| |
| auto context = static_cast<MagmaContext*>(mos_context); |
| |
| context->set_target_engines(std::move(target_engines)); |
| |
| return 0; |
| } |
| |
| struct drm_i915_gem_vm_control* mos_gem_vm_create(struct mos_bufmgr *mos_bufmgr) |
| { |
| // Magma only supports one VM per connection, so for now we fake this support, assuming that |
| // contexts used for command buffer submission will only use one shared VM. |
| auto bufmgr = static_cast<MagmaBufMgr*>(mos_bufmgr); |
| |
| auto vm = new drm_i915_gem_vm_control; |
| |
| vm->extensions = 0; |
| vm->flags = 0; |
| vm->vm_id = bufmgr->next_vm_id(); |
| |
| LOG_VERBOSE("mos_gem_vm_create vm_id %u", vm->vm_id); |
| |
| return vm; |
| } |
| |
| void mos_gem_vm_destroy(struct mos_bufmgr *bufmgr, struct drm_i915_gem_vm_control* vm) |
| { |
| LOG_VERBOSE("mos_gem_vm_destroy vm_id %u", vm->vm_id); |
| |
| delete vm; |
| } |
| |
| int mos_bufmgr_gem_get_devid(struct mos_bufmgr *mos_bufmgr) |
| { |
| auto bufmgr = static_cast<MagmaBufMgr*>(mos_bufmgr); |
| |
| LOG_VERBOSE("mos_bufmgr_gem_get_devid returning 0x%x", bufmgr->device_id()); |
| |
| return bufmgr->device_id(); |
| } |
| |
| bool mos_gem_bo_is_softpin(struct mos_linux_bo *bo) |
| { |
| assert(static_cast<MagmaBo*>(bo)->is_mapped_gpu()); |
| return true; |
| } |
| |
| static uint64_t get_magma_flags(MagmaContext* context, uint32_t exec_ring) |
| { |
| uint64_t flags = 0; |
| |
| if (context->target_engines().size()) { |
| if (exec_ring >= context->target_engines().size()) { |
| LOG_VERBOSE("Unexpected exec ring index: %u", exec_ring); |
| assert(false); |
| } else { |
| uint32_t engine = context->target_engines()[exec_ring]; |
| switch (engine) { |
| case I915_ENGINE_CLASS_RENDER: |
| flags |= kMagmaIntelGenCommandBufferForRender; |
| break; |
| case I915_ENGINE_CLASS_VIDEO: |
| flags |= kMagmaIntelGenCommandBufferForVideo; |
| break; |
| default: |
| LOG_VERBOSE("Unrecognized engine: %u", engine); |
| assert(false); |
| } |
| } |
| } else switch (exec_ring) { |
| case I915_EXEC_RENDER: |
| flags |= kMagmaIntelGenCommandBufferForRender; |
| break; |
| case I915_EXEC_BSD: |
| flags |= kMagmaIntelGenCommandBufferForVideo; |
| break; |
| default: |
| LOG_VERBOSE("unexpected ring 0x%x", exec_ring); |
| assert(false); |
| } |
| |
| return flags; |
| } |
| |
| int mos_gem_bo_context_exec2(struct mos_linux_bo *mos_linux_bo, int used, struct mos_linux_context *mos_context, |
| drm_clip_rect_t *cliprects, int num_cliprects, int DR4, unsigned int flags, int *fence) { |
| |
| constexpr uint32_t kAllowedFlags = I915_EXEC_BSD|I915_EXEC_BSD_RING1|I915_EXEC_RENDER; |
| |
| assert((flags & ~kAllowedFlags) == 0); // only these flags |
| |
| auto context = static_cast<MagmaContext*>(mos_context); |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| auto bufmgr = static_cast<MagmaBufMgr*>(context->bufmgr); |
| |
| uint64_t magma_flags = get_magma_flags(context, flags & I915_EXEC_RING_MASK); |
| |
| uint32_t vm_id = bufmgr->GetVmId(context->vm_id()); |
| |
| LOG_VERBOSE("mos_gem_bo_context_exec2 bo %lu used %d context_id %u vm_id %u bufmgr vm_id %u " |
| "num_cliprects %d DR4 %d flags 0x%x kAllowedFlags 0x%x magma_flags 0x%lx", |
| bo->id(), used, context->ctx_id, context->vm_id(), vm_id, num_cliprects, DR4, |
| flags, kAllowedFlags, magma_flags); |
| |
| if (vm_id != context->vm_id()) { |
| // We don't support more than one VM per connection (see mos_gem_context_create_shared) |
| LOG_VERBOSE("Incompatible VM bufmgr vm_id %u context vm_id %u", vm_id, context->vm_id()); |
| assert(false); |
| return -1; |
| } |
| |
| if (context->ensure_ordering_across_engines()) { |
| uint64_t flags = context->GetCommandBufferFlags(magma_flags); |
| if (flags != magma_flags) { |
| LOG_VERBOSE("Ordering across engines not implemented flags 0x%lx magma_flags 0x%lx", |
| flags, magma_flags); |
| assert(false); |
| return -1; |
| } |
| } |
| |
| // TODO(fxbug.78281) |
| uint64_t* semaphore_ids = nullptr; |
| |
| auto& resources = bo->exec_resources(); |
| |
| magma_exec_command_buffer command_buffer = { |
| .resource_index = 0, |
| .start_offset = 0 |
| }; |
| |
| magma_command_descriptor descriptor = { |
| .resource_count = resources.size(), |
| .command_buffer_count = 1, |
| .wait_semaphore_count = 0, |
| .signal_semaphore_count = 0, |
| .resources = resources.data(), |
| .command_buffers = &command_buffer, |
| .semaphore_ids = semaphore_ids, |
| .flags = magma_flags, |
| }; |
| |
| // Add to inflight list first to avoid race with any other thread reading completions from the |
| // notification channel, in case this thread is preempted just after sending the command buffer |
| // and the completion happens quickly. |
| bufmgr->inflight_list()->AddAndUpdate( |
| bufmgr->connection(), resources.data(), |
| resources.size()); |
| |
| magma_status_t status = magma_connection_execute_command(bo->magma_bufmgr()->connection(), |
| context->ctx_id, &descriptor); |
| |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_execute_command failed: %d", status); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| void mos_gem_bo_clear_relocs(struct mos_linux_bo *bo, int start) |
| { |
| static_cast<MagmaBo*>(bo)->clear_exec_resources(); |
| } |
| |
| int mos_bo_gem_export_to_prime(struct mos_linux_bo *mos_linux_bo, int *prime_fd) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| |
| magma_handle_t handle; |
| magma_status_t status = |
| magma_connection_export_buffer(bo->magma_bufmgr()->connection(), bo->buffer(), &handle); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("magma_export failed: %d", status); |
| return -1; |
| } |
| |
| *prime_fd = handle; |
| return 0; |
| } |
| |
| int mos_gem_bo_wait(struct mos_linux_bo *mos_linux_bo, int64_t timeout_ns) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| auto bufmgr = bo->magma_bufmgr(); |
| magma_status_t status = bufmgr->inflight_list()->WaitForBuffer( |
| bufmgr->connection(), |
| bufmgr->notification_handle(), |
| bo->id(), |
| timeout_ns); |
| if (status != MAGMA_STATUS_OK) { |
| LOG_VERBOSE("WaitForbuffer failed: %d", status); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int bufmgr_bo_busy(struct mos_linux_bo *mos_linux_bo) |
| { |
| auto bo = static_cast<MagmaBo*>(mos_linux_bo); |
| auto bufmgr = bo->magma_bufmgr(); |
| magma_status_t status = bufmgr->inflight_list()->WaitForBuffer( |
| bufmgr->connection(), |
| bufmgr->notification_handle(), |
| bo->id(), |
| 0 /* timeout_ns */); |
| if (status == MAGMA_STATUS_TIMED_OUT) { |
| return 1; |
| } else if (status == MAGMA_STATUS_OK) { |
| return 0; |
| } else { |
| LOG_VERBOSE("bufmgr_bo_busy failed: %d", status); |
| return 0; |
| } |
| } |
| |
| void bufmgr_bo_wait_rendering(struct mos_linux_bo *bo) |
| { |
| mos_gem_bo_wait(bo, INT64_MAX); |
| } |
| |
| int mos_gem_bo_map_wc(struct mos_linux_bo *bo) { |
| // Currently only cache-coherent systems are supported, so WC mapping should be unnecessary. |
| // TODO(fxbug.dev/97510): Add support for WC mappings. |
| return bufmgr_bo_map(bo, 1); |
| } |
| |
| int mos_gem_bo_unmap_wc(struct mos_linux_bo *bo) |
| { |
| return bufmgr_bo_unmap(bo); |
| } |
| |
| int mos_query_engines_count(struct mos_bufmgr *bufmgr, unsigned int *nengine) |
| { |
| // TODO(fxbug.dev/110484) - query the MSD for set of available engines |
| *nengine = 2; |
| return 0; |
| } |
| |
| int mos_query_engines(struct mos_bufmgr *bufmgr, |
| __u16 engine_class, |
| __u64 caps, |
| unsigned int *nengine, |
| struct i915_engine_class_instance *ci) |
| { |
| unsigned int max = *nengine; |
| |
| LOG_VERBOSE("mos_query_engines: class %u max %u", engine_class, max); |
| |
| *nengine = 0; |
| |
| // TODO(fxbug.dev/110484) - query the MSD for set of available engines |
| switch (engine_class) { |
| case I915_ENGINE_CLASS_RENDER: |
| case I915_ENGINE_CLASS_VIDEO: |
| if (*nengine >= max) { |
| LOG_VERBOSE("Can't report engines max %u", max); |
| return -1; |
| } |
| ci[*nengine].engine_class = engine_class; |
| ci[*nengine].engine_instance = 0; |
| *nengine += 1; |
| break; |
| default: |
| LOG_VERBOSE("mos_query_engines: class %u not supported", engine_class); |
| break; |
| } |
| |
| return 0; |
| } |