]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge branch 'kmemleak' of git://linux-arm.org/linux-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Dec 2009 00:00:19 +0000 (16:00 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Dec 2009 00:00:19 +0000 (16:00 -0800)
* 'kmemleak' of git://linux-arm.org/linux-2.6:
  kmemleak: fix kconfig for crc32 build error
  kmemleak: Reduce the false positives by checking for modified objects
  kmemleak: Show the age of an unreferenced object
  kmemleak: Release the object lock before calling put_object()
  kmemleak: Scan the _ftrace_events section in modules
  kmemleak: Simplify the kmemleak_scan_area() function prototype
  kmemleak: Do not use off-slab management with SLAB_NOLEAKTRACE

1  2 
kernel/module.c
lib/Kconfig.debug
mm/kmemleak.c
mm/slab.c

diff --combined kernel/module.c
index a65dc787a27bf6f28e4df980523820dacd1d14b3,dd29ba43c34f5a38de223f728cef9d23a7738735..e96b8ed1cb6aff09033114dd219157b84e8b23a3
@@@ -370,6 -370,8 +370,6 @@@ EXPORT_SYMBOL_GPL(find_module)
  
  #ifdef CONFIG_SMP
  
 -#ifndef CONFIG_HAVE_LEGACY_PER_CPU_AREA
 -
  static void *percpu_modalloc(unsigned long size, unsigned long align,
                             const char *name)
  {
@@@ -393,6 -395,154 +393,6 @@@ static void percpu_modfree(void *freeme
        free_percpu(freeme);
  }
  
 -#else /* ... CONFIG_HAVE_LEGACY_PER_CPU_AREA */
 -
 -/* Number of blocks used and allocated. */
 -static unsigned int pcpu_num_used, pcpu_num_allocated;
 -/* Size of each block.  -ve means used. */
 -static int *pcpu_size;
 -
 -static int split_block(unsigned int i, unsigned short size)
 -{
 -      /* Reallocation required? */
 -      if (pcpu_num_used + 1 > pcpu_num_allocated) {
 -              int *new;
 -
 -              new = krealloc(pcpu_size, sizeof(new[0])*pcpu_num_allocated*2,
 -                             GFP_KERNEL);
 -              if (!new)
 -                      return 0;
 -
 -              pcpu_num_allocated *= 2;
 -              pcpu_size = new;
 -      }
 -
 -      /* Insert a new subblock */
 -      memmove(&pcpu_size[i+1], &pcpu_size[i],
 -              sizeof(pcpu_size[0]) * (pcpu_num_used - i));
 -      pcpu_num_used++;
 -
 -      pcpu_size[i+1] -= size;
 -      pcpu_size[i] = size;
 -      return 1;
 -}
 -
 -static inline unsigned int block_size(int val)
 -{
 -      if (val < 0)
 -              return -val;
 -      return val;
 -}
 -
 -static void *percpu_modalloc(unsigned long size, unsigned long align,
 -                           const char *name)
 -{
 -      unsigned long extra;
 -      unsigned int i;
 -      void *ptr;
 -      int cpu;
 -
 -      if (align > PAGE_SIZE) {
 -              printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n",
 -                     name, align, PAGE_SIZE);
 -              align = PAGE_SIZE;
 -      }
 -
 -      ptr = __per_cpu_start;
 -      for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
 -              /* Extra for alignment requirement. */
 -              extra = ALIGN((unsigned long)ptr, align) - (unsigned long)ptr;
 -              BUG_ON(i == 0 && extra != 0);
 -
 -              if (pcpu_size[i] < 0 || pcpu_size[i] < extra + size)
 -                      continue;
 -
 -              /* Transfer extra to previous block. */
 -              if (pcpu_size[i-1] < 0)
 -                      pcpu_size[i-1] -= extra;
 -              else
 -                      pcpu_size[i-1] += extra;
 -              pcpu_size[i] -= extra;
 -              ptr += extra;
 -
 -              /* Split block if warranted */
 -              if (pcpu_size[i] - size > sizeof(unsigned long))
 -                      if (!split_block(i, size))
 -                              return NULL;
 -
 -              /* add the per-cpu scanning areas */
 -              for_each_possible_cpu(cpu)
 -                      kmemleak_alloc(ptr + per_cpu_offset(cpu), size, 0,
 -                                     GFP_KERNEL);
 -
 -              /* Mark allocated */
 -              pcpu_size[i] = -pcpu_size[i];
 -              return ptr;
 -      }
 -
 -      printk(KERN_WARNING "Could not allocate %lu bytes percpu data\n",
 -             size);
 -      return NULL;
 -}
 -
 -static void percpu_modfree(void *freeme)
 -{
 -      unsigned int i;
 -      void *ptr = __per_cpu_start + block_size(pcpu_size[0]);
 -      int cpu;
 -
 -      /* First entry is core kernel percpu data. */
 -      for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
 -              if (ptr == freeme) {
 -                      pcpu_size[i] = -pcpu_size[i];
 -                      goto free;
 -              }
 -      }
 -      BUG();
 -
 - free:
 -      /* remove the per-cpu scanning areas */
 -      for_each_possible_cpu(cpu)
 -              kmemleak_free(freeme + per_cpu_offset(cpu));
 -
 -      /* Merge with previous? */
 -      if (pcpu_size[i-1] >= 0) {
 -              pcpu_size[i-1] += pcpu_size[i];
 -              pcpu_num_used--;
 -              memmove(&pcpu_size[i], &pcpu_size[i+1],
 -                      (pcpu_num_used - i) * sizeof(pcpu_size[0]));
 -              i--;
 -      }
 -      /* Merge with next? */
 -      if (i+1 < pcpu_num_used && pcpu_size[i+1] >= 0) {
 -              pcpu_size[i] += pcpu_size[i+1];
 -              pcpu_num_used--;
 -              memmove(&pcpu_size[i+1], &pcpu_size[i+2],
 -                      (pcpu_num_used - (i+1)) * sizeof(pcpu_size[0]));
 -      }
 -}
 -
 -static int percpu_modinit(void)
 -{
 -      pcpu_num_used = 2;
 -      pcpu_num_allocated = 2;
 -      pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated,
 -                          GFP_KERNEL);
 -      /* Static in-kernel percpu data (used). */
 -      pcpu_size[0] = -(__per_cpu_end-__per_cpu_start);
 -      /* Free room. */
 -      pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
 -      if (pcpu_size[1] < 0) {
 -              printk(KERN_ERR "No per-cpu room for modules.\n");
 -              pcpu_num_used = 1;
 -      }
 -
 -      return 0;
 -}
 -__initcall(percpu_modinit);
 -
 -#endif /* CONFIG_HAVE_LEGACY_PER_CPU_AREA */
 -
  static unsigned int find_pcpusec(Elf_Ehdr *hdr,
                                 Elf_Shdr *sechdrs,
                                 const char *secstrings)
