summaryrefslogtreecommitdiff
path: root/sys/dev/pci/drm
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2021-01-08 23:02:10 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2021-01-08 23:02:10 +0000
commitadcbb07c87168171c619563e8fb606105746665a (patch)
tree6ca8df1e3716b4602a413b1b3955e0eae28c2983 /sys/dev/pci/drm
parent406225212b5f6d0ad55c1ad46027ad333db102d2 (diff)
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')
-rw-r--r--sys/dev/pci/drm/drm_linux.c111
-rw-r--r--sys/dev/pci/drm/include/linux/mm.h29
-rw-r--r--sys/dev/pci/drm/include/linux/vmalloc.h25
3 files changed, 126 insertions, 39 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
diff --git a/sys/dev/pci/drm/include/linux/mm.h b/sys/dev/pci/drm/include/linux/mm.h
index f7d84207793..6c3a0c640d5 100644
--- a/sys/dev/pci/drm/include/linux/mm.h
+++ b/sys/dev/pci/drm/include/linux/mm.h
@@ -30,38 +30,31 @@
#define PFN_DOWN(x) ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x) ((x) << PAGE_SHIFT)
-#define is_vmalloc_addr(ptr) true
+bool is_vmalloc_addr(const void *);
-static inline void *
-kvmalloc(size_t size, gfp_t flags)
-{
- return malloc(size, M_DRM, flags);
-}
+void *kvmalloc(size_t, gfp_t);
+void kvfree(const void *);
static inline void *
-kvmalloc_array(size_t n, size_t size, int flags)
+kvmalloc_array(size_t n, size_t size, gfp_t flags)
{
if (n != 0 && SIZE_MAX / n < size)
return NULL;
- return malloc(n * size, M_DRM, flags);
+ return kvmalloc(n * size, flags);
}
static inline void *
-kvcalloc(size_t n, size_t size, int flags)
+kvcalloc(size_t n, size_t size, gfp_t flags)
{
- return kvmalloc_array(n, size, flags | M_ZERO);
+ if (n != 0 && SIZE_MAX / n < size)
+ return NULL;
+ return kvmalloc(n * size, flags | M_ZERO);
}
static inline void *
-kvzalloc(size_t size, int flags)
-{
- return malloc(size, M_DRM, flags | M_ZERO);
-}
-
-static inline void
-kvfree(const void *objp)
+kvzalloc(size_t size, gfp_t flags)
{
- free((void *)objp, M_DRM, 0);
+ return kvmalloc(size, flags | M_ZERO);
}
static inline long
diff --git a/sys/dev/pci/drm/include/linux/vmalloc.h b/sys/dev/pci/drm/include/linux/vmalloc.h
index 7daa7bea5bd..fd6cc54b7db 100644
--- a/sys/dev/pci/drm/include/linux/vmalloc.h
+++ b/sys/dev/pci/drm/include/linux/vmalloc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmalloc.h,v 1.1 2019/04/14 10:14:53 jsg Exp $ */
+/* $OpenBSD: vmalloc.h,v 1.2 2021/01/08 23:02:09 kettenis Exp $ */
/*
* Copyright (c) 2013, 2014, 2015 Mark Kettenis
*
@@ -25,25 +25,10 @@
#include <linux/overflow.h>
void *vmap(struct vm_page **, unsigned int, unsigned long, pgprot_t);
-void vunmap(void *, size_t);
-
-static inline void *
-vmalloc(unsigned long size)
-{
- return malloc(size, M_DRM, M_WAITOK | M_CANFAIL);
-}
-
-static inline void *
-vzalloc(unsigned long size)
-{
- return malloc(size, M_DRM, M_WAITOK | M_CANFAIL | M_ZERO);
-}
-
-static inline void
-vfree(void *objp)
-{
- free(objp, M_DRM, 0);
-}
+void vunmap(void *, size_t);
+void *vmalloc(unsigned long);
+void *vzalloc(unsigned long);
+void vfree(const void *);
#endif