AC_MSG_CHECKING([whether detailed kmem tracking is enabled])
AC_MSG_RESULT([$enable_debug_kmem_tracking])
])
+
+dnl #
+dnl # 4.12 API,
+dnl # Added kvmalloc allocation strategy
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_SRC_KVMALLOC], [
+ ZFS_LINUX_TEST_SRC([kvmalloc], [
+ #include <linux/mm.h>
+ ],[
+ void *p __attribute__ ((unused));
+
+ p = kvmalloc(0, GFP_KERNEL);
+ ])
+])
+
+AC_DEFUN([ZFS_AC_KERNEL_KVMALLOC], [
+ AC_MSG_CHECKING([whether kvmalloc(ptr, flags) is available])
+ ZFS_LINUX_TEST_RESULT([kvmalloc], [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_KVMALLOC, 1, [kvmalloc exists])
+ ],[
+ AC_MSG_RESULT(no)
+ ])
+])
ASSERT(ISP2(size));
ptr = (void *)__get_free_pages(lflags, get_order(size));
} else {
- ptr = __vmalloc(size, lflags | __GFP_HIGHMEM, PAGE_KERNEL);
+ /*
+ * GFP_KERNEL allocations can safely use kvmalloc which may
+ * improve performance by avoiding a) high latency caused by
+ * vmalloc's on-access allocation, b) performance loss due to
+ * MMU memory address mapping and c) vmalloc locking overhead.
+ * This has the side-effect that the slab statistics will
+ * incorrectly report this as a vmem allocation, but that is
+ * purely cosmetic.
+ *
+ * For non-GFP_KERNEL allocations we stick to __vmalloc.
+ */
+ if ((lflags & GFP_KERNEL) == GFP_KERNEL) {
+ ptr = spl_kvmalloc(size, lflags);
+ } else {
+ ptr = __vmalloc(size, lflags | __GFP_HIGHMEM,
+ PAGE_KERNEL);
+ }
}
/* Resulting allocated memory will be page aligned */
ASSERT(ISP2(size));
free_pages((unsigned long)ptr, get_order(size));
} else {
- vfree(ptr);
+ spl_kmem_free_impl(ptr, size);
}
}
}
EXPORT_SYMBOL(kmem_strfree);
+/* Kernel compatibility for <4.13 */
+#ifndef __GFP_RETRY_MAYFAIL
+#define __GFP_RETRY_MAYFAIL __GFP_REPEAT
+#endif
+
+void *
+spl_kvmalloc(size_t size, gfp_t lflags)
+{
+#ifdef HAVE_KVMALLOC
+ /*
+ * GFP_KERNEL allocations can safely use kvmalloc which may
+ * improve performance by avoiding a) high latency caused by
+ * vmalloc's on-access allocation, b) performance loss due to
+ * MMU memory address mapping and c) vmalloc locking overhead.
+ * This has the side-effect that the slab statistics will
+ * incorrectly report this as a vmem allocation, but that is
+ * purely cosmetic.
+ */
+ if ((lflags & GFP_KERNEL) == GFP_KERNEL)
+ return (kvmalloc(size, lflags));
+#endif
+
+ gfp_t kmalloc_lflags = lflags;
+
+ if (size > PAGE_SIZE) {
+ /*
+ * We need to set __GFP_NOWARN here since spl_kvmalloc is not
+ * only called by spl_kmem_alloc_impl but can be called
+ * directly with custom lflags, too. In that case
+ * kmem_flags_convert does not get called, which would
+ * implicitly set __GFP_NOWARN.
+ */
+ kmalloc_lflags |= __GFP_NOWARN;
+
+ /*
+ * N.B. __GFP_RETRY_MAYFAIL is supported only for large
+ * e (>32kB) allocations.
+ *
+ * We have to override __GFP_RETRY_MAYFAIL by __GFP_NORETRY
+ * for !costly requests because there is no other way to tell
+ * the allocator that we want to fail rather than retry
+ * endlessly.
+ */
+ if (!(kmalloc_lflags & __GFP_RETRY_MAYFAIL) ||
+ (size <= PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
+ kmalloc_lflags |= __GFP_NORETRY;
+ }
+ }
+
+ /*
+ * We first try kmalloc - even for big sizes - and fall back to
+ * __vmalloc if that fails.
+ *
+ * For non-GFP_KERNEL allocations we always stick to kmalloc_node,
+ * and fail when kmalloc is not successful (returns NULL).
+ * We cannot fall back to __vmalloc in this case because __vmalloc
+ * internally uses GPF_KERNEL allocations.
+ */
+ void *ptr = kmalloc_node(size, kmalloc_lflags, NUMA_NO_NODE);
+ if (ptr || size <= PAGE_SIZE ||
+ (lflags & GFP_KERNEL) != GFP_KERNEL) {
+ return (ptr);
+ }
+
+ return (__vmalloc(size, lflags | __GFP_HIGHMEM, PAGE_KERNEL));
+}
+
/*
* General purpose unified implementation of kmem_alloc(). It is an
* amalgamation of Linux and Illumos allocator design. It should never be
spl_kmem_alloc_impl(size_t size, int flags, int node)
{
gfp_t lflags = kmem_flags_convert(flags);
- int use_vmem = 0;
void *ptr;
/*
* impact performance so frequently manipulating the virtual
* address space is strongly discouraged.
*/
- if ((size > spl_kmem_alloc_max) || use_vmem) {
+ if (size > spl_kmem_alloc_max) {
if (flags & KM_VMEM) {
ptr = __vmalloc(size, lflags | __GFP_HIGHMEM,
PAGE_KERNEL);
return (NULL);
}
} else {
- ptr = kmalloc_node(size, lflags, node);
+ if (flags & KM_VMEM) {
+ ptr = spl_kvmalloc(size, lflags);
+ } else {
+ ptr = kmalloc_node(size, lflags, node);
+ }
}
if (likely(ptr) || (flags & KM_NOSLEEP))
return (ptr);
/*
- * For vmem_alloc() and vmem_zalloc() callers retry immediately
- * using __vmalloc() which is unlikely to fail.
+ * Try hard to satisfy the allocation. However, when progress
+ * cannot be made, the allocation is allowed to fail.
*/
- if ((flags & KM_VMEM) && (use_vmem == 0)) {
- use_vmem = 1;
- continue;
- }
+ if ((lflags & GFP_KERNEL) == GFP_KERNEL)
+ lflags |= __GFP_RETRY_MAYFAIL;
/*
* Use cond_resched() instead of congestion_wait() to avoid