@@@ -880,23 -1030,11 +880,23 @@@ static int try_to_force_load(struct mod
  }
  
  #ifdef CONFIG_MODVERSIONS
 +/* If the arch applies (non-zero) relocations to kernel kcrctab, unapply it. */
 +static unsigned long maybe_relocated(unsigned long crc,
 +                                   const struct module *crc_owner)
 +{
 +#ifdef ARCH_RELOCATES_KCRCTAB
 +      if (crc_owner == NULL)
 +              return crc - (unsigned long)reloc_start;
 +#endif
 +      return crc;
 +}
 +
  static int check_version(Elf_Shdr *sechdrs,
                         unsigned int versindex,
                         const char *symname,
                         struct module *mod, 
 -                       const unsigned long *crc)
 +                       const unsigned long *crc,
 +                       const struct module *crc_owner)
  {
        unsigned int i, num_versions;
        struct modversion_info *versions;
                if (strcmp(versions[i].name, symname) != 0)
                        continue;
  
 -              if (versions[i].crc == *crc)
 +              if (versions[i].crc == maybe_relocated(*crc, crc_owner))
                        return 1;
                DEBUGP("Found checksum %lX vs module %lX\n",
 -                     *crc, versions[i].crc);
 +                     maybe_relocated(*crc, crc_owner), versions[i].crc);
                goto bad_version;
        }
  
@@@ -943,8 -1081,7 +943,8 @@@ static inline int check_modstruct_versi
        if (!find_symbol(MODULE_SYMBOL_PREFIX "module_layout", NULL,
                         &crc, true, false))
                BUG();
 -      return check_version(sechdrs, versindex, "module_layout", mod, crc);
 +      return check_version(sechdrs, versindex, "module_layout", mod, crc,
 +                           NULL);
  }
  
  /* First part is kernel version, which we ignore if module has crcs. */
@@@ -962,8 -1099,7 +962,8 @@@ static inline int check_version(Elf_Shd
                                unsigned int versindex,
                                const char *symname,
                                struct module *mod, 
 -                              const unsigned long *crc)
 +                              const unsigned long *crc,
 +                              const struct module *crc_owner)
  {
        return 1;
  }
@@@ -998,8 -1134,8 +998,8 @@@ static const struct kernel_symbol *reso
        /* use_module can fail due to OOM,
           or module initialization or unloading */
        if (sym) {
 -              if (!check_version(sechdrs, versindex, name, mod, crc) ||
 -                  !use_module(mod, owner))
 +              if (!check_version(sechdrs, versindex, name, mod, crc, owner)
 +                  || !use_module(mod, owner))
                        sym = NULL;
        }
        return sym;
