#include <sys/queue.h>
#include <vm/vm_extern.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/mm.h>
struct vmap {
void *addr;
int npages;
SLIST_ENTRY(vmap) vm_vmaps;
};
struct lock vmap_lock = LOCK_INITIALIZER("dlvml", 0, LK_CANRECURSE);
SLIST_HEAD(vmap_list_head, vmap) vmap_list = SLIST_HEAD_INITIALIZER(vmap_list);
void *
vmap(struct page **pages, unsigned int count,
unsigned long flags, pgprot_t prot)
{
struct vmap *vmp;
vm_offset_t off;
size_t size;
vmp = kmalloc(sizeof(struct vmap), M_DRM, M_WAITOK | M_ZERO);
size = count * PAGE_SIZE;
off = kmem_alloc_nofault(kernel_map, size,
VM_SUBSYS_DRM_VMAP, PAGE_SIZE);
if (off == 0)
return (NULL);
vmp->addr = (void *)off;
vmp->npages = count;
pmap_qenter(off, (struct vm_page **)pages, count);
lockmgr(&vmap_lock, LK_EXCLUSIVE);
SLIST_INSERT_HEAD(&vmap_list, vmp, vm_vmaps);
lockmgr(&vmap_lock, LK_RELEASE);
return (void *)off;
}
void
vunmap(const void *addr)
{
struct vmap *vmp, *tmp_vmp;
size_t size;
SLIST_FOREACH_MUTABLE(vmp, &vmap_list, vm_vmaps, tmp_vmp) {
if (vmp->addr == addr) {
size = vmp->npages * PAGE_SIZE;
pmap_qremove((vm_offset_t)addr, vmp->npages);
kmem_free(kernel_map, (vm_offset_t)addr, size);
goto found;
}
}
found:
lockmgr(&vmap_lock, LK_EXCLUSIVE);
SLIST_REMOVE(&vmap_list, vmp, vmap, vm_vmaps);
lockmgr(&vmap_lock, LK_RELEASE);
kfree(vmp);
}
int
is_vmalloc_addr(const void *x)
{
struct vmap *vmp, *tmp_vmp;
SLIST_FOREACH_MUTABLE(vmp, &vmap_list, vm_vmaps, tmp_vmp) {
if (vmp->addr == x)
return 1;
}
return false;
}
void *
vmalloc(unsigned long size)
{
return kmalloc(size, M_DRM, M_WAITOK);
}
void *
vzalloc(unsigned long size)
{
return kmalloc(size, M_DRM, M_WAITOK | M_ZERO);
}
void *
vmalloc_user(unsigned long size)
{
return kmalloc(size, M_DRM, M_WAITOK | M_ZERO);
}
void
vfree(const void *addr)
{
void *nc_addr;
memcpy(&nc_addr, &addr, sizeof(void *));
kfree(nc_addr);
}
void *
kvmalloc_array(size_t n, size_t size, gfp_t flags)
{
if (n == 0)
return NULL;
if (n > SIZE_MAX / size)
return NULL;
return kmalloc(n * size, M_DRM, flags);
}