Print this page
9525 kmem_dump_size is a corrupting influence
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Dan McDonald <danmcd@joyent.com>
        
*** 22,32 ****
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  /*
!  * Copyright 2011 Joyent, Inc.  All rights reserved.
   * Copyright (c) 2012 by Delphix. All rights reserved.
   */
  
  #include <mdb/mdb_param.h>
  #include <mdb/mdb_modapi.h>
--- 22,32 ----
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  /*
!  * Copyright 2018 Joyent, Inc.  All rights reserved.
   * Copyright (c) 2012 by Delphix. All rights reserved.
   */
  
  #include <mdb/mdb_param.h>
  #include <mdb/mdb_modapi.h>
*** 3010,3020 ****
  
  typedef struct kmem_verify {
          uint64_t *kmv_buf;              /* buffer to read cache contents into */
          size_t kmv_size;                /* number of bytes in kmv_buf */
          int kmv_corruption;             /* > 0 if corruption found. */
!         int kmv_besilent;               /* report actual corruption sites */
          struct kmem_cache kmv_cache;    /* the cache we're operating on */
  } kmem_verify_t;
  
  /*
   * verify_pattern()
--- 3010,3020 ----
  
  typedef struct kmem_verify {
          uint64_t *kmv_buf;              /* buffer to read cache contents into */
          size_t kmv_size;                /* number of bytes in kmv_buf */
          int kmv_corruption;             /* > 0 if corruption found. */
!         uint_t kmv_flags;               /* dcmd flags */
          struct kmem_cache kmv_cache;    /* the cache we're operating on */
  } kmem_verify_t;
  
  /*
   * verify_pattern()
*** 3055,3065 ****
          kmem_verify_t *kmv = (kmem_verify_t *)private;
          uint64_t *buf = kmv->kmv_buf;   /* buf to validate */
          int64_t corrupt;                /* corruption offset */
          kmem_buftag_t *buftagp;         /* ptr to buftag */
          kmem_cache_t *cp = &kmv->kmv_cache;
!         int besilent = kmv->kmv_besilent;
  
          /*LINTED*/
          buftagp = KMEM_BUFTAG(cp, buf);
  
          /*
--- 3055,3065 ----
          kmem_verify_t *kmv = (kmem_verify_t *)private;
          uint64_t *buf = kmv->kmv_buf;   /* buf to validate */
          int64_t corrupt;                /* corruption offset */
          kmem_buftag_t *buftagp;         /* ptr to buftag */
          kmem_cache_t *cp = &kmv->kmv_cache;
!         boolean_t besilent = !!(kmv->kmv_flags & (DCMD_LOOP | DCMD_PIPE_OUT));
  
          /*LINTED*/
          buftagp = KMEM_BUFTAG(cp, buf);
  
          /*
*** 3101,3110 ****
--- 3101,3112 ----
                  goto corrupt;
          }
  
          return (WALK_NEXT);
  corrupt:
+         if (kmv->kmv_flags & DCMD_PIPE_OUT)
+                 mdb_printf("%p\n", addr);
          kmv->kmv_corruption++;
          return (WALK_NEXT);
  }
  
  /*
*** 3122,3132 ****
          /*LINTED*/
          kmem_buftag_t *buftagp = KMEM_BUFTAG(cp, buf);
          uint32_t *ip = (uint32_t *)buftagp;
          uint8_t *bp = (uint8_t *)buf;
          int looks_ok = 0, size_ok = 1;  /* flags for finding corruption */
!         int besilent = kmv->kmv_besilent;
  
          /*
           * Read the buffer to check.
           */
          if (mdb_vread(buf, kmv->kmv_size, addr) == -1) {
--- 3124,3134 ----
          /*LINTED*/
          kmem_buftag_t *buftagp = KMEM_BUFTAG(cp, buf);
          uint32_t *ip = (uint32_t *)buftagp;
          uint8_t *bp = (uint8_t *)buf;
          int looks_ok = 0, size_ok = 1;  /* flags for finding corruption */
!         boolean_t besilent = !!(kmv->kmv_flags & (DCMD_LOOP | DCMD_PIPE_OUT));
  
          /*
           * Read the buffer to check.
           */
          if (mdb_vread(buf, kmv->kmv_size, addr) == -1) {
*** 3180,3189 ****
--- 3182,3194 ----
                  goto corrupt;
          }
  
          return (WALK_NEXT);
  corrupt:
+         if (kmv->kmv_flags & DCMD_PIPE_OUT)
+                 mdb_printf("%p\n", addr);
+ 
          kmv->kmv_corruption++;
          return (WALK_NEXT);
  }
  
  /*ARGSUSED2*/
*** 3198,3211 ****
--- 3203,3224 ----
                      addr) == -1) {
                          mdb_warn("couldn't read kmem_cache %p", addr);
                          return (DCMD_ERR);
                  }
  
+                 if ((kmv.kmv_cache.cache_dump.kd_unsafe ||
+                     kmv.kmv_cache.cache_dump.kd_alloc_fails) &&
+                     !(flags & (DCMD_LOOP | DCMD_PIPE_OUT))) {
+                         mdb_warn("WARNING: cache was used during dump: "
+                             "corruption may be incorrectly reported\n");
+                 }
+ 
                  kmv.kmv_size = kmv.kmv_cache.cache_buftag +
                      sizeof (kmem_buftag_t);
                  kmv.kmv_buf = mdb_alloc(kmv.kmv_size, UM_SLEEP | UM_GC);
                  kmv.kmv_corruption = 0;
