author | cth |
Fri, 22 Feb 2008 09:02:16 -0800 | |
changeset 6065 | b05c5c670963 |
parent 5895 | f251acdd9bdc |
child 6640 | c92ca9b95b9c |
permissions | -rw-r--r-- |
0 | 1 |
/* |
2 |
* CDDL HEADER START |
|
3 |
* |
|
4 |
* The contents of this file are subject to the terms of the |
|
1961
cceb6bfa61a5
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
1826
diff
changeset
|
5 |
* Common Development and Distribution License (the "License"). |
cceb6bfa61a5
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
1826
diff
changeset
|
6 |
* You may not use this file except in compliance with the License. |
0 | 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 |
/* |
|
5895
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
22 |
* Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
0 | 23 |
* Use is subject to license terms. |
24 |
*/ |
|
25 |
||
26 |
#pragma ident "%Z%%M% %I% %E% SMI" |
|
27 |
||
28 |
#include <sys/note.h> |
|
29 |
#include <sys/t_lock.h> |
|
30 |
#include <sys/cmn_err.h> |
|
31 |
#include <sys/instance.h> |
|
32 |
#include <sys/conf.h> |
|
33 |
#include <sys/stat.h> |
|
34 |
#include <sys/ddi.h> |
|
35 |
#include <sys/hwconf.h> |
|
36 |
#include <sys/sunddi.h> |
|
37 |
#include <sys/sunndi.h> |
|
38 |
#include <sys/ddi_impldefs.h> |
|
39 |
#include <sys/ndi_impldefs.h> |
|
40 |
#include <sys/modctl.h> |
|
4845 | 41 |
#include <sys/contract/device_impl.h> |
0 | 42 |
#include <sys/dacf.h> |
43 |
#include <sys/promif.h> |
|
44 |
#include <sys/cpuvar.h> |
|
45 |
#include <sys/pathname.h> |
|
46 |
#include <sys/taskq.h> |
|
47 |
#include <sys/sysevent.h> |
|
48 |
#include <sys/sunmdi.h> |
|
49 |
#include <sys/stream.h> |
|
50 |
#include <sys/strsubr.h> |
|
51 |
#include <sys/fs/snode.h> |
|
52 |
#include <sys/fs/dv_node.h> |
|
2621 | 53 |
#include <sys/reboot.h> |
4845 | 54 |
#include <sys/sysmacros.h> |
55 |
#include <sys/sunldi.h> |
|
56 |
#include <sys/sunldi_impl.h> |
|
0 | 57 |
|
58 |
#ifdef DEBUG |
|
59 |
int ddidebug = DDI_AUDIT; |
|
60 |
#else |
|
61 |
int ddidebug = 0; |
|
62 |
#endif |
|
63 |
||
64 |
#define MT_CONFIG_OP 0 |
|
65 |
#define MT_UNCONFIG_OP 1 |
|
66 |
||
67 |
/* Multi-threaded configuration */ |
|
68 |
struct mt_config_handle { |
|
69 |
kmutex_t mtc_lock; |
|
70 |
kcondvar_t mtc_cv; |
|
71 |
int mtc_thr_count; |
|
72 |
dev_info_t *mtc_pdip; /* parent dip for mt_config_children */ |
|
73 |
dev_info_t **mtc_fdip; /* "a" dip where unconfigure failed */ |
|
74 |
major_t mtc_parmajor; /* parent major for mt_config_driver */ |
|
75 |
major_t mtc_major; |
|
76 |
int mtc_flags; |
|
77 |
int mtc_op; /* config or unconfig */ |
|
78 |
int mtc_error; /* operation error */ |
|
79 |
struct brevq_node **mtc_brevqp; /* outstanding branch events queue */ |
|
80 |
#ifdef DEBUG |
|
81 |
int total_time; |
|
82 |
timestruc_t start_time; |
|
83 |
#endif /* DEBUG */ |
|
84 |
}; |
|
85 |
||
86 |
struct devi_nodeid { |
|
789 | 87 |
pnode_t nodeid; |
0 | 88 |
dev_info_t *dip; |
89 |
struct devi_nodeid *next; |
|
90 |
}; |
|
91 |
||
92 |
struct devi_nodeid_list { |
|
93 |
kmutex_t dno_lock; /* Protects other fields */ |
|
94 |
struct devi_nodeid *dno_head; /* list of devi nodeid elements */ |
|
95 |
struct devi_nodeid *dno_free; /* Free list */ |
|
96 |
uint_t dno_list_length; /* number of dips in list */ |
|
97 |
}; |
|
98 |
||
99 |
/* used to keep track of branch remove events to be generated */ |
|
100 |
struct brevq_node { |
|
1317 | 101 |
char *brn_deviname; |
102 |
struct brevq_node *brn_sibling; |
|
103 |
struct brevq_node *brn_child; |
|
0 | 104 |
}; |
105 |
||
106 |
static struct devi_nodeid_list devi_nodeid_list; |
|
107 |
static struct devi_nodeid_list *devimap = &devi_nodeid_list; |
|
108 |
||
109 |
/* |
|
110 |
* Well known nodes which are attached first at boot time. |
|
111 |
*/ |
|
112 |
dev_info_t *top_devinfo; /* root of device tree */ |
|
113 |
dev_info_t *options_dip; |
|
114 |
dev_info_t *pseudo_dip; |
|
115 |
dev_info_t *clone_dip; |
|
116 |
dev_info_t *scsi_vhci_dip; /* MPXIO dip */ |
|
117 |
major_t clone_major; |
|
118 |
||
2621 | 119 |
/* |
120 |
* A non-global zone's /dev is derived from the device tree. |
|
121 |
* This generation number serves to indicate when a zone's |
|
122 |
* /dev may need to be updated. |
|
123 |
*/ |
|
124 |
volatile ulong_t devtree_gen; /* generation number */ |
|
125 |
||
0 | 126 |
/* block all future dev_info state changes */ |
127 |
static hrtime_t volatile devinfo_freeze = 0; |
|
128 |
||
129 |
/* number of dev_info attaches/detaches currently in progress */ |
|
130 |
static ulong_t devinfo_attach_detach = 0; |
|
131 |
||
132 |
extern kmutex_t global_vhci_lock; |
|
133 |
||
2621 | 134 |
/* bitset of DS_SYSAVAIL & DS_RECONFIG - no races, no lock */ |
135 |
static int devname_state = 0; |
|
136 |
||
0 | 137 |
/* |
138 |
* The devinfo snapshot cache and related variables. |
|
139 |
* The only field in the di_cache structure that needs initialization |
|
140 |
* is the mutex (cache_lock). However, since this is an adaptive mutex |
|
141 |
* (MUTEX_DEFAULT) - it is automatically initialized by being allocated |
|
142 |
* in zeroed memory (static storage class). Therefore no explicit |
|
143 |
* initialization of the di_cache structure is needed. |
|
144 |
*/ |
|
145 |
struct di_cache di_cache = {1}; |
|
146 |
int di_cache_debug = 0; |
|
147 |
||
148 |
/* For ddvis, which needs pseudo children under PCI */ |
|
149 |
int pci_allow_pseudo_children = 0; |
|
150 |
||
4145 | 151 |
/* Allow path-oriented alias driver binding on driver.conf enumerated nodes */ |
152 |
int driver_conf_allow_path_alias = 1; |
|
153 |
||
0 | 154 |
/* |
155 |
* The following switch is for service people, in case a |
|
156 |
* 3rd party driver depends on identify(9e) being called. |
|
157 |
*/ |
|
158 |
int identify_9e = 0; |
|
159 |
||
160 |
int mtc_off; /* turn off mt config */ |
|
161 |
||
162 |
static kmem_cache_t *ddi_node_cache; /* devinfo node cache */ |
|
163 |
static devinfo_log_header_t *devinfo_audit_log; /* devinfo log */ |
|
164 |
static int devinfo_log_size; /* size in pages */ |
|
165 |
||
166 |
static int lookup_compatible(dev_info_t *, uint_t); |
|
167 |
static char *encode_composite_string(char **, uint_t, size_t *, uint_t); |
|
168 |
static void link_to_driver_list(dev_info_t *); |
|
169 |
static void unlink_from_driver_list(dev_info_t *); |
|
170 |
static void add_to_dn_list(struct devnames *, dev_info_t *); |
|
171 |
static void remove_from_dn_list(struct devnames *, dev_info_t *); |
|
172 |
static dev_info_t *find_child_by_callback(dev_info_t *, char *, char *, |
|
173 |
int (*)(dev_info_t *, char *, int)); |
|
174 |
static dev_info_t *find_duplicate_child(); |
|
175 |
static void add_global_props(dev_info_t *); |
|
176 |
static void remove_global_props(dev_info_t *); |
|
177 |
static int uninit_node(dev_info_t *); |
|
178 |
static void da_log_init(void); |
|
179 |
static void da_log_enter(dev_info_t *); |
|
180 |
static int walk_devs(dev_info_t *, int (*f)(dev_info_t *, void *), void *, int); |
|
181 |
static int reset_nexus_flags(dev_info_t *, void *); |
|
182 |
static void ddi_optimize_dtree(dev_info_t *); |
|
183 |
static int is_leaf_node(dev_info_t *); |
|
184 |
static struct mt_config_handle *mt_config_init(dev_info_t *, dev_info_t **, |
|
185 |
int, major_t, int, struct brevq_node **); |
|
186 |
static void mt_config_children(struct mt_config_handle *); |
|
187 |
static void mt_config_driver(struct mt_config_handle *); |
|
188 |
static int mt_config_fini(struct mt_config_handle *); |
|
189 |
static int devi_unconfig_common(dev_info_t *, dev_info_t **, int, major_t, |
|
190 |
struct brevq_node **); |
|
191 |
static int |
|
192 |
ndi_devi_config_obp_args(dev_info_t *parent, char *devnm, |
|
193 |
dev_info_t **childp, int flags); |
|
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
194 |
static void i_link_vhci_node(dev_info_t *); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
195 |
static void ndi_devi_exit_and_wait(dev_info_t *dip, |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
196 |
int circular, clock_t end_time); |
4145 | 197 |
static int ndi_devi_unbind_driver(dev_info_t *dip); |
0 | 198 |
|
4845 | 199 |
static void i_ddi_check_retire(dev_info_t *dip); |
200 |
||
201 |
||
202 |
||
0 | 203 |
/* |
204 |
* dev_info cache and node management |
|
205 |
*/ |
|
206 |
||
207 |
/* initialize dev_info node cache */ |
|
208 |
void |
|
209 |
i_ddi_node_cache_init() |
|
210 |
{ |
|
211 |
ASSERT(ddi_node_cache == NULL); |
|
212 |
ddi_node_cache = kmem_cache_create("dev_info_node_cache", |
|
213 |
sizeof (struct dev_info), 0, NULL, NULL, NULL, NULL, NULL, 0); |
|
214 |
||
215 |
if (ddidebug & DDI_AUDIT) |
|
216 |
da_log_init(); |
|
217 |
} |
|
218 |
||
219 |
/* |
|
220 |
* Allocating a dev_info node, callable from interrupt context with KM_NOSLEEP |
|
221 |
* The allocated node has a reference count of 0. |
|
222 |
*/ |
|
223 |
dev_info_t * |
|
789 | 224 |
i_ddi_alloc_node(dev_info_t *pdip, char *node_name, pnode_t nodeid, |
0 | 225 |
int instance, ddi_prop_t *sys_prop, int flag) |
226 |
{ |
|
227 |
struct dev_info *devi; |
|
228 |
struct devi_nodeid *elem; |
|
229 |
static char failed[] = "i_ddi_alloc_node: out of memory"; |
|
230 |
||
231 |
ASSERT(node_name != NULL); |
|
232 |
||
233 |
if ((devi = kmem_cache_alloc(ddi_node_cache, flag)) == NULL) { |
|
234 |
cmn_err(CE_NOTE, failed); |
|
235 |
return (NULL); |
|
236 |
} |
|
237 |
||
238 |
bzero(devi, sizeof (struct dev_info)); |
|
239 |
||
240 |
if (devinfo_audit_log) { |
|
241 |
devi->devi_audit = kmem_zalloc(sizeof (devinfo_audit_t), flag); |
|
242 |
if (devi->devi_audit == NULL) |
|
243 |
goto fail; |
|
244 |
} |
|
245 |
||
246 |
if ((devi->devi_node_name = i_ddi_strdup(node_name, flag)) == NULL) |
|
247 |
goto fail; |
|
4145 | 248 |
|
0 | 249 |
/* default binding name is node name */ |
250 |
devi->devi_binding_name = devi->devi_node_name; |
|
4145 | 251 |
devi->devi_major = (major_t)-1; /* unbound by default */ |
0 | 252 |
|
253 |
/* |
|
254 |
* Make a copy of system property |
|
255 |
*/ |
|
256 |
if (sys_prop && |
|
257 |
(devi->devi_sys_prop_ptr = i_ddi_prop_list_dup(sys_prop, flag)) |
|
258 |
== NULL) |
|
259 |
goto fail; |
|
260 |
||
261 |
/* |
|
262 |
* Assign devi_nodeid, devi_node_class, devi_node_attributes |
|
263 |
* according to the following algorithm: |
|
264 |
* |
|
265 |
* nodeid arg node class node attributes |
|
266 |
* |
|
267 |
* DEVI_PSEUDO_NODEID DDI_NC_PSEUDO A |
|
268 |
* DEVI_SID_NODEID DDI_NC_PSEUDO A,P |
|
269 |
* other DDI_NC_PROM P |
|
270 |
* |
|
271 |
* Where A = DDI_AUTO_ASSIGNED_NODEID (auto-assign a nodeid) |
|
272 |
* and P = DDI_PERSISTENT |
|
273 |
* |
|
274 |
* auto-assigned nodeids are also auto-freed. |
|
275 |
*/ |
|
276 |
switch (nodeid) { |
|
277 |
case DEVI_SID_NODEID: |
|
278 |
devi->devi_node_attributes = DDI_PERSISTENT; |
|
279 |
if ((elem = kmem_zalloc(sizeof (*elem), flag)) == NULL) |
|
280 |
goto fail; |
|
281 |
/*FALLTHROUGH*/ |
|
282 |
case DEVI_PSEUDO_NODEID: |
|
283 |
devi->devi_node_attributes |= DDI_AUTO_ASSIGNED_NODEID; |
|
284 |
devi->devi_node_class = DDI_NC_PSEUDO; |
|
285 |
if (impl_ddi_alloc_nodeid(&devi->devi_nodeid)) { |
|
286 |
panic("i_ddi_alloc_node: out of nodeids"); |
|
287 |
/*NOTREACHED*/ |
|
288 |
} |
|
289 |
break; |
|
290 |
default: |
|
291 |
if ((elem = kmem_zalloc(sizeof (*elem), flag)) == NULL) |
|
292 |
goto fail; |
|
293 |
/* |
|
294 |
* the nodetype is 'prom', try to 'take' the nodeid now. |
|
295 |
* This requires memory allocation, so check for failure. |
|
296 |
*/ |
|
297 |
if (impl_ddi_take_nodeid(nodeid, flag) != 0) { |
|
298 |
kmem_free(elem, sizeof (*elem)); |
|
299 |
goto fail; |
|
300 |
} |
|
301 |
||
302 |
devi->devi_nodeid = nodeid; |
|
303 |
devi->devi_node_class = DDI_NC_PROM; |
|
304 |
devi->devi_node_attributes = DDI_PERSISTENT; |
|
305 |
||
306 |
} |
|
307 |
||
308 |
if (ndi_dev_is_persistent_node((dev_info_t *)devi)) { |
|
309 |
mutex_enter(&devimap->dno_lock); |
|
310 |
elem->next = devimap->dno_free; |
|
311 |
devimap->dno_free = elem; |
|
312 |
mutex_exit(&devimap->dno_lock); |
|
313 |
} |
|
314 |
||
315 |
/* |
|
316 |
* Instance is normally initialized to -1. In a few special |
|
317 |
* cases, the caller may specify an instance (e.g. CPU nodes). |
|
318 |
*/ |
|
319 |
devi->devi_instance = instance; |
|
320 |
||
321 |
/* |
|
322 |
* set parent and bus_ctl parent |
|
323 |
*/ |
|
324 |
devi->devi_parent = DEVI(pdip); |
|
325 |
devi->devi_bus_ctl = DEVI(pdip); |
|
326 |
||
327 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
328 |
"i_ddi_alloc_node: name=%s id=%d\n", node_name, devi->devi_nodeid)); |
|
329 |
||
330 |
cv_init(&(devi->devi_cv), NULL, CV_DEFAULT, NULL); |
|
331 |
mutex_init(&(devi->devi_lock), NULL, MUTEX_DEFAULT, NULL); |
|
332 |
mutex_init(&(devi->devi_pm_lock), NULL, MUTEX_DEFAULT, NULL); |
|
333 |
mutex_init(&(devi->devi_pm_busy_lock), NULL, MUTEX_DEFAULT, NULL); |
|
334 |
||
4845 | 335 |
RIO_TRACE((CE_NOTE, "i_ddi_alloc_node: Initing contract fields: " |
336 |
"dip=%p, name=%s", (void *)devi, node_name)); |
|
337 |
||
338 |
mutex_init(&(devi->devi_ct_lock), NULL, MUTEX_DEFAULT, NULL); |
|
339 |
cv_init(&(devi->devi_ct_cv), NULL, CV_DEFAULT, NULL); |
|
340 |
devi->devi_ct_count = -1; /* counter not in use if -1 */ |
|
341 |
list_create(&(devi->devi_ct), sizeof (cont_device_t), |
|
342 |
offsetof(cont_device_t, cond_next)); |
|
343 |
||
0 | 344 |
i_ddi_set_node_state((dev_info_t *)devi, DS_PROTO); |
345 |
da_log_enter((dev_info_t *)devi); |
|
346 |
return ((dev_info_t *)devi); |
|
347 |
||
348 |
fail: |
|
349 |
if (devi->devi_sys_prop_ptr) |
|
350 |
i_ddi_prop_list_delete(devi->devi_sys_prop_ptr); |
|
351 |
if (devi->devi_node_name) |
|
352 |
kmem_free(devi->devi_node_name, strlen(node_name) + 1); |
|
353 |
if (devi->devi_audit) |
|
354 |
kmem_free(devi->devi_audit, sizeof (devinfo_audit_t)); |
|
355 |
kmem_cache_free(ddi_node_cache, devi); |
|
356 |
cmn_err(CE_NOTE, failed); |
|
357 |
return (NULL); |
|
358 |
} |
|
359 |
||
360 |
/* |
|
361 |
* free a dev_info structure. |
|
362 |
* NB. Not callable from interrupt since impl_ddi_free_nodeid may block. |
|
363 |
*/ |
|
364 |
void |
|
365 |
i_ddi_free_node(dev_info_t *dip) |
|
366 |
{ |
|
367 |
struct dev_info *devi = DEVI(dip); |
|
368 |
struct devi_nodeid *elem; |
|
369 |
||
370 |
ASSERT(devi->devi_ref == 0); |
|
371 |
ASSERT(devi->devi_addr == NULL); |
|
372 |
ASSERT(devi->devi_node_state == DS_PROTO); |
|
373 |
ASSERT(devi->devi_child == NULL); |
|
374 |
||
439 | 375 |
/* free devi_addr_buf allocated by ddi_set_name_addr() */ |
376 |
if (devi->devi_addr_buf) |
|
377 |
kmem_free(devi->devi_addr_buf, 2 * MAXNAMELEN); |
|
0 | 378 |
|
379 |
if (i_ndi_dev_is_auto_assigned_node(dip)) |
|
380 |
impl_ddi_free_nodeid(DEVI(dip)->devi_nodeid); |
|
381 |
||
382 |
if (ndi_dev_is_persistent_node(dip)) { |
|
383 |
mutex_enter(&devimap->dno_lock); |
|
384 |
ASSERT(devimap->dno_free); |
|
385 |
elem = devimap->dno_free; |
|
386 |
devimap->dno_free = elem->next; |
|
387 |
mutex_exit(&devimap->dno_lock); |
|
388 |
kmem_free(elem, sizeof (*elem)); |
|
389 |
} |
|
390 |
||
391 |
if (DEVI(dip)->devi_compat_names) |
|
392 |
kmem_free(DEVI(dip)->devi_compat_names, |
|
393 |
DEVI(dip)->devi_compat_length); |
|
4145 | 394 |
if (DEVI(dip)->devi_rebinding_name) |
395 |
kmem_free(DEVI(dip)->devi_rebinding_name, |
|
396 |
strlen(DEVI(dip)->devi_rebinding_name) + 1); |
|
0 | 397 |
|
398 |
ddi_prop_remove_all(dip); /* remove driver properties */ |
|
399 |
if (devi->devi_sys_prop_ptr) |
|
400 |
i_ddi_prop_list_delete(devi->devi_sys_prop_ptr); |
|
401 |
if (devi->devi_hw_prop_ptr) |
|
402 |
i_ddi_prop_list_delete(devi->devi_hw_prop_ptr); |
|
403 |
||
404 |
i_ddi_set_node_state(dip, DS_INVAL); |
|
405 |
da_log_enter(dip); |
|
406 |
if (devi->devi_audit) { |
|
407 |
kmem_free(devi->devi_audit, sizeof (devinfo_audit_t)); |
|
408 |
} |
|
409 |
if (devi->devi_device_class) |
|
410 |
kmem_free(devi->devi_device_class, |
|
411 |
strlen(devi->devi_device_class) + 1); |
|
412 |
cv_destroy(&(devi->devi_cv)); |
|
413 |
mutex_destroy(&(devi->devi_lock)); |
|
414 |
mutex_destroy(&(devi->devi_pm_lock)); |
|
415 |
mutex_destroy(&(devi->devi_pm_busy_lock)); |
|
416 |
||
4845 | 417 |
RIO_TRACE((CE_NOTE, "i_ddi_free_node: destroying contract fields: " |
418 |
"dip=%p", (void *)dip)); |
|
419 |
contract_device_remove_dip(dip); |
|
420 |
ASSERT(devi->devi_ct_count == -1); |
|
421 |
ASSERT(list_is_empty(&(devi->devi_ct))); |
|
422 |
cv_destroy(&(devi->devi_ct_cv)); |
|
423 |
list_destroy(&(devi->devi_ct)); |
|
424 |
/* free this last since contract_device_remove_dip() uses it */ |
|
425 |
mutex_destroy(&(devi->devi_ct_lock)); |
|
426 |
RIO_TRACE((CE_NOTE, "i_ddi_free_node: destroyed all contract fields: " |
|
427 |
"dip=%p, name=%s", (void *)dip, devi->devi_node_name)); |
|
428 |
||
429 |
kmem_free(devi->devi_node_name, strlen(devi->devi_node_name) + 1); |
|
430 |
||
0 | 431 |
kmem_cache_free(ddi_node_cache, devi); |
432 |
} |
|
433 |
||
434 |
||
435 |
/* |
|
436 |
* Node state transitions |
|
437 |
*/ |
|
438 |
||
439 |
/* |
|
440 |
* Change the node name |
|
441 |
*/ |
|
442 |
int |
|
443 |
ndi_devi_set_nodename(dev_info_t *dip, char *name, int flags) |
|
444 |
{ |
|
445 |
_NOTE(ARGUNUSED(flags)) |
|
446 |
char *nname, *oname; |
|
447 |
||
448 |
ASSERT(dip && name); |
|
449 |
||
450 |
oname = DEVI(dip)->devi_node_name; |
|
451 |
if (strcmp(oname, name) == 0) |
|
452 |
return (DDI_SUCCESS); |
|
453 |
||
454 |
/* |
|
455 |
* pcicfg_fix_ethernet requires a name change after node |
|
456 |
* is linked into the tree. When pcicfg is fixed, we |
|
457 |
* should only allow name change in DS_PROTO state. |
|
458 |
*/ |
|
459 |
if (i_ddi_node_state(dip) >= DS_BOUND) { |
|
460 |
/* |
|
461 |
* Don't allow name change once node is bound |
|
462 |
*/ |
|
463 |
cmn_err(CE_NOTE, |
|
464 |
"ndi_devi_set_nodename: node already bound dip = %p," |
|
465 |
" %s -> %s", (void *)dip, ddi_node_name(dip), name); |
|
466 |
return (NDI_FAILURE); |
|
467 |
} |
|
468 |
||
469 |
nname = i_ddi_strdup(name, KM_SLEEP); |
|
470 |
DEVI(dip)->devi_node_name = nname; |
|
471 |
i_ddi_set_binding_name(dip, nname); |
|
472 |
kmem_free(oname, strlen(oname) + 1); |
|
473 |
||
474 |
da_log_enter(dip); |
|
475 |
return (NDI_SUCCESS); |
|
476 |
} |
|
477 |
||
478 |
void |
|
479 |
i_ddi_add_devimap(dev_info_t *dip) |
|
480 |
{ |
|
481 |
struct devi_nodeid *elem; |
|
482 |
||
483 |
ASSERT(dip); |
|
484 |
||
485 |
if (!ndi_dev_is_persistent_node(dip)) |
|
486 |
return; |
|
487 |
||
488 |
ASSERT(ddi_get_parent(dip) == NULL || (DEVI_VHCI_NODE(dip)) || |
|
489 |
DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
|
490 |
||
491 |
mutex_enter(&devimap->dno_lock); |
|
492 |
||
493 |
ASSERT(devimap->dno_free); |
|
494 |
||
495 |
elem = devimap->dno_free; |
|
496 |
devimap->dno_free = elem->next; |
|
497 |
||
498 |
elem->nodeid = ddi_get_nodeid(dip); |
|
499 |
elem->dip = dip; |
|
500 |
elem->next = devimap->dno_head; |
|
501 |
devimap->dno_head = elem; |
|
502 |
||
503 |
devimap->dno_list_length++; |
|
504 |
||
505 |
mutex_exit(&devimap->dno_lock); |
|
506 |
} |
|
507 |
||
508 |
static int |
|
509 |
i_ddi_remove_devimap(dev_info_t *dip) |
|
510 |
{ |
|
511 |
struct devi_nodeid *prev, *elem; |
|
512 |
static const char *fcn = "i_ddi_remove_devimap"; |
|
513 |
||
514 |
ASSERT(dip); |
|
515 |
||
516 |
if (!ndi_dev_is_persistent_node(dip)) |
|
517 |
return (DDI_SUCCESS); |
|
518 |
||
519 |
mutex_enter(&devimap->dno_lock); |
|
520 |
||
521 |
/* |
|
522 |
* The following check is done with dno_lock held |
|
523 |
* to prevent race between dip removal and |
|
524 |
* e_ddi_prom_node_to_dip() |
|
525 |
*/ |
|
526 |
if (e_ddi_devi_holdcnt(dip)) { |
|
527 |
mutex_exit(&devimap->dno_lock); |
|
528 |
return (DDI_FAILURE); |
|
529 |
} |
|
530 |
||
531 |
ASSERT(devimap->dno_head); |
|
532 |
ASSERT(devimap->dno_list_length > 0); |
|
533 |
||
534 |
prev = NULL; |
|
535 |
for (elem = devimap->dno_head; elem; elem = elem->next) { |
|
536 |
if (elem->dip == dip) { |
|
537 |
ASSERT(elem->nodeid == ddi_get_nodeid(dip)); |
|
538 |
break; |
|
539 |
} |
|
540 |
prev = elem; |
|
541 |
} |
|
542 |
||
543 |
if (elem && prev) |
|
544 |
prev->next = elem->next; |
|
545 |
else if (elem) |
|
546 |
devimap->dno_head = elem->next; |
|
547 |
else |
|
548 |
panic("%s: devinfo node(%p) not found", |
|
549 |
fcn, (void *)dip); |
|
550 |
||
551 |
devimap->dno_list_length--; |
|
552 |
||
553 |
elem->nodeid = 0; |
|
554 |
elem->dip = NULL; |
|
555 |
||
556 |
elem->next = devimap->dno_free; |
|
557 |
devimap->dno_free = elem; |
|
558 |
||
559 |
mutex_exit(&devimap->dno_lock); |
|
560 |
||
561 |
return (DDI_SUCCESS); |
|
562 |
} |
|
563 |
||
564 |
/* |
|
565 |
* Link this node into the devinfo tree and add to orphan list |
|
566 |
* Not callable from interrupt context |
|
567 |
*/ |
|
568 |
static void |
|
569 |
link_node(dev_info_t *dip) |
|
570 |
{ |
|
571 |
struct dev_info *devi = DEVI(dip); |
|
572 |
struct dev_info *parent = devi->devi_parent; |
|
573 |
dev_info_t **dipp; |
|
574 |
||
575 |
ASSERT(parent); /* never called for root node */ |
|
576 |
||
577 |
NDI_CONFIG_DEBUG((CE_CONT, "link_node: parent = %s child = %s\n", |
|
578 |
parent->devi_node_name, devi->devi_node_name)); |
|
579 |
||
580 |
/* |
|
581 |
* Hold the global_vhci_lock before linking any direct |
|
582 |
* children of rootnex driver. This special lock protects |
|
583 |
* linking and unlinking for rootnext direct children. |
|
584 |
*/ |
|
585 |
if ((dev_info_t *)parent == ddi_root_node()) |
|
586 |
mutex_enter(&global_vhci_lock); |
|
587 |
||
588 |
/* |
|
589 |
* attach the node to end of the list unless the node is already there |
|
590 |
*/ |
|
591 |
dipp = (dev_info_t **)(&DEVI(parent)->devi_child); |
|
592 |
while (*dipp && (*dipp != dip)) { |
|
593 |
dipp = (dev_info_t **)(&DEVI(*dipp)->devi_sibling); |
|
594 |
} |
|
595 |
ASSERT(*dipp == NULL); /* node is not linked */ |
|
596 |
||
597 |
/* |
|
598 |
* Now that we are in the tree, update the devi-nodeid map. |
|
599 |
*/ |
|
600 |
i_ddi_add_devimap(dip); |
|
601 |
||
602 |
/* |
|
603 |
* This is a temporary workaround for Bug 4618861. |
|
604 |
* We keep the scsi_vhci nexus node on the left side of the devinfo |
|
605 |
* tree (under the root nexus driver), so that virtual nodes under |
|
606 |
* scsi_vhci will be SUSPENDed first and RESUMEd last. This ensures |
|
607 |
* that the pHCI nodes are active during times when their clients |
|
608 |
* may be depending on them. This workaround embodies the knowledge |
|
609 |
* that system PM and CPR both traverse the tree left-to-right during |
|
610 |
* SUSPEND and right-to-left during RESUME. |
|
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
611 |
* Extending the workaround to IB Nexus/VHCI |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
612 |
* driver also. |
0 | 613 |
*/ |
4145 | 614 |
if (strcmp(devi->devi_binding_name, "scsi_vhci") == 0) { |
0 | 615 |
/* Add scsi_vhci to beginning of list */ |
616 |
ASSERT((dev_info_t *)parent == top_devinfo); |
|
617 |
/* scsi_vhci under rootnex */ |
|
618 |
devi->devi_sibling = parent->devi_child; |
|
619 |
parent->devi_child = devi; |
|
4145 | 620 |
} else if (strcmp(devi->devi_binding_name, "ib") == 0) { |
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
621 |
i_link_vhci_node(dip); |
0 | 622 |
} else { |
623 |
/* Add to end of list */ |
|
624 |
*dipp = dip; |
|
625 |
DEVI(dip)->devi_sibling = NULL; |
|
626 |
} |
|
627 |
||
628 |
/* |
|
629 |
* Release the global_vhci_lock before linking any direct |
|
630 |
* children of rootnex driver. |
|
631 |
*/ |
|
632 |
if ((dev_info_t *)parent == ddi_root_node()) |
|
633 |
mutex_exit(&global_vhci_lock); |
|
634 |
||
635 |
/* persistent nodes go on orphan list */ |
|
636 |
if (ndi_dev_is_persistent_node(dip)) |
|
637 |
add_to_dn_list(&orphanlist, dip); |
|
638 |
} |
|
639 |
||
640 |
/* |
|
641 |
* Unlink this node from the devinfo tree |
|
642 |
*/ |
|
643 |
static int |
|
644 |
unlink_node(dev_info_t *dip) |
|
645 |
{ |
|
646 |
struct dev_info *devi = DEVI(dip); |
|
647 |
struct dev_info *parent = devi->devi_parent; |
|
648 |
dev_info_t **dipp; |
|
649 |
||
650 |
ASSERT(parent != NULL); |
|
651 |
ASSERT(devi->devi_node_state == DS_LINKED); |
|
652 |
||
653 |
NDI_CONFIG_DEBUG((CE_CONT, "unlink_node: name = %s\n", |
|
654 |
ddi_node_name(dip))); |
|
655 |
||
656 |
/* check references */ |
|
657 |
if (devi->devi_ref || i_ddi_remove_devimap(dip) != DDI_SUCCESS) |
|
658 |
return (DDI_FAILURE); |
|
659 |
||
660 |
/* |
|
661 |
* Hold the global_vhci_lock before linking any direct |
|
662 |
* children of rootnex driver. |
|
663 |
*/ |
|
664 |
if ((dev_info_t *)parent == ddi_root_node()) |
|
665 |
mutex_enter(&global_vhci_lock); |
|
666 |
||
667 |
dipp = (dev_info_t **)(&DEVI(parent)->devi_child); |
|
668 |
while (*dipp && (*dipp != dip)) { |
|
669 |
dipp = (dev_info_t **)(&DEVI(*dipp)->devi_sibling); |
|
670 |
} |
|
671 |
if (*dipp) { |
|
672 |
*dipp = (dev_info_t *)(devi->devi_sibling); |
|
673 |
devi->devi_sibling = NULL; |
|
674 |
} else { |
|
675 |
NDI_CONFIG_DEBUG((CE_NOTE, "unlink_node: %s not linked", |
|
676 |
devi->devi_node_name)); |
|
677 |
} |
|
678 |
||
679 |
/* |
|
680 |
* Release the global_vhci_lock before linking any direct |
|
681 |
* children of rootnex driver. |
|
682 |
*/ |
|
683 |
if ((dev_info_t *)parent == ddi_root_node()) |
|
684 |
mutex_exit(&global_vhci_lock); |
|
685 |
||
686 |
/* Remove node from orphan list */ |
|
687 |
if (ndi_dev_is_persistent_node(dip)) { |
|
688 |
remove_from_dn_list(&orphanlist, dip); |
|
689 |
} |
|
690 |
||
691 |
return (DDI_SUCCESS); |
|
692 |
} |
|
693 |
||
694 |
/* |
|
695 |
* Bind this devinfo node to a driver. If compat is NON-NULL, try that first. |
|
696 |
* Else, use the node-name. |
|
697 |
* |
|
698 |
* NOTE: IEEE1275 specifies that nodename should be tried before compatible. |
|
699 |
* Solaris implementation binds nodename after compatible. |
|
700 |
* |
|
701 |
* If we find a binding, |
|
702 |
* - set the binding name to the the string, |
|
703 |
* - set major number to driver major |
|
704 |
* |
|
705 |
* If we don't find a binding, |
|
706 |
* - return failure |
|
707 |
*/ |
|
708 |
static int |
|
709 |
bind_node(dev_info_t *dip) |
|
710 |
{ |
|
711 |
char *p = NULL; |
|
712 |
major_t major = (major_t)(major_t)-1; |
|
713 |
struct dev_info *devi = DEVI(dip); |
|
714 |
dev_info_t *parent = ddi_get_parent(dip); |
|
715 |
||
716 |
ASSERT(devi->devi_node_state == DS_LINKED); |
|
717 |
||
718 |
NDI_CONFIG_DEBUG((CE_CONT, "bind_node: 0x%p(name = %s)\n", |
|
719 |
(void *)dip, ddi_node_name(dip))); |
|
720 |
||
721 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
722 |
if (DEVI(dip)->devi_flags & DEVI_NO_BIND) { |
|
723 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
724 |
return (DDI_FAILURE); |
|
725 |
} |
|
726 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
727 |
||
728 |
/* find the driver with most specific binding using compatible */ |
|
729 |
major = ddi_compatible_driver_major(dip, &p); |
|
730 |
if (major == (major_t)-1) |
|
731 |
return (DDI_FAILURE); |
|
732 |
||
733 |
devi->devi_major = major; |
|
734 |
if (p != NULL) { |
|
735 |
i_ddi_set_binding_name(dip, p); |
|
736 |
NDI_CONFIG_DEBUG((CE_CONT, "bind_node: %s bound to %s\n", |
|
737 |
devi->devi_node_name, p)); |
|
738 |
} |
|
739 |
||
740 |
/* Link node to per-driver list */ |
|
741 |
link_to_driver_list(dip); |
|
742 |
||
743 |
/* |
|
744 |
* reset parent flag so that nexus will merge .conf props |
|
745 |
*/ |
|
746 |
if (ndi_dev_is_persistent_node(dip)) { |
|
747 |
mutex_enter(&DEVI(parent)->devi_lock); |
|
748 |
DEVI(parent)->devi_flags &= |
|
749 |
~(DEVI_ATTACHED_CHILDREN|DEVI_MADE_CHILDREN); |
|
750 |
mutex_exit(&DEVI(parent)->devi_lock); |
|
751 |
} |
|
752 |
return (DDI_SUCCESS); |
|
753 |
} |
|
754 |
||
755 |
/* |
|
756 |
* Unbind this devinfo node |
|
757 |
* Called before the node is destroyed or driver is removed from system |
|
758 |
*/ |
|
759 |
static int |
|
760 |
unbind_node(dev_info_t *dip) |
|
761 |
{ |
|
762 |
ASSERT(DEVI(dip)->devi_node_state == DS_BOUND); |
|
763 |
ASSERT(DEVI(dip)->devi_major != (major_t)-1); |
|
764 |
||
765 |
/* check references */ |
|
766 |
if (DEVI(dip)->devi_ref) |
|
767 |
return (DDI_FAILURE); |
|
768 |
||
769 |
NDI_CONFIG_DEBUG((CE_CONT, "unbind_node: 0x%p(name = %s)\n", |
|
770 |
(void *)dip, ddi_node_name(dip))); |
|
771 |
||
772 |
unlink_from_driver_list(dip); |
|
4145 | 773 |
|
0 | 774 |
DEVI(dip)->devi_major = (major_t)-1; |
4145 | 775 |
DEVI(dip)->devi_binding_name = DEVI(dip)->devi_node_name; |
0 | 776 |
return (DDI_SUCCESS); |
777 |
} |
|
778 |
||
779 |
/* |
|
780 |
* Initialize a node: calls the parent nexus' bus_ctl ops to do the operation. |
|
781 |
* Must hold parent and per-driver list while calling this function. |
|
782 |
* A successful init_node() returns with an active ndi_hold_devi() hold on |
|
783 |
* the parent. |
|
784 |
*/ |
|
785 |
static int |
|
786 |
init_node(dev_info_t *dip) |
|
787 |
{ |
|
788 |
int error; |
|
789 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
790 |
int (*f)(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); |
|
791 |
char *path; |
|
4145 | 792 |
major_t major; |
0 | 793 |
|
794 |
ASSERT(i_ddi_node_state(dip) == DS_BOUND); |
|
795 |
||
796 |
/* should be DS_READY except for pcmcia ... */ |
|
797 |
ASSERT(i_ddi_node_state(pdip) >= DS_PROBED); |
|
798 |
||
799 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
800 |
(void) ddi_pathname(dip, path); |
|
801 |
NDI_CONFIG_DEBUG((CE_CONT, "init_node: entry: path %s 0x%p\n", |
|
802 |
path, (void *)dip)); |
|
803 |
||
804 |
/* |
|
805 |
* The parent must have a bus_ctl operation. |
|
806 |
*/ |
|
807 |
if ((DEVI(pdip)->devi_ops->devo_bus_ops == NULL) || |
|
808 |
(f = DEVI(pdip)->devi_ops->devo_bus_ops->bus_ctl) == NULL) { |
|
809 |
error = DDI_FAILURE; |
|
810 |
goto out; |
|
811 |
} |
|
812 |
||
813 |
add_global_props(dip); |
|
814 |
||
815 |
/* |
|
816 |
* Invoke the parent's bus_ctl operation with the DDI_CTLOPS_INITCHILD |
|
817 |
* command to transform the child to canonical form 1. If there |
|
818 |
* is an error, ddi_remove_child should be called, to clean up. |
|
819 |
*/ |
|
820 |
error = (*f)(pdip, pdip, DDI_CTLOPS_INITCHILD, dip, NULL); |
|
821 |
if (error != DDI_SUCCESS) { |
|
822 |
NDI_CONFIG_DEBUG((CE_CONT, "init_node: %s 0x%p failed\n", |
|
823 |
path, (void *)dip)); |
|
824 |
remove_global_props(dip); |
|
825 |
/* in case nexus driver didn't clear this field */ |
|
826 |
ddi_set_name_addr(dip, NULL); |
|
827 |
error = DDI_FAILURE; |
|
828 |
goto out; |
|
829 |
} |
|
830 |
||
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
831 |
ndi_hold_devi(pdip); /* initial hold of parent */ |
0 | 832 |
|
4145 | 833 |
/* recompute path after initchild for @addr information */ |
834 |
(void) ddi_pathname(dip, path); |
|
835 |
||
836 |
/* Check for duplicate nodes */ |
|
0 | 837 |
if (find_duplicate_child(pdip, dip) != NULL) { |
838 |
/* |
|
839 |
* uninit_node() the duplicate - a successful uninit_node() |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
840 |
* will release inital hold of parent using ndi_rele_devi(). |
0 | 841 |
*/ |
842 |
if ((error = uninit_node(dip)) != DDI_SUCCESS) { |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
843 |
ndi_rele_devi(pdip); /* release initial hold */ |
0 | 844 |
cmn_err(CE_WARN, "init_node: uninit of duplicate " |
845 |
"node %s failed", path); |
|
846 |
} |
|
847 |
NDI_CONFIG_DEBUG((CE_CONT, "init_node: duplicate uninit " |
|
848 |
"%s 0x%p%s\n", path, (void *)dip, |
|
849 |
(error == DDI_SUCCESS) ? "" : " failed")); |
|
850 |
error = DDI_FAILURE; |
|
851 |
goto out; |
|
852 |
} |
|
853 |
||
854 |
/* |
|
4145 | 855 |
* Check to see if we have a path-oriented driver alias that overrides |
856 |
* the current driver binding. If so, we need to rebind. This check |
|
857 |
* needs to be delayed until after a successful DDI_CTLOPS_INITCHILD, |
|
858 |
* so the unit-address is established on the last component of the path. |
|
859 |
* |
|
860 |
* NOTE: Allowing a path-oriented alias to change the driver binding |
|
861 |
* of a driver.conf node results in non-intuitive property behavior. |
|
862 |
* We provide a tunable (driver_conf_allow_path_alias) to control |
|
863 |
* this behavior. See uninit_node() for more details. |
|
864 |
* |
|
865 |
* NOTE: If you are adding a path-oriented alias for the boot device, |
|
866 |
* and there is mismatch between OBP and the kernel in regard to |
|
867 |
* generic name use, like "disk" .vs. "ssd", then you will need |
|
868 |
* to add a path-oriented alias for both paths. |
|
869 |
*/ |
|
870 |
major = ddi_name_to_major(path); |
|
871 |
if ((major != (major_t)-1) && |
|
872 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED) && |
|
873 |
(major != DEVI(dip)->devi_major) && |
|
874 |
(ndi_dev_is_persistent_node(dip) || driver_conf_allow_path_alias)) { |
|
875 |
||
876 |
/* Mark node for rebind processing. */ |
|
877 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
878 |
DEVI(dip)->devi_flags |= DEVI_REBIND; |
|
879 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
880 |
||
881 |
/* |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
882 |
* Add an extra hold on the parent to prevent it from ever |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
883 |
* having a zero devi_ref during the child rebind process. |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
884 |
* This is necessary to ensure that the parent will never |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
885 |
* detach(9E) during the rebind. |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
886 |
*/ |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
887 |
ndi_hold_devi(pdip); /* extra hold of parent */ |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
888 |
|
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
889 |
/* |
4145 | 890 |
* uninit_node() current binding - a successful uninit_node() |
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
891 |
* will release extra hold of parent using ndi_rele_devi(). |
4145 | 892 |
*/ |
893 |
if ((error = uninit_node(dip)) != DDI_SUCCESS) { |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
894 |
ndi_rele_devi(pdip); /* release extra hold */ |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
895 |
ndi_rele_devi(pdip); /* release initial hold */ |
4145 | 896 |
cmn_err(CE_WARN, "init_node: uninit for rebind " |
897 |
"of node %s failed", path); |
|
898 |
goto out; |
|
899 |
} |
|
900 |
||
901 |
/* Unbind: demote the node back to DS_LINKED. */ |
|
902 |
if ((error = ndi_devi_unbind_driver(dip)) != DDI_SUCCESS) { |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
903 |
ndi_rele_devi(pdip); /* relrease initial hold */ |
4145 | 904 |
cmn_err(CE_WARN, "init_node: unbind for rebind " |
905 |
"of node %s failed", path); |
|
906 |
goto out; |
|
907 |
} |
|
908 |
||
909 |
/* establish rebinding name */ |
|
910 |
if (DEVI(dip)->devi_rebinding_name == NULL) |
|
911 |
DEVI(dip)->devi_rebinding_name = |
|
912 |
i_ddi_strdup(path, KM_SLEEP); |
|
913 |
||
914 |
/* |
|
915 |
* Now that we are demoted and marked for rebind, repromote. |
|
916 |
* We need to do this in steps, instead of just calling |
|
917 |
* ddi_initchild, so that we can redo the merge operation |
|
918 |
* after we are rebound to the path-bound driver. |
|
919 |
* |
|
920 |
* Start by rebinding node to the path-bound driver. |
|
921 |
*/ |
|
922 |
if ((error = ndi_devi_bind_driver(dip, 0)) != DDI_SUCCESS) { |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
923 |
ndi_rele_devi(pdip); /* relrease initial hold */ |
4145 | 924 |
cmn_err(CE_WARN, "init_node: rebind " |
925 |
"of node %s failed", path); |
|
926 |
goto out; |
|
927 |
} |
|
928 |
||
929 |
/* |
|
930 |
* If the node is not a driver.conf node then merge |
|
931 |
* driver.conf properties from new path-bound driver.conf. |
|
932 |
*/ |
|
933 |
if (ndi_dev_is_persistent_node(dip)) |
|
934 |
(void) i_ndi_make_spec_children(pdip, 0); |
|
935 |
||
936 |
/* |
|
937 |
* Now that we have taken care of merge, repromote back |
|
938 |
* to DS_INITIALIZED. |
|
939 |
*/ |
|
940 |
error = ddi_initchild(pdip, dip); |
|
941 |
NDI_CONFIG_DEBUG((CE_CONT, "init_node: rebind " |
|
942 |
"%s 0x%p\n", path, (void *)dip)); |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
943 |
|
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
944 |
/* |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
945 |
* Release our initial hold. If ddi_initchild() was |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
946 |
* successfull then it will return with the active hold. |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
947 |
*/ |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
948 |
ndi_rele_devi(pdip); |
4145 | 949 |
goto out; |
950 |
} |
|
951 |
||
952 |
/* |
|
0 | 953 |
* Apply multi-parent/deep-nexus optimization to the new node |
954 |
*/ |
|
955 |
DEVI(dip)->devi_instance = e_ddi_assign_instance(dip); |
|
956 |
ddi_optimize_dtree(dip); |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
957 |
error = DDI_SUCCESS; /* return with active hold */ |
0 | 958 |
|
4145 | 959 |
out: if (error != DDI_SUCCESS) { |
960 |
/* On failure ensure that DEVI_REBIND is cleared */ |
|
961 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
962 |
DEVI(dip)->devi_flags &= ~DEVI_REBIND; |
|
963 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
964 |
} |
|
965 |
kmem_free(path, MAXPATHLEN); |
|
0 | 966 |
return (error); |
967 |
} |
|
968 |
||
969 |
/* |
|
970 |
* Uninitialize node |
|
971 |
* The per-driver list must be held busy during the call. |
|
972 |
* A successful uninit_node() releases the init_node() hold on |
|
973 |
* the parent by calling ndi_rele_devi(). |
|
974 |
*/ |
|
975 |
static int |
|
976 |
uninit_node(dev_info_t *dip) |
|
977 |
{ |
|
978 |
int node_state_entry; |
|
979 |
dev_info_t *pdip; |
|
980 |
struct dev_ops *ops; |
|
981 |
int (*f)(); |
|
982 |
int error; |
|
983 |
char *addr; |
|
984 |
||
985 |
/* |
|
986 |
* Don't check for references here or else a ref-counted |
|
987 |
* dip cannot be downgraded by the framework. |
|
988 |
*/ |
|
989 |
node_state_entry = i_ddi_node_state(dip); |
|
990 |
ASSERT((node_state_entry == DS_BOUND) || |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
991 |
(node_state_entry == DS_INITIALIZED)); |
0 | 992 |
pdip = ddi_get_parent(dip); |
993 |
ASSERT(pdip); |
|
994 |
||
995 |
NDI_CONFIG_DEBUG((CE_CONT, "uninit_node: 0x%p(%s%d)\n", |
|
996 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
997 |
||
998 |
if (((ops = ddi_get_driver(pdip)) == NULL) || |
|
999 |
(ops->devo_bus_ops == NULL) || |
|
1000 |
((f = ops->devo_bus_ops->bus_ctl) == NULL)) { |
|
1001 |
return (DDI_FAILURE); |
|
1002 |
} |
|
1003 |
||
1004 |
/* |
|
1005 |
* save the @addr prior to DDI_CTLOPS_UNINITCHILD for use in |
|
1006 |
* freeing the instance if it succeeds. |
|
1007 |
*/ |
|
1008 |
if (node_state_entry == DS_INITIALIZED) { |
|
1009 |
addr = ddi_get_name_addr(dip); |
|
1010 |
if (addr) |
|
1011 |
addr = i_ddi_strdup(addr, KM_SLEEP); |
|
1012 |
} else { |
|
1013 |
addr = NULL; |
|
1014 |
} |
|
1015 |
||
1016 |
error = (*f)(pdip, pdip, DDI_CTLOPS_UNINITCHILD, dip, (void *)NULL); |
|
1017 |
if (error == DDI_SUCCESS) { |
|
1018 |
/* if uninitchild forgot to set devi_addr to NULL do it now */ |
|
1019 |
ddi_set_name_addr(dip, NULL); |
|
1020 |
||
1021 |
/* |
|
1022 |
* Free instance number. This is a no-op if instance has |
|
1023 |
* been kept by probe_node(). Avoid free when we are called |
|
1024 |
* from init_node (DS_BOUND) because the instance has not yet |
|
1025 |
* been assigned. |
|
1026 |
*/ |
|
1027 |
if (node_state_entry == DS_INITIALIZED) { |
|
1028 |
e_ddi_free_instance(dip, addr); |
|
1029 |
DEVI(dip)->devi_instance = -1; |
|
1030 |
} |
|
1031 |
||
1032 |
/* release the init_node hold */ |
|
1033 |
ndi_rele_devi(pdip); |
|
1034 |
||
1035 |
remove_global_props(dip); |
|
4145 | 1036 |
|
1037 |
/* |
|
1038 |
* NOTE: The decision on whether to allow a path-oriented |
|
1039 |
* rebind of a driver.conf enumerated node is made by |
|
1040 |
* init_node() based on driver_conf_allow_path_alias. The |
|
1041 |
* rebind code below prevents deletion of system properties |
|
1042 |
* on driver.conf nodes. |
|
1043 |
* |
|
1044 |
* When driver_conf_allow_path_alias is set, property behavior |
|
1045 |
* on rebound driver.conf file is non-intuitive. For a |
|
1046 |
* driver.conf node, the unit-address properties come from |
|
1047 |
* the driver.conf file as system properties. Removing system |
|
1048 |
* properties from a driver.conf node makes the node |
|
1049 |
* useless (we get node without unit-address properties) - so |
|
1050 |
* we leave system properties in place. The result is a node |
|
1051 |
* where system properties come from the node being rebound, |
|
1052 |
* and global properties come from the driver.conf file |
|
1053 |
* of the driver we are rebinding to. If we could determine |
|
1054 |
* that the path-oriented alias driver.conf file defined a |
|
1055 |
* node at the same unit address, it would be best to use |
|
1056 |
* that node and avoid the non-intuitive property behavior. |
|
1057 |
* Unfortunately, the current "merge" code does not support |
|
1058 |
* this, so we live with the non-intuitive property behavior. |
|
1059 |
*/ |
|
1060 |
if (!((ndi_dev_is_persistent_node(dip) == 0) && |
|
1061 |
(DEVI(dip)->devi_flags & DEVI_REBIND))) |
|
1062 |
e_ddi_prop_remove_all(dip); |
|
0 | 1063 |
} else { |
1064 |
NDI_CONFIG_DEBUG((CE_CONT, "uninit_node failed: 0x%p(%s%d)\n", |
|
1065 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1066 |
} |
|
1067 |
||
1068 |
if (addr) |
|
1069 |
kmem_free(addr, strlen(addr) + 1); |
|
1070 |
return (error); |
|
1071 |
} |
|
1072 |
||
1073 |
/* |
|
1074 |
* Invoke driver's probe entry point to probe for existence of hardware. |
|
1075 |
* Keep instance permanent for successful probe and leaf nodes. |
|
1076 |
* |
|
1077 |
* Per-driver list must be held busy while calling this function. |
|
1078 |
*/ |
|
1079 |
static int |
|
1080 |
probe_node(dev_info_t *dip) |
|
1081 |
{ |
|
1082 |
int rv; |
|
1083 |
||
1084 |
ASSERT(i_ddi_node_state(dip) == DS_INITIALIZED); |
|
1085 |
||
1086 |
NDI_CONFIG_DEBUG((CE_CONT, "probe_node: 0x%p(%s%d)\n", |
|
1087 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1088 |
||
1089 |
/* temporarily hold the driver while we probe */ |
|
1090 |
DEVI(dip)->devi_ops = ndi_hold_driver(dip); |
|
1091 |
if (DEVI(dip)->devi_ops == NULL) { |
|
1092 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
1093 |
"probe_node: 0x%p(%s%d) cannot load driver\n", |
|
1094 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1095 |
return (DDI_FAILURE); |
|
1096 |
} |
|
1097 |
||
1098 |
if (identify_9e != 0) |
|
1099 |
(void) devi_identify(dip); |
|
1100 |
||
1101 |
rv = devi_probe(dip); |
|
1102 |
||
1103 |
/* release the driver now that probe is complete */ |
|
1104 |
ndi_rele_driver(dip); |
|
1105 |
DEVI(dip)->devi_ops = NULL; |
|
1106 |
||
1107 |
switch (rv) { |
|
1108 |
case DDI_PROBE_SUCCESS: /* found */ |
|
1109 |
case DDI_PROBE_DONTCARE: /* ddi_dev_is_sid */ |
|
1110 |
e_ddi_keep_instance(dip); /* persist instance */ |
|
1111 |
rv = DDI_SUCCESS; |
|
1112 |
break; |
|
1113 |
||
1114 |
case DDI_PROBE_PARTIAL: /* maybe later */ |
|
1115 |
case DDI_PROBE_FAILURE: /* not found */ |
|
1116 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
1117 |
"probe_node: 0x%p(%s%d) no hardware found%s\n", |
|
1118 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip), |
|
1119 |
(rv == DDI_PROBE_PARTIAL) ? " yet" : "")); |
|
1120 |
rv = DDI_FAILURE; |
|
1121 |
break; |
|
1122 |
||
1123 |
default: |
|
1124 |
#ifdef DEBUG |
|
1125 |
cmn_err(CE_WARN, "probe_node: %s%d: illegal probe(9E) value", |
|
1126 |
ddi_driver_name(dip), ddi_get_instance(dip)); |
|
1127 |
#endif /* DEBUG */ |
|
1128 |
rv = DDI_FAILURE; |
|
1129 |
break; |
|
1130 |
} |
|
1131 |
return (rv); |
|
1132 |
} |
|
1133 |
||
1134 |
/* |
|
1135 |
* Unprobe a node. Simply reset the node state. |
|
1136 |
* Per-driver list must be held busy while calling this function. |
|
1137 |
*/ |
|
1138 |
static int |
|
1139 |
unprobe_node(dev_info_t *dip) |
|
1140 |
{ |
|
1141 |
ASSERT(i_ddi_node_state(dip) == DS_PROBED); |
|
1142 |
||
1143 |
/* |
|
1144 |
* Don't check for references here or else a ref-counted |
|
1145 |
* dip cannot be downgraded by the framework. |
|
1146 |
*/ |
|
1147 |
||
1148 |
NDI_CONFIG_DEBUG((CE_CONT, "unprobe_node: 0x%p(name = %s)\n", |
|
1149 |
(void *)dip, ddi_node_name(dip))); |
|
1150 |
return (DDI_SUCCESS); |
|
1151 |
} |
|
1152 |
||
1153 |
/* |
|
1154 |
* Attach devinfo node. |
|
1155 |
* Per-driver list must be held busy. |
|
1156 |
*/ |
|
1157 |
static int |
|
1158 |
attach_node(dev_info_t *dip) |
|
1159 |
{ |
|
1160 |
int rv; |
|
1161 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1162 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
0 | 1163 |
ASSERT(i_ddi_node_state(dip) == DS_PROBED); |
1164 |
||
1165 |
NDI_CONFIG_DEBUG((CE_CONT, "attach_node: 0x%p(%s%d)\n", |
|
1166 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1167 |
||
1168 |
/* |
|
1169 |
* Tell mpxio framework that a node is about to online. |
|
1170 |
*/ |
|
1171 |
if ((rv = mdi_devi_online(dip, 0)) != NDI_SUCCESS) { |
|
1172 |
return (DDI_FAILURE); |
|
1173 |
} |
|
1174 |
||
1175 |
/* no recursive attachment */ |
|
1176 |
ASSERT(DEVI(dip)->devi_ops == NULL); |
|
1177 |
||
1178 |
/* |
|
1179 |
* Hold driver the node is bound to. |
|
1180 |
*/ |
|
1181 |
DEVI(dip)->devi_ops = ndi_hold_driver(dip); |
|
1182 |
if (DEVI(dip)->devi_ops == NULL) { |
|
1183 |
/* |
|
1184 |
* We were able to load driver for probing, so we should |
|
1185 |
* not get here unless something really bad happened. |
|
1186 |
*/ |
|
1187 |
cmn_err(CE_WARN, "attach_node: no driver for major %d", |
|
1188 |
DEVI(dip)->devi_major); |
|
1189 |
return (DDI_FAILURE); |
|
1190 |
} |
|
1191 |
||
1192 |
if (NEXUS_DRV(DEVI(dip)->devi_ops)) |
|
1193 |
DEVI(dip)->devi_taskq = ddi_taskq_create(dip, |
|
1194 |
"nexus_enum_tq", 1, |
|
1195 |
TASKQ_DEFAULTPRI, 0); |
|
1196 |
||
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1197 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
2009 | 1198 |
DEVI_SET_ATTACHING(dip); |
0 | 1199 |
DEVI_SET_NEED_RESET(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1200 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1201 |
|
0 | 1202 |
rv = devi_attach(dip, DDI_ATTACH); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1203 |
|
2009 | 1204 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
1205 |
DEVI_CLR_ATTACHING(dip); |
|
1206 |
||
1961
cceb6bfa61a5
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
1826
diff
changeset
|
1207 |
if (rv != DDI_SUCCESS) { |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1208 |
DEVI_CLR_NEED_RESET(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1209 |
|
438
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1210 |
/* ensure that devids are unregistered */ |
0 | 1211 |
if (DEVI(dip)->devi_flags & DEVI_REGISTERED_DEVID) { |
438
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1212 |
DEVI(dip)->devi_flags &= ~DEVI_REGISTERED_DEVID; |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1213 |
mutex_exit(&DEVI(dip)->devi_lock); |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1214 |
|
0 | 1215 |
e_devid_cache_unregister(dip); |
438
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1216 |
} else |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1217 |
mutex_exit(&DEVI(dip)->devi_lock); |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1218 |
|
0 | 1219 |
/* |
1220 |
* Cleanup dacf reservations |
|
1221 |
*/ |
|
1222 |
mutex_enter(&dacf_lock); |
|
1223 |
dacf_clr_rsrvs(dip, DACF_OPID_POSTATTACH); |
|
1224 |
dacf_clr_rsrvs(dip, DACF_OPID_PREDETACH); |
|
1225 |
mutex_exit(&dacf_lock); |
|
1226 |
if (DEVI(dip)->devi_taskq) |
|
1227 |
ddi_taskq_destroy(DEVI(dip)->devi_taskq); |
|
1228 |
ddi_remove_minor_node(dip, NULL); |
|
1229 |
||
1230 |
/* release the driver if attach failed */ |
|
1231 |
ndi_rele_driver(dip); |
|
1232 |
DEVI(dip)->devi_ops = NULL; |
|
1233 |
NDI_CONFIG_DEBUG((CE_CONT, "attach_node: 0x%p(%s%d) failed\n", |
|
1234 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1235 |
return (DDI_FAILURE); |
|
2009 | 1236 |
} else |
1237 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
0 | 1238 |
|
1239 |
/* successful attach, return with driver held */ |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1240 |
|
0 | 1241 |
return (DDI_SUCCESS); |
1242 |
} |
|
1243 |
||
1244 |
/* |
|
1245 |
* Detach devinfo node. |
|
1246 |
* Per-driver list must be held busy. |
|
1247 |
*/ |
|
1248 |
static int |
|
1249 |
detach_node(dev_info_t *dip, uint_t flag) |
|
1250 |
{ |
|
53 | 1251 |
struct devnames *dnp; |
1252 |
int rv; |
|
1253 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1254 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
0 | 1255 |
ASSERT(i_ddi_node_state(dip) == DS_ATTACHED); |
1256 |
||
1257 |
/* check references */ |
|
1258 |
if (DEVI(dip)->devi_ref) |
|
1259 |
return (DDI_FAILURE); |
|
1260 |
||
1261 |
NDI_CONFIG_DEBUG((CE_CONT, "detach_node: 0x%p(%s%d)\n", |
|
1262 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1263 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1264 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1265 |
* NOTE: If we are processing a pHCI node then the calling code |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1266 |
* must detect this and ndi_devi_enter() in (vHCI, parent(pHCI)) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1267 |
* order unless pHCI and vHCI are siblings. Code paths leading |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1268 |
* here that must ensure this ordering include: |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1269 |
* unconfig_immediate_children(), devi_unconfig_one(), |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1270 |
* ndi_devi_unconfig_one(), ndi_devi_offline(). |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1271 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1272 |
ASSERT(!MDI_PHCI(dip) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1273 |
(ddi_get_parent(mdi_devi_get_vdip(dip)) == ddi_get_parent(dip)) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1274 |
DEVI_BUSY_OWNED(mdi_devi_get_vdip(dip))); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1275 |
|
0 | 1276 |
/* Offline the device node with the mpxio framework. */ |
1277 |
if (mdi_devi_offline(dip, flag) != NDI_SUCCESS) { |
|
1278 |
return (DDI_FAILURE); |
|
1279 |
} |
|
1280 |
||
1281 |
/* drain the taskq */ |
|
1282 |
if (DEVI(dip)->devi_taskq) |
|
1283 |
ddi_taskq_wait(DEVI(dip)->devi_taskq); |
|
1284 |
||
1285 |
rv = devi_detach(dip, DDI_DETACH); |
|
1286 |
||
1287 |
if (rv != DDI_SUCCESS) { |
|
1288 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
1289 |
"detach_node: 0x%p(%s%d) failed\n", |
|
1290 |
(void *)dip, ddi_driver_name(dip), ddi_get_instance(dip))); |
|
1291 |
return (DDI_FAILURE); |
|
1292 |
} |
|
1293 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1294 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1295 |
DEVI_CLR_NEED_RESET(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1296 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1297 |
|
0 | 1298 |
/* destroy the taskq */ |
1299 |
if (DEVI(dip)->devi_taskq) { |
|
1300 |
ddi_taskq_destroy(DEVI(dip)->devi_taskq); |
|
1301 |
DEVI(dip)->devi_taskq = NULL; |
|
1302 |
} |
|
1303 |
||
1304 |
/* Cleanup dacf reservations */ |
|
1305 |
mutex_enter(&dacf_lock); |
|
1306 |
dacf_clr_rsrvs(dip, DACF_OPID_POSTATTACH); |
|
1307 |
dacf_clr_rsrvs(dip, DACF_OPID_PREDETACH); |
|
1308 |
mutex_exit(&dacf_lock); |
|
1309 |
||
1310 |
/* Remove properties and minor nodes in case driver forgots */ |
|
1311 |
ddi_remove_minor_node(dip, NULL); |
|
1312 |
ddi_prop_remove_all(dip); |
|
1313 |
||
1314 |
/* a detached node can't have attached or .conf children */ |
|
1315 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
1316 |
DEVI(dip)->devi_flags &= ~(DEVI_MADE_CHILDREN|DEVI_ATTACHED_CHILDREN); |
|
438
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1317 |
|
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1318 |
/* ensure that devids registered during attach are unregistered */ |
0 | 1319 |
if (DEVI(dip)->devi_flags & DEVI_REGISTERED_DEVID) { |
1320 |
DEVI(dip)->devi_flags &= ~DEVI_REGISTERED_DEVID; |
|
438
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1321 |
mutex_exit(&DEVI(dip)->devi_lock); |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1322 |
|
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1323 |
e_devid_cache_unregister(dip); |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1324 |
} else |
221f0e038fcc
6305386 detach_node() should not hold devi_lock across call to e_devid_cache_unregister()
cth
parents:
298
diff
changeset
|
1325 |
mutex_exit(&DEVI(dip)->devi_lock); |
0 | 1326 |
|
53 | 1327 |
/* |
1328 |
* If the instance has successfully detached in detach_driver() context, |
|
1329 |
* clear DN_DRIVER_HELD for correct ddi_hold_installed_driver() |
|
1330 |
* behavior. Consumers like qassociate() depend on this (via clnopen()). |
|
1331 |
*/ |
|
1332 |
if (flag & NDI_DETACH_DRIVER) { |
|
1333 |
dnp = &(devnamesp[DEVI(dip)->devi_major]); |
|
1334 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
1335 |
dnp->dn_flags &= ~DN_DRIVER_HELD; |
|
1336 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
1337 |
} |
|
1338 |
||
0 | 1339 |
/* successful detach, release the driver */ |
1340 |
ndi_rele_driver(dip); |
|
1341 |
DEVI(dip)->devi_ops = NULL; |
|
1342 |
return (DDI_SUCCESS); |
|
1343 |
} |
|
1344 |
||
1345 |
/* |
|
1346 |
* Run dacf post_attach routines |
|
1347 |
*/ |
|
1348 |
static int |
|
1349 |
postattach_node(dev_info_t *dip) |
|
1350 |
{ |
|
1351 |
int rval; |
|
1352 |
||
1353 |
/* |
|
1354 |
* For hotplug busses like USB, it's possible that devices |
|
1355 |
* are removed but dip is still around. We don't want to |
|
1356 |
* run dacf routines as part of detach failure recovery. |
|
1357 |
* |
|
1358 |
* Pretend success until we figure out how to prevent |
|
1359 |
* access to such devinfo nodes. |
|
1360 |
*/ |
|
1361 |
if (DEVI_IS_DEVICE_REMOVED(dip)) |
|
1362 |
return (DDI_SUCCESS); |
|
1363 |
||
1364 |
/* |
|
1365 |
* if dacf_postattach failed, report it to the framework |
|
1366 |
* so that it can be retried later at the open time. |
|
1367 |
*/ |
|
1368 |
mutex_enter(&dacf_lock); |
|
1369 |
rval = dacfc_postattach(dip); |
|
1370 |
mutex_exit(&dacf_lock); |
|
1371 |
||
1372 |
/* |
|
1373 |
* Plumbing during postattach may fail because of the |
|
1374 |
* underlying device is not ready. This will fail ndi_devi_config() |
|
1375 |
* in dv_filldir() and a warning message is issued. The message |
|
1376 |
* from here will explain what happened |
|
1377 |
*/ |
|
1378 |
if (rval != DACF_SUCCESS) { |
|
1379 |
cmn_err(CE_WARN, "Postattach failed for %s%d\n", |
|
1380 |
ddi_driver_name(dip), ddi_get_instance(dip)); |
|
1381 |
return (DDI_FAILURE); |
|
1382 |
} |
|
1383 |
||
1384 |
return (DDI_SUCCESS); |
|
1385 |
} |
|
1386 |
||
1387 |
/* |
|
1388 |
* Run dacf pre-detach routines |
|
1389 |
*/ |
|
1390 |
static int |
|
1391 |
predetach_node(dev_info_t *dip, uint_t flag) |
|
1392 |
{ |
|
1393 |
int ret; |
|
1394 |
||
1395 |
/* |
|
1396 |
* Don't auto-detach if DDI_FORCEATTACH or DDI_NO_AUTODETACH |
|
1397 |
* properties are set. |
|
1398 |
*/ |
|
1399 |
if (flag & NDI_AUTODETACH) { |
|
1400 |
struct devnames *dnp; |
|
1401 |
int pflag = DDI_PROP_NOTPROM | DDI_PROP_DONTPASS; |
|
1402 |
||
1403 |
if ((ddi_prop_get_int(DDI_DEV_T_ANY, dip, |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
1404 |
pflag, DDI_FORCEATTACH, 0) == 1) || |
0 | 1405 |
(ddi_prop_get_int(DDI_DEV_T_ANY, dip, |
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
1406 |
pflag, DDI_NO_AUTODETACH, 0) == 1)) |
0 | 1407 |
return (DDI_FAILURE); |
1408 |
||
1409 |
/* check for driver global version of DDI_NO_AUTODETACH */ |
|
1410 |
dnp = &devnamesp[DEVI(dip)->devi_major]; |
|
1411 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
1412 |
if (dnp->dn_flags & DN_NO_AUTODETACH) { |
|
1413 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
1414 |
return (DDI_FAILURE); |
|
1415 |
} |
|
1416 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
1417 |
} |
|
1418 |
||
1419 |
mutex_enter(&dacf_lock); |
|
1420 |
ret = dacfc_predetach(dip); |
|
1421 |
mutex_exit(&dacf_lock); |
|
1422 |
||
1423 |
return (ret); |
|
1424 |
} |
|
1425 |
||
1426 |
/* |
|
1427 |
* Wrapper for making multiple state transitions |
|
1428 |
*/ |
|
1429 |
||
1430 |
/* |
|
1431 |
* i_ndi_config_node: upgrade dev_info node into a specified state. |
|
1432 |
* It is a bit tricky because the locking protocol changes before and |
|
1433 |
* after a node is bound to a driver. All locks are held external to |
|
1434 |
* this function. |
|
1435 |
*/ |
|
1436 |
int |
|
1437 |
i_ndi_config_node(dev_info_t *dip, ddi_node_state_t state, uint_t flag) |
|
1438 |
{ |
|
1439 |
_NOTE(ARGUNUSED(flag)) |
|
1440 |
int rv = DDI_SUCCESS; |
|
1441 |
||
1442 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
|
1443 |
||
1444 |
while ((i_ddi_node_state(dip) < state) && (rv == DDI_SUCCESS)) { |
|
1445 |
||
1446 |
/* don't allow any more changes to the device tree */ |
|
1447 |
if (devinfo_freeze) { |
|
1448 |
rv = DDI_FAILURE; |
|
1449 |
break; |
|
1450 |
} |
|
1451 |
||
1452 |
switch (i_ddi_node_state(dip)) { |
|
1453 |
case DS_PROTO: |
|
1454 |
/* |
|
1455 |
* only caller can reference this node, no external |
|
1456 |
* locking needed. |
|
1457 |
*/ |
|
1458 |
link_node(dip); |
|
1459 |
i_ddi_set_node_state(dip, DS_LINKED); |
|
1460 |
break; |
|
1461 |
case DS_LINKED: |
|
1462 |
/* |
|
1463 |
* Three code path may attempt to bind a node: |
|
1464 |
* - boot code |
|
1465 |
* - add_drv |
|
1466 |
* - hotplug thread |
|
1467 |
* Boot code is single threaded, add_drv synchronize |
|
1468 |
* on a userland lock, and hotplug synchronize on |
|
1469 |
* hotplug_lk. There could be a race between add_drv |
|
1470 |
* and hotplug thread. We'll live with this until the |
|
1471 |
* conversion to top-down loading. |
|
1472 |
*/ |
|
1473 |
if ((rv = bind_node(dip)) == DDI_SUCCESS) |
|
1474 |
i_ddi_set_node_state(dip, DS_BOUND); |
|
4145 | 1475 |
|
0 | 1476 |
break; |
1477 |
case DS_BOUND: |
|
1478 |
/* |
|
1479 |
* The following transitions synchronizes on the |
|
1480 |
* per-driver busy changing flag, since we already |
|
1481 |
* have a driver. |
|
1482 |
*/ |
|
1483 |
if ((rv = init_node(dip)) == DDI_SUCCESS) |
|
1484 |
i_ddi_set_node_state(dip, DS_INITIALIZED); |
|
1485 |
break; |
|
1486 |
case DS_INITIALIZED: |
|
1487 |
if ((rv = probe_node(dip)) == DDI_SUCCESS) |
|
1488 |
i_ddi_set_node_state(dip, DS_PROBED); |
|
1489 |
break; |
|
1490 |
case DS_PROBED: |
|
4845 | 1491 |
i_ddi_check_retire(dip); |
0 | 1492 |
atomic_add_long(&devinfo_attach_detach, 1); |
1493 |
if ((rv = attach_node(dip)) == DDI_SUCCESS) |
|
1494 |
i_ddi_set_node_state(dip, DS_ATTACHED); |
|
1495 |
atomic_add_long(&devinfo_attach_detach, -1); |
|
1496 |
break; |
|
1497 |
case DS_ATTACHED: |
|
1498 |
if ((rv = postattach_node(dip)) == DDI_SUCCESS) |
|
1499 |
i_ddi_set_node_state(dip, DS_READY); |
|
1500 |
break; |
|
1501 |
case DS_READY: |
|
1502 |
break; |
|
1503 |
default: |
|
1504 |
/* should never reach here */ |
|
1505 |
ASSERT("unknown devinfo state"); |
|
1506 |
} |
|
1507 |
} |
|
1508 |
||
1509 |
if (ddidebug & DDI_AUDIT) |
|
1510 |
da_log_enter(dip); |
|
1511 |
return (rv); |
|
1512 |
} |
|
1513 |
||
1514 |
/* |
|
1515 |
* i_ndi_unconfig_node: downgrade dev_info node into a specified state. |
|
1516 |
*/ |
|
1517 |
int |
|
1518 |
i_ndi_unconfig_node(dev_info_t *dip, ddi_node_state_t state, uint_t flag) |
|
1519 |
{ |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
1520 |
int rv = DDI_SUCCESS; |
0 | 1521 |
|
1522 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
|
1523 |
||
1524 |
while ((i_ddi_node_state(dip) > state) && (rv == DDI_SUCCESS)) { |
|
1525 |
||
1526 |
/* don't allow any more changes to the device tree */ |
|
1527 |
if (devinfo_freeze) { |
|
1528 |
rv = DDI_FAILURE; |
|
1529 |
break; |
|
1530 |
} |
|
1531 |
||
1532 |
switch (i_ddi_node_state(dip)) { |
|
1533 |
case DS_PROTO: |
|
1534 |
break; |
|
1535 |
case DS_LINKED: |
|
1536 |
/* |
|
1537 |
* Persistent nodes are only removed by hotplug code |
|
1538 |
* .conf nodes synchronizes on per-driver list. |
|
1539 |
*/ |
|
1540 |
if ((rv = unlink_node(dip)) == DDI_SUCCESS) |
|
1541 |
i_ddi_set_node_state(dip, DS_PROTO); |
|
1542 |
break; |
|
1543 |
case DS_BOUND: |
|
1544 |
/* |
|
1545 |
* The following transitions synchronizes on the |
|
1546 |
* per-driver busy changing flag, since we already |
|
1547 |
* have a driver. |
|
1548 |
*/ |
|
1549 |
if ((rv = unbind_node(dip)) == DDI_SUCCESS) |
|
1550 |
i_ddi_set_node_state(dip, DS_LINKED); |
|
1551 |
break; |
|
1552 |
case DS_INITIALIZED: |
|
1553 |
if ((rv = uninit_node(dip)) == DDI_SUCCESS) |
|
1554 |
i_ddi_set_node_state(dip, DS_BOUND); |
|
1555 |
break; |
|
1556 |
case DS_PROBED: |
|
1557 |
if ((rv = unprobe_node(dip)) == DDI_SUCCESS) |
|
1558 |
i_ddi_set_node_state(dip, DS_INITIALIZED); |
|
1559 |
break; |
|
1560 |
case DS_ATTACHED: |
|
1561 |
atomic_add_long(&devinfo_attach_detach, 1); |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1562 |
|
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1563 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 1564 |
DEVI_SET_DETACHING(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1565 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1566 |
|
0 | 1567 |
membar_enter(); /* ensure visibility for hold_devi */ |
1568 |
||
1569 |
if ((rv = detach_node(dip, flag)) == DDI_SUCCESS) |
|
1570 |
i_ddi_set_node_state(dip, DS_PROBED); |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1571 |
|
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1572 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 1573 |
DEVI_CLR_DETACHING(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1574 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
1575 |
|
0 | 1576 |
atomic_add_long(&devinfo_attach_detach, -1); |
1577 |
break; |
|
1578 |
case DS_READY: |
|
1579 |
if ((rv = predetach_node(dip, flag)) == DDI_SUCCESS) |
|
1580 |
i_ddi_set_node_state(dip, DS_ATTACHED); |
|
1581 |
break; |
|
1582 |
default: |
|
1583 |
ASSERT("unknown devinfo state"); |
|
1584 |
} |
|
1585 |
} |
|
1586 |
da_log_enter(dip); |
|
1587 |
return (rv); |
|
1588 |
} |
|
1589 |
||
1590 |
/* |
|
1591 |
* ddi_initchild: transform node to DS_INITIALIZED state |
|
1592 |
*/ |
|
1593 |
int |
|
1594 |
ddi_initchild(dev_info_t *parent, dev_info_t *proto) |
|
1595 |
{ |
|
1596 |
int ret, circ; |
|
1597 |
||
1598 |
ndi_devi_enter(parent, &circ); |
|
1599 |
ret = i_ndi_config_node(proto, DS_INITIALIZED, 0); |
|
1600 |
ndi_devi_exit(parent, circ); |
|
1601 |
||
1602 |
return (ret); |
|
1603 |
} |
|
1604 |
||
1605 |
/* |
|
1606 |
* ddi_uninitchild: transform node down to DS_BOUND state |
|
1607 |
*/ |
|
1608 |
int |
|
1609 |
ddi_uninitchild(dev_info_t *dip) |
|
1610 |
{ |
|
1611 |
int ret, circ; |
|
1612 |
dev_info_t *parent = ddi_get_parent(dip); |
|
1613 |
ASSERT(parent); |
|
1614 |
||
1615 |
ndi_devi_enter(parent, &circ); |
|
1616 |
ret = i_ndi_unconfig_node(dip, DS_BOUND, 0); |
|
1617 |
ndi_devi_exit(parent, circ); |
|
1618 |
||
1619 |
return (ret); |
|
1620 |
} |
|
1621 |
||
1622 |
/* |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
1623 |
* i_ddi_attachchild: transform node to DS_READY/i_ddi_devi_attached() state |
0 | 1624 |
*/ |
1625 |
static int |
|
1626 |
i_ddi_attachchild(dev_info_t *dip) |
|
1627 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1628 |
dev_info_t *parent = ddi_get_parent(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1629 |
int ret; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1630 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1631 |
ASSERT(parent && DEVI_BUSY_OWNED(parent)); |
0 | 1632 |
|
1633 |
if ((i_ddi_node_state(dip) < DS_BOUND) || DEVI_IS_DEVICE_OFFLINE(dip)) |
|
1634 |
return (DDI_FAILURE); |
|
1635 |
||
1636 |
ret = i_ndi_config_node(dip, DS_READY, 0); |
|
1637 |
if (ret == NDI_SUCCESS) { |
|
1638 |
ret = DDI_SUCCESS; |
|
1639 |
} else { |
|
1640 |
/* |
|
1641 |
* Take it down to DS_INITIALIZED so pm_pre_probe is run |
|
1642 |
* on the next attach |
|
1643 |
*/ |
|
1644 |
(void) i_ndi_unconfig_node(dip, DS_INITIALIZED, 0); |
|
1645 |
ret = DDI_FAILURE; |
|
1646 |
} |
|
1647 |
||
1648 |
return (ret); |
|
1649 |
} |
|
1650 |
||
1651 |
/* |
|
1652 |
* i_ddi_detachchild: transform node down to DS_PROBED state |
|
1653 |
* If it fails, put it back to DS_READY state. |
|
1654 |
* NOTE: A node that fails detach may be at DS_ATTACHED instead |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
1655 |
* of DS_READY for a small amount of time - this is the source of |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
1656 |
* transient DS_READY->DS_ATTACHED->DS_READY state changes. |
0 | 1657 |
*/ |
1658 |
static int |
|
1659 |
i_ddi_detachchild(dev_info_t *dip, uint_t flags) |
|
1660 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1661 |
dev_info_t *parent = ddi_get_parent(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1662 |
int ret; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1663 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1664 |
ASSERT(parent && DEVI_BUSY_OWNED(parent)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1665 |
|
0 | 1666 |
ret = i_ndi_unconfig_node(dip, DS_PROBED, flags); |
1667 |
if (ret != DDI_SUCCESS) |
|
1668 |
(void) i_ndi_config_node(dip, DS_READY, 0); |
|
1669 |
else |
|
1670 |
/* allow pm_pre_probe to reestablish pm state */ |
|
1671 |
(void) i_ndi_unconfig_node(dip, DS_INITIALIZED, 0); |
|
1672 |
return (ret); |
|
1673 |
} |
|
1674 |
||
1675 |
/* |
|
1676 |
* Add a child and bind to driver |
|
1677 |
*/ |
|
1678 |
dev_info_t * |
|
1679 |
ddi_add_child(dev_info_t *pdip, char *name, uint_t nodeid, uint_t unit) |
|
1680 |
{ |
|
1681 |
int circ; |
|
1682 |
dev_info_t *dip; |
|
1683 |
||
1684 |
/* allocate a new node */ |
|
1685 |
dip = i_ddi_alloc_node(pdip, name, nodeid, (int)unit, NULL, KM_SLEEP); |
|
1686 |
||
1687 |
ndi_devi_enter(pdip, &circ); |
|
1688 |
(void) i_ndi_config_node(dip, DS_BOUND, 0); |
|
1689 |
ndi_devi_exit(pdip, circ); |
|
1690 |
return (dip); |
|
1691 |
} |
|
1692 |
||
1693 |
/* |
|
1694 |
* ddi_remove_child: remove the dip. The parent must be attached and held |
|
1695 |
*/ |
|
1696 |
int |
|
1697 |
ddi_remove_child(dev_info_t *dip, int dummy) |
|
1698 |
{ |
|
1699 |
_NOTE(ARGUNUSED(dummy)) |
|
1700 |
int circ, ret; |
|
1701 |
dev_info_t *parent = ddi_get_parent(dip); |
|
1702 |
ASSERT(parent); |
|
1703 |
||
1704 |
ndi_devi_enter(parent, &circ); |
|
1705 |
||
1706 |
/* |
|
1707 |
* If we still have children, for example SID nodes marked |
|
1708 |
* as persistent but not attached, attempt to remove them. |
|
1709 |
*/ |
|
1710 |
if (DEVI(dip)->devi_child) { |
|
1711 |
ret = ndi_devi_unconfig(dip, NDI_DEVI_REMOVE); |
|
1712 |
if (ret != NDI_SUCCESS) { |
|
1713 |
ndi_devi_exit(parent, circ); |
|
1714 |
return (DDI_FAILURE); |
|
1715 |
} |
|
1716 |
ASSERT(DEVI(dip)->devi_child == NULL); |
|
1717 |
} |
|
1718 |
||
1719 |
ret = i_ndi_unconfig_node(dip, DS_PROTO, 0); |
|
1720 |
ndi_devi_exit(parent, circ); |
|
1721 |
||
1722 |
if (ret != DDI_SUCCESS) |
|
1723 |
return (ret); |
|
1724 |
||
1725 |
ASSERT(i_ddi_node_state(dip) == DS_PROTO); |
|
1726 |
i_ddi_free_node(dip); |
|
1727 |
return (DDI_SUCCESS); |
|
1728 |
} |
|
1729 |
||
1730 |
/* |
|
1731 |
* NDI wrappers for ref counting, node allocation, and transitions |
|
1732 |
*/ |
|
1733 |
||
1734 |
/* |
|
1735 |
* Hold/release the devinfo node itself. |
|
1736 |
* Caller is assumed to prevent the devi from detaching during this call |
|
1737 |
*/ |
|
1738 |
void |
|
1739 |
ndi_hold_devi(dev_info_t *dip) |
|
1740 |
{ |
|
1741 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
1742 |
ASSERT(DEVI(dip)->devi_ref >= 0); |
|
1743 |
DEVI(dip)->devi_ref++; |
|
1744 |
membar_enter(); /* make sure stores are flushed */ |
|
1745 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
1746 |
} |
|
1747 |
||
1748 |
void |
|
1749 |
ndi_rele_devi(dev_info_t *dip) |
|
1750 |
{ |
|
1751 |
ASSERT(DEVI(dip)->devi_ref > 0); |
|
1752 |
||
1753 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
1754 |
DEVI(dip)->devi_ref--; |
|
1755 |
membar_enter(); /* make sure stores are flushed */ |
|
1756 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
1757 |
} |
|
1758 |
||
1759 |
int |
|
1760 |
e_ddi_devi_holdcnt(dev_info_t *dip) |
|
1761 |
{ |
|
1762 |
return (DEVI(dip)->devi_ref); |
|
1763 |
} |
|
1764 |
||
1765 |
/* |
|
1766 |
* Hold/release the driver the devinfo node is bound to. |
|
1767 |
*/ |
|
1768 |
struct dev_ops * |
|
1769 |
ndi_hold_driver(dev_info_t *dip) |
|
1770 |
{ |
|
1771 |
if (i_ddi_node_state(dip) < DS_BOUND) |
|
1772 |
return (NULL); |
|
1773 |
||
1774 |
ASSERT(DEVI(dip)->devi_major != -1); |
|
1775 |
return (mod_hold_dev_by_major(DEVI(dip)->devi_major)); |
|
1776 |
} |
|
1777 |
||
1778 |
void |
|
1779 |
ndi_rele_driver(dev_info_t *dip) |
|
1780 |
{ |
|
1781 |
ASSERT(i_ddi_node_state(dip) >= DS_BOUND); |
|
1782 |
mod_rele_dev_by_major(DEVI(dip)->devi_major); |
|
1783 |
} |
|
1784 |
||
1785 |
/* |
|
1786 |
* Single thread entry into devinfo node for modifying its children. |
|
1787 |
* To verify in ASSERTS use DEVI_BUSY_OWNED macro. |
|
1788 |
*/ |
|
1789 |
void |
|
1790 |
ndi_devi_enter(dev_info_t *dip, int *circular) |
|
1791 |
{ |
|
1792 |
struct dev_info *devi = DEVI(dip); |
|
1793 |
ASSERT(dip != NULL); |
|
1794 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1795 |
/* for vHCI, enforce (vHCI, pHCI) ndi_deve_enter() order */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1796 |
ASSERT(!MDI_VHCI(dip) || (mdi_devi_pdip_entered(dip) == 0) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1797 |
DEVI_BUSY_OWNED(dip)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1798 |
|
0 | 1799 |
mutex_enter(&devi->devi_lock); |
1800 |
if (devi->devi_busy_thread == curthread) { |
|
1801 |
devi->devi_circular++; |
|
1802 |
} else { |
|
1803 |
while (DEVI_BUSY_CHANGING(devi) && !panicstr) |
|
1804 |
cv_wait(&(devi->devi_cv), &(devi->devi_lock)); |
|
1805 |
if (panicstr) { |
|
1806 |
mutex_exit(&devi->devi_lock); |
|
1807 |
return; |
|
1808 |
} |
|
1809 |
devi->devi_flags |= DEVI_BUSY; |
|
1810 |
devi->devi_busy_thread = curthread; |
|
1811 |
} |
|
1812 |
*circular = devi->devi_circular; |
|
1813 |
mutex_exit(&devi->devi_lock); |
|
1814 |
} |
|
1815 |
||
1816 |
/* |
|
1817 |
* Release ndi_devi_enter or successful ndi_devi_tryenter. |
|
1818 |
*/ |
|
1819 |
void |
|
1820 |
ndi_devi_exit(dev_info_t *dip, int circular) |
|
1821 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1822 |
struct dev_info *devi = DEVI(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1823 |
struct dev_info *vdevi; |
0 | 1824 |
ASSERT(dip != NULL); |
1825 |
||
1826 |
if (panicstr) |
|
1827 |
return; |
|
1828 |
||
1829 |
mutex_enter(&(devi->devi_lock)); |
|
1830 |
if (circular != 0) { |
|
1831 |
devi->devi_circular--; |
|
1832 |
} else { |
|
1833 |
devi->devi_flags &= ~DEVI_BUSY; |
|
1834 |
ASSERT(devi->devi_busy_thread == curthread); |
|
1835 |
devi->devi_busy_thread = NULL; |
|
1836 |
cv_broadcast(&(devi->devi_cv)); |
|
1837 |
} |
|
1838 |
mutex_exit(&(devi->devi_lock)); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1839 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1840 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1841 |
* For pHCI exit we issue a broadcast to vHCI for ndi_devi_config_one() |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1842 |
* doing cv_wait on vHCI. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1843 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1844 |
if (MDI_PHCI(dip)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1845 |
vdevi = DEVI(mdi_devi_get_vdip(dip)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1846 |
if (vdevi) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1847 |
mutex_enter(&(vdevi->devi_lock)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1848 |
if (vdevi->devi_flags & DEVI_PHCI_SIGNALS_VHCI) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1849 |
vdevi->devi_flags &= ~DEVI_PHCI_SIGNALS_VHCI; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1850 |
cv_broadcast(&(vdevi->devi_cv)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1851 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1852 |
mutex_exit(&(vdevi->devi_lock)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1853 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1854 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1855 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1856 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1857 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1858 |
* Release ndi_devi_enter and wait for possibility of new children, avoiding |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1859 |
* possibility of missing broadcast before getting to cv_timedwait(). |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1860 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1861 |
static void |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1862 |
ndi_devi_exit_and_wait(dev_info_t *dip, int circular, clock_t end_time) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1863 |
{ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1864 |
struct dev_info *devi = DEVI(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1865 |
ASSERT(dip != NULL); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1866 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1867 |
if (panicstr) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1868 |
return; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1869 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1870 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1871 |
* We are called to wait for of a new child, and new child can |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1872 |
* only be added if circular is zero. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1873 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1874 |
ASSERT(circular == 0); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1875 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1876 |
/* like ndi_devi_exit with circular of zero */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1877 |
mutex_enter(&(devi->devi_lock)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1878 |
devi->devi_flags &= ~DEVI_BUSY; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1879 |
ASSERT(devi->devi_busy_thread == curthread); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1880 |
devi->devi_busy_thread = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1881 |
cv_broadcast(&(devi->devi_cv)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1882 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1883 |
/* now wait for new children while still holding devi_lock */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1884 |
(void) cv_timedwait(&devi->devi_cv, &(devi->devi_lock), end_time); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
1885 |
mutex_exit(&(devi->devi_lock)); |
0 | 1886 |
} |
1887 |
||
1888 |
/* |
|
1889 |
* Attempt to single thread entry into devinfo node for modifying its children. |
|
1890 |
*/ |
|
1891 |
int |
|
1892 |
ndi_devi_tryenter(dev_info_t *dip, int *circular) |
|
1893 |
{ |
|
1894 |
int rval = 1; /* assume we enter */ |
|
1895 |
struct dev_info *devi = DEVI(dip); |
|
1896 |
ASSERT(dip != NULL); |
|
1897 |
||
1898 |
mutex_enter(&devi->devi_lock); |
|
1899 |
if (devi->devi_busy_thread == (void *)curthread) { |
|
1900 |
devi->devi_circular++; |
|
1901 |
} else { |
|
1902 |
if (!DEVI_BUSY_CHANGING(devi)) { |
|
1903 |
devi->devi_flags |= DEVI_BUSY; |
|
1904 |
devi->devi_busy_thread = (void *)curthread; |
|
1905 |
} else { |
|
1906 |
rval = 0; /* devi is busy */ |
|
1907 |
} |
|
1908 |
} |
|
1909 |
*circular = devi->devi_circular; |
|
1910 |
mutex_exit(&devi->devi_lock); |
|
1911 |
return (rval); |
|
1912 |
} |
|
1913 |
||
1914 |
/* |
|
1915 |
* Allocate and initialize a new dev_info structure. |
|
1916 |
* |
|
1917 |
* This routine may be called at interrupt time by a nexus in |
|
1918 |
* response to a hotplug event, therefore memory allocations are |
|
1919 |
* not allowed to sleep. |
|
1920 |
*/ |
|
1921 |
int |
|
789 | 1922 |
ndi_devi_alloc(dev_info_t *parent, char *node_name, pnode_t nodeid, |
0 | 1923 |
dev_info_t **ret_dip) |
1924 |
{ |
|
1925 |
ASSERT(node_name != NULL); |
|
1926 |
ASSERT(ret_dip != NULL); |
|
1927 |
||
1928 |
*ret_dip = i_ddi_alloc_node(parent, node_name, nodeid, -1, NULL, |
|
1929 |
KM_NOSLEEP); |
|
1930 |
if (*ret_dip == NULL) { |
|
1931 |
return (NDI_NOMEM); |
|
1932 |
} |
|
1933 |
||
1934 |
return (NDI_SUCCESS); |
|
1935 |
} |
|
1936 |
||
1937 |
/* |
|
1938 |
* Allocate and initialize a new dev_info structure |
|
1939 |
* This routine may sleep and should not be called at interrupt time |
|
1940 |
*/ |
|
1941 |
void |
|
789 | 1942 |
ndi_devi_alloc_sleep(dev_info_t *parent, char *node_name, pnode_t nodeid, |
0 | 1943 |
dev_info_t **ret_dip) |
1944 |
{ |
|
1945 |
ASSERT(node_name != NULL); |
|
1946 |
ASSERT(ret_dip != NULL); |
|
1947 |
||
1948 |
*ret_dip = i_ddi_alloc_node(parent, node_name, nodeid, -1, NULL, |
|
1949 |
KM_SLEEP); |
|
1950 |
ASSERT(*ret_dip); |
|
1951 |
} |
|
1952 |
||
1953 |
/* |
|
1954 |
* Remove an initialized (but not yet attached) dev_info |
|
1955 |
* node from it's parent. |
|
1956 |
*/ |
|
1957 |
int |
|
1958 |
ndi_devi_free(dev_info_t *dip) |
|
1959 |
{ |
|
1960 |
ASSERT(dip != NULL); |
|
1961 |
||
1962 |
if (i_ddi_node_state(dip) >= DS_INITIALIZED) |
|
1963 |
return (DDI_FAILURE); |
|
1964 |
||
1965 |
NDI_CONFIG_DEBUG((CE_CONT, "ndi_devi_free: %s%d (%p)\n", |
|
1966 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip)); |
|
1967 |
||
1968 |
(void) ddi_remove_child(dip, 0); |
|
1969 |
||
1970 |
return (NDI_SUCCESS); |
|
1971 |
} |
|
1972 |
||
1973 |
/* |
|
1974 |
* ndi_devi_bind_driver() binds a driver to a given device. If it fails |
|
1975 |
* to bind the driver, it returns an appropriate error back. Some drivers |
|
1976 |
* may want to know if the actually failed to bind. |
|
1977 |
*/ |
|
1978 |
int |
|
1979 |
ndi_devi_bind_driver(dev_info_t *dip, uint_t flags) |
|
1980 |
{ |
|
1981 |
int ret = NDI_FAILURE; |
|
1982 |
int circ; |
|
1983 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
1984 |
ASSERT(pdip); |
|
1985 |
||
1986 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
1987 |
"ndi_devi_bind_driver: %s%d (%p) flags: %x\n", |
|
1988 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
1989 |
||
1990 |
ndi_devi_enter(pdip, &circ); |
|
1991 |
if (i_ndi_config_node(dip, DS_BOUND, flags) == DDI_SUCCESS) |
|
1992 |
ret = NDI_SUCCESS; |
|
1993 |
ndi_devi_exit(pdip, circ); |
|
1994 |
||
1995 |
return (ret); |
|
1996 |
} |
|
1997 |
||
1998 |
/* |
|
1999 |
* ndi_devi_unbind_driver: unbind the dip |
|
2000 |
*/ |
|
2001 |
static int |
|
2002 |
ndi_devi_unbind_driver(dev_info_t *dip) |
|
2003 |
{ |
|
2004 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
|
2005 |
||
2006 |
return (i_ndi_unconfig_node(dip, DS_LINKED, 0)); |
|
2007 |
} |
|
2008 |
||
2009 |
/* |
|
2010 |
* Misc. help routines called by framework only |
|
2011 |
*/ |
|
2012 |
||
2013 |
/* |
|
2014 |
* Get the state of node |
|
2015 |
*/ |
|
2016 |
ddi_node_state_t |
|
2017 |
i_ddi_node_state(dev_info_t *dip) |
|
2018 |
{ |
|
2019 |
return (DEVI(dip)->devi_node_state); |
|
2020 |
} |
|
2021 |
||
2022 |
/* |
|
2023 |
* Set the state of node |
|
2024 |
*/ |
|
2025 |
void |
|
2026 |
i_ddi_set_node_state(dev_info_t *dip, ddi_node_state_t state) |
|
2027 |
{ |
|
2028 |
DEVI(dip)->devi_node_state = state; |
|
2029 |
membar_enter(); /* make sure stores are flushed */ |
|
2030 |
} |
|
2031 |
||
2032 |
/* |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2033 |
* Determine if node is attached. The implementation accommodates transient |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2034 |
* DS_READY->DS_ATTACHED->DS_READY state changes. Outside this file, this |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2035 |
* function should be instead of i_ddi_node_state() DS_ATTACHED/DS_READY |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2036 |
* state checks. |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2037 |
*/ |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2038 |
int |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2039 |
i_ddi_devi_attached(dev_info_t *dip) |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2040 |
{ |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2041 |
return (DEVI(dip)->devi_node_state >= DS_ATTACHED); |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2042 |
} |
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2043 |
|
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
2044 |
/* |
0 | 2045 |
* Common function for finding a node in a sibling list given name and addr. |
2046 |
* |
|
2047 |
* By default, name is matched with devi_node_name. The following |
|
2048 |
* alternative match strategies are supported: |
|
2049 |
* |
|
4145 | 2050 |
* FIND_NODE_BY_NODENAME: Match on node name - typical use. |
2051 |
* FIND_NODE_BY_DRIVER: A match on driver name bound to node is conducted. |
|
0 | 2052 |
* This support is used for support of OBP generic names and |
4145 | 2053 |
* for the conversion from driver names to generic names. When |
0 | 2054 |
* more consistency in the generic name environment is achieved |
2055 |
* (and not needed for upgrade) this support can be removed. |
|
4145 | 2056 |
* FIND_NODE_BY_ADDR: Match on just the addr. |
2057 |
* This support is only used/needed during boot to match |
|
2058 |
* a node bound via a path-based driver alias. |
|
0 | 2059 |
* |
2060 |
* If a child is not named (dev_addr == NULL), there are three |
|
2061 |
* possible actions: |
|
2062 |
* |
|
2063 |
* (1) skip it |
|
2064 |
* (2) FIND_ADDR_BY_INIT: bring child to DS_INITIALIZED state |
|
2065 |
* (3) FIND_ADDR_BY_CALLBACK: use a caller-supplied callback function |
|
2066 |
*/ |
|
4145 | 2067 |
#define FIND_NODE_BY_NODENAME 0x01 |
2068 |
#define FIND_NODE_BY_DRIVER 0x02 |
|
2069 |
#define FIND_NODE_BY_ADDR 0x04 |
|
0 | 2070 |
#define FIND_ADDR_BY_INIT 0x10 |
2071 |
#define FIND_ADDR_BY_CALLBACK 0x20 |
|
2072 |
||
2073 |
static dev_info_t * |
|
2074 |
find_sibling(dev_info_t *head, char *cname, char *caddr, uint_t flag, |
|
2075 |
int (*callback)(dev_info_t *, char *, int)) |
|
2076 |
{ |
|
2077 |
dev_info_t *dip; |
|
2078 |
char *addr, *buf; |
|
2079 |
major_t major; |
|
4145 | 2080 |
uint_t by; |
2081 |
||
2082 |
/* only one way to find a node */ |
|
2083 |
by = flag & |
|
2084 |
(FIND_NODE_BY_DRIVER | FIND_NODE_BY_NODENAME | FIND_NODE_BY_ADDR); |
|
2085 |
ASSERT(by && BIT_ONLYONESET(by)); |
|
0 | 2086 |
|
2087 |
/* only one way to name a node */ |
|
2088 |
ASSERT(((flag & FIND_ADDR_BY_INIT) == 0) || |
|
2089 |
((flag & FIND_ADDR_BY_CALLBACK) == 0)); |
|
2090 |
||
4145 | 2091 |
if (by == FIND_NODE_BY_DRIVER) { |
0 | 2092 |
major = ddi_name_to_major(cname); |
2093 |
if (major == (major_t)-1) |
|
2094 |
return (NULL); |
|
2095 |
} |
|
2096 |
||
2097 |
/* preallocate buffer of naming node by callback */ |
|
2098 |
if (flag & FIND_ADDR_BY_CALLBACK) |
|
2099 |
buf = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
2100 |
||
2101 |
/* |
|
2102 |
* Walk the child list to find a match |
|
2103 |
*/ |
|
2104 |
||
2105 |
for (dip = head; dip; dip = ddi_get_next_sibling(dip)) { |
|
4145 | 2106 |
if (by == FIND_NODE_BY_NODENAME) { |
2107 |
/* match node name */ |
|
2108 |
if (strcmp(cname, DEVI(dip)->devi_node_name) != 0) |
|
2109 |
continue; |
|
2110 |
} else if (by == FIND_NODE_BY_DRIVER) { |
|
0 | 2111 |
/* match driver major */ |
2112 |
if (DEVI(dip)->devi_major != major) |
|
2113 |
continue; |
|
2114 |
} |
|
2115 |
||
2116 |
if ((addr = DEVI(dip)->devi_addr) == NULL) { |
|
2117 |
/* name the child based on the flag */ |
|
2118 |
if (flag & FIND_ADDR_BY_INIT) { |
|
2119 |
if (ddi_initchild(ddi_get_parent(dip), dip) |
|
2120 |
!= DDI_SUCCESS) |
|
2121 |
continue; |
|
2122 |
addr = DEVI(dip)->devi_addr; |
|
2123 |
} else if (flag & FIND_ADDR_BY_CALLBACK) { |
|
2124 |
if ((callback == NULL) || (callback( |
|
2125 |
dip, buf, MAXNAMELEN) != DDI_SUCCESS)) |
|
2126 |
continue; |
|
2127 |
addr = buf; |
|
2128 |
} else { |
|
2129 |
continue; /* skip */ |
|
2130 |
} |
|
2131 |
} |
|
2132 |
||
2133 |
/* match addr */ |
|
2134 |
ASSERT(addr != NULL); |
|
2135 |
if (strcmp(caddr, addr) == 0) |
|
2136 |
break; /* node found */ |
|
2137 |
||
2138 |
} |
|
2139 |
if (flag & FIND_ADDR_BY_CALLBACK) |
|
2140 |
kmem_free(buf, MAXNAMELEN); |
|
2141 |
return (dip); |
|
2142 |
} |
|
2143 |
||
2144 |
/* |
|
2145 |
* Find child of pdip with name: cname@caddr |
|
2146 |
* Called by init_node() to look for duplicate nodes |
|
2147 |
*/ |
|
2148 |
static dev_info_t * |
|
2149 |
find_duplicate_child(dev_info_t *pdip, dev_info_t *dip) |
|
2150 |
{ |
|
2151 |
dev_info_t *dup; |
|
2152 |
char *cname = DEVI(dip)->devi_node_name; |
|
2153 |
char *caddr = DEVI(dip)->devi_addr; |
|
2154 |
||
2155 |
/* search nodes before dip */ |
|
4145 | 2156 |
dup = find_sibling(ddi_get_child(pdip), cname, caddr, |
2157 |
FIND_NODE_BY_NODENAME, NULL); |
|
0 | 2158 |
if (dup != dip) |
2159 |
return (dup); |
|
2160 |
||
2161 |
/* |
|
2162 |
* search nodes after dip; normally this is not needed, |
|
2163 |
*/ |
|
2164 |
return (find_sibling(ddi_get_next_sibling(dip), cname, caddr, |
|
4145 | 2165 |
FIND_NODE_BY_NODENAME, NULL)); |
0 | 2166 |
} |
2167 |
||
2168 |
/* |
|
2169 |
* Find a child of a given name and address, using a callback to name |
|
2170 |
* unnamed children. cname is the binding name. |
|
2171 |
*/ |
|
2172 |
static dev_info_t * |
|
2173 |
find_child_by_callback(dev_info_t *pdip, char *cname, char *caddr, |
|
2174 |
int (*name_node)(dev_info_t *, char *, int)) |
|
2175 |
{ |
|
2176 |
return (find_sibling(ddi_get_child(pdip), cname, caddr, |
|
4145 | 2177 |
FIND_NODE_BY_DRIVER|FIND_ADDR_BY_CALLBACK, name_node)); |
0 | 2178 |
} |
2179 |
||
2180 |
/* |
|
2181 |
* Find a child of a given name and address, invoking initchild to name |
|
2182 |
* unnamed children. cname is the node name. |
|
2183 |
*/ |
|
2184 |
static dev_info_t * |
|
2185 |
find_child_by_name(dev_info_t *pdip, char *cname, char *caddr) |
|
2186 |
{ |
|
2187 |
dev_info_t *dip; |
|
2188 |
||
4145 | 2189 |
/* attempt search without changing state of preceding siblings */ |
2190 |
dip = find_sibling(ddi_get_child(pdip), cname, caddr, |
|
2191 |
FIND_NODE_BY_NODENAME, NULL); |
|
0 | 2192 |
if (dip) |
2193 |
return (dip); |
|
2194 |
||
2195 |
return (find_sibling(ddi_get_child(pdip), cname, caddr, |
|
4145 | 2196 |
FIND_NODE_BY_NODENAME|FIND_ADDR_BY_INIT, NULL)); |
0 | 2197 |
} |
2198 |
||
2199 |
/* |
|
2200 |
* Find a child of a given name and address, invoking initchild to name |
|
2201 |
* unnamed children. cname is the node name. |
|
2202 |
*/ |
|
2203 |
static dev_info_t * |
|
2204 |
find_child_by_driver(dev_info_t *pdip, char *cname, char *caddr) |
|
2205 |
{ |
|
2206 |
dev_info_t *dip; |
|
2207 |
||
4145 | 2208 |
/* attempt search without changing state of preceding siblings */ |
0 | 2209 |
dip = find_sibling(ddi_get_child(pdip), cname, caddr, |
4145 | 2210 |
FIND_NODE_BY_DRIVER, NULL); |
0 | 2211 |
if (dip) |
2212 |
return (dip); |
|
2213 |
||
2214 |
return (find_sibling(ddi_get_child(pdip), cname, caddr, |
|
4145 | 2215 |
FIND_NODE_BY_DRIVER|FIND_ADDR_BY_INIT, NULL)); |
2216 |
} |
|
2217 |
||
2218 |
/* |
|
2219 |
* Find a child of a given address, invoking initchild to name |
|
2220 |
* unnamed children. cname is the node name. |
|
2221 |
* |
|
2222 |
* NOTE: This function is only used during boot. One would hope that |
|
2223 |
* unique sibling unit-addresses on hardware branches of the tree would |
|
2224 |
* be a requirement to avoid two drivers trying to control the same |
|
2225 |
* piece of hardware. Unfortunately there are some cases where this |
|
2226 |
* situation exists (/ssm@0,0/pci@1c,700000 /ssm@0,0/sghsc@1c,700000). |
|
2227 |
* Until unit-address uniqueness of siblings is guaranteed, use of this |
|
2228 |
* interface for purposes other than boot should be avoided. |
|
2229 |
*/ |
|
2230 |
static dev_info_t * |
|
2231 |
find_child_by_addr(dev_info_t *pdip, char *caddr) |
|
2232 |
{ |
|
2233 |
dev_info_t *dip; |
|
2234 |
||
4540
f18ef423e3a7
6571223 find_child_by_addr() returns unwanted dip if input caddr is an empty string
cth
parents:
4411
diff
changeset
|
2235 |
/* return NULL if called without a unit-address */ |
f18ef423e3a7
6571223 find_child_by_addr() returns unwanted dip if input caddr is an empty string
cth
parents:
4411
diff
changeset
|
2236 |
if ((caddr == NULL) || (*caddr == '\0')) |
f18ef423e3a7
6571223 find_child_by_addr() returns unwanted dip if input caddr is an empty string
cth
parents:
4411
diff
changeset
|
2237 |
return (NULL); |
f18ef423e3a7
6571223 find_child_by_addr() returns unwanted dip if input caddr is an empty string
cth
parents:
4411
diff
changeset
|
2238 |
|
4145 | 2239 |
/* attempt search without changing state of preceding siblings */ |
2240 |
dip = find_sibling(ddi_get_child(pdip), NULL, caddr, |
|
2241 |
FIND_NODE_BY_ADDR, NULL); |
|
2242 |
if (dip) |
|
2243 |
return (dip); |
|
2244 |
||
2245 |
return (find_sibling(ddi_get_child(pdip), NULL, caddr, |
|
2246 |
FIND_NODE_BY_ADDR|FIND_ADDR_BY_INIT, NULL)); |
|
0 | 2247 |
} |
2248 |
||
2249 |
/* |
|
2250 |
* Deleting a property list. Take care, since some property structures |
|
2251 |
* may not be fully built. |
|
2252 |
*/ |
|
2253 |
void |
|
2254 |
i_ddi_prop_list_delete(ddi_prop_t *prop) |
|
2255 |
{ |
|
2256 |
while (prop) { |
|
2257 |
ddi_prop_t *next = prop->prop_next; |
|
2258 |
if (prop->prop_name) |
|
2259 |
kmem_free(prop->prop_name, strlen(prop->prop_name) + 1); |
|
2260 |
if ((prop->prop_len != 0) && prop->prop_val) |
|
2261 |
kmem_free(prop->prop_val, prop->prop_len); |
|
2262 |
kmem_free(prop, sizeof (struct ddi_prop)); |
|
2263 |
prop = next; |
|
2264 |
} |
|
2265 |
} |
|
2266 |
||
2267 |
/* |
|
2268 |
* Duplicate property list |
|
2269 |
*/ |
|
2270 |
ddi_prop_t * |
|
2271 |
i_ddi_prop_list_dup(ddi_prop_t *prop, uint_t flag) |
|
2272 |
{ |
|
2273 |
ddi_prop_t *result, *prev, *copy; |
|
2274 |
||
2275 |
if (prop == NULL) |
|
2276 |
return (NULL); |
|
2277 |
||
2278 |
result = prev = NULL; |
|
2279 |
for (; prop != NULL; prop = prop->prop_next) { |
|
2280 |
ASSERT(prop->prop_name != NULL); |
|
2281 |
copy = kmem_zalloc(sizeof (struct ddi_prop), flag); |
|
2282 |
if (copy == NULL) |
|
2283 |
goto fail; |
|
2284 |
||
2285 |
copy->prop_dev = prop->prop_dev; |
|
2286 |
copy->prop_flags = prop->prop_flags; |
|
2287 |
copy->prop_name = i_ddi_strdup(prop->prop_name, flag); |
|
2288 |
if (copy->prop_name == NULL) |
|
2289 |
goto fail; |
|
2290 |
||
2291 |
if ((copy->prop_len = prop->prop_len) != 0) { |
|
2292 |
copy->prop_val = kmem_zalloc(prop->prop_len, flag); |
|
2293 |
if (copy->prop_val == NULL) |
|
2294 |
goto fail; |
|
2295 |
||
2296 |
bcopy(prop->prop_val, copy->prop_val, prop->prop_len); |
|
2297 |
} |
|
2298 |
||
2299 |
if (prev == NULL) |
|
2300 |
result = prev = copy; |
|
2301 |
else |
|
2302 |
prev->prop_next = copy; |
|
2303 |
prev = copy; |
|
2304 |
} |
|
2305 |
return (result); |
|
2306 |
||
2307 |
fail: |
|
2308 |
i_ddi_prop_list_delete(result); |
|
2309 |
return (NULL); |
|
2310 |
} |
|
2311 |
||
2312 |
/* |
|
2313 |
* Create a reference property list, currently used only for |
|
2314 |
* driver global properties. Created with ref count of 1. |
|
2315 |
*/ |
|
2316 |
ddi_prop_list_t * |
|
2317 |
i_ddi_prop_list_create(ddi_prop_t *props) |
|
2318 |
{ |
|
2319 |
ddi_prop_list_t *list = kmem_alloc(sizeof (*list), KM_SLEEP); |
|
2320 |
list->prop_list = props; |
|
2321 |
list->prop_ref = 1; |
|
2322 |
return (list); |
|
2323 |
} |
|
2324 |
||
2325 |
/* |
|
2326 |
* Increment/decrement reference count. The reference is |
|
2327 |
* protected by dn_lock. The only interfaces modifying |
|
2328 |
* dn_global_prop_ptr is in impl_make[free]_parlist(). |
|
2329 |
*/ |
|
2330 |
void |
|
2331 |
i_ddi_prop_list_hold(ddi_prop_list_t *prop_list, struct devnames *dnp) |
|
2332 |
{ |
|
2333 |
ASSERT(prop_list->prop_ref >= 0); |
|
2334 |
ASSERT(mutex_owned(&dnp->dn_lock)); |
|
2335 |
prop_list->prop_ref++; |
|
2336 |
} |
|
2337 |
||
2338 |
void |
|
2339 |
i_ddi_prop_list_rele(ddi_prop_list_t *prop_list, struct devnames *dnp) |
|
2340 |
{ |
|
2341 |
ASSERT(prop_list->prop_ref > 0); |
|
2342 |
ASSERT(mutex_owned(&dnp->dn_lock)); |
|
2343 |
prop_list->prop_ref--; |
|
2344 |
||
2345 |
if (prop_list->prop_ref == 0) { |
|
2346 |
i_ddi_prop_list_delete(prop_list->prop_list); |
|
2347 |
kmem_free(prop_list, sizeof (*prop_list)); |
|
2348 |
} |
|
2349 |
} |
|
2350 |
||
2351 |
/* |
|
2352 |
* Free table of classes by drivers |
|
2353 |
*/ |
|
2354 |
void |
|
2355 |
i_ddi_free_exported_classes(char **classes, int n) |
|
2356 |
{ |
|
2357 |
if ((n == 0) || (classes == NULL)) |
|
2358 |
return; |
|
2359 |
||
2360 |
kmem_free(classes, n * sizeof (char *)); |
|
2361 |
} |
|
2362 |
||
2363 |
/* |
|
2364 |
* Get all classes exported by dip |
|
2365 |
*/ |
|
2366 |
int |
|
2367 |
i_ddi_get_exported_classes(dev_info_t *dip, char ***classes) |
|
2368 |
{ |
|
2369 |
extern void lock_hw_class_list(); |
|
2370 |
extern void unlock_hw_class_list(); |
|
2371 |
extern int get_class(const char *, char **); |
|
2372 |
||
2373 |
static char *rootclass = "root"; |
|
2374 |
int n = 0, nclass = 0; |
|
2375 |
char **buf; |
|
2376 |
||
2377 |
ASSERT(i_ddi_node_state(dip) >= DS_BOUND); |
|
2378 |
||
2379 |
if (dip == ddi_root_node()) /* rootnode exports class "root" */ |
|
2380 |
nclass = 1; |
|
2381 |
lock_hw_class_list(); |
|
2382 |
nclass += get_class(ddi_driver_name(dip), NULL); |
|
2383 |
if (nclass == 0) { |
|
2384 |
unlock_hw_class_list(); |
|
2385 |
return (0); /* no class exported */ |
|
2386 |
} |
|
2387 |
||
2388 |
*classes = buf = kmem_alloc(nclass * sizeof (char *), KM_SLEEP); |
|
2389 |
if (dip == ddi_root_node()) { |
|
2390 |
*buf++ = rootclass; |
|
2391 |
n = 1; |
|
2392 |
} |
|
2393 |
n += get_class(ddi_driver_name(dip), buf); |
|
2394 |
unlock_hw_class_list(); |
|
2395 |
||
2396 |
ASSERT(n == nclass); /* make sure buf wasn't overrun */ |
|
2397 |
return (nclass); |
|
2398 |
} |
|
2399 |
||
2400 |
/* |
|
2401 |
* Helper functions, returns NULL if no memory. |
|
2402 |
*/ |
|
2403 |
char * |
|
2404 |
i_ddi_strdup(char *str, uint_t flag) |
|
2405 |
{ |
|
2406 |
char *copy; |
|
2407 |
||
2408 |
if (str == NULL) |
|
2409 |
return (NULL); |
|
2410 |
||
2411 |
copy = kmem_alloc(strlen(str) + 1, flag); |
|
2412 |
if (copy == NULL) |
|
2413 |
return (NULL); |
|
2414 |
||
2415 |
(void) strcpy(copy, str); |
|
2416 |
return (copy); |
|
2417 |
} |
|
2418 |
||
2419 |
/* |
|
2420 |
* Load driver.conf file for major. Load all if major == -1. |
|
2421 |
* |
|
2422 |
* This is called |
|
2423 |
* - early in boot after devnames array is initialized |
|
2424 |
* - from vfs code when certain file systems are mounted |
|
2425 |
* - from add_drv when a new driver is added |
|
2426 |
*/ |
|
2427 |
int |
|
2428 |
i_ddi_load_drvconf(major_t major) |
|
2429 |
{ |
|
2430 |
extern int modrootloaded; |
|
2431 |
||
2432 |
major_t low, high, m; |
|
2433 |
||
2434 |
if (major == (major_t)-1) { |
|
2435 |
low = 0; |
|
2436 |
high = devcnt - 1; |
|
2437 |
} else { |
|
2438 |
if (major >= devcnt) |
|
2439 |
return (EINVAL); |
|
2440 |
low = high = major; |
|
2441 |
} |
|
2442 |
||
2443 |
for (m = low; m <= high; m++) { |
|
2444 |
struct devnames *dnp = &devnamesp[m]; |
|
2445 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
2446 |
dnp->dn_flags &= ~DN_DRIVER_HELD; |
|
2447 |
(void) impl_make_parlist(m); |
|
2448 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
2449 |
} |
|
2450 |
||
2451 |
if (modrootloaded) { |
|
2452 |
ddi_walk_devs(ddi_root_node(), reset_nexus_flags, |
|
2453 |
(void *)(uintptr_t)major); |
|
2454 |
} |
|
2455 |
||
2456 |
/* build dn_list from old entries in path_to_inst */ |
|
2457 |
e_ddi_unorphan_instance_nos(); |
|
2458 |
return (0); |
|
2459 |
} |
|
2460 |
||
2461 |
/* |
|
2462 |
* Unload a specific driver.conf. |
|
2463 |
* Don't support unload all because it doesn't make any sense |
|
2464 |
*/ |
|
2465 |
int |
|
2466 |
i_ddi_unload_drvconf(major_t major) |
|
2467 |
{ |
|
2468 |
int error; |
|
2469 |
struct devnames *dnp; |
|
2470 |
||
2471 |
if (major >= devcnt) |
|
2472 |
return (EINVAL); |
|
2473 |
||
2474 |
/* |
|
2475 |
* Take the per-driver lock while unloading driver.conf |
|
2476 |
*/ |
|
2477 |
dnp = &devnamesp[major]; |
|
2478 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
2479 |
error = impl_free_parlist(major); |
|
2480 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
2481 |
return (error); |
|
2482 |
} |
|
2483 |
||
2484 |
/* |
|
2485 |
* Merge a .conf node. This is called by nexus drivers to augment |
|
2486 |
* hw node with properties specified in driver.conf file. This function |
|
2487 |
* takes a callback routine to name nexus children. |
|
2488 |
* The parent node must be held busy. |
|
2489 |
* |
|
2490 |
* It returns DDI_SUCCESS if the node is merged and DDI_FAILURE otherwise. |
|
2491 |
*/ |
|
2492 |
int |
|
2493 |
ndi_merge_node(dev_info_t *dip, int (*name_node)(dev_info_t *, char *, int)) |
|
2494 |
{ |
|
2495 |
dev_info_t *hwdip; |
|
2496 |
||
2497 |
ASSERT(ndi_dev_is_persistent_node(dip) == 0); |
|
2498 |
ASSERT(ddi_get_name_addr(dip) != NULL); |
|
2499 |
||
2500 |
hwdip = find_child_by_callback(ddi_get_parent(dip), |
|
2501 |
ddi_binding_name(dip), ddi_get_name_addr(dip), name_node); |
|
2502 |
||
2503 |
/* |
|
2504 |
* Look for the hardware node that is the target of the merge; |
|
2505 |
* return failure if not found. |
|
2506 |
*/ |
|
2507 |
if ((hwdip == NULL) || (hwdip == dip)) { |
|
2508 |
char *buf = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
2509 |
NDI_CONFIG_DEBUG((CE_WARN, "No HW node to merge conf node %s", |
|
2510 |
ddi_deviname(dip, buf))); |
|
2511 |
kmem_free(buf, MAXNAMELEN); |
|
2512 |
return (DDI_FAILURE); |
|
2513 |
} |
|
2514 |
||
2515 |
/* |
|
2516 |
* Make sure the hardware node is uninitialized and has no property. |
|
2517 |
* This may not be the case if new .conf files are load after some |
|
2518 |
* hardware nodes have already been initialized and attached. |
|
2519 |
* |
|
2520 |
* N.B. We return success here because the node was *intended* |
|
2521 |
* to be a merge node because there is a hw node with the name. |
|
2522 |
*/ |
|
2523 |
mutex_enter(&DEVI(hwdip)->devi_lock); |
|
2524 |
if (ndi_dev_is_persistent_node(hwdip) == 0) { |
|
2525 |
char *buf; |
|
2526 |
mutex_exit(&DEVI(hwdip)->devi_lock); |
|
2527 |
||
2528 |
buf = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
2529 |
NDI_CONFIG_DEBUG((CE_NOTE, "Duplicate .conf node %s", |
|
2530 |
ddi_deviname(dip, buf))); |
|
2531 |
kmem_free(buf, MAXNAMELEN); |
|
2532 |
return (DDI_SUCCESS); |
|
2533 |
} |
|
2534 |
||
2535 |
/* |
|
2536 |
* If it is possible that the hardware has already been touched |
|
2537 |
* then don't merge. |
|
2538 |
*/ |
|
2539 |
if (i_ddi_node_state(hwdip) >= DS_INITIALIZED || |
|
2540 |
(DEVI(hwdip)->devi_sys_prop_ptr != NULL) || |
|
2541 |
(DEVI(hwdip)->devi_drv_prop_ptr != NULL)) { |
|
2542 |
char *buf; |
|
2543 |
mutex_exit(&DEVI(hwdip)->devi_lock); |
|
2544 |
||
2545 |
buf = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
2546 |
NDI_CONFIG_DEBUG((CE_NOTE, |
|
2547 |
"!Cannot merge .conf node %s with hw node %p " |
|
2548 |
"-- not in proper state", |
|
2549 |
ddi_deviname(dip, buf), (void *)hwdip)); |
|
2550 |
kmem_free(buf, MAXNAMELEN); |
|
2551 |
return (DDI_SUCCESS); |
|
2552 |
} |
|
2553 |
||
2554 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
2555 |
DEVI(hwdip)->devi_sys_prop_ptr = DEVI(dip)->devi_sys_prop_ptr; |
|
2556 |
DEVI(hwdip)->devi_drv_prop_ptr = DEVI(dip)->devi_drv_prop_ptr; |
|
2557 |
DEVI(dip)->devi_sys_prop_ptr = NULL; |
|
2558 |
DEVI(dip)->devi_drv_prop_ptr = NULL; |
|
2559 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
2560 |
mutex_exit(&DEVI(hwdip)->devi_lock); |
|
2561 |
||
2562 |
return (DDI_SUCCESS); |
|
2563 |
} |
|
2564 |
||
2565 |
/* |
|
2566 |
* Merge a "wildcard" .conf node. This is called by nexus drivers to |
|
2567 |
* augment a set of hw node with properties specified in driver.conf file. |
|
2568 |
* The parent node must be held busy. |
|
2569 |
* |
|
2570 |
* There is no failure mode, since the nexus may or may not have child |
|
2571 |
* node bound the driver specified by the wildcard node. |
|
2572 |
*/ |
|
2573 |
void |
|
2574 |
ndi_merge_wildcard_node(dev_info_t *dip) |
|
2575 |
{ |
|
2576 |
dev_info_t *hwdip; |
|
2577 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
2578 |
major_t major = ddi_driver_major(dip); |
|
2579 |
||
2580 |
/* never attempt to merge a hw node */ |
|
2581 |
ASSERT(ndi_dev_is_persistent_node(dip) == 0); |
|
2582 |
/* must be bound to a driver major number */ |
|
2583 |
ASSERT(major != (major_t)-1); |
|
2584 |
||
2585 |
/* |
|
2586 |
* Walk the child list to find all nodes bound to major |
|
2587 |
* and copy properties. |
|
2588 |
*/ |
|
2589 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
2590 |
for (hwdip = ddi_get_child(pdip); hwdip; |
|
2591 |
hwdip = ddi_get_next_sibling(hwdip)) { |
|
2592 |
/* |
|
2593 |
* Skip nodes not bound to same driver |
|
2594 |
*/ |
|
2595 |
if (ddi_driver_major(hwdip) != major) |
|
2596 |
continue; |
|
2597 |
||
2598 |
/* |
|
2599 |
* Skip .conf nodes |
|
2600 |
*/ |
|
2601 |
if (ndi_dev_is_persistent_node(hwdip) == 0) |
|
2602 |
continue; |
|
2603 |
||
2604 |
/* |
|
2605 |
* Make sure the node is uninitialized and has no property. |
|
2606 |
*/ |
|
2607 |
mutex_enter(&DEVI(hwdip)->devi_lock); |
|
2608 |
if (i_ddi_node_state(hwdip) >= DS_INITIALIZED || |
|
2609 |
(DEVI(hwdip)->devi_sys_prop_ptr != NULL) || |
|
2610 |
(DEVI(hwdip)->devi_drv_prop_ptr != NULL)) { |
|
2611 |
mutex_exit(&DEVI(hwdip)->devi_lock); |
|
2612 |
NDI_CONFIG_DEBUG((CE_NOTE, "HW node %p state not " |
|
2613 |
"suitable for merging wildcard conf node %s", |
|
2614 |
(void *)hwdip, ddi_node_name(dip))); |
|
2615 |
continue; |
|
2616 |
} |
|
2617 |
||
2618 |
DEVI(hwdip)->devi_sys_prop_ptr = |
|
2619 |
i_ddi_prop_list_dup(DEVI(dip)->devi_sys_prop_ptr, KM_SLEEP); |
|
2620 |
DEVI(hwdip)->devi_drv_prop_ptr = |
|
2621 |
i_ddi_prop_list_dup(DEVI(dip)->devi_drv_prop_ptr, KM_SLEEP); |
|
2622 |
mutex_exit(&DEVI(hwdip)->devi_lock); |
|
2623 |
} |
|
2624 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
2625 |
} |
|
2626 |
||
2627 |
/* |
|
2628 |
* Return the major number based on the compatible property. This interface |
|
2629 |
* may be used in situations where we are trying to detect if a better driver |
|
2630 |
* now exists for a device, so it must use the 'compatible' property. If |
|
2631 |
* a non-NULL formp is specified and the binding was based on compatible then |
|
2632 |
* return the pointer to the form used in *formp. |
|
2633 |
*/ |
|
2634 |
major_t |
|
2635 |
ddi_compatible_driver_major(dev_info_t *dip, char **formp) |
|
2636 |
{ |
|
2637 |
struct dev_info *devi = DEVI(dip); |
|
2638 |
void *compat; |
|
2639 |
size_t len; |
|
2640 |
char *p = NULL; |
|
2641 |
major_t major = (major_t)-1; |
|
2642 |
||
2643 |
if (formp) |
|
2644 |
*formp = NULL; |
|
2645 |
||
4145 | 2646 |
/* |
2647 |
* Highest precedence binding is a path-oriented alias. Since this |
|
2648 |
* requires a 'path', this type of binding occurs via more obtuse |
|
2649 |
* 'rebind'. The need for a path-oriented alias 'rebind' is detected |
|
2650 |
* after a successful DDI_CTLOPS_INITCHILD to another driver: this is |
|
2651 |
* is the first point at which the unit-address (or instance) of the |
|
2652 |
* last component of the path is available (even though the path is |
|
2653 |
* bound to the wrong driver at this point). |
|
2654 |
*/ |
|
2655 |
if (devi->devi_flags & DEVI_REBIND) { |
|
2656 |
p = devi->devi_rebinding_name; |
|
2657 |
major = ddi_name_to_major(p); |
|
2658 |
if ((major != (major_t)-1) && |
|
2659 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED)) { |
|
2660 |
if (formp) |
|
2661 |
*formp = p; |
|
2662 |
return (major); |
|
2663 |
} |
|
2664 |
||
2665 |
/* |
|
2666 |
* If for some reason devi_rebinding_name no longer resolves |
|
2667 |
* to a proper driver then clear DEVI_REBIND. |
|
2668 |
*/ |
|
2669 |
mutex_enter(&devi->devi_lock); |
|
2670 |
devi->devi_flags &= ~DEVI_REBIND; |
|
2671 |
mutex_exit(&devi->devi_lock); |
|
2672 |
} |
|
2673 |
||
0 | 2674 |
/* look up compatible property */ |
2675 |
(void) lookup_compatible(dip, KM_SLEEP); |
|
2676 |
compat = (void *)(devi->devi_compat_names); |
|
2677 |
len = devi->devi_compat_length; |
|
2678 |
||
2679 |
/* find the highest precedence compatible form with a driver binding */ |
|
2680 |
while ((p = prom_decode_composite_string(compat, len, p)) != NULL) { |
|
2681 |
major = ddi_name_to_major(p); |
|
2682 |
if ((major != (major_t)-1) && |
|
2683 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED)) { |
|
2684 |
if (formp) |
|
2685 |
*formp = p; |
|
2686 |
return (major); |
|
2687 |
} |
|
2688 |
} |
|
2689 |
||
2690 |
/* |
|
2691 |
* none of the compatible forms have a driver binding, see if |
|
2692 |
* the node name has a driver binding. |
|
2693 |
*/ |
|
2694 |
major = ddi_name_to_major(ddi_node_name(dip)); |
|
2695 |
if ((major != (major_t)-1) && |
|
2696 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED)) |
|
2697 |
return (major); |
|
2698 |
||
2699 |
/* no driver */ |
|
2700 |
return ((major_t)-1); |
|
2701 |
} |
|
2702 |
||
2703 |
/* |
|
2704 |
* Static help functions |
|
2705 |
*/ |
|
2706 |
||
2707 |
/* |
|
2708 |
* lookup the "compatible" property and cache it's contents in the |
|
2709 |
* device node. |
|
2710 |
*/ |
|
2711 |
static int |
|
2712 |
lookup_compatible(dev_info_t *dip, uint_t flag) |
|
2713 |
{ |
|
2714 |
int rv; |
|
2715 |
int prop_flags; |
|
2716 |
uint_t ncompatstrs; |
|
2717 |
char **compatstrpp; |
|
2718 |
char *di_compat_strp; |
|
2719 |
size_t di_compat_strlen; |
|
2720 |
||
2721 |
if (DEVI(dip)->devi_compat_names) { |
|
2722 |
return (DDI_SUCCESS); |
|
2723 |
} |
|
2724 |
||
2725 |
prop_flags = DDI_PROP_TYPE_STRING | DDI_PROP_DONTPASS; |
|
2726 |
||
2727 |
if (flag & KM_NOSLEEP) { |
|
2728 |
prop_flags |= DDI_PROP_DONTSLEEP; |
|
2729 |
} |
|
2730 |
||
2731 |
if (ndi_dev_is_prom_node(dip) == 0) { |
|
2732 |
prop_flags |= DDI_PROP_NOTPROM; |
|
2733 |
} |
|
2734 |
||
2735 |
rv = ddi_prop_lookup_common(DDI_DEV_T_ANY, dip, prop_flags, |
|
2736 |
"compatible", &compatstrpp, &ncompatstrs, |
|
2737 |
ddi_prop_fm_decode_strings); |
|
2738 |
||
2739 |
if (rv == DDI_PROP_NOT_FOUND) { |
|
2740 |
return (DDI_SUCCESS); |
|
2741 |
} |
|
2742 |
||
2743 |
if (rv != DDI_PROP_SUCCESS) { |
|
2744 |
return (DDI_FAILURE); |
|
2745 |
} |
|
2746 |
||
2747 |
/* |
|
2748 |
* encode the compatible property data in the dev_info node |
|
2749 |
*/ |
|
2750 |
rv = DDI_SUCCESS; |
|
2751 |
if (ncompatstrs != 0) { |
|
2752 |
di_compat_strp = encode_composite_string(compatstrpp, |
|
2753 |
ncompatstrs, &di_compat_strlen, flag); |
|
2754 |
if (di_compat_strp != NULL) { |
|
2755 |
DEVI(dip)->devi_compat_names = di_compat_strp; |
|
2756 |
DEVI(dip)->devi_compat_length = di_compat_strlen; |
|
2757 |
} else { |
|
2758 |
rv = DDI_FAILURE; |
|
2759 |
} |
|
2760 |
} |
|
2761 |
ddi_prop_free(compatstrpp); |
|
2762 |
return (rv); |
|
2763 |
} |
|
2764 |
||
2765 |
/* |
|
2766 |
* Create a composite string from a list of strings. |
|
2767 |
* |
|
2768 |
* A composite string consists of a single buffer containing one |
|
2769 |
* or more NULL terminated strings. |
|
2770 |
*/ |
|
2771 |
static char * |
|
2772 |
encode_composite_string(char **strings, uint_t nstrings, size_t *retsz, |
|
2773 |
uint_t flag) |
|
2774 |
{ |
|
2775 |
uint_t index; |
|
2776 |
char **strpp; |
|
2777 |
uint_t slen; |
|
2778 |
size_t cbuf_sz = 0; |
|
2779 |
char *cbuf_p; |
|
2780 |
char *cbuf_ip; |
|
2781 |
||
2782 |
if (strings == NULL || nstrings == 0 || retsz == NULL) { |
|
2783 |
return (NULL); |
|
2784 |
} |
|
2785 |
||
2786 |
for (index = 0, strpp = strings; index < nstrings; index++) |
|
2787 |
cbuf_sz += strlen(*(strpp++)) + 1; |
|
2788 |
||
2789 |
if ((cbuf_p = kmem_alloc(cbuf_sz, flag)) == NULL) { |
|
2790 |
cmn_err(CE_NOTE, |
|
2791 |
"?failed to allocate device node compatstr"); |
|
2792 |
return (NULL); |
|
2793 |
} |
|
2794 |
||
2795 |
cbuf_ip = cbuf_p; |
|
2796 |
for (index = 0, strpp = strings; index < nstrings; index++) { |
|
2797 |
slen = strlen(*strpp); |
|
2798 |
bcopy(*(strpp++), cbuf_ip, slen); |
|
2799 |
cbuf_ip += slen; |
|
2800 |
*(cbuf_ip++) = '\0'; |
|
2801 |
} |
|
2802 |
||
2803 |
*retsz = cbuf_sz; |
|
2804 |
return (cbuf_p); |
|
2805 |
} |
|
2806 |
||
2807 |
static void |
|
2808 |
link_to_driver_list(dev_info_t *dip) |
|
2809 |
{ |
|
2810 |
major_t major = DEVI(dip)->devi_major; |
|
2811 |
struct devnames *dnp; |
|
2812 |
||
2813 |
ASSERT(major != (major_t)-1); |
|
2814 |
||
2815 |
/* |
|
2816 |
* Remove from orphan list |
|
2817 |
*/ |
|
2818 |
if (ndi_dev_is_persistent_node(dip)) { |
|
2819 |
dnp = &orphanlist; |
|
2820 |
remove_from_dn_list(dnp, dip); |
|
2821 |
} |
|
2822 |
||
2823 |
/* |
|
2824 |
* Add to per driver list |
|
2825 |
*/ |
|
2826 |
dnp = &devnamesp[major]; |
|
2827 |
add_to_dn_list(dnp, dip); |
|
2828 |
} |
|
2829 |
||
2830 |
static void |
|
2831 |
unlink_from_driver_list(dev_info_t *dip) |
|
2832 |
{ |
|
2833 |
major_t major = DEVI(dip)->devi_major; |
|
2834 |
struct devnames *dnp; |
|
2835 |
||
2836 |
ASSERT(major != (major_t)-1); |
|
2837 |
||
2838 |
/* |
|
2839 |
* Remove from per-driver list |
|
2840 |
*/ |
|
2841 |
dnp = &devnamesp[major]; |
|
2842 |
remove_from_dn_list(dnp, dip); |
|
2843 |
||
2844 |
/* |
|
2845 |
* Add to orphan list |
|
2846 |
*/ |
|
2847 |
if (ndi_dev_is_persistent_node(dip)) { |
|
2848 |
dnp = &orphanlist; |
|
2849 |
add_to_dn_list(dnp, dip); |
|
2850 |
} |
|
2851 |
} |
|
2852 |
||
2853 |
/* |
|
2854 |
* scan the per-driver list looking for dev_info "dip" |
|
2855 |
*/ |
|
2856 |
static dev_info_t * |
|
2857 |
in_dn_list(struct devnames *dnp, dev_info_t *dip) |
|
2858 |
{ |
|
2859 |
struct dev_info *idevi; |
|
2860 |
||
2861 |
if ((idevi = DEVI(dnp->dn_head)) == NULL) |
|
2862 |
return (NULL); |
|
2863 |
||
2864 |
while (idevi) { |
|
2865 |
if (idevi == DEVI(dip)) |
|
2866 |
return (dip); |
|
2867 |
idevi = idevi->devi_next; |
|
2868 |
} |
|
2869 |
return (NULL); |
|
2870 |
} |
|
2871 |
||
2872 |
/* |
|
2873 |
* insert devinfo node 'dip' into the per-driver instance list |
|
2874 |
* headed by 'dnp' |
|
2875 |
* |
|
2876 |
* Nodes on the per-driver list are ordered: HW - SID - PSEUDO. The order is |
|
2877 |
* required for merging of .conf file data to work properly. |
|
2878 |
*/ |
|
2879 |
static void |
|
2880 |
add_to_ordered_dn_list(struct devnames *dnp, dev_info_t *dip) |
|
2881 |
{ |
|
2882 |
dev_info_t **dipp; |
|
2883 |
||
2884 |
ASSERT(mutex_owned(&(dnp->dn_lock))); |
|
2885 |
||
2886 |
dipp = &dnp->dn_head; |
|
2887 |
if (ndi_dev_is_prom_node(dip)) { |
|
2888 |
/* |
|
2889 |
* Find the first non-prom node or end of list |
|
2890 |
*/ |
|
2891 |
while (*dipp && (ndi_dev_is_prom_node(*dipp) != 0)) { |
|
2892 |
dipp = (dev_info_t **)&DEVI(*dipp)->devi_next; |
|
2893 |
} |
|
2894 |
} else if (ndi_dev_is_persistent_node(dip)) { |
|
2895 |
/* |
|
2896 |
* Find the first non-persistent node |
|
2897 |
*/ |
|
2898 |
while (*dipp && (ndi_dev_is_persistent_node(*dipp) != 0)) { |
|
2899 |
dipp = (dev_info_t **)&DEVI(*dipp)->devi_next; |
|
2900 |
} |
|
2901 |
} else { |
|
2902 |
/* |
|
2903 |
* Find the end of the list |
|
2904 |
*/ |
|
2905 |
while (*dipp) { |
|
2906 |
dipp = (dev_info_t **)&DEVI(*dipp)->devi_next; |
|
2907 |
} |
|
2908 |
} |
|
2909 |
||
2910 |
DEVI(dip)->devi_next = DEVI(*dipp); |
|
2911 |
*dipp = dip; |
|
2912 |
} |
|
2913 |
||
2914 |
/* |
|
2915 |
* add a list of device nodes to the device node list in the |
|
2916 |
* devnames structure |
|
2917 |
*/ |
|
2918 |
static void |
|
2919 |
add_to_dn_list(struct devnames *dnp, dev_info_t *dip) |
|
2920 |
{ |
|
2921 |
/* |
|
2922 |
* Look to see if node already exists |
|
2923 |
*/ |
|
2924 |
LOCK_DEV_OPS(&(dnp->dn_lock)); |
|
2925 |
if (in_dn_list(dnp, dip)) { |
|
2926 |
cmn_err(CE_NOTE, "add_to_dn_list: node %s already in list", |
|
2927 |
DEVI(dip)->devi_node_name); |
|
2928 |
} else { |
|
2929 |
add_to_ordered_dn_list(dnp, dip); |
|
2930 |
} |
|
2931 |
UNLOCK_DEV_OPS(&(dnp->dn_lock)); |
|
2932 |
} |
|
2933 |
||
2934 |
static void |
|
2935 |
remove_from_dn_list(struct devnames *dnp, dev_info_t *dip) |
|
2936 |
{ |
|
2937 |
dev_info_t **plist; |
|
2938 |
||
2939 |
LOCK_DEV_OPS(&(dnp->dn_lock)); |
|
2940 |
||
2941 |
plist = (dev_info_t **)&dnp->dn_head; |
|
2942 |
while (*plist && (*plist != dip)) { |
|
2943 |
plist = (dev_info_t **)&DEVI(*plist)->devi_next; |
|
2944 |
} |
|
2945 |
||
2946 |
if (*plist != NULL) { |
|
2947 |
ASSERT(*plist == dip); |
|
2948 |
*plist = (dev_info_t *)(DEVI(dip)->devi_next); |
|
2949 |
DEVI(dip)->devi_next = NULL; |
|
2950 |
} else { |
|
2951 |
NDI_CONFIG_DEBUG((CE_NOTE, |
|
2952 |
"remove_from_dn_list: node %s not found in list", |
|
2953 |
DEVI(dip)->devi_node_name)); |
|
2954 |
} |
|
2955 |
||
2956 |
UNLOCK_DEV_OPS(&(dnp->dn_lock)); |
|
2957 |
} |
|
2958 |
||
2959 |
/* |
|
2960 |
* Add and remove reference driver global property list |
|
2961 |
*/ |
|
2962 |
static void |
|
2963 |
add_global_props(dev_info_t *dip) |
|
2964 |
{ |
|
2965 |
struct devnames *dnp; |
|
2966 |
ddi_prop_list_t *plist; |
|
2967 |
||
2968 |
ASSERT(DEVI(dip)->devi_global_prop_list == NULL); |
|
2969 |
ASSERT(DEVI(dip)->devi_major != (major_t)-1); |
|
2970 |
||
2971 |
dnp = &devnamesp[DEVI(dip)->devi_major]; |
|
2972 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
2973 |
plist = dnp->dn_global_prop_ptr; |
|
2974 |
if (plist == NULL) { |
|
2975 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
2976 |
return; |
|
2977 |
} |
|
2978 |
i_ddi_prop_list_hold(plist, dnp); |
|
2979 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
2980 |
||
2981 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
2982 |
DEVI(dip)->devi_global_prop_list = plist; |
|
2983 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
2984 |
} |
|
2985 |
||
2986 |
static void |
|
2987 |
remove_global_props(dev_info_t *dip) |
|
2988 |
{ |
|
2989 |
ddi_prop_list_t *proplist; |
|
2990 |
||
2991 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
2992 |
proplist = DEVI(dip)->devi_global_prop_list; |
|
2993 |
DEVI(dip)->devi_global_prop_list = NULL; |
|
2994 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
2995 |
||
2996 |
if (proplist) { |
|
2997 |
major_t major; |
|
2998 |
struct devnames *dnp; |
|
2999 |
||
3000 |
major = ddi_driver_major(dip); |
|
3001 |
ASSERT(major != (major_t)-1); |
|
3002 |
dnp = &devnamesp[major]; |
|
3003 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
3004 |
i_ddi_prop_list_rele(proplist, dnp); |
|
3005 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
3006 |
} |
|
3007 |
} |
|
3008 |
||
3009 |
#ifdef DEBUG |
|
3010 |
/* |
|
3011 |
* Set this variable to '0' to disable the optimization, |
|
3012 |
* and to 2 to print debug message. |
|
3013 |
*/ |
|
3014 |
static int optimize_dtree = 1; |
|
3015 |
||
3016 |
static void |
|
3017 |
debug_dtree(dev_info_t *devi, struct dev_info *adevi, char *service) |
|
3018 |
{ |
|
3019 |
char *adeviname, *buf; |
|
3020 |
||
3021 |
/* |
|
3022 |
* Don't print unless optimize dtree is set to 2+ |
|
3023 |
*/ |
|
3024 |
if (optimize_dtree <= 1) |
|
3025 |
return; |
|
3026 |
||
3027 |
buf = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
3028 |
adeviname = ddi_deviname((dev_info_t *)adevi, buf); |
|
3029 |
if (*adeviname == '\0') |
|
3030 |
adeviname = "root"; |
|
3031 |
||
3032 |
cmn_err(CE_CONT, "%s %s -> %s\n", |
|
3033 |
ddi_deviname(devi, buf), service, adeviname); |
|
3034 |
||
3035 |
kmem_free(buf, MAXNAMELEN); |
|
3036 |
} |
|
3037 |
#else /* DEBUG */ |
|
3038 |
#define debug_dtree(a1, a2, a3) /* nothing */ |
|
3039 |
#endif /* DEBUG */ |
|
3040 |
||
3041 |
static void |
|
3042 |
ddi_optimize_dtree(dev_info_t *devi) |
|
3043 |
{ |
|
3044 |
struct dev_info *pdevi; |
|
3045 |
struct bus_ops *b; |
|
3046 |
||
3047 |
pdevi = DEVI(devi)->devi_parent; |
|
3048 |
ASSERT(pdevi); |
|
3049 |
||
3050 |
/* |
|
3051 |
* Set the unoptimized values |
|
3052 |
*/ |
|
3053 |
DEVI(devi)->devi_bus_map_fault = pdevi; |
|
3054 |
DEVI(devi)->devi_bus_dma_map = pdevi; |
|
3055 |
DEVI(devi)->devi_bus_dma_allochdl = pdevi; |
|
3056 |
DEVI(devi)->devi_bus_dma_freehdl = pdevi; |
|
3057 |
DEVI(devi)->devi_bus_dma_bindhdl = pdevi; |
|
3058 |
DEVI(devi)->devi_bus_dma_bindfunc = |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
3059 |
pdevi->devi_ops->devo_bus_ops->bus_dma_bindhdl; |
0 | 3060 |
DEVI(devi)->devi_bus_dma_unbindhdl = pdevi; |
3061 |
DEVI(devi)->devi_bus_dma_unbindfunc = |
|
3062 |
pdevi->devi_ops->devo_bus_ops->bus_dma_unbindhdl; |
|
3063 |
DEVI(devi)->devi_bus_dma_flush = pdevi; |
|
3064 |
DEVI(devi)->devi_bus_dma_win = pdevi; |
|
3065 |
DEVI(devi)->devi_bus_dma_ctl = pdevi; |
|
3066 |
DEVI(devi)->devi_bus_ctl = pdevi; |
|
3067 |
||
3068 |
#ifdef DEBUG |
|
3069 |
if (optimize_dtree == 0) |
|
3070 |
return; |
|
3071 |
#endif /* DEBUG */ |
|
3072 |
||
3073 |
b = pdevi->devi_ops->devo_bus_ops; |
|
3074 |
||
3075 |
if (i_ddi_map_fault == b->bus_map_fault) { |
|
3076 |
DEVI(devi)->devi_bus_map_fault = pdevi->devi_bus_map_fault; |
|
3077 |
debug_dtree(devi, DEVI(devi)->devi_bus_map_fault, |
|
3078 |
"bus_map_fault"); |
|
3079 |
} |
|
3080 |
||
3081 |
if (ddi_dma_map == b->bus_dma_map) { |
|
3082 |
DEVI(devi)->devi_bus_dma_map = pdevi->devi_bus_dma_map; |
|
3083 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_map, "bus_dma_map"); |
|
3084 |
} |
|
3085 |
||
3086 |
if (ddi_dma_allochdl == b->bus_dma_allochdl) { |
|
3087 |
DEVI(devi)->devi_bus_dma_allochdl = |
|
3088 |
pdevi->devi_bus_dma_allochdl; |
|
3089 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_allochdl, |
|
3090 |
"bus_dma_allochdl"); |
|
3091 |
} |
|
3092 |
||
3093 |
if (ddi_dma_freehdl == b->bus_dma_freehdl) { |
|
3094 |
DEVI(devi)->devi_bus_dma_freehdl = pdevi->devi_bus_dma_freehdl; |
|
3095 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_freehdl, |
|
3096 |
"bus_dma_freehdl"); |
|
3097 |
} |
|
3098 |
||
3099 |
if (ddi_dma_bindhdl == b->bus_dma_bindhdl) { |
|
3100 |
DEVI(devi)->devi_bus_dma_bindhdl = pdevi->devi_bus_dma_bindhdl; |
|
3101 |
DEVI(devi)->devi_bus_dma_bindfunc = |
|
3102 |
pdevi->devi_bus_dma_bindhdl->devi_ops-> |
|
3103 |
devo_bus_ops->bus_dma_bindhdl; |
|
3104 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_bindhdl, |
|
3105 |
"bus_dma_bindhdl"); |
|
3106 |
} |
|
3107 |
||
3108 |
if (ddi_dma_unbindhdl == b->bus_dma_unbindhdl) { |
|
3109 |
DEVI(devi)->devi_bus_dma_unbindhdl = |
|
3110 |
pdevi->devi_bus_dma_unbindhdl; |
|
3111 |
DEVI(devi)->devi_bus_dma_unbindfunc = |
|
3112 |
pdevi->devi_bus_dma_unbindhdl->devi_ops-> |
|
3113 |
devo_bus_ops->bus_dma_unbindhdl; |
|
3114 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_unbindhdl, |
|
3115 |
"bus_dma_unbindhdl"); |
|
3116 |
} |
|
3117 |
||
3118 |
if (ddi_dma_flush == b->bus_dma_flush) { |
|
3119 |
DEVI(devi)->devi_bus_dma_flush = pdevi->devi_bus_dma_flush; |
|
3120 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_flush, |
|
3121 |
"bus_dma_flush"); |
|
3122 |
} |
|
3123 |
||
3124 |
if (ddi_dma_win == b->bus_dma_win) { |
|
3125 |
DEVI(devi)->devi_bus_dma_win = pdevi->devi_bus_dma_win; |
|
3126 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_win, |
|
3127 |
"bus_dma_win"); |
|
3128 |
} |
|
3129 |
||
3130 |
if (ddi_dma_mctl == b->bus_dma_ctl) { |
|
3131 |
DEVI(devi)->devi_bus_dma_ctl = pdevi->devi_bus_dma_ctl; |
|
3132 |
debug_dtree(devi, DEVI(devi)->devi_bus_dma_ctl, "bus_dma_ctl"); |
|
3133 |
} |
|
3134 |
||
3135 |
if (ddi_ctlops == b->bus_ctl) { |
|
3136 |
DEVI(devi)->devi_bus_ctl = pdevi->devi_bus_ctl; |
|
3137 |
debug_dtree(devi, DEVI(devi)->devi_bus_ctl, "bus_ctl"); |
|
3138 |
} |
|
3139 |
} |
|
3140 |
||
3141 |
#define MIN_DEVINFO_LOG_SIZE max_ncpus |
|
3142 |
#define MAX_DEVINFO_LOG_SIZE max_ncpus * 10 |
|
3143 |
||
3144 |
static void |
|
3145 |
da_log_init() |
|
3146 |
{ |
|
3147 |
devinfo_log_header_t *dh; |
|
3148 |
int logsize = devinfo_log_size; |
|
3149 |
||
3150 |
if (logsize == 0) |
|
3151 |
logsize = MIN_DEVINFO_LOG_SIZE; |
|
3152 |
else if (logsize > MAX_DEVINFO_LOG_SIZE) |
|
3153 |
logsize = MAX_DEVINFO_LOG_SIZE; |
|
3154 |
||
3155 |
dh = kmem_alloc(logsize * PAGESIZE, KM_SLEEP); |
|
3156 |
mutex_init(&dh->dh_lock, NULL, MUTEX_DEFAULT, NULL); |
|
3157 |
dh->dh_max = ((logsize * PAGESIZE) - sizeof (*dh)) / |
|
3158 |
sizeof (devinfo_audit_t) + 1; |
|
3159 |
dh->dh_curr = -1; |
|
3160 |
dh->dh_hits = 0; |
|
3161 |
||
3162 |
devinfo_audit_log = dh; |
|
3163 |
} |
|
3164 |
||
3165 |
/* |
|
3166 |
* Log the stack trace in per-devinfo audit structure and also enter |
|
3167 |
* it into a system wide log for recording the time history. |
|
3168 |
*/ |
|
3169 |
static void |
|
3170 |
da_log_enter(dev_info_t *dip) |
|
3171 |
{ |
|
3172 |
devinfo_audit_t *da_log, *da = DEVI(dip)->devi_audit; |
|
3173 |
devinfo_log_header_t *dh = devinfo_audit_log; |
|
3174 |
||
3175 |
if (devinfo_audit_log == NULL) |
|
3176 |
return; |
|
3177 |
||
3178 |
ASSERT(da != NULL); |
|
3179 |
||
3180 |
da->da_devinfo = dip; |
|
3181 |
da->da_timestamp = gethrtime(); |
|
3182 |
da->da_thread = curthread; |
|
3183 |
da->da_node_state = DEVI(dip)->devi_node_state; |
|
3184 |
da->da_device_state = DEVI(dip)->devi_state; |
|
3185 |
da->da_depth = getpcstack(da->da_stack, DDI_STACK_DEPTH); |
|
3186 |
||
3187 |
/* |
|
3188 |
* Copy into common log and note the location for tracing history |
|
3189 |
*/ |
|
3190 |
mutex_enter(&dh->dh_lock); |
|
3191 |
dh->dh_hits++; |
|
3192 |
dh->dh_curr++; |
|
3193 |
if (dh->dh_curr >= dh->dh_max) |
|
3194 |
dh->dh_curr -= dh->dh_max; |
|
3195 |
da_log = &dh->dh_entry[dh->dh_curr]; |
|
3196 |
mutex_exit(&dh->dh_lock); |
|
3197 |
||
3198 |
bcopy(da, da_log, sizeof (devinfo_audit_t)); |
|
3199 |
da->da_lastlog = da_log; |
|
3200 |
} |
|
3201 |
||
3202 |
static void |
|
3203 |
attach_drivers() |
|
3204 |
{ |
|
3205 |
int i; |
|
3206 |
for (i = 0; i < devcnt; i++) { |
|
3207 |
struct devnames *dnp = &devnamesp[i]; |
|
3208 |
if ((dnp->dn_flags & DN_FORCE_ATTACH) && |
|
3209 |
(ddi_hold_installed_driver((major_t)i) != NULL)) |
|
3210 |
ddi_rele_driver((major_t)i); |
|
3211 |
} |
|
3212 |
} |
|
3213 |
||
3214 |
/* |
|
3215 |
* Launch a thread to force attach drivers. This avoids penalty on boot time. |
|
3216 |
*/ |
|
3217 |
void |
|
3218 |
i_ddi_forceattach_drivers() |
|
3219 |
{ |
|
3220 |
/* |
|
3221 |
* On i386, the USB drivers need to load and take over from the |
|
3222 |
* SMM BIOS drivers ASAP after consconfig(), so make sure they |
|
3223 |
* get loaded right here rather than letting the thread do it. |
|
3224 |
* |
|
3225 |
* The order here is important. EHCI must be loaded first, as |
|
3226 |
* we have observed many systems on which hangs occur if the |
|
3227 |
* {U,O}HCI companion controllers take over control from the BIOS |
|
3228 |
* before EHCI does. These hangs are also caused by BIOSes leaving |
|
3229 |
* interrupt-on-port-change enabled in the ehci controller, so that |
|
3230 |
* when uhci/ohci reset themselves, it induces a port change on |
|
3231 |
* the ehci companion controller. Since there's no interrupt handler |
|
3232 |
* installed at the time, the moment that interrupt is unmasked, an |
|
3233 |
* interrupt storm will occur. All this is averted when ehci is |
|
3234 |
* loaded first. And now you know..... the REST of the story. |
|
3235 |
* |
|
3236 |
* Regardless of platform, ehci needs to initialize first to avoid |
|
3237 |
* unnecessary connects and disconnects on the companion controller |
|
3238 |
* when ehci sets up the routing. |
|
3239 |
*/ |
|
3240 |
(void) ddi_hold_installed_driver(ddi_name_to_major("ehci")); |
|
3241 |
(void) ddi_hold_installed_driver(ddi_name_to_major("uhci")); |
|
3242 |
(void) ddi_hold_installed_driver(ddi_name_to_major("ohci")); |
|
3243 |
||
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3244 |
/* |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3245 |
* Attach IB VHCI driver before the force-attach thread attaches the |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3246 |
* IB HCA driver. IB HCA driver will fail if IB Nexus has not yet |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3247 |
* been attached. |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3248 |
*/ |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3249 |
(void) ddi_hold_installed_driver(ddi_name_to_major("ib")); |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
3250 |
|
0 | 3251 |
(void) thread_create(NULL, 0, (void (*)())attach_drivers, NULL, 0, &p0, |
3252 |
TS_RUN, minclsyspri); |
|
3253 |
} |
|
3254 |
||
3255 |
/* |
|
3256 |
* This is a private DDI interface for optimizing boot performance. |
|
3257 |
* I/O subsystem initialization is considered complete when devfsadm |
|
3258 |
* is executed. |
|
3259 |
* |
|
2621 | 3260 |
* NOTE: The start of syseventd happens to be a convenient indicator |
3261 |
* of the completion of I/O initialization during boot. |
|
0 | 3262 |
* The implementation should be replaced by something more robust. |
3263 |
*/ |
|
3264 |
int |
|
3265 |
i_ddi_io_initialized() |
|
3266 |
{ |
|
3267 |
extern int sysevent_daemon_init; |
|
3268 |
return (sysevent_daemon_init); |
|
3269 |
} |
|
3270 |
||
2621 | 3271 |
/* |
3272 |
* May be used to determine system boot state |
|
3273 |
* "Available" means the system is for the most part up |
|
3274 |
* and initialized, with all system services either up or |
|
3275 |
* capable of being started. This state is set by devfsadm |
|
3276 |
* during the boot process. The /dev filesystem infers |
|
3277 |
* from this when implicit reconfig can be performed, |
|
3278 |
* ie, devfsadm can be invoked. Please avoid making |
|
3279 |
* further use of this unless it's really necessary. |
|
3280 |
*/ |
|
3281 |
int |
|
3282 |
i_ddi_sysavail() |
|
3283 |
{ |
|
3284 |
return (devname_state & DS_SYSAVAIL); |
|
3285 |
} |
|
3286 |
||
3287 |
/* |
|
3288 |
* May be used to determine if boot is a reconfigure boot. |
|
3289 |
*/ |
|
3290 |
int |
|
3291 |
i_ddi_reconfig() |
|
3292 |
{ |
|
3293 |
return (devname_state & DS_RECONFIG); |
|
3294 |
} |
|
3295 |
||
3296 |
/* |
|
3297 |
* Note system services are up, inform /dev. |
|
3298 |
*/ |
|
3299 |
void |
|
3300 |
i_ddi_set_sysavail() |
|
3301 |
{ |
|
3302 |
if ((devname_state & DS_SYSAVAIL) == 0) { |
|
3303 |
devname_state |= DS_SYSAVAIL; |
|
3304 |
sdev_devstate_change(); |
|
3305 |
} |
|
3306 |
} |
|
3307 |
||
3308 |
/* |
|
3309 |
* Note reconfiguration boot, inform /dev. |
|
3310 |
*/ |
|
3311 |
void |
|
3312 |
i_ddi_set_reconfig() |
|
3313 |
{ |
|
3314 |
if ((devname_state & DS_RECONFIG) == 0) { |
|
3315 |
devname_state |= DS_RECONFIG; |
|
3316 |
sdev_devstate_change(); |
|
3317 |
} |
|
3318 |
} |
|
3319 |
||
0 | 3320 |
|
3321 |
/* |
|
3322 |
* device tree walking |
|
3323 |
*/ |
|
3324 |
||
3325 |
struct walk_elem { |
|
3326 |
struct walk_elem *next; |
|
3327 |
dev_info_t *dip; |
|
3328 |
}; |
|
3329 |
||
3330 |
static void |
|
3331 |
free_list(struct walk_elem *list) |
|
3332 |
{ |
|
3333 |
while (list) { |
|
3334 |
struct walk_elem *next = list->next; |
|
3335 |
kmem_free(list, sizeof (*list)); |
|
3336 |
list = next; |
|
3337 |
} |
|
3338 |
} |
|
3339 |
||
3340 |
static void |
|
3341 |
append_node(struct walk_elem **list, dev_info_t *dip) |
|
3342 |
{ |
|
3343 |
struct walk_elem *tail; |
|
3344 |
struct walk_elem *elem = kmem_alloc(sizeof (*elem), KM_SLEEP); |
|
3345 |
||
3346 |
elem->next = NULL; |
|
3347 |
elem->dip = dip; |
|
3348 |
||
3349 |
if (*list == NULL) { |
|
3350 |
*list = elem; |
|
3351 |
return; |
|
3352 |
} |
|
3353 |
||
3354 |
tail = *list; |
|
3355 |
while (tail->next) |
|
3356 |
tail = tail->next; |
|
3357 |
||
3358 |
tail->next = elem; |
|
3359 |
} |
|
3360 |
||
3361 |
/* |
|
3362 |
* The implementation of ddi_walk_devs(). |
|
3363 |
*/ |
|
3364 |
static int |
|
3365 |
walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg, |
|
3366 |
int do_locking) |
|
3367 |
{ |
|
3368 |
struct walk_elem *head = NULL; |
|
3369 |
||
3370 |
/* |
|
3371 |
* Do it in two passes. First pass invoke callback on each |
|
3372 |
* dip on the sibling list. Second pass invoke callback on |
|
3373 |
* children of each dip. |
|
3374 |
*/ |
|
3375 |
while (dip) { |
|
3376 |
switch ((*f)(dip, arg)) { |
|
3377 |
case DDI_WALK_TERMINATE: |
|
3378 |
free_list(head); |
|
3379 |
return (DDI_WALK_TERMINATE); |
|
3380 |
||
3381 |
case DDI_WALK_PRUNESIB: |
|
3382 |
/* ignore sibling by setting dip to NULL */ |
|
3383 |
append_node(&head, dip); |
|
3384 |
dip = NULL; |
|
3385 |
break; |
|
3386 |
||
3387 |
case DDI_WALK_PRUNECHILD: |
|
3388 |
/* don't worry about children */ |
|
3389 |
dip = ddi_get_next_sibling(dip); |
|
3390 |
break; |
|
3391 |
||
3392 |
case DDI_WALK_CONTINUE: |
|
3393 |
default: |
|
3394 |
append_node(&head, dip); |
|
3395 |
dip = ddi_get_next_sibling(dip); |
|
3396 |
break; |
|
3397 |
} |
|
3398 |
||
3399 |
} |
|
3400 |
||
3401 |
/* second pass */ |
|
3402 |
while (head) { |
|
3403 |
int circ; |
|
3404 |
struct walk_elem *next = head->next; |
|
3405 |
||
3406 |
if (do_locking) |
|
3407 |
ndi_devi_enter(head->dip, &circ); |
|
3408 |
if (walk_devs(ddi_get_child(head->dip), f, arg, do_locking) == |
|
3409 |
DDI_WALK_TERMINATE) { |
|
3410 |
if (do_locking) |
|
3411 |
ndi_devi_exit(head->dip, circ); |
|
3412 |
free_list(head); |
|
3413 |
return (DDI_WALK_TERMINATE); |
|
3414 |
} |
|
3415 |
if (do_locking) |
|
3416 |
ndi_devi_exit(head->dip, circ); |
|
3417 |
kmem_free(head, sizeof (*head)); |
|
3418 |
head = next; |
|
3419 |
} |
|
3420 |
||
3421 |
return (DDI_WALK_CONTINUE); |
|
3422 |
} |
|
3423 |
||
3424 |
/* |
|
3425 |
* This general-purpose routine traverses the tree of dev_info nodes, |
|
3426 |
* starting from the given node, and calls the given function for each |
|
3427 |
* node that it finds with the current node and the pointer arg (which |
|
3428 |
* can point to a structure of information that the function |
|
3429 |
* needs) as arguments. |
|
3430 |
* |
|
3431 |
* It does the walk a layer at a time, not depth-first. The given function |
|
3432 |
* must return one of the following values: |
|
3433 |
* DDI_WALK_CONTINUE |
|
3434 |
* DDI_WALK_PRUNESIB |
|
3435 |
* DDI_WALK_PRUNECHILD |
|
3436 |
* DDI_WALK_TERMINATE |
|
3437 |
* |
|
3438 |
* N.B. Since we walk the sibling list, the caller must ensure that |
|
3439 |
* the parent of dip is held against changes, unless the parent |
|
3440 |
* is rootnode. ndi_devi_enter() on the parent is sufficient. |
|
3441 |
* |
|
3442 |
* To avoid deadlock situations, caller must not attempt to |
|
3443 |
* configure/unconfigure/remove device node in (*f)(), nor should |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3444 |
* it attempt to recurse on other nodes in the system. Any |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3445 |
* ndi_devi_enter() done by (*f)() must occur 'at-or-below' the |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3446 |
* node entered prior to ddi_walk_devs(). Furthermore, if (*f)() |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3447 |
* does any multi-threading (in framework *or* in driver) then the |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3448 |
* ndi_devi_enter() calls done by dependent threads must be |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
3449 |
* 'strictly-below'. |
0 | 3450 |
* |
3451 |
* This is not callable from device autoconfiguration routines. |
|
3452 |
* They include, but not limited to, _init(9e), _fini(9e), probe(9e), |
|
3453 |
* attach(9e), and detach(9e). |
|
3454 |
*/ |
|
3455 |
||
3456 |
void |
|
3457 |
ddi_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg) |
|
3458 |
{ |
|
3459 |
||
3460 |
ASSERT(dip == NULL || ddi_get_parent(dip) == NULL || |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
3461 |
DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
0 | 3462 |
|
3463 |
(void) walk_devs(dip, f, arg, 1); |
|
3464 |
} |
|
3465 |
||
3466 |
/* |
|
3467 |
* This is a general-purpose routine traverses the per-driver list |
|
3468 |
* and calls the given function for each node. must return one of |
|
3469 |
* the following values: |
|
3470 |
* DDI_WALK_CONTINUE |
|
3471 |
* DDI_WALK_TERMINATE |
|
3472 |
* |
|
3473 |
* N.B. The same restrictions from ddi_walk_devs() apply. |
|
3474 |
*/ |
|
3475 |
||
3476 |
void |
|
3477 |
e_ddi_walk_driver(char *drv, int (*f)(dev_info_t *, void *), void *arg) |
|
3478 |
{ |
|
3479 |
major_t major; |
|
3480 |
struct devnames *dnp; |
|
3481 |
dev_info_t *dip; |
|
3482 |
||
3483 |
major = ddi_name_to_major(drv); |
|
3484 |
if (major == (major_t)-1) |
|
3485 |
return; |
|
3486 |
||
3487 |
dnp = &devnamesp[major]; |
|
3488 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
3489 |
dip = dnp->dn_head; |
|
3490 |
while (dip) { |
|
3491 |
ndi_hold_devi(dip); |
|
3492 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
3493 |
if ((*f)(dip, arg) == DDI_WALK_TERMINATE) { |
|
3494 |
ndi_rele_devi(dip); |
|
3495 |
return; |
|
3496 |
} |
|
3497 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
3498 |
ndi_rele_devi(dip); |
|
3499 |
dip = ddi_get_next(dip); |
|
3500 |
} |
|
3501 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
3502 |
} |
|
3503 |
||
3504 |
/* |
|
3505 |
* argument to i_find_devi, a devinfo node search callback function. |
|
3506 |
*/ |
|
3507 |
struct match_info { |
|
3508 |
dev_info_t *dip; /* result */ |
|
3509 |
char *nodename; /* if non-null, nodename must match */ |
|
3510 |
int instance; /* if != -1, instance must match */ |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
3511 |
int attached; /* if != 0, i_ddi_devi_attached() */ |
0 | 3512 |
}; |
3513 |
||
3514 |
static int |
|
3515 |
i_find_devi(dev_info_t *dip, void *arg) |
|
3516 |
{ |
|
3517 |
struct match_info *info = (struct match_info *)arg; |
|
3518 |
||
3519 |
if (((info->nodename == NULL) || |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
3520 |
(strcmp(ddi_node_name(dip), info->nodename) == 0)) && |
0 | 3521 |
((info->instance == -1) || |
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
3522 |
(ddi_get_instance(dip) == info->instance)) && |
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
3523 |
((info->attached == 0) || i_ddi_devi_attached(dip))) { |
0 | 3524 |
info->dip = dip; |
3525 |
ndi_hold_devi(dip); |
|
3526 |
return (DDI_WALK_TERMINATE); |
|
3527 |
} |
|
3528 |
||
3529 |
return (DDI_WALK_CONTINUE); |
|
3530 |
} |
|
3531 |
||
3532 |
/* |
|
3533 |
* Find dip with a known node name and instance and return with it held |
|
3534 |
*/ |
|
3535 |
dev_info_t * |
|
3536 |
ddi_find_devinfo(char *nodename, int instance, int attached) |
|
3537 |
{ |
|
3538 |
struct match_info info; |
|
3539 |
||
3540 |
info.nodename = nodename; |
|
3541 |
info.instance = instance; |
|
3542 |
info.attached = attached; |
|
3543 |
info.dip = NULL; |
|
3544 |
||
3545 |
ddi_walk_devs(ddi_root_node(), i_find_devi, &info); |
|
3546 |
return (info.dip); |
|
3547 |
} |
|
3548 |
||
3549 |
/* |
|
3550 |
* Parse for name, addr, and minor names. Some args may be NULL. |
|
3551 |
*/ |
|
3552 |
void |
|
3553 |
i_ddi_parse_name(char *name, char **nodename, char **addrname, char **minorname) |
|
3554 |
{ |
|
3555 |
char *cp; |
|
3556 |
static char nulladdrname[] = ""; |
|
3557 |
||
3558 |
/* default values */ |
|
3559 |
if (nodename) |
|
3560 |
*nodename = name; |
|
3561 |
if (addrname) |
|
3562 |
*addrname = nulladdrname; |
|
3563 |
if (minorname) |
|
3564 |
*minorname = NULL; |
|
3565 |
||
3566 |
cp = name; |
|
3567 |
while (*cp != '\0') { |
|
3568 |
if (addrname && *cp == '@') { |
|
3569 |
*addrname = cp + 1; |
|
3570 |
*cp = '\0'; |
|
3571 |
} else if (minorname && *cp == ':') { |
|
3572 |
*minorname = cp + 1; |
|
3573 |
*cp = '\0'; |
|
3574 |
} |
|
3575 |
++cp; |
|
3576 |
} |
|
3577 |
} |
|
3578 |
||
3579 |
static char * |
|
3580 |
child_path_to_driver(dev_info_t *parent, char *child_name, char *unit_address) |
|
3581 |
{ |
|
3582 |
char *p, *drvname = NULL; |
|
3583 |
major_t maj; |
|
3584 |
||
3585 |
/* |
|
3586 |
* Construct the pathname and ask the implementation |
|
3587 |
* if it can do a driver = f(pathname) for us, if not |
|
3588 |
* we'll just default to using the node-name that |
|
3589 |
* was given to us. We want to do this first to |
|
3590 |
* allow the platform to use 'generic' names for |
|
3591 |
* legacy device drivers. |
|
3592 |
*/ |
|
3593 |
p = kmem_zalloc(MAXPATHLEN, KM_SLEEP); |
|
3594 |
(void) ddi_pathname(parent, p); |
|
3595 |
(void) strcat(p, "/"); |
|
3596 |
(void) strcat(p, child_name); |
|
3597 |
if (unit_address && *unit_address) { |
|
3598 |
(void) strcat(p, "@"); |
|
3599 |
(void) strcat(p, unit_address); |
|
3600 |
} |
|
3601 |
||
3602 |
/* |
|
3603 |
* Get the binding. If there is none, return the child_name |
|
3604 |
* and let the caller deal with it. |
|
3605 |
*/ |
|
3606 |
maj = path_to_major(p); |
|
3607 |
||
3608 |
kmem_free(p, MAXPATHLEN); |
|
3609 |
||
3610 |
if (maj != (major_t)-1) |
|
3611 |
drvname = ddi_major_to_name(maj); |
|
3612 |
if (drvname == NULL) |
|
3613 |
drvname = child_name; |
|
3614 |
||
3615 |
return (drvname); |
|
3616 |
} |
|
3617 |
||
3618 |
||
3619 |
/* |
|
3620 |
* Given the pathname of a device, fill in the dev_info_t value and/or the |
|
3621 |
* dev_t value and/or the spectype, depending on which parameters are non-NULL. |
|
3622 |
* If there is an error, this function returns -1. |
|
3623 |
* |
|
3624 |
* NOTE: If this function returns the dev_info_t structure, then it |
|
3625 |
* does so with a hold on the devi. Caller should ensure that they get |
|
3626 |
* decremented via ddi_release_devi() or ndi_rele_devi(); |
|
3627 |
* |
|
3628 |
* This function can be invoked in the boot case for a pathname without |
|
3629 |
* device argument (:xxxx), traditionally treated as a minor name. |
|
3630 |
* In this case, we do the following |
|
3631 |
* (1) search the minor node of type DDM_DEFAULT. |
|
3632 |
* (2) if no DDM_DEFAULT minor exists, then the first non-alias minor is chosen. |
|
3633 |
* (3) if neither exists, a dev_t is faked with minor number = instance. |
|
3634 |
* As of S9 FCS, no instance of #1 exists. #2 is used by several platforms |
|
3635 |
* to default the boot partition to :a possibly by other OBP definitions. |
|
3636 |
* #3 is used for booting off network interfaces, most SPARC network |
|
3637 |
* drivers support Style-2 only, so only DDM_ALIAS minor exists. |
|
3638 |
* |
|
3639 |
* It is possible for OBP to present device args at the end of the path as |
|
3640 |
* well as in the middle. For example, with IB the following strings are |
|
3641 |
* valid boot paths. |
|
3642 |
* a /pci@8,700000/ib@1,2:port=1,pkey=ff,dhcp,... |
|
3643 |
* b /pci@8,700000/ib@1,1:port=1/ioc@xxxxxx,yyyyyyy:dhcp |
|
3644 |
* Case (a), we first look for minor node "port=1,pkey...". |
|
3645 |
* Failing that, we will pass "port=1,pkey..." to the bus_config |
|
3646 |
* entry point of ib (HCA) driver. |
|
3647 |
* Case (b), configure ib@1,1 as usual. Then invoke ib's bus_config |
|
3648 |
* with argument "ioc@xxxxxxx,yyyyyyy:port=1". After configuring |
|
3649 |
* the ioc, look for minor node dhcp. If not found, pass ":dhcp" |
|
3650 |
* to ioc's bus_config entry point. |
|
3651 |
*/ |
|
3652 |
int |
|
3653 |
resolve_pathname(char *pathname, |
|
3654 |
dev_info_t **dipp, dev_t *devtp, int *spectypep) |
|
3655 |
{ |
|
3656 |
int error; |
|
3657 |
dev_info_t *parent, *child; |
|
3658 |
struct pathname pn; |
|
3659 |
char *component, *config_name; |
|
3660 |
char *minorname = NULL; |
|
3661 |
char *prev_minor = NULL; |
|
3662 |
dev_t devt = NODEV; |
|
3663 |
int spectype; |
|
3664 |
struct ddi_minor_data *dmn; |
|
3665 |
||
3666 |
if (*pathname != '/') |
|
3667 |
return (EINVAL); |
|
3668 |
parent = ddi_root_node(); /* Begin at the top of the tree */ |
|
3669 |
||
3670 |
if (error = pn_get(pathname, UIO_SYSSPACE, &pn)) |
|
3671 |
return (error); |
|
3672 |
pn_skipslash(&pn); |
|
3673 |
||
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
3674 |
ASSERT(i_ddi_devi_attached(parent)); |
0 | 3675 |
ndi_hold_devi(parent); |
3676 |
||
3677 |
component = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
3678 |
config_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
3679 |
||
3680 |
while (pn_pathleft(&pn)) { |
|
3681 |
/* remember prev minor (:xxx) in the middle of path */ |
|
3682 |
if (minorname) |
|
3683 |
prev_minor = i_ddi_strdup(minorname, KM_SLEEP); |
|
3684 |
||
3685 |
/* Get component and chop off minorname */ |
|
3686 |
(void) pn_getcomponent(&pn, component); |
|
3687 |
i_ddi_parse_name(component, NULL, NULL, &minorname); |
|
3688 |
||
3689 |
if (prev_minor == NULL) { |
|
3690 |
(void) snprintf(config_name, MAXNAMELEN, "%s", |
|
3691 |
component); |
|
3692 |
} else { |
|
3693 |
(void) snprintf(config_name, MAXNAMELEN, "%s:%s", |
|
3694 |
component, prev_minor); |
|
3695 |
kmem_free(prev_minor, strlen(prev_minor) + 1); |
|
3696 |
prev_minor = NULL; |
|
3697 |
} |
|
3698 |
||
3699 |
/* |
|
3700 |
* Find and configure the child |
|
3701 |
*/ |
|
3702 |
if (ndi_devi_config_one(parent, config_name, &child, |
|
3703 |
NDI_PROMNAME | NDI_NO_EVENT) != NDI_SUCCESS) { |
|
3704 |
ndi_rele_devi(parent); |
|
3705 |
pn_free(&pn); |
|
3706 |
kmem_free(component, MAXNAMELEN); |
|
3707 |
kmem_free(config_name, MAXNAMELEN); |
|
3708 |
return (-1); |
|
3709 |
} |
|
3710 |
||
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
3711 |
ASSERT(i_ddi_devi_attached(child)); |
0 | 3712 |
ndi_rele_devi(parent); |
3713 |
parent = child; |
|
3714 |
pn_skipslash(&pn); |
|
3715 |
} |
|
3716 |
||
3717 |
/* |
|
3718 |
* First look for a minor node matching minorname. |
|
3719 |
* Failing that, try to pass minorname to bus_config(). |
|
3720 |
*/ |
|
3721 |
if (minorname && i_ddi_minorname_to_devtspectype(parent, |
|
3722 |
minorname, &devt, &spectype) == DDI_FAILURE) { |
|
3723 |
(void) snprintf(config_name, MAXNAMELEN, "%s", minorname); |
|
3724 |
if (ndi_devi_config_obp_args(parent, |
|
3725 |
config_name, &child, 0) != NDI_SUCCESS) { |
|
3726 |
ndi_rele_devi(parent); |
|
3727 |
pn_free(&pn); |
|
3728 |
kmem_free(component, MAXNAMELEN); |
|
3729 |
kmem_free(config_name, MAXNAMELEN); |
|
3730 |
NDI_CONFIG_DEBUG((CE_NOTE, |
|
3731 |
"%s: minor node not found\n", pathname)); |
|
3732 |
return (-1); |
|
3733 |
} |
|
3734 |
minorname = NULL; /* look for default minor */ |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
3735 |
ASSERT(i_ddi_devi_attached(child)); |
0 | 3736 |
ndi_rele_devi(parent); |
3737 |
parent = child; |
|
3738 |
} |
|
3739 |
||
3740 |
if (devtp || spectypep) { |
|
3741 |
if (minorname == NULL) { |
|
3742 |
/* search for a default entry */ |
|
3743 |
mutex_enter(&(DEVI(parent)->devi_lock)); |
|
3744 |
for (dmn = DEVI(parent)->devi_minor; dmn; |
|
3745 |
dmn = dmn->next) { |
|
3746 |
if (dmn->type == DDM_DEFAULT) { |
|
3747 |
devt = dmn->ddm_dev; |
|
3748 |
spectype = dmn->ddm_spec_type; |
|
3749 |
break; |
|
3750 |
} |
|
3751 |
} |
|
3752 |
||
3753 |
if (devt == NODEV) { |
|
3754 |
/* |
|
3755 |
* No default minor node, try the first one; |
|
3756 |
* else, assume 1-1 instance-minor mapping |
|
3757 |
*/ |
|
3758 |
dmn = DEVI(parent)->devi_minor; |
|
3759 |
if (dmn && ((dmn->type == DDM_MINOR) || |
|
3760 |
(dmn->type == DDM_INTERNAL_PATH))) { |
|
3761 |
devt = dmn->ddm_dev; |
|
3762 |
spectype = dmn->ddm_spec_type; |
|
3763 |
} else { |
|
3764 |
devt = makedevice( |
|
3765 |
DEVI(parent)->devi_major, |
|
3766 |
ddi_get_instance(parent)); |
|
3767 |
spectype = S_IFCHR; |
|
3768 |
} |
|
3769 |
} |
|
3770 |
mutex_exit(&(DEVI(parent)->devi_lock)); |
|
3771 |
} |
|
3772 |
if (devtp) |
|
3773 |
*devtp = devt; |
|
3774 |
if (spectypep) |
|
3775 |
*spectypep = spectype; |
|
3776 |
} |
|
3777 |
||
3778 |
pn_free(&pn); |
|
3779 |
kmem_free(component, MAXNAMELEN); |
|
3780 |
kmem_free(config_name, MAXNAMELEN); |
|
3781 |
||
3782 |
/* |
|
3783 |
* If there is no error, return the appropriate parameters |
|
3784 |
*/ |
|
3785 |
if (dipp != NULL) |
|
3786 |
*dipp = parent; |
|
3787 |
else { |
|
3788 |
/* |
|
3789 |
* We should really keep the ref count to keep the node from |
|
3790 |
* detaching but ddi_pathname_to_dev_t() specifies a NULL dipp, |
|
3791 |
* so we have no way of passing back the held dip. Not holding |
|
3792 |
* the dip allows detaches to occur - which can cause problems |
|
3793 |
* for subsystems which call ddi_pathname_to_dev_t (console). |
|
3794 |
* |
|
3795 |
* Instead of holding the dip, we place a ddi-no-autodetach |
|
3796 |
* property on the node to prevent auto detaching. |
|
3797 |
* |
|
3798 |
* The right fix is to remove ddi_pathname_to_dev_t and replace |
|
3799 |
* it, and all references, with a call that specifies a dipp. |
|
3800 |
* In addition, the callers of this new interfaces would then |
|
3801 |
* need to call ndi_rele_devi when the reference is complete. |
|
3802 |
*/ |
|
3803 |
(void) ddi_prop_update_int(DDI_DEV_T_NONE, parent, |
|
3804 |
DDI_NO_AUTODETACH, 1); |
|
3805 |
ndi_rele_devi(parent); |
|
3806 |
} |
|
3807 |
||
3808 |
return (0); |
|
3809 |
} |
|
3810 |
||
3811 |
/* |
|
3812 |
* Given the pathname of a device, return the dev_t of the corresponding |
|
3813 |
* device. Returns NODEV on failure. |
|
3814 |
* |
|
3815 |
* Note that this call sets the DDI_NO_AUTODETACH property on the devinfo node. |
|
3816 |
*/ |
|
3817 |
dev_t |
|
3818 |
ddi_pathname_to_dev_t(char *pathname) |
|
3819 |
{ |
|
3820 |
dev_t devt; |
|
3821 |
int error; |
|
3822 |
||
3823 |
error = resolve_pathname(pathname, NULL, &devt, NULL); |
|
3824 |
||
3825 |
return (error ? NODEV : devt); |
|
3826 |
} |
|
3827 |
||
3828 |
/* |
|
3829 |
* Translate a prom pathname to kernel devfs pathname. |
|
3830 |
* Caller is assumed to allocate devfspath memory of |
|
3831 |
* size at least MAXPATHLEN |
|
3832 |
* |
|
3833 |
* The prom pathname may not include minor name, but |
|
3834 |
* devfs pathname has a minor name portion. |
|
3835 |
*/ |
|
3836 |
int |
|
3837 |
i_ddi_prompath_to_devfspath(char *prompath, char *devfspath) |
|
3838 |
{ |
|
3839 |
dev_t devt = (dev_t)NODEV; |
|
3840 |
dev_info_t *dip = NULL; |
|
3841 |
char *minor_name = NULL; |
|
3842 |
int spectype; |
|
3843 |
int error; |
|
3844 |
||
3845 |
error = resolve_pathname(prompath, &dip, &devt, &spectype); |
|
3846 |
if (error) |
|
3847 |
return (DDI_FAILURE); |
|
3848 |
ASSERT(dip && devt != NODEV); |
|
3849 |
||
3850 |
/* |
|
3851 |
* Get in-kernel devfs pathname |
|
3852 |
*/ |
|
3853 |
(void) ddi_pathname(dip, devfspath); |
|
3854 |
||
3855 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
|
3856 |
minor_name = i_ddi_devtspectype_to_minorname(dip, devt, spectype); |
|
3857 |
if (minor_name) { |
|
3858 |
(void) strcat(devfspath, ":"); |
|
3859 |
(void) strcat(devfspath, minor_name); |
|
3860 |
} else { |
|
3861 |
/* |
|
3862 |
* If minor_name is NULL, we have an alias minor node. |
|
3863 |
* So manufacture a path to the corresponding clone minor. |
|
3864 |
*/ |
|
3865 |
(void) snprintf(devfspath, MAXPATHLEN, "%s:%s", |
|
3866 |
CLONE_PATH, ddi_driver_name(dip)); |
|
3867 |
} |
|
3868 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
|
3869 |
||
3870 |
/* release hold from resolve_pathname() */ |
|
3871 |
ndi_rele_devi(dip); |
|
3872 |
return (0); |
|
3873 |
} |
|
3874 |
||
3875 |
/* |
|
3876 |
* Reset all the pure leaf drivers on the system at halt time |
|
3877 |
*/ |
|
3878 |
static int |
|
3879 |
reset_leaf_device(dev_info_t *dip, void *arg) |
|
3880 |
{ |
|
3881 |
_NOTE(ARGUNUSED(arg)) |
|
3882 |
struct dev_ops *ops; |
|
3883 |
||
3884 |
/* if the device doesn't need to be reset then there's nothing to do */ |
|
3885 |
if (!DEVI_NEED_RESET(dip)) |
|
3886 |
return (DDI_WALK_CONTINUE); |
|
3887 |
||
3888 |
/* |
|
3889 |
* if the device isn't a char/block device or doesn't have a |
|
3890 |
* reset entry point then there's nothing to do. |
|
3891 |
*/ |
|
3892 |
ops = ddi_get_driver(dip); |
|
3893 |
if ((ops == NULL) || (ops->devo_cb_ops == NULL) || |
|
3894 |
(ops->devo_reset == nodev) || (ops->devo_reset == nulldev) || |
|
3895 |
(ops->devo_reset == NULL)) |
|
3896 |
return (DDI_WALK_CONTINUE); |
|
3897 |
||
3898 |
if (DEVI_IS_ATTACHING(dip) || DEVI_IS_DETACHING(dip)) { |
|
3899 |
static char path[MAXPATHLEN]; |
|
3900 |
||
3901 |
/* |
|
3902 |
* bad news, this device has blocked in it's attach or |
|
3903 |
* detach routine, which means it not safe to call it's |
|
3904 |
* devo_reset() entry point. |
|
3905 |
*/ |
|
3906 |
cmn_err(CE_WARN, "unable to reset device: %s", |
|
3907 |
ddi_pathname(dip, path)); |
|
3908 |
return (DDI_WALK_CONTINUE); |
|
3909 |
} |
|
3910 |
||
3911 |
NDI_CONFIG_DEBUG((CE_NOTE, "resetting %s%d\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
3912 |
ddi_driver_name(dip), ddi_get_instance(dip))); |
0 | 3913 |
|
3914 |
(void) devi_reset(dip, DDI_RESET_FORCE); |
|
3915 |
return (DDI_WALK_CONTINUE); |
|
3916 |
} |
|
3917 |
||
3918 |
void |
|
3919 |
reset_leaves(void) |
|
3920 |
{ |
|
3921 |
/* |
|
3922 |
* if we're reached here, the device tree better not be changing. |
|
3923 |
* so either devinfo_freeze better be set or we better be panicing. |
|
3924 |
*/ |
|
3925 |
ASSERT(devinfo_freeze || panicstr); |
|
3926 |
||
3927 |
(void) walk_devs(top_devinfo, reset_leaf_device, NULL, 0); |
|
3928 |
} |
|
3929 |
||
3930 |
/* |
|
3931 |
* devtree_freeze() must be called before reset_leaves() during a |
|
3932 |
* normal system shutdown. It attempts to ensure that there are no |
|
3933 |
* outstanding attach or detach operations in progress when reset_leaves() |
|
3934 |
* is invoked. It must be called before the system becomes single-threaded |
|
3935 |
* because device attach and detach are multi-threaded operations. (note |
|
3936 |
* that during system shutdown the system doesn't actually become |
|
3937 |
* single-thread since other threads still exist, but the shutdown thread |
|
3938 |
* will disable preemption for itself, raise it's pil, and stop all the |
|
3939 |
* other cpus in the system there by effectively making the system |
|
3940 |
* single-threaded.) |
|
3941 |
*/ |
|
3942 |
void |
|
3943 |
devtree_freeze(void) |
|
3944 |
{ |
|
3945 |
int delayed = 0; |
|
3946 |
||
3947 |
/* if we're panicing then the device tree isn't going to be changing */ |
|
3948 |
if (panicstr) |
|
3949 |
return; |
|
3950 |
||
3951 |
/* stop all dev_info state changes in the device tree */ |
|
3952 |
devinfo_freeze = gethrtime(); |
|
3953 |
||
3954 |
/* |
|
3955 |
* if we're not panicing and there are on-going attach or detach |
|
3956 |
* operations, wait for up to 3 seconds for them to finish. This |
|
3957 |
* is a randomly chosen interval but this should be ok because: |
|
3958 |
* - 3 seconds is very small relative to the deadman timer. |
|
3959 |
* - normal attach and detach operations should be very quick. |
|
3960 |
* - attach and detach operations are fairly rare. |
|
3961 |
*/ |
|
3962 |
while (!panicstr && atomic_add_long_nv(&devinfo_attach_detach, 0) && |
|
3963 |
(delayed < 3)) { |
|
3964 |
delayed += 1; |
|
3965 |
||
3966 |
/* do a sleeping wait for one second */ |
|
3967 |
ASSERT(!servicing_interrupt()); |
|
3968 |
delay(drv_usectohz(MICROSEC)); |
|
3969 |
} |
|
3970 |
} |
|
3971 |
||
3972 |
static int |
|
3973 |
bind_dip(dev_info_t *dip, void *arg) |
|
3974 |
{ |
|
3975 |
_NOTE(ARGUNUSED(arg)) |
|
4145 | 3976 |
char *path; |
3977 |
major_t major, pmajor; |
|
3978 |
||
3979 |
/* |
|
3980 |
* If the node is currently bound to the wrong driver, try to unbind |
|
3981 |
* so that we can rebind to the correct driver. |
|
3982 |
*/ |
|
3983 |
if (i_ddi_node_state(dip) >= DS_BOUND) { |
|
3984 |
major = ddi_compatible_driver_major(dip, NULL); |
|
3985 |
if ((DEVI(dip)->devi_major == major) && |
|
3986 |
(i_ddi_node_state(dip) >= DS_INITIALIZED)) { |
|
3987 |
/* |
|
3988 |
* Check for a path-oriented driver alias that |
|
3989 |
* takes precedence over current driver binding. |
|
3990 |
*/ |
|
3991 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
3992 |
(void) ddi_pathname(dip, path); |
|
3993 |
pmajor = ddi_name_to_major(path); |
|
3994 |
if ((pmajor != (major_t)-1) && |
|
3995 |
!(devnamesp[pmajor].dn_flags & DN_DRIVER_REMOVED)) |
|
3996 |
major = pmajor; |
|
3997 |
kmem_free(path, MAXPATHLEN); |
|
3998 |
} |
|
3999 |
||
4000 |
/* attempt unbind if current driver is incorrect */ |
|
4001 |
if ((major != (major_t)-1) && |
|
4002 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED) && |
|
4003 |
(major != DEVI(dip)->devi_major)) |
|
4004 |
(void) ndi_devi_unbind_driver(dip); |
|
4005 |
} |
|
4006 |
||
4007 |
/* If unbound, try to bind to a driver */ |
|
0 | 4008 |
if (i_ddi_node_state(dip) < DS_BOUND) |
4009 |
(void) ndi_devi_bind_driver(dip, 0); |
|
4010 |
||
4011 |
return (DDI_WALK_CONTINUE); |
|
4012 |
} |
|
4013 |
||
4014 |
void |
|
4015 |
i_ddi_bind_devs(void) |
|
4016 |
{ |
|
4145 | 4017 |
/* flush devfs so that ndi_devi_unbind_driver will work when possible */ |
4018 |
(void) devfs_clean(top_devinfo, NULL, 0); |
|
4019 |
||
0 | 4020 |
ddi_walk_devs(top_devinfo, bind_dip, (void *)NULL); |
4021 |
} |
|
4022 |
||
4023 |
static int |
|
4024 |
unbind_children(dev_info_t *dip, void *arg) |
|
4025 |
{ |
|
4026 |
int circ; |
|
4027 |
dev_info_t *cdip; |
|
4028 |
major_t major = (major_t)(uintptr_t)arg; |
|
4029 |
||
4030 |
ndi_devi_enter(dip, &circ); |
|
4031 |
cdip = ddi_get_child(dip); |
|
4032 |
/* |
|
4033 |
* We are called either from rem_drv or update_drv. |
|
4034 |
* In both cases, we unbind persistent nodes and destroy |
|
4035 |
* .conf nodes. In the case of rem_drv, this will be the |
|
4036 |
* final state. In the case of update_drv, i_ddi_bind_devs() |
|
4037 |
* will be invoked later to reenumerate (new) driver.conf |
|
4038 |
* rebind persistent nodes. |
|
4039 |
*/ |
|
4040 |
while (cdip) { |
|
4041 |
dev_info_t *next = ddi_get_next_sibling(cdip); |
|
4042 |
if ((i_ddi_node_state(cdip) > DS_INITIALIZED) || |
|
4043 |
(ddi_driver_major(cdip) != major)) { |
|
4044 |
cdip = next; |
|
4045 |
continue; |
|
4046 |
} |
|
4047 |
(void) ndi_devi_unbind_driver(cdip); |
|
4048 |
if (ndi_dev_is_persistent_node(cdip) == 0) |
|
4049 |
(void) ddi_remove_child(cdip, 0); |
|
4050 |
cdip = next; |
|
4051 |
} |
|
4052 |
ndi_devi_exit(dip, circ); |
|
4053 |
||
4054 |
return (DDI_WALK_CONTINUE); |
|
4055 |
} |
|
4056 |
||
4057 |
void |
|
4058 |
i_ddi_unbind_devs(major_t major) |
|
4059 |
{ |
|
4060 |
ddi_walk_devs(top_devinfo, unbind_children, (void *)(uintptr_t)major); |
|
4061 |
} |
|
4062 |
||
4063 |
/* |
|
4064 |
* I/O Hotplug control |
|
4065 |
*/ |
|
4066 |
||
4067 |
/* |
|
4068 |
* create and attach a dev_info node from a .conf file spec |
|
4069 |
*/ |
|
4070 |
static void |
|
4071 |
init_spec_child(dev_info_t *pdip, struct hwc_spec *specp, uint_t flags) |
|
4072 |
{ |
|
4073 |
_NOTE(ARGUNUSED(flags)) |
|
4074 |
dev_info_t *dip; |
|
4075 |
char *node_name; |
|
4076 |
||
4077 |
if (((node_name = specp->hwc_devi_name) == NULL) || |
|
4078 |
(ddi_name_to_major(node_name) == (major_t)-1)) { |
|
4079 |
char *tmp = node_name; |
|
4080 |
if (tmp == NULL) |
|
4081 |
tmp = "<none>"; |
|
4082 |
cmn_err(CE_CONT, |
|
4083 |
"init_spec_child: parent=%s, bad spec (%s)\n", |
|
4084 |
ddi_node_name(pdip), tmp); |
|
4085 |
return; |
|
4086 |
} |
|
4087 |
||
789 | 4088 |
dip = i_ddi_alloc_node(pdip, node_name, (pnode_t)DEVI_PSEUDO_NODEID, |
0 | 4089 |
-1, specp->hwc_devi_sys_prop_ptr, KM_SLEEP); |
4090 |
||
4091 |
if (dip == NULL) |
|
4092 |
return; |
|
4093 |
||
4094 |
if (ddi_initchild(pdip, dip) != DDI_SUCCESS) |
|
4095 |
(void) ddi_remove_child(dip, 0); |
|
4096 |
} |
|
4097 |
||
4098 |
/* |
|
4099 |
* Lookup hwc specs from hash tables and make children from the spec |
|
4100 |
* Because some .conf children are "merge" nodes, we also initialize |
|
4101 |
* .conf children to merge properties onto hardware nodes. |
|
4102 |
* |
|
4103 |
* The pdip must be held busy. |
|
4104 |
*/ |
|
4105 |
int |
|
4106 |
i_ndi_make_spec_children(dev_info_t *pdip, uint_t flags) |
|
4107 |
{ |
|
4108 |
extern struct hwc_spec *hwc_get_child_spec(dev_info_t *, major_t); |
|
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4109 |
int circ; |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4110 |
struct hwc_spec *list, *spec; |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4111 |
|
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4112 |
ndi_devi_enter(pdip, &circ); |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4113 |
if (DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN) { |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4114 |
ndi_devi_exit(pdip, circ); |
0 | 4115 |
return (DDI_SUCCESS); |
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4116 |
} |
0 | 4117 |
|
4118 |
list = hwc_get_child_spec(pdip, (major_t)-1); |
|
4119 |
for (spec = list; spec != NULL; spec = spec->hwc_next) { |
|
4120 |
init_spec_child(pdip, spec, flags); |
|
4121 |
} |
|
4122 |
hwc_free_spec_list(list); |
|
4123 |
||
4124 |
mutex_enter(&DEVI(pdip)->devi_lock); |
|
4125 |
DEVI(pdip)->devi_flags |= DEVI_MADE_CHILDREN; |
|
4126 |
mutex_exit(&DEVI(pdip)->devi_lock); |
|
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
4127 |
ndi_devi_exit(pdip, circ); |
0 | 4128 |
return (DDI_SUCCESS); |
4129 |
} |
|
4130 |
||
4131 |
/* |
|
4132 |
* Run initchild on all child nodes such that instance assignment |
|
4133 |
* for multiport network cards are contiguous. |
|
4134 |
* |
|
4135 |
* The pdip must be held busy. |
|
4136 |
*/ |
|
4137 |
static void |
|
4138 |
i_ndi_init_hw_children(dev_info_t *pdip, uint_t flags) |
|
4139 |
{ |
|
4140 |
dev_info_t *dip; |
|
4141 |
||
4142 |
ASSERT(DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN); |
|
4143 |
||
4144 |
/* contiguous instance assignment */ |
|
4145 |
e_ddi_enter_instance(); |
|
4146 |
dip = ddi_get_child(pdip); |
|
4147 |
while (dip) { |
|
4148 |
if (ndi_dev_is_persistent_node(dip)) |
|
4149 |
(void) i_ndi_config_node(dip, DS_INITIALIZED, flags); |
|
4150 |
dip = ddi_get_next_sibling(dip); |
|
4151 |
} |
|
4152 |
e_ddi_exit_instance(); |
|
4153 |
} |
|
4154 |
||
4155 |
/* |
|
4156 |
* report device status |
|
4157 |
*/ |
|
4158 |
static void |
|
4159 |
i_ndi_devi_report_status_change(dev_info_t *dip, char *path) |
|
4160 |
{ |
|
4161 |
char *status; |
|
4162 |
||
4163 |
if (!DEVI_NEED_REPORT(dip) || |
|
4164 |
(i_ddi_node_state(dip) < DS_INITIALIZED)) { |
|
4165 |
return; |
|
4166 |
} |
|
4167 |
||
4168 |
if (DEVI_IS_DEVICE_OFFLINE(dip)) { |
|
4169 |
status = "offline"; |
|
4170 |
} else if (DEVI_IS_DEVICE_DOWN(dip)) { |
|
4171 |
status = "down"; |
|
4172 |
} else if (DEVI_IS_BUS_QUIESCED(dip)) { |
|
4173 |
status = "quiesced"; |
|
4174 |
} else if (DEVI_IS_BUS_DOWN(dip)) { |
|
4175 |
status = "down"; |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
4176 |
} else if (i_ddi_devi_attached(dip)) { |
0 | 4177 |
status = "online"; |
4178 |
} else { |
|
4179 |
status = "unknown"; |
|
4180 |
} |
|
4181 |
||
4182 |
if (path == NULL) { |
|
4183 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4184 |
cmn_err(CE_CONT, "?%s (%s%d) %s\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
4185 |
ddi_pathname(dip, path), ddi_driver_name(dip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
4186 |
ddi_get_instance(dip), status); |
0 | 4187 |
kmem_free(path, MAXPATHLEN); |
4188 |
} else { |
|
4189 |
cmn_err(CE_CONT, "?%s (%s%d) %s\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
4190 |
path, ddi_driver_name(dip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
4191 |
ddi_get_instance(dip), status); |
0 | 4192 |
} |
4193 |
||
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4194 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 4195 |
DEVI_REPORT_DONE(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4196 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 4197 |
} |
4198 |
||
4199 |
/* |
|
4200 |
* log a notification that a dev_info node has been configured. |
|
4201 |
*/ |
|
4202 |
static int |
|
4203 |
i_log_devfs_add_devinfo(dev_info_t *dip, uint_t flags) |
|
4204 |
{ |
|
4205 |
int se_err; |
|
4206 |
char *pathname; |
|
4207 |
sysevent_t *ev; |
|
4208 |
sysevent_id_t eid; |
|
4209 |
sysevent_value_t se_val; |
|
4210 |
sysevent_attr_list_t *ev_attr_list = NULL; |
|
4211 |
char *class_name; |
|
4212 |
int no_transport = 0; |
|
4213 |
||
4214 |
ASSERT(dip); |
|
4215 |
||
4216 |
/* |
|
4217 |
* Invalidate the devinfo snapshot cache |
|
4218 |
*/ |
|
4219 |
i_ddi_di_cache_invalidate(KM_SLEEP); |
|
4220 |
||
4221 |
/* do not generate ESC_DEVFS_DEVI_ADD event during boot */ |
|
4222 |
if (!i_ddi_io_initialized()) |
|
4223 |
return (DDI_SUCCESS); |
|
4224 |
||
4225 |
ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_DEVI_ADD, EP_DDI, SE_SLEEP); |
|
4226 |
||
4227 |
pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4228 |
||
4229 |
(void) ddi_pathname(dip, pathname); |
|
4230 |
ASSERT(strlen(pathname)); |
|
4231 |
||
4232 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4233 |
se_val.value.sv_string = pathname; |
|
4234 |
if (sysevent_add_attr(&ev_attr_list, DEVFS_PATHNAME, |
|
4235 |
&se_val, SE_SLEEP) != 0) { |
|
4236 |
goto fail; |
|
4237 |
} |
|
4238 |
||
4239 |
/* add the device class attribute */ |
|
4240 |
if ((class_name = i_ddi_devi_class(dip)) != NULL) { |
|
4241 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4242 |
se_val.value.sv_string = class_name; |
|
4243 |
||
4244 |
if (sysevent_add_attr(&ev_attr_list, |
|
4245 |
DEVFS_DEVI_CLASS, &se_val, SE_SLEEP) != 0) { |
|
4246 |
sysevent_free_attr(ev_attr_list); |
|
4247 |
goto fail; |
|
4248 |
} |
|
4249 |
} |
|
4250 |
||
4251 |
/* |
|
4252 |
* must log a branch event too unless NDI_BRANCH_EVENT_OP is set, |
|
4253 |
* in which case the branch event will be logged by the caller |
|
4254 |
* after the entire branch has been configured. |
|
4255 |
*/ |
|
4256 |
if ((flags & NDI_BRANCH_EVENT_OP) == 0) { |
|
4257 |
/* |
|
4258 |
* Instead of logging a separate branch event just add |
|
4259 |
* DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to |
|
4260 |
* generate a EC_DEV_BRANCH event. |
|
4261 |
*/ |
|
4262 |
se_val.value_type = SE_DATA_TYPE_INT32; |
|
4263 |
se_val.value.sv_int32 = 1; |
|
4264 |
if (sysevent_add_attr(&ev_attr_list, |
|
4265 |
DEVFS_BRANCH_EVENT, &se_val, SE_SLEEP) != 0) { |
|
4266 |
sysevent_free_attr(ev_attr_list); |
|
4267 |
goto fail; |
|
4268 |
} |
|
4269 |
} |
|
4270 |
||
4271 |
if (sysevent_attach_attributes(ev, ev_attr_list) != 0) { |
|
4272 |
sysevent_free_attr(ev_attr_list); |
|
4273 |
goto fail; |
|
4274 |
} |
|
4275 |
||
4276 |
if ((se_err = log_sysevent(ev, SE_SLEEP, &eid)) != 0) { |
|
4277 |
if (se_err == SE_NO_TRANSPORT) |
|
4278 |
no_transport = 1; |
|
4279 |
goto fail; |
|
4280 |
} |
|
4281 |
||
4282 |
sysevent_free(ev); |
|
4283 |
kmem_free(pathname, MAXPATHLEN); |
|
4284 |
||
4285 |
return (DDI_SUCCESS); |
|
4286 |
||
4287 |
fail: |
|
4288 |
cmn_err(CE_WARN, "failed to log ESC_DEVFS_DEVI_ADD event for %s%s", |
|
4289 |
pathname, (no_transport) ? " (syseventd not responding)" : ""); |
|
4290 |
||
4291 |
cmn_err(CE_WARN, "/dev may not be current for driver %s. " |
|
4292 |
"Run devfsadm -i %s", |
|
4293 |
ddi_driver_name(dip), ddi_driver_name(dip)); |
|
4294 |
||
4295 |
sysevent_free(ev); |
|
4296 |
kmem_free(pathname, MAXPATHLEN); |
|
4297 |
return (DDI_SUCCESS); |
|
4298 |
} |
|
4299 |
||
4300 |
/* |
|
4301 |
* log a notification that a dev_info node has been unconfigured. |
|
4302 |
*/ |
|
4303 |
static int |
|
4304 |
i_log_devfs_remove_devinfo(char *pathname, char *class_name, char *driver_name, |
|
4305 |
int instance, uint_t flags) |
|
4306 |
{ |
|
4307 |
sysevent_t *ev; |
|
4308 |
sysevent_id_t eid; |
|
4309 |
sysevent_value_t se_val; |
|
4310 |
sysevent_attr_list_t *ev_attr_list = NULL; |
|
4311 |
int se_err; |
|
4312 |
int no_transport = 0; |
|
4313 |
||
4314 |
i_ddi_di_cache_invalidate(KM_SLEEP); |
|
4315 |
||
4316 |
if (!i_ddi_io_initialized()) |
|
4317 |
return (DDI_SUCCESS); |
|
4318 |
||
4319 |
ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_DEVI_REMOVE, EP_DDI, SE_SLEEP); |
|
4320 |
||
4321 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4322 |
se_val.value.sv_string = pathname; |
|
4323 |
if (sysevent_add_attr(&ev_attr_list, DEVFS_PATHNAME, |
|
4324 |
&se_val, SE_SLEEP) != 0) { |
|
4325 |
goto fail; |
|
4326 |
} |
|
4327 |
||
4328 |
if (class_name) { |
|
4329 |
/* add the device class, driver name and instance attributes */ |
|
4330 |
||
4331 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4332 |
se_val.value.sv_string = class_name; |
|
4333 |
if (sysevent_add_attr(&ev_attr_list, |
|
4334 |
DEVFS_DEVI_CLASS, &se_val, SE_SLEEP) != 0) { |
|
4335 |
sysevent_free_attr(ev_attr_list); |
|
4336 |
goto fail; |
|
4337 |
} |
|
4338 |
||
4339 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4340 |
se_val.value.sv_string = driver_name; |
|
4341 |
if (sysevent_add_attr(&ev_attr_list, |
|
4342 |
DEVFS_DRIVER_NAME, &se_val, SE_SLEEP) != 0) { |
|
4343 |
sysevent_free_attr(ev_attr_list); |
|
4344 |
goto fail; |
|
4345 |
} |
|
4346 |
||
4347 |
se_val.value_type = SE_DATA_TYPE_INT32; |
|
4348 |
se_val.value.sv_int32 = instance; |
|
4349 |
if (sysevent_add_attr(&ev_attr_list, |
|
4350 |
DEVFS_INSTANCE, &se_val, SE_SLEEP) != 0) { |
|
4351 |
sysevent_free_attr(ev_attr_list); |
|
4352 |
goto fail; |
|
4353 |
} |
|
4354 |
} |
|
4355 |
||
4356 |
/* |
|
4357 |
* must log a branch event too unless NDI_BRANCH_EVENT_OP is set, |
|
4358 |
* in which case the branch event will be logged by the caller |
|
4359 |
* after the entire branch has been unconfigured. |
|
4360 |
*/ |
|
4361 |
if ((flags & NDI_BRANCH_EVENT_OP) == 0) { |
|
4362 |
/* |
|
4363 |
* Instead of logging a separate branch event just add |
|
4364 |
* DEVFS_BRANCH_EVENT attribute. It indicates devfsadmd to |
|
4365 |
* generate a EC_DEV_BRANCH event. |
|
4366 |
*/ |
|
4367 |
se_val.value_type = SE_DATA_TYPE_INT32; |
|
4368 |
se_val.value.sv_int32 = 1; |
|
4369 |
if (sysevent_add_attr(&ev_attr_list, |
|
4370 |
DEVFS_BRANCH_EVENT, &se_val, SE_SLEEP) != 0) { |
|
4371 |
sysevent_free_attr(ev_attr_list); |
|
4372 |
goto fail; |
|
4373 |
} |
|
4374 |
} |
|
4375 |
||
4376 |
if (sysevent_attach_attributes(ev, ev_attr_list) != 0) { |
|
4377 |
sysevent_free_attr(ev_attr_list); |
|
4378 |
goto fail; |
|
4379 |
} |
|
4380 |
||
4381 |
if ((se_err = log_sysevent(ev, SE_SLEEP, &eid)) != 0) { |
|
4382 |
if (se_err == SE_NO_TRANSPORT) |
|
4383 |
no_transport = 1; |
|
4384 |
goto fail; |
|
4385 |
} |
|
4386 |
||
4387 |
sysevent_free(ev); |
|
4388 |
return (DDI_SUCCESS); |
|
4389 |
||
4390 |
fail: |
|
4391 |
sysevent_free(ev); |
|
4392 |
cmn_err(CE_WARN, "failed to log ESC_DEVFS_DEVI_REMOVE event for %s%s", |
|
4393 |
pathname, (no_transport) ? " (syseventd not responding)" : ""); |
|
4394 |
return (DDI_SUCCESS); |
|
4395 |
} |
|
4396 |
||
4397 |
/* |
|
4398 |
* log an event that a dev_info branch has been configured or unconfigured. |
|
4399 |
*/ |
|
4400 |
static int |
|
4401 |
i_log_devfs_branch(char *node_path, char *subclass) |
|
4402 |
{ |
|
4403 |
int se_err; |
|
4404 |
sysevent_t *ev; |
|
4405 |
sysevent_id_t eid; |
|
4406 |
sysevent_value_t se_val; |
|
4407 |
sysevent_attr_list_t *ev_attr_list = NULL; |
|
4408 |
int no_transport = 0; |
|
4409 |
||
4410 |
/* do not generate the event during boot */ |
|
4411 |
if (!i_ddi_io_initialized()) |
|
4412 |
return (DDI_SUCCESS); |
|
4413 |
||
4414 |
ev = sysevent_alloc(EC_DEVFS, subclass, EP_DDI, SE_SLEEP); |
|
4415 |
||
4416 |
se_val.value_type = SE_DATA_TYPE_STRING; |
|
4417 |
se_val.value.sv_string = node_path; |
|
4418 |
||
4419 |
if (sysevent_add_attr(&ev_attr_list, DEVFS_PATHNAME, |
|
4420 |
&se_val, SE_SLEEP) != 0) { |
|
4421 |
goto fail; |
|
4422 |
} |
|
4423 |
||
4424 |
if (sysevent_attach_attributes(ev, ev_attr_list) != 0) { |
|
4425 |
sysevent_free_attr(ev_attr_list); |
|
4426 |
goto fail; |
|
4427 |
} |
|
4428 |
||
4429 |
if ((se_err = log_sysevent(ev, SE_SLEEP, &eid)) != 0) { |
|
4430 |
if (se_err == SE_NO_TRANSPORT) |
|
4431 |
no_transport = 1; |
|
4432 |
goto fail; |
|
4433 |
} |
|
4434 |
||
4435 |
sysevent_free(ev); |
|
4436 |
return (DDI_SUCCESS); |
|
4437 |
||
4438 |
fail: |
|
4439 |
cmn_err(CE_WARN, "failed to log %s branch event for %s%s", |
|
4440 |
subclass, node_path, |
|
4441 |
(no_transport) ? " (syseventd not responding)" : ""); |
|
4442 |
||
4443 |
sysevent_free(ev); |
|
4444 |
return (DDI_FAILURE); |
|
4445 |
} |
|
4446 |
||
4447 |
/* |
|
4448 |
* log an event that a dev_info tree branch has been configured. |
|
4449 |
*/ |
|
4450 |
static int |
|
4451 |
i_log_devfs_branch_add(dev_info_t *dip) |
|
4452 |
{ |
|
4453 |
char *node_path; |
|
4454 |
int rv; |
|
4455 |
||
4456 |
node_path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4457 |
(void) ddi_pathname(dip, node_path); |
|
4458 |
rv = i_log_devfs_branch(node_path, ESC_DEVFS_BRANCH_ADD); |
|
4459 |
kmem_free(node_path, MAXPATHLEN); |
|
4460 |
||
4461 |
return (rv); |
|
4462 |
} |
|
4463 |
||
4464 |
/* |
|
4465 |
* log an event that a dev_info tree branch has been unconfigured. |
|
4466 |
*/ |
|
4467 |
static int |
|
4468 |
i_log_devfs_branch_remove(char *node_path) |
|
4469 |
{ |
|
4470 |
return (i_log_devfs_branch(node_path, ESC_DEVFS_BRANCH_REMOVE)); |
|
4471 |
} |
|
4472 |
||
4473 |
/* |
|
4474 |
* enqueue the dip's deviname on the branch event queue. |
|
4475 |
*/ |
|
4476 |
static struct brevq_node * |
|
4477 |
brevq_enqueue(struct brevq_node **brevqp, dev_info_t *dip, |
|
4478 |
struct brevq_node *child) |
|
4479 |
{ |
|
4480 |
struct brevq_node *brn; |
|
4481 |
char *deviname; |
|
4482 |
||
4483 |
deviname = kmem_alloc(MAXNAMELEN, KM_SLEEP); |
|
4484 |
(void) ddi_deviname(dip, deviname); |
|
4485 |
||
4486 |
brn = kmem_zalloc(sizeof (*brn), KM_SLEEP); |
|
1317 | 4487 |
brn->brn_deviname = i_ddi_strdup(deviname, KM_SLEEP); |
0 | 4488 |
kmem_free(deviname, MAXNAMELEN); |
1317 | 4489 |
brn->brn_child = child; |
4490 |
brn->brn_sibling = *brevqp; |
|
0 | 4491 |
*brevqp = brn; |
4492 |
||
4493 |
return (brn); |
|
4494 |
} |
|
4495 |
||
4496 |
/* |
|
4497 |
* free the memory allocated for the elements on the branch event queue. |
|
4498 |
*/ |
|
4499 |
static void |
|
4500 |
free_brevq(struct brevq_node *brevq) |
|
4501 |
{ |
|
4502 |
struct brevq_node *brn, *next_brn; |
|
4503 |
||
4504 |
for (brn = brevq; brn != NULL; brn = next_brn) { |
|
1317 | 4505 |
next_brn = brn->brn_sibling; |
4506 |
ASSERT(brn->brn_child == NULL); |
|
4507 |
kmem_free(brn->brn_deviname, strlen(brn->brn_deviname) + 1); |
|
0 | 4508 |
kmem_free(brn, sizeof (*brn)); |
4509 |
} |
|
4510 |
} |
|
4511 |
||
4512 |
/* |
|
4513 |
* log the events queued up on the branch event queue and free the |
|
4514 |
* associated memory. |
|
4515 |
* |
|
4516 |
* node_path must have been allocated with at least MAXPATHLEN bytes. |
|
4517 |
*/ |
|
4518 |
static void |
|
4519 |
log_and_free_brevq(char *node_path, struct brevq_node *brevq) |
|
4520 |
{ |
|
4521 |
struct brevq_node *brn; |
|
4522 |
char *p; |
|
4523 |
||
4524 |
p = node_path + strlen(node_path); |
|
1317 | 4525 |
for (brn = brevq; brn != NULL; brn = brn->brn_sibling) { |
4526 |
(void) strcpy(p, brn->brn_deviname); |
|
0 | 4527 |
(void) i_log_devfs_branch_remove(node_path); |
4528 |
} |
|
4529 |
*p = '\0'; |
|
4530 |
||
4531 |
free_brevq(brevq); |
|
4532 |
} |
|
4533 |
||
4534 |
/* |
|
4535 |
* log the events queued up on the branch event queue and free the |
|
4536 |
* associated memory. Same as the previous function but operates on dip. |
|
4537 |
*/ |
|
4538 |
static void |
|
4539 |
log_and_free_brevq_dip(dev_info_t *dip, struct brevq_node *brevq) |
|
4540 |
{ |
|
4541 |
char *path; |
|
4542 |
||
4543 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4544 |
(void) ddi_pathname(dip, path); |
|
4545 |
log_and_free_brevq(path, brevq); |
|
4546 |
kmem_free(path, MAXPATHLEN); |
|
4547 |
} |
|
4548 |
||
4549 |
/* |
|
4550 |
* log the outstanding branch remove events for the grand children of the dip |
|
4551 |
* and free the associated memory. |
|
4552 |
*/ |
|
4553 |
static void |
|
4554 |
log_and_free_br_events_on_grand_children(dev_info_t *dip, |
|
4555 |
struct brevq_node *brevq) |
|
4556 |
{ |
|
4557 |
struct brevq_node *brn; |
|
4558 |
char *path; |
|
4559 |
char *p; |
|
4560 |
||
4561 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4562 |
(void) ddi_pathname(dip, path); |
|
4563 |
p = path + strlen(path); |
|
1317 | 4564 |
for (brn = brevq; brn != NULL; brn = brn->brn_sibling) { |
4565 |
if (brn->brn_child) { |
|
4566 |
(void) strcpy(p, brn->brn_deviname); |
|
0 | 4567 |
/* now path contains the node path to the dip's child */ |
1317 | 4568 |
log_and_free_brevq(path, brn->brn_child); |
4569 |
brn->brn_child = NULL; |
|
0 | 4570 |
} |
4571 |
} |
|
4572 |
kmem_free(path, MAXPATHLEN); |
|
4573 |
} |
|
4574 |
||
4575 |
/* |
|
4576 |
* log and cleanup branch remove events for the grand children of the dip. |
|
4577 |
*/ |
|
4578 |
static void |
|
4579 |
cleanup_br_events_on_grand_children(dev_info_t *dip, struct brevq_node **brevqp) |
|
4580 |
{ |
|
4581 |
dev_info_t *child; |
|
4582 |
struct brevq_node *brevq, *brn, *prev_brn, *next_brn; |
|
4583 |
char *path; |
|
4584 |
int circ; |
|
4585 |
||
4586 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
4587 |
prev_brn = NULL; |
|
4588 |
brevq = *brevqp; |
|
4589 |
||
4590 |
ndi_devi_enter(dip, &circ); |
|
4591 |
for (brn = brevq; brn != NULL; brn = next_brn) { |
|
1317 | 4592 |
next_brn = brn->brn_sibling; |
0 | 4593 |
for (child = ddi_get_child(dip); child != NULL; |
4594 |
child = ddi_get_next_sibling(child)) { |
|
4595 |
if (i_ddi_node_state(child) >= DS_INITIALIZED) { |
|
4596 |
(void) ddi_deviname(child, path); |
|
1317 | 4597 |
if (strcmp(path, brn->brn_deviname) == 0) |
0 | 4598 |
break; |
4599 |
} |
|
4600 |
} |
|
4601 |
||
4602 |
if (child != NULL && !(DEVI_EVREMOVE(child))) { |
|
4603 |
/* |
|
4604 |
* Event state is not REMOVE. So branch remove event |
|
1317 | 4605 |
* is not going be generated on brn->brn_child. |
0 | 4606 |
* If any branch remove events were queued up on |
1317 | 4607 |
* brn->brn_child log them and remove the brn |
0 | 4608 |
* from the queue. |
4609 |
*/ |
|
1317 | 4610 |
if (brn->brn_child) { |
0 | 4611 |
(void) ddi_pathname(dip, path); |
1317 | 4612 |
(void) strcat(path, brn->brn_deviname); |
4613 |
log_and_free_brevq(path, brn->brn_child); |
|
0 | 4614 |
} |
4615 |
||
4616 |
if (prev_brn) |
|
1317 | 4617 |
prev_brn->brn_sibling = next_brn; |
0 | 4618 |
else |
4619 |
*brevqp = next_brn; |
|
4620 |
||
1317 | 4621 |
kmem_free(brn->brn_deviname, |
4622 |
strlen(brn->brn_deviname) + 1); |
|
0 | 4623 |
kmem_free(brn, sizeof (*brn)); |
4624 |
} else { |
|
4625 |
/* |
|
4626 |
* Free up the outstanding branch remove events |
|
1317 | 4627 |
* queued on brn->brn_child since brn->brn_child |
0 | 4628 |
* itself is eligible for branch remove event. |
4629 |
*/ |
|
1317 | 4630 |
if (brn->brn_child) { |
4631 |
free_brevq(brn->brn_child); |
|
4632 |
brn->brn_child = NULL; |
|
0 | 4633 |
} |
4634 |
prev_brn = brn; |
|
4635 |
} |
|
4636 |
} |
|
4637 |
||
4638 |
ndi_devi_exit(dip, circ); |
|
4639 |
kmem_free(path, MAXPATHLEN); |
|
4640 |
} |
|
4641 |
||
4642 |
static int |
|
4643 |
need_remove_event(dev_info_t *dip, int flags) |
|
4644 |
{ |
|
4645 |
if ((flags & (NDI_NO_EVENT | NDI_AUTODETACH)) == 0 && |
|
4646 |
(flags & (NDI_DEVI_OFFLINE | NDI_UNCONFIG | NDI_DEVI_REMOVE)) && |
|
4647 |
!(DEVI_EVREMOVE(dip))) |
|
4648 |
return (1); |
|
4649 |
else |
|
4650 |
return (0); |
|
4651 |
} |
|
4652 |
||
4653 |
/* |
|
4654 |
* Unconfigure children/descendants of the dip. |
|
4655 |
* |
|
4656 |
* If the operation involves a branch event NDI_BRANCH_EVENT_OP is set |
|
4657 |
* through out the unconfiguration. On successful return *brevqp is set to |
|
4658 |
* a queue of dip's child devinames for which branch remove events need |
|
4659 |
* to be generated. |
|
4660 |
*/ |
|
4661 |
static int |
|
4662 |
devi_unconfig_branch(dev_info_t *dip, dev_info_t **dipp, int flags, |
|
4663 |
struct brevq_node **brevqp) |
|
4664 |
{ |
|
4665 |
int rval; |
|
4666 |
||
4667 |
*brevqp = NULL; |
|
4668 |
||
4669 |
if ((!(flags & NDI_BRANCH_EVENT_OP)) && need_remove_event(dip, flags)) |
|
4670 |
flags |= NDI_BRANCH_EVENT_OP; |
|
4671 |
||
4672 |
if (flags & NDI_BRANCH_EVENT_OP) { |
|
4673 |
rval = devi_unconfig_common(dip, dipp, flags, (major_t)-1, |
|
4674 |
brevqp); |
|
4675 |
||
4676 |
if (rval != NDI_SUCCESS && (*brevqp)) { |
|
4677 |
log_and_free_brevq_dip(dip, *brevqp); |
|
4678 |
*brevqp = NULL; |
|
4679 |
} |
|
4680 |
} else |
|
4681 |
rval = devi_unconfig_common(dip, dipp, flags, (major_t)-1, |
|
4682 |
NULL); |
|
4683 |
||
4684 |
return (rval); |
|
4685 |
} |
|
4686 |
||
4687 |
/* |
|
4688 |
* If the dip is already bound to a driver transition to DS_INITIALIZED |
|
4689 |
* in order to generate an event in the case where the node was left in |
|
4690 |
* DS_BOUND state since boot (never got attached) and the node is now |
|
4691 |
* being offlined. |
|
4692 |
*/ |
|
4693 |
static void |
|
4694 |
init_bound_node_ev(dev_info_t *pdip, dev_info_t *dip, int flags) |
|
4695 |
{ |
|
4696 |
if (need_remove_event(dip, flags) && |
|
4697 |
i_ddi_node_state(dip) == DS_BOUND && |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
4698 |
i_ddi_devi_attached(pdip) && !DEVI_IS_DEVICE_OFFLINE(dip)) |
0 | 4699 |
(void) ddi_initchild(pdip, dip); |
4700 |
} |
|
4701 |
||
4702 |
/* |
|
4703 |
* attach a node/branch with parent already held busy |
|
4704 |
*/ |
|
4705 |
static int |
|
4706 |
devi_attach_node(dev_info_t *dip, uint_t flags) |
|
4707 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4708 |
dev_info_t *pdip = ddi_get_parent(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4709 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4710 |
ASSERT(pdip && DEVI_BUSY_OWNED(pdip)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4711 |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4712 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 4713 |
if (flags & NDI_DEVI_ONLINE) { |
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
4714 |
if (!i_ddi_devi_attached(dip)) |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4715 |
DEVI_SET_REPORT(dip); |
0 | 4716 |
DEVI_SET_DEVICE_ONLINE(dip); |
4717 |
} |
|
4718 |
if (DEVI_IS_DEVICE_OFFLINE(dip)) { |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4719 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 4720 |
return (NDI_FAILURE); |
4721 |
} |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4722 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 4723 |
|
4724 |
if (i_ddi_attachchild(dip) != DDI_SUCCESS) { |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4725 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 4726 |
DEVI_SET_EVUNINIT(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4727 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4728 |
|
0 | 4729 |
if (ndi_dev_is_persistent_node(dip)) |
4730 |
(void) ddi_uninitchild(dip); |
|
4731 |
else { |
|
4732 |
/* |
|
4733 |
* Delete .conf nodes and nodes that are not |
|
4734 |
* well formed. |
|
4735 |
*/ |
|
4736 |
(void) ddi_remove_child(dip, 0); |
|
4737 |
} |
|
4738 |
return (NDI_FAILURE); |
|
4739 |
} |
|
4740 |
||
4741 |
i_ndi_devi_report_status_change(dip, NULL); |
|
4742 |
||
4743 |
/* |
|
4744 |
* log an event, but not during devfs lookups in which case |
|
4745 |
* NDI_NO_EVENT is set. |
|
4746 |
*/ |
|
4747 |
if ((flags & NDI_NO_EVENT) == 0 && !(DEVI_EVADD(dip))) { |
|
4748 |
(void) i_log_devfs_add_devinfo(dip, flags); |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4749 |
|
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4750 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 4751 |
DEVI_SET_EVADD(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4752 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4753 |
} else if (!(flags & NDI_NO_EVENT_STATE_CHNG)) { |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4754 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 4755 |
DEVI_SET_EVADD(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4756 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
4757 |
} |
0 | 4758 |
|
4759 |
return (NDI_SUCCESS); |
|
4760 |
} |
|
4761 |
||
4762 |
/* internal function to config immediate children */ |
|
4763 |
static int |
|
4764 |
config_immediate_children(dev_info_t *pdip, uint_t flags, major_t major) |
|
4765 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4766 |
dev_info_t *child, *next; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4767 |
int circ; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4768 |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
4769 |
ASSERT(i_ddi_devi_attached(pdip)); |
0 | 4770 |
|
4771 |
if (!NEXUS_DRV(ddi_get_driver(pdip))) |
|
4772 |
return (NDI_SUCCESS); |
|
4773 |
||
4774 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
4775 |
"config_immediate_children: %s%d (%p), flags=%x\n", |
|
4776 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
|
4777 |
(void *)pdip, flags)); |
|
4778 |
||
4779 |
ndi_devi_enter(pdip, &circ); |
|
4780 |
||
4781 |
if (flags & NDI_CONFIG_REPROBE) { |
|
4782 |
mutex_enter(&DEVI(pdip)->devi_lock); |
|
4783 |
DEVI(pdip)->devi_flags &= ~DEVI_MADE_CHILDREN; |
|
4784 |
mutex_exit(&DEVI(pdip)->devi_lock); |
|
4785 |
} |
|
4786 |
(void) i_ndi_make_spec_children(pdip, flags); |
|
4787 |
i_ndi_init_hw_children(pdip, flags); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4788 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4789 |
child = ddi_get_child(pdip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4790 |
while (child) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4791 |
/* NOTE: devi_attach_node() may remove the dip */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4792 |
next = ddi_get_next_sibling(child); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4793 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4794 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4795 |
* Configure all nexus nodes or leaf nodes with |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4796 |
* matching driver major |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4797 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4798 |
if ((major == (major_t)-1) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4799 |
(major == ddi_driver_major(child)) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4800 |
((flags & NDI_CONFIG) && (is_leaf_node(child) == 0))) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4801 |
(void) devi_attach_node(child, flags); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4802 |
child = next; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4803 |
} |
0 | 4804 |
|
4805 |
ndi_devi_exit(pdip, circ); |
|
4806 |
||
4807 |
return (NDI_SUCCESS); |
|
4808 |
} |
|
4809 |
||
4810 |
/* internal function to config grand children */ |
|
4811 |
static int |
|
4812 |
config_grand_children(dev_info_t *pdip, uint_t flags, major_t major) |
|
4813 |
{ |
|
4814 |
struct mt_config_handle *hdl; |
|
4815 |
||
4816 |
/* multi-threaded configuration of child nexus */ |
|
4817 |
hdl = mt_config_init(pdip, NULL, flags, major, MT_CONFIG_OP, NULL); |
|
4818 |
mt_config_children(hdl); |
|
4819 |
||
4820 |
return (mt_config_fini(hdl)); /* wait for threads to exit */ |
|
4821 |
} |
|
4822 |
||
4823 |
/* |
|
4824 |
* Common function for device tree configuration, |
|
4825 |
* either BUS_CONFIG_ALL or BUS_CONFIG_DRIVER. |
|
4826 |
* The NDI_CONFIG flag causes recursive configuration of |
|
4827 |
* grandchildren, devfs usage should not recurse. |
|
4828 |
*/ |
|
4829 |
static int |
|
4830 |
devi_config_common(dev_info_t *dip, int flags, major_t major) |
|
4831 |
{ |
|
4832 |
int error; |
|
4833 |
int (*f)(); |
|
4834 |
||
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
4835 |
if (!i_ddi_devi_attached(dip)) |
0 | 4836 |
return (NDI_FAILURE); |
4837 |
||
4838 |
if (pm_pre_config(dip, NULL) != DDI_SUCCESS) |
|
4839 |
return (NDI_FAILURE); |
|
4840 |
||
4841 |
if ((DEVI(dip)->devi_ops->devo_bus_ops == NULL) || |
|
4842 |
(DEVI(dip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) || |
|
4843 |
(f = DEVI(dip)->devi_ops->devo_bus_ops->bus_config) == NULL) { |
|
4844 |
error = config_immediate_children(dip, flags, major); |
|
4845 |
} else { |
|
4846 |
/* call bus_config entry point */ |
|
4847 |
ddi_bus_config_op_t bus_op = (major == (major_t)-1) ? |
|
4848 |
BUS_CONFIG_ALL : BUS_CONFIG_DRIVER; |
|
4849 |
error = (*f)(dip, |
|
4850 |
flags, bus_op, (void *)(uintptr_t)major, NULL, 0); |
|
4851 |
} |
|
4852 |
||
4853 |
if (error) { |
|
4854 |
pm_post_config(dip, NULL); |
|
4855 |
return (error); |
|
4856 |
} |
|
4857 |
||
4858 |
/* |
|
4859 |
* Some callers, notably SCSI, need to mark the devfs cache |
|
4860 |
* to be rebuilt together with the config operation. |
|
4861 |
*/ |
|
4862 |
if (flags & NDI_DEVFS_CLEAN) |
|
4863 |
(void) devfs_clean(dip, NULL, 0); |
|
4864 |
||
4865 |
if (flags & NDI_CONFIG) |
|
4866 |
(void) config_grand_children(dip, flags, major); |
|
4867 |
||
4868 |
pm_post_config(dip, NULL); |
|
4869 |
||
4870 |
return (NDI_SUCCESS); |
|
4871 |
} |
|
4872 |
||
4873 |
/* |
|
4874 |
* Framework entry point for BUS_CONFIG_ALL |
|
4875 |
*/ |
|
4876 |
int |
|
4877 |
ndi_devi_config(dev_info_t *dip, int flags) |
|
4878 |
{ |
|
4879 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
4880 |
"ndi_devi_config: par = %s%d (%p), flags = 0x%x\n", |
|
4881 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
4882 |
||
4883 |
return (devi_config_common(dip, flags, (major_t)-1)); |
|
4884 |
} |
|
4885 |
||
4886 |
/* |
|
4887 |
* Framework entry point for BUS_CONFIG_DRIVER, bound to major |
|
4888 |
*/ |
|
4889 |
int |
|
4890 |
ndi_devi_config_driver(dev_info_t *dip, int flags, major_t major) |
|
4891 |
{ |
|
4892 |
/* don't abuse this function */ |
|
4893 |
ASSERT(major != (major_t)-1); |
|
4894 |
||
4895 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
4896 |
"ndi_devi_config_driver: par = %s%d (%p), flags = 0x%x\n", |
|
4897 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
4898 |
||
4899 |
return (devi_config_common(dip, flags, major)); |
|
4900 |
} |
|
4901 |
||
4902 |
/* |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4903 |
* Called by nexus drivers to configure its children. |
0 | 4904 |
*/ |
4905 |
static int |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4906 |
devi_config_one(dev_info_t *pdip, char *devnm, dev_info_t **cdipp, |
0 | 4907 |
uint_t flags, clock_t timeout) |
4908 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4909 |
dev_info_t *vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4910 |
char *drivername = NULL; |
4145 | 4911 |
int find_by_addr = 0; |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4912 |
char *name, *addr; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4913 |
int v_circ, p_circ; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4914 |
clock_t end_time; /* 60 sec */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4915 |
int probed; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4916 |
dev_info_t *cdip; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4917 |
mdi_pathinfo_t *cpip; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4918 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4919 |
*cdipp = NULL; |
0 | 4920 |
|
4921 |
if (!NEXUS_DRV(ddi_get_driver(pdip))) |
|
4922 |
return (NDI_FAILURE); |
|
4923 |
||
4924 |
/* split name into "name@addr" parts */ |
|
4925 |
i_ddi_parse_name(devnm, &name, &addr, NULL); |
|
4926 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4927 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4928 |
* If the nexus is a pHCI and we are not processing a pHCI from |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4929 |
* mdi bus_config code then we need to know the vHCI. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4930 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4931 |
if (MDI_PHCI(pdip)) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4932 |
vdip = mdi_devi_get_vdip(pdip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4933 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4934 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4935 |
* We may have a genericname on a system that creates drivername |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4936 |
* nodes (from .conf files). Find the drivername by nodeid. If we |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4937 |
* can't find a node with devnm as the node name then we search by |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4938 |
* drivername. This allows an implementation to supply a genericly |
5742
b0948deb63bc
6642582 Testcase initfini failing on all machines (sparc & x86) in s10u5_05
cth
parents:
4950
diff
changeset
|
4939 |
* named boot path (disk) and locate drivename nodes (sd). The |
b0948deb63bc
6642582 Testcase initfini failing on all machines (sparc & x86) in s10u5_05
cth
parents:
4950
diff
changeset
|
4940 |
* NDI_PROMNAME flag does not apply to /devices/pseudo paths. |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4941 |
*/ |
5742
b0948deb63bc
6642582 Testcase initfini failing on all machines (sparc & x86) in s10u5_05
cth
parents:
4950
diff
changeset
|
4942 |
if ((flags & NDI_PROMNAME) && (pdip != pseudo_dip)) { |
2009 | 4943 |
drivername = child_path_to_driver(pdip, name, addr); |
4145 | 4944 |
find_by_addr = 1; |
4945 |
} |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4946 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4947 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4948 |
* Determine end_time: This routine should *not* be called with a |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4949 |
* constant non-zero timeout argument, the caller should be adjusting |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4950 |
* the timeout argument relative to when it *started* its asynchronous |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4951 |
* enumeration. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4952 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4953 |
if (timeout > 0) |
2009 | 4954 |
end_time = ddi_get_lbolt() + timeout; |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4955 |
|
2009 | 4956 |
for (;;) { |
1961
cceb6bfa61a5
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
1826
diff
changeset
|
4957 |
/* |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4958 |
* For pHCI, enter (vHCI, pHCI) and search for pathinfo/client |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4959 |
* child - break out of for(;;) loop if child found. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4960 |
* NOTE: Lock order for ndi_devi_enter is (vHCI, pHCI). |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4961 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4962 |
if (vdip) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4963 |
/* use mdi_devi_enter ordering */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4964 |
ndi_devi_enter(vdip, &v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4965 |
ndi_devi_enter(pdip, &p_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4966 |
cpip = mdi_pi_find(pdip, NULL, addr); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4967 |
cdip = mdi_pi_get_client(cpip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4968 |
if (cdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4969 |
break; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4970 |
} else |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4971 |
ndi_devi_enter(pdip, &p_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4972 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4973 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4974 |
* When not a vHCI or not all pHCI devices are required to |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4975 |
* enumerated under the vHCI (NDI_MDI_FALLBACK) search for |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4976 |
* devinfo child. |
0 | 4977 |
*/ |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4978 |
if ((vdip == NULL) || (flags & NDI_MDI_FALLBACK)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4979 |
/* determine if .conf nodes already built */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4980 |
probed = (DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4981 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4982 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4983 |
* Search for child by name, if not found then search |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4984 |
* for a node bound to the drivername driver with the |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4985 |
* specified "@addr". Break out of for(;;) loop if |
4145 | 4986 |
* child found. To support path-oriented aliases |
4987 |
* binding on boot-device, we do a search_by_addr too. |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4988 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4989 |
again: (void) i_ndi_make_spec_children(pdip, flags); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4990 |
cdip = find_child_by_name(pdip, name, addr); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4991 |
if ((cdip == NULL) && drivername) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4992 |
cdip = find_child_by_driver(pdip, |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4993 |
drivername, addr); |
4145 | 4994 |
if ((cdip == NULL) && find_by_addr) |
4995 |
cdip = find_child_by_addr(pdip, addr); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4996 |
if (cdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4997 |
break; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4998 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
4999 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5000 |
* determine if we should reenumerate .conf nodes |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5001 |
* and look for child again. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5002 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5003 |
if (probed && |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5004 |
i_ddi_io_initialized() && |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5005 |
(flags & NDI_CONFIG_REPROBE) && |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5006 |
((timeout <= 0) || (ddi_get_lbolt() >= end_time))) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5007 |
probed = 0; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5008 |
mutex_enter(&DEVI(pdip)->devi_lock); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5009 |
DEVI(pdip)->devi_flags &= ~DEVI_MADE_CHILDREN; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5010 |
mutex_exit(&DEVI(pdip)->devi_lock); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5011 |
goto again; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5012 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5013 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5014 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5015 |
/* break out of for(;;) if time expired */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5016 |
if ((timeout <= 0) || (ddi_get_lbolt() >= end_time)) |
0 | 5017 |
break; |
5018 |
||
5019 |
/* |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5020 |
* Child not found, exit and wait for asynchronous enumeration |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5021 |
* to add child (or timeout). The addition of a new child (vhci |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5022 |
* or phci) requires the asynchronous enumeration thread to |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5023 |
* ndi_devi_enter/ndi_devi_exit. This exit will signal devi_cv |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5024 |
* and cause us to return from ndi_devi_exit_and_wait, after |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5025 |
* which we loop and search for the requested child again. |
0 | 5026 |
*/ |
5027 |
NDI_DEBUG(flags, (CE_CONT, |
|
5028 |
"%s%d: waiting for child %s@%s, timeout %ld", |
|
5029 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
|
5030 |
name, addr, timeout)); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5031 |
if (vdip) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5032 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5033 |
* Mark vHCI for pHCI ndi_devi_exit broadcast. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5034 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5035 |
mutex_enter(&DEVI(vdip)->devi_lock); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5036 |
DEVI(vdip)->devi_flags |= |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5037 |
DEVI_PHCI_SIGNALS_VHCI; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5038 |
mutex_exit(&DEVI(vdip)->devi_lock); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5039 |
ndi_devi_exit(pdip, p_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5040 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5041 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5042 |
* NB: There is a small race window from above |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5043 |
* ndi_devi_exit() of pdip to cv_wait() in |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5044 |
* ndi_devi_exit_and_wait() which can result in |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5045 |
* not immediately finding a new pHCI child |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5046 |
* of a pHCI that uses NDI_MDI_FAILBACK. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5047 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5048 |
ndi_devi_exit_and_wait(vdip, v_circ, end_time); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5049 |
} else { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5050 |
ndi_devi_exit_and_wait(pdip, p_circ, end_time); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5051 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5052 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5053 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5054 |
/* done with paddr, fixup i_ddi_parse_name '@'->'\0' change */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5055 |
if (addr && *addr != '\0') |
0 | 5056 |
*(addr - 1) = '@'; |
5057 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5058 |
/* attach and hold the child, returning pointer to child */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5059 |
if (cdip && (devi_attach_node(cdip, flags) == NDI_SUCCESS)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5060 |
ndi_hold_devi(cdip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5061 |
*cdipp = cdip; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5062 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5063 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5064 |
ndi_devi_exit(pdip, p_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5065 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5066 |
ndi_devi_exit(vdip, v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5067 |
return (*cdipp ? NDI_SUCCESS : NDI_FAILURE); |
0 | 5068 |
} |
5069 |
||
5070 |
/* |
|
5071 |
* Enumerate and attach a child specified by name 'devnm'. |
|
5072 |
* Called by devfs lookup and DR to perform a BUS_CONFIG_ONE. |
|
5073 |
* Note: devfs does not make use of NDI_CONFIG to configure |
|
5074 |
* an entire branch. |
|
5075 |
*/ |
|
5076 |
int |
|
5077 |
ndi_devi_config_one(dev_info_t *dip, char *devnm, dev_info_t **dipp, int flags) |
|
5078 |
{ |
|
5079 |
int error; |
|
5080 |
int (*f)(); |
|
5081 |
int branch_event = 0; |
|
5082 |
||
5083 |
ASSERT(dipp); |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
5084 |
ASSERT(i_ddi_devi_attached(dip)); |
0 | 5085 |
|
5086 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
5087 |
"ndi_devi_config_one: par = %s%d (%p), child = %s\n", |
|
5088 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, devnm)); |
|
5089 |
||
5090 |
if (pm_pre_config(dip, devnm) != DDI_SUCCESS) |
|
5091 |
return (NDI_FAILURE); |
|
5092 |
||
5093 |
if ((flags & (NDI_NO_EVENT | NDI_BRANCH_EVENT_OP)) == 0 && |
|
5094 |
(flags & NDI_CONFIG)) { |
|
5095 |
flags |= NDI_BRANCH_EVENT_OP; |
|
5096 |
branch_event = 1; |
|
5097 |
} |
|
5098 |
||
5099 |
if ((DEVI(dip)->devi_ops->devo_bus_ops == NULL) || |
|
5100 |
(DEVI(dip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) || |
|
5101 |
(f = DEVI(dip)->devi_ops->devo_bus_ops->bus_config) == NULL) { |
|
5102 |
error = devi_config_one(dip, devnm, dipp, flags, 0); |
|
5103 |
} else { |
|
5104 |
/* call bus_config entry point */ |
|
5105 |
error = (*f)(dip, flags, BUS_CONFIG_ONE, (void *)devnm, dipp); |
|
5106 |
} |
|
5107 |
||
5108 |
if (error || (flags & NDI_CONFIG) == 0) { |
|
5109 |
pm_post_config(dip, devnm); |
|
5110 |
return (error); |
|
5111 |
} |
|
5112 |
||
5113 |
/* |
|
4145 | 5114 |
* DR usage (i.e. call with NDI_CONFIG) recursively configures |
0 | 5115 |
* grandchildren, performing a BUS_CONFIG_ALL from the node attached |
5116 |
* by the BUS_CONFIG_ONE. |
|
5117 |
*/ |
|
5118 |
ASSERT(*dipp); |
|
5119 |
||
5120 |
error = devi_config_common(*dipp, flags, (major_t)-1); |
|
5121 |
||
5122 |
pm_post_config(dip, devnm); |
|
5123 |
||
5124 |
if (branch_event) |
|
5125 |
(void) i_log_devfs_branch_add(*dipp); |
|
5126 |
||
5127 |
return (error); |
|
5128 |
} |
|
5129 |
||
5130 |
||
5131 |
/* |
|
5132 |
* Enumerate and attach a child specified by name 'devnm'. |
|
5133 |
* Called during configure the OBP options. This configures |
|
5134 |
* only one node. |
|
5135 |
*/ |
|
5136 |
static int |
|
5137 |
ndi_devi_config_obp_args(dev_info_t *parent, char *devnm, |
|
5138 |
dev_info_t **childp, int flags) |
|
5139 |
{ |
|
5140 |
int error; |
|
5141 |
int (*f)(); |
|
5142 |
||
5143 |
ASSERT(childp); |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
5144 |
ASSERT(i_ddi_devi_attached(parent)); |
0 | 5145 |
|
5146 |
NDI_CONFIG_DEBUG((CE_CONT, "ndi_devi_config_obp_args: " |
|
5147 |
"par = %s%d (%p), child = %s\n", ddi_driver_name(parent), |
|
5148 |
ddi_get_instance(parent), (void *)parent, devnm)); |
|
5149 |
||
5150 |
if ((DEVI(parent)->devi_ops->devo_bus_ops == NULL) || |
|
5151 |
(DEVI(parent)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) || |
|
5152 |
(f = DEVI(parent)->devi_ops->devo_bus_ops->bus_config) == NULL) { |
|
5153 |
error = NDI_FAILURE; |
|
5154 |
} else { |
|
5155 |
/* call bus_config entry point */ |
|
5156 |
error = (*f)(parent, flags, |
|
5157 |
BUS_CONFIG_OBP_ARGS, (void *)devnm, childp); |
|
5158 |
} |
|
5159 |
return (error); |
|
5160 |
} |
|
5161 |
||
4845 | 5162 |
/* |
5163 |
* Pay attention, the following is a bit tricky: |
|
5164 |
* There are three possible cases when constraints are applied |
|
5165 |
* |
|
5166 |
* - A constraint is applied and the offline is disallowed. |
|
5167 |
* Simply return failure and block the offline |
|
5168 |
* |
|
5169 |
* - A constraint is applied and the offline is allowed. |
|
5170 |
* Mark the dip as having passed the constraint and allow |
|
5171 |
* offline to proceed. |
|
5172 |
* |
|
5173 |
* - A constraint is not applied. Allow the offline to proceed for now. |
|
5174 |
* |
|
5175 |
* In the latter two cases we allow the offline to proceed. If the |
|
5176 |
* offline succeeds (no users) everything is fine. It is ok for an unused |
|
5177 |
* device to be offlined even if no constraints were imposed on the offline. |
|
5178 |
* If the offline fails because there are users, we look at the constraint |
|
5179 |
* flag on the dip. If the constraint flag is set (implying that it passed |
|
5180 |
* a constraint) we allow the dip to be retired. If not, we don't allow |
|
5181 |
* the retire. This ensures that we don't allow unconstrained retire. |
|
5182 |
*/ |
|
5183 |
int |
|
5184 |
e_ddi_offline_notify(dev_info_t *dip) |
|
5185 |
{ |
|
5186 |
int retval; |
|
5187 |
int constraint; |
|
5188 |
int failure; |
|
5189 |
||
5190 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): entered: dip=%p", |
|
5191 |
(void *) dip)); |
|
5192 |
||
5193 |
constraint = 0; |
|
5194 |
failure = 0; |
|
5195 |
||
5196 |
/* |
|
5197 |
* Start with userland constraints first - applied via device contracts |
|
5198 |
*/ |
|
5199 |
retval = contract_device_offline(dip, DDI_DEV_T_ANY, 0); |
|
5200 |
switch (retval) { |
|
5201 |
case CT_NACK: |
|
5202 |
RIO_DEBUG((CE_NOTE, "Received NACK for dip=%p", (void *)dip)); |
|
5203 |
failure = 1; |
|
5204 |
goto out; |
|
5205 |
case CT_ACK: |
|
5206 |
constraint = 1; |
|
5207 |
RIO_DEBUG((CE_NOTE, "Received ACK for dip=%p", (void *)dip)); |
|
5208 |
break; |
|
5209 |
case CT_NONE: |
|
5210 |
/* no contracts */ |
|
5211 |
RIO_DEBUG((CE_NOTE, "No contracts on dip=%p", (void *)dip)); |
|
5212 |
break; |
|
5213 |
default: |
|
5214 |
ASSERT(retval == CT_NONE); |
|
5215 |
} |
|
5216 |
||
5217 |
/* |
|
5218 |
* Next, use LDI to impose kernel constraints |
|
5219 |
*/ |
|
5220 |
retval = ldi_invoke_notify(dip, DDI_DEV_T_ANY, 0, LDI_EV_OFFLINE, NULL); |
|
5221 |
switch (retval) { |
|
5222 |
case LDI_EV_FAILURE: |
|
5223 |
contract_device_negend(dip, DDI_DEV_T_ANY, 0, CT_EV_FAILURE); |
|
5224 |
RIO_DEBUG((CE_NOTE, "LDI callback failed on dip=%p", |
|
5225 |
(void *)dip)); |
|
5226 |
failure = 1; |
|
5227 |
goto out; |
|
5228 |
case LDI_EV_SUCCESS: |
|
5229 |
constraint = 1; |
|
5230 |
RIO_DEBUG((CE_NOTE, "LDI callback success on dip=%p", |
|
5231 |
(void *)dip)); |
|
5232 |
break; |
|
5233 |
case LDI_EV_NONE: |
|
5234 |
/* no matching LDI callbacks */ |
|
5235 |
RIO_DEBUG((CE_NOTE, "No LDI callbacks for dip=%p", |
|
5236 |
(void *)dip)); |
|
5237 |
break; |
|
5238 |
default: |
|
5239 |
ASSERT(retval == LDI_EV_NONE); |
|
5240 |
} |
|
5241 |
||
5242 |
out: |
|
5243 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
|
5244 |
if ((DEVI(dip)->devi_flags & DEVI_RETIRING) && failure) { |
|
5245 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): setting " |
|
5246 |
"BLOCKED flag. dip=%p", (void *)dip)); |
|
5247 |
DEVI(dip)->devi_flags |= DEVI_R_BLOCKED; |
|
5248 |
if (DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT) { |
|
5249 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): " |
|
5250 |
"blocked. clearing RCM CONSTRAINT flag. dip=%p", |
|
5251 |
(void *)dip)); |
|
5252 |
DEVI(dip)->devi_flags &= ~DEVI_R_CONSTRAINT; |
|
5253 |
} |
|
5254 |
} else if ((DEVI(dip)->devi_flags & DEVI_RETIRING) && constraint) { |
|
5255 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): setting " |
|
5256 |
"CONSTRAINT flag. dip=%p", (void *)dip)); |
|
5257 |
DEVI(dip)->devi_flags |= DEVI_R_CONSTRAINT; |
|
5258 |
} else if ((DEVI(dip)->devi_flags & DEVI_RETIRING) && |
|
5259 |
DEVI(dip)->devi_ref == 0) { |
|
5260 |
/* also allow retire if device is not in use */ |
|
5261 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): device not in " |
|
5262 |
"use. Setting CONSTRAINT flag. dip=%p", (void *)dip)); |
|
5263 |
DEVI(dip)->devi_flags |= DEVI_R_CONSTRAINT; |
|
5264 |
} else { |
|
5265 |
/* |
|
5266 |
* Note: We cannot ASSERT here that DEVI_R_CONSTRAINT is |
|
5267 |
* not set, since other sources (such as RCM) may have |
|
5268 |
* set the flag. |
|
5269 |
*/ |
|
5270 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): not setting " |
|
5271 |
"constraint flag. dip=%p", (void *)dip)); |
|
5272 |
} |
|
5273 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
|
5274 |
||
5275 |
||
5276 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_notify(): exit: dip=%p", |
|
5277 |
(void *) dip)); |
|
5278 |
||
5279 |
return (failure ? DDI_FAILURE : DDI_SUCCESS); |
|
5280 |
} |
|
5281 |
||
5282 |
void |
|
5283 |
e_ddi_offline_finalize(dev_info_t *dip, int result) |
|
5284 |
{ |
|
5285 |
RIO_DEBUG((CE_NOTE, "e_ddi_offline_finalize(): entry: result=%s, " |
|
5286 |
"dip=%p", result == DDI_SUCCESS ? "SUCCESS" : "FAILURE", |
|
5287 |
(void *)dip)); |
|
5288 |
||
5289 |
contract_device_negend(dip, DDI_DEV_T_ANY, 0, result == DDI_SUCCESS ? |
|
5290 |
CT_EV_SUCCESS : CT_EV_FAILURE); |
|
5291 |
||
5292 |
ldi_invoke_finalize(dip, DDI_DEV_T_ANY, 0, |
|
5293 |
LDI_EV_OFFLINE, result == DDI_SUCCESS ? |
|
5294 |
LDI_EV_SUCCESS : LDI_EV_FAILURE, NULL); |
|
5295 |
||
5296 |
RIO_VERBOSE((CE_NOTE, "e_ddi_offline_finalize(): exit: dip=%p", |
|
5297 |
(void *)dip)); |
|
5298 |
} |
|
5299 |
||
5300 |
void |
|
5301 |
e_ddi_degrade_finalize(dev_info_t *dip) |
|
5302 |
{ |
|
5303 |
RIO_DEBUG((CE_NOTE, "e_ddi_degrade_finalize(): entry: " |
|
5304 |
"result always = DDI_SUCCESS, dip=%p", (void *)dip)); |
|
5305 |
||
5306 |
contract_device_degrade(dip, DDI_DEV_T_ANY, 0); |
|
5307 |
contract_device_negend(dip, DDI_DEV_T_ANY, 0, CT_EV_SUCCESS); |
|
5308 |
||
5309 |
ldi_invoke_finalize(dip, DDI_DEV_T_ANY, 0, LDI_EV_DEGRADE, |
|
5310 |
LDI_EV_SUCCESS, NULL); |
|
5311 |
||
5312 |
RIO_VERBOSE((CE_NOTE, "e_ddi_degrade_finalize(): exit: dip=%p", |
|
5313 |
(void *)dip)); |
|
5314 |
} |
|
5315 |
||
5316 |
void |
|
5317 |
e_ddi_undegrade_finalize(dev_info_t *dip) |
|
5318 |
{ |
|
5319 |
RIO_DEBUG((CE_NOTE, "e_ddi_undegrade_finalize(): entry: " |
|
5320 |
"result always = DDI_SUCCESS, dip=%p", (void *)dip)); |
|
5321 |
||
5322 |
contract_device_undegrade(dip, DDI_DEV_T_ANY, 0); |
|
5323 |
contract_device_negend(dip, DDI_DEV_T_ANY, 0, CT_EV_SUCCESS); |
|
5324 |
||
5325 |
RIO_VERBOSE((CE_NOTE, "e_ddi_undegrade_finalize(): exit: dip=%p", |
|
5326 |
(void *)dip)); |
|
5327 |
} |
|
0 | 5328 |
|
5329 |
/* |
|
5330 |
* detach a node with parent already held busy |
|
5331 |
*/ |
|
5332 |
static int |
|
5333 |
devi_detach_node(dev_info_t *dip, uint_t flags) |
|
5334 |
{ |
|
5335 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
5336 |
int ret = NDI_SUCCESS; |
|
5337 |
ddi_eventcookie_t cookie; |
|
5338 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5339 |
ASSERT(pdip && DEVI_BUSY_OWNED(pdip)); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5340 |
|
4845 | 5341 |
/* |
5342 |
* Invoke notify if offlining |
|
5343 |
*/ |
|
5344 |
if (flags & NDI_DEVI_OFFLINE) { |
|
5345 |
RIO_DEBUG((CE_NOTE, "devi_detach_node: offlining dip=%p", |
|
5346 |
(void *)dip)); |
|
5347 |
if (e_ddi_offline_notify(dip) != DDI_SUCCESS) { |
|
5348 |
RIO_DEBUG((CE_NOTE, "devi_detach_node: offline NACKed" |
|
5349 |
"dip=%p", (void *)dip)); |
|
5350 |
return (NDI_FAILURE); |
|
5351 |
} |
|
5352 |
} |
|
5353 |
||
0 | 5354 |
if (flags & NDI_POST_EVENT) { |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5355 |
if (i_ddi_devi_attached(pdip)) { |
0 | 5356 |
if (ddi_get_eventcookie(dip, DDI_DEVI_REMOVE_EVENT, |
5357 |
&cookie) == NDI_SUCCESS) |
|
5358 |
(void) ndi_post_event(dip, dip, cookie, NULL); |
|
5359 |
} |
|
5360 |
} |
|
5361 |
||
4845 | 5362 |
if (i_ddi_detachchild(dip, flags) != DDI_SUCCESS) { |
5363 |
if (flags & NDI_DEVI_OFFLINE) { |
|
5364 |
RIO_DEBUG((CE_NOTE, "devi_detach_node: offline failed." |
|
5365 |
" Calling e_ddi_offline_finalize with result=%d. " |
|
5366 |
"dip=%p", DDI_FAILURE, (void *)dip)); |
|
5367 |
e_ddi_offline_finalize(dip, DDI_FAILURE); |
|
5368 |
} |
|
0 | 5369 |
return (NDI_FAILURE); |
4845 | 5370 |
} |
5371 |
||
5372 |
if (flags & NDI_DEVI_OFFLINE) { |
|
5373 |
RIO_DEBUG((CE_NOTE, "devi_detach_node: offline succeeded." |
|
5374 |
" Calling e_ddi_offline_finalize with result=%d, " |
|
5375 |
"dip=%p", DDI_SUCCESS, (void *)dip)); |
|
5376 |
e_ddi_offline_finalize(dip, DDI_SUCCESS); |
|
5377 |
} |
|
0 | 5378 |
|
5379 |
if (flags & NDI_AUTODETACH) |
|
5380 |
return (NDI_SUCCESS); |
|
5381 |
||
5382 |
/* |
|
5383 |
* For DR, even bound nodes may need to have offline |
|
5384 |
* flag set. |
|
5385 |
*/ |
|
5386 |
if (flags & NDI_DEVI_OFFLINE) { |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5387 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 5388 |
DEVI_SET_DEVICE_OFFLINE(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5389 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 5390 |
} |
5391 |
||
5392 |
if (i_ddi_node_state(dip) == DS_INITIALIZED) { |
|
5393 |
char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
5394 |
(void) ddi_pathname(dip, path); |
|
5395 |
if (flags & NDI_DEVI_OFFLINE) |
|
5396 |
i_ndi_devi_report_status_change(dip, path); |
|
5397 |
||
5398 |
if (need_remove_event(dip, flags)) { |
|
5399 |
(void) i_log_devfs_remove_devinfo(path, |
|
5400 |
i_ddi_devi_class(dip), |
|
5401 |
(char *)ddi_driver_name(dip), |
|
5402 |
ddi_get_instance(dip), |
|
5403 |
flags); |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5404 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 5405 |
DEVI_SET_EVREMOVE(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5406 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 5407 |
} |
5408 |
kmem_free(path, MAXPATHLEN); |
|
5409 |
} |
|
5410 |
||
5411 |
if (flags & (NDI_UNCONFIG | NDI_DEVI_REMOVE)) { |
|
5412 |
ret = ddi_uninitchild(dip); |
|
5413 |
if (ret == NDI_SUCCESS) { |
|
5414 |
/* |
|
5415 |
* Remove uninitialized pseudo nodes because |
|
5416 |
* system props are lost and the node cannot be |
|
5417 |
* reattached. |
|
5418 |
*/ |
|
5419 |
if (!ndi_dev_is_persistent_node(dip)) |
|
5420 |
flags |= NDI_DEVI_REMOVE; |
|
5421 |
||
5422 |
if (flags & NDI_DEVI_REMOVE) |
|
5423 |
ret = ddi_remove_child(dip, 0); |
|
5424 |
} |
|
5425 |
} |
|
5426 |
||
5427 |
return (ret); |
|
5428 |
} |
|
5429 |
||
5430 |
/* |
|
5431 |
* unconfigure immediate children of bus nexus device |
|
5432 |
*/ |
|
5433 |
static int |
|
5434 |
unconfig_immediate_children( |
|
5435 |
dev_info_t *dip, |
|
5436 |
dev_info_t **dipp, |
|
5437 |
int flags, |
|
5438 |
major_t major) |
|
5439 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5440 |
int rv = NDI_SUCCESS; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5441 |
int circ, vcirc; |
0 | 5442 |
dev_info_t *child; |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5443 |
dev_info_t *vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5444 |
dev_info_t *next; |
0 | 5445 |
|
5446 |
ASSERT(dipp == NULL || *dipp == NULL); |
|
5447 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5448 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5449 |
* Scan forward to see if we will be processing a pHCI child. If we |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5450 |
* have a child that is a pHCI and vHCI and pHCI are not siblings then |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5451 |
* enter vHCI before parent(pHCI) to prevent deadlock with mpxio |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5452 |
* Client power management operations. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5453 |
*/ |
0 | 5454 |
ndi_devi_enter(dip, &circ); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5455 |
for (child = ddi_get_child(dip); child; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5456 |
child = ddi_get_next_sibling(child)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5457 |
/* skip same nodes we skip below */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5458 |
if (((major != (major_t)-1) && |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5459 |
(major != ddi_driver_major(child))) || |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5460 |
((flags & NDI_AUTODETACH) && !is_leaf_node(child))) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5461 |
continue; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5462 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5463 |
if (MDI_PHCI(child)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5464 |
vdip = mdi_devi_get_vdip(child); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5465 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5466 |
* If vHCI and vHCI is not a sibling of pHCI |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5467 |
* then enter in (vHCI, parent(pHCI)) order. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5468 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5469 |
if (vdip && (ddi_get_parent(vdip) != dip)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5470 |
ndi_devi_exit(dip, circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5471 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5472 |
/* use mdi_devi_enter ordering */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5473 |
ndi_devi_enter(vdip, &vcirc); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5474 |
ndi_devi_enter(dip, &circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5475 |
break; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5476 |
} else |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5477 |
vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5478 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5479 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5480 |
|
0 | 5481 |
child = ddi_get_child(dip); |
5482 |
while (child) { |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5483 |
next = ddi_get_next_sibling(child); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5484 |
|
0 | 5485 |
if ((major != (major_t)-1) && |
5486 |
(major != ddi_driver_major(child))) { |
|
5487 |
child = next; |
|
5488 |
continue; |
|
5489 |
} |
|
5490 |
||
5491 |
/* skip nexus nodes during autodetach */ |
|
5492 |
if ((flags & NDI_AUTODETACH) && !is_leaf_node(child)) { |
|
5493 |
child = next; |
|
5494 |
continue; |
|
5495 |
} |
|
5496 |
||
5497 |
if (devi_detach_node(child, flags) != NDI_SUCCESS) { |
|
5498 |
if (dipp && *dipp == NULL) { |
|
5499 |
ndi_hold_devi(child); |
|
5500 |
*dipp = child; |
|
5501 |
} |
|
5502 |
rv = NDI_FAILURE; |
|
5503 |
} |
|
5504 |
||
5505 |
/* |
|
5506 |
* Continue upon failure--best effort algorithm |
|
5507 |
*/ |
|
5508 |
child = next; |
|
5509 |
} |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5510 |
|
0 | 5511 |
ndi_devi_exit(dip, circ); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5512 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5513 |
ndi_devi_exit(vdip, vcirc); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5514 |
|
0 | 5515 |
return (rv); |
5516 |
} |
|
5517 |
||
5518 |
/* |
|
5519 |
* unconfigure grand children of bus nexus device |
|
5520 |
*/ |
|
5521 |
static int |
|
5522 |
unconfig_grand_children( |
|
5523 |
dev_info_t *dip, |
|
5524 |
dev_info_t **dipp, |
|
5525 |
int flags, |
|
5526 |
major_t major, |
|
5527 |
struct brevq_node **brevqp) |
|
5528 |
{ |
|
5529 |
struct mt_config_handle *hdl; |
|
5530 |
||
5531 |
if (brevqp) |
|
5532 |
*brevqp = NULL; |
|
5533 |
||
5534 |
/* multi-threaded configuration of child nexus */ |
|
5535 |
hdl = mt_config_init(dip, dipp, flags, major, MT_UNCONFIG_OP, brevqp); |
|
5536 |
mt_config_children(hdl); |
|
5537 |
||
5538 |
return (mt_config_fini(hdl)); /* wait for threads to exit */ |
|
5539 |
} |
|
5540 |
||
5541 |
/* |
|
5542 |
* Unconfigure children/descendants of the dip. |
|
5543 |
* |
|
5544 |
* If brevqp is not NULL, on return *brevqp is set to a queue of dip's |
|
5545 |
* child devinames for which branch remove events need to be generated. |
|
5546 |
*/ |
|
5547 |
static int |
|
5548 |
devi_unconfig_common( |
|
5549 |
dev_info_t *dip, |
|
5550 |
dev_info_t **dipp, |
|
5551 |
int flags, |
|
5552 |
major_t major, |
|
5553 |
struct brevq_node **brevqp) |
|
5554 |
{ |
|
5555 |
int rv; |
|
5556 |
int pm_cookie; |
|
5557 |
int (*f)(); |
|
5558 |
ddi_bus_config_op_t bus_op; |
|
5559 |
||
5560 |
if (dipp) |
|
5561 |
*dipp = NULL; |
|
5562 |
if (brevqp) |
|
5563 |
*brevqp = NULL; |
|
5564 |
||
5565 |
/* |
|
5566 |
* Power up the dip if it is powered off. If the flag bit |
|
5567 |
* NDI_AUTODETACH is set and the dip is not at its full power, |
|
5568 |
* skip the rest of the branch. |
|
5569 |
*/ |
|
5570 |
if (pm_pre_unconfig(dip, flags, &pm_cookie, NULL) != DDI_SUCCESS) |
|
5571 |
return ((flags & NDI_AUTODETACH) ? NDI_SUCCESS : |
|
5572 |
NDI_FAILURE); |
|
5573 |
||
5574 |
/* |
|
5575 |
* Some callers, notably SCSI, need to clear out the devfs |
|
5576 |
* cache together with the unconfig to prevent stale entries. |
|
5577 |
*/ |
|
5578 |
if (flags & NDI_DEVFS_CLEAN) |
|
5579 |
(void) devfs_clean(dip, NULL, 0); |
|
5580 |
||
5581 |
rv = unconfig_grand_children(dip, dipp, flags, major, brevqp); |
|
5582 |
||
5583 |
if ((rv != NDI_SUCCESS) && ((flags & NDI_AUTODETACH) == 0)) { |
|
5584 |
if (brevqp && *brevqp) { |
|
5585 |
log_and_free_br_events_on_grand_children(dip, *brevqp); |
|
5586 |
free_brevq(*brevqp); |
|
5587 |
*brevqp = NULL; |
|
5588 |
} |
|
5589 |
pm_post_unconfig(dip, pm_cookie, NULL); |
|
5590 |
return (rv); |
|
5591 |
} |
|
5592 |
||
5593 |
if (dipp && *dipp) { |
|
5594 |
ndi_rele_devi(*dipp); |
|
5595 |
*dipp = NULL; |
|
5596 |
} |
|
5597 |
||
5598 |
/* |
|
5599 |
* It is possible to have a detached nexus with children |
|
5600 |
* and grandchildren (for example: a branch consisting |
|
5601 |
* entirely of bound nodes.) Since the nexus is detached |
|
5602 |
* the bus_unconfig entry point cannot be used to remove |
|
5603 |
* or unconfigure the descendants. |
|
5604 |
*/ |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
5605 |
if (!i_ddi_devi_attached(dip) || |
0 | 5606 |
(DEVI(dip)->devi_ops->devo_bus_ops == NULL) || |
5607 |
(DEVI(dip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) || |
|
5608 |
(f = DEVI(dip)->devi_ops->devo_bus_ops->bus_unconfig) == NULL) { |
|
5609 |
rv = unconfig_immediate_children(dip, dipp, flags, major); |
|
5610 |
} else { |
|
5611 |
/* |
|
5612 |
* call bus_unconfig entry point |
|
5613 |
* It should reset nexus flags if unconfigure succeeds. |
|
5614 |
*/ |
|
5615 |
bus_op = (major == (major_t)-1) ? |
|
5616 |
BUS_UNCONFIG_ALL : BUS_UNCONFIG_DRIVER; |
|
5617 |
rv = (*f)(dip, flags, bus_op, (void *)(uintptr_t)major); |
|
5618 |
} |
|
5619 |
||
5620 |
pm_post_unconfig(dip, pm_cookie, NULL); |
|
5621 |
||
5622 |
if (brevqp && *brevqp) |
|
5623 |
cleanup_br_events_on_grand_children(dip, brevqp); |
|
5624 |
||
5625 |
return (rv); |
|
5626 |
} |
|
5627 |
||
5628 |
/* |
|
5629 |
* called by devfs/framework to unconfigure children bound to major |
|
5630 |
* If NDI_AUTODETACH is specified, this is invoked by either the |
|
5631 |
* moduninstall daemon or the modunload -i 0 command. |
|
5632 |
*/ |
|
5633 |
int |
|
5634 |
ndi_devi_unconfig_driver(dev_info_t *dip, int flags, major_t major) |
|
5635 |
{ |
|
5636 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
5637 |
"ndi_devi_unconfig_driver: par = %s%d (%p), flags = 0x%x\n", |
|
5638 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
5639 |
||
5640 |
return (devi_unconfig_common(dip, NULL, flags, major, NULL)); |
|
5641 |
} |
|
5642 |
||
5643 |
int |
|
5644 |
ndi_devi_unconfig(dev_info_t *dip, int flags) |
|
5645 |
{ |
|
5646 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
5647 |
"ndi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n", |
|
5648 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
5649 |
||
5650 |
return (devi_unconfig_common(dip, NULL, flags, (major_t)-1, NULL)); |
|
5651 |
} |
|
5652 |
||
5653 |
int |
|
5654 |
e_ddi_devi_unconfig(dev_info_t *dip, dev_info_t **dipp, int flags) |
|
5655 |
{ |
|
5656 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
5657 |
"e_ddi_devi_unconfig: par = %s%d (%p), flags = 0x%x\n", |
|
5658 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, flags)); |
|
5659 |
||
5660 |
return (devi_unconfig_common(dip, dipp, flags, (major_t)-1, NULL)); |
|
5661 |
} |
|
5662 |
||
5663 |
/* |
|
5664 |
* Unconfigure child by name |
|
5665 |
*/ |
|
5666 |
static int |
|
5667 |
devi_unconfig_one(dev_info_t *pdip, char *devnm, int flags) |
|
5668 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5669 |
int rv, circ; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5670 |
dev_info_t *child; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5671 |
dev_info_t *vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5672 |
int v_circ; |
0 | 5673 |
|
5674 |
ndi_devi_enter(pdip, &circ); |
|
5675 |
child = ndi_devi_findchild(pdip, devnm); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5676 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5677 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5678 |
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5679 |
* before parent(pHCI) to avoid deadlock with mpxio Client power |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5680 |
* management operations. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5681 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5682 |
if (child && MDI_PHCI(child)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5683 |
vdip = mdi_devi_get_vdip(child); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5684 |
if (vdip && (ddi_get_parent(vdip) != pdip)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5685 |
ndi_devi_exit(pdip, circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5686 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5687 |
/* use mdi_devi_enter ordering */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5688 |
ndi_devi_enter(vdip, &v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5689 |
ndi_devi_enter(pdip, &circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5690 |
child = ndi_devi_findchild(pdip, devnm); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5691 |
} else |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5692 |
vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5693 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5694 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5695 |
if (child) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5696 |
rv = devi_detach_node(child, flags); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5697 |
} else { |
0 | 5698 |
NDI_CONFIG_DEBUG((CE_CONT, |
5699 |
"devi_unconfig_one: %s not found\n", devnm)); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5700 |
rv = NDI_SUCCESS; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5701 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5702 |
|
0 | 5703 |
ndi_devi_exit(pdip, circ); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5704 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5705 |
ndi_devi_exit(pdip, v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5706 |
|
0 | 5707 |
return (rv); |
5708 |
} |
|
5709 |
||
5710 |
int |
|
5711 |
ndi_devi_unconfig_one( |
|
5712 |
dev_info_t *pdip, |
|
5713 |
char *devnm, |
|
5714 |
dev_info_t **dipp, |
|
5715 |
int flags) |
|
5716 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5717 |
int (*f)(); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5718 |
int circ, rv; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5719 |
int pm_cookie; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5720 |
dev_info_t *child; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5721 |
dev_info_t *vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5722 |
int v_circ; |
0 | 5723 |
struct brevq_node *brevq = NULL; |
5724 |
||
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
5725 |
ASSERT(i_ddi_devi_attached(pdip)); |
0 | 5726 |
|
5727 |
NDI_CONFIG_DEBUG((CE_CONT, |
|
5728 |
"ndi_devi_unconfig_one: par = %s%d (%p), child = %s\n", |
|
5729 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
|
5730 |
(void *)pdip, devnm)); |
|
5731 |
||
5732 |
if (pm_pre_unconfig(pdip, flags, &pm_cookie, devnm) != DDI_SUCCESS) |
|
5733 |
return (NDI_FAILURE); |
|
5734 |
||
5735 |
if (dipp) |
|
5736 |
*dipp = NULL; |
|
5737 |
||
5738 |
ndi_devi_enter(pdip, &circ); |
|
5739 |
child = ndi_devi_findchild(pdip, devnm); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5740 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5741 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5742 |
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5743 |
* before parent(pHCI) to avoid deadlock with mpxio Client power |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5744 |
* management operations. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5745 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5746 |
if (child && MDI_PHCI(child)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5747 |
vdip = mdi_devi_get_vdip(child); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5748 |
if (vdip && (ddi_get_parent(vdip) != pdip)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5749 |
ndi_devi_exit(pdip, circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5750 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5751 |
/* use mdi_devi_enter ordering */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5752 |
ndi_devi_enter(vdip, &v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5753 |
ndi_devi_enter(pdip, &circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5754 |
child = ndi_devi_findchild(pdip, devnm); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5755 |
} else |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5756 |
vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5757 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5758 |
|
0 | 5759 |
if (child == NULL) { |
5760 |
NDI_CONFIG_DEBUG((CE_CONT, "ndi_devi_unconfig_one: %s" |
|
5761 |
" not found\n", devnm)); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5762 |
rv = NDI_SUCCESS; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5763 |
goto out; |
0 | 5764 |
} |
5765 |
||
5766 |
/* |
|
5767 |
* Unconfigure children/descendants of named child |
|
5768 |
*/ |
|
5769 |
rv = devi_unconfig_branch(child, dipp, flags | NDI_UNCONFIG, &brevq); |
|
5770 |
if (rv != NDI_SUCCESS) |
|
5771 |
goto out; |
|
5772 |
||
5773 |
init_bound_node_ev(pdip, child, flags); |
|
5774 |
||
5775 |
if ((DEVI(pdip)->devi_ops->devo_bus_ops == NULL) || |
|
5776 |
(DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) || |
|
5777 |
(f = DEVI(pdip)->devi_ops->devo_bus_ops->bus_unconfig) == NULL) { |
|
5778 |
rv = devi_detach_node(child, flags); |
|
5779 |
} else { |
|
5780 |
/* call bus_config entry point */ |
|
5781 |
rv = (*f)(pdip, flags, BUS_UNCONFIG_ONE, (void *)devnm); |
|
5782 |
} |
|
5783 |
||
5784 |
if (brevq) { |
|
5785 |
if (rv != NDI_SUCCESS) |
|
5786 |
log_and_free_brevq_dip(child, brevq); |
|
5787 |
else |
|
5788 |
free_brevq(brevq); |
|
5789 |
} |
|
5790 |
||
5791 |
if (dipp && rv != NDI_SUCCESS) { |
|
5792 |
ndi_hold_devi(child); |
|
5793 |
ASSERT(*dipp == NULL); |
|
5794 |
*dipp = child; |
|
5795 |
} |
|
5796 |
||
5797 |
out: |
|
5798 |
ndi_devi_exit(pdip, circ); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5799 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5800 |
ndi_devi_exit(pdip, v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5801 |
|
0 | 5802 |
pm_post_unconfig(pdip, pm_cookie, devnm); |
5803 |
||
5804 |
return (rv); |
|
5805 |
} |
|
5806 |
||
5807 |
struct async_arg { |
|
5808 |
dev_info_t *dip; |
|
5809 |
uint_t flags; |
|
5810 |
}; |
|
5811 |
||
5812 |
/* |
|
5813 |
* Common async handler for: |
|
5814 |
* ndi_devi_bind_driver_async |
|
5815 |
* ndi_devi_online_async |
|
5816 |
*/ |
|
5817 |
static int |
|
5818 |
i_ndi_devi_async_common(dev_info_t *dip, uint_t flags, void (*func)()) |
|
5819 |
{ |
|
5820 |
int tqflag; |
|
5821 |
int kmflag; |
|
5822 |
struct async_arg *arg; |
|
5823 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
5824 |
||
5825 |
ASSERT(pdip); |
|
5826 |
ASSERT(DEVI(pdip)->devi_taskq); |
|
5827 |
ASSERT(ndi_dev_is_persistent_node(dip)); |
|
5828 |
||
5829 |
if (flags & NDI_NOSLEEP) { |
|
5830 |
kmflag = KM_NOSLEEP; |
|
5831 |
tqflag = TQ_NOSLEEP; |
|
5832 |
} else { |
|
5833 |
kmflag = KM_SLEEP; |
|
5834 |
tqflag = TQ_SLEEP; |
|
5835 |
} |
|
5836 |
||
5837 |
arg = kmem_alloc(sizeof (*arg), kmflag); |
|
5838 |
if (arg == NULL) |
|
5839 |
goto fail; |
|
5840 |
||
5841 |
arg->flags = flags; |
|
5842 |
arg->dip = dip; |
|
5843 |
if (ddi_taskq_dispatch(DEVI(pdip)->devi_taskq, func, arg, tqflag) == |
|
5844 |
DDI_SUCCESS) { |
|
5845 |
return (NDI_SUCCESS); |
|
5846 |
} |
|
5847 |
||
5848 |
fail: |
|
5849 |
NDI_CONFIG_DEBUG((CE_CONT, "%s%d: ddi_taskq_dispatch failed", |
|
5850 |
ddi_driver_name(pdip), ddi_get_instance(pdip))); |
|
5851 |
||
5852 |
if (arg) |
|
5853 |
kmem_free(arg, sizeof (*arg)); |
|
5854 |
return (NDI_FAILURE); |
|
5855 |
} |
|
5856 |
||
5857 |
static void |
|
5858 |
i_ndi_devi_bind_driver_cb(struct async_arg *arg) |
|
5859 |
{ |
|
5860 |
(void) ndi_devi_bind_driver(arg->dip, arg->flags); |
|
5861 |
kmem_free(arg, sizeof (*arg)); |
|
5862 |
} |
|
5863 |
||
5864 |
int |
|
5865 |
ndi_devi_bind_driver_async(dev_info_t *dip, uint_t flags) |
|
5866 |
{ |
|
5867 |
return (i_ndi_devi_async_common(dip, flags, |
|
5868 |
(void (*)())i_ndi_devi_bind_driver_cb)); |
|
5869 |
} |
|
5870 |
||
5871 |
/* |
|
5872 |
* place the devinfo in the ONLINE state. |
|
5873 |
*/ |
|
5874 |
int |
|
5875 |
ndi_devi_online(dev_info_t *dip, uint_t flags) |
|
5876 |
{ |
|
5877 |
int circ, rv; |
|
5878 |
dev_info_t *pdip = ddi_get_parent(dip); |
|
5879 |
int branch_event = 0; |
|
5880 |
||
5881 |
ASSERT(pdip); |
|
5882 |
||
5883 |
NDI_CONFIG_DEBUG((CE_CONT, "ndi_devi_online: %s%d (%p)\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
5884 |
ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip)); |
0 | 5885 |
|
5886 |
ndi_devi_enter(pdip, &circ); |
|
5887 |
/* bind child before merging .conf nodes */ |
|
5888 |
rv = i_ndi_config_node(dip, DS_BOUND, flags); |
|
5889 |
if (rv != NDI_SUCCESS) { |
|
5890 |
ndi_devi_exit(pdip, circ); |
|
5891 |
return (rv); |
|
5892 |
} |
|
5893 |
||
5894 |
/* merge .conf properties */ |
|
5895 |
(void) i_ndi_make_spec_children(pdip, flags); |
|
5896 |
||
2009 | 5897 |
flags |= (NDI_DEVI_ONLINE | NDI_CONFIG); |
0 | 5898 |
|
5899 |
if (flags & NDI_NO_EVENT) { |
|
5900 |
/* |
|
5901 |
* Caller is specifically asking for not to generate an event. |
|
5902 |
* Set the following flag so that devi_attach_node() don't |
|
5903 |
* change the event state. |
|
5904 |
*/ |
|
5905 |
flags |= NDI_NO_EVENT_STATE_CHNG; |
|
5906 |
} |
|
5907 |
||
5908 |
if ((flags & (NDI_NO_EVENT | NDI_BRANCH_EVENT_OP)) == 0 && |
|
5909 |
((flags & NDI_CONFIG) || DEVI_NEED_NDI_CONFIG(dip))) { |
|
5910 |
flags |= NDI_BRANCH_EVENT_OP; |
|
5911 |
branch_event = 1; |
|
5912 |
} |
|
5913 |
||
5914 |
/* |
|
5915 |
* devi_attach_node() may remove dip on failure |
|
5916 |
*/ |
|
5917 |
if ((rv = devi_attach_node(dip, flags)) == NDI_SUCCESS) { |
|
5918 |
if ((flags & NDI_CONFIG) || DEVI_NEED_NDI_CONFIG(dip)) { |
|
5919 |
(void) ndi_devi_config(dip, flags); |
|
5920 |
} |
|
5921 |
||
5922 |
if (branch_event) |
|
5923 |
(void) i_log_devfs_branch_add(dip); |
|
5924 |
} |
|
5925 |
||
5926 |
ndi_devi_exit(pdip, circ); |
|
5927 |
||
5928 |
/* |
|
5929 |
* Notify devfs that we have a new node. Devfs needs to invalidate |
|
5930 |
* cached directory contents. |
|
5931 |
* |
|
5932 |
* For PCMCIA devices, it is possible the pdip is not fully |
|
5933 |
* attached. In this case, calling back into devfs will |
|
5934 |
* result in a loop or assertion error. Hence, the check |
|
5935 |
* on node state. |
|
5936 |
* |
|
5937 |
* If we own parent lock, this is part of a branch operation. |
|
5938 |
* We skip the devfs_clean() step because the cache invalidation |
|
5939 |
* is done higher up in the device tree. |
|
5940 |
*/ |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
5941 |
if (rv == NDI_SUCCESS && i_ddi_devi_attached(pdip) && |
0 | 5942 |
!DEVI_BUSY_OWNED(pdip)) |
5943 |
(void) devfs_clean(pdip, NULL, 0); |
|
5944 |
return (rv); |
|
5945 |
} |
|
5946 |
||
5947 |
static void |
|
5948 |
i_ndi_devi_online_cb(struct async_arg *arg) |
|
5949 |
{ |
|
5950 |
(void) ndi_devi_online(arg->dip, arg->flags); |
|
5951 |
kmem_free(arg, sizeof (*arg)); |
|
5952 |
} |
|
5953 |
||
5954 |
int |
|
5955 |
ndi_devi_online_async(dev_info_t *dip, uint_t flags) |
|
5956 |
{ |
|
5957 |
/* mark child as need config if requested. */ |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5958 |
if (flags & NDI_CONFIG) { |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5959 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 5960 |
DEVI_SET_NDI_CONFIG(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5961 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
5962 |
} |
0 | 5963 |
|
5964 |
return (i_ndi_devi_async_common(dip, flags, |
|
5965 |
(void (*)())i_ndi_devi_online_cb)); |
|
5966 |
} |
|
5967 |
||
5968 |
/* |
|
5969 |
* Take a device node Offline |
|
5970 |
* To take a device Offline means to detach the device instance from |
|
5971 |
* the driver and prevent devfs requests from re-attaching the device |
|
5972 |
* instance. |
|
5973 |
* |
|
5974 |
* The flag NDI_DEVI_REMOVE causes removes the device node from |
|
5975 |
* the driver list and the device tree. In this case, the device |
|
5976 |
* is assumed to be removed from the system. |
|
5977 |
*/ |
|
5978 |
int |
|
5979 |
ndi_devi_offline(dev_info_t *dip, uint_t flags) |
|
5980 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5981 |
int circ, rval = 0; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5982 |
dev_info_t *pdip = ddi_get_parent(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5983 |
dev_info_t *vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5984 |
int v_circ; |
0 | 5985 |
struct brevq_node *brevq = NULL; |
5986 |
||
5987 |
ASSERT(pdip); |
|
5988 |
||
5989 |
flags |= NDI_DEVI_OFFLINE; |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5990 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5991 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5992 |
* If child is pHCI and vHCI and pHCI are not siblings then enter vHCI |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5993 |
* before parent(pHCI) to avoid deadlock with mpxio Client power |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5994 |
* management operations. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5995 |
*/ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5996 |
if (MDI_PHCI(dip)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5997 |
vdip = mdi_devi_get_vdip(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5998 |
if (vdip && (ddi_get_parent(vdip) != pdip)) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
5999 |
ndi_devi_enter(vdip, &v_circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6000 |
else |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6001 |
vdip = NULL; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6002 |
} |
0 | 6003 |
ndi_devi_enter(pdip, &circ); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6004 |
|
0 | 6005 |
if (i_ddi_node_state(dip) == DS_READY) { |
6006 |
/* |
|
6007 |
* If dip is in DS_READY state, there may be cached dv_nodes |
|
6008 |
* referencing this dip, so we invoke devfs code path. |
|
6009 |
* Note that we must release busy changing on pdip to |
|
6010 |
* avoid deadlock against devfs. |
|
6011 |
*/ |
|
6012 |
char *devname = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); |
|
6013 |
(void) ddi_deviname(dip, devname); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6014 |
|
0 | 6015 |
ndi_devi_exit(pdip, circ); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6016 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6017 |
ndi_devi_exit(vdip, v_circ); |
0 | 6018 |
|
6019 |
/* |
|
6020 |
* If we own parent lock, this is part of a branch |
|
6021 |
* operation. We skip the devfs_clean() step. |
|
6022 |
*/ |
|
6023 |
if (!DEVI_BUSY_OWNED(pdip)) |
|
4411
d3fb57af2eda
6565553 devfs_clean() should not be used to test for offlinability
vikram
parents:
4145
diff
changeset
|
6024 |
(void) devfs_clean(pdip, devname + 1, DV_CLEAN_FORCE); |
0 | 6025 |
kmem_free(devname, MAXNAMELEN + 1); |
6026 |
||
4411
d3fb57af2eda
6565553 devfs_clean() should not be used to test for offlinability
vikram
parents:
4145
diff
changeset
|
6027 |
rval = devi_unconfig_branch(dip, NULL, flags|NDI_UNCONFIG, |
d3fb57af2eda
6565553 devfs_clean() should not be used to test for offlinability
vikram
parents:
4145
diff
changeset
|
6028 |
&brevq); |
d3fb57af2eda
6565553 devfs_clean() should not be used to test for offlinability
vikram
parents:
4145
diff
changeset
|
6029 |
|
0 | 6030 |
if (rval) |
6031 |
return (NDI_FAILURE); |
|
6032 |
||
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6033 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6034 |
ndi_devi_enter(vdip, &v_circ); |
0 | 6035 |
ndi_devi_enter(pdip, &circ); |
6036 |
} |
|
6037 |
||
6038 |
init_bound_node_ev(pdip, dip, flags); |
|
6039 |
||
6040 |
rval = devi_detach_node(dip, flags); |
|
6041 |
if (brevq) { |
|
6042 |
if (rval != NDI_SUCCESS) |
|
6043 |
log_and_free_brevq_dip(dip, brevq); |
|
6044 |
else |
|
6045 |
free_brevq(brevq); |
|
6046 |
} |
|
6047 |
||
6048 |
ndi_devi_exit(pdip, circ); |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6049 |
if (vdip) |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6050 |
ndi_devi_exit(vdip, v_circ); |
0 | 6051 |
|
6052 |
return (rval); |
|
6053 |
} |
|
6054 |
||
6055 |
/* |
|
6056 |
* Find the child dev_info node of parent nexus 'p' whose name |
|
6057 |
* matches "cname@caddr". Recommend use of ndi_devi_findchild() instead. |
|
6058 |
*/ |
|
6059 |
dev_info_t * |
|
6060 |
ndi_devi_find(dev_info_t *pdip, char *cname, char *caddr) |
|
6061 |
{ |
|
6062 |
dev_info_t *child; |
|
6063 |
int circ; |
|
6064 |
||
6065 |
if (pdip == NULL || cname == NULL || caddr == NULL) |
|
6066 |
return ((dev_info_t *)NULL); |
|
6067 |
||
6068 |
ndi_devi_enter(pdip, &circ); |
|
4145 | 6069 |
child = find_sibling(ddi_get_child(pdip), cname, caddr, |
6070 |
FIND_NODE_BY_NODENAME, NULL); |
|
0 | 6071 |
ndi_devi_exit(pdip, circ); |
6072 |
return (child); |
|
6073 |
} |
|
6074 |
||
6075 |
/* |
|
6076 |
* Find the child dev_info node of parent nexus 'p' whose name |
|
6077 |
* matches devname "name@addr". Permits caller to hold the parent. |
|
6078 |
*/ |
|
6079 |
dev_info_t * |
|
6080 |
ndi_devi_findchild(dev_info_t *pdip, char *devname) |
|
6081 |
{ |
|
6082 |
dev_info_t *child; |
|
6083 |
char *cname, *caddr; |
|
6084 |
char *devstr; |
|
6085 |
||
6086 |
ASSERT(DEVI_BUSY_OWNED(pdip)); |
|
6087 |
||
6088 |
devstr = i_ddi_strdup(devname, KM_SLEEP); |
|
6089 |
i_ddi_parse_name(devstr, &cname, &caddr, NULL); |
|
6090 |
||
6091 |
if (cname == NULL || caddr == NULL) { |
|
6092 |
kmem_free(devstr, strlen(devname)+1); |
|
6093 |
return ((dev_info_t *)NULL); |
|
6094 |
} |
|
6095 |
||
4145 | 6096 |
child = find_sibling(ddi_get_child(pdip), cname, caddr, |
6097 |
FIND_NODE_BY_NODENAME, NULL); |
|
0 | 6098 |
kmem_free(devstr, strlen(devname)+1); |
6099 |
return (child); |
|
6100 |
} |
|
6101 |
||
6102 |
/* |
|
6103 |
* Misc. routines called by framework only |
|
6104 |
*/ |
|
6105 |
||
6106 |
/* |
|
6107 |
* Clear the DEVI_MADE_CHILDREN/DEVI_ATTACHED_CHILDREN flags |
|
6108 |
* if new child spec has been added. |
|
6109 |
*/ |
|
6110 |
static int |
|
6111 |
reset_nexus_flags(dev_info_t *dip, void *arg) |
|
6112 |
{ |
|
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6113 |
struct hwc_spec *list; |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6114 |
int circ; |
0 | 6115 |
|
6116 |
if (((DEVI(dip)->devi_flags & DEVI_MADE_CHILDREN) == 0) || |
|
6117 |
((list = hwc_get_child_spec(dip, (major_t)(uintptr_t)arg)) == NULL)) |
|
6118 |
return (DDI_WALK_CONTINUE); |
|
6119 |
||
6120 |
hwc_free_spec_list(list); |
|
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6121 |
|
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6122 |
/* coordinate child state update */ |
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6123 |
ndi_devi_enter(dip, &circ); |
0 | 6124 |
mutex_enter(&DEVI(dip)->devi_lock); |
6125 |
DEVI(dip)->devi_flags &= ~(DEVI_MADE_CHILDREN | DEVI_ATTACHED_CHILDREN); |
|
6126 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
298
dde238e879a8
6303210 Assertion failure: DEVI(pdip)->devi_flags & DEVI_MADE_CHILDREN
cth
parents:
53
diff
changeset
|
6127 |
ndi_devi_exit(dip, circ); |
0 | 6128 |
|
6129 |
return (DDI_WALK_CONTINUE); |
|
6130 |
} |
|
6131 |
||
6132 |
/* |
|
6133 |
* Helper functions, returns NULL if no memory. |
|
6134 |
*/ |
|
6135 |
||
6136 |
/* |
|
6137 |
* path_to_major: |
|
6138 |
* |
|
6139 |
* Return an alternate driver name binding for the leaf device |
|
6140 |
* of the given pathname, if there is one. The purpose of this |
|
6141 |
* function is to deal with generic pathnames. The default action |
|
6142 |
* for platforms that can't do this (ie: x86 or any platform that |
|
6143 |
* does not have prom_finddevice functionality, which matches |
|
6144 |
* nodenames and unit-addresses without the drivers participation) |
|
6145 |
* is to return (major_t)-1. |
|
6146 |
* |
|
6147 |
* Used in loadrootmodules() in the swapgeneric module to |
|
6148 |
* associate a given pathname with a given leaf driver. |
|
6149 |
* |
|
6150 |
*/ |
|
6151 |
major_t |
|
6152 |
path_to_major(char *path) |
|
6153 |
{ |
|
6154 |
dev_info_t *dip; |
|
6155 |
char *p, *q; |
|
789 | 6156 |
pnode_t nodeid; |
4145 | 6157 |
major_t major; |
6158 |
||
6159 |
/* check for path-oriented alias */ |
|
6160 |
major = ddi_name_to_major(path); |
|
6161 |
if ((major != (major_t)-1) && |
|
6162 |
!(devnamesp[major].dn_flags & DN_DRIVER_REMOVED)) { |
|
6163 |
NDI_CONFIG_DEBUG((CE_NOTE, "path_to_major: %s path bound %s\n", |
|
6164 |
path, ddi_major_to_name(major))); |
|
6165 |
return (major); |
|
6166 |
} |
|
0 | 6167 |
|
6168 |
/* |
|
6169 |
* Get the nodeid of the given pathname, if such a mapping exists. |
|
6170 |
*/ |
|
6171 |
dip = NULL; |
|
6172 |
nodeid = prom_finddevice(path); |
|
6173 |
if (nodeid != OBP_BADNODE) { |
|
6174 |
/* |
|
6175 |
* Find the nodeid in our copy of the device tree and return |
|
6176 |
* whatever name we used to bind this node to a driver. |
|
6177 |
*/ |
|
6178 |
dip = e_ddi_nodeid_to_dip(nodeid); |
|
6179 |
} |
|
6180 |
||
6181 |
if (dip == NULL) { |
|
6182 |
NDI_CONFIG_DEBUG((CE_WARN, |
|
6183 |
"path_to_major: can't bind <%s>\n", path)); |
|
6184 |
return ((major_t)-1); |
|
6185 |
} |
|
6186 |
||
6187 |
/* |
|
6188 |
* If we're bound to something other than the nodename, |
|
6189 |
* note that in the message buffer and system log. |
|
6190 |
*/ |
|
6191 |
p = ddi_binding_name(dip); |
|
6192 |
q = ddi_node_name(dip); |
|
6193 |
if (p && q && (strcmp(p, q) != 0)) |
|
6194 |
NDI_CONFIG_DEBUG((CE_NOTE, "path_to_major: %s bound to %s\n", |
|
6195 |
path, p)); |
|
6196 |
||
4145 | 6197 |
major = ddi_name_to_major(p); |
6198 |
||
6199 |
ndi_rele_devi(dip); /* release e_ddi_nodeid_to_dip hold */ |
|
6200 |
||
6201 |
return (major); |
|
0 | 6202 |
} |
6203 |
||
6204 |
/* |
|
6205 |
* Return the held dip for the specified major and instance, attempting to do |
|
6206 |
* an attach if specified. Return NULL if the devi can't be found or put in |
|
6207 |
* the proper state. The caller must release the hold via ddi_release_devi if |
|
6208 |
* a non-NULL value is returned. |
|
6209 |
* |
|
6210 |
* Some callers expect to be able to perform a hold_devi() while in a context |
|
6211 |
* where using ndi_devi_enter() to ensure the hold might cause deadlock (see |
|
6212 |
* open-from-attach code in consconfig_dacf.c). Such special-case callers |
|
6213 |
* must ensure that an ndi_devi_enter(parent)/ndi_devi_hold() from a safe |
|
6214 |
* context is already active. The hold_devi() implementation must accommodate |
|
6215 |
* these callers. |
|
6216 |
*/ |
|
6217 |
static dev_info_t * |
|
6218 |
hold_devi(major_t major, int instance, int flags) |
|
6219 |
{ |
|
6220 |
struct devnames *dnp; |
|
6221 |
dev_info_t *dip; |
|
6222 |
char *path; |
|
6223 |
||
6224 |
if ((major >= devcnt) || (instance == -1)) |
|
6225 |
return (NULL); |
|
6226 |
||
6227 |
/* try to find the instance in the per driver list */ |
|
6228 |
dnp = &(devnamesp[major]); |
|
6229 |
LOCK_DEV_OPS(&(dnp->dn_lock)); |
|
6230 |
for (dip = dnp->dn_head; dip; |
|
6231 |
dip = (dev_info_t *)DEVI(dip)->devi_next) { |
|
6232 |
/* skip node if instance field is not valid */ |
|
6233 |
if (i_ddi_node_state(dip) < DS_INITIALIZED) |
|
6234 |
continue; |
|
6235 |
||
6236 |
/* look for instance match */ |
|
6237 |
if (DEVI(dip)->devi_instance == instance) { |
|
6238 |
/* |
|
6239 |
* To accommodate callers that can't block in |
|
6240 |
* ndi_devi_enter() we do an ndi_devi_hold(), and |
|
6241 |
* afterwards check that the node is in a state where |
|
6242 |
* the hold prevents detach(). If we did not manage to |
|
6243 |
* prevent detach then we ndi_rele_devi() and perform |
|
6244 |
* the slow path below (which can result in a blocking |
|
6245 |
* ndi_devi_enter() while driving attach top-down). |
|
6246 |
* This code depends on the ordering of |
|
6247 |
* DEVI_SET_DETACHING and the devi_ref check in the |
|
6248 |
* detach_node() code path. |
|
6249 |
*/ |
|
6250 |
ndi_hold_devi(dip); |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
6251 |
if (i_ddi_devi_attached(dip) && |
0 | 6252 |
!DEVI_IS_DETACHING(dip)) { |
6253 |
UNLOCK_DEV_OPS(&(dnp->dn_lock)); |
|
6254 |
return (dip); /* fast-path with devi held */ |
|
6255 |
} |
|
6256 |
ndi_rele_devi(dip); |
|
6257 |
||
6258 |
/* try slow-path */ |
|
6259 |
dip = NULL; |
|
6260 |
break; |
|
6261 |
} |
|
6262 |
} |
|
6263 |
ASSERT(dip == NULL); |
|
6264 |
UNLOCK_DEV_OPS(&(dnp->dn_lock)); |
|
6265 |
||
6266 |
if (flags & E_DDI_HOLD_DEVI_NOATTACH) |
|
6267 |
return (NULL); /* told not to drive attach */ |
|
6268 |
||
6269 |
/* slow-path may block, so it should not occur from interrupt */ |
|
6270 |
ASSERT(!servicing_interrupt()); |
|
6271 |
if (servicing_interrupt()) |
|
6272 |
return (NULL); |
|
6273 |
||
6274 |
/* reconstruct the path and drive attach by path through devfs. */ |
|
6275 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
6276 |
if (e_ddi_majorinstance_to_path(major, instance, path) == 0) |
|
6277 |
dip = e_ddi_hold_devi_by_path(path, flags); |
|
6278 |
kmem_free(path, MAXPATHLEN); |
|
6279 |
return (dip); /* with devi held */ |
|
6280 |
} |
|
6281 |
||
6282 |
/* |
|
6283 |
* The {e_}ddi_hold_devi{_by_{instance|dev|path}} hold the devinfo node |
|
6284 |
* associated with the specified arguments. This hold should be released |
|
6285 |
* by calling ddi_release_devi. |
|
6286 |
* |
|
6287 |
* The E_DDI_HOLD_DEVI_NOATTACH flag argument allows the caller to to specify |
|
6288 |
* a failure return if the node is not already attached. |
|
6289 |
* |
|
6290 |
* NOTE: by the time we make e_ddi_hold_devi public, we should be able to reuse |
|
6291 |
* ddi_hold_devi again. |
|
6292 |
*/ |
|
6293 |
dev_info_t * |
|
6294 |
ddi_hold_devi_by_instance(major_t major, int instance, int flags) |
|
6295 |
{ |
|
6296 |
return (hold_devi(major, instance, flags)); |
|
6297 |
} |
|
6298 |
||
6299 |
dev_info_t * |
|
6300 |
e_ddi_hold_devi_by_dev(dev_t dev, int flags) |
|
6301 |
{ |
|
6302 |
major_t major = getmajor(dev); |
|
6303 |
dev_info_t *dip; |
|
6304 |
struct dev_ops *ops; |
|
6305 |
dev_info_t *ddip = NULL; |
|
6306 |
||
6307 |
dip = hold_devi(major, dev_to_instance(dev), flags); |
|
6308 |
||
6309 |
/* |
|
6310 |
* The rest of this routine is legacy support for drivers that |
|
6311 |
* have broken DDI_INFO_DEVT2INSTANCE implementations but may have |
|
6312 |
* functional DDI_INFO_DEVT2DEVINFO implementations. This code will |
|
6313 |
* diagnose inconsistency and, for maximum compatibility with legacy |
|
6314 |
* drivers, give preference to the drivers DDI_INFO_DEVT2DEVINFO |
|
6315 |
* implementation over the above derived dip based the driver's |
|
6316 |
* DDI_INFO_DEVT2INSTANCE implementation. This legacy support should |
|
6317 |
* be removed when DDI_INFO_DEVT2DEVINFO is deprecated. |
|
6318 |
* |
|
6319 |
* NOTE: The following code has a race condition. DEVT2DEVINFO |
|
6320 |
* returns a dip which is not held. By the time we ref ddip, |
|
6321 |
* it could have been freed. The saving grace is that for |
|
6322 |
* most drivers, the dip returned from hold_devi() is the |
|
6323 |
* same one as the one returned by DEVT2DEVINFO, so we are |
|
6324 |
* safe for drivers with the correct getinfo(9e) impl. |
|
6325 |
*/ |
|
6326 |
if (((ops = ddi_hold_driver(major)) != NULL) && |
|
6327 |
CB_DRV_INSTALLED(ops) && ops->devo_getinfo) { |
|
6328 |
if ((*ops->devo_getinfo)(NULL, DDI_INFO_DEVT2DEVINFO, |
|
6329 |
(void *)dev, (void **)&ddip) != DDI_SUCCESS) |
|
6330 |
ddip = NULL; |
|
6331 |
} |
|
6332 |
||
6333 |
/* give preference to the driver returned DEVT2DEVINFO dip */ |
|
6334 |
if (ddip && (dip != ddip)) { |
|
6335 |
#ifdef DEBUG |
|
6336 |
cmn_err(CE_WARN, "%s: inconsistent getinfo(9E) implementation", |
|
6337 |
ddi_driver_name(ddip)); |
|
6338 |
#endif /* DEBUG */ |
|
6339 |
ndi_hold_devi(ddip); |
|
6340 |
if (dip) |
|
6341 |
ndi_rele_devi(dip); |
|
6342 |
dip = ddip; |
|
6343 |
} |
|
6344 |
||
6345 |
if (ops) |
|
6346 |
ddi_rele_driver(major); |
|
6347 |
||
6348 |
return (dip); |
|
6349 |
} |
|
6350 |
||
6351 |
/* |
|
6352 |
* For compatibility only. Do not call this function! |
|
6353 |
*/ |
|
6354 |
dev_info_t * |
|
6355 |
e_ddi_get_dev_info(dev_t dev, vtype_t type) |
|
6356 |
{ |
|
6357 |
dev_info_t *dip = NULL; |
|
6358 |
if (getmajor(dev) >= devcnt) |
|
6359 |
return (NULL); |
|
6360 |
||
6361 |
switch (type) { |
|
6362 |
case VCHR: |
|
6363 |
case VBLK: |
|
6364 |
dip = e_ddi_hold_devi_by_dev(dev, 0); |
|
6365 |
default: |
|
6366 |
break; |
|
6367 |
} |
|
6368 |
||
6369 |
/* |
|
6370 |
* For compatibility reasons, we can only return the dip with |
|
6371 |
* the driver ref count held. This is not a safe thing to do. |
|
6372 |
* For certain broken third-party software, we are willing |
|
6373 |
* to venture into unknown territory. |
|
6374 |
*/ |
|
6375 |
if (dip) { |
|
6376 |
(void) ndi_hold_driver(dip); |
|
6377 |
ndi_rele_devi(dip); |
|
6378 |
} |
|
6379 |
return (dip); |
|
6380 |
} |
|
6381 |
||
6382 |
dev_info_t * |
|
6383 |
e_ddi_hold_devi_by_path(char *path, int flags) |
|
6384 |
{ |
|
6385 |
dev_info_t *dip; |
|
6386 |
||
6387 |
/* can't specify NOATTACH by path */ |
|
6388 |
ASSERT(!(flags & E_DDI_HOLD_DEVI_NOATTACH)); |
|
6389 |
||
6390 |
return (resolve_pathname(path, &dip, NULL, NULL) ? NULL : dip); |
|
6391 |
} |
|
6392 |
||
6393 |
void |
|
6394 |
e_ddi_hold_devi(dev_info_t *dip) |
|
6395 |
{ |
|
6396 |
ndi_hold_devi(dip); |
|
6397 |
} |
|
6398 |
||
6399 |
void |
|
6400 |
ddi_release_devi(dev_info_t *dip) |
|
6401 |
{ |
|
6402 |
ndi_rele_devi(dip); |
|
6403 |
} |
|
6404 |
||
6405 |
/* |
|
6406 |
* Associate a streams queue with a devinfo node |
|
6407 |
* NOTE: This function is called by STREAM driver's put procedure. |
|
6408 |
* It cannot block. |
|
6409 |
*/ |
|
6410 |
void |
|
6411 |
ddi_assoc_queue_with_devi(queue_t *q, dev_info_t *dip) |
|
6412 |
{ |
|
6413 |
queue_t *rq = _RD(q); |
|
6414 |
struct stdata *stp; |
|
6415 |
vnode_t *vp; |
|
6416 |
||
6417 |
/* set flag indicating that ddi_assoc_queue_with_devi was called */ |
|
6418 |
mutex_enter(QLOCK(rq)); |
|
6419 |
rq->q_flag |= _QASSOCIATED; |
|
6420 |
mutex_exit(QLOCK(rq)); |
|
6421 |
||
6422 |
/* get the vnode associated with the queue */ |
|
6423 |
stp = STREAM(rq); |
|
6424 |
vp = stp->sd_vnode; |
|
6425 |
ASSERT(vp); |
|
6426 |
||
6427 |
/* change the hardware association of the vnode */ |
|
6428 |
spec_assoc_vp_with_devi(vp, dip); |
|
6429 |
} |
|
6430 |
||
6431 |
/* |
|
6432 |
* ddi_install_driver(name) |
|
6433 |
* |
|
6434 |
* Driver installation is currently a byproduct of driver loading. This |
|
6435 |
* may change. |
|
6436 |
*/ |
|
6437 |
int |
|
6438 |
ddi_install_driver(char *name) |
|
6439 |
{ |
|
6440 |
major_t major = ddi_name_to_major(name); |
|
6441 |
||
6442 |
if ((major == (major_t)-1) || |
|
6443 |
(ddi_hold_installed_driver(major) == NULL)) { |
|
6444 |
return (DDI_FAILURE); |
|
6445 |
} |
|
6446 |
ddi_rele_driver(major); |
|
6447 |
return (DDI_SUCCESS); |
|
6448 |
} |
|
6449 |
||
6450 |
struct dev_ops * |
|
6451 |
ddi_hold_driver(major_t major) |
|
6452 |
{ |
|
6453 |
return (mod_hold_dev_by_major(major)); |
|
6454 |
} |
|
6455 |
||
6456 |
||
6457 |
void |
|
6458 |
ddi_rele_driver(major_t major) |
|
6459 |
{ |
|
6460 |
mod_rele_dev_by_major(major); |
|
6461 |
} |
|
6462 |
||
6463 |
||
6464 |
/* |
|
6465 |
* This is called during boot to force attachment order of special dips |
|
6466 |
* dip must be referenced via ndi_hold_devi() |
|
6467 |
*/ |
|
6468 |
int |
|
6469 |
i_ddi_attach_node_hierarchy(dev_info_t *dip) |
|
6470 |
{ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6471 |
dev_info_t *parent; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6472 |
int ret, circ; |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6473 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6474 |
/* |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6475 |
* Recurse up until attached parent is found. |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6476 |
*/ |
2009 | 6477 |
if (i_ddi_devi_attached(dip)) |
6478 |
return (DDI_SUCCESS); |
|
0 | 6479 |
parent = ddi_get_parent(dip); |
6480 |
if (i_ddi_attach_node_hierarchy(parent) != DDI_SUCCESS) |
|
6481 |
return (DDI_FAILURE); |
|
6482 |
||
6483 |
/* |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6484 |
* Come top-down, expanding .conf nodes under this parent |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6485 |
* and driving attach. |
0 | 6486 |
*/ |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6487 |
ndi_devi_enter(parent, &circ); |
0 | 6488 |
(void) i_ndi_make_spec_children(parent, 0); |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6489 |
ret = i_ddi_attachchild(dip); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6490 |
ndi_devi_exit(parent, circ); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6491 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6492 |
return (ret); |
0 | 6493 |
} |
6494 |
||
6495 |
/* keep this function static */ |
|
6496 |
static int |
|
6497 |
attach_driver_nodes(major_t major) |
|
6498 |
{ |
|
6499 |
struct devnames *dnp; |
|
6500 |
dev_info_t *dip; |
|
6501 |
int error = DDI_FAILURE; |
|
6502 |
||
6503 |
dnp = &devnamesp[major]; |
|
6504 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
6505 |
dip = dnp->dn_head; |
|
6506 |
while (dip) { |
|
6507 |
ndi_hold_devi(dip); |
|
6508 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
6509 |
if (i_ddi_attach_node_hierarchy(dip) == DDI_SUCCESS) |
|
6510 |
error = DDI_SUCCESS; |
|
6511 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
6512 |
ndi_rele_devi(dip); |
|
6513 |
dip = ddi_get_next(dip); |
|
6514 |
} |
|
6515 |
if (error == DDI_SUCCESS) |
|
6516 |
dnp->dn_flags |= DN_NO_AUTODETACH; |
|
6517 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
6518 |
||
6519 |
||
6520 |
return (error); |
|
6521 |
} |
|
6522 |
||
6523 |
/* |
|
6524 |
* i_ddi_attach_hw_nodes configures and attaches all hw nodes |
|
6525 |
* bound to a specific driver. This function replaces calls to |
|
6526 |
* ddi_hold_installed_driver() for drivers with no .conf |
|
6527 |
* enumerated nodes. |
|
6528 |
* |
|
6529 |
* This facility is typically called at boot time to attach |
|
6530 |
* platform-specific hardware nodes, such as ppm nodes on xcal |
|
6531 |
* and grover and keyswitch nodes on cherrystone. It does not |
|
6532 |
* deal with .conf enumerated node. Calling it beyond the boot |
|
6533 |
* process is strongly discouraged. |
|
6534 |
*/ |
|
6535 |
int |
|
6536 |
i_ddi_attach_hw_nodes(char *driver) |
|
6537 |
{ |
|
6538 |
major_t major; |
|
6539 |
||
6540 |
major = ddi_name_to_major(driver); |
|
6541 |
if (major == (major_t)-1) |
|
6542 |
return (DDI_FAILURE); |
|
6543 |
||
6544 |
return (attach_driver_nodes(major)); |
|
6545 |
} |
|
6546 |
||
6547 |
/* |
|
6548 |
* i_ddi_attach_pseudo_node configures pseudo drivers which |
|
6549 |
* has a single node. The .conf nodes must be enumerated |
|
6550 |
* before calling this interface. The dip is held attached |
|
6551 |
* upon returning. |
|
6552 |
* |
|
6553 |
* This facility should only be called only at boot time |
|
6554 |
* by the I/O framework. |
|
6555 |
*/ |
|
6556 |
dev_info_t * |
|
6557 |
i_ddi_attach_pseudo_node(char *driver) |
|
6558 |
{ |
|
6559 |
major_t major; |
|
6560 |
dev_info_t *dip; |
|
6561 |
||
6562 |
major = ddi_name_to_major(driver); |
|
6563 |
if (major == (major_t)-1) |
|
6564 |
return (NULL); |
|
6565 |
||
6566 |
if (attach_driver_nodes(major) != DDI_SUCCESS) |
|
6567 |
return (NULL); |
|
6568 |
||
6569 |
dip = devnamesp[major].dn_head; |
|
6570 |
ASSERT(dip && ddi_get_next(dip) == NULL); |
|
6571 |
ndi_hold_devi(dip); |
|
6572 |
return (dip); |
|
6573 |
} |
|
6574 |
||
6575 |
static void |
|
6576 |
diplist_to_parent_major(dev_info_t *head, char parents[]) |
|
6577 |
{ |
|
6578 |
major_t major; |
|
6579 |
dev_info_t *dip, *pdip; |
|
6580 |
||
6581 |
for (dip = head; dip != NULL; dip = ddi_get_next(dip)) { |
|
6582 |
pdip = ddi_get_parent(dip); |
|
6583 |
ASSERT(pdip); /* disallow rootnex.conf nodes */ |
|
6584 |
major = ddi_driver_major(pdip); |
|
6585 |
if ((major != (major_t)-1) && parents[major] == 0) |
|
6586 |
parents[major] = 1; |
|
6587 |
} |
|
6588 |
} |
|
6589 |
||
6590 |
/* |
|
6591 |
* Call ddi_hold_installed_driver() on each parent major |
|
6592 |
* and invoke mt_config_driver() to attach child major. |
|
6593 |
* This is part of the implementation of ddi_hold_installed_driver. |
|
6594 |
*/ |
|
6595 |
static int |
|
6596 |
attach_driver_by_parent(major_t child_major, char parents[]) |
|
6597 |
{ |
|
6598 |
major_t par_major; |
|
6599 |
struct mt_config_handle *hdl; |
|
6600 |
int flags = NDI_DEVI_PERSIST | NDI_NO_EVENT; |
|
6601 |
||
6602 |
hdl = mt_config_init(NULL, NULL, flags, child_major, MT_CONFIG_OP, |
|
6603 |
NULL); |
|
6604 |
for (par_major = 0; par_major < devcnt; par_major++) { |
|
6605 |
/* disallow recursion on the same driver */ |
|
6606 |
if (parents[par_major] == 0 || par_major == child_major) |
|
6607 |
continue; |
|
6608 |
if (ddi_hold_installed_driver(par_major) == NULL) |
|
6609 |
continue; |
|
6610 |
hdl->mtc_parmajor = par_major; |
|
6611 |
mt_config_driver(hdl); |
|
6612 |
ddi_rele_driver(par_major); |
|
6613 |
} |
|
6614 |
(void) mt_config_fini(hdl); |
|
6615 |
||
6616 |
return (i_ddi_devs_attached(child_major)); |
|
6617 |
} |
|
6618 |
||
6619 |
int |
|
6620 |
i_ddi_devs_attached(major_t major) |
|
6621 |
{ |
|
6622 |
dev_info_t *dip; |
|
6623 |
struct devnames *dnp; |
|
6624 |
int error = DDI_FAILURE; |
|
6625 |
||
6626 |
/* check for attached instances */ |
|
6627 |
dnp = &devnamesp[major]; |
|
6628 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
6629 |
for (dip = dnp->dn_head; dip != NULL; dip = ddi_get_next(dip)) { |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
6630 |
if (i_ddi_devi_attached(dip)) { |
0 | 6631 |
error = DDI_SUCCESS; |
6632 |
break; |
|
6633 |
} |
|
6634 |
} |
|
6635 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
6636 |
||
6637 |
return (error); |
|
6638 |
} |
|
6639 |
||
5895
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6640 |
int |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6641 |
i_ddi_minor_node_count(dev_info_t *ddip, const char *node_type) |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6642 |
{ |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6643 |
struct ddi_minor_data *dp; |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6644 |
int count = 0; |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6645 |
|
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6646 |
mutex_enter(&(DEVI(ddip)->devi_lock)); |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6647 |
for (dp = DEVI(ddip)->devi_minor; dp != NULL; dp = dp->next) |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6648 |
if (strcmp(dp->ddm_node_type, node_type) == 0) |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6649 |
count++; |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6650 |
mutex_exit(&(DEVI(ddip)->devi_lock)); |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6651 |
return (count); |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6652 |
} |
f251acdd9bdc
PSARC/2006/499 Clearview Nemo unification and vanity naming
yz147064
parents:
5742
diff
changeset
|
6653 |
|
0 | 6654 |
/* |
6655 |
* ddi_hold_installed_driver configures and attaches all |
|
6656 |
* instances of the specified driver. To accomplish this |
|
6657 |
* it configures and attaches all possible parents of |
|
6658 |
* the driver, enumerated both in h/w nodes and in the |
|
6659 |
* driver's .conf file. |
|
6660 |
* |
|
6661 |
* NOTE: This facility is for compatibility purposes only and will |
|
6662 |
* eventually go away. Its usage is strongly discouraged. |
|
6663 |
*/ |
|
6664 |
static void |
|
6665 |
enter_driver(struct devnames *dnp) |
|
6666 |
{ |
|
6667 |
mutex_enter(&dnp->dn_lock); |
|
6668 |
ASSERT(dnp->dn_busy_thread != curthread); |
|
6669 |
while (dnp->dn_flags & DN_DRIVER_BUSY) |
|
6670 |
cv_wait(&dnp->dn_wait, &dnp->dn_lock); |
|
6671 |
dnp->dn_flags |= DN_DRIVER_BUSY; |
|
6672 |
dnp->dn_busy_thread = curthread; |
|
6673 |
mutex_exit(&dnp->dn_lock); |
|
6674 |
} |
|
6675 |
||
6676 |
static void |
|
6677 |
exit_driver(struct devnames *dnp) |
|
6678 |
{ |
|
6679 |
mutex_enter(&dnp->dn_lock); |
|
6680 |
ASSERT(dnp->dn_busy_thread == curthread); |
|
6681 |
dnp->dn_flags &= ~DN_DRIVER_BUSY; |
|
6682 |
dnp->dn_busy_thread = NULL; |
|
6683 |
cv_broadcast(&dnp->dn_wait); |
|
6684 |
mutex_exit(&dnp->dn_lock); |
|
6685 |
} |
|
6686 |
||
6687 |
struct dev_ops * |
|
6688 |
ddi_hold_installed_driver(major_t major) |
|
6689 |
{ |
|
6690 |
struct dev_ops *ops; |
|
6691 |
struct devnames *dnp; |
|
6692 |
char *parents; |
|
6693 |
int error; |
|
6694 |
||
6695 |
ops = ddi_hold_driver(major); |
|
6696 |
if (ops == NULL) |
|
6697 |
return (NULL); |
|
6698 |
||
6699 |
/* |
|
6700 |
* Return immediately if all the attach operations associated |
|
6701 |
* with a ddi_hold_installed_driver() call have already been done. |
|
6702 |
*/ |
|
6703 |
dnp = &devnamesp[major]; |
|
6704 |
enter_driver(dnp); |
|
6705 |
if (dnp->dn_flags & DN_DRIVER_HELD) { |
|
6706 |
exit_driver(dnp); |
|
6707 |
if (i_ddi_devs_attached(major) == DDI_SUCCESS) |
|
6708 |
return (ops); |
|
6709 |
ddi_rele_driver(major); |
|
6710 |
return (NULL); |
|
6711 |
} |
|
6712 |
||
6713 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
6714 |
dnp->dn_flags |= (DN_DRIVER_HELD | DN_NO_AUTODETACH); |
|
6715 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
6716 |
||
6717 |
DCOMPATPRINTF((CE_CONT, |
|
6718 |
"ddi_hold_installed_driver: %s\n", dnp->dn_name)); |
|
6719 |
||
6720 |
/* |
|
6721 |
* When the driver has no .conf children, it is sufficient |
|
6722 |
* to attach existing nodes in the device tree. Nodes not |
|
6723 |
* enumerated by the OBP are not attached. |
|
6724 |
*/ |
|
6725 |
if (dnp->dn_pl == NULL) { |
|
6726 |
if (attach_driver_nodes(major) == DDI_SUCCESS) { |
|
6727 |
exit_driver(dnp); |
|
6728 |
return (ops); |
|
6729 |
} |
|
6730 |
exit_driver(dnp); |
|
6731 |
ddi_rele_driver(major); |
|
6732 |
return (NULL); |
|
6733 |
} |
|
6734 |
||
6735 |
/* |
|
6736 |
* Driver has .conf nodes. We find all possible parents |
|
6737 |
* and recursively all ddi_hold_installed_driver on the |
|
6738 |
* parent driver; then we invoke ndi_config_driver() |
|
6739 |
* on all possible parent node in parallel to speed up |
|
6740 |
* performance. |
|
6741 |
*/ |
|
6742 |
parents = kmem_zalloc(devcnt * sizeof (char), KM_SLEEP); |
|
6743 |
||
6744 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
6745 |
/* find .conf parents */ |
|
6746 |
(void) impl_parlist_to_major(dnp->dn_pl, parents); |
|
6747 |
/* find hw node parents */ |
|
6748 |
diplist_to_parent_major(dnp->dn_head, parents); |
|
6749 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
6750 |
||
6751 |
error = attach_driver_by_parent(major, parents); |
|
6752 |
kmem_free(parents, devcnt * sizeof (char)); |
|
6753 |
if (error == DDI_SUCCESS) { |
|
6754 |
exit_driver(dnp); |
|
6755 |
return (ops); |
|
6756 |
} |
|
6757 |
||
6758 |
exit_driver(dnp); |
|
6759 |
ddi_rele_driver(major); |
|
6760 |
return (NULL); |
|
6761 |
} |
|
6762 |
||
6763 |
/* |
|
6764 |
* Default bus_config entry point for nexus drivers |
|
6765 |
*/ |
|
6766 |
int |
|
6767 |
ndi_busop_bus_config(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op, |
|
6768 |
void *arg, dev_info_t **child, clock_t timeout) |
|
6769 |
{ |
|
6770 |
major_t major; |
|
6771 |
||
6772 |
/* |
|
6773 |
* A timeout of 30 minutes or more is probably a mistake |
|
6774 |
* This is intended to catch uses where timeout is in |
|
6775 |
* the wrong units. timeout must be in units of ticks. |
|
6776 |
*/ |
|
6777 |
ASSERT(timeout < SEC_TO_TICK(1800)); |
|
6778 |
||
6779 |
major = (major_t)-1; |
|
6780 |
switch (op) { |
|
6781 |
case BUS_CONFIG_ONE: |
|
6782 |
NDI_DEBUG(flags, (CE_CONT, "%s%d: bus config %s timeout=%ld\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6783 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6784 |
(char *)arg, timeout)); |
0 | 6785 |
return (devi_config_one(pdip, (char *)arg, child, flags, |
6786 |
timeout)); |
|
6787 |
||
6788 |
case BUS_CONFIG_DRIVER: |
|
6789 |
major = (major_t)(uintptr_t)arg; |
|
6790 |
/*FALLTHROUGH*/ |
|
6791 |
case BUS_CONFIG_ALL: |
|
6792 |
NDI_DEBUG(flags, (CE_CONT, "%s%d: bus config timeout=%ld\n", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6793 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6794 |
timeout)); |
0 | 6795 |
if (timeout > 0) { |
6796 |
NDI_DEBUG(flags, (CE_CONT, |
|
6797 |
"%s%d: bus config all timeout=%ld\n", |
|
6798 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
|
6799 |
timeout)); |
|
6800 |
delay(timeout); |
|
6801 |
} |
|
6802 |
return (config_immediate_children(pdip, flags, major)); |
|
6803 |
||
6804 |
default: |
|
6805 |
return (NDI_FAILURE); |
|
6806 |
} |
|
6807 |
/*NOTREACHED*/ |
|
6808 |
} |
|
6809 |
||
6810 |
/* |
|
6811 |
* Default busop bus_unconfig handler for nexus drivers |
|
6812 |
*/ |
|
6813 |
int |
|
6814 |
ndi_busop_bus_unconfig(dev_info_t *pdip, uint_t flags, ddi_bus_config_op_t op, |
|
6815 |
void *arg) |
|
6816 |
{ |
|
6817 |
major_t major; |
|
6818 |
||
6819 |
major = (major_t)-1; |
|
6820 |
switch (op) { |
|
6821 |
case BUS_UNCONFIG_ONE: |
|
6822 |
NDI_DEBUG(flags, (CE_CONT, "%s%d: bus unconfig %s\n", |
|
6823 |
ddi_driver_name(pdip), ddi_get_instance(pdip), |
|
6824 |
(char *)arg)); |
|
6825 |
return (devi_unconfig_one(pdip, (char *)arg, flags)); |
|
6826 |
||
6827 |
case BUS_UNCONFIG_DRIVER: |
|
6828 |
major = (major_t)(uintptr_t)arg; |
|
6829 |
/*FALLTHROUGH*/ |
|
6830 |
case BUS_UNCONFIG_ALL: |
|
6831 |
NDI_DEBUG(flags, (CE_CONT, "%s%d: bus unconfig all\n", |
|
6832 |
ddi_driver_name(pdip), ddi_get_instance(pdip))); |
|
6833 |
return (unconfig_immediate_children(pdip, NULL, flags, major)); |
|
6834 |
||
6835 |
default: |
|
6836 |
return (NDI_FAILURE); |
|
6837 |
} |
|
6838 |
/*NOTREACHED*/ |
|
6839 |
} |
|
6840 |
||
6841 |
/* |
|
6842 |
* dummy functions to be removed |
|
6843 |
*/ |
|
6844 |
void |
|
6845 |
impl_rem_dev_props(dev_info_t *dip) |
|
6846 |
{ |
|
6847 |
_NOTE(ARGUNUSED(dip)) |
|
6848 |
/* do nothing */ |
|
6849 |
} |
|
6850 |
||
6851 |
/* |
|
6852 |
* Determine if a node is a leaf node. If not sure, return false (0). |
|
6853 |
*/ |
|
6854 |
static int |
|
6855 |
is_leaf_node(dev_info_t *dip) |
|
6856 |
{ |
|
6857 |
major_t major = ddi_driver_major(dip); |
|
6858 |
||
6859 |
if (major == (major_t)-1) |
|
6860 |
return (0); |
|
6861 |
||
6862 |
return (devnamesp[major].dn_flags & DN_LEAF_DRIVER); |
|
6863 |
} |
|
6864 |
||
6865 |
/* |
|
6866 |
* Multithreaded [un]configuration |
|
6867 |
*/ |
|
6868 |
static struct mt_config_handle * |
|
6869 |
mt_config_init(dev_info_t *pdip, dev_info_t **dipp, int flags, |
|
6870 |
major_t major, int op, struct brevq_node **brevqp) |
|
6871 |
{ |
|
6872 |
struct mt_config_handle *hdl = kmem_alloc(sizeof (*hdl), KM_SLEEP); |
|
6873 |
||
6874 |
mutex_init(&hdl->mtc_lock, NULL, MUTEX_DEFAULT, NULL); |
|
6875 |
cv_init(&hdl->mtc_cv, NULL, CV_DEFAULT, NULL); |
|
6876 |
hdl->mtc_pdip = pdip; |
|
6877 |
hdl->mtc_fdip = dipp; |
|
6878 |
hdl->mtc_parmajor = (major_t)-1; |
|
6879 |
hdl->mtc_flags = flags; |
|
6880 |
hdl->mtc_major = major; |
|
6881 |
hdl->mtc_thr_count = 0; |
|
6882 |
hdl->mtc_op = op; |
|
6883 |
hdl->mtc_error = 0; |
|
6884 |
hdl->mtc_brevqp = brevqp; |
|
6885 |
||
6886 |
#ifdef DEBUG |
|
6887 |
gethrestime(&hdl->start_time); |
|
6888 |
hdl->total_time = 0; |
|
6889 |
#endif /* DEBUG */ |
|
6890 |
||
6891 |
return (hdl); |
|
6892 |
} |
|
6893 |
||
6894 |
#ifdef DEBUG |
|
6895 |
static int |
|
6896 |
time_diff_in_msec(timestruc_t start, timestruc_t end) |
|
6897 |
{ |
|
6898 |
int nsec, sec; |
|
6899 |
||
6900 |
sec = end.tv_sec - start.tv_sec; |
|
6901 |
nsec = end.tv_nsec - start.tv_nsec; |
|
6902 |
if (nsec < 0) { |
|
6903 |
nsec += NANOSEC; |
|
6904 |
sec -= 1; |
|
6905 |
} |
|
6906 |
||
6907 |
return (sec * (NANOSEC >> 20) + (nsec >> 20)); |
|
6908 |
} |
|
6909 |
||
6910 |
#endif /* DEBUG */ |
|
6911 |
||
6912 |
static int |
|
6913 |
mt_config_fini(struct mt_config_handle *hdl) |
|
6914 |
{ |
|
6915 |
int rv; |
|
6916 |
#ifdef DEBUG |
|
6917 |
int real_time; |
|
6918 |
timestruc_t end_time; |
|
6919 |
#endif /* DEBUG */ |
|
6920 |
||
6921 |
mutex_enter(&hdl->mtc_lock); |
|
6922 |
while (hdl->mtc_thr_count > 0) |
|
6923 |
cv_wait(&hdl->mtc_cv, &hdl->mtc_lock); |
|
6924 |
rv = hdl->mtc_error; |
|
6925 |
mutex_exit(&hdl->mtc_lock); |
|
6926 |
||
6927 |
#ifdef DEBUG |
|
6928 |
gethrestime(&end_time); |
|
6929 |
real_time = time_diff_in_msec(hdl->start_time, end_time); |
|
6930 |
if ((ddidebug & DDI_MTCONFIG) && hdl->mtc_pdip) |
|
6931 |
cmn_err(CE_NOTE, |
|
6932 |
"config %s%d: total time %d msec, real time %d msec", |
|
4950
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6933 |
ddi_driver_name(hdl->mtc_pdip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6934 |
ddi_get_instance(hdl->mtc_pdip), |
a7a707716fea
6594969 path oriented rebind code needs to protect nexus parent from detach
cth
parents:
4845
diff
changeset
|
6935 |
hdl->total_time, real_time); |
0 | 6936 |
#endif /* DEBUG */ |
6937 |
||
6938 |
cv_destroy(&hdl->mtc_cv); |
|
6939 |
mutex_destroy(&hdl->mtc_lock); |
|
6940 |
kmem_free(hdl, sizeof (*hdl)); |
|
6941 |
||
6942 |
return (rv); |
|
6943 |
} |
|
6944 |
||
6945 |
struct mt_config_data { |
|
6946 |
struct mt_config_handle *mtc_hdl; |
|
6947 |
dev_info_t *mtc_dip; |
|
6948 |
major_t mtc_major; |
|
6949 |
int mtc_flags; |
|
6950 |
struct brevq_node *mtc_brn; |
|
6951 |
struct mt_config_data *mtc_next; |
|
6952 |
}; |
|
6953 |
||
6954 |
static void |
|
6955 |
mt_config_thread(void *arg) |
|
6956 |
{ |
|
6957 |
struct mt_config_data *mcd = (struct mt_config_data *)arg; |
|
6958 |
struct mt_config_handle *hdl = mcd->mtc_hdl; |
|
6959 |
dev_info_t *dip = mcd->mtc_dip; |
|
6960 |
dev_info_t *rdip, **dipp; |
|
6961 |
major_t major = mcd->mtc_major; |
|
6962 |
int flags = mcd->mtc_flags; |
|
6963 |
int rv = 0; |
|
6964 |
||
6965 |
#ifdef DEBUG |
|
6966 |
timestruc_t start_time, end_time; |
|
6967 |
gethrestime(&start_time); |
|
6968 |
#endif /* DEBUG */ |
|
6969 |
||
6970 |
rdip = NULL; |
|
6971 |
dipp = hdl->mtc_fdip ? &rdip : NULL; |
|
6972 |
||
6973 |
switch (hdl->mtc_op) { |
|
6974 |
case MT_CONFIG_OP: |
|
6975 |
rv = devi_config_common(dip, flags, major); |
|
6976 |
break; |
|
6977 |
case MT_UNCONFIG_OP: |
|
6978 |
if (mcd->mtc_brn) { |
|
6979 |
struct brevq_node *brevq = NULL; |
|
6980 |
rv = devi_unconfig_common(dip, dipp, flags, major, |
|
6981 |
&brevq); |
|
1317 | 6982 |
mcd->mtc_brn->brn_child = brevq; |
0 | 6983 |
} else |
6984 |
rv = devi_unconfig_common(dip, dipp, flags, major, |
|
6985 |
NULL); |
|
6986 |
break; |
|
6987 |
} |
|
6988 |
||
6989 |
mutex_enter(&hdl->mtc_lock); |
|
6990 |
#ifdef DEBUG |
|
6991 |
gethrestime(&end_time); |
|
6992 |
hdl->total_time += time_diff_in_msec(start_time, end_time); |
|
6993 |
#endif /* DEBUG */ |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6994 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6995 |
if ((rv != NDI_SUCCESS) && (hdl->mtc_error == 0)) { |
0 | 6996 |
hdl->mtc_error = rv; |
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6997 |
#ifdef DEBUG |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6998 |
if ((ddidebug & DDI_DEBUG) && (major != (major_t)-1)) { |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
6999 |
char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7000 |
|
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7001 |
(void) ddi_pathname(dip, path); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7002 |
cmn_err(CE_NOTE, "mt_config_thread: " |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7003 |
"op %d.%d.%x at %s failed %d", |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7004 |
hdl->mtc_op, major, flags, path, rv); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7005 |
kmem_free(path, MAXPATHLEN); |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7006 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7007 |
#endif /* DEBUG */ |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7008 |
} |
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7009 |
|
0 | 7010 |
if (hdl->mtc_fdip && *hdl->mtc_fdip == NULL) { |
7011 |
*hdl->mtc_fdip = rdip; |
|
7012 |
rdip = NULL; |
|
7013 |
} |
|
7014 |
||
7015 |
if (rdip) { |
|
7016 |
ASSERT(rv != NDI_SUCCESS); |
|
7017 |
ndi_rele_devi(rdip); |
|
7018 |
} |
|
7019 |
||
7020 |
ndi_rele_devi(dip); |
|
1826
8b76ba84edbf
6405619 mt_config_thread() says it is done before it really is
bm42561
parents:
1333
diff
changeset
|
7021 |
|
8b76ba84edbf
6405619 mt_config_thread() says it is done before it really is
bm42561
parents:
1333
diff
changeset
|
7022 |
if (--hdl->mtc_thr_count == 0) |
8b76ba84edbf
6405619 mt_config_thread() says it is done before it really is
bm42561
parents:
1333
diff
changeset
|
7023 |
cv_broadcast(&hdl->mtc_cv); |
8b76ba84edbf
6405619 mt_config_thread() says it is done before it really is
bm42561
parents:
1333
diff
changeset
|
7024 |
mutex_exit(&hdl->mtc_lock); |
0 | 7025 |
kmem_free(mcd, sizeof (*mcd)); |
7026 |
} |
|
7027 |
||
7028 |
/* |
|
7029 |
* Multi-threaded config/unconfig of child nexus |
|
7030 |
*/ |
|
7031 |
static void |
|
7032 |
mt_config_children(struct mt_config_handle *hdl) |
|
7033 |
{ |
|
7034 |
dev_info_t *pdip = hdl->mtc_pdip; |
|
7035 |
major_t major = hdl->mtc_major; |
|
7036 |
dev_info_t *dip; |
|
7037 |
int circ; |
|
1317 | 7038 |
struct brevq_node *brn; |
0 | 7039 |
struct mt_config_data *mcd_head = NULL; |
7040 |
struct mt_config_data *mcd_tail = NULL; |
|
7041 |
struct mt_config_data *mcd; |
|
7042 |
#ifdef DEBUG |
|
7043 |
timestruc_t end_time; |
|
7044 |
||
7045 |
/* Update total_time in handle */ |
|
7046 |
gethrestime(&end_time); |
|
7047 |
hdl->total_time += time_diff_in_msec(hdl->start_time, end_time); |
|
7048 |
#endif |
|
7049 |
||
7050 |
ndi_devi_enter(pdip, &circ); |
|
7051 |
dip = ddi_get_child(pdip); |
|
7052 |
while (dip) { |
|
7053 |
if (hdl->mtc_op == MT_UNCONFIG_OP && hdl->mtc_brevqp && |
|
7054 |
!(DEVI_EVREMOVE(dip)) && |
|
7055 |
i_ddi_node_state(dip) >= DS_INITIALIZED) { |
|
7056 |
/* |
|
7057 |
* Enqueue this dip's deviname. |
|
7058 |
* No need to hold a lock while enqueuing since this |
|
7059 |
* is the only thread doing the enqueue and no one |
|
7060 |
* walks the queue while we are in multithreaded |
|
7061 |
* unconfiguration. |
|
7062 |
*/ |
|
7063 |
brn = brevq_enqueue(hdl->mtc_brevqp, dip, NULL); |
|
1317 | 7064 |
} else |
7065 |
brn = NULL; |
|
0 | 7066 |
|
7067 |
/* |
|
7068 |
* Hold the child that we are processing so he does not get |
|
7069 |
* removed. The corrisponding ndi_rele_devi() for children |
|
7070 |
* that are not being skipped is done at the end of |
|
7071 |
* mt_config_thread(). |
|
7072 |
*/ |
|
7073 |
ndi_hold_devi(dip); |
|
7074 |
||
7075 |
/* |
|
7076 |
* skip leaf nodes and (for configure) nodes not |
|
7077 |
* fully attached. |
|
7078 |
*/ |
|
7079 |
if (is_leaf_node(dip) || |
|
7080 |
(hdl->mtc_op == MT_CONFIG_OP && |
|
7081 |
i_ddi_node_state(dip) < DS_READY)) { |
|
7082 |
ndi_rele_devi(dip); |
|
7083 |
dip = ddi_get_next_sibling(dip); |
|
7084 |
continue; |
|
7085 |
} |
|
7086 |
||
7087 |
mcd = kmem_alloc(sizeof (*mcd), KM_SLEEP); |
|
7088 |
mcd->mtc_dip = dip; |
|
7089 |
mcd->mtc_hdl = hdl; |
|
7090 |
mcd->mtc_brn = brn; |
|
7091 |
||
7092 |
/* |
|
7093 |
* Switch a 'driver' operation to an 'all' operation below a |
|
7094 |
* node bound to the driver. |
|
7095 |
*/ |
|
662
524269c34bb5
6295108 When installing to second disk, grub defaults to hd0 instead of hd1
szhou
parents:
495
diff
changeset
|
7096 |
if ((major == (major_t)-1) || (major == ddi_driver_major(dip))) |
0 | 7097 |
mcd->mtc_major = (major_t)-1; |
7098 |
else |
|
7099 |
mcd->mtc_major = major; |
|
7100 |
||
7101 |
/* |
|
7102 |
* The unconfig-driver to unconfig-all conversion above |
|
7103 |
* constitutes an autodetach for NDI_DETACH_DRIVER calls, |
|
7104 |
* set NDI_AUTODETACH. |
|
7105 |
*/ |
|
7106 |
mcd->mtc_flags = hdl->mtc_flags; |
|
7107 |
if ((mcd->mtc_flags & NDI_DETACH_DRIVER) && |
|
7108 |
(hdl->mtc_op == MT_UNCONFIG_OP) && |
|
7109 |
(major == ddi_driver_major(pdip))) |
|
7110 |
mcd->mtc_flags |= NDI_AUTODETACH; |
|
7111 |
||
7112 |
mutex_enter(&hdl->mtc_lock); |
|
7113 |
hdl->mtc_thr_count++; |
|
7114 |
mutex_exit(&hdl->mtc_lock); |
|
7115 |
||
7116 |
/* |
|
7117 |
* Add to end of list to process after ndi_devi_exit to avoid |
|
7118 |
* locking differences depending on value of mtc_off. |
|
7119 |
*/ |
|
7120 |
mcd->mtc_next = NULL; |
|
7121 |
if (mcd_head == NULL) |
|
7122 |
mcd_head = mcd; |
|
7123 |
else |
|
7124 |
mcd_tail->mtc_next = mcd; |
|
7125 |
mcd_tail = mcd; |
|
7126 |
||
7127 |
dip = ddi_get_next_sibling(dip); |
|
7128 |
} |
|
7129 |
ndi_devi_exit(pdip, circ); |
|
7130 |
||
7131 |
/* go through the list of held children */ |
|
7132 |
for (mcd = mcd_head; mcd; mcd = mcd_head) { |
|
7133 |
mcd_head = mcd->mtc_next; |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7134 |
if (mtc_off || (mcd->mtc_flags & NDI_MTC_OFF)) |
0 | 7135 |
mt_config_thread(mcd); |
7136 |
else |
|
7137 |
(void) thread_create(NULL, 0, mt_config_thread, mcd, |
|
7138 |
0, &p0, TS_RUN, minclsyspri); |
|
7139 |
} |
|
7140 |
} |
|
7141 |
||
7142 |
static void |
|
7143 |
mt_config_driver(struct mt_config_handle *hdl) |
|
7144 |
{ |
|
7145 |
major_t par_major = hdl->mtc_parmajor; |
|
7146 |
major_t major = hdl->mtc_major; |
|
7147 |
struct devnames *dnp = &devnamesp[par_major]; |
|
7148 |
dev_info_t *dip; |
|
7149 |
struct mt_config_data *mcd_head = NULL; |
|
7150 |
struct mt_config_data *mcd_tail = NULL; |
|
7151 |
struct mt_config_data *mcd; |
|
7152 |
#ifdef DEBUG |
|
7153 |
timestruc_t end_time; |
|
7154 |
||
7155 |
/* Update total_time in handle */ |
|
7156 |
gethrestime(&end_time); |
|
7157 |
hdl->total_time += time_diff_in_msec(hdl->start_time, end_time); |
|
7158 |
#endif |
|
7159 |
ASSERT(par_major != (major_t)-1); |
|
7160 |
ASSERT(major != (major_t)-1); |
|
7161 |
||
7162 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
7163 |
dip = devnamesp[par_major].dn_head; |
|
7164 |
while (dip) { |
|
7165 |
/* |
|
7166 |
* Hold the child that we are processing so he does not get |
|
7167 |
* removed. The corrisponding ndi_rele_devi() for children |
|
7168 |
* that are not being skipped is done at the end of |
|
7169 |
* mt_config_thread(). |
|
7170 |
*/ |
|
7171 |
ndi_hold_devi(dip); |
|
7172 |
||
7173 |
/* skip leaf nodes and nodes not fully attached */ |
|
1333
9eb72fd56702
6369724 DS_READY references should be checked for correctness
cth
parents:
1317
diff
changeset
|
7174 |
if (!i_ddi_devi_attached(dip) || is_leaf_node(dip)) { |
0 | 7175 |
ndi_rele_devi(dip); |
7176 |
dip = ddi_get_next(dip); |
|
7177 |
continue; |
|
7178 |
} |
|
7179 |
||
7180 |
mcd = kmem_alloc(sizeof (*mcd), KM_SLEEP); |
|
7181 |
mcd->mtc_dip = dip; |
|
7182 |
mcd->mtc_hdl = hdl; |
|
7183 |
mcd->mtc_major = major; |
|
7184 |
mcd->mtc_flags = hdl->mtc_flags; |
|
7185 |
||
7186 |
mutex_enter(&hdl->mtc_lock); |
|
7187 |
hdl->mtc_thr_count++; |
|
7188 |
mutex_exit(&hdl->mtc_lock); |
|
7189 |
||
7190 |
/* |
|
7191 |
* Add to end of list to process after UNLOCK_DEV_OPS to avoid |
|
7192 |
* locking differences depending on value of mtc_off. |
|
7193 |
*/ |
|
7194 |
mcd->mtc_next = NULL; |
|
7195 |
if (mcd_head == NULL) |
|
7196 |
mcd_head = mcd; |
|
7197 |
else |
|
7198 |
mcd_tail->mtc_next = mcd; |
|
7199 |
mcd_tail = mcd; |
|
7200 |
||
7201 |
dip = ddi_get_next(dip); |
|
7202 |
} |
|
7203 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
7204 |
||
7205 |
/* go through the list of held children */ |
|
7206 |
for (mcd = mcd_head; mcd; mcd = mcd_head) { |
|
7207 |
mcd_head = mcd->mtc_next; |
|
2155
9ba71f09c173
6423041 PSARC 2006/242 'mdi interfaces to support pHCI driver locking' and related fixes
cth
parents:
2009
diff
changeset
|
7208 |
if (mtc_off || (mcd->mtc_flags & NDI_MTC_OFF)) |
0 | 7209 |
mt_config_thread(mcd); |
7210 |
else |
|
7211 |
(void) thread_create(NULL, 0, mt_config_thread, mcd, |
|
7212 |
0, &p0, TS_RUN, minclsyspri); |
|
7213 |
} |
|
7214 |
} |
|
7215 |
||
7216 |
/* |
|
7217 |
* Given the nodeid for a persistent (PROM or SID) node, return |
|
7218 |
* the corresponding devinfo node |
|
7219 |
* NOTE: This function will return NULL for .conf nodeids. |
|
7220 |
*/ |
|
7221 |
dev_info_t * |
|
789 | 7222 |
e_ddi_nodeid_to_dip(pnode_t nodeid) |
0 | 7223 |
{ |
7224 |
dev_info_t *dip = NULL; |
|
7225 |
struct devi_nodeid *prev, *elem; |
|
7226 |
||
7227 |
mutex_enter(&devimap->dno_lock); |
|
7228 |
||
7229 |
prev = NULL; |
|
7230 |
for (elem = devimap->dno_head; elem; elem = elem->next) { |
|
7231 |
if (elem->nodeid == nodeid) { |
|
7232 |
ndi_hold_devi(elem->dip); |
|
7233 |
dip = elem->dip; |
|
7234 |
break; |
|
7235 |
} |
|
7236 |
prev = elem; |
|
7237 |
} |
|
7238 |
||
7239 |
/* |
|
7240 |
* Move to head for faster lookup next time |
|
7241 |
*/ |
|
7242 |
if (elem && prev) { |
|
7243 |
prev->next = elem->next; |
|
7244 |
elem->next = devimap->dno_head; |
|
7245 |
devimap->dno_head = elem; |
|
7246 |
} |
|
7247 |
||
7248 |
mutex_exit(&devimap->dno_lock); |
|
7249 |
return (dip); |
|
7250 |
} |
|
7251 |
||
7252 |
static void |
|
7253 |
free_cache_task(void *arg) |
|
7254 |
{ |
|
7255 |
ASSERT(arg == NULL); |
|
7256 |
||
7257 |
mutex_enter(&di_cache.cache_lock); |
|
7258 |
||
7259 |
/* |
|
7260 |
* The cache can be invalidated without holding the lock |
|
7261 |
* but it can be made valid again only while the lock is held. |
|
7262 |
* So if the cache is invalid when the lock is held, it will |
|
7263 |
* stay invalid until lock is released. |
|
7264 |
*/ |
|
7265 |
if (!di_cache.cache_valid) |
|
7266 |
i_ddi_di_cache_free(&di_cache); |
|
7267 |
||
7268 |
mutex_exit(&di_cache.cache_lock); |
|
7269 |
||
7270 |
if (di_cache_debug) |
|
7271 |
cmn_err(CE_NOTE, "system_taskq: di_cache freed"); |
|
7272 |
} |
|
7273 |
||
7274 |
extern int modrootloaded; |
|
7275 |
||
7276 |
void |
|
7277 |
i_ddi_di_cache_free(struct di_cache *cache) |
|
7278 |
{ |
|
7279 |
int error; |
|
7280 |
||
7281 |
ASSERT(mutex_owned(&cache->cache_lock)); |
|
7282 |
||
7283 |
if (cache->cache_size) { |
|
7284 |
ASSERT(cache->cache_size > 0); |
|
7285 |
ASSERT(cache->cache_data); |
|
7286 |
||
7287 |
kmem_free(cache->cache_data, cache->cache_size); |
|
7288 |
cache->cache_data = NULL; |
|
7289 |
cache->cache_size = 0; |
|
7290 |
||
7291 |
if (di_cache_debug) |
|
7292 |
cmn_err(CE_NOTE, "i_ddi_di_cache_free: freed cachemem"); |
|
7293 |
} else { |
|
7294 |
ASSERT(cache->cache_data == NULL); |
|
7295 |
if (di_cache_debug) |
|
7296 |
cmn_err(CE_NOTE, "i_ddi_di_cache_free: NULL cache"); |
|
7297 |
} |
|
7298 |
||
7299 |
if (!modrootloaded || rootvp == NULL || vn_is_readonly(rootvp)) { |
|
7300 |
if (di_cache_debug) { |
|
7301 |
cmn_err(CE_WARN, "/ not mounted/RDONLY. Skip unlink"); |
|
7302 |
} |
|
7303 |
return; |
|
7304 |
} |
|
7305 |
||
7306 |
error = vn_remove(DI_CACHE_FILE, UIO_SYSSPACE, RMFILE); |
|
7307 |
if (di_cache_debug && error && error != ENOENT) { |
|
7308 |
cmn_err(CE_WARN, "%s: unlink failed: %d", DI_CACHE_FILE, error); |
|
7309 |
} else if (di_cache_debug && !error) { |
|
7310 |
cmn_err(CE_NOTE, "i_ddi_di_cache_free: unlinked cache file"); |
|
7311 |
} |
|
7312 |
} |
|
7313 |
||
7314 |
void |
|
7315 |
i_ddi_di_cache_invalidate(int kmflag) |
|
7316 |
{ |
|
6065 | 7317 |
int cache_valid; |
0 | 7318 |
|
7319 |
if (!modrootloaded || !i_ddi_io_initialized()) { |
|
7320 |
if (di_cache_debug) |
|
7321 |
cmn_err(CE_NOTE, "I/O not inited. Skipping invalidate"); |
|
7322 |
return; |
|
7323 |
} |
|
7324 |
||
6065 | 7325 |
/* Increment devtree generation number. */ |
2621 | 7326 |
atomic_inc_ulong(&devtree_gen); |
0 | 7327 |
|
6065 | 7328 |
/* Invalidate the in-core cache and dispatch free on valid->invalid */ |
7329 |
cache_valid = atomic_swap_uint(&di_cache.cache_valid, 0); |
|
7330 |
if (cache_valid) { |
|
7331 |
(void) taskq_dispatch(system_taskq, free_cache_task, NULL, |
|
7332 |
(kmflag == KM_SLEEP) ? TQ_SLEEP : TQ_NOSLEEP); |
|
7333 |
} |
|
0 | 7334 |
|
7335 |
if (di_cache_debug) { |
|
7336 |
cmn_err(CE_NOTE, "invalidation with km_flag: %s", |
|
7337 |
kmflag == KM_SLEEP ? "KM_SLEEP" : "KM_NOSLEEP"); |
|
7338 |
} |
|
7339 |
} |
|
7340 |
||
7341 |
||
7342 |
static void |
|
7343 |
i_bind_vhci_node(dev_info_t *dip) |
|
7344 |
{ |
|
4145 | 7345 |
DEVI(dip)->devi_major = ddi_name_to_major(ddi_node_name(dip)); |
0 | 7346 |
i_ddi_set_node_state(dip, DS_BOUND); |
7347 |
} |
|
7348 |
||
7349 |
static char vhci_node_addr[2]; |
|
7350 |
||
7351 |
static int |
|
7352 |
i_init_vhci_node(dev_info_t *dip) |
|
7353 |
{ |
|
7354 |
add_global_props(dip); |
|
7355 |
DEVI(dip)->devi_ops = ndi_hold_driver(dip); |
|
7356 |
if (DEVI(dip)->devi_ops == NULL) |
|
7357 |
return (-1); |
|
7358 |
||
7359 |
DEVI(dip)->devi_instance = e_ddi_assign_instance(dip); |
|
7360 |
e_ddi_keep_instance(dip); |
|
7361 |
vhci_node_addr[0] = '\0'; |
|
7362 |
ddi_set_name_addr(dip, vhci_node_addr); |
|
7363 |
i_ddi_set_node_state(dip, DS_INITIALIZED); |
|
7364 |
return (0); |
|
7365 |
} |
|
7366 |
||
7367 |
static void |
|
7368 |
i_link_vhci_node(dev_info_t *dip) |
|
7369 |
{ |
|
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
7370 |
ASSERT(MUTEX_HELD(&global_vhci_lock)); |
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
7371 |
|
0 | 7372 |
/* |
7373 |
* scsi_vhci should be kept left most of the device tree. |
|
7374 |
*/ |
|
7375 |
if (scsi_vhci_dip) { |
|
7376 |
DEVI(dip)->devi_sibling = DEVI(scsi_vhci_dip)->devi_sibling; |
|
7377 |
DEVI(scsi_vhci_dip)->devi_sibling = DEVI(dip); |
|
7378 |
} else { |
|
7379 |
DEVI(dip)->devi_sibling = DEVI(top_devinfo)->devi_child; |
|
7380 |
DEVI(top_devinfo)->devi_child = DEVI(dip); |
|
7381 |
} |
|
7382 |
} |
|
7383 |
||
7384 |
||
7385 |
/* |
|
7386 |
* This a special routine to enumerate vhci node (child of rootnex |
|
7387 |
* node) without holding the ndi_devi_enter() lock. The device node |
|
7388 |
* is allocated, initialized and brought into DS_READY state before |
|
7389 |
* inserting into the device tree. The VHCI node is handcrafted |
|
7390 |
* here to bring the node to DS_READY, similar to rootnex node. |
|
7391 |
* |
|
7392 |
* The global_vhci_lock protects linking the node into the device |
|
7393 |
* as same lock is held before linking/unlinking any direct child |
|
7394 |
* of rootnex children. |
|
7395 |
* |
|
7396 |
* This routine is a workaround to handle a possible deadlock |
|
7397 |
* that occurs while trying to enumerate node in a different sub-tree |
|
7398 |
* during _init/_attach entry points. |
|
7399 |
*/ |
|
7400 |
/*ARGSUSED*/ |
|
7401 |
dev_info_t * |
|
7402 |
ndi_devi_config_vhci(char *drvname, int flags) |
|
7403 |
{ |
|
7404 |
struct devnames *dnp; |
|
7405 |
dev_info_t *dip; |
|
7406 |
major_t major = ddi_name_to_major(drvname); |
|
7407 |
||
7408 |
if (major == -1) |
|
7409 |
return (NULL); |
|
7410 |
||
7411 |
/* Make sure we create the VHCI node only once */ |
|
7412 |
dnp = &devnamesp[major]; |
|
7413 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
7414 |
if (dnp->dn_head) { |
|
7415 |
dip = dnp->dn_head; |
|
7416 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
7417 |
return (dip); |
|
7418 |
} |
|
7419 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
7420 |
||
7421 |
/* Allocate the VHCI node */ |
|
7422 |
ndi_devi_alloc_sleep(top_devinfo, drvname, DEVI_SID_NODEID, &dip); |
|
7423 |
ndi_hold_devi(dip); |
|
7424 |
||
7425 |
/* Mark the node as VHCI */ |
|
7426 |
DEVI(dip)->devi_node_attributes |= DDI_VHCI_NODE; |
|
7427 |
||
7428 |
i_ddi_add_devimap(dip); |
|
7429 |
i_bind_vhci_node(dip); |
|
7430 |
if (i_init_vhci_node(dip) == -1) { |
|
7431 |
ndi_rele_devi(dip); |
|
7432 |
(void) ndi_devi_free(dip); |
|
7433 |
return (NULL); |
|
7434 |
} |
|
7435 |
||
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
7436 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 7437 |
DEVI_SET_ATTACHING(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
7438 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
7439 |
|
0 | 7440 |
if (devi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) { |
7441 |
cmn_err(CE_CONT, "Could not attach %s driver", drvname); |
|
7442 |
e_ddi_free_instance(dip, vhci_node_addr); |
|
7443 |
ndi_rele_devi(dip); |
|
7444 |
(void) ndi_devi_free(dip); |
|
7445 |
return (NULL); |
|
7446 |
} |
|
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
7447 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
0 | 7448 |
DEVI_CLR_ATTACHING(dip); |
495
310ccf2a1604
6213273 Hang in i_devi_enter from fcip detach ddi_remove_minor_node call
cth
parents:
439
diff
changeset
|
7449 |
mutex_exit(&(DEVI(dip)->devi_lock)); |
0 | 7450 |
|
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
7451 |
mutex_enter(&global_vhci_lock); |
0 | 7452 |
i_link_vhci_node(dip); |
1093
4dc7aec69dc9
6299476 DM Does not interoperate with TS90 switch, cannot discover SRP targets
hiremath
parents:
789
diff
changeset
|
7453 |
mutex_exit(&global_vhci_lock); |
0 | 7454 |
i_ddi_set_node_state(dip, DS_READY); |
7455 |
||
7456 |
LOCK_DEV_OPS(&dnp->dn_lock); |
|
7457 |
dnp->dn_flags |= DN_DRIVER_HELD; |
|
7458 |
dnp->dn_head = dip; |
|
7459 |
UNLOCK_DEV_OPS(&dnp->dn_lock); |
|
7460 |
||
7461 |
i_ndi_devi_report_status_change(dip, NULL); |
|
7462 |
||
7463 |
return (dip); |
|
7464 |
} |
|
7465 |
||
7466 |
/* |
|
7467 |
* ibt_hw_is_present() returns 0 when there is no IB hardware actively |
|
7468 |
* running. This is primarily useful for modules like rpcmod which |
|
7469 |
* needs a quick check to decide whether or not it should try to use |
|
7470 |
* InfiniBand |
|
7471 |
*/ |
|
7472 |
int ib_hw_status = 0; |
|
7473 |
int |
|
7474 |
ibt_hw_is_present() |
|
7475 |
{ |
|
7476 |
return (ib_hw_status); |
|
7477 |
} |
|
4845 | 7478 |
|
7479 |
/* |
|
7480 |
* ASSERT that constraint flag is not set and then set the "retire attempt" |
|
7481 |
* flag. |
|
7482 |
*/ |
|
7483 |
int |
|
7484 |
e_ddi_mark_retiring(dev_info_t *dip, void *arg) |
|
7485 |
{ |
|
7486 |
char **cons_array = (char **)arg; |
|
7487 |
char *path; |
|
7488 |
int constraint; |
|
7489 |
int i; |
|
7490 |
||
7491 |
constraint = 0; |
|
7492 |
if (cons_array) { |
|
7493 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
7494 |
(void) ddi_pathname(dip, path); |
|
7495 |
for (i = 0; cons_array[i] != NULL; i++) { |
|
7496 |
if (strcmp(path, cons_array[i]) == 0) { |
|
7497 |
constraint = 1; |
|
7498 |
break; |
|
7499 |
} |
|
7500 |
} |
|
7501 |
kmem_free(path, MAXPATHLEN); |
|
7502 |
} |
|
7503 |
||
7504 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
7505 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)); |
|
7506 |
DEVI(dip)->devi_flags |= DEVI_RETIRING; |
|
7507 |
if (constraint) |
|
7508 |
DEVI(dip)->devi_flags |= DEVI_R_CONSTRAINT; |
|
7509 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7510 |
||
7511 |
RIO_VERBOSE((CE_NOTE, "marked dip as undergoing retire process dip=%p", |
|
7512 |
(void *)dip)); |
|
7513 |
||
7514 |
if (constraint) |
|
7515 |
RIO_DEBUG((CE_NOTE, "marked dip as constrained, dip=%p", |
|
7516 |
(void *)dip)); |
|
7517 |
||
7518 |
if (MDI_PHCI(dip)) |
|
7519 |
mdi_phci_mark_retiring(dip, cons_array); |
|
7520 |
||
7521 |
return (DDI_WALK_CONTINUE); |
|
7522 |
} |
|
7523 |
||
7524 |
static void |
|
7525 |
free_array(char **cons_array) |
|
7526 |
{ |
|
7527 |
int i; |
|
7528 |
||
7529 |
if (cons_array == NULL) |
|
7530 |
return; |
|
7531 |
||
7532 |
for (i = 0; cons_array[i] != NULL; i++) { |
|
7533 |
kmem_free(cons_array[i], strlen(cons_array[i]) + 1); |
|
7534 |
} |
|
7535 |
kmem_free(cons_array, (i+1) * sizeof (char *)); |
|
7536 |
} |
|
7537 |
||
7538 |
/* |
|
7539 |
* Walk *every* node in subtree and check if it blocks, allows or has no |
|
7540 |
* comment on a proposed retire. |
|
7541 |
*/ |
|
7542 |
int |
|
7543 |
e_ddi_retire_notify(dev_info_t *dip, void *arg) |
|
7544 |
{ |
|
7545 |
int *constraint = (int *)arg; |
|
7546 |
||
7547 |
RIO_DEBUG((CE_NOTE, "retire notify: dip = %p", (void *)dip)); |
|
7548 |
||
7549 |
(void) e_ddi_offline_notify(dip); |
|
7550 |
||
7551 |
mutex_enter(&(DEVI(dip)->devi_lock)); |
|
7552 |
if (!(DEVI(dip)->devi_flags & DEVI_RETIRING)) { |
|
7553 |
RIO_DEBUG((CE_WARN, "retire notify: dip in retire " |
|
7554 |
"subtree is not marked: dip = %p", (void *)dip)); |
|
7555 |
*constraint = 0; |
|
7556 |
} else if (DEVI(dip)->devi_flags & DEVI_R_BLOCKED) { |
|
7557 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)); |
|
7558 |
RIO_DEBUG((CE_NOTE, "retire notify: BLOCKED: dip = %p", |
|
7559 |
(void *)dip)); |
|
7560 |
*constraint = 0; |
|
7561 |
} else if (!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)) { |
|
7562 |
RIO_DEBUG((CE_NOTE, "retire notify: NO CONSTRAINT: " |
|
7563 |
"dip = %p", (void *)dip)); |
|
7564 |
*constraint = 0; |
|
7565 |
} else { |
|
7566 |
RIO_DEBUG((CE_NOTE, "retire notify: CONSTRAINT set: " |
|
7567 |
"dip = %p", (void *)dip)); |
|
7568 |
} |
|
7569 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7570 |
||
7571 |
if (MDI_PHCI(dip)) |
|
7572 |
mdi_phci_retire_notify(dip, constraint); |
|
7573 |
||
7574 |
return (DDI_WALK_CONTINUE); |
|
7575 |
} |
|
7576 |
||
7577 |
int |
|
7578 |
e_ddi_retire_finalize(dev_info_t *dip, void *arg) |
|
7579 |
{ |
|
7580 |
int constraint = *(int *)arg; |
|
7581 |
int finalize; |
|
7582 |
int phci_only; |
|
7583 |
||
7584 |
ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(dip))); |
|
7585 |
||
7586 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
7587 |
if (!(DEVI(dip)->devi_flags & DEVI_RETIRING)) { |
|
7588 |
RIO_DEBUG((CE_WARN, |
|
7589 |
"retire: unmarked dip(%p) in retire subtree", |
|
7590 |
(void *)dip)); |
|
7591 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_RETIRED)); |
|
7592 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)); |
|
7593 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_BLOCKED)); |
|
7594 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7595 |
return (DDI_WALK_CONTINUE); |
|
7596 |
} |
|
7597 |
||
7598 |
/* |
|
7599 |
* retire the device if constraints have been applied |
|
7600 |
* or if the device is not in use |
|
7601 |
*/ |
|
7602 |
finalize = 0; |
|
7603 |
if (constraint) { |
|
7604 |
ASSERT(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT); |
|
7605 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_BLOCKED)); |
|
7606 |
DEVI(dip)->devi_flags &= ~DEVI_R_CONSTRAINT; |
|
7607 |
DEVI(dip)->devi_flags &= ~DEVI_RETIRING; |
|
7608 |
DEVI(dip)->devi_flags |= DEVI_RETIRED; |
|
7609 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7610 |
(void) spec_fence_snode(dip, NULL); |
|
7611 |
RIO_DEBUG((CE_NOTE, "Fenced off: dip = %p", (void *)dip)); |
|
7612 |
e_ddi_offline_finalize(dip, DDI_SUCCESS); |
|
7613 |
} else { |
|
7614 |
if (DEVI(dip)->devi_flags & DEVI_R_BLOCKED) { |
|
7615 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)); |
|
7616 |
DEVI(dip)->devi_flags &= ~DEVI_R_BLOCKED; |
|
7617 |
DEVI(dip)->devi_flags &= ~DEVI_RETIRING; |
|
7618 |
/* we have already finalized during notify */ |
|
7619 |
} else if (DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT) { |
|
7620 |
DEVI(dip)->devi_flags &= ~DEVI_R_CONSTRAINT; |
|
7621 |
DEVI(dip)->devi_flags &= ~DEVI_RETIRING; |
|
7622 |
finalize = 1; |
|
7623 |
} else { |
|
7624 |
DEVI(dip)->devi_flags &= ~DEVI_RETIRING; |
|
7625 |
/* |
|
7626 |
* even if no contracts, need to call finalize |
|
7627 |
* to clear the contract barrier on the dip |
|
7628 |
*/ |
|
7629 |
finalize = 1; |
|
7630 |
} |
|
7631 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7632 |
RIO_DEBUG((CE_NOTE, "finalize: NOT retired: dip = %p", |
|
7633 |
(void *)dip)); |
|
7634 |
if (finalize) |
|
7635 |
e_ddi_offline_finalize(dip, DDI_FAILURE); |
|
7636 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
7637 |
DEVI_SET_DEVICE_DEGRADED(dip); |
|
7638 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7639 |
} |
|
7640 |
||
7641 |
/* |
|
7642 |
* phci_only variable indicates no client checking, just |
|
7643 |
* offline the PHCI. We set that to 0 to enable client |
|
7644 |
* checking |
|
7645 |
*/ |
|
7646 |
phci_only = 0; |
|
7647 |
if (MDI_PHCI(dip)) |
|
7648 |
mdi_phci_retire_finalize(dip, phci_only); |
|
7649 |
||
7650 |
return (DDI_WALK_CONTINUE); |
|
7651 |
} |
|
7652 |
||
7653 |
/* |
|
7654 |
* Returns |
|
7655 |
* DDI_SUCCESS if constraints allow retire |
|
7656 |
* DDI_FAILURE if constraints don't allow retire. |
|
7657 |
* cons_array is a NULL terminated array of node paths for |
|
7658 |
* which constraints have already been applied. |
|
7659 |
*/ |
|
7660 |
int |
|
7661 |
e_ddi_retire_device(char *path, char **cons_array) |
|
7662 |
{ |
|
7663 |
dev_info_t *dip; |
|
7664 |
dev_info_t *pdip; |
|
7665 |
int circ; |
|
7666 |
int circ2; |
|
7667 |
int constraint; |
|
7668 |
char *devnm; |
|
7669 |
||
7670 |
/* |
|
7671 |
* First, lookup the device |
|
7672 |
*/ |
|
7673 |
dip = e_ddi_hold_devi_by_path(path, 0); |
|
7674 |
if (dip == NULL) { |
|
7675 |
/* |
|
7676 |
* device does not exist. This device cannot be |
|
7677 |
* a critical device since it is not in use. Thus |
|
7678 |
* this device is always retireable. Return DDI_SUCCESS |
|
7679 |
* to indicate this. If this device is ever |
|
7680 |
* instantiated, I/O framework will consult the |
|
7681 |
* the persistent retire store, mark it as |
|
7682 |
* retired and fence it off. |
|
7683 |
*/ |
|
7684 |
RIO_DEBUG((CE_NOTE, "Retire device: device doesn't exist." |
|
7685 |
" NOP. Just returning SUCCESS. path=%s", path)); |
|
7686 |
free_array(cons_array); |
|
7687 |
return (DDI_SUCCESS); |
|
7688 |
} |
|
7689 |
||
7690 |
RIO_DEBUG((CE_NOTE, "Retire device: found dip = %p.", (void *)dip)); |
|
7691 |
||
7692 |
pdip = ddi_get_parent(dip); |
|
7693 |
ndi_hold_devi(pdip); |
|
7694 |
||
7695 |
/* |
|
7696 |
* Run devfs_clean() in case dip has no constraints and is |
|
7697 |
* not in use, so is retireable but there are dv_nodes holding |
|
7698 |
* ref-count on the dip. Note that devfs_clean() always returns |
|
7699 |
* success. |
|
7700 |
*/ |
|
7701 |
devnm = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); |
|
7702 |
(void) ddi_deviname(dip, devnm); |
|
7703 |
(void) devfs_clean(pdip, devnm + 1, DV_CLEAN_FORCE); |
|
7704 |
kmem_free(devnm, MAXNAMELEN + 1); |
|
7705 |
||
7706 |
ndi_devi_enter(pdip, &circ); |
|
7707 |
||
7708 |
/* release hold from e_ddi_hold_devi_by_path */ |
|
7709 |
ndi_rele_devi(dip); |
|
7710 |
||
7711 |
/* |
|
7712 |
* If it cannot make a determination, is_leaf_node() assumes |
|
7713 |
* dip is a nexus. |
|
7714 |
*/ |
|
7715 |
(void) e_ddi_mark_retiring(dip, cons_array); |
|
7716 |
if (!is_leaf_node(dip)) { |
|
7717 |
ndi_devi_enter(dip, &circ2); |
|
7718 |
ddi_walk_devs(ddi_get_child(dip), e_ddi_mark_retiring, |
|
7719 |
cons_array); |
|
7720 |
ndi_devi_exit(dip, circ2); |
|
7721 |
} |
|
7722 |
free_array(cons_array); |
|
7723 |
||
7724 |
/* |
|
7725 |
* apply constraints |
|
7726 |
*/ |
|
7727 |
RIO_DEBUG((CE_NOTE, "retire: subtree retire notify: path = %s", path)); |
|
7728 |
||
7729 |
constraint = 1; /* assume constraints allow retire */ |
|
7730 |
(void) e_ddi_retire_notify(dip, &constraint); |
|
7731 |
if (!is_leaf_node(dip)) { |
|
7732 |
ndi_devi_enter(dip, &circ2); |
|
7733 |
ddi_walk_devs(ddi_get_child(dip), e_ddi_retire_notify, |
|
7734 |
&constraint); |
|
7735 |
ndi_devi_exit(dip, circ2); |
|
7736 |
} |
|
7737 |
||
7738 |
/* |
|
7739 |
* Now finalize the retire |
|
7740 |
*/ |
|
7741 |
(void) e_ddi_retire_finalize(dip, &constraint); |
|
7742 |
if (!is_leaf_node(dip)) { |
|
7743 |
ndi_devi_enter(dip, &circ2); |
|
7744 |
ddi_walk_devs(ddi_get_child(dip), e_ddi_retire_finalize, |
|
7745 |
&constraint); |
|
7746 |
ndi_devi_exit(dip, circ2); |
|
7747 |
} |
|
7748 |
||
7749 |
if (!constraint) { |
|
7750 |
RIO_DEBUG((CE_WARN, "retire failed: path = %s", path)); |
|
7751 |
} else { |
|
7752 |
RIO_DEBUG((CE_NOTE, "retire succeeded: path = %s", path)); |
|
7753 |
} |
|
7754 |
||
7755 |
ndi_devi_exit(pdip, circ); |
|
7756 |
ndi_rele_devi(pdip); |
|
7757 |
return (constraint ? DDI_SUCCESS : DDI_FAILURE); |
|
7758 |
} |
|
7759 |
||
7760 |
static int |
|
7761 |
unmark_and_unfence(dev_info_t *dip, void *arg) |
|
7762 |
{ |
|
7763 |
char *path = (char *)arg; |
|
7764 |
||
7765 |
ASSERT(path); |
|
7766 |
||
7767 |
(void) ddi_pathname(dip, path); |
|
7768 |
||
7769 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
7770 |
DEVI(dip)->devi_flags &= ~DEVI_RETIRED; |
|
7771 |
DEVI_SET_DEVICE_ONLINE(dip); |
|
7772 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7773 |
||
7774 |
RIO_VERBOSE((CE_NOTE, "Cleared RETIRED flag: dip=%p, path=%s", |
|
7775 |
(void *)dip, path)); |
|
7776 |
||
7777 |
(void) spec_unfence_snode(dip); |
|
7778 |
RIO_DEBUG((CE_NOTE, "Unfenced device: %s", path)); |
|
7779 |
||
7780 |
if (MDI_PHCI(dip)) |
|
7781 |
mdi_phci_unretire(dip); |
|
7782 |
||
7783 |
return (DDI_WALK_CONTINUE); |
|
7784 |
} |
|
7785 |
||
7786 |
struct find_dip { |
|
7787 |
char *fd_buf; |
|
7788 |
char *fd_path; |
|
7789 |
dev_info_t *fd_dip; |
|
7790 |
}; |
|
7791 |
||
7792 |
static int |
|
7793 |
find_dip_fcn(dev_info_t *dip, void *arg) |
|
7794 |
{ |
|
7795 |
struct find_dip *findp = (struct find_dip *)arg; |
|
7796 |
||
7797 |
(void) ddi_pathname(dip, findp->fd_buf); |
|
7798 |
||
7799 |
if (strcmp(findp->fd_path, findp->fd_buf) != 0) |
|
7800 |
return (DDI_WALK_CONTINUE); |
|
7801 |
||
7802 |
ndi_hold_devi(dip); |
|
7803 |
findp->fd_dip = dip; |
|
7804 |
||
7805 |
return (DDI_WALK_TERMINATE); |
|
7806 |
} |
|
7807 |
||
7808 |
int |
|
7809 |
e_ddi_unretire_device(char *path) |
|
7810 |
{ |
|
7811 |
int circ; |
|
7812 |
char *path2; |
|
7813 |
dev_info_t *pdip; |
|
7814 |
dev_info_t *dip; |
|
7815 |
struct find_dip find_dip; |
|
7816 |
||
7817 |
ASSERT(path); |
|
7818 |
ASSERT(*path == '/'); |
|
7819 |
||
7820 |
if (strcmp(path, "/") == 0) { |
|
7821 |
cmn_err(CE_WARN, "Root node cannot be retired. Skipping " |
|
7822 |
"device unretire: %s", path); |
|
7823 |
return (0); |
|
7824 |
} |
|
7825 |
||
7826 |
/* |
|
7827 |
* We can't lookup the dip (corresponding to path) via |
|
7828 |
* e_ddi_hold_devi_by_path() because the dip may be offline |
|
7829 |
* and may not attach. Use ddi_walk_devs() instead; |
|
7830 |
*/ |
|
7831 |
find_dip.fd_buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
7832 |
find_dip.fd_path = path; |
|
7833 |
find_dip.fd_dip = NULL; |
|
7834 |
||
7835 |
pdip = ddi_root_node(); |
|
7836 |
||
7837 |
ndi_devi_enter(pdip, &circ); |
|
7838 |
ddi_walk_devs(ddi_get_child(pdip), find_dip_fcn, &find_dip); |
|
7839 |
ndi_devi_exit(pdip, circ); |
|
7840 |
||
7841 |
kmem_free(find_dip.fd_buf, MAXPATHLEN); |
|
7842 |
||
7843 |
if (find_dip.fd_dip == NULL) { |
|
7844 |
cmn_err(CE_WARN, "Device not found in device tree. Skipping " |
|
7845 |
"device unretire: %s", path); |
|
7846 |
return (0); |
|
7847 |
} |
|
7848 |
||
7849 |
dip = find_dip.fd_dip; |
|
7850 |
||
7851 |
pdip = ddi_get_parent(dip); |
|
7852 |
||
7853 |
ndi_hold_devi(pdip); |
|
7854 |
||
7855 |
ndi_devi_enter(pdip, &circ); |
|
7856 |
||
7857 |
path2 = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
7858 |
||
7859 |
(void) unmark_and_unfence(dip, path2); |
|
7860 |
if (!is_leaf_node(dip)) { |
|
7861 |
ndi_devi_enter(dip, &circ); |
|
7862 |
ddi_walk_devs(ddi_get_child(dip), unmark_and_unfence, path2); |
|
7863 |
ndi_devi_exit(dip, circ); |
|
7864 |
} |
|
7865 |
||
7866 |
kmem_free(path2, MAXPATHLEN); |
|
7867 |
||
7868 |
/* release hold from find_dip_fcn() */ |
|
7869 |
ndi_rele_devi(dip); |
|
7870 |
||
7871 |
ndi_devi_exit(pdip, circ); |
|
7872 |
||
7873 |
ndi_rele_devi(pdip); |
|
7874 |
||
7875 |
return (0); |
|
7876 |
} |
|
7877 |
||
7878 |
/* |
|
7879 |
* Called before attach on a dip that has been retired. |
|
7880 |
*/ |
|
7881 |
static int |
|
7882 |
mark_and_fence(dev_info_t *dip, void *arg) |
|
7883 |
{ |
|
7884 |
char *fencepath = (char *)arg; |
|
7885 |
||
7886 |
/* |
|
7887 |
* We have already decided to retire this device. The various |
|
7888 |
* constraint checking should not be set. |
|
7889 |
* NOTE that the retire flag may already be set due to |
|
7890 |
* fenced -> detach -> fenced transitions. |
|
7891 |
*/ |
|
7892 |
mutex_enter(&DEVI(dip)->devi_lock); |
|
7893 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_CONSTRAINT)); |
|
7894 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_R_BLOCKED)); |
|
7895 |
ASSERT(!(DEVI(dip)->devi_flags & DEVI_RETIRING)); |
|
7896 |
DEVI(dip)->devi_flags |= DEVI_RETIRED; |
|
7897 |
mutex_exit(&DEVI(dip)->devi_lock); |
|
7898 |
RIO_VERBOSE((CE_NOTE, "marked as RETIRED dip=%p", (void *)dip)); |
|
7899 |
||
7900 |
if (fencepath) { |
|
7901 |
(void) spec_fence_snode(dip, NULL); |
|
7902 |
RIO_DEBUG((CE_NOTE, "Fenced: %s", |
|
7903 |
ddi_pathname(dip, fencepath))); |
|
7904 |
} |
|
7905 |
||
7906 |
return (DDI_WALK_CONTINUE); |
|
7907 |
} |
|
7908 |
||
7909 |
/* |
|
7910 |
* Checks the retire database and: |
|
7911 |
* |
|
7912 |
* - if device is present in the retire database, marks the device retired |
|
7913 |
* and fences it off. |
|
7914 |
* - if device is not in retire database, allows the device to attach normally |
|
7915 |
* |
|
7916 |
* To be called only by framework attach code on first attach attempt. |
|
7917 |
* |
|
7918 |
*/ |
|
7919 |
static void |
|
7920 |
i_ddi_check_retire(dev_info_t *dip) |
|
7921 |
{ |
|
7922 |
char *path; |
|
7923 |
dev_info_t *pdip; |
|
7924 |
int circ; |
|
7925 |
int phci_only; |
|
7926 |
||
7927 |
pdip = ddi_get_parent(dip); |
|
7928 |
||
7929 |
/* |
|
7930 |
* Root dip is treated special and doesn't take this code path. |
|
7931 |
* Also root can never be retired. |
|
7932 |
*/ |
|
7933 |
ASSERT(pdip); |
|
7934 |
ASSERT(DEVI_BUSY_OWNED(pdip)); |
|
7935 |
ASSERT(i_ddi_node_state(dip) < DS_ATTACHED); |
|
7936 |
||
7937 |
path = kmem_alloc(MAXPATHLEN, KM_SLEEP); |
|
7938 |
||
7939 |
(void) ddi_pathname(dip, path); |
|
7940 |
||
7941 |
RIO_VERBOSE((CE_NOTE, "Checking if dip should attach: dip=%p, path=%s", |
|
7942 |
(void *)dip, path)); |
|
7943 |
||
7944 |
/* |
|
7945 |
* Check if this device is in the "retired" store i.e. should |
|
7946 |
* be retired. If not, we have nothing to do. |
|
7947 |
*/ |
|
7948 |
if (e_ddi_device_retired(path) == 0) { |
|
7949 |
RIO_VERBOSE((CE_NOTE, "device is NOT retired: path=%s", path)); |
|
7950 |
kmem_free(path, MAXPATHLEN); |
|
7951 |
return; |
|
7952 |
} |
|
7953 |
||
7954 |
RIO_DEBUG((CE_NOTE, "attach: device is retired: path=%s", path)); |
|
7955 |
||
7956 |
/* |
|
7957 |
* Mark dips and fence off snodes (if any) |
|
7958 |
*/ |
|
7959 |
RIO_DEBUG((CE_NOTE, "attach: Mark and fence subtree: path=%s", path)); |
|
7960 |
(void) mark_and_fence(dip, path); |
|
7961 |
if (!is_leaf_node(dip)) { |
|
7962 |
ndi_devi_enter(dip, &circ); |
|
7963 |
ddi_walk_devs(ddi_get_child(dip), mark_and_fence, path); |
|
7964 |
ndi_devi_exit(dip, circ); |
|
7965 |
} |
|
7966 |
||
7967 |
kmem_free(path, MAXPATHLEN); |
|
7968 |
||
7969 |
/* |
|
7970 |
* We don't want to check the client. We just want to |
|
7971 |
* offline the PHCI |
|
7972 |
*/ |
|
7973 |
phci_only = 1; |
|
7974 |
if (MDI_PHCI(dip)) |
|
7975 |
mdi_phci_retire_finalize(dip, phci_only); |
|
7976 |
} |