xref: /illumos-gate/usr/src/lib/sun_fc/common/FCSyseventBridge.cc (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
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 
28 #include "FCSyseventBridge.h"
29 #include "Exceptions.h"
30 #include "Trace.h"
31 #include "AdapterAddEvent.h"
32 #include "AdapterEvent.h"
33 #include "AdapterPortEvent.h"
34 #include "AdapterDeviceEvent.h"
35 #include "TargetEvent.h"
36 #include "sun_fc.h"
37 #include <libnvpair.h>
38 #include <iostream>
39 
40 using namespace std;
41 
42 FCSyseventBridge* FCSyseventBridge::_instance = NULL;
43 
44 FCSyseventBridge* FCSyseventBridge::getInstance() {
45     Trace log("FCSyseventBridge::getInstance");
46     if (_instance == NULL) {
47 	_instance = new FCSyseventBridge();
48     }
49     return (_instance);
50 
51 }
52 
53 
54 void FCSyseventBridge::addListener(AdapterAddEventListener *listener) {
55     lock();
56     try {
57 	adapterAddEventListeners.insert(adapterAddEventListeners.begin(),
58 		listener);
59 	validateRegistration();
60 	unlock();
61     } catch (...) {
62 	unlock();
63 	throw;
64     }
65 }
66 void FCSyseventBridge::addListener(AdapterEventListener *listener, HBA *hba) {
67     lock();
68     try {
69 	adapterEventListeners.insert(adapterEventListeners.begin(), listener);
70 	validateRegistration();
71 	unlock();
72     } catch (...) {
73 	unlock();
74 	throw;
75     }
76 }
77 void FCSyseventBridge::addListener(AdapterPortEventListener *listener,
78 	    HBAPort *port) {
79     lock();
80     try {
81 	adapterPortEventListeners.insert(adapterPortEventListeners.begin(),
82 		listener);
83 	validateRegistration();
84 	unlock();
85     } catch (...) {
86 	unlock();
87 	throw;
88     }
89 }
90 void FCSyseventBridge::addListener(AdapterDeviceEventListener *listener,
91     HBAPort *port) {
92 	lock();
93 	try {
94 		adapterDeviceEventListeners.insert(adapterDeviceEventListeners.begin(),
95 		    listener);
96 		validateRegistration();
97 		unlock();
98 	} catch (...) {
99 		unlock();
100 		throw;
101 	}
102 }
103 void FCSyseventBridge::addListener(TargetEventListener *listener,
104 	    HBAPort *port, uint64_t targetWWN, bool filter) {
105     lock();
106     try {
107 	targetEventListeners.insert(targetEventListeners.begin(), listener);
108 	validateRegistration();
109 	unlock();
110     } catch (...) {
111 	unlock();
112 	throw;
113     }
114 }
115 
116 void FCSyseventBridge::removeListener(AdapterAddEventListener *listener) {
117     lock();
118     try {
119 	typedef vector<AdapterAddEventListener *>::iterator Iter;
120 	for (Iter tmp = adapterAddEventListeners.begin();
121 		tmp != adapterAddEventListeners.end(); tmp++) {
122 	    if (*tmp == listener) {
123 		adapterAddEventListeners.erase(tmp);
124 		unlock();
125 		return;
126 	    }
127 	}
128 	throw InvalidHandleException();
129     } catch (...) {
130 	unlock();
131 	throw;
132     }
133 }
134 
135 void FCSyseventBridge::removeListener(AdapterEventListener *listener) {
136     lock();
137     try {
138 	typedef vector<AdapterEventListener *>::iterator Iter;
139 	for (Iter tmp = adapterEventListeners.begin();
140 		tmp != adapterEventListeners.end(); tmp++) {
141 	    if (*tmp == listener) {
142 		adapterEventListeners.erase(tmp);
143 		unlock();
144 		return;
145 	    }
146 	}
147 	throw InvalidHandleException();
148     } catch (...) {
149 	unlock();
150 	throw;
151     }
152 }
153 
154 void FCSyseventBridge::removeListener(AdapterPortEventListener *listener) {
155     lock();
156     try {
157 	typedef vector<AdapterPortEventListener *>::iterator Iter;
158 	for (Iter tmp = adapterPortEventListeners.begin();
159 		tmp != adapterPortEventListeners.end(); tmp++) {
160 	    if (*tmp == listener) {
161 		adapterPortEventListeners.erase(tmp);
162 		unlock();
163 		return;
164 	    }
165 	}
166 	throw InvalidHandleException();
167     } catch (...) {
168 	unlock();
169 	throw;
170     }
171 }
172 
173 void FCSyseventBridge::removeListener(AdapterDeviceEventListener *listener) {
174 	lock();
175 	try {
176 		typedef vector<AdapterDeviceEventListener *>::iterator Iter;
177 		for (Iter tmp = adapterDeviceEventListeners.begin();
178 		    tmp != adapterDeviceEventListeners.end(); tmp++) {
179 			if (*tmp == listener) {
180 				adapterDeviceEventListeners.erase(tmp);
181 				unlock();
182 				return;
183 			}
184 		}
185 		throw InvalidHandleException();
186 	} catch (...) {
187 		unlock();
188 		throw;
189 	}
190 }
191 
192 void FCSyseventBridge::removeListener(TargetEventListener *listener) {
193     lock();
194     try {
195 	typedef vector<TargetEventListener *>::iterator Iter;
196 	for (Iter tmp = targetEventListeners.begin();
197 		tmp != targetEventListeners.end(); tmp++) {
198 	    if (*tmp == listener) {
199 		targetEventListeners.erase(tmp);
200 		unlock();
201 		return;
202 	    }
203 	}
204 	throw InvalidHandleException();
205     } catch (...) {
206 	unlock();
207 	throw;
208     }
209 }
210 
211 extern "C" void static_dispatch(sysevent_t *ev) {
212     Trace log("static_dispatch");
213     FCSyseventBridge::getInstance()->dispatch(ev);
214 }
215 
216 void FCSyseventBridge::dispatch(sysevent_t *ev) {
217     Trace log("FCSyseventBridge::dispatch");
218     nvlist_t		    *list = NULL;
219     hrtime_t			when;
220 
221     if (ev == NULL) {
222 	log.debug("Null event.");
223 	return;
224     }
225 
226     if (sysevent_get_attr_list(ev, &list) || list == NULL) {
227 	log.debug("Empty event.");
228 	return;
229     }
230 
231     string eventVendor = sysevent_get_vendor_name(ev);
232     string eventPublisher = sysevent_get_pub_name(ev);
233     string eventClass = sysevent_get_class_name(ev);
234     string eventSubClass = sysevent_get_subclass_name(ev);
235 
236     sysevent_get_time(ev, &when);
237 
238     // Now that we know what type of event it is, handle it accordingly
239     if (eventClass == "EC_sunfc") {
240 
241 	// All events of this class type have instance and port-wwn for
242 	// the HBA port.
243 	uint32_t	instance;
244 	if (nvlist_lookup_uint32(list, (char *)"instance",
245 		&instance)) {
246 	    log.genericIOError(
247 		"Improperly formed event: no instance field.");
248 	    nvlist_free(list);
249 	    return;
250 	}
251 	uchar_t		*rawPortWWN;
252 	uint32_t	rawPortWWNLength;
253 
254 	if (nvlist_lookup_byte_array(list, (char *)"port-wwn",
255 		&rawPortWWN, &rawPortWWNLength)) {
256 	    log.genericIOError(
257 		"Improperly formed event: no port-wwn field.");
258 	    nvlist_free(list);
259 	    return;
260 	}
261 
262 	// Now deal with the specific details of each subclass type
263 	if (eventSubClass == "ESC_sunfc_port_offline") {
264 
265 	    // Create event instance
266 	    AdapterPortEvent event(
267 		wwnConversion(rawPortWWN),
268 		AdapterPortEvent::OFFLINE,
269 		0);
270 
271 	    // Dispatch to interested parties.
272 	    lock();
273 	    try {
274 		typedef vector<AdapterPortEventListener *>::iterator Iter;
275 		for (Iter tmp = adapterPortEventListeners.begin();
276 			tmp != adapterPortEventListeners.end(); tmp++) {
277 		    (*tmp)->dispatch(event);
278 		}
279 	    } catch (...) {
280 		unlock();
281 		nvlist_free(list);
282 		throw;
283 	    }
284 	    unlock();
285 
286 	} else if (eventSubClass == "ESC_sunfc_port_online") {
287 
288 	    // Create event instance
289 	    AdapterPortEvent event(
290 		wwnConversion(rawPortWWN),
291 		AdapterPortEvent::ONLINE,
292 		0);
293 
294 	    // Dispatch to interested parties.
295 	    lock();
296 	    try {
297 		typedef vector<AdapterPortEventListener *>::iterator Iter;
298 		for (Iter tmp = adapterPortEventListeners.begin();
299 			tmp != adapterPortEventListeners.end(); tmp++) {
300 		    (*tmp)->dispatch(event);
301 		}
302 	    } catch (...) {
303 		unlock();
304 		nvlist_free(list);
305 		throw;
306 	    }
307 	    unlock();
308 
309 	} else if (eventSubClass == "ESC_sunfc_device_online") {
310 		AdapterDeviceEvent event(
311 		    wwnConversion(rawPortWWN),
312 		    AdapterDeviceEvent::ONLINE,
313 		    0);
314 		lock();
315 		try {
316 			typedef vector<AdapterDeviceEventListener *>::iterator Iter;
317 			for (Iter tmp = adapterDeviceEventListeners.begin();
318 			    tmp != adapterDeviceEventListeners.end(); tmp++) {
319 				(*tmp)->dispatch(event);
320 			}
321 		} catch (...) {
322 			unlock();
323 			nvlist_free(list);
324 			throw;
325 		}
326 		unlock();
327 
328 	} else if (eventSubClass == "ESC_sunfc_device_offline") {
329 		AdapterDeviceEvent event(
330 		    wwnConversion(rawPortWWN),
331 		    AdapterDeviceEvent::OFFLINE,
332 		    0);
333 		lock();
334 		try {
335 			typedef vector<AdapterDeviceEventListener *>::iterator Iter;
336 			for (Iter tmp = adapterDeviceEventListeners.begin();
337 			    tmp != adapterDeviceEventListeners.end(); tmp++) {
338 				(*tmp)->dispatch(event);
339 			}
340 		} catch (...) {
341 			unlock();
342 			nvlist_free(list);
343 			throw;
344 		}
345 		unlock();
346 
347 	} else if (eventSubClass == "ESC_sunfc_port_rscn") {
348 	    /*
349 	     * RSCNs are a little tricky.  There can be multiple
350 	     * affected page properties, each numbered.  To make sure
351 	     * we get them all, we loop through all properties
352 	     * in the nvlist and if their name begins with "affected_page_"
353 	     * then we send an event for them.
354 	     */
355 	    uint32_t	affected_page;
356 	    nvpair_t    *attr = NULL;
357 	    for (attr = nvlist_next_nvpair(list, NULL);
358 		    attr != NULL;
359 		    attr = nvlist_next_nvpair(list, attr)) {
360 		string name = nvpair_name(attr);
361 		if (name.find("affected_page_") != name.npos) {
362 
363 		    if (nvpair_value_uint32(attr, &affected_page)) {
364 			log.genericIOError(
365 			    "Improperly formed event: "
366 			    "corrupt affected_page field");
367 			continue;
368 		    }
369 		    // Create event instance
370 		    AdapterPortEvent event(
371 			wwnConversion(rawPortWWN),
372 			AdapterPortEvent::FABRIC,
373 			affected_page);
374 
375 		    // Dispatch to interested parties.
376 		    lock();
377 		    typedef vector<AdapterPortEventListener *>::iterator Iter;
378 		    try {
379 			for (Iter tmp = adapterPortEventListeners.begin();
380 				tmp != adapterPortEventListeners.end(); tmp++) {
381 			    (*tmp)->dispatch(event);
382 			}
383 		    } catch (...) {
384 			unlock();
385 			nvlist_free(list);
386 			throw;
387 		    }
388 		    unlock();
389 		}
390 	    }
391 	} else if (eventSubClass == "ESC_sunfc_target_add") {
392 	    uchar_t	*rawTargetPortWWN;
393 	    uint32_t	rawTargetPortWWNLength;
394 
395 	    if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn",
396 		    &rawTargetPortWWN, &rawTargetPortWWNLength)) {
397 		log.genericIOError(
398 		    "Improperly formed event: no target-port-wwn field.");
399 		nvlist_free(list);
400 		return;
401 	    }
402 
403 	    // Create event instance
404 	    AdapterPortEvent event(
405 		wwnConversion(rawPortWWN),
406 		AdapterPortEvent::NEW_TARGETS,
407 		0);
408 
409 	    // Dispatch to interested parties.
410 	    lock();
411 	    try {
412 		typedef vector<AdapterPortEventListener *>::iterator Iter;
413 		for (Iter tmp = adapterPortEventListeners.begin();
414 			tmp != adapterPortEventListeners.end(); tmp++) {
415 		    (*tmp)->dispatch(event);
416 		}
417 	    } catch (...) {
418 		unlock();
419 		nvlist_free(list);
420 		throw;
421 	    }
422 	    unlock();
423 	} else if (eventSubClass == "ESC_sunfc_target_remove") {
424 	    uchar_t	*rawTargetPortWWN;
425 	    uint32_t	rawTargetPortWWNLength;
426 
427 	    if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn",
428 		    &rawTargetPortWWN, &rawTargetPortWWNLength)) {
429 		log.genericIOError(
430 		    "Improperly formed event: no target-port-wwn field.");
431 		nvlist_free(list);
432 		return;
433 	    }
434 	    // Create event instance
435 	    TargetEvent event(
436 		wwnConversion(rawPortWWN),
437 		wwnConversion(rawTargetPortWWN),
438 		TargetEvent::REMOVED);
439 
440 	    // Dispatch to interested parties.
441 	    lock();
442 	    try {
443 		typedef vector<TargetEventListener *>::iterator Iter;
444 		for (Iter tmp = targetEventListeners.begin();
445 			tmp != targetEventListeners.end(); tmp++) {
446 		    (*tmp)->dispatch(event);
447 		}
448 	    } catch (...) {
449 		unlock();
450 		nvlist_free(list);
451 		throw;
452 	    }
453 	    unlock();
454 	} else if (eventSubClass == "ESC_sunfc_port_attach") {
455 	    // Create event instance
456 	    AdapterAddEvent event(wwnConversion(rawPortWWN));
457 	    // Dispatch to interested parties.
458 	    lock();
459 	    try {
460 		typedef vector<AdapterAddEventListener *>::iterator Iter;
461 		for (Iter tmp = adapterAddEventListeners.begin();
462 			tmp != adapterAddEventListeners.end(); tmp++) {
463 		    (*tmp)->dispatch(event);
464 		}
465 	    } catch (...) {
466 		unlock();
467 		nvlist_free(list);
468 		throw;
469 	    }
470 	    unlock();
471 	} else if (eventSubClass == "ESC_sunfc_port_detach") {
472 	    // Technically, we should probably try to coalesce
473 	    // all detach events for the same multi-ported adapter
474 	    // and only send one event to the client, but for now,
475 	    // we'll just blindly send duplicates.
476 
477 	    // Create event instance
478 	    AdapterEvent event(
479 		wwnConversion(rawPortWWN),
480 		AdapterEvent::REMOVE);
481 
482 	    // Dispatch to interested parties.
483 	    lock();
484 	    try {
485 		typedef vector<AdapterEventListener *>::iterator Iter;
486 		for (Iter tmp = adapterEventListeners.begin();
487 			tmp != adapterEventListeners.end(); tmp++) {
488 		    (*tmp)->dispatch(event);
489 		}
490 	    } catch (...) {
491 		unlock();
492 		nvlist_free(list);
493 		throw;
494 	    }
495 	    unlock();
496 
497 	} else {
498 	    log.genericIOError(
499 		    "Unrecognized subclass \"%s\": Ignoring event",
500 		    eventSubClass.c_str());
501 	}
502     } else {
503 	// This should not happen, as we only asked for specific classes.
504 	log.genericIOError(
505 		"Unrecognized class \"%s\": Ignoring event",
506 		eventClass.c_str());
507     }
508     nvlist_free(list);
509 }
510 
511 void FCSyseventBridge::validateRegistration() {
512     Trace log("FCSyseventBridge::validateRegistration");
513     uint64_t count = 0;
514     count = adapterAddEventListeners.size() +
515 	    adapterEventListeners.size() +
516 	    adapterPortEventListeners.size() +
517 	    targetEventListeners.size();
518     if (count == 1) {
519 	handle = sysevent_bind_handle(static_dispatch);
520 	if (handle == NULL) {
521 	    log.genericIOError(
522 		"Unable to bind sysevent handle.");
523 	    return;
524 	}
525 	const char *subclass_list[9] = {
526 		"ESC_sunfc_port_attach",
527 		"ESC_sunfc_port_detach",
528 		"ESC_sunfc_port_offline",
529 		"ESC_sunfc_port_online",
530 		"ESC_sunfc_port_rscn",
531 		"ESC_sunfc_target_add",
532 		"ESC_sunfc_target_remove",
533 		"ESC_sunfc_device_online",
534 		"ESC_sunfc_device_offline"
535 	    };
536 	if (sysevent_subscribe_event(handle,
537 		"EC_sunfc", (const char **)subclass_list, 9)) {
538 	    log.genericIOError(
539 		"Unable to subscribe to sun_fc events.");
540 	    sysevent_unbind_handle(handle);
541 	    handle = NULL;
542 	}
543     } else if (count == 0 && handle != NULL) {
544 	// Remove subscription
545 	sysevent_unbind_handle(handle);
546 	handle == NULL;
547     } // Else do nothing
548 }
549 
550 int32_t FCSyseventBridge::getMaxListener() {
551     return (INT_MAX);
552 }
553