@@@ -1051,8 -1187,7 +1051,8 @@@ static void add_sect_attrs(struct modul
  
        /* Count loaded sections and allocate structures */
        for (i = 0; i < nsect; i++)
 -              if (sechdrs[i].sh_flags & SHF_ALLOC)
 +              if (sechdrs[i].sh_flags & SHF_ALLOC
 +                  && sechdrs[i].sh_size)
                        nloaded++;
        size[0] = ALIGN(sizeof(*sect_attrs)
                        + nloaded * sizeof(sect_attrs->attrs[0]),
        for (i = 0; i < nsect; i++) {
                if (! (sechdrs[i].sh_flags & SHF_ALLOC))
                        continue;
 +              if (!sechdrs[i].sh_size)
 +                      continue;
                sattr->address = sechdrs[i].sh_addr;
                sattr->name = kstrdup(secstrings + sechdrs[i].sh_name,
                                        GFP_KERNEL);
@@@ -1910,9 -2043,7 +1910,7 @@@ static void kmemleak_load_module(struc
        unsigned int i;
  
        /* only scan the sections containing data */
-       kmemleak_scan_area(mod->module_core, (unsigned long)mod -
-                          (unsigned long)mod->module_core,
-                          sizeof(struct module), GFP_KERNEL);
+       kmemleak_scan_area(mod, sizeof(struct module), GFP_KERNEL);
  
        for (i = 1; i < hdr->e_shnum; i++) {
                if (!(sechdrs[i].sh_flags & SHF_ALLOC))
                    && strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) != 0)
                        continue;
  
-               kmemleak_scan_area(mod->module_core, sechdrs[i].sh_addr -
-                                  (unsigned long)mod->module_core,
+               kmemleak_scan_area((void *)sechdrs[i].sh_addr,
                                   sechdrs[i].sh_size, GFP_KERNEL);
        }
  }
@@@ -2250,6 -2380,12 +2247,12 @@@ static noinline struct module *load_mod
                                         "_ftrace_events",
                                         sizeof(*mod->trace_events),
                                         &mod->num_trace_events);
+       /*
+        * This section contains pointers to allocated objects in the trace
+        * code and not scanning it leads to false positives.
+        */
+       kmemleak_scan_area(mod->trace_events, sizeof(*mod->trace_events) *
+                          mod->num_trace_events, GFP_KERNEL);
  #endif
  #ifdef CONFIG_FTRACE_MCOUNT_RECORD
        /* sechdrs[0].sh_size is always zero */
diff --combined lib/Kconfig.debug
index 8cf9938dd147840d1dbc67166fa30fc22208bf78,732b9f6ab1ee8bcf0d7deca60a3e0e2377c2a2c3..25c3ed594c547966c1c291750d8adeb3b9e5674b
@@@ -105,7 -105,7 +105,7 @@@ config DEBUG_SECTION_MISMATC
        bool "Enable full Section mismatch analysis"
        depends on UNDEFINED
        # This option is on purpose disabled for now.
 -      # It will be enabled when we are down to a resonable number
 +      # It will be enabled when we are down to a reasonable number
        # of section mismatch warnings (< 10 for an allyesconfig build)
        help
          The section mismatch analysis checks if there are illegal
@@@ -298,14 -298,6 +298,14 @@@ config DEBUG_OBJECTS_TIMER
          timer routines to track the life time of timer objects and
          validate the timer operations.
  
 +config DEBUG_OBJECTS_WORK
 +      bool "Debug work objects"
 +      depends on DEBUG_OBJECTS
 +      help
 +        If you say Y here, additional code will be inserted into the
 +        work queue routines to track the life time of work objects and
 +        validate the work operations.
 +
  config DEBUG_OBJECTS_ENABLE_DEFAULT
        int "debug_objects bootup default value (0-1)"
          range 0 1
@@@ -360,6 -352,7 +360,7 @@@ config DEBUG_KMEMLEA
        select DEBUG_FS if SYSFS
        select STACKTRACE if STACKTRACE_SUPPORT
        select KALLSYMS
+       select CRC32
        help
          Say Y here if you want to enable the memory leak
          detector. The memory allocation/freeing is traced in a way
@@@ -400,7 -393,7 +401,7 @@@ config DEBUG_KMEMLEAK_TES
  
  config DEBUG_PREEMPT
        bool "Debug preemptible kernel"
 -      depends on DEBUG_KERNEL && PREEMPT && (TRACE_IRQFLAGS_SUPPORT || PPC64)
 +      depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT
        default y
        help
          If you say Y here then the kernel will use a debug variant of the
@@@ -575,7 -568,7 +576,7 @@@ config DEBUG_BUGVERBOS
        depends on BUG
        depends on ARM || AVR32 || M32R || M68K || SPARC32 || SPARC64 || \
                   FRV || SUPERH || GENERIC_BUG || BLACKFIN || MN10300
 -      default !EMBEDDED
 +      default y
        help
          Say Y here to make BUG() panics output the file name and line number
          of the BUG call as well as the EIP and oops trace.  This aids
@@@ -758,7 -751,7 +759,7 @@@ config RCU_TORTURE_TEST_RUNNABL
  config RCU_CPU_STALL_DETECTOR
        bool "Check for stalled CPUs delaying RCU grace periods"
        depends on TREE_RCU || TREE_PREEMPT_RCU
 -      default n
 +      default y
        help
          This option causes RCU to printk information on which
          CPUs are delaying the current grace period, but only when
@@@ -920,7 -913,7 +921,7 @@@ config LATENCYTO
  
  config SYSCTL_SYSCALL_CHECK
        bool "Sysctl checks"
 -      depends on SYSCTL_SYSCALL
 +      depends on SYSCTL
        ---help---
          sys_sysctl uses binary paths that have been found challenging
          to properly maintain and use. This enables checks that help
diff --combined mm/kmemleak.c
index 13f33b3081ec2794afdae74d9071a88ec6aa9df3,002adc3cf3a18c230bf6cbffdcc2d7b0811e8078..5b069e4f5e485ac8f5906c4a0cad7dbdeef75f7e
@@@ -93,6 -93,7 +93,7 @@@
  #include <linux/nodemask.h>
  #include <linux/mm.h>
  #include <linux/workqueue.h>
+ #include <linux/crc32.h>
  
  #include <asm/sections.h>
  #include <asm/processor.h>
  #define MSECS_MIN_AGE         5000    /* minimum object age for reporting */
  #define SECS_FIRST_SCAN               60      /* delay before the first scan */
  #define SECS_SCAN_WAIT                600     /* subsequent auto scanning delay */
- #define GRAY_LIST_PASSES      25      /* maximum number of gray list scans */
  #define MAX_SCAN_SIZE         4096    /* maximum size of a scanned block */
  
  #define BYTES_PER_POINTER     sizeof(void *)
  /* scanning area inside a memory block */
  struct kmemleak_scan_area {
        struct hlist_node node;
-       unsigned long offset;
-       size_t length;
+       unsigned long start;
+       size_t size;
  };
  
  #define KMEMLEAK_GREY 0
@@@ -149,6 -149,8 +149,8 @@@ struct kmemleak_object 
        int min_count;
        /* the total number of pointers found pointing to this object */
        int count;
+       /* checksum for detecting modified objects */
+       u32 checksum;
        /* memory ranges to be scanned inside an object (empty for all) */
        struct hlist_head area_list;
        unsigned long trace[MAX_TRACE];
  #define OBJECT_REPORTED               (1 << 1)
  /* flag set to not scan the object */
  #define OBJECT_NO_SCAN                (1 << 2)
- /* flag set on newly allocated objects */
- #define OBJECT_NEW            (1 << 3)
  
  /* number of bytes to print per line; must be 16 or 32 */
  #define HEX_ROW_SIZE          16
@@@ -241,8 -241,6 +241,6 @@@ struct early_log 
        const void *ptr;                /* allocated/freed memory block */
        size_t size;                    /* memory block size */
        int min_count;                  /* minimum reference count */
-       unsigned long offset;           /* scan area offset */
-       size_t length;                  /* scan area length */
        unsigned long trace[MAX_TRACE]; /* stack trace */
        unsigned int trace_len;         /* stack trace length */
  };
