diff options
author | 2021-01-08 23:02:09 +0000 | |
---|---|---|
committer | 2021-01-08 23:02:09 +0000 | |
commit | 2fb53b285c85042a93e33372b66fb452083680e6 (patch) | |
tree | f0f6ba0bde5fe09770b89470954510040d9231ba /sys/dev/pci/drm/drm_linux.c | |
parent | accept_reserve() counter function argument doesn't need to be volatile. (diff) | |
download | wireguard-openbsd-2fb53b285c85042a93e33372b66fb452083680e6.tar.xz wireguard-openbsd-2fb53b285c85042a93e33372b66fb452083680e6.zip |
Bring the emulated Linux memory allocation interfaces more in line with
what Linux does. Let vmalloc() use km_alloc(9) instead of malloc(9) and
let kvmalloc() only use malloc(9) for small (less than a page) allocations
and atomic allocations. This should reduce the pressure on the
"interrupt-safe" map.
ok jsg@
Diffstat (limited to 'sys/dev/pci/drm/drm_linux.c')
-rw-r--r-- | sys/dev/pci/drm/drm_linux.c | 111 |
1 files changed, 110 insertions, 1 deletions
diff --git a/sys/dev/pci/drm/drm_linux.c b/sys/dev/pci/drm/drm_linux.c index d01db47d517..a893c4de89b 100644 --- a/sys/dev/pci/drm/drm_linux.c +++ b/sys/dev/pci/drm/drm_linux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.c,v 1.74 2020/12/31 06:31:55 jsg Exp $ */ +/* $OpenBSD: drm_linux.c,v 1.75 2021/01/08 23:02:09 kettenis Exp $ */ /* * Copyright (c) 2013 Jonathan Gray <jsg@openbsd.org> * Copyright (c) 2015, 2016 Mark Kettenis <kettenis@openbsd.org> @@ -430,6 +430,111 @@ dmi_check_system(const struct dmi_system_id *sysid) return (num); } +struct vmalloc_entry { + const void *addr; + size_t size; + RBT_ENTRY(vmalloc_entry) vmalloc_node; +}; + +struct pool vmalloc_pool; +RBT_HEAD(vmalloc_tree, vmalloc_entry) vmalloc_tree; + +RBT_PROTOTYPE(vmalloc_tree, vmalloc_entry, vmalloc_node, vmalloc_compare); + +static inline int +vmalloc_compare(const struct vmalloc_entry *a, const struct vmalloc_entry *b) +{ + vaddr_t va = (vaddr_t)a->addr; + vaddr_t vb = (vaddr_t)b->addr; + + return va < vb ? -1 : va > vb; +} + +RBT_GENERATE(vmalloc_tree, vmalloc_entry, vmalloc_node, vmalloc_compare); + +bool +is_vmalloc_addr(const void *addr) +{ + struct vmalloc_entry key; + struct vmalloc_entry *entry; + + key.addr = addr; + entry = RBT_FIND(vmalloc_tree, &vmalloc_tree, &key); + return (entry != NULL); +} + +void * +vmalloc(unsigned long size) +{ + struct vmalloc_entry *entry; + void *addr; + + size = round_page(size); + addr = km_alloc(size, &kv_any, &kp_dirty, &kd_waitok); + if (addr) { + entry = pool_get(&vmalloc_pool, PR_WAITOK); + entry->addr = addr; + entry->size = size; + RBT_INSERT(vmalloc_tree, &vmalloc_tree, entry); + } + + return addr; +} + +void * +vzalloc(unsigned long size) +{ + struct vmalloc_entry *entry; + void *addr; + + size = round_page(size); + addr = km_alloc(size, &kv_any, &kp_zero, &kd_waitok); + if (addr) { + entry = pool_get(&vmalloc_pool, PR_WAITOK); + entry->addr = addr; + entry->size = size; + RBT_INSERT(vmalloc_tree, &vmalloc_tree, entry); + } + + return addr; +} + +void +vfree(const void *addr) +{ + struct vmalloc_entry key; + struct vmalloc_entry *entry; + + key.addr = addr; + entry = RBT_FIND(vmalloc_tree, &vmalloc_tree, &key); + if (entry == NULL) + panic("%s: non vmalloced addr %p", __func__, addr); + + RBT_REMOVE(vmalloc_tree, &vmalloc_tree, entry); + km_free((void *)addr, entry->size, &kv_any, &kp_dirty); + pool_put(&vmalloc_pool, entry); +} + +void * +kvmalloc(size_t size, gfp_t flags) +{ + if ((flags & M_NOWAIT) || size < PAGE_SIZE) + return malloc(size, M_DRM, flags); + if (flags & M_ZERO) + return vzalloc(size); + else + return vmalloc(size); +} + +void +kvfree(const void *addr) +{ + if (is_vmalloc_addr(addr)) + vfree(addr); + else + free((void *)addr, M_DRM, 0); +} + struct vm_page * alloc_pages(unsigned int gfp_mask, unsigned int order) { @@ -1939,6 +2044,10 @@ drm_linux_init(void) pool_init(&idr_pool, sizeof(struct idr_entry), 0, IPL_TTY, 0, "idrpl", NULL); + + pool_init(&vmalloc_pool, sizeof(struct vmalloc_entry), 0, IPL_NONE, 0, + "vmallocpl", NULL); + RBT_INIT(vmalloc_tree, &vmalloc_tree); } void |