xref: /illumos-gate/usr/src/uts/sun4u/ngdr/io/dr_io.c (revision aef83d42)
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 2009 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 		sbd_error_t	*err;
171 
172 		err = drmach_get_dip(ip->sbi_cm.sbdev_id, &dip);
173 		if (err)
174 			DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
175 		else if (dip != NULL) {
176 			ref = 0;
177 			ASSERT(e_ddi_branch_held(dip));
178 			dr_check_devices(dip, &ref, hp, NULL, NULL, 0);
179 			hp->h_err = NULL;
180 			if (ref) {
181 				dr_dev_err(CE_WARN, &ip->sbi_cm, ESBD_BUSY);
182 			}
183 			PR_IO("%s: dip(%s) ref = %d\n",
184 			    f, ddi_get_name(dip), ref);
185 			reftotal += ref;
186 		} else {
187 			PR_IO("%s: NO dip for id (0x%x)\n",
188 			    f, (uint_t)(uintptr_t)ip->sbi_cm.sbdev_id);
189 		}
190 	}
191 
192 	return (reftotal);
193 }
194 
195 int
196 dr_pre_release_io(dr_handle_t *hp,
197 	dr_common_unit_t **devlist, int devnum)
198 {
199 	static fn_t	f = "dr_pre_release_io";
200 	int	d;
201 
202 	ASSERT(devnum > 0);
203 
204 	/* fail if any I/O device pre-release fails */
205 	for (d = 0; d < devnum; d++) {
206 		dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
207 
208 		if ((hp->h_err = drmach_io_pre_release(
209 		    ip->sbi_cm.sbdev_id)) != 0) {
210 			return (-1);
211 		}
212 	}
213 
214 	for (d = 0; d < devnum; d++) {
215 		dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
216 		sbd_error_t *err;
217 
218 		err = drmach_release(ip->sbi_cm.sbdev_id);
219 		if (err) {
220 			DRERR_SET_C(&ip->sbi_cm.sbdev_error,
221 			    &err);
222 			return (-1);
223 		}
224 	}
225 
226 	/* fail if any I/O devices are still referenced */
227 	if (dr_check_io_refs(hp, devlist, devnum) > 0) {
228 		PR_IO("%s: failed - I/O devices ref'd\n", f);
229 
230 		/* recover before return error */
231 		for (d = 0; d < devnum; d++) {
232 			dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d];
233 			sbd_error_t *err;
234 			err = drmach_io_unrelease(ip->sbi_cm.sbdev_id);
235 			if (err) {
236 				DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
237 				return (-1);
238 			}
239 		}
240 		return (-1);
241 	}
242 	return (0);
243 }
244 
245 /*ARGSUSED*/
246 int
247 dr_pre_detach_io(dr_handle_t *hp,
248 	dr_common_unit_t **devlist, int devnum)
249 {
250 	int		d;
251 
252 	ASSERT(devnum > 0);
253 
254 	for (d = 0; d < devnum; d++) {
255 		dr_common_unit_t *cp = devlist[d];
256 
257 		cmn_err(CE_CONT, "OS unconfigure %s", cp->sbdev_path);
258 	}
259 
260 	return (0);
261 }
262 
263 /*ARGSUSED*/
264 int
265 dr_post_detach_io(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
266 {
267 	register int	i;
268 	int		rv = 0;
269 	static fn_t	f = "dr_post_detach_io";
270 
271 	ASSERT(devnum > 0);
272 	for (i = 0; i < devnum; i++) {
273 		dr_common_unit_t	*cp = devlist[i];
274 		if (cp->sbdev_error != NULL) {
275 			PR_IO("%s: Failed\n", f);
276 			rv = -1;
277 			break;
278 		}
279 	}
280 	return (rv);
281 }
282 
283 static void
284 dr_get_comp_cond(dr_io_unit_t *ip, dev_info_t *dip)
285 {
286 	if (dip == NULL) {
287 		ip->sbi_cm.sbdev_cond = SBD_COND_UNKNOWN;
288 		return;
289 	}
290 
291 	if (DEVI(dip)->devi_flags & DEVI_RETIRED) {
292 		ip->sbi_cm.sbdev_cond = SBD_COND_FAILED;
293 		return;
294 	}
295 
296 	if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) {
297 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
298 	} else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) {
299 		ip->sbi_cm.sbdev_cond = SBD_COND_OK;
300 	}
301 }
302 
303 int
304 dr_io_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
305 {
306 	int		i, ix;
307 	dr_board_t	*bp;
308 	sbd_io_stat_t	*isp;
309 	dr_io_unit_t	*ip;
310 
311 	bp = hp->h_bd;
312 
313 	/*
314 	 * Only look for requested devices that are actually present.
315 	 */
316 	devset &= DR_DEVS_PRESENT(bp);
317 
318 	for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
319 		drmachid_t	 id;
320 		dev_info_t	*dip;
321 		sbd_error_t	*err;
322 		drmach_status_t	 pstat;
323 
324 		if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0)
325 			continue;
326 
327 		ip = dr_get_io_unit(bp, i);
328 
329 		if (ip->sbi_cm.sbdev_state == DR_STATE_EMPTY) {
330 			/* present, but not fully initialized */
331 			continue;
332 		}
333 
334 		id = ip->sbi_cm.sbdev_id;
335 		if (id == (drmachid_t)0)
336 			continue;
337 
338 		err = drmach_status(ip->sbi_cm.sbdev_id, &pstat);
339 		if (err) {
340 			DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err);
341 			return (-1);
342 		}
343 
344 		dip = NULL;
345 		err = drmach_get_dip(id, &dip);
346 		if (err) {
347 			/* catch this in debug kernels */
348 			ASSERT(0);
349 
350 			sbd_err_clear(&err);
351 			continue;
352 		}
353 
354 		isp = &dsp->d_io;
355 		bzero((caddr_t)isp, sizeof (*isp));
356 
357 		isp->is_cm.c_id.c_type = ip->sbi_cm.sbdev_type;
358 		isp->is_cm.c_id.c_unit = ip->sbi_cm.sbdev_unum;
359 		(void) strncpy(isp->is_cm.c_id.c_name, pstat.type,
360 		    sizeof (isp->is_cm.c_id.c_name));
361 
362 		dr_get_comp_cond(ip, dip);
363 		isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond;
364 		isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy | pstat.busy;
365 		isp->is_cm.c_time = ip->sbi_cm.sbdev_time;
366 		isp->is_cm.c_ostate = ip->sbi_cm.sbdev_ostate;
367 		isp->is_cm.c_sflags = 0;
368 
369 		if (dip == NULL) {
370 			isp->is_pathname[0] = '\0';
371 			isp->is_referenced = 0;
372 			isp->is_unsafe_count = 0;
373 		} else {
374 			int		refcount = 0, idx = 0;
375 			uint64_t	unsafe_devs[SBD_MAX_UNSAFE];
376 
377 			ASSERT(e_ddi_branch_held(dip));
378 			(void) ddi_pathname(dip, isp->is_pathname);
379 
380 			/* check reference and unsafe counts on devices */
381 			isp->is_unsafe_count = 0;
382 			dr_check_devices(dip, &refcount, hp, unsafe_devs,
383 			    &idx, SBD_MAX_UNSAFE);
384 			while (idx > 0) {
385 				isp->is_unsafe_list[idx-1] = unsafe_devs[idx-1];
386 				--idx;
387 			}
388 
389 			isp->is_referenced = (refcount == 0) ? 0 : 1;
390 
391 			hp->h_err = NULL;
392 		}
393 		ix++;
394 		dsp++;
395 	}
396 
397 	return (ix);
398 }
399