@@@ -323,11 -321,6 +321,6 @@@ static bool color_gray(const struct kme
                object->count >= object->min_count;
  }
  
- static bool color_black(const struct kmemleak_object *object)
- {
-       return object->min_count == KMEMLEAK_BLACK;
- }
  /*
   * Objects are considered unreferenced only if their color is white, they have
   * not be deleted and have a minimum age to avoid false positives caused by
   */
  static bool unreferenced_object(struct kmemleak_object *object)
  {
-       return (object->flags & OBJECT_ALLOCATED) && color_white(object) &&
+       return (color_white(object) && object->flags & OBJECT_ALLOCATED) &&
                time_before_eq(object->jiffies + jiffies_min_age,
                               jiffies_last_scan);
  }
@@@ -348,11 -341,13 +341,13 @@@ static void print_unreferenced(struct s
                               struct kmemleak_object *object)
  {
        int i;
+       unsigned int msecs_age = jiffies_to_msecs(jiffies - object->jiffies);
  
        seq_printf(seq, "unreferenced object 0x%08lx (size %zu):\n",
                   object->pointer, object->size);
-       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu\n",
-                  object->comm, object->pid, object->jiffies);
+       seq_printf(seq, "  comm \"%s\", pid %d, jiffies %lu (age %d.%03ds)\n",
+                  object->comm, object->pid, object->jiffies,
+                  msecs_age / 1000, msecs_age % 1000);
        hex_dump_object(seq, object);
        seq_printf(seq, "  backtrace:\n");
  
@@@ -381,6 -376,7 +376,7 @@@ static void dump_object_info(struct kme
        pr_notice("  min_count = %d\n", object->min_count);
        pr_notice("  count = %d\n", object->count);
        pr_notice("  flags = 0x%lx\n", object->flags);
+       pr_notice("  checksum = %d\n", object->checksum);
        pr_notice("  backtrace:\n");
        print_stack_trace(&trace, 4);
  }
@@@ -522,12 -518,13 +518,13 @@@ static struct kmemleak_object *create_o
        INIT_HLIST_HEAD(&object->area_list);
        spin_lock_init(&object->lock);
        atomic_set(&object->use_count, 1);
-       object->flags = OBJECT_ALLOCATED | OBJECT_NEW;
+       object->flags = OBJECT_ALLOCATED;
        object->pointer = ptr;
        object->size = size;
        object->min_count = min_count;
-       object->count = -1;                     /* no color initially */
+       object->count = 0;                      /* white color initially */
        object->jiffies = jiffies;
