1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <fcntl.h>
29#include <libdevinfo.h>
30#include <stdlib.h>
31#include <sys/sunddi.h>
32#include <sys/types.h>
33
34#include "libdiskmgt.h"
35#include "disks_private.h"
36
37static int		add_path_state(descriptor_t *dp, nvlist_t *attrs);
38static int		add_wwn(descriptor_t *dp, nvlist_t *attrs);
39static descriptor_t	**get_assoc_drives(descriptor_t *desc, int *errp);
40static descriptor_t	**get_assoc_controllers(descriptor_t *desc, int *errp);
41static char		*path_state_name(di_path_state_t st);
42
43descriptor_t **
44path_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, int *errp)
45{
46	switch (type) {
47	case DM_DRIVE:
48	    return (get_assoc_drives(desc, errp));
49	case DM_CONTROLLER:
50	    return (get_assoc_controllers(desc, errp));
51	}
52
53	*errp = EINVAL;
54	return (NULL);
55}
56
57nvlist_t *
58path_get_attributes(descriptor_t *dp, int *errp)
59{
60	path_t		*pp;
61	nvlist_t	*attrs = NULL;
62
63	pp = dp->p.path;
64
65	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
66	    *errp = ENOMEM;
67	    return (NULL);
68	}
69
70	if (nvlist_add_string(attrs, DM_CTYPE, pp->ctype) != 0) {
71	    nvlist_free(attrs);
72	    *errp = ENOMEM;
73	    return (NULL);
74	}
75
76	/*
77	 * We add the path state and wwn attributes only for descriptors that
78	 * we got via their association to a specific drive (since these
79	 * attributes are drive specific).
80	 */
81	if (dp->name != NULL) {
82	    if (add_path_state(dp, attrs) != 0) {
83		nvlist_free(attrs);
84		*errp = ENOMEM;
85		return (NULL);
86	    }
87	    if (add_wwn(dp, attrs) != 0) {
88		nvlist_free(attrs);
89		*errp = ENOMEM;
90		return (NULL);
91	    }
92	}
93
94	*errp = 0;
95	return (attrs);
96}
97
98descriptor_t *
99path_get_descriptor_by_name(char *name, int *errp)
100{
101	descriptor_t	**paths;
102	int		i;
103	descriptor_t	*path = NULL;
104
105	paths = cache_get_descriptors(DM_PATH, errp);
106	if (*errp != 0) {
107	    return (NULL);
108	}
109
110	for (i = 0; paths[i]; i++) {
111	    if (libdiskmgt_str_eq(name, paths[i]->p.path->name)) {
112		path = paths[i];
113	    } else {
114		/* clean up the unused descriptors */
115		cache_free_descriptor(paths[i]);
116	    }
117	}
118	free(paths);
119
120	if (path == NULL) {
121	    *errp = ENODEV;
122	}
123
124	return (path);
125}
126
127/* ARGSUSED */
128descriptor_t **
129path_get_descriptors(int filter[], int *errp)
130{
131	return (cache_get_descriptors(DM_PATH, errp));
132}
133
134char *
135path_get_name(descriptor_t *desc)
136{
137	return (desc->p.path->name);
138}
139
140/* ARGSUSED */
141nvlist_t *
142path_get_stats(descriptor_t *dp, int stat_type, int *errp)
143{
144	/* There are no stat types defined for paths */
145	*errp = EINVAL;
146	return (NULL);
147}
148
149int
150path_make_descriptors()
151{
152	int		error;
153	controller_t	*cp;
154
155	cp = cache_get_controllerlist();
156	while (cp != NULL) {
157	    if (cp->paths != NULL) {
158		int i;
159
160		for (i = 0; cp->paths[i]; i++) {
161		    cache_load_desc(DM_PATH, cp->paths[i], NULL, NULL, &error);
162		    if (error != 0) {
163			return (error);
164		    }
165		}
166	    }
167	    cp = cp->next;
168	}
169
170	return (0);
171}
172
173/*
174 * This is called when we have a name in the descriptor name field.  That
175 * only will be the case when the descriptor was created by getting the
176 * association from a drive to the path.  Since we filled in the name with
177 * the drive device id in that case, we can use the device id to look up the
178 * drive-path state.
179 */
180static int
181add_path_state(descriptor_t *dp, nvlist_t *attrs)
182{
183	ddi_devid_t	devid;
184	path_t		*pp;
185	int		i;
186	int		status = 0;
187
188	if (devid_str_decode(dp->name, &devid, NULL) != 0) {
189	    return (0);
190	}
191
192	/* find the index of the correct drive assoc. */
193	pp = dp->p.path;
194	for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) {
195	    if (pp->disks[i]->devid != NULL &&
196		devid_compare(pp->disks[i]->devid, devid) == 0) {
197
198		/* add the corresponding state */
199		if (nvlist_add_string(attrs, DM_PATH_STATE,
200		    path_state_name(pp->states[i])) != 0) {
201		    status = ENOMEM;
202		}
203		break;
204	    }
205	}
206	devid_free(devid);
207
208	return (status);
209}
210
211/*
212 * This is called when we have a name in the descriptor name field.  That
213 * only will be the case when the descriptor was created by getting the
214 * association from a drive to the path.  Since we filled in the name with
215 * the drive device id in that case, we can use the device id to look up the
216 * drive wwn.
217 */
218static int
219add_wwn(descriptor_t *dp, nvlist_t *attrs)
220{
221	ddi_devid_t	devid;
222	path_t		*pp;
223	int		i;
224	int		status = 0;
225
226	if (devid_str_decode(dp->name, &devid, NULL) != 0) {
227	    return (0);
228	}
229
230	/* find the index of the correct drive assoc. */
231	pp = dp->p.path;
232	for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) {
233	    if (pp->disks[i]->devid != NULL &&
234		devid_compare(pp->disks[i]->devid, devid) == 0) {
235
236		/* add the corresponding state */
237		if (nvlist_add_string(attrs, DM_WWN, pp->wwns[i]) != 0) {
238		    status = ENOMEM;
239		}
240		break;
241	    }
242	}
243	devid_free(devid);
244
245	return (status);
246}
247
248static descriptor_t **
249get_assoc_controllers(descriptor_t *desc, int *errp)
250{
251	path_t		*pp;
252	descriptor_t	**controllers;
253	int		i;
254
255	pp = desc->p.path;
256
257	/* a path can have just one controller */
258
259	controllers = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
260	if (controllers == NULL) {
261	    *errp = ENOMEM;
262	    return (NULL);
263	}
264
265	i = 0;
266	if (pp->controller != NULL) {
267	    controllers[i++] = cache_get_desc(DM_CONTROLLER,
268		pp->controller, NULL, NULL, errp);
269	    if (*errp != 0) {
270		cache_free_descriptors(controllers);
271		return (NULL);
272	    }
273	}
274
275	controllers[i] = NULL;
276
277	*errp = 0;
278	return (controllers);
279}
280
281static descriptor_t **
282get_assoc_drives(descriptor_t *desc, int *errp)
283{
284	path_t		*pp;
285	descriptor_t	**drives;
286	int		cnt;
287	int		i;
288
289	pp = desc->p.path;
290
291	/* Count how many we have. */
292	for (cnt = 0; pp->disks[cnt]; cnt++);
293
294	/* make the snapshot */
295	drives = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
296	if (drives == NULL) {
297	    *errp = ENOMEM;
298	    return (NULL);
299	}
300
301	for (i = 0; pp->disks[i]; i++) {
302	    drives[i] = cache_get_desc(DM_DRIVE, pp->disks[i], NULL, NULL,
303		errp);
304	    if (*errp != 0) {
305		cache_free_descriptors(drives);
306		return (NULL);
307	    }
308	}
309	drives[i] = NULL;
310
311	*errp = 0;
312	return (drives);
313}
314
315static char *
316path_state_name(di_path_state_t st)
317{
318	switch (st) {
319	    case DI_PATH_STATE_ONLINE:
320		return ("online");
321	    case DI_PATH_STATE_STANDBY:
322		return ("standby");
323	    case DI_PATH_STATE_OFFLINE:
324		return ("offline");
325	    case DI_PATH_STATE_FAULT:
326		return ("faulted");
327	}
328	return ("unknown");
329}
330