+                 kmv.kmv_flags = flags;
  
                  if ((kmv.kmv_cache.cache_flags & KMF_REDZONE)) {
                          check_alloc = 1;
                          if (kmv.kmv_cache.cache_flags & KMF_DEADBEEF)
                                  check_free = 1;
*** 3216,3276 ****
                                      kmv.kmv_cache.cache_name);
                          }
                          return (DCMD_ERR);
                  }
  
!                 if (flags & DCMD_LOOP) {
!                         /*
!                          * table mode, don't print out every corrupt buffer
!                          */
!                         kmv.kmv_besilent = 1;
!                 } else {
                          mdb_printf("Summary for cache '%s'\n",
                              kmv.kmv_cache.cache_name);
                          mdb_inc_indent(2);
-                         kmv.kmv_besilent = 0;
                  }
  
                  if (check_alloc)
                          (void) mdb_pwalk("kmem", verify_alloc, &kmv, addr);
                  if (check_free)
                          (void) mdb_pwalk("freemem", verify_free, &kmv, addr);
  
                  if (flags & DCMD_LOOP) {
                          if (kmv.kmv_corruption == 0) {
                                  mdb_printf("%-*s %?p clean\n",
                                      KMEM_CACHE_NAMELEN,
                                      kmv.kmv_cache.cache_name, addr);
                          } else {
!                                 char *s = "";   /* optional s in "buffer[s]" */
!                                 if (kmv.kmv_corruption > 1)
!                                         s = "s";
! 
!                                 mdb_printf("%-*s %?p %d corrupt buffer%s\n",
!                                     KMEM_CACHE_NAMELEN,
                                      kmv.kmv_cache.cache_name, addr,
!                                     kmv.kmv_corruption, s);
                          }
                  } else {
                          /*
!                          * This is the more verbose mode, when the user has
!                          * type addr::kmem_verify.  If the cache was clean,
!                          * nothing will have yet been printed. So say something.
                           */
                          if (kmv.kmv_corruption == 0)
                                  mdb_printf("clean\n");
  
                          mdb_dec_indent(2);
                  }
          } else {
                  /*
                   * If the user didn't specify a cache to verify, we'll walk all
                   * kmem_cache's, specifying ourself as a callback for each...
                   * this is the equivalent of '::walk kmem_cache .::kmem_verify'
                   */
!                 mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", KMEM_CACHE_NAMELEN,
!                     "Cache Name", "Addr", "Cache Integrity");
                  (void) (mdb_walk_dcmd("kmem_cache", "kmem_verify", 0, NULL));
          }
  
          return (DCMD_OK);
  }
--- 3229,3298 ----
                                      kmv.kmv_cache.cache_name);
                          }
                          return (DCMD_ERR);
                  }
  
!                 if (!(flags & (DCMD_LOOP | DCMD_PIPE_OUT))) {
                          mdb_printf("Summary for cache '%s'\n",
                              kmv.kmv_cache.cache_name);
                          mdb_inc_indent(2);
                  }
  
                  if (check_alloc)
                          (void) mdb_pwalk("kmem", verify_alloc, &kmv, addr);
                  if (check_free)
                          (void) mdb_pwalk("freemem", verify_free, &kmv, addr);
  
+                 if (!(flags & DCMD_PIPE_OUT)) {
                          if (flags & DCMD_LOOP) {
                                  if (kmv.kmv_corruption == 0) {
                                          mdb_printf("%-*s %?p clean\n",
                                              KMEM_CACHE_NAMELEN,
                                              kmv.kmv_cache.cache_name, addr);
                                  } else {
!                                         mdb_printf("%-*s %?p %d corrupt "
!                                             "buffer%s\n", KMEM_CACHE_NAMELEN,
                                              kmv.kmv_cache.cache_name, addr,
!                                             kmv.kmv_corruption,
!                                             kmv.kmv_corruption > 1 ? "s" : "");
                                  }
                          } else {
                                  /*
!                                  * This is the more verbose mode, when the user
!                                  * typed addr::kmem_verify.  If the cache was
!                                  * clean, nothing will have yet been printed. So
!                                  * say something.
                                   */
                                  if (kmv.kmv_corruption == 0)
                                          mdb_printf("clean\n");
  
                                  mdb_dec_indent(2);
                          }
+                 }
          } else {
                  /*
                   * If the user didn't specify a cache to verify, we'll walk all
                   * kmem_cache's, specifying ourself as a callback for each...
                   * this is the equivalent of '::walk kmem_cache .::kmem_verify'
                   */
! 
!                 if (!(flags & DCMD_PIPE_OUT)) {
!                         uintptr_t dump_curr;
!                         uintptr_t dump_end;
! 
!                         if (mdb_readvar(&dump_curr, "kmem_dump_curr") != -1 &&
!                             mdb_readvar(&dump_end, "kmem_dump_end") != -1 &&
!                             dump_curr == dump_end) {
!                                 mdb_warn("WARNING: exceeded kmem_dump_size; "
!                                     "corruption may be incorrectly reported\n");
!                         }
! 
!                         mdb_printf("%<u>%-*s %-?s %-20s%</b>\n",
!                             KMEM_CACHE_NAMELEN, "Cache Name", "Addr",
!                             "Cache Integrity");
!                 }
! 
                  (void) (mdb_walk_dcmd("kmem_cache", "kmem_verify", 0, NULL));
          }
  
          return (DCMD_OK);
  }