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
39using namespace std;
40
41FCSyseventBridge* FCSyseventBridge::_instance = NULL;
42
43FCSyseventBridge* FCSyseventBridge::getInstance() {
44    Trace log("FCSyseventBridge::getInstance");
45    if (_instance == NULL) {
46	_instance = new FCSyseventBridge();
47    }
48    return (_instance);
49
50}
51
52
53void 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}
65void 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}
76void 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}
89void 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}
102void 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
115void 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
134void 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
153void 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
172void 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
191void 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
210extern "C" void static_dispatch(sysevent_t *ev) {
211    Trace log("static_dispatch");
212    FCSyseventBridge::getInstance()->dispatch(ev);
213}
214
215void 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
510void 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
549int32_t FCSyseventBridge::getMaxListener() {
550    return (INT_MAX);
551}
552