summaryrefslogtreecommitdiff
path: root/usr/src/lib/sun_sas/common/event.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/sun_sas/common/event.c')
-rw-r--r--usr/src/lib/sun_sas/common/event.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/usr/src/lib/sun_sas/common/event.c b/usr/src/lib/sun_sas/common/event.c
new file mode 100644
index 0000000000..a4030d5d1b
--- /dev/null
+++ b/usr/src/lib/sun_sas/common/event.c
@@ -0,0 +1,268 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sun_sas.h>
+#include <libsysevent.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+
+/* Remove these 5 when the header containing the event names aver available. */
+/*
+ * Event definitions
+ */
+/* Event Class */
+#define EC_HBA "EC_hba"
+#define EC_DR "EC_dr"
+/* Event Sub-Class */
+#define ESC_SAS_HBA_PORT_BROADCAST "ESC_sas_hba_port_broadcast"
+#define ESC_SAS_PHY_EVENT "ESC_sas_phy_event"
+#define ESC_DR_TARGET_STATE_CHANGE "ESC_dr_target_state_change"
+
+/* Broadcast Event Types */
+#define SAS_PORT_BROADCAST_CHANGE "port_broadcast_change"
+#define SAS_PORT_BROADCAST_SES "port_broadcast_ses"
+#define SAS_PORT_BROADCAST_D24_0 "port_broadcast_d24_0"
+#define SAS_PORT_BROADCAST_D27_4 "port_broadcast_d27_4"
+#define SAS_PORT_BROADCAST_D01_4 "port_broadcast_d01_4"
+#define SAS_PORT_BROADCAST_D04_7 "port_broadcast_d04_7"
+#define SAS_PORT_BROADCAST_D16_7 "port_broadcast_d16_7"
+#define SAS_PORT_BROADCAST_D29_7 "port_broadcast_d29_7"
+
+/* Phy Event Types */
+#define SAS_PHY_ONLINE "port_online"
+#define SAS_PHY_OFFLINE "port_offline"
+#define SAS_PHY_REMOVE "port_remove"
+
+/* Event payload */
+#define SAS_DRV_INST "driver_instance"
+#define SAS_PORT_ADDR "port_address"
+#define SAS_DEVFS_PATH "devfs_path"
+#define SAS_EVENT_TYPE "event_type"
+
+#define HBA_PORT_MATCH 1
+#define TARGET_PORT_MATCH 2
+#define PHY_MATCH 3
+
+#define REMOVED 1
+#define ONLINE 2
+#define OFFLINE 3
+
+sysevent_handle_t *gSysEventHandle = NULL;
+
+/* Calls the client callback function, if one is registered */
+static HBA_STATUS
+updateMatchingPhy(HBA_WWN portAddr, uint8_t phyId, int update, uint8_t linkRate)
+{
+ const char ROUTINE[] = "updateMatchingPhy";
+ struct sun_sas_hba *hba_ptr;
+ struct sun_sas_port *hba_port_ptr;
+ struct phy_info *phy_ptr;
+
+ log(LOG_DEBUG, ROUTINE, "- phy matching");
+ /* grab write lock */
+ lock(&all_hbas_lock);
+ /* loop through HBAs */
+ for (hba_ptr = global_hba_head; hba_ptr != NULL;
+ hba_ptr = hba_ptr->next) {
+ /* loop through HBA ports */
+ for (hba_port_ptr = hba_ptr->first_port;
+ hba_port_ptr != NULL;
+ hba_port_ptr = hba_port_ptr->next) {
+ if (wwnConversion(hba_port_ptr->
+ port_attributes.PortSpecificAttribute.
+ SASPort->LocalSASAddress.wwn) ==
+ wwnConversion(portAddr.wwn)) {
+ /* loop through phys */
+ for (phy_ptr = hba_port_ptr->first_phy;
+ phy_ptr != NULL; phy_ptr =
+ phy_ptr->next) {
+ if (phy_ptr->phy.PhyIdentifier ==
+ phyId) {
+ if (update == REMOVED) {
+ phy_ptr->invalid =
+ B_TRUE;
+ } else if (update == OFFLINE) {
+ phy_ptr->phy.
+ NegotiatedLinkRate
+ = 0;
+ } else { /* online */
+ phy_ptr->phy.
+ NegotiatedLinkRate
+ = linkRate;
+ }
+ unlock(&all_hbas_lock);
+ return (HBA_STATUS_OK);
+ }
+ } /* for phys */
+ } /* wwn mismatch. continue */
+ } /* for HBA ports */
+ } /* for HBAs */
+
+ unlock(&all_hbas_lock);
+ return (HBA_STATUS_ERROR);
+}
+
+/* Event handler called by system */
+static void
+syseventHandler(sysevent_t *ev)
+{
+
+ const char ROUTINE[] = "syseventHandler";
+ nvlist_t *attrList = NULL;
+ char *eventStr, *portAddrStr, *charptr;
+ int update;
+ uint64_t addr;
+ uint8_t phyId, linkRate;
+ HBA_WWN portAddr;
+
+ /* Is the event one of ours? */
+ if (strncmp(EC_HBA, sysevent_get_class_name(ev), strlen(EC_HBA)) == 0) {
+ /* handle phy events */
+ if (strncmp(ESC_SAS_PHY_EVENT, sysevent_get_subclass_name(ev),
+ strlen(ESC_SAS_PHY_EVENT)) == 0) {
+ if (sysevent_get_attr_list(ev, &attrList) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "Failed to get event attributes on %s/%s",
+ EC_HBA, ESC_SAS_PHY_EVENT);
+ return;
+ } else {
+ if (nvlist_lookup_string(attrList,
+ "event_type", &eventStr) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "Event type not found");
+ return;
+ } else {
+ if (strncmp(eventStr, "phy_online",
+ sizeof (eventStr)) == 0) {
+ update = ONLINE;
+ if (nvlist_lookup_uint8(
+ attrList, "link_rate",
+ &linkRate) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "Link Rate not \
+ found");
+ return;
+ }
+ } else if (strncmp(eventStr,
+ "phy_offline",
+ sizeof (eventStr)) == 0) {
+ update = OFFLINE;
+ } else if (strncmp(eventStr,
+ "phy_remove",
+ sizeof (eventStr)) == 0) {
+ update = REMOVED;
+ } else {
+ log(LOG_DEBUG, ROUTINE,
+ "Invalid event type");
+ return;
+ }
+ }
+ if (nvlist_lookup_string(attrList,
+ "port_address", &portAddrStr) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "Port SAS address not found");
+ return;
+ } else {
+ for (charptr = portAddrStr;
+ charptr != NULL; charptr++) {
+ if (isxdigit(*charptr)) {
+ break;
+ }
+ }
+ addr = htonll(strtoll(charptr,
+ NULL, 16));
+ (void) memcpy(portAddr.wwn, &addr, 8);
+ }
+ if (nvlist_lookup_uint8(attrList,
+ "PhyIdentifier", &phyId) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "Port SAS address not found");
+ return;
+ }
+ }
+ if (updateMatchingPhy(portAddr, phyId, update,
+ linkRate) != HBA_STATUS_OK) {
+ log(LOG_DEBUG, ROUTINE,
+ "updating phy for the events failed.");
+ }
+ }
+ } else if (strncmp(EC_DR, sysevent_get_class_name(ev), 2) == 0) {
+ /* handle DR events */
+ log(LOG_DEBUG, ROUTINE,
+ "handle EC_dr events.");
+ } else {
+ log(LOG_DEBUG, ROUTINE,
+ "Found Unregistered event. - exit");
+ return;
+ }
+
+ log(LOG_DEBUG, ROUTINE, "- exit");
+}
+
+/* Registers events to the sysevent framework */
+HBA_STATUS
+registerSysevent() {
+
+ const char ROUTINE[] = "registerSysevent";
+ const char *hba_subclass_list[] = {
+ ESC_SAS_PHY_EVENT
+ };
+ const char *dr_subclass_list[] = {
+ ESC_DR_TARGET_STATE_CHANGE
+ };
+
+ gSysEventHandle = sysevent_bind_handle(syseventHandler);
+ if (gSysEventHandle == NULL) {
+ log(LOG_DEBUG, ROUTINE,
+ "- sysevent_bind_handle() failed");
+ log(LOG_DEBUG, ROUTINE, "- error exit");
+ return (HBA_STATUS_ERROR);
+ }
+
+ if (sysevent_subscribe_event(gSysEventHandle, EC_HBA,
+ hba_subclass_list, 1) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "- sysevent_subscribe_event() failed for EC_HBA subclass");
+ log(LOG_DEBUG, ROUTINE, "- error exit");
+ sysevent_unbind_handle(gSysEventHandle);
+ return (HBA_STATUS_ERROR);
+ }
+
+ if (sysevent_subscribe_event(gSysEventHandle, EC_DR,
+ dr_subclass_list, 1) != 0) {
+ log(LOG_DEBUG, ROUTINE,
+ "- sysevent_subscribe_event() failed for DR subclass");
+ log(LOG_DEBUG, ROUTINE, "- error exit");
+ sysevent_unbind_handle(gSysEventHandle);
+ return (HBA_STATUS_ERROR);
+ }
+
+ log(LOG_DEBUG, ROUTINE, "- exit");
+
+ return (HBA_STATUS_ERROR);
+}