summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/drm/drm_linux.c
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2021-01-08 23:02:09 +0000
committerkettenis <kettenis@openbsd.org>2021-01-08 23:02:09 +0000
commit2fb53b285c85042a93e33372b66fb452083680e6 (patch)
treef0f6ba0bde5fe09770b89470954510040d9231ba /sys/dev/pci/drm/drm_linux.c
parentaccept_reserve() counter function argument doesn't need to be volatile. (diff)
downloadwireguard-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.c111
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