/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "FCSyseventBridge.h" #include "Exceptions.h" #include "Trace.h" #include "AdapterAddEvent.h" #include "AdapterEvent.h" #include "AdapterPortEvent.h" #include "AdapterDeviceEvent.h" #include "TargetEvent.h" #include "sun_fc.h" #include #include using namespace std; FCSyseventBridge* FCSyseventBridge::_instance = NULL; FCSyseventBridge* FCSyseventBridge::getInstance() { Trace log("FCSyseventBridge::getInstance"); if (_instance == NULL) { _instance = new FCSyseventBridge(); } return (_instance); } void FCSyseventBridge::addListener(AdapterAddEventListener *listener) { lock(); try { adapterAddEventListeners.insert(adapterAddEventListeners.begin(), listener); validateRegistration(); unlock(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::addListener(AdapterEventListener *listener, HBA *hba) { lock(); try { adapterEventListeners.insert(adapterEventListeners.begin(), listener); validateRegistration(); unlock(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::addListener(AdapterPortEventListener *listener, HBAPort *port) { lock(); try { adapterPortEventListeners.insert(adapterPortEventListeners.begin(), listener); validateRegistration(); unlock(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::addListener(AdapterDeviceEventListener *listener, HBAPort *port) { lock(); try { adapterDeviceEventListeners.insert(adapterDeviceEventListeners.begin(), listener); validateRegistration(); unlock(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::addListener(TargetEventListener *listener, HBAPort *port, uint64_t targetWWN, bool filter) { lock(); try { targetEventListeners.insert(targetEventListeners.begin(), listener); validateRegistration(); unlock(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::removeListener(AdapterAddEventListener *listener) { lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterAddEventListeners.begin(); tmp != adapterAddEventListeners.end(); tmp++) { if (*tmp == listener) { adapterAddEventListeners.erase(tmp); unlock(); return; } } throw InvalidHandleException(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::removeListener(AdapterEventListener *listener) { lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterEventListeners.begin(); tmp != adapterEventListeners.end(); tmp++) { if (*tmp == listener) { adapterEventListeners.erase(tmp); unlock(); return; } } throw InvalidHandleException(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::removeListener(AdapterPortEventListener *listener) { lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterPortEventListeners.begin(); tmp != adapterPortEventListeners.end(); tmp++) { if (*tmp == listener) { adapterPortEventListeners.erase(tmp); unlock(); return; } } throw InvalidHandleException(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::removeListener(AdapterDeviceEventListener *listener) { lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterDeviceEventListeners.begin(); tmp != adapterDeviceEventListeners.end(); tmp++) { if (*tmp == listener) { adapterDeviceEventListeners.erase(tmp); unlock(); return; } } throw InvalidHandleException(); } catch (...) { unlock(); throw; } } void FCSyseventBridge::removeListener(TargetEventListener *listener) { lock(); try { typedef vector::iterator Iter; for (Iter tmp = targetEventListeners.begin(); tmp != targetEventListeners.end(); tmp++) { if (*tmp == listener) { targetEventListeners.erase(tmp); unlock(); return; } } throw InvalidHandleException(); } catch (...) { unlock(); throw; } } extern "C" void static_dispatch(sysevent_t *ev) { Trace log("static_dispatch"); FCSyseventBridge::getInstance()->dispatch(ev); } void FCSyseventBridge::dispatch(sysevent_t *ev) { Trace log("FCSyseventBridge::dispatch"); nvlist_t *list = NULL; hrtime_t when; if (ev == NULL) { log.debug("Null event."); return; } if (sysevent_get_attr_list(ev, &list) || list == NULL) { log.debug("Empty event."); return; } string eventVendor = sysevent_get_vendor_name(ev); string eventPublisher = sysevent_get_pub_name(ev); string eventClass = sysevent_get_class_name(ev); string eventSubClass = sysevent_get_subclass_name(ev); sysevent_get_time(ev, &when); // Now that we know what type of event it is, handle it accordingly if (eventClass == "EC_sunfc") { // All events of this class type have instance and port-wwn for // the HBA port. uint32_t instance; if (nvlist_lookup_uint32(list, (char *)"instance", &instance)) { log.genericIOError( "Improperly formed event: no instance field."); nvlist_free(list); return; } uchar_t *rawPortWWN; uint32_t rawPortWWNLength; if (nvlist_lookup_byte_array(list, (char *)"port-wwn", &rawPortWWN, &rawPortWWNLength)) { log.genericIOError( "Improperly formed event: no port-wwn field."); nvlist_free(list); return; } // Now deal with the specific details of each subclass type if (eventSubClass == "ESC_sunfc_port_offline") { // Create event instance AdapterPortEvent event( wwnConversion(rawPortWWN), AdapterPortEvent::OFFLINE, 0); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterPortEventListeners.begin(); tmp != adapterPortEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_port_online") { // Create event instance AdapterPortEvent event( wwnConversion(rawPortWWN), AdapterPortEvent::ONLINE, 0); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterPortEventListeners.begin(); tmp != adapterPortEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_device_online") { AdapterDeviceEvent event( wwnConversion(rawPortWWN), AdapterDeviceEvent::ONLINE, 0); lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterDeviceEventListeners.begin(); tmp != adapterDeviceEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_device_offline") { AdapterDeviceEvent event( wwnConversion(rawPortWWN), AdapterDeviceEvent::OFFLINE, 0); lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterDeviceEventListeners.begin(); tmp != adapterDeviceEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_port_rscn") { /* * RSCNs are a little tricky. There can be multiple * affected page properties, each numbered. To make sure * we get them all, we loop through all properties * in the nvlist and if their name begins with "affected_page_" * then we send an event for them. */ uint32_t affected_page; nvpair_t *attr = NULL; for (attr = nvlist_next_nvpair(list, NULL); attr != NULL; attr = nvlist_next_nvpair(list, attr)) { string name = nvpair_name(attr); if (name.find("affected_page_") != name.npos) { if (nvpair_value_uint32(attr, &affected_page)) { log.genericIOError( "Improperly formed event: " "corrupt affected_page field"); continue; } // Create event instance AdapterPortEvent event( wwnConversion(rawPortWWN), AdapterPortEvent::FABRIC, affected_page); // Dispatch to interested parties. lock(); typedef vector::iterator Iter; try { for (Iter tmp = adapterPortEventListeners.begin(); tmp != adapterPortEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } } } else if (eventSubClass == "ESC_sunfc_target_add") { uchar_t *rawTargetPortWWN; uint32_t rawTargetPortWWNLength; if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn", &rawTargetPortWWN, &rawTargetPortWWNLength)) { log.genericIOError( "Improperly formed event: no target-port-wwn field."); nvlist_free(list); return; } // Create event instance AdapterPortEvent event( wwnConversion(rawPortWWN), AdapterPortEvent::NEW_TARGETS, 0); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterPortEventListeners.begin(); tmp != adapterPortEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_target_remove") { uchar_t *rawTargetPortWWN; uint32_t rawTargetPortWWNLength; if (nvlist_lookup_byte_array(list, (char *)"target-port-wwn", &rawTargetPortWWN, &rawTargetPortWWNLength)) { log.genericIOError( "Improperly formed event: no target-port-wwn field."); nvlist_free(list); return; } // Create event instance TargetEvent event( wwnConversion(rawPortWWN), wwnConversion(rawTargetPortWWN), TargetEvent::REMOVED); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = targetEventListeners.begin(); tmp != targetEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_port_attach") { // Create event instance AdapterAddEvent event(wwnConversion(rawPortWWN)); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterAddEventListeners.begin(); tmp != adapterAddEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else if (eventSubClass == "ESC_sunfc_port_detach") { // Technically, we should probably try to coalesce // all detach events for the same multi-ported adapter // and only send one event to the client, but for now, // we'll just blindly send duplicates. // Create event instance AdapterEvent event( wwnConversion(rawPortWWN), AdapterEvent::REMOVE); // Dispatch to interested parties. lock(); try { typedef vector::iterator Iter; for (Iter tmp = adapterEventListeners.begin(); tmp != adapterEventListeners.end(); tmp++) { (*tmp)->dispatch(event); } } catch (...) { unlock(); nvlist_free(list); throw; } unlock(); } else { log.genericIOError( "Unrecognized subclass \"%s\": Ignoring event", eventSubClass.c_str()); } } else { // This should not happen, as we only asked for specific classes. log.genericIOError( "Unrecognized class \"%s\": Ignoring event", eventClass.c_str()); } nvlist_free(list); } void FCSyseventBridge::validateRegistration() { Trace log("FCSyseventBridge::validateRegistration"); uint64_t count = 0; count = adapterAddEventListeners.size() + adapterEventListeners.size() + adapterPortEventListeners.size() + targetEventListeners.size(); if (count == 1) { handle = sysevent_bind_handle(static_dispatch); if (handle == NULL) { log.genericIOError( "Unable to bind sysevent handle."); return; } const char *subclass_list[9] = { "ESC_sunfc_port_attach", "ESC_sunfc_port_detach", "ESC_sunfc_port_offline", "ESC_sunfc_port_online", "ESC_sunfc_port_rscn", "ESC_sunfc_target_add", "ESC_sunfc_target_remove", "ESC_sunfc_device_online", "ESC_sunfc_device_offline" }; if (sysevent_subscribe_event(handle, "EC_sunfc", (const char **)subclass_list, 9)) { log.genericIOError( "Unable to subscribe to sun_fc events."); sysevent_unbind_handle(handle); handle = NULL; } } else if (count == 0 && handle != NULL) { // Remove subscription sysevent_unbind_handle(handle); handle == NULL; } // Else do nothing } int32_t FCSyseventBridge::getMaxListener() { return (INT_MAX); }