summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/scsi/impl/scsi_fm.c
blob: 1ae635b48254f4ea208d2afa994b08d8ca3f7079 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * SCSI FMA implementation
 */

#include <sys/scsi/scsi_types.h>
#include <sys/sunmdi.h>
#include <sys/va_list.h>

#include <sys/ddi_impldefs.h>

/* consolidation private interface to generate dev scheme ereport */
extern void fm_dev_ereport_postv(dev_info_t *dip, dev_info_t *eqdip,
    const char *devpath, const char *minor_name, const char *devid,
    const char *tpl0, const char *error_class, uint64_t ena, int sflag,
    nvlist_t *, va_list ap);
extern char *mdi_pi_pathname_by_instance(int);

#define	FM_SCSI_CLASS	"scsi"
#define	ERPT_CLASS_SZ   sizeof (FM_SCSI_CLASS) + 1 + DDI_MAX_ERPT_CLASS + 1

/*
 * scsi_fm_init: Initialize fma capabilities and register with IO
 * fault services.
 */
void
scsi_fm_init(struct scsi_device *sd)
{
	dev_info_t	*dip = sd->sd_dev;

	/*
	 * fm-capable in driver.conf can be used to set fm_capabilities.
	 * If fm-capable is not defined, then the last argument passed to
	 * ddi_prop_get_int will be returned as the capabilities.
	 *
	 * NOTE: by default scsi_fm_capable sets DDI_FM_EREPORT_CAPABLE.
	 */
	sd->sd_fm_capable = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "fm-capable",
	    scsi_fm_capable);

	/*
	 * Register capabilities with IO Fault Services. The capabilities
	 * set above may not be supported by the parent nexus, in that
	 * case some/all capability bits may be cleared.
	 *
	 * NOTE: iblock cookies are not important because scsi HBAs
	 * always interrupt below LOCK_LEVEL.
	 */
	if (sd->sd_fm_capable != DDI_FM_NOT_CAPABLE)
		ddi_fm_init(dip, &sd->sd_fm_capable, NULL);
}

/*
 * scsi_fm_fini: un-register with IO fault services.
 */
void
scsi_fm_fini(struct scsi_device *sd)
{
	dev_info_t	*dip = sd->sd_dev;

	if (sd->sd_fm_capable != DDI_FM_NOT_CAPABLE)
		ddi_fm_fini(dip);
}

/*
 *
 * scsi_fm_ereport_post - Post an ereport
 */
void
scsi_fm_ereport_post(struct scsi_device *sd, int path_instance,
    char *devpath, const char *error_class, uint64_t ena,
    char *devid, char *tpl0, int sflag, nvlist_t *pl, ...)
{
	char		class[ERPT_CLASS_SZ];
	dev_info_t	*dip = sd->sd_dev;
	dev_info_t	*eqdip = dip;
	char		*minor_name;
	va_list		ap;

	/*
	 * If the scsi_device eqdip is not yet ereport capable, send the
	 * report based on parent capabilities.  This is needed for
	 * telemetry during enumeration.
	 */
	if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(eqdip)))
		eqdip = ddi_get_parent(eqdip);

	/* Add "scsi." as a prefix to the class */
	(void) snprintf(class, ERPT_CLASS_SZ, "%s.%s",
	    FM_SCSI_CLASS, error_class);

	/*
	 * Get the path:
	 *
	 * If path_instance is non-zero then the packet was
	 * sent to scsi_vhci. We return the pathinfo path_string associated
	 * with the path_instance path - which refers to the actual hardware.
	 *
	 * If path_instance is zero then use the devpath provided by the
	 * caller;  if it was NULL then this will cause fm_dev_ereport_post
	 * to use the devinfo path of the first devi we pass to it, ie
	 * sd->sd_dev.
	 */
	if (path_instance)
		devpath = mdi_pi_pathname_by_instance(path_instance);

	/*
	 * Set the minor_name to NULL. The block location of a media error
	 * is described by the 'lba' property. We use the 'lba' instead of
	 * the partition (minor_name) because the defect stays in the same
	 * place even when a repartition operation may result in the defect
	 * showing up in a different partition (minor_name). To support
	 * retire at the block/partition level, the user level retire agent
	 * should map the 'lba' to the current effected partition.
	 */
	minor_name = NULL;

	/*
	 * NOTE: If there is a 'linked' ena to be had, it should likely come
	 * from the buf structure via the scsi_pkt pkt->pkt_bp.
	 */

	/* Post the ereport */
	va_start(ap, pl);
	fm_dev_ereport_postv(dip, eqdip, devpath, minor_name, devid, tpl0,
	    class, ena, sflag, pl, ap);
	va_end(ap);
}