xref: /illumos-gate/usr/src/uts/sun4u/ngdr/io/dr_io.c (revision ed093b41a93e8563e6e1e5dae0768dda2a7bcc27)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * I/O support routines for DR
29  */
30 
31 #include <sys/debug.h>
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <sys/cred.h>
35 #include <sys/dditypes.h>
36 #include <sys/devops.h>
37 #include <sys/modctl.h>
38 #include <sys/poll.h>
39 #include <sys/conf.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/sunndi.h>
43 #include <sys/ndi_impldefs.h>
44 #include <sys/stat.h>
45 #include <sys/kmem.h>
46 #include <sys/processor.h>
47 #include <sys/cpuvar.h>
48 #include <sys/mem_config.h>
49 #include <sys/promif.h>
50 #include <sys/x_call.h>
51 #include <sys/cpu_sgnblk_defs.h>
52 #include <sys/membar.h>
53 #include <sys/stack.h>
54 #include <sys/sysmacros.h>
55 #include <sys/machsystm.h>
56 #include <sys/spitregs.h>
57 #include <sys/cpupart.h>
58 
59 #include <sys/archsystm.h>
60 #include <vm/hat_sfmmu.h>
61 #include <sys/pte.h>
62 #include <sys/mmu.h>
63 #include <sys/x_call.h>
64 #include <sys/cpu_module.h>
65 
66 #include <sys/cmn_err.h>
67 
68 #include <sys/dr.h>
69 #include <sys/dr_util.h>
70 #include <sys/drmach.h>
71 
72 void
73 dr_init_io_unit(dr_io_unit_t *ip)
74 {
75 	dr_state_t	new_state;
76 
77 	if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) {
78 		new_state = DR_STATE_CONFIGURED;
79 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
80 	} else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) {
81 		new_state = DR_STATE_CONNECTED;
82 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
83 	} else {
84 		new_state = DR_STATE_EMPTY;
85 	}
86 	dr_device_transition(&ip->sbi_cm, new_state);
87 }
88 
89 /*ARGSUSED*/
90 void
91 dr_attach_io(dr_handle_t *hp, dr_common_unit_t *cp)
92 {
93 	sbd_error_t *err;
94 
95 	dr_lock_status(hp->h_bd);
96 	err = drmach_configure(cp->sbdev_id, 0);
97 	dr_unlock_status(hp->h_bd);
98 
99 	if (!err)
100 		err = drmach_io_post_attach(cp->sbdev_id);
101 
102 	if (err)
103 		DRERR_SET_C(&cp->sbdev_error, &err);
104 }
105 
106 /*
107  * remove device nodes for the branch indicated by cp
108  */
109 /*ARGSUSED*/
110 void
111 dr_detach_io(dr_handle_t *hp, dr_common_unit_t *cp)
112 {
113 	sbd_error_t *err;
114 
115 	err = drmach_unconfigure(cp->sbdev_id, 0);
116 
117 	if (!err)
118 		err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY);
119 
120 	if (!err)
121 		err = drmach_io_post_release(cp->sbdev_id);
122 
123 	if (err) {
124 		dr_device_transition(cp, DR_STATE_CONFIGURED);
125 		DRERR_SET_C(&cp->sbdev_error, &err);
126 	}
127 }
128 
129 /*ARGSUSED*/
130 int
131 dr_disconnect_io(dr_io_unit_t *ip)
132 {
133 	return (0);
134 }
135 
136 /*ARGSUSED*/
137 int
138 dr_pre_attach_io(dr_handle_t *hp,
139 	dr_common_unit_t **devlist, int devnum)
140 {
141 	int		d;
142 
143 	for (d = 0; d < devnum; d++) {
144 		dr_common_unit_t *cp = devlist[d];
145 
146 		cmn_err(CE_CONT, "OS configure %s", cp->sbdev_path);
147 	}
148 
149 	return (0);
150 }
151 
152 /*ARGSUSED*/
153 int
154 dr_post_attach_io(dr_handle_t *hp,
155 	dr_common_unit_t **devlist, int devnum)
156 {
157 	return (0);
158 }
159 
160 static int
161 dr_check_io_refs(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
162 {
163 	register int	i, reftotal = 0;
164 	static fn_t	f = "dr_check_io_refs";
165 
166 	for (i = 0; i < devnum; i++) {
167 		dr_io_unit_t	*ip = (dr_io_unit_t *)devlist[i];
168 		dev_info_t	*dip;
169 		int		ref;
170 		int		refcount_non_gldv3;
171 		sbd_error_t	*err;
172 
173 		err = drmach_get_dip(ip->sbi_cm.sbdev_id, &dip);
174 		if (err)
175 			DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
176 		else if (dip != NULL) {
177 			ref = 0;
178 			refcount_non_gldv3 = 0;
179 			ASSERT(e_ddi_branch_held(dip));
180 			dr_check_devices(dip, &ref, hp, NULL, NULL,
181 			    0, &refcount_non_gldv3);
182 			ASSERT(refcount_non_gldv3 >= 0);
183 			ASSERT(ref >= refcount_non_gldv3);
184 			/*
185 			 * Ignore reference counts of non-gldv3 network devices
186 			 * as Crossbow creates reference counts for non-active
187 			 * (unplumbed) instances.  Reference count check in
188 			 * detach() known to prevent device from detaching
189 			 * as necessary.
190 			 */
191 			ref -= refcount_non_gldv3;
192 			hp->h_err = NULL;
193 			if (ref) {
194 				dr_dev_err(CE_WARN, &ip->sbi_cm, ESBD_BUSY);
195 			}
196 			PR_IO("%s: dip(%s) ref = %d\n",
197 			    f, ddi_get_name(dip), ref);
198 			reftotal += ref;
199 		} else {
200 			PR_IO("%s: NO dip for id (0x%x)\n",
201 			    f, (uint_t)(uintptr_t)ip->sbi_cm.sbdev_id);
202 		}
203 	}
204 
205 	return (reftotal);
206 }
207 
208 int
209 dr_pre_release_io(dr_handle_t *hp,
210 	dr_common_unit_t **devlist, int devnum)
211 {
212 	static fn_t	f = "dr_pre_release_io";
213 	int	d;
214 
215 	ASSERT(devnum > 0);
216 
217 	/* fail if any I/O device pre-release fails */
218 	for (d = 0; d < devnum; d++) {
219 		dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
220 
221 		if ((hp->h_err = drmach_io_pre_release(
222 		    ip->sbi_cm.sbdev_id)) != 0) {
223 			return (-1);
224 		}
225 	}
226 
227 	for (d = 0; d < devnum; d++) {
228 		dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
229 		sbd_error_t *err;
230 
231 		err = drmach_release(ip->sbi_cm.sbdev_id);
232 		if (err) {
233 			DRERR_SET_C(&ip->sbi_cm.sbdev_error,
234 			    &err);
235 			return (-1);
236 		}
237 	}
238 
239 	/* fail if any I/O devices are still referenced */
240 	if (dr_check_io_refs(hp, devlist, devnum) > 0) {
241 		PR_IO("%s: failed - I/O devices ref'd\n", f);
242 
243 		/* recover before return error */
244 		for (d = 0; d < devnum; d++) {
245 			dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
246 			sbd_error_t *err;
247 			err = drmach_io_unrelease(ip->sbi_cm.sbdev_id);
248 			if (err) {
249 				DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
250 				return (-1);
251 			}
252 		}
253 		return (-1);
254 	}
255 	return (0);
256 }
257 
258 /*ARGSUSED*/
259 int
260 dr_pre_detach_io(dr_handle_t *hp,
261 	dr_common_unit_t **devlist, int devnum)
262 {
263 	int		d;
264 
265 	ASSERT(devnum > 0);
266 
267 	for (d = 0; d < devnum; d++) {
268 		dr_common_unit_t *cp = devlist[d];
269 
270 		cmn_err(CE_CONT, "OS unconfigure %s", cp->sbdev_path);
271 	}
272 
273 	return (0);
274 }
275 
276 /*ARGSUSED*/
277 int
278 dr_post_detach_io(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
279 {
280 	register int	i;
281 	int		rv = 0;
282 	static fn_t	f = "dr_post_detach_io";
283 
284 	ASSERT(devnum > 0);
285 	for (i = 0; i < devnum; i++) {
286 		dr_common_unit_t	*cp = devlist[i];
287 		if (cp->sbdev_error != NULL) {
288 			PR_IO("%s: Failed\n", f);
289 			rv = -1;
290 			break;
291 		}
292 	}
293 	return (rv);
294 }
295 
296 static void
297 dr_get_comp_cond(dr_io_unit_t *ip, dev_info_t *dip)
298 {
299 	if (dip == NULL) {
300 		ip->sbi_cm.sbdev_cond = SBD_COND_UNKNOWN;
301 		return;
302 	}
303 
304 	if (DEVI(dip)->devi_flags & DEVI_RETIRED) {
305 		ip->sbi_cm.sbdev_cond = SBD_COND_FAILED;
306 		return;
307 	}
308 
309 	if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) {
310 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
311 	} else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) {
312 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
313 	}
314 }
315 
316 int
317 dr_io_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
318 {
319 	int		i, ix;
320 	dr_board_t	*bp;
321 	sbd_io_stat_t	*isp;
322 	dr_io_unit_t	*ip;
323 
324 	bp = hp->h_bd;
325 
326 	/*
327 	 * Only look for requested devices that are actually present.
328 	 */
329 	devset &= DR_DEVS_PRESENT(bp);
330 
331 	for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
332 		drmachid_t	 id;
333 		dev_info_t	*dip;
334 		sbd_error_t	*err;
335 		drmach_status_t	 pstat;
336 
337 		if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
338 			continue;
339 
340 		ip = dr_get_io_unit(bp, i);
341 
342 		if (ip->sbi_cm.sbdev_state == DR_STATE_EMPTY) {
343 			/* present, but not fully initialized */
344 			continue;
345 		}
346 
347 		id = ip->sbi_cm.sbdev_id;
348 		if (id == (drmachid_t)0)
349 			continue;
350 
351 		err = drmach_status(ip->sbi_cm.sbdev_id, &pstat);
352 		if (err) {
353 			DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
354 			return (-1);
355 		}
356 
357 		dip = NULL;
358 		err = drmach_get_dip(id, &dip);
359 		if (err) {
360 			/* catch this in debug kernels */
361 			ASSERT(0);
362 
363 			sbd_err_clear(&err);
364 			continue;
365 		}
366 
367 		isp = &dsp->d_io;
368 		bzero((caddr_t)isp, sizeof (*isp));
369 
370 		isp->is_cm.c_id.c_type = ip->sbi_cm.sbdev_type;
371 		isp->is_cm.c_id.c_unit = ip->sbi_cm.sbdev_unum;
372 		(void) strncpy(isp->is_cm.c_id.c_name, pstat.type,
373 		    sizeof (isp->is_cm.c_id.c_name));
374 
375 		dr_get_comp_cond(ip, dip);
376 		isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond;
377 		isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy | pstat.busy;
378 		isp->is_cm.c_time = ip->sbi_cm.sbdev_time;
379 		isp->is_cm.c_ostate = ip->sbi_cm.sbdev_ostate;
380 		isp->is_cm.c_sflags = 0;
381 
382 		if (dip == NULL) {
383 			isp->is_pathname[0] = '\0';
384 			isp->is_referenced = 0;
385 			isp->is_unsafe_count = 0;
386 		} else {
387 			int		refcount = 0, idx = 0;
388 			uint64_t	unsafe_devs[SBD_MAX_UNSAFE];
389 
390 			ASSERT(e_ddi_branch_held(dip));
391 			(void) ddi_pathname(dip, isp->is_pathname);
392 
393 			/* check reference and unsafe counts on devices */
394 			isp->is_unsafe_count = 0;
395 			dr_check_devices(dip, &refcount, hp, unsafe_devs,
396 			    &idx, SBD_MAX_UNSAFE, NULL);
397 			while (idx > 0) {
398 				isp->is_unsafe_list[idx-1] = unsafe_devs[idx-1];
399 				--idx;
400 			}
401 
402 			isp->is_referenced = (refcount == 0) ? 0 : 1;
403 
404 			hp->h_err = NULL;
405 		}
406 		ix++;
407 		dsp++;
408 	}
409 
410 	return (ix);
411 }
412