+       object->checksum = 0;
  
        /* task information */
        if (in_irq()) {
@@@ -720,14 -717,13 +717,13 @@@ static void make_black_object(unsigned 
   * Add a scanning area to the object. If at least one such area is added,
   * kmemleak will only scan these ranges rather than the whole memory block.
   */
- static void add_scan_area(unsigned long ptr, unsigned long offset,
-                         size_t length, gfp_t gfp)
+ static void add_scan_area(unsigned long ptr, size_t size, gfp_t gfp)
  {
        unsigned long flags;
        struct kmemleak_object *object;
        struct kmemleak_scan_area *area;
  
-       object = find_and_get_object(ptr, 0);
+       object = find_and_get_object(ptr, 1);
        if (!object) {
                kmemleak_warn("Adding scan area to unknown object at 0x%08lx\n",
                              ptr);
        }
  
        spin_lock_irqsave(&object->lock, flags);
-       if (offset + length > object->size) {
+       if (ptr + size > object->pointer + object->size) {
                kmemleak_warn("Scan area larger than object 0x%08lx\n", ptr);
                dump_object_info(object);
                kmem_cache_free(scan_area_cache, area);
        }
  
        INIT_HLIST_NODE(&area->node);
-       area->offset = offset;
-       area->length = length;
+       area->start = ptr;
+       area->size = size;
  
        hlist_add_head(&area->node, &object->area_list);
  out_unlock:
@@@ -786,7 -782,7 +782,7 @@@ static void object_no_scan(unsigned lon
   * processed later once kmemleak is fully initialized.
   */
  static void __init log_early(int op_type, const void *ptr, size_t size,
-                            int min_count, unsigned long offset, size_t length)
+                            int min_count)
  {
        unsigned long flags;
        struct early_log *log;
        log->ptr = ptr;
        log->size = size;
        log->min_count = min_count;
-       log->offset = offset;
-       log->length = length;
        if (op_type == KMEMLEAK_ALLOC)
                log->trace_len = __save_stack_trace(log->trace);
        crt_early_log++;
@@@ -858,7 -852,7 +852,7 @@@ void __ref kmemleak_alloc(const void *p
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                create_object((unsigned long)ptr, size, min_count, gfp);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_ALLOC, ptr, size, min_count, 0, 0);
+               log_early(KMEMLEAK_ALLOC, ptr, size, min_count);
  }
  EXPORT_SYMBOL_GPL(kmemleak_alloc);
  
@@@ -873,7 -867,7 +867,7 @@@ void __ref kmemleak_free(const void *pt
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                delete_object_full((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_FREE, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_FREE, ptr, 0, 0);
  }
  EXPORT_SYMBOL_GPL(kmemleak_free);
  
@@@ -888,7 -882,7 +882,7 @@@ void __ref kmemleak_free_part(const voi
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                delete_object_part((unsigned long)ptr, size);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_FREE_PART, ptr, size, 0, 0, 0);
+               log_early(KMEMLEAK_FREE_PART, ptr, size, 0);
  }
  EXPORT_SYMBOL_GPL(kmemleak_free_part);
  
@@@ -903,7 -897,7 +897,7 @@@ void __ref kmemleak_not_leak(const voi
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                make_gray_object((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_NOT_LEAK, ptr, 0, 0);
  }
  EXPORT_SYMBOL(kmemleak_not_leak);
  
@@@ -919,22 -913,21 +913,21 @@@ void __ref kmemleak_ignore(const void *
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                make_black_object((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_IGNORE, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_IGNORE, ptr, 0, 0);
  }
  EXPORT_SYMBOL(kmemleak_ignore);
  
  /*
   * Limit the range to be scanned in an allocated memory block.
   */
- void __ref kmemleak_scan_area(const void *ptr, unsigned long offset,
-                             size_t length, gfp_t gfp)
+ void __ref kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp)
  {
        pr_debug("%s(0x%p)\n", __func__, ptr);
  
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
-               add_scan_area((unsigned long)ptr, offset, length, gfp);
+               add_scan_area((unsigned long)ptr, size, gfp);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_SCAN_AREA, ptr, 0, 0, offset, length);
+               log_early(KMEMLEAK_SCAN_AREA, ptr, size, 0);
  }
  EXPORT_SYMBOL(kmemleak_scan_area);
  
@@@ -948,10 -941,24 +941,24 @@@ void __ref kmemleak_no_scan(const void 
        if (atomic_read(&kmemleak_enabled) && ptr && !IS_ERR(ptr))
                object_no_scan((unsigned long)ptr);
        else if (atomic_read(&kmemleak_early_log))
-               log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0, 0, 0);
+               log_early(KMEMLEAK_NO_SCAN, ptr, 0, 0);
  }
  EXPORT_SYMBOL(kmemleak_no_scan);
  
+ /*
+  * Update an object's checksum and return true if it was modified.
+  */
+ static bool update_checksum(struct kmemleak_object *object)
+ {
+       u32 old_csum = object->checksum;
+       if (!kmemcheck_is_obj_initialized(object->pointer, object->size))
+               return false;
+       object->checksum = crc32(0, (void *)object->pointer, object->size);
+       return object->checksum != old_csum;
+ }
  /*
   * Memory scanning is a long process and it needs to be interruptable. This
   * function checks whether such interrupt condition occured.
@@@ -1031,11 -1038,14 +1038,14 @@@ static void scan_block(void *_start, vo
                 * added to the gray_list.
                 */
                object->count++;
-               if (color_gray(object))
+               if (color_gray(object)) {
                        list_add_tail(&object->gray_list, &gray_list);
-               else
-                       put_object(object);
+                       spin_unlock_irqrestore(&object->lock, flags);
+                       continue;
+               }
                spin_unlock_irqrestore(&object->lock, flags);
+               put_object(object);
        }
  }
  
@@@ -1050,8 -1060,8 +1060,8 @@@ static void scan_object(struct kmemleak
        unsigned long flags;
  
        /*
 -       * Once the object->lock is aquired, the corresponding memory block
 -       * cannot be freed (the same lock is aquired in delete_object).
 +       * Once the object->lock is acquired, the corresponding memory block
 +       * cannot be freed (the same lock is acquired in delete_object).
         */
        spin_lock_irqsave(&object->lock, flags);
        if (object->flags & OBJECT_NO_SCAN)
                }
        } else
                hlist_for_each_entry(area, elem, &object->area_list, node)
-                       scan_block((void *)(object->pointer + area->offset),
-                                  (void *)(object->pointer + area->offset
-                                           + area->length), object, 0);
+                       scan_block((void *)area->start,
+                                  (void *)(area->start + area->size),
+                                  object, 0);
  out:
        spin_unlock_irqrestore(&object->lock, flags);
  }
  
