#include <sys/kmem.h>
#include <sys/vmem.h>
#include <linux/mm.h>
-#include <linux/ratelimit.h>
/*
* As a general rule kmem_alloc() allocations should be small, preferably
* rate limited warning will be printed to the console for any kmem_alloc()
* which exceeds a reasonable threshold.
*
- * The default warning threshold is set to eight pages but capped at 32K to
+ * The default warning threshold is set to sixteen pages but capped at 64K to
* accommodate systems using large pages. This value was selected to be small
* enough to ensure the largest allocations are quickly noticed and fixed.
* But large enough to avoid logging any warnings when a allocation size is
* allocations are quickly caught. These warnings may be disabled by setting
* the threshold to zero.
*/
-unsigned int spl_kmem_alloc_warn = MAX(8 * PAGE_SIZE, 32 * 1024);
+/* BEGIN CSTYLED */
+unsigned int spl_kmem_alloc_warn = MIN(16 * PAGE_SIZE, 64 * 1024);
module_param(spl_kmem_alloc_warn, uint, 0644);
MODULE_PARM_DESC(spl_kmem_alloc_warn,
"Warning threshold in bytes for a kmem_alloc()");
MODULE_PARM_DESC(spl_kmem_alloc_max,
"Maximum size in bytes for a kmem_alloc()");
EXPORT_SYMBOL(spl_kmem_alloc_max);
+/* END CSTYLED */
int
kmem_debugging(void)
do {
va_copy(aq, ap);
- ptr = kvasprintf(GFP_KERNEL, fmt, aq);
+ ptr = kvasprintf(kmem_flags_convert(KM_SLEEP), fmt, aq);
va_end(aq);
} while (ptr == NULL);
do {
va_start(ap, fmt);
- ptr = kvasprintf(GFP_KERNEL, fmt, ap);
+ ptr = kvasprintf(kmem_flags_convert(KM_SLEEP), fmt, ap);
va_end(ap);
} while (ptr == NULL);
}
EXPORT_SYMBOL(strfree);
-/*
- * Limit the number of large allocation stack traces dumped to not more than
- * 5 every 60 seconds to prevent denial-of-service attacks from debug code.
- */
-DEFINE_RATELIMIT_STATE(kmem_alloc_ratelimit_state, 60 * HZ, 5);
-
/*
* 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;
/*
* through the vmem_alloc()/vmem_zalloc() interfaces.
*/
if ((spl_kmem_alloc_warn > 0) && (size > spl_kmem_alloc_warn) &&
- !(flags & KM_VMEM) && __ratelimit(&kmem_alloc_ratelimit_state)) {
+ !(flags & KM_VMEM)) {
printk(KERN_WARNING
"Large kmem_alloc(%lu, 0x%x), please file an issue at:\n"
"https://github.com/zfsonlinux/zfs/issues/new\n",
* impact performance so frequently manipulating the virtual
* address space is strongly discouraged.
*/
- if (unlikely(size > spl_kmem_alloc_max)) {
+ if ((size > spl_kmem_alloc_max) || use_vmem) {
if (flags & KM_VMEM) {
- ptr = spl_vmalloc(size, lflags, PAGE_KERNEL);
+ ptr = __vmalloc(size, lflags | __GFP_HIGHMEM,
+ PAGE_KERNEL);
} else {
return (NULL);
}
if (likely(ptr) || (flags & KM_NOSLEEP))
return (ptr);
- if (unlikely(__ratelimit(&kmem_alloc_ratelimit_state))) {
- printk(KERN_WARNING
- "Possible memory allocation deadlock: "
- "size=%lu lflags=0x%x",
- (unsigned long)size, lflags);
- dump_stack();
+ /*
+ * For vmem_alloc() and vmem_zalloc() callers retry immediately
+ * using __vmalloc() which is unlikely to fail.
+ */
+ if ((flags & KM_VMEM) && (use_vmem == 0)) {
+ use_vmem = 1;
+ continue;
}
/*
{
kmem_debug_t *dptr;
+ /* Ignore NULL pointer since we haven't tracked it at all */
+ if (ptr == NULL)
+ return;
+
/* Must exist in hash due to kmem_alloc() */
dptr = kmem_del_init(&kmem_lock, kmem_table, KMEM_HASH_BITS, ptr);
ASSERT3P(dptr, !=, NULL);
printk(KERN_WARNING "%-16s %-5s %-16s %s:%s\n", "address",
"size", "data", "func", "line");
- list_for_each_entry(kd, list, kd_list)
+ list_for_each_entry(kd, list, kd_list) {
printk(KERN_WARNING "%p %-5d %-16s %s:%d\n", kd->kd_addr,
(int)kd->kd_size, spl_sprintf_addr(kd, str, 17, 8),
kd->kd_func, kd->kd_line);
+ }
spin_unlock_irqrestore(lock, flags);
}
int
spl_kmem_init(void)
{
+
#ifdef DEBUG_KMEM
kmem_alloc_used_set(0);
+
+
#ifdef DEBUG_KMEM_TRACKING
spl_kmem_init_tracking(&kmem_list, &kmem_lock, KMEM_TABLE_SIZE);
#endif /* DEBUG_KMEM_TRACKING */