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