diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2021-01-08 23:02:10 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2021-01-08 23:02:10 +0000 |
commit | adcbb07c87168171c619563e8fb606105746665a (patch) | |
tree | 6ca8df1e3716b4602a413b1b3955e0eae28c2983 /sys/dev/pci/drm | |
parent | 406225212b5f6d0ad55c1ad46027ad333db102d2 (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.c | 111 | ||||
-rw-r--r-- | sys/dev/pci/drm/include/linux/mm.h | 29 | ||||
-rw-r--r-- | sys/dev/pci/drm/include/linux/vmalloc.h | 25 |
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 |