diff --git a/runtime/mem_obj/buffer.cpp b/runtime/mem_obj/buffer.cpp index c0b51c5840..f72713a13b 100644 --- a/runtime/mem_obj/buffer.cpp +++ b/runtime/mem_obj/buffer.cpp @@ -132,6 +132,12 @@ Buffer *Buffer::create(Context *context, //Host ptr was not created with clSVMAlloc - create graphic allocation memory = memoryManager->createGraphicsAllocationWithRequiredBitness(size, hostPtr, true); } + if (!memory && Buffer::isReadOnlyMemoryPermittedByFlags(flags)) { + memory = memoryManager->createGraphicsAllocationWithRequiredBitness(size, nullptr, true); + zeroCopy = false; + copyMemoryFromHostPtr = true; + allocateMemory = true; + } } if (!memory) { @@ -229,6 +235,14 @@ void Buffer::checkMemory(cl_mem_flags flags, return; } +bool Buffer::isReadOnlyMemoryPermittedByFlags(cl_mem_flags flags) { + // Host won't access or will only read and kernel will only read + if ((flags & (CL_MEM_HOST_NO_ACCESS | CL_MEM_HOST_READ_ONLY)) && (flags & CL_MEM_READ_ONLY)) { + return true; + } + return false; +} + Buffer *Buffer::createSubBuffer(cl_mem_flags flags, const cl_buffer_region *region, cl_int &errcodeRet) { diff --git a/runtime/mem_obj/buffer.h b/runtime/mem_obj/buffer.h index 14090be97c..898ccfb7a0 100644 --- a/runtime/mem_obj/buffer.h +++ b/runtime/mem_obj/buffer.h @@ -127,6 +127,8 @@ class Buffer : public MemObj { bool ©MemoryFromHostPtr, MemoryManager *memMngr); + static bool isReadOnlyMemoryPermittedByFlags(cl_mem_flags flags); + void transferData(void *dst, void *src, size_t copySize, size_t copyOffset); }; diff --git a/runtime/memory_manager/memory_manager.h b/runtime/memory_manager/memory_manager.h index b8db28bd98..5b5229fb87 100644 --- a/runtime/memory_manager/memory_manager.h +++ b/runtime/memory_manager/memory_manager.h @@ -185,7 +185,7 @@ class MemoryManager { return createGraphicsAllocationWithRequiredBitness(size, ptr, false); } - GraphicsAllocation *createGraphicsAllocationWithRequiredBitness(size_t size, void *ptr, bool forcePin) { + MOCKABLE_VIRTUAL GraphicsAllocation *createGraphicsAllocationWithRequiredBitness(size_t size, void *ptr, bool forcePin) { if (force32bitAllocations && is64bit) { return allocate32BitGraphicsMemory(size, ptr, MemoryType::EXTERNAL_ALLOCATION); } else { diff --git a/unit_tests/mem_obj/buffer_tests.cpp b/unit_tests/mem_obj/buffer_tests.cpp index caaac45c04..4354e458cd 100644 --- a/unit_tests/mem_obj/buffer_tests.cpp +++ b/unit_tests/mem_obj/buffer_tests.cpp @@ -25,6 +25,7 @@ #include "runtime/gmm_helper/gmm_helper.h" #include "unit_tests/fixtures/device_fixture.h" #include "unit_tests/fixtures/memory_management_fixture.h" +#include "unit_tests/gen_common/matchers.h" #include "unit_tests/helpers/debug_manager_state_restore.h" #include "unit_tests/helpers/memory_management.h" #include "unit_tests/mocks/mock_context.h" @@ -66,6 +67,158 @@ TEST(Buffer, givenBufferWhenAskedForPtrLengthThenReturnCorrectValue) { EXPECT_EQ(size[0], retOffset); } +TEST(Buffer, givenReadOnlySetOfInputFlagsWhenPassedToisReadOnlyMemoryPermittedByFlagsThenTrueIsReturned) { + class MockBuffer : public Buffer { + public: + using Buffer::isReadOnlyMemoryPermittedByFlags; + }; + cl_mem_flags flags = CL_MEM_HOST_NO_ACCESS | CL_MEM_READ_ONLY; + EXPECT_TRUE(MockBuffer::isReadOnlyMemoryPermittedByFlags(flags)); + + flags = CL_MEM_HOST_READ_ONLY | CL_MEM_READ_ONLY; + EXPECT_TRUE(MockBuffer::isReadOnlyMemoryPermittedByFlags(flags)); +} + +class BufferReadOnlyTest : public testing::TestWithParam { +}; + +TEST_P(BufferReadOnlyTest, givenNonReadOnlySetOfInputFlagsWhenPassedToisReadOnlyMemoryPermittedByFlagsThenFalseIsReturned) { + class MockBuffer : public Buffer { + public: + using Buffer::isReadOnlyMemoryPermittedByFlags; + }; + + cl_mem_flags flags = GetParam() | CL_MEM_USE_HOST_PTR; + EXPECT_FALSE(MockBuffer::isReadOnlyMemoryPermittedByFlags(flags)); +} +static cl_mem_flags nonReadOnlyFlags[] = { + CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY, + CL_MEM_WRITE_ONLY, + CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY, + CL_MEM_HOST_READ_ONLY, + CL_MEM_HOST_WRITE_ONLY, + CL_MEM_HOST_NO_ACCESS, + CL_MEM_HOST_READ_ONLY | CL_MEM_WRITE_ONLY, + CL_MEM_HOST_WRITE_ONLY | CL_MEM_WRITE_ONLY, + 0}; + +INSTANTIATE_TEST_CASE_P( + nonReadOnlyFlags, + BufferReadOnlyTest, + testing::ValuesIn(nonReadOnlyFlags)); + +class GMockMemoryManagerFailFirstAllocation : public MockMemoryManager { + public: + MOCK_METHOD3(createGraphicsAllocationWithRequiredBitness, GraphicsAllocation *(size_t size, void *ptr, bool forcePin)); + GraphicsAllocation *baseCreateGraphicsAllocationWithRequiredBitness(size_t size, void *ptr, bool forcePin) { + return MockMemoryManager::createGraphicsAllocationWithRequiredBitness(size, ptr, forcePin); + } +}; + +TEST(Buffer, givenReadOnlyHostPtrMemoryWhenBufferIsCreatedWithReadOnlyFlagsThenBufferHasAllocatedNewMemoryStorageAndBufferIsNotZeroCopy) { + void *memory = alignedMalloc(MemoryConstants::pageSize, MemoryConstants::pageSize); + ASSERT_NE(nullptr, memory); + + memset(memory, 0xAA, MemoryConstants::pageSize); + + std::unique_ptr device(DeviceHelper<>::create(nullptr)); + ::testing::NiceMock *memoryManager = new ::testing::NiceMock; + + device->injectMemoryManager(memoryManager); + MockContext ctx(device.get()); + + // First fail in createGraphicsAllocation simulates error for read only memory allocation + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(MemoryConstants::pageSize, (void *)memory, true)) + .WillOnce(::testing::Return(nullptr)); + + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(::testing::_, nullptr, ::testing::_)) + .WillRepeatedly(::testing::Invoke(memoryManager, &GMockMemoryManagerFailFirstAllocation::baseCreateGraphicsAllocationWithRequiredBitness)); + + cl_int retVal; + cl_mem_flags flags = CL_MEM_HOST_READ_ONLY | CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR; + + std::unique_ptr buffer(Buffer::create(&ctx, flags, MemoryConstants::pageSize, (void *)memory, retVal)); + + EXPECT_FALSE(buffer->isMemObjZeroCopy()); + void *memoryStorage = buffer->getCpuAddressForMemoryTransfer(); + EXPECT_NE((void *)memory, memoryStorage); + EXPECT_THAT(buffer->getCpuAddressForMemoryTransfer(), MemCompare(memory, MemoryConstants::pageSize)); + + alignedFree(memory); +} + +TEST(Buffer, givenReadOnlyHostPtrMemoryWhenBufferIsCreatedWithReadOnlyFlagsAndSecondAllocationFailsThenNullptrIsReturned) { + void *memory = alignedMalloc(MemoryConstants::pageSize, MemoryConstants::pageSize); + ASSERT_NE(nullptr, memory); + + memset(memory, 0xAA, MemoryConstants::pageSize); + + std::unique_ptr device(DeviceHelper<>::create(nullptr)); + ::testing::NiceMock *memoryManager = new ::testing::NiceMock; + + device->injectMemoryManager(memoryManager); + MockContext ctx(device.get()); + + // First fail in createGraphicsAllocation simulates error for read only memory allocation + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(MemoryConstants::pageSize, (void *)memory, true)) + .WillOnce(::testing::Return(nullptr)); + + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(::testing::_, nullptr, ::testing::_)) + .WillOnce(::testing::Return(nullptr)); + + cl_int retVal; + cl_mem_flags flags = CL_MEM_HOST_READ_ONLY | CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR; + + std::unique_ptr buffer(Buffer::create(&ctx, flags, MemoryConstants::pageSize, (void *)memory, retVal)); + + EXPECT_EQ(nullptr, buffer.get()); + alignedFree(memory); +} + +TEST(Buffer, givenReadOnlyHostPtrMemoryWhenBufferIsCreatedWithKernelWriteFlagThenBufferAllocationFailsAndReturnsNullptr) { + void *memory = alignedMalloc(MemoryConstants::pageSize, MemoryConstants::pageSize); + ASSERT_NE(nullptr, memory); + + memset(memory, 0xAA, MemoryConstants::pageSize); + + std::unique_ptr device(DeviceHelper<>::create(nullptr)); + ::testing::NiceMock *memoryManager = new ::testing::NiceMock; + + device->injectMemoryManager(memoryManager); + MockContext ctx(device.get()); + + // First fail in createGraphicsAllocation simulates error for read only memory allocation + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(MemoryConstants::pageSize, (void *)memory, true)) + .WillOnce(::testing::Return(nullptr)); + + cl_int retVal; + cl_mem_flags flags = CL_MEM_HOST_READ_ONLY | CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR; + + std::unique_ptr buffer(Buffer::create(&ctx, flags, MemoryConstants::pageSize, (void *)memory, retVal)); + + EXPECT_EQ(nullptr, buffer.get()); + alignedFree(memory); +} + +TEST(Buffer, givenNullPtrWhenBufferIsCreatedWithKernelReadOnlyFlagsThenBufferAllocationFailsAndReturnsNullptr) { + std::unique_ptr device(DeviceHelper<>::create(nullptr)); + ::testing::NiceMock *memoryManager = new ::testing::NiceMock; + + device->injectMemoryManager(memoryManager); + MockContext ctx(device.get()); + + // First fail in createGraphicsAllocation simulates error for read only memory allocation + EXPECT_CALL(*memoryManager, createGraphicsAllocationWithRequiredBitness(::testing::_, nullptr, ::testing::_)) + .WillOnce(::testing::Return(nullptr)); + + cl_int retVal; + cl_mem_flags flags = CL_MEM_HOST_READ_ONLY | CL_MEM_WRITE_ONLY; + + std::unique_ptr buffer(Buffer::create(&ctx, flags, MemoryConstants::pageSize, nullptr, retVal)); + + EXPECT_EQ(nullptr, buffer.get()); +} + class BufferTest : public DeviceFixture, public testing::TestWithParam { public: