/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/ib/mgt/ibmf/ibmf_saa_impl.h>
#include <sys/ib/mgt/ibmf/ibmf_saa_utils.h>
extern saa_state_t *saa_statep;
extern int ibmf_trace_level;
static void
ibmf_saa_informinfo_cb(void *arg, size_t length, char *buffer,
int status, uint32_t producer_type);
static int
ibmf_saa_send_informinfo(saa_port_t *saa_portp, uint32_t producer_type,
boolean_t subscribe, boolean_t unseq_unsubscribe);
static void
ibmf_saa_notify_event_client_task(void *args);
static void
ibmf_saa_process_subnet_event(saa_port_t *saa_portp, ib_mad_notice_t *notice);
/*
* ibmf_saa_subscribe_events:
* Subscribe or unsubscribe to subnet events for a certain port.
* ibmf_saa_subscribe_events() will send an InformInfo request for each of the
* four notice producer types.
*
* Subscribes generally occur when the first client for a port opens a session
* and when a port with registered ibmf_saa clients transitions to active.
* Subscribes are done as asynchronous, sequenced transactions.
*
* ibmf_saa sends unsubscribe requests when the last client for a port
* unregisters and when an CI_OFFLINE message is received from ibtf (via ibmf).
* For the first case, the unsubscribe is done as an asynchronous, sequenced
* transaction. For the second case, the request is asynchronous, unsequenced.
* This means that the unsubscribes will not be retried. Because the port is
* going away we cannot wait for responses. Unsubscribes are not required
* anyway as the SA will remove subscription records from ports it determines to
* be down.
*
* For subscribe requests, clients are notified that the request failed through
* the event notification mechanism. For unsubscribe requests, clients are not
* notified if the request fails. Therefore, this function returns void.
*
* Input Arguments
* saa_portp pointer to port state structure
* subscribe B_TRUE if request is a Subscribe, B_FALSE if unsubscribe
* unseq_unsubscribe B_TRUE if unsubscribe request should be unsequenced
* (called from CI_OFFLINE event handler)
* B_FALSE if sequenced (wait for response) or for all
* subscribe requests
*
* Output Arguments
* none
*
* Returns
* void
*/
void
ibmf_saa_subscribe_events(saa_port_t *saa_portp, boolean_t subscribe,
boolean_t unseq_unsubscribe)
{
int res;
ibmf_saa_event_details_t event_details;
boolean_t notify_clients = B_FALSE;
uint8_t success_mask;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_subscribe_events_start, IBMF_TNF_TRACE, "",
"ibmf_saa_subscribe_events() enter\n");
/* subscribes should always be sychronous */
ASSERT((subscribe == B_FALSE) || (unseq_unsubscribe == B_FALSE));
/*
* reset the arrive and success masks to indicate no responses have come
* back; technically only used for subscriptions but reset the values
* anyway
*/
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
success_mask = saa_portp->saa_pt_event_sub_success_mask;
saa_portp->saa_pt_event_sub_arrive_mask = 0;
saa_portp->saa_pt_event_sub_success_mask = 0;
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
/*
* now subscribe/unsubscribe for each of the notice producer types;
* send_informinfo returns 1 on success, 0 on failure. If the "or" of
* all four results is 0 then none of the informinfo's succeed and we
* should notify the client. If it's not 0, then informinfo_cb will be
* called at least once, taking care of notifying the clients that there
* was a failure.
* For each producer type, send the request only if it's a subscribe or
* if it's an unsubscribe for a subscribe which succeeded
*/
/*
* subscribe for all traps generated by the SM;
* gid in service/out of service, mgid created/deleted, etc.
*/
if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM) ||
(subscribe == B_TRUE))
res = ibmf_saa_send_informinfo(saa_portp,
MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT, subscribe,
unseq_unsubscribe);
/* subscribe for all traps generated by a CA */
if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA) ||
(subscribe == B_TRUE))
res |= ibmf_saa_send_informinfo(saa_portp,
MAD_INFORMINFO_NODETYPE_CA, subscribe, unseq_unsubscribe);
/* subscribe for all traps generated by a switch */
if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH) ||
(subscribe == B_TRUE))
res |= ibmf_saa_send_informinfo(saa_portp,
MAD_INFORMINFO_NODETYPE_SWITCH, subscribe,
unseq_unsubscribe);
/* subscribe for all traps generated by a router */
if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER) ||
(subscribe == B_TRUE))
res |= ibmf_saa_send_informinfo(saa_portp,
MAD_INFORMINFO_NODETYPE_ROUTER, subscribe,
unseq_unsubscribe);
/* if none of the subscribe requests succeeded notify the clients */
if ((res == 0) && (subscribe == B_TRUE)) {
IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_subscribe_events_err, IBMF_TNF_ERROR, "",
"ibmf_saa_subscribe_events: %s\n", tnf_string, msg,
"Could not subscribe for any of the four producer types");
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
/* all events should have "arrived" */
ASSERT(saa_portp->saa_pt_event_sub_arrive_mask ==
IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE);
/* status mask should be 0 since all failed */
ASSERT(saa_portp->saa_pt_event_sub_success_mask == 0);
/* notify clients if success mask changed */
if (saa_portp->saa_pt_event_sub_last_success_mask !=
saa_portp->saa_pt_event_sub_success_mask)
notify_clients = B_TRUE;
/* update last mask for next set of subscription requests */
saa_portp->saa_pt_event_sub_last_success_mask =
saa_portp->saa_pt_event_sub_arrive_mask = 0;
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
mutex_enter(&saa_portp->saa_pt_mutex);
/*
* Sending the four InformInfos is treated as one port client
* reference. Now that all have returned decrement the
* reference count.
*/
ASSERT(saa_portp->saa_pt_reference_count > 0);
saa_portp->saa_pt_reference_count--;
mutex_exit(&saa_portp->saa_pt_mutex);
}
/*
* for unsequenced unsubscribes, decrement the reference count here
* since no callbacks will ever do it
*/
if (unseq_unsubscribe == B_TRUE) {
mutex_enter(&saa_portp->saa_pt_mutex);
/*
* Sending the four InformInfos is treated as one port client
* reference. Now that all have returned decrement the
* reference count.
*/
ASSERT(saa_portp->saa_pt_reference_count > 0);
saa_portp->saa_pt_reference_count--;
mutex_exit(&saa_portp->saa_pt_mutex);
}
if (notify_clients == B_TRUE) {
bzero(&event_details, sizeof (ibmf_saa_event_details_t));
ibmf_saa_notify_event_clients(saa_portp, &event_details,
IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, NULL);
}
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_saa_subscribe_events_end,
IBMF_TNF_TRACE, "", "ibmf_saa_subscribe_events() exit\n");
}
/*
* ibmf_saa_send_informinfo:
*
* Sends an InformInfo request to the SA. There are two types of request,
* Subscribes and Unsubscribes. This function is called from
* ibmf_saa_subscribe_events. See that function's comment for usage of
* subscribe, unseq_unsubscribe booleans.
*
* This function generates a standard ibmf_saa transaction and sends using
* ibmf_saa_impl_send_request(). For asynchronous callbacks, the function
* ibmf_saa_informinfo_cb() will be called.
*
* This function blocks allocating resources, but not waiting for response
* packets.
*
* Input Arguments
* saa_portp pointer to port data
* producer_type InformInfo producer type to subscribe for
* subscribe B_TRUE if subscribe request, B_FALSE if unsubscribe
* unseq_unsubscribe B_TRUE if unsubscribe request should be unsequenced
* (called from CI_OFFLINE event handler)
* B_FALSE if sequenced (wait for response) or for all
* subscribe requests
*
* Output Arguments
* none
*
* Returns
* 1 if the transaction succeeded, 0 if it failed
*/
static int
ibmf_saa_send_informinfo(saa_port_t *saa_portp, uint32_t producer_type,
boolean_t subscribe, boolean_t unseq_unsubscribe)
{
ib_mad_informinfo_t inform_info;
saa_impl_trans_info_t *trans_info;
int res;
uint8_t producer_type_mask;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_send_informinfo_start, IBMF_TNF_TRACE, "",
"ibmf_saa_send_informinfo() enter\n");
IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_saa_send_informinfo,
IBMF_TNF_TRACE, "", "ibmf_saa_send_informinfo: %s, producer_type ="
"%x, subscribe = %x, unseq_unsubscribe = %x\n",
tnf_string, msg, "Sending informinfo request",
tnf_opaque, producer_type, producer_type,
tnf_int, subscribe, subscribe,
tnf_int, unseq_unsubscribe, unseq_unsubscribe);
bzero(&inform_info, sizeof (ib_mad_informinfo_t));
/* initialize inform_info packet */
inform_info.LIDRangeBegin = MAD_INFORMINFO_ALL_ENDPORTS_RANGE;
inform_info.IsGeneric = MAD_INFORMINFO_FORWARD_GENERIC;
if (subscribe == B_TRUE)
inform_info.Subscribe = MAD_INFORMINFO_SUBSCRIBE;
else {
inform_info.Subscribe = MAD_INFORMINFO_UNSUBSCRIBE;
inform_info.QPN = saa_portp->saa_pt_qpn;
}
inform_info.Type = MAD_INFORMINFO_TRAP_TYPE_FORWARD_ALL;
inform_info.TrapNumber_DeviceID =
MAD_INFORMINFO_TRAP_NUMBER_FORWARD_ALL;
inform_info.ProducerType_VendorID = producer_type;
trans_info = kmem_zalloc(sizeof (saa_impl_trans_info_t), KM_SLEEP);
/* no specific client associated with this transaction */
trans_info->si_trans_client_data = NULL;
trans_info->si_trans_port = saa_portp;
trans_info->si_trans_method = SA_SUBN_ADM_SET;
trans_info->si_trans_attr_id = SA_INFORMINFO_ATTRID;
trans_info->si_trans_component_mask = 0;
trans_info->si_trans_template = &inform_info;
trans_info->si_trans_template_length = sizeof (ib_mad_informinfo_t);
trans_info->si_trans_unseq_unsubscribe = unseq_unsubscribe;
/*
* if this isn't an unsequenced unsubscribe (the only synchronous
* request) then set up the callback
*/
if (unseq_unsubscribe == B_FALSE) {
trans_info->si_trans_sub_callback =
ibmf_saa_informinfo_cb;
trans_info->si_trans_callback_arg = saa_portp;
/*
* if this is a subscribe, set the producer type so we can know
* which one's failed
*/
if (subscribe == B_TRUE) {
trans_info->si_trans_sub_producer_type = producer_type;
}
}
mutex_enter(&saa_portp->saa_pt_kstat_mutex);
IBMF_SAA_ADD32_KSTATS(saa_portp, outstanding_requests, 1);
IBMF_SAA_ADD32_KSTATS(saa_portp, total_requests, 1);
mutex_exit(&saa_portp->saa_pt_kstat_mutex);
res = ibmf_saa_impl_send_request(trans_info);
if (res != IBMF_SUCCESS) {
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_send_informinfo, IBMF_TNF_ERROR, "",
"ibmf_saa_send_informinfo: %s, ibmf_status = %d\n",
tnf_string, msg, "ibmf_saa_impl_send_request() failed",
tnf_int, ibmf_status, res);
res = 0;
} else {
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_send_informinfo, IBMF_TNF_TRACE, "",
"ibmf_saa_send_informinfo: %s\n", tnf_string, msg,
"Request sent successfully");
res = 1;
/*
* if this was an asynchronous transaction (not the unsequenced
* unsubscribe case) return here.
* The callback will clean up everything.
*/
if (unseq_unsubscribe == B_FALSE) {
goto bail;
}
}
kmem_free(trans_info, sizeof (saa_impl_trans_info_t));
mutex_enter(&saa_portp->saa_pt_kstat_mutex);
IBMF_SAA_SUB32_KSTATS(saa_portp, outstanding_requests, 1);
IBMF_SAA_ADD32_KSTATS(saa_portp, failed_requests, 1);
mutex_exit(&saa_portp->saa_pt_kstat_mutex);
/*
* if subscribe transaction failed, update status mask
* to indicate "response"
*/
if ((res == 0) && (subscribe == B_TRUE)) {
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
saa_portp->saa_pt_event_sub_arrive_mask = 0;
saa_portp->saa_pt_event_sub_success_mask = 0;
switch (producer_type) {
case MAD_INFORMINFO_NODETYPE_CA:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA;
break;
case MAD_INFORMINFO_NODETYPE_SWITCH:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH;
break;
case MAD_INFORMINFO_NODETYPE_ROUTER:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER;
break;
case MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM;
break;
default:
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_send_informinfo, IBMF_TNF_ERROR,
"", "ibmf_saa_send_informinfo: %s, "
"producer_type = 0x%x\n",
tnf_string, msg, "Unknown producer type",
tnf_opaque, producer_type, producer_type);
ASSERT(0);
producer_type_mask = 0;
break;
}
saa_portp->saa_pt_event_sub_arrive_mask |= producer_type_mask;
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
}
bail:
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_send_informinfo_end, IBMF_TNF_TRACE, "",
"ibmf_saa_send_informinfo() exit: result = 0x%x\n",
tnf_opaque, result, res);
return (res);
}
/*
* ibmf_saa_informinfo_cb:
*
* Called when the asynchronous informinfo request receives its response.
* Checks the status (whether the ibmf_saa was able to subscribe with the SA for
* events) and updates the status mask for the specific producer. If all four
* producer types have arrived then the event clients are notified if there has
* been a change in the status.
*
* Input Arguments
* arg user-specified pointer (points to the current port data)
* length length of payload returned (should be size of informinfo_rec)
* buffer pointer to informinfo response returned (should not be null)
* status status of sa access request
* producer_type for subscriptions, indicates the notice producer type that was
* requested; ignored for unsubscribes
*
* Output Arguments
* none
*
* Returns void
*/
static void
ibmf_saa_informinfo_cb(void *arg, size_t length, char *buffer,
int status, uint32_t producer_type)
{
saa_port_t *saa_portp;
uint8_t producer_type_mask;
boolean_t notify_clients;
uint8_t event_status_mask;
ibmf_saa_event_details_t event_details;
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_saa_informinfo_cb_start,
IBMF_TNF_TRACE, "", "ibmf_saa_informinfo_cb() enter: producer_type "
"= 0x%x, status = %d\n", tnf_opaque, producer_type, producer_type,
tnf_int, status, status);
saa_portp = (saa_port_t *)arg;
notify_clients = B_FALSE;
/* if producer type is 0 this was an unsubscribe */
if (producer_type == 0) {
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_informinfo_cb(): %s",
tnf_string, msg, "handling unsubscribe");
if (buffer != NULL)
kmem_free(buffer, length);
goto bail;
}
/* determine which event it was */
switch (producer_type) {
case MAD_INFORMINFO_NODETYPE_CA:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA;
break;
case MAD_INFORMINFO_NODETYPE_SWITCH:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH;
break;
case MAD_INFORMINFO_NODETYPE_ROUTER:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER;
break;
case MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT:
producer_type_mask =
IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM;
break;
default:
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_send_informinfo_cb, IBMF_TNF_ERROR,
"", "ibmf_saa_informinfo_cb: %s, "
"producer_type = 0x%x\n",
tnf_string, msg, "Unknown producer type",
tnf_opaque, producer_type, producer_type);
producer_type_mask = 0;
break;
}
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
if (saa_portp->saa_pt_event_sub_arrive_mask & producer_type_mask) {
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_informinfo_cb(): %s, prod_type_mask = 0x%x",
tnf_string, msg, "Received duplicate response",
tnf_opaque, producer_type_mask, producer_type_mask);
if (buffer != NULL)
kmem_free(buffer, length);
goto bail;
}
saa_portp->saa_pt_event_sub_arrive_mask |= producer_type_mask;
/* process response */
if ((status != IBMF_SUCCESS) || (buffer == NULL)) {
IBMF_TRACE_4(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_informinfo_cb, IBMF_TNF_ERROR, "",
"ibmf_saa_informinfo_cb: %s, status = %d,"
" buffer = 0x%p, length = %d\n",
tnf_string, msg, "could not get informinfo resp",
tnf_int, status, status, tnf_opaque, buffer, buffer,
tnf_uint, length, length);
} else if (buffer != NULL) {
kmem_free(buffer, length);
saa_portp->saa_pt_event_sub_success_mask |= producer_type_mask;
}
/* if all four InformInfo responses have arrived */
if (saa_portp->saa_pt_event_sub_arrive_mask ==
IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE) {
IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_informinfo_cb(): %s, success mask = 0x%x,"
" last success mask = 0x%x\n",
tnf_string, msg, "all informinfo responses have arrived",
tnf_opaque, success_mask,
saa_portp->saa_pt_event_sub_success_mask,
tnf_opaque, last_success_mask,
saa_portp->saa_pt_event_sub_last_success_mask);
mutex_enter(&saa_portp->saa_pt_mutex);
/*
* Sending the four InformInfos is treated as one port client
* reference. Now that all have returned decrement the
* reference count.
*/
ASSERT(saa_portp->saa_pt_reference_count > 0);
saa_portp->saa_pt_reference_count--;
mutex_exit(&saa_portp->saa_pt_mutex);
if (saa_portp->saa_pt_event_sub_last_success_mask !=
saa_portp->saa_pt_event_sub_success_mask) {
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_informinfo_cb(): %s\n",
tnf_string, msg,
"success mask different - notifying clients");
/*
* save status mask to give to clients and update last
* mask for next set of subscription requests
*/
event_status_mask =
saa_portp->saa_pt_event_sub_last_success_mask =
saa_portp->saa_pt_event_sub_success_mask;
notify_clients = B_TRUE;
}
}
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
if (notify_clients == B_TRUE) {
bzero(&event_details, sizeof (ibmf_saa_event_details_t));
event_details.ie_producer_event_status_mask =
event_status_mask;
ibmf_saa_notify_event_clients(saa_portp, &event_details,
IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, NULL);
}
bail:
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_informinfo_cb_end,
IBMF_TNF_TRACE, "", "ibmf_saa_informinfo_cb() exit\n");
}
/*
* ibmf_saa_notify_event_client_task
*
* Calls the event notification callback for a registered saa client. Called
* from ibmf_saa_notify_event_clients() for each client that has registered for
* events. ibmf_saa_notify_event_clients() will dispatch this task on the
* saa_event_taskq so the client's callback can be invoked directly.
*
* Input Arguments
* args pointer to ibmf_saa_event_taskq_args_t
* this function will free memory associated with args
*
* Output Arguments
* none
*
* Returns
* void
*/
static void
ibmf_saa_notify_event_client_task(void *args)
{
ibmf_saa_event_taskq_args_t *event_taskq_args;
saa_client_data_t *client;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_notify_event_client_task_start,
IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_client_task() enter\n");
event_taskq_args = (ibmf_saa_event_taskq_args_t *)args;
client = event_taskq_args->et_client;
/* call client's callback (client pointer is ibmf_saa_handle) */
(event_taskq_args->et_callback)((ibmf_saa_handle_t)client,
event_taskq_args->et_subnet_event,
event_taskq_args->et_event_details,
event_taskq_args->et_callback_arg);
kmem_free(event_taskq_args->et_event_details,
sizeof (ibmf_saa_event_details_t));
kmem_free(event_taskq_args, sizeof (ibmf_saa_event_taskq_args_t));
/* decrement the callback count and signal a waiting client */
mutex_enter(&client->saa_client_mutex);
client->saa_client_event_cb_num_active--;
if (client->saa_client_event_cb_num_active == 0) {
cv_signal(&client->saa_client_event_cb_cv);
}
mutex_exit(&client->saa_client_mutex);
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_notify_event_client_task_end,
IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_client_task() exit\n");
}
/*
* ibmf_saa_process_subnet_event:
*
* Called when the ibmf_saa is notified of a forwarded notice. Converts the
* notice into an ibmf_saa_event_details structure and calls
* ibmf_saa_notify_event_clients() which will notify each interested client.
*
* Input Arguments
* saa_portp pointer to saa_port data
* notice notice that was forwarded from SA
*
* Output Arguments
* none
*
* Returns
* void
*/
static void
ibmf_saa_process_subnet_event(saa_port_t *saa_portp, ib_mad_notice_t *notice)
{
ibmf_saa_event_details_t event_details;
sm_trap_64_t trap_data_details;
sm_trap_144_t cap_mask_trap_data_details;
sm_trap_145_t sys_img_trap_data_details;
ibmf_saa_subnet_event_t subnet_event;
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_process_subnet_event_start,
IBMF_TNF_TRACE, "", "ibmf_saa_process_subnet_event() enter: "
"trap_number = 0x%x\n",
tnf_opaque, trap_number, notice->TrapNumber_DeviceID);
bzero(&event_details, sizeof (ibmf_saa_event_details_t));
/*
* fill in the appropriate fields of event_details depending on
* the trap number
*/
switch (notice->TrapNumber_DeviceID) {
case SM_GID_IN_SERVICE_TRAP:
ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
&trap_data_details);
event_details.ie_gid = trap_data_details.GIDADDR;
subnet_event = IBMF_SAA_EVENT_GID_AVAILABLE;
break;
case SM_GID_OUT_OF_SERVICE_TRAP:
ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
&trap_data_details);
event_details.ie_gid = trap_data_details.GIDADDR;
subnet_event = IBMF_SAA_EVENT_GID_UNAVAILABLE;
break;
case SM_MGID_CREATED_TRAP:
ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
&trap_data_details);
event_details.ie_gid = trap_data_details.GIDADDR;
subnet_event = IBMF_SAA_EVENT_MCG_CREATED;
break;
case SM_MGID_DESTROYED_TRAP:
ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
&trap_data_details);
event_details.ie_gid = trap_data_details.GIDADDR;
subnet_event = IBMF_SAA_EVENT_MCG_DELETED;
break;
case SM_CAP_MASK_CHANGED_TRAP:
ibmf_saa_capmask_chg_trap_parse_buffer(
notice->DataDetails, &cap_mask_trap_data_details);
event_details.ie_lid =
cap_mask_trap_data_details.LIDADDR;
event_details.ie_capability_mask =
cap_mask_trap_data_details.CAPABILITYMASK;
subnet_event = IBMF_SAA_EVENT_CAP_MASK_CHG;
break;
case SM_SYS_IMG_GUID_CHANGED_TRAP:
ibmf_saa_sysimg_guid_chg_trap_parse_buffer(
notice->DataDetails, &sys_img_trap_data_details);
event_details.ie_lid =
sys_img_trap_data_details.LIDADDR;
event_details.ie_sysimg_guid =
sys_img_trap_data_details.SYSTEMIMAGEGUID;
subnet_event = IBMF_SAA_EVENT_SYS_IMG_GUID_CHG;
break;
default:
/*
* do nothing if it's not one of the traps we care about
*/
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_process_subnet_event_end,
IBMF_TNF_TRACE, "",
"ibmf_saa_process_subnet_event() exit: %s\n",
tnf_string, msg,
"not one of the six ibmf_saa subnet events");
return;
}
ibmf_saa_notify_event_clients(saa_portp, &event_details, subnet_event,
NULL);
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_process_subnet_event_end,
IBMF_TNF_TRACE, "", "ibmf_saa_process_subnet_event() exit\n");
}
/*
* ibmf_saa_notify_event_clients:
*
* Called when a trap for one of the six saa subnet events arrives or there is a
* change in the status of event subscriptions. Searches the list of clients
* with callbacks and dispatches a taskq thread to notify the client that the
* event occured.
*
* If some subscription request fails and a subsequent client registers for
* events that client needs to know that it may not receive all events. To
* facilitate this, notify_event_clients() takes an optional parameter which
* specifies a specific client. If registering_client is non-NULL only this
* client is notified. If the parameter is NULL, all clients in the list are
* notified.
*
* Input Arguments
* saa_portp pointer to saa_port data
* event_details pointer to ibmf_saa_event_details_t for this event
* subnet_event type of event that occured
* registering_client pointer to client_data_t if notification should go to a
* specific client; NULL if notification should go to all
* clients which subscribed for events
*
* Output Arguments
* none
*
* Returns
* none
*/
void
ibmf_saa_notify_event_clients(saa_port_t *saa_portp,
ibmf_saa_event_details_t *event_details,
ibmf_saa_subnet_event_t subnet_event, saa_client_data_t *registering_client)
{
saa_client_data_t *client;
ibmf_saa_event_taskq_args_t *event_taskq_args;
int status;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_notify_event_clients_start,
IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_clients() enter\n");
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
if (registering_client != NULL)
client = registering_client;
else
client = saa_portp->saa_pt_event_sub_client_list;
while (client != NULL) {
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*event_taskq_args))
event_taskq_args = kmem_zalloc(
sizeof (ibmf_saa_event_taskq_args_t), KM_NOSLEEP);
if (event_taskq_args == NULL) {
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
"", "ibmf_saa_notify_event_clients: %s, client = "
"0x%x\n", tnf_string, msg,
"could not allocate memory for taskq args",
tnf_opaque, client, client);
/*
* if a particular client was not specified continue
* processing the client list
*/
if (registering_client == NULL)
client = client->next;
else
client = NULL;
continue;
}
/*
* each task needs its own pointer, the task will free
* up this memory
*/
event_taskq_args->et_event_details = kmem_zalloc(
sizeof (ibmf_saa_event_details_t), KM_NOSLEEP);
if (event_taskq_args->et_event_details == NULL) {
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
"", "ibmf_saa_notify_event_clients: %s, client = "
"0x%x\n", tnf_string, msg,
"could not allocate memory for taskq event details",
tnf_opaque, client, client);
kmem_free(event_taskq_args,
sizeof (ibmf_saa_event_taskq_args_t));
/*
* if a particular client was not specified continue
* processing the client list
*/
client =
(registering_client == NULL) ? client->next: NULL;
continue;
}
mutex_enter(&client->saa_client_mutex);
/*
* don't generate callbacks if client is not active
* (it's probably closing the session)
*/
if (client->saa_client_state != SAA_CLIENT_STATE_ACTIVE) {
IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_notify_event_clients, IBMF_TNF_TRACE,
"", "ibmf_saa_notify_event_clients: %s, client = "
"0x%x, state = 0x%x\n", tnf_string, msg,
"client state not active",
tnf_opaque, client, client,
tnf_opaque, state, client->saa_client_state);
mutex_exit(&client->saa_client_mutex);
kmem_free(event_taskq_args->et_event_details,
sizeof (ibmf_saa_event_details_t));
kmem_free(event_taskq_args,
sizeof (ibmf_saa_event_taskq_args_t));
/*
* if a particular client was not specified continue
* processing the client list
*/
client =
(registering_client == NULL) ? client->next: NULL;
continue;
}
/*
* increment the callback count so the client cannot close the
* session while callbacks are active
*/
client->saa_client_event_cb_num_active++;
mutex_exit(&client->saa_client_mutex);
event_taskq_args->et_client = client;
event_taskq_args->et_subnet_event = subnet_event;
bcopy(event_details, event_taskq_args->et_event_details,
sizeof (ibmf_saa_event_details_t));
event_taskq_args->et_callback = client->saa_client_event_cb;
event_taskq_args->et_callback_arg =
client->saa_client_event_cb_arg;
_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*event_taskq_args))
/* dispatch taskq thread to notify client */
status = taskq_dispatch(saa_statep->saa_event_taskq,
ibmf_saa_notify_event_client_task, event_taskq_args,
KM_NOSLEEP);
if (status == 0) {
IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
"", "ibmf_saa_notify_event_clients: %s, client = "
"0x%x\n",
tnf_string, msg, "Could not dispatch event taskq",
tnf_opaque, client, client);
kmem_free(event_taskq_args->et_event_details,
sizeof (ibmf_saa_event_details_t));
kmem_free(event_taskq_args,
sizeof (ibmf_saa_event_taskq_args_t));
/*
* decrement the callback count and signal a waiting
* client
*/
mutex_enter(&client->saa_client_mutex);
client->saa_client_event_cb_num_active--;
if (client->saa_client_event_cb_num_active == 0) {
cv_signal(&client->saa_client_event_cb_cv);
}
mutex_exit(&client->saa_client_mutex);
} else {
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
"", "ibmf_saa_notify_event_clients: %s, client = "
"0x%x\n",
tnf_string, msg, "Dispatched task to notify client",
tnf_opaque, client, client);
}
/*
* if a particular client was not specified continue processing
* the client list
*/
client = (registering_client == NULL) ? client->next: NULL;
}
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_notify_event_clients_end,
IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_clients() exit\n");
}
/*
* ibmf_saa_report_cb:
*
* Called when a forwarded notice Report is received by ibmf_saa from the SA.
* Converts the Report into an ib_mad_notice_t and calls
* ibmf_saa_notify_event_clients() which will notify each subscribed ibmf_saa
* client. Also sends a response to the report to acknowledge to the SA that
* this port is still up.
*
* This is the registered async callback with ibmf. Only Reports should come
* through this interface as all other transactions with ibmf_saa are sequenced
* (ibmf_saa makes the initial request).
*
* This function cannot block since it is called from an ibmf callback.
*
* Input Arguments
* ibmf_handle ibmf handle
* msgp pointer to ibmf_msg_t
* args pointer to saa_port data
*
* Output Arguments
* none
*
* Returns
* none
*/
void
ibmf_saa_report_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
void *args)
{
ib_mad_hdr_t *req_mad_hdr, *resp_mad_hdr;
saa_port_t *saa_portp, *saa_port_list_entry;
ibmf_retrans_t ibmf_retrans;
int ibmf_status;
ib_mad_notice_t *notice_report;
saa_impl_trans_info_t *trans_info;
boolean_t port_valid;
uint16_t mad_status;
uint16_t attr_id;
boolean_t response_sent = B_FALSE;
size_t length;
int status;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_report_cb_start,
IBMF_TNF_TRACE, "", "ibmf_saa_report_cb() enter\n");
_NOTE(ASSUMING_PROTECTED(*msgp))
saa_portp = (saa_port_t *)args;
port_valid = B_FALSE;
/* check whether this portp is still valid */
mutex_enter(&saa_statep->saa_port_list_mutex);
saa_port_list_entry = saa_statep->saa_port_list;
while (saa_port_list_entry != NULL) {
if (saa_port_list_entry == saa_portp) {
port_valid = ibmf_saa_is_valid(saa_portp, B_FALSE);
break;
}
saa_port_list_entry = saa_port_list_entry->next;
}
mutex_exit(&saa_statep->saa_port_list_mutex);
if (port_valid == B_FALSE) {
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, saa_port = 0x%p\n",
tnf_string, msg, "port no longer valid",
tnf_opaque, saa_portp, saa_portp);
goto bail;
}
req_mad_hdr = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
/* drop packet if status is bad */
if ((msgp->im_msg_status != IBMF_SUCCESS) ||
(req_mad_hdr == NULL) ||
((mad_status = b2h16(req_mad_hdr->Status)) != SA_STATUS_NO_ERROR)) {
IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L1,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, msg_status = 0x%x,"
" req_mad_hdr = 0x%p, mad_status = 0x%x\n",
tnf_string, msg, "Bad ibmf status",
tnf_opaque, msg_status, msgp->im_msg_status,
tnf_opaque, req_mad_hdr, req_mad_hdr,
tnf_opaque, mad_status,
(req_mad_hdr == NULL ? 0 : mad_status));
goto bail;
}
/* drop packet if class version is not correct */
if (req_mad_hdr->ClassVersion != SAA_MAD_CLASS_VERSION) {
IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L1,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, msg_class_ver = 0x%x,"
" ibmf_saa_class_ver = 0x%x\n",
tnf_string, msg, "Bad class version",
tnf_opaque, msg_class_ver, req_mad_hdr->ClassVersion,
tnf_opaque, ibmf_saa_class_ver, SAA_MAD_CLASS_VERSION);
goto bail;
}
/*
* only care about notice reports(); should not get any other type
* of method or attribute
*/
if (((attr_id = b2h16(req_mad_hdr->AttributeID)) != SA_NOTICE_ATTRID) ||
(req_mad_hdr->R_Method != SA_SUBN_ADM_REPORT)) {
IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, attr_id = 0x%x, "
"method = 0x%x\n",
tnf_string, msg, "Unsolicited message not notice report",
tnf_opaque, attr_id, attr_id,
tnf_opaque, method, req_mad_hdr->R_Method);
goto bail;
}
/*
* unpack the data into a ib_mad_notice_t; the data details are left
* as packed data and will be unpacked by process_subnet_event()
* is_get_resp parameter is set to B_TRUE since cl_data_len will
* probably be set to 200 bytes by ibmf (it's not an RMPP trans)
*/
status = ibmf_saa_utils_unpack_payload(
msgp->im_msgbufs_recv.im_bufs_cl_data,
msgp->im_msgbufs_recv.im_bufs_cl_data_len, SA_NOTICE_ATTRID,
(void **)&notice_report, &length, 0, B_TRUE, KM_NOSLEEP);
if (status != IBMF_SUCCESS) {
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, status = %d",
tnf_string, msg, "Could not unpack data",
tnf_int, status, status);
goto bail;
}
ASSERT(length == sizeof (ib_mad_notice_t));
ibmf_saa_process_subnet_event(saa_portp, notice_report);
kmem_free(notice_report, length);
_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*resp_mad_hdr))
/* send ReportResp */
resp_mad_hdr = kmem_zalloc(sizeof (ib_mad_hdr_t), KM_SLEEP);
bcopy(req_mad_hdr, resp_mad_hdr, sizeof (ib_mad_hdr_t));
resp_mad_hdr->R_Method = SA_SUBN_ADM_REPORT_RESP;
msgp->im_msgbufs_send.im_bufs_mad_hdr = resp_mad_hdr;
msgp->im_msgbufs_send.im_bufs_cl_hdr = kmem_zalloc(
msgp->im_msgbufs_recv.im_bufs_cl_hdr_len, KM_SLEEP);
msgp->im_msgbufs_send.im_bufs_cl_hdr_len =
msgp->im_msgbufs_recv.im_bufs_cl_hdr_len;
/* only headers are needed */
msgp->im_msgbufs_send.im_bufs_cl_data = NULL;
msgp->im_msgbufs_send.im_bufs_cl_data_len = 0;
/*
* report_cb cannot block because it's in the context of an ibmf
* callback. So the response needs to be sent asynchronously.
* ibmf_saa_async_cb is an appropriate callback to use for the response.
* Set up a trans_info structure as saa_async_cb expects. But don't use
* ibmf_saa_impl_send_request() to send the response since that function
* does unncessary steps in this case (like allocating a new ibmf msg).
* Only the si_trans_port field needs to be filled in.
*/
trans_info = kmem_zalloc(sizeof (saa_impl_trans_info_t), KM_NOSLEEP);
if (trans_info == NULL) {
IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
ibmf_saa_report_cb_err, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s",
tnf_string, msg, "could not allocate trans_info structure");
goto bail;
}
trans_info->si_trans_port = saa_portp;
mutex_enter(&saa_portp->saa_pt_mutex);
bcopy(&saa_portp->saa_pt_ibmf_retrans, &ibmf_retrans,
sizeof (ibmf_retrans_t));
saa_portp->saa_pt_num_outstanding_trans++;
mutex_exit(&saa_portp->saa_pt_mutex);
ASSERT(ibmf_handle == saa_portp->saa_pt_ibmf_handle);
ibmf_status = ibmf_msg_transport(ibmf_handle,
saa_portp->saa_pt_qp_handle, msgp, &ibmf_retrans, ibmf_saa_async_cb,
trans_info, 0);
if (ibmf_status != IBMF_SUCCESS) {
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L1,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s, msg_status = 0x%x\n",
tnf_string, msg, "Could not send report response",
tnf_int, ibmf_status, ibmf_status);
mutex_enter(&saa_portp->saa_pt_mutex);
ASSERT(saa_portp->saa_pt_num_outstanding_trans > 0);
saa_portp->saa_pt_num_outstanding_trans--;
mutex_exit(&saa_portp->saa_pt_mutex);
kmem_free(trans_info, sizeof (saa_impl_trans_info_t));
} else {
IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb: %s\n",
tnf_string, msg, "Asynchronous Report response sent");
response_sent = B_TRUE;
}
bail:
if (response_sent == B_FALSE) {
ibmf_status = ibmf_free_msg(ibmf_handle, &msgp);
ASSERT(ibmf_status == IBMF_SUCCESS);
}
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_informinfo_cb_end, IBMF_TNF_TRACE, "",
"ibmf_saa_report_cb() exit\n");
}
/*
* ibmf_saa_add_event_subscriber:
*
* Adds an interested client to the list of subscribers for events for a port.
* If it's the first client, generates the subscription requests.
* This function must only be called if event_args is not null
*
* Input Arguments
*
* client pointer to client data (client->saa_port should be set)
* event_args pointer to event_args passed in from client (non-NULL)
*
* Output Arguments
* none
*
* Returns
* void
*/
void
ibmf_saa_add_event_subscriber(saa_client_data_t *client,
ibmf_saa_subnet_event_args_t *event_args)
{
saa_port_t *saa_portp;
boolean_t first_client;
uint8_t producer_status_mask;
ibmf_saa_event_details_t event_details;
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_add_event_subscriber_start, IBMF_TNF_TRACE, "",
"ibmf_saa_add_event_subscriber() enter\n");
/* event_args should be checked before calling this function */
ASSERT(event_args != NULL);
/* don't add client if no callback function is specified */
if (event_args->is_event_callback == NULL)
return;
saa_portp = client->saa_client_port;
client->saa_client_event_cb = event_args->is_event_callback;
client->saa_client_event_cb_arg = event_args->is_event_callback_arg;
/*
* insert this client onto the list; this list is used when a
* Report arrives to call each client's callback
*/
mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
ibmf_sa_session_open, IBMF_TNF_TRACE, "",
"ibmf_saa_add_event_subscriber: %s, client = 0x%x\n",
tnf_string, msg, "Adding client to event subscriber list",
tnf_opaque, client, client);
if (saa_portp->saa_pt_event_sub_client_list == NULL)
first_client = B_TRUE;
else {
first_client = B_FALSE;
producer_status_mask =
saa_portp->saa_pt_event_sub_last_success_mask;
}
client->next = saa_portp->saa_pt_event_sub_client_list;
saa_portp->saa_pt_event_sub_client_list = client;
mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
if (first_client == B_TRUE) {
/*
* increment the reference count by one to account for
* the subscription requests. All four InformInfo's are
* sent as one port client reference.
*/
mutex_enter(&saa_portp->saa_pt_mutex);
saa_portp->saa_pt_reference_count++;
mutex_exit(&saa_portp->saa_pt_mutex);
/* subscribe for subnet events */
ibmf_saa_subscribe_events(saa_portp, B_TRUE, B_FALSE);
} else if (producer_status_mask != IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE) {
/*
* if this is not the first client and the producer status mask
* is not all success, generate a callback to indicate to the
* client that not all events will be forwarded
*/
bzero(&event_details, sizeof (ibmf_saa_event_details_t));
event_details.ie_producer_event_status_mask =
producer_status_mask;
ibmf_saa_notify_event_clients(saa_portp, &event_details,
IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, client);
}
IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
ibmf_saa_add_event_subscriber_end, IBMF_TNF_TRACE, "",
"ibmf_saa_add_event_subscriber() exit\n");
}