xref: /illumos-gate/usr/src/uts/sun4u/io/sbd_io.c (revision e86372a01d2d16a5dd4a64e144ed978ba17fe7dd)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/debug.h>
27 #include <sys/types.h>
28 #include <sys/errno.h>
29 #include <sys/cred.h>
30 #include <sys/dditypes.h>
31 #include <sys/sunddi.h>
32 #include <sys/sunndi.h>
33 #include <sys/ddi.h>
34 #include <sys/ddi_impldefs.h>
35 #include <sys/ndi_impldefs.h>
36 #include <sys/kmem.h>
37 #include <sys/note.h>
38 
39 #include <sys/sbdpriv.h>
40 #include <sys/sbd_io.h>
41 #include <sys/machsystm.h>
42 
43 
44 extern void sbd_errno_decode(int err, sbderror_t *ep, dev_info_t *dip);
45 extern sbd_state_t ostate_cvt(sbd_istate_t);
46 
47 /*
48  * Given a dev_info_t of a branch root, walk down the
49  * branch to attach drivers
50  */
51 /*ARGSUSED*/
52 void
53 sbd_attach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
54 {
55 	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
56 
57 	ASSERT(e_ddi_branch_held(dip));
58 
59 	(void) e_ddi_branch_configure(dip, NULL, 0);
60 
61 	ASSERT(sbp->sb_iopath[unit] != NULL);
62 
63 	(void) ddi_pathname(dip, sbp->sb_iopath[unit]);
64 }
65 
66 /*
67  * remove device nodes for the branch indicated by dip
68  * Hold the status lock so that status can safely do ddi_pathname().
69  */
70 /*ARGSUSED*/
71 void
72 sbd_detach_io(sbd_handle_t *hp, sbderror_t *ep, dev_info_t *dip, int unit)
73 {
74 	int rv;
75 	dev_info_t *fdip = NULL;
76 	sbd_board_t *sbp = SBDH2BD(hp->h_sbd);
77 
78 	ASSERT(e_ddi_branch_held(dip));
79 	mutex_enter(&sbp->sb_slock);
80 	rv = e_ddi_branch_unconfigure(dip, &fdip, DEVI_BRANCH_EVENT);
81 	mutex_exit(&sbp->sb_slock);
82 	if (rv) {
83 		/*
84 		 * If non-NULL, fdip is returned held and must be released.
85 		 */
86 		if (fdip != NULL) {
87 			sbd_errno_decode(rv, ep, fdip);
88 			ddi_release_devi(fdip);
89 		} else {
90 			sbd_errno_decode(rv, ep, dip);
91 		}
92 	}
93 }
94 
95 /*ARGSUSED*/
96 void
97 sbd_init_io_unit(sbd_board_t *sbp, int unit)
98 {
99 	sbd_istate_t	new_state;
100 	sbd_io_unit_t	*ip;
101 	dev_info_t	*dip;
102 
103 	ip = SBD_GET_BOARD_IOUNIT(sbp, unit);
104 
105 	if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_IO, unit)) {
106 		new_state = SBD_STATE_CONFIGURED;
107 	} else if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_IO, unit)) {
108 		new_state = SBD_STATE_CONNECTED;
109 	} else {
110 		new_state = SBD_STATE_EMPTY;
111 	}
112 	dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][unit];
113 	ip->sbi_cm.sbdev_cond = sbd_get_comp_cond(dip);
114 
115 	/*
116 	 * Any changes to this io component should be performed above
117 	 * this call to ensure the component is fully initialized
118 	 * before transitioning to the new state.
119 	 */
120 	SBD_DEVICE_TRANSITION(sbp, SBD_COMP_IO, unit, new_state);
121 }
122 
123 /*ARGSUSED*/
124 int
125 sbd_disconnect_io(sbd_handle_t *hp, int unit)
126 {
127 	return (0);
128 }
129 
130 int
131 sbd_pre_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
132 {
133 	_NOTE(ARGUNUSED(hp))
134 	_NOTE(ARGUNUSED(devlist))
135 	_NOTE(ARGUNUSED(devnum))
136 
137 	return (0);
138 }
139 
140 /*ARGSUSED*/
141 int
142 sbd_pre_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
143 {
144 	fn_t	f = "sbd_pre_detach_io";
145 
146 	PR_IO("%s...\n", f);
147 
148 	if (devnum <= 0)
149 		return (-1);
150 
151 	/* fail if any I/O devices are referenced */
152 	if (sbd_check_io_refs(hp, devlist, devnum) > 0) {
153 		PR_IO("%s: failed - I/O devices ref'd\n", f);
154 		return (-1);
155 	}
156 
157 	return (0);
158 }
159 
160 /*ARGSUSED*/
161 int
162 sbd_post_attach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
163 {
164 	_NOTE(ARGUNUSED(hp))
165 	_NOTE(ARGUNUSED(devlist))
166 	_NOTE(ARGUNUSED(devnum))
167 
168 	return (0);
169 }
170 
171 /*ARGSUSED*/
172 int
173 sbd_post_detach_io(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
174 {
175 	return (0);
176 }
177 
178 /*ARGSUSED*/
179 int
180 sbd_io_status(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp)
181 {
182 	int		i, ix;
183 	sbd_board_t	*sbp;
184 	sbd_io_stat_t	*isp;
185 	sbd_io_unit_t	*ip;
186 	sbd_istate_t	dstate;
187 	sbdp_handle_t	*hdp;
188 	sbderror_t	*ep;
189 	sbd_error_t	*sep;
190 
191 	/*
192 	 * Only look for requested devices that are actually present.
193 	 */
194 	sbp = SBDH2BD(hp->h_sbd);
195 
196 	ep = HD2MACHERR(hp);
197 	sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP);
198 	hdp = sbd_get_sbdp_handle(sbp, hp);
199 
200 	/*
201 	 * Concurrent status and unconfigure, disconnect are allowed.
202 	 * To prevent DR code from accessing stale dips, check the
203 	 * present devset and access the dips with status lock held.
204 	 * Disconnect and unconfigure code change dip state with
205 	 * status lock (sb_slock) held.
206 	 */
207 	mutex_enter(&sbp->sb_slock);
208 
209 	devset &= SBD_DEVS_PRESENT(sbp);
210 
211 	for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
212 		dev_info_t	*dip;
213 		int		unit;
214 		int		namelen;
215 		int		refcount = 0;
216 
217 		if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
218 			continue;
219 		/*
220 		 * Check to make sure the io component is in a state
221 		 * where its fully initialized.
222 		 */
223 		if (SBD_DEVICE_STATE(sbp, SBD_COMP_IO, i) == SBD_STATE_EMPTY)
224 			continue;
225 
226 		dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i];
227 		if (dip == NULL)
228 			continue;
229 
230 		isp = &dsp->d_io;
231 
232 		bzero((caddr_t)isp, sizeof (*isp));
233 		namelen = sizeof (isp->is_name);
234 		(void) ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
235 		    DDI_PROP_DONTPASS, OBP_DEVICETYPE,
236 		    (caddr_t)isp->is_name, &namelen);
237 
238 		isp->is_unit = sbdp_get_unit_num(hdp, dip);
239 		if (isp->is_unit < 0) {
240 			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
241 				continue;
242 			else {
243 				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
244 				break;
245 			}
246 		}
247 		unit = isp->is_unit;
248 
249 		dstate = SBD_DEVICE_STATE(sbp, SBD_COMP_IO, unit);
250 		isp->is_ostate	= ostate_cvt(dstate);
251 		isp->is_type = SBD_COMP_IO;
252 		ip = SBD_GET_BOARD_IOUNIT(sbp, unit);
253 		ip->sbi_cm.sbdev_cond = sbd_get_comp_cond(dip);
254 		isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond;
255 		isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy;
256 		isp->is_cm.c_time = ip->sbi_cm.sbdev_time;
257 
258 
259 		/*
260 		 * This is safe to do as unconfigure and disconnect
261 		 * hold the status lock while changing dip state.
262 		 */
263 		(void) ddi_pathname(dip, isp->is_pathname);
264 
265 		/*
266 		 * We use a dummy handle in which to collect
267 		 * the major numbers of unsafe devices.
268 		 */
269 		sbdp_check_devices(dip, &refcount, sep, NULL);
270 
271 		isp->is_referenced = (refcount == 0) ? 0 : 1;
272 
273 		isp->is_unsafe_count = 0;
274 
275 		/*
276 		 * Reset error field since we don't care about
277 		 * errors at this level.  The unsafe devices
278 		 * will be reported in the structure.
279 		 */
280 		SBD_SET_ERR(ep, ESBD_NOERROR);
281 		ep->e_rsc[0] = '\0';
282 
283 		ix++;
284 		dsp++;
285 	}
286 
287 	mutex_exit(&sbp->sb_slock);
288 
289 	kmem_free(sep, sizeof (sbd_error_t));
290 	sbd_release_sbdp_handle(hdp);
291 
292 	return (ix);
293 }
294 
295 /*ARGSUSED*/
296 int
297 sbd_io_cnt(sbd_handle_t *hp, sbd_devset_t devset)
298 {
299 	int		i, ix;
300 	sbd_board_t	*sbp;
301 
302 	sbp = SBDH2BD(hp->h_sbd);
303 
304 	/*
305 	 * Only look for requested devices that are actually present.
306 	 */
307 	devset &= SBD_DEVS_PRESENT(sbp);
308 
309 	for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
310 		dev_info_t	*dip;
311 
312 		if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
313 			continue;
314 
315 		dip = sbp->sb_devlist[NIX(SBD_COMP_IO)][i];
316 		if (dip == NULL)
317 			continue;
318 
319 		ix++;
320 	}
321 
322 	return (ix);
323 }
324 
325 int
326 sbd_check_io_refs(sbd_handle_t *hp, sbd_devlist_t devlist[], int devnum)
327 {
328 	register int	i, reftotal = 0;
329 	fn_t	f = "sbd_check_io_refs";
330 	sbd_error_t *sep;
331 	sbderror_t *ep;
332 
333 	sep = kmem_zalloc(sizeof (sbd_error_t), KM_SLEEP);
334 	ep = HD2MACHERR(hp);
335 
336 	for (i = 0; i < devnum; i++) {
337 		dev_info_t	*dip;
338 		int		ref;
339 		int		refcount_non_gldv3;
340 
341 		dip = devlist[i].dv_dip;
342 		ref = 0;
343 		refcount_non_gldv3 = 0;
344 		sbdp_check_devices(dip, &ref, sep, &refcount_non_gldv3);
345 		ASSERT(refcount_non_gldv3 >= 0);
346 		ASSERT(ref >= refcount_non_gldv3);
347 		/*
348 		 * Ignore reference counts of non-gldv3 network devices
349 		 * as Crossbow creates reference counts for non-active
350 		 * (unplumbed) instances.  Reference count check in
351 		 * detach() known to prevent device from detaching
352 		 * as necessary.
353 		 */
354 		ref -= refcount_non_gldv3;
355 		if (ref) {
356 			if (SBD_GET_ERR(ep) == 0) {
357 				SBD_GET_PERR(sep, ep);
358 			}
359 			SBD_GET_PERR(sep, &devlist[i].dv_error);
360 		}
361 		PR_IO("%s: dip(%s) ref = %d\n", f, ddi_get_name(dip), ref);
362 		reftotal += ref;
363 	}
364 
365 	kmem_free(sep, sizeof (sbd_error_t));
366 
367 	return (reftotal);
368 }
369 
370 int
371 sbd_check_io_attached(dev_info_t *dip, void *arg)
372 {
373 	dev_info_t **tdip;
374 
375 	tdip = (dev_info_t **)arg;
376 
377 	if (dip == *tdip) {
378 		int state;
379 
380 		state = ddi_get_devstate(dip);
381 		if (i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP)) {
382 			*tdip = NULL;
383 			return (DDI_WALK_TERMINATE);
384 		}
385 	}
386 	return (DDI_WALK_CONTINUE);
387 }
388 
389 int
390 sbd_pre_release_io(sbd_handle_t *hp,
391 	sbd_devlist_t *devlist, int devnum)
392 {
393 	fn_t	f = "sbd_pre_release_io";
394 	int	rv = 0;
395 	int	i;
396 
397 	ASSERT(devnum > 0);
398 
399 	/* fail if any I/O devices are referenced */
400 	if ((rv = sbd_check_io_refs(hp, devlist, devnum)) > 0) {
401 		/*
402 		 * One of the devices may have failed check to see which
403 		 * and set in the main handle
404 		 */
405 		for (i = 0; i < devnum; i++) {
406 			if (SBD_GET_ERR(&devlist[i].dv_error) != 0) {
407 				(void) sbd_set_err_in_hdl(hp,
408 				    &devlist[i].dv_error);
409 				break;
410 			}
411 		}
412 		PR_IO("%s: failed - I/O devices ref'd\n", f);
413 	}
414 
415 	return (rv);
416 }
417