Print this page
    
NEX-9654 sd(7D) leaks memory when posting ereports
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/io/scsi/impl/scsi_fm.c
          +++ new/usr/src/uts/common/io/scsi/impl/scsi_fm.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  
    | 
      ↓ open down ↓ | 
    10 lines elided | 
    
      ↑ open up ↑ | 
  
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
       21 +
  21   22  /*
  22   23   * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2017 Nexenta Systems, Inc.
  23   25   */
  24   26  
  25   27  /*
  26   28   * SCSI FMA implementation
  27   29   */
  28   30  
  29   31  #include <sys/scsi/scsi_types.h>
  30   32  #include <sys/sunmdi.h>
  31   33  #include <sys/va_list.h>
  32   34  
  33   35  #include <sys/ddi_impldefs.h>
  34   36  
  35   37  /* consolidation private interface to generate dev scheme ereport */
  36   38  extern void fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip,
  37   39      const char *devpath, const char *minor_name, const char *devid,
  38   40      const char *tpl0, const char *error_class, uint64_t ena, int sflag,
  39   41      nvlist_t *, va_list ap);
  40   42  extern char *mdi_pi_pathname_by_instance(int);
  41   43  
  42   44  #define FM_SCSI_CLASS   "scsi"
  43   45  #define ERPT_CLASS_SZ   sizeof (FM_SCSI_CLASS) + 1 + DDI_MAX_ERPT_CLASS + 1
  44   46  
  45   47  /*
  46   48   * scsi_fm_init: Initialize fma capabilities and register with IO
  47   49   * fault services.
  48   50   */
  49   51  void
  50   52  scsi_fm_init(struct scsi_device *sd)
  51   53  {
  52   54          dev_info_t      *dip = sd->sd_dev;
  53   55  
  54   56          /*
  55   57           * fm-capable in driver.conf can be used to set fm_capabilities.
  56   58           * If fm-capable is not defined, then the last argument passed to
  57   59           * ddi_prop_get_int will be returned as the capabilities.
  58   60           *
  59   61           * NOTE: by default scsi_fm_capable sets DDI_FM_EREPORT_CAPABLE.
  60   62           */
  61   63          sd->sd_fm_capable = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
  62   64              DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "fm-capable",
  63   65              scsi_fm_capable);
  64   66  
  65   67          /*
  66   68           * Register capabilities with IO Fault Services. The capabilities
  67   69           * set above may not be supported by the parent nexus, in that
  68   70           * case some/all capability bits may be cleared.
  69   71           *
  70   72           * NOTE: iblock cookies are not important because scsi HBAs
  71   73           * always interrupt below LOCK_LEVEL.
  72   74           */
  73   75          if (sd->sd_fm_capable != DDI_FM_NOT_CAPABLE)
  74   76                  ddi_fm_init(dip, &sd->sd_fm_capable, NULL);
  75   77  }
  76   78  
  77   79  /*
  78   80   * scsi_fm_fini: un-register with IO fault services.
  79   81   */
  80   82  void
  81   83  scsi_fm_fini(struct scsi_device *sd)
  82   84  {
  83   85          dev_info_t      *dip = sd->sd_dev;
  84   86  
  85   87          if (sd->sd_fm_capable != DDI_FM_NOT_CAPABLE)
  86   88                  ddi_fm_fini(dip);
  87   89  }
  88   90  
  89   91  /*
  90   92   *
  91   93   * scsi_fm_ereport_post - Post an ereport
  
    | 
      ↓ open down ↓ | 
    59 lines elided | 
    
      ↑ open up ↑ | 
  
  92   94   */
  93   95  void
  94   96  scsi_fm_ereport_post(struct scsi_device *sd, int path_instance,
  95   97      char *devpath, const char *error_class, uint64_t ena,
  96   98      char *devid, char *tpl0, int sflag, nvlist_t *pl, ...)
  97   99  {
  98  100          char            class[ERPT_CLASS_SZ];
  99  101          dev_info_t      *dip = sd->sd_dev;
 100  102          dev_info_t      *eqdip = dip;
 101  103          char            *minor_name;
      104 +        nvlist_t        *nvl;
      105 +        char            *prop;
 102  106          va_list         ap;
 103  107  
 104  108          /*
 105  109           * If the scsi_device eqdip is not yet ereport capable, send the
 106  110           * report based on parent capabilities.  This is needed for
 107  111           * telemetry during enumeration.
 108  112           */
 109  113          if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip)))
 110  114                  eqdip = ddi_get_parent(eqdip);
 111  115  
 112  116          /* Add "scsi." as a prefix to the class */
 113  117          (void) snprintf(class, ERPT_CLASS_SZ, "%s.%s",
 114  118              FM_SCSI_CLASS, error_class);
 115  119  
 116  120          /*
 117  121           * Get the path:
 118  122           *
 119  123           * If path_instance is non-zero then the packet was
 120  124           * sent to scsi_vhci. We return the pathinfo path_string associated
 121  125           * with the path_instance path - which refers to the actual hardware.
 122  126           *
 123  127           * If path_instance is zero then use the devpath provided by the
 124  128           * caller;  if it was NULL then this will cause fm_dev_ereport_post
 125  129           * to use the devinfo path of the first devi we pass to it, ie
 126  130           * sd->sd_dev.
 127  131           */
 128  132          if (path_instance)
 129  133                  devpath = mdi_pi_pathname_by_instance(path_instance);
 130  134  
 131  135          /*
  
    | 
      ↓ open down ↓ | 
    20 lines elided | 
    
      ↑ open up ↑ | 
  
 132  136           * Set the minor_name to NULL. The block location of a media error
 133  137           * is described by the 'lba' property. We use the 'lba' instead of
 134  138           * the partition (minor_name) because the defect stays in the same
 135  139           * place even when a repartition operation may result in the defect
 136  140           * showing up in a different partition (minor_name). To support
 137  141           * retire at the block/partition level, the user level retire agent
 138  142           * should map the 'lba' to the current effected partition.
 139  143           */
 140  144          minor_name = NULL;
 141  145  
      146 +        /* Create nvlist containing inquiry properties (if available) */
      147 +        if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, (sflag & DDI_NOSLEEP) ?
      148 +            KM_NOSLEEP : KM_SLEEP) != 0)
      149 +                return;
      150 +        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
      151 +            INQUIRY_VENDOR_ID, &prop) == DDI_PROP_SUCCESS) {
      152 +                (void) nvlist_add_string(nvl, "vendor", prop);
      153 +                ddi_prop_free(prop);
      154 +        }
      155 +        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
      156 +            INQUIRY_PRODUCT_ID, &prop) == DDI_PROP_SUCCESS) {
      157 +                (void) nvlist_add_string(nvl, "product", prop);
      158 +                ddi_prop_free(prop);
      159 +        }
      160 +        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
      161 +            INQUIRY_REVISION_ID, &prop) == DDI_PROP_SUCCESS) {
      162 +                (void) nvlist_add_string(nvl, "revision", prop);
      163 +                ddi_prop_free(prop);
      164 +        }
      165 +        if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
      166 +            INQUIRY_SERIAL_NO, &prop) == DDI_PROP_SUCCESS) {
      167 +                (void) nvlist_add_string(nvl, "serial", prop);
      168 +                ddi_prop_free(prop);
      169 +        }
      170 +        /* Merge the provided payload if any */
      171 +        if (pl != NULL)
      172 +                (void) nvlist_merge(nvl, pl, 0);
      173 +
 142  174          /*
 143  175           * NOTE: If there is a 'linked' ena to be had, it should likely come
 144  176           * from the buf structure via the scsi_pkt pkt->pkt_bp.
 145  177           */
 146  178  
 147  179          /* Post the ereport */
 148  180          va_start(ap, pl);
 149  181          fm_dev_ereport_postv(dip, eqdip, devpath, minor_name, devid, tpl0,
 150      -            class, ena, sflag, pl, ap);
      182 +            class, ena, sflag, nvl, ap);
 151  183          va_end(ap);
      184 +
      185 +        nvlist_free(nvl);
 152  186  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX