blob: eaf3f49a2852a94b32b432d651883b7eef9fbaeb [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "address_space.h"
#include "gtt.h"
std::unique_ptr<GpuMapping> AddressSpace::MapBufferGpu(std::shared_ptr<AddressSpace> address_space,
std::shared_ptr<MsdIntelBuffer> buffer,
uint64_t offset, uint64_t length)
{
DASSERT(address_space);
DASSERT(buffer);
length = address_space->GetMappedSize(length);
if (!magma::is_page_aligned(offset))
return DRETP(nullptr, "offset (0x%lx) not page aligned", offset);
if (offset + length > buffer->platform_buffer()->size())
return DRETP(nullptr, "offset (0x%lx) + length (0x%lx) > buffer size (0x%lx)", offset,
length, buffer->platform_buffer()->size());
if (length > address_space->Size())
return DRETP(nullptr, "length (0x%lx) > address space size (0x%lx)", length,
address_space->Size());
uint64_t align_pow2;
if (!magma::get_pow2(PAGE_SIZE, &align_pow2))
return DRETP(nullptr, "alignment is not power of 2");
// Casting to uint8_t below
DASSERT((align_pow2 & ~0xFF) == 0);
DASSERT(magma::is_page_aligned(length));
gpu_addr_t gpu_addr;
if (!address_space->Alloc(length, static_cast<uint8_t>(align_pow2), &gpu_addr))
return DRETP(nullptr, "failed to allocate gpu address");
DLOG("MapBufferGpu offset 0x%lx length 0x%lx allocated gpu_addr 0x%lx", offset, length,
gpu_addr);
uint64_t page_offset = offset / PAGE_SIZE;
uint32_t page_count = length / PAGE_SIZE;
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping;
if (address_space->type() == ADDRESS_SPACE_PPGTT) {
bus_mapping = address_space->owner_->GetBusMapper()->MapPageRangeBus(
buffer->platform_buffer(), page_offset, page_count);
if (!bus_mapping)
return DRETP(nullptr, "failed to bus map the page range");
if (!address_space->Insert(gpu_addr, bus_mapping.get()))
return DRETP(nullptr, "failed to insert into address_space");
} else {
if (!static_cast<Gtt*>(address_space.get())
->GlobalGttInsert(gpu_addr, buffer->platform_buffer(), page_offset, page_count))
return DRETP(nullptr, "failed to insert into address_space");
}
return std::unique_ptr<GpuMapping>(
new GpuMapping(address_space, buffer, offset, length, gpu_addr, std::move(bus_mapping)));
}
magma::Status AddressSpace::MapBufferGpu(std::shared_ptr<AddressSpace> address_space,
std::shared_ptr<MsdIntelBuffer> buffer,
gpu_addr_t gpu_addr, uint64_t page_offset,
uint64_t page_count,
std::shared_ptr<GpuMapping>* gpu_mapping_out)
{
DASSERT(address_space);
DASSERT(address_space->type() == ADDRESS_SPACE_PPGTT);
DASSERT(buffer);
if (!magma::is_page_aligned(gpu_addr))
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "gpu_addr 0x%lx not page aligned", gpu_addr);
if (gpu_addr + page_count * PAGE_SIZE > address_space->Size())
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS,
"gpu_addr 0x%lx + page_count (%lu) > address space size (0x%lx)", gpu_addr,
page_count, address_space->Size());
magma::PlatformBuffer* platform_buffer = buffer->platform_buffer();
if ((page_offset + page_count) * PAGE_SIZE > platform_buffer->size())
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS,
"page_offset (%lu) + page_count (%lu) > buffer size (0x%lx)", page_offset,
page_count, platform_buffer->size());
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping =
address_space->owner_->GetBusMapper()->MapPageRangeBus(platform_buffer, page_offset,
page_count);
if (!bus_mapping)
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "failed to map page range to bus");
if (!address_space->Insert(gpu_addr, bus_mapping.get()))
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "failed to insert into address_space");
*gpu_mapping_out =
std::make_unique<GpuMapping>(address_space, buffer, page_offset * PAGE_SIZE,
page_count * PAGE_SIZE, gpu_addr, std::move(bus_mapping));
return MAGMA_STATUS_OK;
}
std::shared_ptr<GpuMapping> AddressSpace::FindGpuMapping(std::shared_ptr<MsdIntelBuffer> buffer,
uint64_t offset, uint64_t length) const
{
DASSERT(buffer);
auto range = mappings_by_buffer_.equal_range(buffer->platform_buffer());
for (auto iter = range.first; iter != range.second; iter++) {
auto& mapping = iter->second->second;
if (mapping->offset() == offset && mapping->length() >= GetMappedSize(length))
return mapping;
}
return nullptr;
}
std::shared_ptr<GpuMapping> AddressSpace::FindGpuMapping(uint64_t gpu_addr) const
{
auto iter = mappings_.find(gpu_addr);
return (iter != mappings_.end()) ? iter->second : nullptr;
}
bool AddressSpace::AddMapping(std::shared_ptr<GpuMapping> gpu_mapping)
{
auto iter = mappings_.upper_bound(gpu_mapping->gpu_addr());
if (iter != mappings_.end() &&
(gpu_mapping->gpu_addr() + gpu_mapping->length() > iter->second->gpu_addr()))
return DRETF(false, "Mapping overlaps existing mapping");
// Find the mapping with the highest VA that's <= this.
if (iter != mappings_.begin()) {
--iter;
// Check if the previous mapping overlaps this.
if (iter->second->gpu_addr() + iter->second->length() > gpu_mapping->gpu_addr())
return DRETF(false, "Mapping overlaps existing mapping");
}
std::pair<map_container_t::iterator, bool> result =
mappings_.insert({gpu_mapping->gpu_addr(), gpu_mapping});
DASSERT(result.second);
mappings_by_buffer_.insert({gpu_mapping->buffer()->platform_buffer(), result.first});
return true;
}
bool AddressSpace::ReleaseMapping(magma::PlatformBuffer* buffer, gpu_addr_t gpu_addr,
std::shared_ptr<GpuMapping>* mapping_out)
{
auto range = mappings_by_buffer_.equal_range(buffer);
for (auto iter = range.first; iter != range.second; iter++) {
std::shared_ptr<GpuMapping> gpu_mapping = iter->second->second;
if (gpu_mapping->gpu_addr() == gpu_addr) {
mappings_.erase(iter->second);
mappings_by_buffer_.erase(iter);
*mapping_out = std::move(gpu_mapping);
return true;
}
}
return DRETF(false, "failed to remove mapping");
}
void AddressSpace::ReleaseBuffer(magma::PlatformBuffer* buffer,
std::vector<std::shared_ptr<GpuMapping>>* released_mappings_out)
{
released_mappings_out->clear();
auto range = mappings_by_buffer_.equal_range(buffer);
for (auto iter = range.first; iter != range.second;) {
released_mappings_out->emplace_back(std::move(iter->second->second));
mappings_.erase(iter->second);
iter = mappings_by_buffer_.erase(iter);
}
}
magma::Status AddressSpace::GrowMapping(GpuMapping* mapping, uint64_t page_increment)
{
const uint64_t length = mapping->length() + page_increment * PAGE_SIZE;
if (mapping->gpu_addr() + length > Size())
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS,
"gpu_addr 0x%lx + length %lu > address space size (0x%lx)",
mapping->gpu_addr(), length, Size());
auto iter = mappings_.upper_bound(mapping->gpu_addr());
if (iter != mappings_.end() && (mapping->gpu_addr() + length > iter->second->gpu_addr()))
return DRETF(false, "Mapping overlaps existing mapping");
magma::PlatformBuffer* platform_buffer = mapping->buffer()->platform_buffer();
if (mapping->offset() + length > platform_buffer->size())
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS,
"offset (%lu) + length (%lu) > buffer size (0x%lx)", mapping->offset(),
length, platform_buffer->size());
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping =
owner_->GetBusMapper()->MapPageRangeBus(
platform_buffer, (mapping->offset() + mapping->length()) / magma::page_size(),
page_increment);
if (!bus_mapping)
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "failed to map page range to bus");
if (!Insert(mapping->gpu_addr() + mapping->length(), bus_mapping.get()))
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "failed to insert into address_space");
mapping->Grow(std::move(bus_mapping));
return MAGMA_STATUS_OK;
}