1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.
  14  */
  15 
  16 #include <fm/fmd_api.h>
  17 #include <sys/note.h>
  18 #include <fm/libtopo.h>
  19 #include <sys/fm/protocol.h>
  20 #include <sys/fm/io/disk.h>
  21 #include <strings.h>
  22 
  23 typedef struct disk_sense_stat {
  24         fmd_stat_t bad_fmri;
  25         fmd_stat_t bad_scheme;
  26 } disk_sense_stat_t;
  27 
  28 disk_sense_stat_t disk_sense_stats = {
  29         { "bad_FMRI", FMD_TYPE_UINT64,
  30             "event FMRI is missing or invalid" },
  31         { "bad_scheme", FMD_TYPE_UINT64,
  32             "event does not contain a valid detector"},
  33 };
  34 
  35 static const fmd_prop_t fmd_props [] = {
  36         { "io_N", FMD_TYPE_INT32, "10" },
  37         { "io_T", FMD_TYPE_TIME, "10min"},
  38         { NULL, 0, NULL }
  39 };
  40 
  41 void
  42 disk_sense_close(fmd_hdl_t *hdl, fmd_case_t *c)
  43 {
  44         char *devid = fmd_case_getspecific(hdl, c);
  45         if (devid != NULL && (fmd_serd_exists(hdl, devid) == FMD_B_TRUE)) {
  46                 fmd_hdl_debug(hdl, "Destroying serd: %s", devid);
  47                 fmd_serd_destroy(hdl, devid);
  48         }
  49 }
  50 
  51 static void
  52 disk_sense_case_solve(fmd_hdl_t *hdl, const char *faultclass, fmd_case_t *c,
  53     char *devid, nvlist_t *detector)
  54 {
  55         fmd_hdl_topo_node_info_t *node;
  56         char faultname[PATH_MAX];
  57         nvlist_t *fault = NULL;
  58 
  59         (void) snprintf(faultname, sizeof (faultname),
  60             "fault.io.disk.%s", faultclass);
  61 
  62         if ((node = fmd_hdl_topo_node_get_by_devid(hdl, devid)) == NULL) {
  63                 fault = fmd_nvl_create_fault(hdl, faultname, 100,
  64                     detector, NULL, NULL);
  65         } else {
  66                 fault = fmd_nvl_create_fault(hdl, faultname, 100,
  67                     detector, node->fru, node->resource);
  68                 nvlist_free(node->fru);
  69                 nvlist_free(node->resource);
  70                 fmd_hdl_free(hdl, node, sizeof (fmd_hdl_topo_node_info_t));
  71         }
  72 
  73         fmd_case_add_suspect(hdl, c, fault);
  74         fmd_case_setspecific(hdl, c, devid);
  75         fmd_case_solve(hdl, c);
  76 }
  77 
  78 void
  79 disk_sense_recv(fmd_hdl_t *hdl, fmd_event_t *event, nvlist_t *nvl,
  80     const char *class)
  81 {
  82         nvlist_t *detector = NULL;
  83         char *devid = NULL;
  84         uint8_t key = 0;
  85         uint8_t asc = 0;
  86         uint8_t ascq = 0;
  87         _NOTE(ARGUNUSED(class));
  88 
  89         if (nvlist_lookup_nvlist(nvl, "detector", &detector) != 0) {
  90                 disk_sense_stats.bad_scheme.fmds_value.ui64++;
  91                 return;
  92         }
  93 
  94         if (nvlist_lookup_string(detector, "devid", &devid) != 0) {
  95                 disk_sense_stats.bad_fmri.fmds_value.ui64++;
  96                 return;
  97         }
  98 
  99         if (nvlist_lookup_uint8(nvl, "key", &key) != 0) {
 100                 disk_sense_stats.bad_fmri.fmds_value.ui64++;
 101                 fmd_hdl_debug(hdl, "Invalid ereport payload");
 102                 return;
 103         }
 104 
 105         (void) nvlist_lookup_uint8(nvl, "asc", &asc);
 106         (void) nvlist_lookup_uint8(nvl, "ascq", &ascq);
 107 
 108         if (key == 0x1 && asc == 0xb && ascq == 0x1) {
 109                 /* over temp reported by drive sense data */
 110                 fmd_case_t *c = fmd_case_open(hdl, NULL);
 111                 fmd_case_add_ereport(hdl, c, event);
 112                 disk_sense_case_solve(hdl, FM_FAULT_DISK_OVERTEMP, c,
 113                     devid, detector);
 114                 fmd_hdl_debug(hdl, "Disk sense data reports drive over-temp");
 115                 return;
 116         }
 117 }
 118 
 119 static const fmd_hdl_ops_t fmd_ops = {
 120         disk_sense_recv,
 121         NULL,
 122         disk_sense_close,
 123         NULL,
 124         NULL,
 125 };
 126 
 127 static const fmd_hdl_info_t fmd_info = {
 128         "disk-sense-de", "0.5", &fmd_ops, fmd_props
 129 };
 130 
 131 void
 132 _fmd_init(fmd_hdl_t *hdl)
 133 {
 134         if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) {
 135                 fmd_hdl_debug(hdl, "Internal error\n");
 136                 return;
 137         }
 138 
 139         (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
 140             sizeof (disk_sense_stats) / sizeof (fmd_stat_t),
 141             (fmd_stat_t *)&disk_sense_stats);
 142 }
 143 
 144 void
 145 _fmd_fini(fmd_hdl_t *hdl)
 146 {
 147         _NOTE(ARGUNUSED(hdl));
 148 }