summaryrefslogtreecommitdiff
path: root/libcheckers/emc_clariion.c
blob: 6c7167e70253b6a617b6c65508250039ae9b3ae6 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
 * Copyright (c) 2004, 2005 Lars Marowsky-Bree
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>

#include "../libmultipath/sg_include.h"
#include "libsg.h"
#include "checkers.h"

#define INQUIRY_CMD     0x12
#define INQUIRY_CMDLEN  6
#define HEAVY_CHECK_COUNT       10

/*
 * Mechanism to track CLARiiON inactive snapshot LUs.
 * This is done so that we can fail passive paths
 * to an inactive snapshot LU even though since a
 * simple read test would return 02/04/03 instead
 * of 05/25/01 sensekey/ASC/ASCQ data.
 */
#define	IS_INACTIVE_SNAP(c)   (c->mpcontext ?				   \
			       ((struct emc_clariion_checker_LU_context *) \
					(*c->mpcontext))->inactive_snap	   \
					    : 0)

#define	SET_INACTIVE_SNAP(c)  if (c->mpcontext)				   \
				((struct emc_clariion_checker_LU_context *)\
					(*c->mpcontext))->inactive_snap = 1

#define	CLR_INACTIVE_SNAP(c)  if (c->mpcontext)				   \
				((struct emc_clariion_checker_LU_context *)\
					(*c->mpcontext))->inactive_snap = 0

struct emc_clariion_checker_path_context {
	char wwn[16];
	unsigned wwn_set;
};

struct emc_clariion_checker_LU_context {
	int inactive_snap;
};

extern void
hexadecimal_to_ascii(char * wwn, char *wwnstr)
{
	int i,j, nbl;

	for (i=0,j=0;i<16;i++) {
		wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ?
					'0' + nbl : 'a' + (nbl - 10);
		wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ?
					'0' + nbl : 'a' + (nbl - 10);
	}
	wwnstr[32]=0;
}

int emc_clariion_init (struct checker * c)
{
	/*
	 * Allocate and initialize the path specific context.
	 */
	c->context = malloc(sizeof(struct emc_clariion_checker_path_context));
	if (!c->context)
		return 1;
	((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0;

	/*
	 * Allocate and initialize the multi-path global context.
	 */
	if (c->mpcontext) {
		void * mpctxt = malloc(sizeof(int));
		*c->mpcontext = mpctxt;
		CLR_INACTIVE_SNAP(c);
	}

	return 0;
}

void emc_clariion_free (struct checker * c)
{
	free(c->context);
}

int emc_clariion(struct checker * c)
{
	unsigned char sense_buffer[128] = { 0, };
	unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb;
	unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0,
						sizeof(sense_buffer), 0};
	struct sg_io_hdr io_hdr;
	struct emc_clariion_checker_path_context * ct =
		(struct emc_clariion_checker_path_context *)c->context;
	char wwnstr[33];
	int ret;

	memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
	io_hdr.interface_id = 'S';
	io_hdr.cmd_len = sizeof (inqCmdBlk);
	io_hdr.mx_sb_len = sizeof (sb);
	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
	io_hdr.dxfer_len = sizeof (sense_buffer);
	io_hdr.dxferp = sense_buffer;
	io_hdr.cmdp = inqCmdBlk;
	io_hdr.sbp = sb;
	io_hdr.timeout = DEF_TIMEOUT;
	io_hdr.pack_id = 0;
	if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
		MSG(c, "emc_clariion_checker: sending query command failed");
		return PATH_DOWN;
	}
	if (io_hdr.info & SG_INFO_OK_MASK) {
		MSG(c, "emc_clariion_checker: query command indicates error");
		return PATH_DOWN;
	}
	if (/* Verify the code page - right page & revision */
	    sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) {
		MSG(c, "emc_clariion_checker: Path unit report page in "
		    "unknown format");
		return PATH_DOWN;
	}

	if ( /* Effective initiator type */
	    	sense_buffer[27] != 0x03
		/*
		 * Failover mode should be set to 1 (PNR failover mode)
		 * or 4 (ALUA failover mode).
		 */
		|| (((sense_buffer[28] & 0x07) != 0x04) &&
		    ((sense_buffer[28] & 0x07) != 0x06))
		/* Arraycommpath should be set to 1 */
		|| (sense_buffer[30] & 0x04) != 0x04) {
		MSG(c, "emc_clariion_checker: Path not correctly configured "
		    "for failover");
		return PATH_DOWN;
	}

	if ( /* LUN operations should indicate normal operations */
		sense_buffer[48] != 0x00) {
		MSG(c, "emc_clariion_checker: Path not available for normal "
		    "operations");
		return PATH_SHAKY;
	}

	if ( /* LUN should at least be bound somewhere and not be LUNZ */
		sense_buffer[4] == 0x00) {
		MSG(c, "emc_clariion_checker: Logical Unit is unbound "
		    "or LUNZ");
		return PATH_DOWN;
	}
	
	/*
	 * store the LUN WWN there and compare that it indeed did not
	 * change in between, to protect against the path suddenly
	 * pointing somewhere else.
	 */
	if (ct->wwn_set) {
		if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) {
			MSG(c, "emc_clariion_checker: Logical Unit WWN "
			    "has changed!");
			return PATH_DOWN;
		}
	} else {
		memcpy(ct->wwn, &sense_buffer[10], 16);
		ct->wwn_set = 1;
	}
	
	/*
	 * Issue read on active path to determine if inactive snapshot.
	 */
	if (sense_buffer[4] == 2) {/* if active path */
		unsigned char buf[4096];

		ret = sg_read(c->fd, &buf[0], sbb = &sb[0]);
		if (ret == PATH_DOWN) {
			hexadecimal_to_ascii(ct->wwn, wwnstr);

			/*
		 	 * Check for inactive snapshot LU this way.  Must
			 * fail these.
	 	 	 */
			if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) &&
			    (sbb[13]==1)) {
				/*
			 	 * Do this so that we can fail even the
			 	 * passive paths which will return
				 * 02/04/03 not 05/25/01 on read.
			 	 */
				SET_INACTIVE_SNAP(c);
				MSG(c, "emc_clariion_checker: Active "
					"path to inactive snapshot WWN %s.",
					wwnstr);
			} else
				MSG(c, "emc_clariion_checker: Read "
					"error for WWN %s.  Sense data are "
					"0x%x/0x%x/0x%x.", wwnstr,
					sbb[2]&0xf, sbb[12], sbb[13]);
		} else {
			MSG(c, "emc_clariion_checker: Active path is "
			    "healthy.");
			/*
		 	 * Remove the path from the set of paths to inactive
		 	 * snapshot LUs if it was in this list since the
		 	 * snapshot is no longer inactive.
		 	 */
			CLR_INACTIVE_SNAP(c);
		}
	} else {
		if (IS_INACTIVE_SNAP(c)) {
			hexadecimal_to_ascii(ct->wwn, wwnstr);
			MSG(c, "emc_clariion_checker: Passive "
				"path to inactive snapshot WWN %s.",
				wwnstr);
			ret = PATH_DOWN;
		} else {
			MSG(c,
		    	    "emc_clariion_checker: Passive path is healthy.");
			ret = PATH_UP;	/* not ghost */
		}
	}

	return ret;
}