+ /*
+  * Scan the objects already referenced (gray objects). More objects will be
+  * referenced and, if there are no memory leaks, all the objects are scanned.
+  */
+ static void scan_gray_list(void)
+ {
+       struct kmemleak_object *object, *tmp;
+       /*
+        * The list traversal is safe for both tail additions and removals
+        * from inside the loop. The kmemleak objects cannot be freed from
+        * outside the loop because their use_count was incremented.
+        */
+       object = list_entry(gray_list.next, typeof(*object), gray_list);
+       while (&object->gray_list != &gray_list) {
+               cond_resched();
+               /* may add new objects to the list */
+               if (!scan_should_stop())
+                       scan_object(object);
+               tmp = list_entry(object->gray_list.next, typeof(*object),
+                                gray_list);
+               /* remove the object from the list and release it */
+               list_del(&object->gray_list);
+               put_object(object);
+               object = tmp;
+       }
+       WARN_ON(!list_empty(&gray_list));
+ }
  /*
   * Scan data sections and all the referenced memory blocks allocated via the
   * kernel's standard allocators. This function must be called with the
  static void kmemleak_scan(void)
  {
        unsigned long flags;
-       struct kmemleak_object *object, *tmp;
+       struct kmemleak_object *object;
        int i;
        int new_leaks = 0;
-       int gray_list_pass = 0;
  
        jiffies_last_scan = jiffies;
  
  #endif
                /* reset the reference count (whiten the object) */
                object->count = 0;
-               object->flags &= ~OBJECT_NEW;
                if (color_gray(object) && get_object(object))
                        list_add_tail(&object->gray_list, &gray_list);
  
  
        /*
         * Scan the objects already referenced from the sections scanned
-        * above. More objects will be referenced and, if there are no memory
-        * leaks, all the objects will be scanned. The list traversal is safe
-        * for both tail additions and removals from inside the loop. The
-        * kmemleak objects cannot be freed from outside the loop because their
-        * use_count was increased.
+        * above.
         */
- repeat:
-       object = list_entry(gray_list.next, typeof(*object), gray_list);
-       while (&object->gray_list != &gray_list) {
-               cond_resched();
-               /* may add new objects to the list */
-               if (!scan_should_stop())
-                       scan_object(object);
-               tmp = list_entry(object->gray_list.next, typeof(*object),
-                                gray_list);
-               /* remove the object from the list and release it */
-               list_del(&object->gray_list);
-               put_object(object);
-               object = tmp;
-       }
-       if (scan_should_stop() || ++gray_list_pass >= GRAY_LIST_PASSES)
-               goto scan_end;
+       scan_gray_list();
  
        /*
-        * Check for new objects allocated during this scanning and add them
-        * to the gray list.
+        * Check for new or unreferenced objects modified since the previous
+        * scan and color them gray until the next scan.
         */
        rcu_read_lock();
        list_for_each_entry_rcu(object, &object_list, object_list) {
                spin_lock_irqsave(&object->lock, flags);
-               if ((object->flags & OBJECT_NEW) && !color_black(object) &&
-                   get_object(object)) {
-                       object->flags &= ~OBJECT_NEW;
+               if (color_white(object) && (object->flags & OBJECT_ALLOCATED)
+                   && update_checksum(object) && get_object(object)) {
+                       /* color it gray temporarily */
+                       object->count = object->min_count;
                        list_add_tail(&object->gray_list, &gray_list);
                }
                spin_unlock_irqrestore(&object->lock, flags);
        }
        rcu_read_unlock();
  
-       if (!list_empty(&gray_list))
-               goto repeat;
- scan_end:
-       WARN_ON(!list_empty(&gray_list));
+       /*
+        * Re-scan the gray list for modified unreferenced objects.
+        */
+       scan_gray_list();
  
        /*
-        * If scanning was stopped or new objects were being allocated at a
-        * higher rate than gray list scanning, do not report any new
-        * unreferenced objects.
+        * If scanning was stopped do not report any new unreferenced objects.
         */
-       if (scan_should_stop() || gray_list_pass >= GRAY_LIST_PASSES)
+       if (scan_should_stop())
                return;
  
        /*
@@@ -1642,8 -1657,7 +1657,7 @@@ void __init kmemleak_init(void
                        kmemleak_ignore(log->ptr);
                        break;
                case KMEMLEAK_SCAN_AREA:
-                       kmemleak_scan_area(log->ptr, log->offset, log->length,
-                                          GFP_KERNEL);
+                       kmemleak_scan_area(log->ptr, log->size, GFP_KERNEL);
                        break;
                case KMEMLEAK_NO_SCAN:
                        kmemleak_no_scan(log->ptr);
diff --combined mm/slab.c
index 3f4822938f4605f4e6e1cf55f996f812332cd4a6,d2713a944ebdc5b358b660cc9521bcf974c2012f..e17cc2c337b8b6d2794a1a92e0117a8dec16bf0a
+++ b/mm/slab.c
@@@ -490,7 -490,7 +490,7 @@@ static void **dbg_userword(struct kmem_
  
  #endif
  
 -#ifdef CONFIG_KMEMTRACE
 +#ifdef CONFIG_TRACING
  size_t slab_buffer_size(struct kmem_cache *cachep)
  {
        return cachep->buffer_size;
@@@ -604,26 -604,6 +604,26 @@@ static struct kmem_cache cache_cache = 
  
  #define BAD_ALIEN_MAGIC 0x01020304ul
  
 +/*
 + * chicken and egg problem: delay the per-cpu array allocation
 + * until the general caches are up.
 + */
 +static enum {
 +      NONE,
 +      PARTIAL_AC,
 +      PARTIAL_L3,
 +      EARLY,
 +      FULL
 +} g_cpucache_up;
 +
 +/*
 + * used by boot code to determine if it can use slab based allocator
 + */
 +int slab_is_available(void)
 +{
 +      return g_cpucache_up >= EARLY;
 +}
 +
  #ifdef CONFIG_LOCKDEP
  
  /*
  static struct lock_class_key on_slab_l3_key;
  static struct lock_class_key on_slab_alc_key;
  
 -static inline void init_lock_keys(void)
 -
 +static void init_node_lock_keys(int q)
  {
 -      int q;
        struct cache_sizes *s = malloc_sizes;
  
 -      while (s->cs_size != ULONG_MAX) {
 -              for_each_node(q) {
 -                      struct array_cache **alc;
 -                      int r;
 -                      struct kmem_list3 *l3 = s->cs_cachep->nodelists[q];
 -                      if (!l3 || OFF_SLAB(s->cs_cachep))
 -                              continue;
 -                      lockdep_set_class(&l3->list_lock, &on_slab_l3_key);
 -                      alc = l3->alien;
 -                      /*
 -                       * FIXME: This check for BAD_ALIEN_MAGIC
 -                       * should go away when common slab code is taught to
 -                       * work even without alien caches.
 -                       * Currently, non NUMA code returns BAD_ALIEN_MAGIC
 -                       * for alloc_alien_cache,
 -                       */
 -                      if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
 -                              continue;
 -                      for_each_node(r) {
 -                              if (alc[r])
 -                                      lockdep_set_class(&alc[r]->lock,
 -                                           &on_slab_alc_key);
 -                      }
 +      if (g_cpucache_up != FULL)
 +              return;
 +
 +      for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) {
 +              struct array_cache **alc;
 +              struct kmem_list3 *l3;
 +              int r;
 +
 +              l3 = s->cs_cachep->nodelists[q];
 +              if (!l3 || OFF_SLAB(s->cs_cachep))
 +                      return;
 +              lockdep_set_class(&l3->list_lock, &on_slab_l3_key);
 +              alc = l3->alien;
 +              /*
 +               * FIXME: This check for BAD_ALIEN_MAGIC
 +               * should go away when common slab code is taught to
 +               * work even without alien caches.
 +               * Currently, non NUMA code returns BAD_ALIEN_MAGIC
 +               * for alloc_alien_cache,
 +               */
 +              if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
 +                      return;
 +              for_each_node(r) {
 +                      if (alc[r])
 +                              lockdep_set_class(&alc[r]->lock,
 +                                      &on_slab_alc_key);
                }
 -              s++;
        }
  }
 +
 +static inline void init_lock_keys(void)
 +{
 +      int node;
 +
 +      for_each_node(node)
 +              init_node_lock_keys(node);
 +}
  #else
 +static void init_node_lock_keys(int q)
 +{
 +}
 +
  static inline void init_lock_keys(void)
  {
  }
  static DEFINE_MUTEX(cache_chain_mutex);
  static struct list_head cache_chain;
  
 -/*
 - * chicken and egg problem: delay the per-cpu array allocation
 - * until the general caches are up.
 - */
 -static enum {
 -      NONE,
 -      PARTIAL_AC,
 -      PARTIAL_L3,
 -      EARLY,
 -      FULL
 -} g_cpucache_up;
 -
 -/*
 - * used by boot code to determine if it can use slab based allocator
 - */
 -int slab_is_available(void)
 -{
 -      return g_cpucache_up >= EARLY;
 -}
 -
 -static DEFINE_PER_CPU(struct delayed_work, reap_work);
 +static DEFINE_PER_CPU(struct delayed_work, slab_reap_work);
  
  static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep)
  {
@@@ -838,7 -826,7 +838,7 @@@ __setup("noaliencache", noaliencache_se
   * objects freed on different nodes from which they were allocated) and the
   * flushing of remote pcps by calling drain_node_pages.
   */
 -static DEFINE_PER_CPU(unsigned long, reap_node);
 +static DEFINE_PER_CPU(unsigned long, slab_reap_node);
  
  static void init_reap_node(int cpu)
  {
        if (node == MAX_NUMNODES)
                node = first_node(node_online_map);
  
 -      per_cpu(reap_node, cpu) = node;
 +      per_cpu(slab_reap_node, cpu) = node;
  }
  
  static void next_reap_node(void)
  {
 -      int node = __get_cpu_var(reap_node);
 +      int node = __get_cpu_var(slab_reap_node);
  
        node = next_node(node, node_online_map);
        if (unlikely(node >= MAX_NUMNODES))
                node = first_node(node_online_map);
 -      __get_cpu_var(reap_node) = node;
 +      __get_cpu_var(slab_reap_node) = node;
  }
  
  #else
   */
  static void __cpuinit start_cpu_timer(int cpu)
  {
 -      struct delayed_work *reap_work = &per_cpu(reap_work, cpu);
 +      struct delayed_work *reap_work = &per_cpu(slab_reap_work, cpu);
  
        /*
         * When this gets called from do_initcalls via cpucache_init(),
@@@ -1039,7 -1027,7 +1039,7 @@@ static void __drain_alien_cache(struct 
   */
  static void reap_alien(struct kmem_cache *cachep, struct kmem_list3 *l3)
  {
 -      int node = __get_cpu_var(reap_node);
 +      int node = __get_cpu_var(slab_reap_node);
  
        if (l3->alien) {
                struct array_cache *ac = l3->alien[node];
@@@ -1266,8 -1254,6 +1266,8 @@@ static int __cpuinit cpuup_prepare(lon
                kfree(shared);
                free_alien_cache(alien);
        }
 +      init_node_lock_keys(node);
 +
        return 0;
  bad:
        cpuup_canceled(cpu);
@@@ -1300,9 -1286,9 +1300,9 @@@ static int __cpuinit cpuup_callback(str
                 * anything expensive but will only modify reap_work
                 * and reschedule the timer.
                */
 -              cancel_rearming_delayed_work(&per_cpu(reap_work, cpu));
 +              cancel_rearming_delayed_work(&per_cpu(slab_reap_work, cpu));
                /* Now the cache_reaper is guaranteed to be not running. */
 -              per_cpu(reap_work, cpu).work.func = NULL;
 +              per_cpu(slab_reap_work, cpu).work.func = NULL;
                break;
        case CPU_DOWN_FAILED:
        case CPU_DOWN_FAILED_FROZEN:
@@@ -2275,9 -2261,11 +2275,11 @@@ kmem_cache_create (const char *name, si
        /*
         * Determine if the slab management is 'on' or 'off' slab.
         * (bootstrapping cannot cope with offslab caches so don't do
-        * it too early on.)
+        * it too early on. Always use on-slab management when
+        * SLAB_NOLEAKTRACE to avoid recursive calls into kmemleak)
         */
-       if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init)
+       if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init &&
+           !(flags & SLAB_NOLEAKTRACE))
                /*
                 * Size is large, assume best to place the slab management obj
                 * off-slab (should allow better packing of objs).
@@@ -2596,8 -2584,8 +2598,8 @@@ static struct slab *alloc_slabmgmt(stru
                 * kmemleak does not treat the ->s_mem pointer as a reference
                 * to the object. Otherwise we will not report the leak.
                 */
-               kmemleak_scan_area(slabp, offsetof(struct slab, list),
-                                  sizeof(struct list_head), local_flags);
+               kmemleak_scan_area(&slabp->list, sizeof(struct list_head),
+                                  local_flags);
                if (!slabp)
                        return NULL;
        } else {
@@@ -3117,19 -3105,13 +3119,19 @@@ static inline void *____cache_alloc(str
        } else {
                STATS_INC_ALLOCMISS(cachep);
                objp = cache_alloc_refill(cachep, flags);
 +              /*
 +               * the 'ac' may be updated by cache_alloc_refill(),
 +               * and kmemleak_erase() requires its correct value.
 +               */
 +              ac = cpu_cache_get(cachep);
        }
        /*
         * To avoid a false negative, if an object that is in one of the
         * per-CPU caches is leaked, we need to make sure kmemleak doesn't
         * treat the array pointers as a reference to the object.
         */
 -      kmemleak_erase(&ac->entry[ac->avail]);
 +      if (objp)
 +              kmemleak_erase(&ac->entry[ac->avail]);
        return objp;
  }
  
@@@ -3326,7 -3308,7 +3328,7 @@@ __cache_alloc_node(struct kmem_cache *c
        cache_alloc_debugcheck_before(cachep, flags);
        local_irq_save(save_flags);
  
 -      if (unlikely(nodeid == -1))
 +      if (nodeid == -1)
                nodeid = numa_node_id();
  
        if (unlikely(!cachep->nodelists[nodeid])) {
@@@ -3578,7 -3560,7 +3580,7 @@@ void *kmem_cache_alloc(struct kmem_cach
  }
  EXPORT_SYMBOL(kmem_cache_alloc);
  
 -#ifdef CONFIG_KMEMTRACE
 +#ifdef CONFIG_TRACING
  void *kmem_cache_alloc_notrace(struct kmem_cache *cachep, gfp_t flags)
  {
        return __cache_alloc(cachep, flags, __builtin_return_address(0));
@@@ -3641,7 -3623,7 +3643,7 @@@ void *kmem_cache_alloc_node(struct kmem
  }
  EXPORT_SYMBOL(kmem_cache_alloc_node);
  
 -#ifdef CONFIG_KMEMTRACE
 +#ifdef CONFIG_TRACING
  void *kmem_cache_alloc_node_notrace(struct kmem_cache *cachep,
                                    gfp_t flags,
                                    int nodeid)
@@@ -3669,7 -3651,7 +3671,7 @@@ __do_kmalloc_node(size_t size, gfp_t fl
        return ret;
  }
  
 -#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_KMEMTRACE)
 +#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_TRACING)
  void *__kmalloc_node(size_t size, gfp_t flags, int node)
  {
        return __do_kmalloc_node(size, flags, node,
@@@ -3689,7 -3671,7 +3691,7 @@@ void *__kmalloc_node(size_t size, gfp_
        return __do_kmalloc_node(size, flags, node, NULL);
  }
  EXPORT_SYMBOL(__kmalloc_node);
 -#endif /* CONFIG_DEBUG_SLAB */
 +#endif /* CONFIG_DEBUG_SLAB || CONFIG_TRACING */
  #endif /* CONFIG_NUMA */
  
  /**
@@@ -3721,7 -3703,7 +3723,7 @@@ static __always_inline void *__do_kmall
  }
  
  
 -#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_KMEMTRACE)
 +#if defined(CONFIG_DEBUG_SLAB) || defined(CONFIG_TRACING)
  void *__kmalloc(size_t size, gfp_t flags)
  {
        return __do_kmalloc(size, flags, __builtin_return_address(0));