xref: /illumos-gate/usr/src/uts/common/io/hotplug/hpcsvc/hpcsvc.c (revision 39b361b2ebefcef5612a54ae5cbd2179e19be296)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * hot-plug services module
28  */
29 
30 #include <sys/modctl.h>
31 #include <sys/kmem.h>
32 #include <sys/sunddi.h>
33 #include <sys/sunndi.h>
34 #include <sys/disp.h>
35 #include <sys/stat.h>
36 #include <sys/hotplug/hpcsvc.h>
37 #include <sys/callb.h>
38 
39 /*
40  * debug macros:
41  */
42 #if defined(DEBUG)
43 
44 int hpcsvc_debug = 0;
45 
46 static void debug(char *, uintptr_t, uintptr_t, uintptr_t,
47 	uintptr_t, uintptr_t);
48 
49 #define	DEBUG0(fmt)	\
50 	debug(fmt, 0, 0, 0, 0, 0);
51 #define	DEBUG1(fmt, a1)	\
52 	debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0);
53 #define	DEBUG2(fmt, a1, a2)	\
54 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0);
55 #define	DEBUG3(fmt, a1, a2, a3)	\
56 	debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0);
57 #else
58 #define	DEBUG0(fmt)
59 #define	DEBUG1(fmt, a1)
60 #define	DEBUG2(fmt, a1, a2)
61 #define	DEBUG3(fmt, a1, a2, a3)
62 #endif
63 
64 /*
65  * Definitions for the bus node registration list:
66  *
67  * The hot-plug service module maintains a linked list of items
68  * representing the device bus nodes that have been registered via
69  * hpc_nexus_register, or identified as candidates for registration
70  * by the bus argument to hpc_slot_register.
71  *
72  * The head of the linked listed is stored in hpc_bus_list_head. Insertions
73  * and removals from the list should be locked with mutex hpc_bus_mutex.
74  *
75  * Items in the list are allocated/freed with the macros hpc_alloc_bus_entry()
76  * and hpc_free_bus_entry().
77  *
78  * Each item in the list contains the following fields:
79  *
80  *	bus_dip - pointer to devinfo node of the registering bus
81  *
82  *	bus_name - device path name of the bus (ie /pci@1f,4000)
83  *
84  *	bus_callback - bus nexus driver callback function registered
85  *		with the bus
86  *
87  *	bus_registered - a boolean value which is true if the bus has
88  *		been registered with hpc_nexus_register, false otherwise
89  *
90  *	bus_mutex - mutex lock to be held while updating this list entry
91  *
92  *	bus_slot_list - linked list of the slots registered for this
93  *		bus node (see slot list details below)
94  *
95  *	bus_thread - kernel thread for running slot event handlers for
96  *		slots associated with this bus
97  *
98  *	bus_thread_cv - condition variable for synchronization between
99  *		the service routines and the thread for running slot
100  *		event handlers
101  *
102  *	bus_thread_exit - a boolean value used to instruct the thread
103  *		for invoking the slot event handlers to exit
104  *
105  *	bus_slot_event_list_head - the head of the linked list of instances
106  *		of slot event handlers to be run
107  *		handlers to be invoked
108  *
109  *	bus_next - pointer to next list entry
110  */
111 
112 typedef struct hpc_bus_entry hpc_bus_entry_t;
113 typedef struct hpc_slot_entry hpc_slot_entry_t;
114 typedef struct hpc_event_entry hpc_event_entry_t;
115 
116 struct hpc_event_entry {
117 	hpc_slot_entry_t *slotp;
118 	int event;
119 	hpc_event_entry_t *next;
120 };
121 
122 #define	hpc_alloc_event_entry()	\
123 	(hpc_event_entry_t *)kmem_zalloc(sizeof (hpc_event_entry_t), KM_SLEEP)
124 
125 #define	hpc_free_event_entry(a)	\
126 	kmem_free((a), sizeof (hpc_event_entry_t))
127 
128 struct hpc_bus_entry {
129 	dev_info_t *bus_dip;
130 	char bus_name[MAXPATHLEN + 1];
131 	boolean_t bus_registered;
132 	kmutex_t bus_mutex;
133 	int (* bus_callback)(dev_info_t *dip, hpc_slot_t hdl,
134 		hpc_slot_info_t *slot_info, int slot_state);
135 	hpc_slot_entry_t *bus_slot_list;
136 	kthread_t *bus_thread;
137 	kcondvar_t bus_thread_cv;
138 	boolean_t bus_thread_exit;
139 	hpc_event_entry_t *bus_slot_event_list_head;
140 	hpc_bus_entry_t *bus_next;
141 };
142 
143 #define	hpc_alloc_bus_entry()	\
144 	(hpc_bus_entry_t *)kmem_zalloc(sizeof (hpc_bus_entry_t), KM_SLEEP)
145 
146 #define	hpc_free_bus_entry(a)	\
147 	kmem_free((a), sizeof (hpc_bus_entry_t))
148 
149 
150 /*
151  * Definitions for the per-bus node slot registration list:
152  *
153  * For each bus node in the bus list, the hot-plug service module maintains
154  * a doubly linked link list of items representing the slots that have been
155  * registered (by hot-plug controllers) for that bus.
156  *
157  * The head of the linked listed is stored in bus_slot_list field of the bus
158  * node.  Insertions and removals from this list should locked with the mutex
159  * in the bus_mutex field of the bus node.
160  *
161  * Items in the list are allocated/freed with the macros hpc_alloc_slot_entry()
162  * and hpc_free_slot_entry().
163  *
164  * Each item in the list contains the following fields:
165  *
166  *	slot_handle - handle for slot (hpc_slot_t)
167  *
168  *	slot_info - information registered with the slot (hpc_slot_info_t)
169  *
170  *	slot_ops - ops vector registered with the slot (hpc_slot_ops_t)
171  *
172  *	slot_ops_arg - argument to be passed to ops routines (caddr_t)
173  *
174  *	slot_event_handler - handler registered for slot events
175  *
176  *	slot_event_handler_arg - argument to be passed to event handler
177  *
178  *	slot_event_mask - the set of events for which the event handler
179  *		gets invoked
180  *
181  *	slot_bus - pointer to bus node for the slot
182  *
183  *	slot_hpc_dip  -  devinfo node pointer to the HPC driver instance
184  *			 that controls this slot
185  *
186  *	slot_{prev,next} - point to {previous,next} node in the list
187  */
188 
189 struct hpc_slot_entry {
190 	hpc_slot_t slot_handle;
191 	hpc_slot_info_t slot_info;	/* should be static & copied */
192 	hpc_slot_ops_t slot_ops;
193 	caddr_t slot_ops_arg;
194 	int (* slot_event_handler)(caddr_t, uint_t);
195 	caddr_t slot_event_handler_arg;
196 	uint_t slot_event_mask;
197 	hpc_bus_entry_t *slot_bus;
198 	dev_info_t *slot_hpc_dip;
199 	hpc_slot_entry_t *slot_next, *slot_prev;
200 };
201 
202 #define	hpc_alloc_slot_entry()	\
203 	(hpc_slot_entry_t *)kmem_zalloc(sizeof (hpc_slot_entry_t), KM_SLEEP)
204 
205 #define	hpc_free_slot_entry(a)	\
206 	kmem_free((a), sizeof (hpc_slot_entry_t))
207 
208 
209 /*
210  * Definitions for slot registration callback table.
211  */
212 
213 typedef struct hpc_callback_entry hpc_callback_entry_t;
214 
215 struct hpc_callback_entry {
216 	int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
217 		hpc_slot_info_t *slot_info, int slot_state);
218 	dev_info_t *dip;
219 	hpc_slot_t hdl;
220 	hpc_slot_info_t *slot_info;
221 	int slot_state;
222 	hpc_callback_entry_t *next;
223 };
224 
225 #define	hpc_alloc_callback_entry()	\
226 	(hpc_callback_entry_t *)	\
227 		kmem_zalloc(sizeof (hpc_callback_entry_t), KM_SLEEP)
228 
229 #define	hpc_free_callback_entry(a)	\
230 	kmem_free((a), sizeof (hpc_callback_entry_t))
231 
232 
233 
234 /*
235  * Mutex lock for bus registration table and table head.
236  */
237 static kmutex_t hpc_bus_mutex;
238 static hpc_bus_entry_t *hpc_bus_list_head;
239 
240 
241 /*
242  * Forward function declarations.
243  */
244 static hpc_bus_entry_t *hpc_find_bus_by_name(char *name);
245 static void hpc_slot_event_dispatcher(hpc_bus_entry_t *busp);
246 
247 
248 /*
249  * loadable module definitions:
250  */
251 extern struct mod_ops mod_miscops;
252 
253 static struct modlmisc modlmisc = {
254 	&mod_miscops,	/* Type of module */
255 	"hot-plug controller services"
256 };
257 
258 static struct modlinkage modlinkage = {
259 	MODREV_1, (void *)&modlmisc, NULL
260 };
261 
262 int
_init(void)263 _init(void)
264 {
265 	int e;
266 
267 	mutex_init(&hpc_bus_mutex, NULL, MUTEX_DRIVER, NULL);
268 
269 	/*
270 	 * Install the module.
271 	 */
272 	e = mod_install(&modlinkage);
273 	if (e != 0) {
274 		mutex_destroy(&hpc_bus_mutex);
275 	}
276 	return (e);
277 }
278 
279 int
_fini(void)280 _fini(void)
281 {
282 	int e;
283 
284 	e = mod_remove(&modlinkage);
285 	if (e == 0) {
286 		mutex_destroy(&hpc_bus_mutex);
287 	}
288 	return (e);
289 }
290 
291 int
_info(struct modinfo * modinfop)292 _info(struct modinfo *modinfop)
293 {
294 	return (mod_info(&modlinkage, modinfop));
295 }
296 
297 
298 
299 hpc_slot_ops_t *
hpc_alloc_slot_ops(int flag)300 hpc_alloc_slot_ops(int flag)
301 {
302 	hpc_slot_ops_t *ops;
303 
304 	ops = (hpc_slot_ops_t *)kmem_zalloc(sizeof (hpc_slot_ops_t), flag);
305 	return (ops);
306 }
307 
308 
309 void
hpc_free_slot_ops(hpc_slot_ops_t * ops)310 hpc_free_slot_ops(hpc_slot_ops_t *ops)
311 {
312 	kmem_free((void *)ops, sizeof (hpc_slot_ops_t));
313 }
314 
315 
316 /*ARGSUSED2*/
317 int
hpc_nexus_register_bus(dev_info_t * dip,int (* callback)(dev_info_t * dip,hpc_slot_t hdl,hpc_slot_info_t * slot_info,int slot_state),uint_t flags)318 hpc_nexus_register_bus(dev_info_t *dip,
319 	int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
320 	hpc_slot_info_t *slot_info, int slot_state), uint_t flags)
321 {
322 	hpc_bus_entry_t *busp;
323 	hpc_slot_entry_t *slotp;
324 	char bus_path[MAXPATHLEN + 1];
325 
326 	DEBUG2("hpc_nexus_register_bus: %s%d",
327 	    ddi_node_name(dip), ddi_get_instance(dip));
328 	mutex_enter(&hpc_bus_mutex);
329 	(void) ddi_pathname(dip, bus_path);
330 	busp = hpc_find_bus_by_name(bus_path);
331 	if (busp == NULL) {
332 
333 		/*
334 		 * Initialize the new bus node and link it at the head
335 		 * of the bus list.
336 		 */
337 		DEBUG0("hpc_nexus_register_bus: not in bus list");
338 		busp = hpc_alloc_bus_entry();
339 		busp->bus_dip = dip;
340 		busp->bus_registered = B_TRUE;
341 		(void) strcpy(busp->bus_name, bus_path);
342 		mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL);
343 		busp->bus_callback = callback;
344 		busp->bus_slot_list = NULL;
345 		busp->bus_next = hpc_bus_list_head;
346 		hpc_bus_list_head = busp;
347 
348 	} else {
349 
350 		/*
351 		 * The bus is in the bus list but isn't registered yet.
352 		 * Mark it as registered, and run the registration callbacks
353 		 * for it slots.
354 		 */
355 		DEBUG0("hpc_nexus_register_bus: in list, but not registered");
356 		mutex_enter(&busp->bus_mutex);
357 		if (busp->bus_registered == B_TRUE) {
358 			mutex_exit(&busp->bus_mutex);
359 			mutex_exit(&hpc_bus_mutex);
360 			return (HPC_ERR_BUS_DUPLICATE);
361 		}
362 		busp->bus_dip = dip;
363 		busp->bus_callback = callback;
364 		busp->bus_registered = B_TRUE;
365 
366 		mutex_exit(&busp->bus_mutex);
367 		mutex_exit(&hpc_bus_mutex);
368 		if (callback) {
369 			DEBUG0("hpc_nexus_register_bus: running callbacks");
370 			for (slotp = busp->bus_slot_list; slotp;
371 			    slotp = slotp->slot_next) {
372 				(void) callback(dip, slotp, &slotp->slot_info,
373 				    HPC_SLOT_ONLINE);
374 			}
375 		}
376 		return (HPC_SUCCESS);
377 	}
378 	mutex_exit(&hpc_bus_mutex);
379 	return (HPC_SUCCESS);
380 }
381 
382 
383 int
hpc_nexus_unregister_bus(dev_info_t * dip)384 hpc_nexus_unregister_bus(dev_info_t *dip)
385 {
386 	hpc_bus_entry_t *busp, *busp_prev;
387 	hpc_slot_entry_t *slotp;
388 
389 	/*
390 	 * Search the list for the bus node and remove it.
391 	 */
392 	DEBUG2("hpc_nexus_unregister_bus: %s%d",
393 	    ddi_node_name(dip), ddi_get_instance(dip));
394 	mutex_enter(&hpc_bus_mutex);
395 	for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp,
396 	    busp = busp->bus_next) {
397 		if (busp->bus_dip == dip)
398 			break;
399 	}
400 	if (busp == NULL) {
401 		mutex_exit(&hpc_bus_mutex);
402 		return (HPC_ERR_BUS_NOTREGISTERED);
403 	}
404 
405 	/*
406 	 * If the bus has slots, mark the bus as unregistered, otherwise
407 	 * remove the bus entry from the list.
408 	 */
409 	mutex_enter(&busp->bus_mutex);
410 	if (busp->bus_slot_list == NULL) {
411 		if (busp == hpc_bus_list_head)
412 			hpc_bus_list_head = busp->bus_next;
413 		else
414 			busp_prev->bus_next = busp->bus_next;
415 		mutex_exit(&busp->bus_mutex);
416 		mutex_destroy(&busp->bus_mutex);
417 		hpc_free_bus_entry(busp);
418 		mutex_exit(&hpc_bus_mutex);
419 		return (HPC_SUCCESS);
420 	}
421 
422 	/*
423 	 * unregister event handlers for all the slots on this bus.
424 	 */
425 	for (slotp = busp->bus_slot_list; slotp != NULL;
426 		slotp = slotp->slot_next) {
427 		slotp->slot_event_handler = NULL;
428 		slotp->slot_event_handler_arg = NULL;
429 	}
430 	busp->bus_registered = B_FALSE;
431 	mutex_exit(&busp->bus_mutex);
432 	mutex_exit(&hpc_bus_mutex);
433 	return (HPC_SUCCESS);
434 }
435 
436 
437 /*ARGSUSED5*/
438 int
hpc_slot_register(dev_info_t * hpc_dip,char * bus,hpc_slot_info_t * infop,hpc_slot_t * handlep,hpc_slot_ops_t * opsp,caddr_t ops_arg,uint_t flags)439 hpc_slot_register(dev_info_t *hpc_dip, char *bus, hpc_slot_info_t *infop,
440 	hpc_slot_t *handlep, hpc_slot_ops_t *opsp,
441 	caddr_t ops_arg, uint_t flags)
442 {
443 	hpc_bus_entry_t *busp;
444 	hpc_slot_entry_t *slotp, *slot_list_head;
445 	boolean_t run_callback = B_FALSE;
446 	int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
447 		hpc_slot_info_t *slot_info, int slot_state);
448 	dev_info_t *dip;
449 	kthread_t *t;
450 
451 	/*
452 	 * Validate the arguments.
453 	 */
454 	DEBUG1("hpc_slot_register: %s", bus);
455 	if (handlep == NULL || infop == NULL || opsp == NULL || hpc_dip == NULL)
456 		return (HPC_ERR_INVALID);
457 
458 	/*
459 	 * The bus for the slot may or may not be in the bus list.  If it's
460 	 * not, we create a node for the bus in the bus list and mark it as
461 	 * not registered.
462 	 */
463 	mutex_enter(&hpc_bus_mutex);
464 	busp = hpc_find_bus_by_name(bus);
465 	if (busp == NULL) {
466 
467 		/*
468 		 * Initialize the new bus node and link it at the
469 		 * head of the bus list.
470 		 */
471 		DEBUG1("hpc_slot_register: %s not in bus list", bus);
472 		busp = hpc_alloc_bus_entry();
473 		busp->bus_registered = B_FALSE;
474 		(void) strcpy(busp->bus_name, bus);
475 		mutex_init(&busp->bus_mutex, NULL, MUTEX_DRIVER, NULL);
476 		busp->bus_slot_list = NULL;
477 		busp->bus_next = hpc_bus_list_head;
478 		hpc_bus_list_head = busp;
479 
480 	} else {
481 		if (busp->bus_registered == B_TRUE) {
482 			run_callback = B_TRUE;
483 			callback = busp->bus_callback;
484 			dip = busp->bus_dip;
485 		}
486 	}
487 
488 	mutex_enter(&busp->bus_mutex);
489 	slot_list_head = busp->bus_slot_list;
490 	if (slot_list_head == NULL) {
491 
492 		/*
493 		 * The slot list was empty, so this is the first slot
494 		 * registered for the bus.  Create a per-bus thread
495 		 * for running the slot event handlers.
496 		 */
497 		DEBUG0("hpc_slot_register: creating event callback thread");
498 		cv_init(&busp->bus_thread_cv, NULL, CV_DRIVER, NULL);
499 		busp->bus_thread_exit = B_FALSE;
500 		t = thread_create(NULL, 0, hpc_slot_event_dispatcher,
501 		    (caddr_t)busp, 0, &p0, TS_RUN, minclsyspri);
502 		busp->bus_thread = t;
503 	}
504 
505 	/*
506 	 * Create and initialize a new entry in the slot list for the bus.
507 	 */
508 	slotp = hpc_alloc_slot_entry();
509 	slotp->slot_handle = (hpc_slot_t)slotp;
510 	slotp->slot_info = *infop;
511 	slotp->slot_ops = *opsp;
512 	slotp->slot_ops_arg = ops_arg;
513 	slotp->slot_bus = busp;
514 	slotp->slot_hpc_dip = hpc_dip;
515 	slotp->slot_prev = NULL;
516 	busp->bus_slot_list = slotp;
517 	slotp->slot_next = slot_list_head;
518 	if (slot_list_head != NULL)
519 		slot_list_head->slot_prev = slotp;
520 	mutex_exit(&busp->bus_mutex);
521 	mutex_exit(&hpc_bus_mutex);
522 
523 	/*
524 	 * Return the handle to the caller prior to return to avoid recursion.
525 	 */
526 	*handlep = (hpc_slot_t)slotp;
527 
528 	/*
529 	 * If the bus was registered, we run the callback registered by
530 	 * the bus node.
531 	 */
532 	if (run_callback) {
533 		DEBUG0("hpc_slot_register: running callback");
534 
535 		(void) callback(dip, slotp, infop, HPC_SLOT_ONLINE);
536 	}
537 
538 	/*
539 	 * Keep hpc driver in memory
540 	 */
541 	(void) ndi_hold_driver(hpc_dip);
542 
543 	return (HPC_SUCCESS);
544 }
545 
546 
547 int
hpc_slot_unregister(hpc_slot_t * handlep)548 hpc_slot_unregister(hpc_slot_t *handlep)
549 {
550 	hpc_slot_entry_t *slotp;
551 	hpc_bus_entry_t *busp, *busp_prev;
552 	boolean_t run_callback;
553 	int (* callback)(dev_info_t *dip, hpc_slot_t hdl,
554 		hpc_slot_info_t *slot_info, int slot_state);
555 	int r;
556 	dev_info_t *dip;
557 	char *bus_name;
558 
559 	DEBUG0("hpc_slot_unregister:");
560 
561 	ASSERT(handlep != NULL);
562 
563 	/* validate the handle */
564 	slotp = (hpc_slot_entry_t *)*handlep;
565 	if ((slotp == NULL) || slotp->slot_handle != *handlep)
566 		return (HPC_ERR_INVALID);
567 
568 	/*
569 	 * Get the bus list entry from the slot to grap the mutex for
570 	 * the slot list of the bus.
571 	 */
572 	mutex_enter(&hpc_bus_mutex);
573 	busp = slotp->slot_bus;
574 	DEBUG2("hpc_slot_unregister: handlep=%x, slotp=%x", handlep, slotp);
575 	if (busp == NULL) {
576 		mutex_exit(&hpc_bus_mutex);
577 		return (HPC_ERR_SLOT_NOTREGISTERED);
578 	}
579 
580 	/*
581 	 * Determine if we need to run the slot offline callback and
582 	 * save the data necessary to do so.
583 	 */
584 	callback = busp->bus_callback;
585 	run_callback = (busp->bus_registered == B_TRUE) && (callback != NULL);
586 	dip = busp->bus_dip;
587 	bus_name = busp->bus_name;
588 
589 	/*
590 	 * Run the slot offline callback if necessary.
591 	 */
592 	if (run_callback) {
593 		mutex_exit(&hpc_bus_mutex);
594 		DEBUG0("hpc_slot_unregister: running callback");
595 		r = callback(dip, (hpc_slot_t)slotp, &slotp->slot_info,
596 		    HPC_SLOT_OFFLINE);
597 		DEBUG1("hpc_slot_unregister: callback returned %x", r);
598 		if (r != HPC_SUCCESS)
599 			return (HPC_ERR_FAILED);
600 		mutex_enter(&hpc_bus_mutex);
601 	}
602 
603 	/*
604 	 * Remove the slot from list and free the memory associated with it.
605 	 */
606 	mutex_enter(&busp->bus_mutex);
607 	DEBUG1("hpc_slot_unregister: freeing slot, bus_slot_list=%x",
608 		busp->bus_slot_list);
609 	if (slotp->slot_prev != NULL)
610 		slotp->slot_prev->slot_next = slotp->slot_next;
611 	if (slotp->slot_next != NULL)
612 		slotp->slot_next->slot_prev = slotp->slot_prev;
613 	if (slotp == busp->bus_slot_list)
614 		busp->bus_slot_list = slotp->slot_next;
615 
616 	/*
617 	 * Release hold from slot registration
618 	 */
619 	ndi_rele_driver(slotp->slot_hpc_dip);
620 
621 	/* Free the memory associated with the slot entry structure */
622 	hpc_free_slot_entry(slotp);
623 
624 	/*
625 	 * If the slot list is empty then stop the event handler thread.
626 	 */
627 	if (busp->bus_slot_list == NULL) {
628 		DEBUG0("hpc_slot_unregister: stopping thread");
629 		busp->bus_thread_exit = B_TRUE;
630 		cv_signal(&busp->bus_thread_cv);
631 		DEBUG0("hpc_slot_unregister: waiting for thread to exit");
632 		cv_wait(&busp->bus_thread_cv, &busp->bus_mutex);
633 		DEBUG0("hpc_slot_unregister: thread exit");
634 		cv_destroy(&busp->bus_thread_cv);
635 	}
636 
637 	/*
638 	 * If the bus is unregistered and this is the last slot for this bus
639 	 * then remove the entry from the bus list.
640 	 */
641 	if (busp->bus_registered == B_FALSE && busp->bus_slot_list == NULL) {
642 		/* locate the previous entry in the bus list */
643 		for (busp = hpc_bus_list_head; busp != NULL; busp_prev = busp,
644 		    busp = busp->bus_next)
645 			if (strcmp(bus_name, busp->bus_name) == 0)
646 				break;
647 
648 		if (busp == hpc_bus_list_head)
649 			hpc_bus_list_head = busp->bus_next;
650 		else
651 			busp_prev->bus_next = busp->bus_next;
652 
653 		mutex_exit(&busp->bus_mutex);
654 		mutex_destroy(&busp->bus_mutex);
655 		hpc_free_bus_entry(busp);
656 	} else
657 		mutex_exit(&busp->bus_mutex);
658 	mutex_exit(&hpc_bus_mutex);
659 
660 	/*
661 	 * reset the slot handle.
662 	 */
663 	*handlep = NULL;
664 	return (HPC_SUCCESS);
665 }
666 
667 
668 int
hpc_install_event_handler(hpc_slot_t handle,uint_t event_mask,int (* event_handler)(caddr_t,uint_t),caddr_t arg)669 hpc_install_event_handler(hpc_slot_t handle, uint_t event_mask,
670 	int (*event_handler)(caddr_t, uint_t), caddr_t arg)
671 {
672 	hpc_slot_entry_t *slotp;
673 	hpc_bus_entry_t *busp;
674 
675 	DEBUG3("hpc_install_event_handler: handle=%x, mask=%x, arg=%x",
676 		handle, event_mask, arg);
677 	ASSERT((handle != NULL) && (event_handler != NULL));
678 	slotp = (hpc_slot_entry_t *)handle;
679 	busp = slotp->slot_bus;
680 	ASSERT(slotp == slotp->slot_handle);
681 	mutex_enter(&busp->bus_mutex);
682 	slotp->slot_event_mask = event_mask;
683 	slotp->slot_event_handler = event_handler;
684 	slotp->slot_event_handler_arg = arg;
685 	mutex_exit(&busp->bus_mutex);
686 	return (HPC_SUCCESS);
687 }
688 
689 
690 int
hpc_remove_event_handler(hpc_slot_t handle)691 hpc_remove_event_handler(hpc_slot_t handle)
692 {
693 	hpc_slot_entry_t *slotp;
694 	hpc_bus_entry_t *busp;
695 
696 	DEBUG1("hpc_remove_event_handler: handle=%x", handle);
697 	ASSERT(handle != NULL);
698 	slotp = (hpc_slot_entry_t *)handle;
699 	ASSERT(slotp == slotp->slot_handle);
700 	busp = slotp->slot_bus;
701 	mutex_enter(&busp->bus_mutex);
702 	slotp->slot_event_mask = 0;
703 	slotp->slot_event_handler = NULL;
704 	slotp->slot_event_handler_arg = NULL;
705 	mutex_exit(&busp->bus_mutex);
706 	return (HPC_SUCCESS);
707 }
708 
709 
710 /*ARGSUSED2*/
711 int
hpc_slot_event_notify(hpc_slot_t handle,uint_t event,uint_t flags)712 hpc_slot_event_notify(hpc_slot_t handle, uint_t event, uint_t flags)
713 {
714 	hpc_slot_entry_t *slotp;
715 	hpc_bus_entry_t *busp;
716 	hpc_event_entry_t *eventp;
717 
718 	DEBUG2("hpc_slot_event_notify: handle=%x event=%x", handle, event);
719 	ASSERT(handle != NULL);
720 	slotp = (hpc_slot_entry_t *)handle;
721 	ASSERT(slotp == slotp->slot_handle);
722 
723 	if (slotp->slot_event_handler == NULL)
724 		return (HPC_EVENT_UNCLAIMED);
725 
726 	/*
727 	 * If the request is to handle the event synchronously, then call
728 	 * the event handler without queuing the event.
729 	 */
730 	if (flags == HPC_EVENT_SYNCHRONOUS) {
731 		caddr_t arg;
732 		int (* func)(caddr_t, uint_t);
733 
734 		func = slotp->slot_event_handler;
735 		arg = slotp->slot_event_handler_arg;
736 		return (func(arg, event));
737 	}
738 	/*
739 	 * Insert the event into the bus slot event handler list and
740 	 * signal the bus slot event handler dispatch thread.
741 	 */
742 	busp = slotp->slot_bus;
743 	mutex_enter(&busp->bus_mutex);
744 
745 	if (busp->bus_slot_event_list_head == NULL) {
746 		eventp = busp->bus_slot_event_list_head =
747 		    hpc_alloc_event_entry();
748 	} else {
749 		for (eventp = busp->bus_slot_event_list_head;
750 			    eventp->next != NULL; eventp = eventp->next)
751 			;
752 		eventp->next = hpc_alloc_event_entry();
753 		eventp = eventp->next;
754 	}
755 	eventp->slotp = slotp;
756 	eventp->event = event;
757 	eventp->next = NULL;
758 	DEBUG2("hpc_slot_event_notify: busp=%x event=%x", busp, event);
759 	cv_signal(&busp->bus_thread_cv);
760 	mutex_exit(&busp->bus_mutex);
761 	return (HPC_EVENT_CLAIMED);
762 }
763 
764 
765 int
hpc_nexus_connect(hpc_slot_t handle,void * data,uint_t flags)766 hpc_nexus_connect(hpc_slot_t handle, void *data, uint_t flags)
767 {
768 	hpc_slot_entry_t *slotp;
769 
770 	ASSERT(handle != NULL);
771 	slotp = (hpc_slot_entry_t *)handle;
772 	if (slotp->slot_ops.hpc_op_connect)
773 		return (slotp->slot_ops.hpc_op_connect(slotp->slot_ops_arg,
774 			handle, data, flags));
775 	return (HPC_ERR_FAILED);
776 }
777 
778 
779 int
hpc_nexus_disconnect(hpc_slot_t handle,void * data,uint_t flags)780 hpc_nexus_disconnect(hpc_slot_t handle, void *data, uint_t flags)
781 {
782 	hpc_slot_entry_t *slotp;
783 
784 	ASSERT(handle != NULL);
785 	slotp = (hpc_slot_entry_t *)handle;
786 	if (slotp->slot_ops.hpc_op_disconnect)
787 		return (slotp->slot_ops.hpc_op_disconnect(slotp->slot_ops_arg,
788 			handle, data, flags));
789 	return (HPC_ERR_FAILED);
790 }
791 
792 
793 int
hpc_nexus_insert(hpc_slot_t handle,void * data,uint_t flags)794 hpc_nexus_insert(hpc_slot_t handle, void *data, uint_t flags)
795 {
796 	hpc_slot_entry_t *slotp;
797 
798 	ASSERT(handle != NULL);
799 	slotp = (hpc_slot_entry_t *)handle;
800 	if (slotp->slot_ops.hpc_op_insert)
801 		return (slotp->slot_ops.hpc_op_insert(slotp->slot_ops_arg,
802 			handle, data, flags));
803 	return (HPC_ERR_FAILED);
804 }
805 
806 
807 int
hpc_nexus_remove(hpc_slot_t handle,void * data,uint_t flags)808 hpc_nexus_remove(hpc_slot_t handle, void *data, uint_t flags)
809 {
810 	hpc_slot_entry_t *slotp;
811 
812 	ASSERT(handle != NULL);
813 	slotp = (hpc_slot_entry_t *)handle;
814 	if (slotp->slot_ops.hpc_op_remove)
815 		return (slotp->slot_ops.hpc_op_remove(slotp->slot_ops_arg,
816 			handle, data, flags));
817 	return (HPC_ERR_FAILED);
818 }
819 
820 
821 int
hpc_nexus_control(hpc_slot_t handle,int request,caddr_t arg)822 hpc_nexus_control(hpc_slot_t handle, int request, caddr_t arg)
823 {
824 	hpc_slot_entry_t *slotp;
825 
826 	ASSERT(handle != NULL);
827 	slotp = (hpc_slot_entry_t *)handle;
828 	if (slotp->slot_ops.hpc_op_control)
829 		return (slotp->slot_ops.hpc_op_control(slotp->slot_ops_arg,
830 			handle, request, arg));
831 	return (HPC_ERR_FAILED);
832 }
833 
834 /*
835  * The following function is run from the bus entries slot event handling
836  * thread.
837  */
838 static void
hpc_slot_event_dispatcher(hpc_bus_entry_t * busp)839 hpc_slot_event_dispatcher(hpc_bus_entry_t *busp)
840 {
841 	hpc_event_entry_t *eventp;
842 	hpc_slot_entry_t *slotp;
843 	int event;
844 	caddr_t arg;
845 	int (* func)(caddr_t, uint_t);
846 	callb_cpr_t cprinfo;
847 
848 	/*
849 	 * The creator of this thread is waiting to be signaled that
850 	 * the thread has been started.
851 	 */
852 	DEBUG1("hpc_slot_event_dispatcher: busp=%x", busp);
853 
854 	CALLB_CPR_INIT(&cprinfo, &busp->bus_mutex, callb_generic_cpr,
855 	    "hpc_slot_event_dispatcher");
856 
857 	mutex_enter(&busp->bus_mutex);
858 	/*
859 	 * Wait for events to queue and then process them.
860 	 */
861 	for (;;) {
862 
863 		/*
864 		 * Note we only hold the mutex while determining
865 		 * the number of entries that have been added to
866 		 * the event list, while updating the event list
867 		 * after processing the event list entries.
868 		 */
869 		if (busp->bus_slot_event_list_head == NULL) {
870 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
871 			cv_wait(&busp->bus_thread_cv, &busp->bus_mutex);
872 			CALLB_CPR_SAFE_END(&cprinfo, &busp->bus_mutex);
873 			if (busp->bus_thread_exit)
874 				break;
875 			continue;
876 		}
877 
878 		/*
879 		 * We have an event handler instance in the list to
880 		 * process.  Remove the head of the list, saving the
881 		 * information required to run the event handler.
882 		 * Then run the event handler while the bus mutex
883 		 * is released.
884 		 */
885 		eventp = busp->bus_slot_event_list_head;
886 		slotp = eventp->slotp;
887 		event = eventp->event;
888 		func = slotp->slot_event_handler;
889 		arg = slotp->slot_event_handler_arg;
890 		busp->bus_slot_event_list_head = eventp->next;
891 		hpc_free_event_entry(eventp);
892 		mutex_exit(&busp->bus_mutex);
893 		func(arg, event);
894 		mutex_enter(&busp->bus_mutex);
895 
896 		if (busp->bus_thread_exit)
897 			break;
898 	}
899 
900 	DEBUG0("hpc_slot_event_dispatcher: thread_exit");
901 	cv_signal(&busp->bus_thread_cv);
902 	CALLB_CPR_EXIT(&cprinfo);
903 	thread_exit();
904 }
905 
906 
907 static hpc_bus_entry_t *
hpc_find_bus_by_name(char * path)908 hpc_find_bus_by_name(char *path)
909 {
910 	hpc_bus_entry_t *busp;
911 
912 	for (busp = hpc_bus_list_head; busp != NULL; busp = busp->bus_next) {
913 		if (strcmp(path, busp->bus_name) == 0)
914 			break;
915 	}
916 	return (busp);
917 }
918 
919 boolean_t
hpc_bus_registered(hpc_slot_t slot_hdl)920 hpc_bus_registered(hpc_slot_t slot_hdl)
921 {
922 	hpc_slot_entry_t *slotp;
923 	hpc_bus_entry_t *busp;
924 
925 	slotp = (hpc_slot_entry_t *)slot_hdl;
926 	busp = slotp->slot_bus;
927 	return (busp->bus_registered);
928 }
929 
930 
931 #ifdef DEBUG
932 
933 extern void prom_printf(const char *, ...);
934 
935 static void
debug(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)936 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
937     uintptr_t a4, uintptr_t a5)
938 {
939 	if (hpcsvc_debug != 0) {
940 		cmn_err(CE_CONT, "hpcsvc: ");
941 		cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
942 		cmn_err(CE_CONT, "\n");
943 	}
944 }
945 #endif
946