| /* |
| * Copyright (C) 2018 Samsung Electronics Co., Ltd. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include <iostream> |
| #include <cerrno> |
| #include <cstring> |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| |
| #include "ion_test_fixture.h" |
| #include "ion_test_define.h" |
| |
| #define TEST_ALLOC_CACHED 1 |
| #define TEST_ALLOC_BUDDY 2 |
| |
| using namespace std; |
| |
| class Allocate : public IonAllocTest { |
| protected: |
| struct test_type_struct { |
| int type_flags; |
| const char *type_title; |
| }; |
| off_t checkZero(int fd, size_t size, unsigned long *val) { |
| unsigned long *p = reinterpret_cast<unsigned long *>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); |
| if (p == MAP_FAILED) |
| return -1; |
| |
| off_t idx; |
| for (idx = 0; idx < static_cast<off_t>(size / sizeof(*p)); idx++) { |
| if (p[idx] != 0) { |
| if (val) |
| *val = p[idx]; |
| break; |
| } |
| } |
| |
| munmap(p, size); |
| |
| return idx * sizeof(*p); |
| } |
| |
| void flushShrinker() { |
| int fd = open("/sys/kernel/debug/ion_system_heap_shrink", O_RDWR); |
| if (fd < 0) |
| return; |
| |
| unsigned long val = mb(256); // This is very big enough to flush shrinker |
| if (write(fd, &val, sizeof(val)) < 0) |
| FAIL() << "Failed to write " << val << " to 'ion_system_heap_shrink': " << strerror(errno); |
| if (read(fd, &val, sizeof(val)) < 0) |
| FAIL() << "Failed to read from 'ion_system_heap_shrink': " << strerror(errno); |
| if (val > 0) |
| FAIL() << "ion_system_heap_shrink still has value " << val; |
| close(fd); |
| } |
| }; |
| |
| TEST_F(Allocate, Allocate) |
| { |
| static const size_t allocation_sizes[] = { |
| mkb(16, 716), mkb(12, 4), mkb(8, 912), mkb(4, 60), mkb(2, 520), mkb(1, 92), |
| mb(16), mb(12), mb(8), mb(4), mb(2), mb(1), kb(64), kb(4), |
| }; |
| static const test_type_struct test_types[] = { |
| {0, "uncached"}, |
| {TEST_ALLOC_CACHED, "cached"}, |
| {TEST_ALLOC_BUDDY, "uncached|flush_pool"}, |
| {TEST_ALLOC_CACHED | TEST_ALLOC_BUDDY, "cached|flush_pool"}, |
| }; |
| |
| for (test_type_struct type: test_types) { |
| for (unsigned int i = 0; i < getHeapCount(); i++) { |
| if ((type.type_flags & TEST_ALLOC_BUDDY) && !(getHeapFlags(i) & ION_HEAPDATA_FLAGS_DEFER_FREE)) |
| continue; |
| |
| if (getCmaUsed(getHeapName(i)) > 0) |
| continue; |
| |
| for (size_t size : allocation_sizes) { |
| if (size > getHeapSize(i)) |
| continue; |
| |
| ion_allocation_data_modern data; |
| int ret; |
| |
| data.len = size; |
| data.heap_id_mask = getHeapMask(i); |
| data.flags = (type.type_flags & TEST_ALLOC_CACHED) ? ION_FLAG_CACHED : 0; |
| data.fd = 0; |
| |
| SCOPED_TRACE(::testing::Message() << "heap: " << getHeapName(i) << ", heapmask: " << getHeapMask(i)); |
| SCOPED_TRACE(::testing::Message() << "size: " << size << ", flags: " << data.flags); |
| SCOPED_TRACE(::testing::Message() << "test type: " << type.type_title); |
| |
| if (type.type_flags & TEST_ALLOC_BUDDY) |
| flushShrinker(); |
| |
| EXPECT_EQ(0, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)) << ": " << strerror(errno); |
| |
| EXPECT_LT(2U, data.fd); |
| EXPECT_GT(1024U, data.fd); |
| if (ret == 0) { |
| if (!(getHeapFlags(i) & ION_HEAPDATA_FLAGS_UNTOUCHABLE)) { |
| off_t erridx; |
| unsigned long val = 0; |
| EXPECT_EQ(static_cast<off_t>(size), erridx = checkZero(data.fd, size, &val)) |
| << "non-zero " << val << " found at " << erridx << " byte"; |
| } |
| EXPECT_EQ(0, close(data.fd)); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(Allocate, Large) |
| { |
| static const test_type_struct test_types[] = { |
| {0, "uncached"}, |
| {TEST_ALLOC_CACHED, "cached"}, |
| {TEST_ALLOC_BUDDY, "uncached|flush_pool"}, |
| {TEST_ALLOC_CACHED | TEST_ALLOC_BUDDY, "cached|flush_pool"}, |
| }; |
| |
| for (test_type_struct type: test_types) { |
| for (unsigned int i = 0; i < getHeapCount(); i++) { |
| if ((type.type_flags & TEST_ALLOC_BUDDY) && !(getHeapFlags(i) & ION_HEAPDATA_FLAGS_DEFER_FREE)) |
| continue; |
| |
| if (getCmaUsed(getHeapName(i)) > 0) |
| continue; |
| |
| __u64 size = 0; |
| switch(getHeapType(i)) { |
| case ION_HEAP_TYPE_SYSTEM: |
| size = getMemTotal() / 2 - 4096; |
| break; |
| case ION_HEAP_TYPE_CARVEOUT: |
| case ION_HEAP_TYPE_DMA: |
| size = getHeapSize(i); |
| break; |
| default: |
| continue; |
| } |
| |
| ion_allocation_data_modern data; |
| int ret; |
| |
| data.len = size; |
| data.heap_id_mask = getHeapMask(i); |
| data.flags = (type.type_flags & TEST_ALLOC_CACHED) ? ION_FLAG_CACHED : 0; |
| data.fd = 0; |
| |
| SCOPED_TRACE(::testing::Message() << "heap: " << getHeapName(i) << ", heapmask: " << getHeapMask(i)); |
| SCOPED_TRACE(::testing::Message() << "size: " << size << ", flags: " << data.flags); |
| SCOPED_TRACE(::testing::Message() << "test type: " << type.type_title); |
| |
| if (type.type_flags & TEST_ALLOC_BUDDY) |
| flushShrinker(); |
| |
| EXPECT_EQ(0, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)) << ": " << strerror(errno); |
| |
| EXPECT_LT(2U, data.fd); |
| EXPECT_GT(1024U, data.fd); |
| if (ret == 0) |
| EXPECT_EQ(0, close(data.fd)); |
| } |
| } |
| } |
| |
| TEST_F(Allocate, Nozeroed) |
| { |
| static const size_t allocation_sizes[] = { mkb(4, 60), mkb(2, 520), mkb(1, 92) }; |
| static const unsigned long allocation_flags[] = { 0, ION_FLAG_CACHED, ION_FLAG_SYNC_FORCE }; |
| |
| for (unsigned int i = 0; i < getHeapCount(); i++) { |
| if (getCmaUsed(getHeapName(i)) > 0) |
| continue; |
| |
| for (unsigned long flag : allocation_flags) { |
| for (size_t size : allocation_sizes) { |
| if (size > getHeapSize(i)) |
| continue; |
| |
| ion_allocation_data_modern data; |
| int ret; |
| |
| data.len = size; |
| data.heap_id_mask = getHeapMask(i); |
| data.flags = ION_FLAG_NOZEROED | flag; |
| data.fd = 0; |
| |
| SCOPED_TRACE(::testing::Message() << "heap " << getHeapName(i) << " mask " << getHeapMask(i)); |
| SCOPED_TRACE(::testing::Message() << "size " << size << ", flags " << data.flags); |
| |
| flushShrinker(); |
| |
| EXPECT_EQ(0, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)) << ": " << strerror(errno); |
| |
| EXPECT_LT(2U, data.fd); |
| EXPECT_GT(1024U, data.fd); |
| if (ret == 0) { |
| void *p; |
| |
| EXPECT_EQ(MAP_FAILED, p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0)); |
| EXPECT_EQ(EACCES, errno); |
| if (p != MAP_FAILED) |
| munmap(p, size); |
| EXPECT_EQ(0, close(data.fd)); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(Allocate, Protected) |
| { |
| static const size_t allocation_sizes[] = {kb(4), kb(64), mb(1), mb(2)}; |
| static const unsigned long allocation_flags[] = { 0, ION_FLAG_CACHED, ION_FLAG_SYNC_FORCE }; |
| |
| for (unsigned int i = 0; i < getHeapCount(); i++) { |
| if (getCmaUsed(getHeapName(i)) > 0) |
| continue; |
| |
| for (unsigned long flags: allocation_flags) { |
| for (size_t size : allocation_sizes) { |
| if (size > getHeapSize(i)) |
| continue; |
| |
| ion_allocation_data_modern data; |
| int ret; |
| |
| data.len = size; |
| data.heap_id_mask = getHeapMask(i); |
| data.flags = ION_FLAG_PROTECTED | flags; |
| data.fd = 0; |
| |
| SCOPED_TRACE(::testing::Message() << "heap: " << getHeapName(i) << ", heapmask: " << getHeapMask(i)); |
| SCOPED_TRACE(::testing::Message() << "size: " << size << ", flags: " << data.flags); |
| |
| EXPECT_EQ(0, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)) << ": " << strerror(errno); |
| |
| EXPECT_LT(2U, data.fd); |
| EXPECT_GT(1024U, data.fd); |
| if (ret == 0) { |
| void *p; |
| |
| EXPECT_EQ(MAP_FAILED, p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, data.fd, 0)); |
| EXPECT_EQ(EACCES, errno); |
| |
| if (p != MAP_FAILED) |
| munmap(p, size); |
| |
| EXPECT_EQ(0, close(data.fd)); |
| } |
| } |
| } |
| } |
| } |
| |
| TEST_F(Allocate, InvalidValues) |
| { |
| ion_allocation_data_modern data; |
| int ret; |
| |
| data.len = kb(4); |
| data.heap_id_mask = 1; // any first heap |
| data.flags = 0; |
| data.fd = 0; |
| |
| // incorrect /dev/ion fd |
| EXPECT_EQ(-1, ret = ioctl(0, ION_IOC_ALLOC_MODERN, &data)); |
| EXPECT_TRUE(errno == EINVAL || errno == ENOTTY) << " unexpected error: " << strerror(errno); |
| |
| // invalid fd |
| EXPECT_EQ(-1, ret = ioctl(-1, ION_IOC_ALLOC_MODERN, &data)); |
| EXPECT_EQ(EBADF, errno) << " unexpected error: " << strerror(errno); |
| |
| // invalid heap id |
| data.heap_id_mask = 0; |
| EXPECT_EQ(-1, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)); |
| EXPECT_EQ(ENODEV, errno) << " unexpected error: " << strerror(errno); |
| |
| // unavailable heap id (the largest heap id + 1) |
| data.heap_id_mask = 1 << (getMaxHeapId() + 1); |
| EXPECT_EQ(-1, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)) |
| << "unexpected success with heapmask " << data.heap_id_mask; |
| EXPECT_EQ(ENODEV, errno) << " unexpected error: " << strerror(errno); |
| |
| // zero size |
| data.len = 0; |
| EXPECT_EQ(-1, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)); |
| EXPECT_EQ(EINVAL, errno) << " unexpected error: " << strerror(errno); |
| |
| // too large size |
| for (unsigned int i = 0; i < getHeapCount(); i++) { |
| data.heap_id_mask = getHeapMask(i); |
| |
| switch(getHeapType(i)) { |
| case ION_HEAP_TYPE_SYSTEM: |
| data.len = kb(getMemTotal() / 2) + kb(4); |
| break; |
| case ION_HEAP_TYPE_CARVEOUT: |
| case ION_HEAP_TYPE_DMA: |
| data.len = getHeapSize(i) + 1; |
| break; |
| case ION_HEAP_TYPE_HPA: |
| data.len = ((sysconf(_SC_PAGESIZE) * 2) / 8) * (64 * 1024) + 1; // see hpa heap of ION driver |
| break; |
| default: |
| continue; |
| } |
| |
| SCOPED_TRACE(::testing::Message() << "heap: " << getHeapName(i) << ", heapmask: " << getHeapMask(i)); |
| SCOPED_TRACE(::testing::Message() << "size: " << data.len << ", flags: " << data.flags); |
| |
| EXPECT_EQ(-1, ret = ioctl(getIonFd(), ION_IOC_ALLOC_MODERN, &data)); |
| EXPECT_EQ(ENOMEM, errno) << " unexpected error: " << strerror(errno); |
| } |
| } |