* IN THE SOFTWARE.
*/
-#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
-#include <linux/futex.h>
#include <linux/memfd.h>
-#include <sys/time.h>
#include <sys/mman.h>
-#include <sys/syscall.h>
#include "anv_private.h"
#include "util/hash_table.h"
+#include "util/simple_mtx.h"
#ifdef HAVE_VALGRIND
#define VG_NOACCESS_READ(__ptr) ({ \
#define ANV_MMAP_CLEANUP_INIT ((struct anv_mmap_cleanup){0})
-static inline long
-sys_futex(void *addr1, int op, int val1,
- struct timespec *timeout, void *addr2, int val3)
-{
- return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
-}
-
-static inline int
-futex_wake(uint32_t *addr, int count)
-{
- return sys_futex(addr, FUTEX_WAKE, count, NULL, NULL, 0);
-}
-
-static inline int
-futex_wait(uint32_t *addr, int32_t value)
-{
- return sys_futex(addr, FUTEX_WAIT, value, NULL, NULL, 0);
-}
-
static inline int
memfd_create(const char *name, unsigned int flags)
{
VkResult
anv_block_pool_init(struct anv_block_pool *pool,
struct anv_device *device,
- uint32_t initial_size)
+ uint32_t initial_size,
+ uint64_t bo_flags)
{
VkResult result;
pool->device = device;
+ pool->bo_flags = bo_flags;
anv_bo_init(&pool->bo, 0, 0);
pool->fd = memfd_create("block pool", MFD_CLOEXEC);
MAP_SHARED | MAP_POPULATE, pool->fd,
BLOCK_POOL_MEMFD_CENTER - center_bo_offset);
if (map == MAP_FAILED)
- return vk_errorf(VK_ERROR_MEMORY_MAP_FAILED, "mmap failed: %m");
+ return vk_errorf(pool->device->instance, pool->device,
+ VK_ERROR_MEMORY_MAP_FAILED, "mmap failed: %m");
gem_handle = anv_gem_userptr(pool->device, map, size);
if (gem_handle == 0) {
munmap(map, size);
- return vk_errorf(VK_ERROR_TOO_MANY_OBJECTS, "userptr failed: %m");
+ return vk_errorf(pool->device->instance, pool->device,
+ VK_ERROR_TOO_MANY_OBJECTS, "userptr failed: %m");
}
cleanup->map = map;
* hard work for us.
*/
anv_bo_init(&pool->bo, gem_handle, size);
+ pool->bo.flags = pool->bo_flags;
pool->bo.map = map;
return VK_SUCCESS;
result = anv_block_pool_expand_range(pool, center_bo_offset, size);
- if (pool->device->instance->physicalDevice.has_exec_async)
- pool->bo.flags |= EXEC_OBJECT_ASYNC;
+ pool->bo.flags = pool->bo_flags;
done:
pthread_mutex_unlock(&pool->device->mutex);
futex_wake(&pool_state->end, INT_MAX);
return state.next;
} else {
- futex_wait(&pool_state->end, state.end);
+ futex_wait(&pool_state->end, state.end, NULL);
continue;
}
}
VkResult
anv_state_pool_init(struct anv_state_pool *pool,
struct anv_device *device,
- uint32_t block_size)
+ uint32_t block_size,
+ uint64_t bo_flags)
{
VkResult result = anv_block_pool_init(&pool->block_pool, device,
- block_size * 16);
+ block_size * 16,
+ bo_flags);
if (result != VK_SUCCESS)
return result;
futex_wake(&pool->block.end, INT_MAX);
return offset;
} else {
- futex_wait(&pool->block.end, block.end);
+ futex_wait(&pool->block.end, block.end, NULL);
goto restart;
}
}
};
void
-anv_bo_pool_init(struct anv_bo_pool *pool, struct anv_device *device)
+anv_bo_pool_init(struct anv_bo_pool *pool, struct anv_device *device,
+ uint64_t bo_flags)
{
pool->device = device;
+ pool->bo_flags = bo_flags;
memset(pool->free_list, 0, sizeof(pool->free_list));
VG(VALGRIND_CREATE_MEMPOOL(pool, 0, false));
if (result != VK_SUCCESS)
return result;
- if (pool->device->instance->physicalDevice.supports_48bit_addresses)
- new_bo.flags |= EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
-
- if (pool->device->instance->physicalDevice.has_exec_async)
- new_bo.flags |= EXEC_OBJECT_ASYNC;
+ new_bo.flags = pool->bo_flags;
assert(new_bo.size == pow2_size);
if (pthread_mutex_init(&cache->mutex, NULL)) {
_mesa_hash_table_destroy(cache->bo_map, NULL);
- return vk_errorf(VK_ERROR_OUT_OF_HOST_MEMORY,
+ return vk_errorf(NULL, NULL, VK_ERROR_OUT_OF_HOST_MEMORY,
"pthread_mutex_init failed: %m");
}
VkResult
anv_bo_cache_import(struct anv_device *device,
struct anv_bo_cache *cache,
- int fd, uint64_t size, struct anv_bo **bo_out)
+ int fd, struct anv_bo **bo_out)
{
pthread_mutex_lock(&cache->mutex);
- /* The kernel is going to give us whole pages anyway */
- size = align_u64(size, 4096);
-
uint32_t gem_handle = anv_gem_fd_to_handle(device, fd);
if (!gem_handle) {
pthread_mutex_unlock(&cache->mutex);
struct anv_cached_bo *bo = anv_bo_cache_lookup_locked(cache, gem_handle);
if (bo) {
- if (bo->bo.size != size) {
- pthread_mutex_unlock(&cache->mutex);
- return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
- }
__sync_fetch_and_add(&bo->refcount, 1);
} else {
- /* For security purposes, we reject BO imports where the size does not
- * match exactly. This prevents a malicious client from passing a
- * buffer to a trusted client, lying about the size, and telling the
- * trusted client to try and texture from an image that goes
- * out-of-bounds. This sort of thing could lead to GPU hangs or worse
- * in the trusted client. The trusted client can protect itself against
- * this sort of attack but only if it can trust the buffer size.
- */
- off_t import_size = lseek(fd, 0, SEEK_END);
- if (import_size == (off_t)-1 || import_size != size) {
+ off_t size = lseek(fd, 0, SEEK_END);
+ if (size == (off_t)-1) {
anv_gem_close(device, gem_handle);
pthread_mutex_unlock(&cache->mutex);
return vk_error(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
}
pthread_mutex_unlock(&cache->mutex);
-
- /* From the Vulkan spec:
- *
- * "Importing memory from a file descriptor transfers ownership of
- * the file descriptor from the application to the Vulkan
- * implementation. The application must not perform any operations on
- * the file descriptor after a successful import."
- *
- * If the import fails, we leave the file descriptor open.
- */
- close(fd);
-
*bo_out = &bo->bo;
return VK_SUCCESS;