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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/ib/mgt/ibmf/ibmf_saa_impl.h>
28 #include <sys/ib/mgt/ibmf/ibmf_saa_utils.h>
29 
30 extern	saa_state_t	*saa_statep;
31 extern	int	ibmf_trace_level;
32 
33 static void
34 ibmf_saa_informinfo_cb(void *arg, size_t length, char *buffer,
35     int status, uint32_t producer_type);
36 
37 static int
38 ibmf_saa_send_informinfo(saa_port_t *saa_portp, uint32_t producer_type,
39     boolean_t subscribe, boolean_t unseq_unsubscribe);
40 
41 static void
42 ibmf_saa_notify_event_client_task(void *args);
43 
44 static void
45 ibmf_saa_process_subnet_event(saa_port_t *saa_portp, ib_mad_notice_t *notice);
46 
47 /*
48  * ibmf_saa_subscribe_events:
49  * Subscribe or unsubscribe to subnet events for a certain port.
50  * ibmf_saa_subscribe_events() will send an InformInfo request for each of the
51  * four notice producer types.
52  *
53  * Subscribes generally occur when the first client for a port opens a session
54  * and when a port with registered ibmf_saa clients transitions to active.
55  * Subscribes are done as asynchronous, sequenced transactions.
56  *
57  * ibmf_saa sends unsubscribe requests when the last client for a port
58  * unregisters and when an CI_OFFLINE message is received from ibtf (via ibmf).
59  * For the first case, the unsubscribe is done as an asynchronous, sequenced
60  * transaction.  For the second case, the request is asynchronous, unsequenced.
61  * This means that the unsubscribes will not be retried.  Because the port is
62  * going away we cannot wait for responses.  Unsubscribes are not required
63  * anyway as the SA will remove subscription records from ports it determines to
64  * be down.
65  *
66  * For subscribe requests, clients are notified that the request failed through
67  * the event notification mechanism.  For unsubscribe requests,  clients are not
68  * notified if the request fails.  Therefore, this function returns void.
69  *
70  * Input Arguments
71  * saa_portp		pointer to port state structure
72  * subscribe		B_TRUE if request is a Subscribe, B_FALSE if unsubscribe
73  * unseq_unsubscribe	B_TRUE if unsubscribe request should be unsequenced
74  * 			(called from CI_OFFLINE event handler)
75  * 			B_FALSE if sequenced (wait for response) or for all
76  *			subscribe requests
77  *
78  * Output Arguments
79  * none
80  *
81  * Returns
82  * void
83  */
84 void
ibmf_saa_subscribe_events(saa_port_t * saa_portp,boolean_t subscribe,boolean_t unseq_unsubscribe)85 ibmf_saa_subscribe_events(saa_port_t *saa_portp, boolean_t subscribe,
86     boolean_t unseq_unsubscribe)
87 {
88 	int				res;
89 	ibmf_saa_event_details_t	event_details;
90 	boolean_t			notify_clients = B_FALSE;
91 	uint8_t				success_mask;
92 
93 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
94 	    ibmf_saa_subscribe_events_start, IBMF_TNF_TRACE, "",
95 	    "ibmf_saa_subscribe_events() enter\n");
96 
97 	/* subscribes should always be sychronous */
98 	ASSERT((subscribe == B_FALSE) || (unseq_unsubscribe == B_FALSE));
99 
100 	/*
101 	 * reset the arrive and success masks to indicate no responses have come
102 	 * back; technically only used for subscriptions but reset the values
103 	 * anyway
104 	 */
105 	mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
106 
107 	success_mask = saa_portp->saa_pt_event_sub_success_mask;
108 
109 	saa_portp->saa_pt_event_sub_arrive_mask = 0;
110 	saa_portp->saa_pt_event_sub_success_mask = 0;
111 
112 	mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
113 
114 	/*
115 	 * now subscribe/unsubscribe for each of the notice producer types;
116 	 * send_informinfo returns 1 on success, 0 on failure.  If the "or" of
117 	 * all four results is 0 then none of the informinfo's succeed and we
118 	 * should notify the client.  If it's not 0, then informinfo_cb will be
119 	 * called at least once, taking care of notifying the clients that there
120 	 * was a failure.
121 	 * For each producer type, send the request only if it's a subscribe or
122 	 * if it's an unsubscribe for a subscribe which succeeded
123 	 */
124 
125 	/*
126 	 * subscribe for all traps generated by the SM;
127 	 * gid in service/out of service, mgid created/deleted, etc.
128 	 */
129 	if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM) ||
130 	    (subscribe == B_TRUE))
131 		res = ibmf_saa_send_informinfo(saa_portp,
132 		    MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT, subscribe,
133 		    unseq_unsubscribe);
134 
135 	/* subscribe for all traps generated by a CA */
136 	if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA) ||
137 	    (subscribe == B_TRUE))
138 		res |= ibmf_saa_send_informinfo(saa_portp,
139 		    MAD_INFORMINFO_NODETYPE_CA, subscribe, unseq_unsubscribe);
140 
141 	/* subscribe for all traps generated by a switch */
142 	if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH) ||
143 	    (subscribe == B_TRUE))
144 		res |= ibmf_saa_send_informinfo(saa_portp,
145 		    MAD_INFORMINFO_NODETYPE_SWITCH, subscribe,
146 		    unseq_unsubscribe);
147 
148 	/* subscribe for all traps generated by a router */
149 	if ((success_mask & IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER) ||
150 	    (subscribe == B_TRUE))
151 		res |= ibmf_saa_send_informinfo(saa_portp,
152 		    MAD_INFORMINFO_NODETYPE_ROUTER, subscribe,
153 		    unseq_unsubscribe);
154 
155 	/* if none of the subscribe requests succeeded notify the clients */
156 	if ((res == 0) && (subscribe == B_TRUE)) {
157 
158 		IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
159 		    ibmf_saa_subscribe_events_err, IBMF_TNF_ERROR, "",
160 		    "ibmf_saa_subscribe_events: %s\n", tnf_string, msg,
161 		    "Could not subscribe for any of the four producer types");
162 
163 		mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
164 
165 		/* all events should have "arrived" */
166 		ASSERT(saa_portp->saa_pt_event_sub_arrive_mask ==
167 		    IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE);
168 
169 		/* status mask should be 0 since all failed */
170 		ASSERT(saa_portp->saa_pt_event_sub_success_mask == 0);
171 
172 		/* notify clients if success mask changed */
173 		if (saa_portp->saa_pt_event_sub_last_success_mask !=
174 		    saa_portp->saa_pt_event_sub_success_mask)
175 			notify_clients = B_TRUE;
176 
177 		/* update last mask for next set of subscription requests */
178 		saa_portp->saa_pt_event_sub_last_success_mask =
179 		    saa_portp->saa_pt_event_sub_arrive_mask = 0;
180 
181 		mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
182 
183 		mutex_enter(&saa_portp->saa_pt_mutex);
184 
185 		/*
186 		 * Sending the four InformInfos is treated as one port client
187 		 * reference.  Now that all have returned decrement the
188 		 * reference count.
189 		 */
190 		ASSERT(saa_portp->saa_pt_reference_count > 0);
191 		saa_portp->saa_pt_reference_count--;
192 
193 		mutex_exit(&saa_portp->saa_pt_mutex);
194 	}
195 
196 	/*
197 	 * for unsequenced unsubscribes, decrement the reference count here
198 	 * since no callbacks will ever do it
199 	 */
200 	if (unseq_unsubscribe == B_TRUE) {
201 
202 		mutex_enter(&saa_portp->saa_pt_mutex);
203 
204 		/*
205 		 * Sending the four InformInfos is treated as one port client
206 		 * reference.  Now that all have returned decrement the
207 		 * reference count.
208 		 */
209 		ASSERT(saa_portp->saa_pt_reference_count > 0);
210 		saa_portp->saa_pt_reference_count--;
211 
212 		mutex_exit(&saa_portp->saa_pt_mutex);
213 	}
214 
215 	if (notify_clients == B_TRUE) {
216 
217 		bzero(&event_details, sizeof (ibmf_saa_event_details_t));
218 
219 		ibmf_saa_notify_event_clients(saa_portp, &event_details,
220 		    IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, NULL);
221 	}
222 
223 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_saa_subscribe_events_end,
224 	    IBMF_TNF_TRACE, "", "ibmf_saa_subscribe_events() exit\n");
225 }
226 
227 /*
228  * ibmf_saa_send_informinfo:
229  *
230  * Sends an InformInfo request to the SA.  There are two types of request,
231  * Subscribes and Unsubscribes.  This function is called from
232  * ibmf_saa_subscribe_events.  See that function's comment for usage of
233  * subscribe, unseq_unsubscribe booleans.
234  *
235  * This function generates a standard ibmf_saa transaction and sends using
236  * ibmf_saa_impl_send_request().  For asynchronous callbacks, the function
237  * ibmf_saa_informinfo_cb() will be called.
238  *
239  * This function blocks allocating resources, but not waiting for response
240  * packets.
241  *
242  * Input Arguments
243  * saa_portp		pointer to port data
244  * producer_type	InformInfo producer type to subscribe for
245  * subscribe		B_TRUE if subscribe request, B_FALSE if unsubscribe
246  * unseq_unsubscribe	B_TRUE if unsubscribe request should be unsequenced
247  *			(called from CI_OFFLINE event handler)
248  * 			B_FALSE if sequenced (wait for response) or for all
249  *			subscribe requests
250  *
251  * Output Arguments
252  * none
253  *
254  * Returns
255  * 1 if the transaction succeeded, 0 if it failed
256  */
257 static int
ibmf_saa_send_informinfo(saa_port_t * saa_portp,uint32_t producer_type,boolean_t subscribe,boolean_t unseq_unsubscribe)258 ibmf_saa_send_informinfo(saa_port_t *saa_portp, uint32_t producer_type,
259     boolean_t subscribe, boolean_t unseq_unsubscribe)
260 {
261 	ib_mad_informinfo_t	inform_info;
262 	saa_impl_trans_info_t	*trans_info;
263 	int			res;
264 	uint8_t			producer_type_mask;
265 
266 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
267 	    ibmf_saa_send_informinfo_start, IBMF_TNF_TRACE, "",
268 	    "ibmf_saa_send_informinfo() enter\n");
269 
270 	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_saa_send_informinfo,
271 	    IBMF_TNF_TRACE, "", "ibmf_saa_send_informinfo: %s, producer_type ="
272 	    "%x, subscribe = %x, unseq_unsubscribe = %x\n",
273 	    tnf_string, msg, "Sending informinfo request",
274 	    tnf_opaque, producer_type, producer_type,
275 	    tnf_int, subscribe, subscribe,
276 	    tnf_int, unseq_unsubscribe, unseq_unsubscribe);
277 
278 	bzero(&inform_info, sizeof (ib_mad_informinfo_t));
279 
280 	/* initialize inform_info packet */
281 	inform_info.LIDRangeBegin = MAD_INFORMINFO_ALL_ENDPORTS_RANGE;
282 	inform_info.IsGeneric = MAD_INFORMINFO_FORWARD_GENERIC;
283 
284 	if (subscribe == B_TRUE)
285 		inform_info.Subscribe = MAD_INFORMINFO_SUBSCRIBE;
286 	else {
287 		inform_info.Subscribe = MAD_INFORMINFO_UNSUBSCRIBE;
288 		inform_info.QPN = saa_portp->saa_pt_qpn;
289 	}
290 
291 	inform_info.Type = MAD_INFORMINFO_TRAP_TYPE_FORWARD_ALL;
292 	inform_info.TrapNumber_DeviceID =
293 	    MAD_INFORMINFO_TRAP_NUMBER_FORWARD_ALL;
294 	inform_info.ProducerType_VendorID = producer_type;
295 
296 	trans_info = kmem_zalloc(sizeof (saa_impl_trans_info_t), KM_SLEEP);
297 
298 	/* no specific client associated with this transaction */
299 	trans_info->si_trans_client_data = NULL;
300 	trans_info->si_trans_port = saa_portp;
301 	trans_info->si_trans_method = SA_SUBN_ADM_SET;
302 	trans_info->si_trans_attr_id = SA_INFORMINFO_ATTRID;
303 	trans_info->si_trans_component_mask = 0;
304 	trans_info->si_trans_template = &inform_info;
305 	trans_info->si_trans_template_length = sizeof (ib_mad_informinfo_t);
306 	trans_info->si_trans_unseq_unsubscribe = unseq_unsubscribe;
307 
308 	/*
309 	 * if this isn't an unsequenced unsubscribe (the only synchronous
310 	 * request) then set up the callback
311 	 */
312 	if (unseq_unsubscribe == B_FALSE) {
313 		trans_info->si_trans_sub_callback =
314 		    ibmf_saa_informinfo_cb;
315 		trans_info->si_trans_callback_arg = saa_portp;
316 
317 		/*
318 		 * if this is a subscribe, set the producer type so we can know
319 		 * which one's failed
320 		 */
321 		if (subscribe == B_TRUE) {
322 			trans_info->si_trans_sub_producer_type = producer_type;
323 		}
324 	}
325 
326 	mutex_enter(&saa_portp->saa_pt_kstat_mutex);
327 
328 	IBMF_SAA_ADD32_KSTATS(saa_portp, outstanding_requests, 1);
329 	IBMF_SAA_ADD32_KSTATS(saa_portp, total_requests, 1);
330 
331 	mutex_exit(&saa_portp->saa_pt_kstat_mutex);
332 
333 	res = ibmf_saa_impl_send_request(trans_info);
334 	if (res != IBMF_SUCCESS) {
335 
336 		IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
337 		    ibmf_saa_send_informinfo, IBMF_TNF_ERROR, "",
338 		    "ibmf_saa_send_informinfo: %s, ibmf_status = %d\n",
339 		    tnf_string, msg, "ibmf_saa_impl_send_request() failed",
340 		    tnf_int, ibmf_status, res);
341 
342 		res = 0;
343 
344 	} else {
345 
346 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
347 		    ibmf_saa_send_informinfo, IBMF_TNF_TRACE, "",
348 		    "ibmf_saa_send_informinfo: %s\n", tnf_string, msg,
349 		    "Request sent successfully");
350 
351 		res = 1;
352 
353 		/*
354 		 * if this was an asynchronous transaction (not the unsequenced
355 		 * unsubscribe case) return here.
356 		 * The callback will clean up everything.
357 		 */
358 		if (unseq_unsubscribe == B_FALSE) {
359 
360 			goto bail;
361 		}
362 	}
363 
364 	kmem_free(trans_info, sizeof (saa_impl_trans_info_t));
365 
366 	mutex_enter(&saa_portp->saa_pt_kstat_mutex);
367 
368 	IBMF_SAA_SUB32_KSTATS(saa_portp, outstanding_requests, 1);
369 	IBMF_SAA_ADD32_KSTATS(saa_portp, failed_requests, 1);
370 
371 	mutex_exit(&saa_portp->saa_pt_kstat_mutex);
372 
373 	/*
374 	 * if subscribe transaction failed, update status mask
375 	 * to indicate "response"
376 	 */
377 	if ((res == 0) && (subscribe == B_TRUE)) {
378 
379 		mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
380 
381 		saa_portp->saa_pt_event_sub_arrive_mask = 0;
382 		saa_portp->saa_pt_event_sub_success_mask = 0;
383 
384 		switch (producer_type) {
385 
386 			case MAD_INFORMINFO_NODETYPE_CA:
387 				producer_type_mask =
388 				    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA;
389 			break;
390 			case MAD_INFORMINFO_NODETYPE_SWITCH:
391 				producer_type_mask =
392 				    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH;
393 			break;
394 			case MAD_INFORMINFO_NODETYPE_ROUTER:
395 				producer_type_mask =
396 				    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER;
397 			break;
398 			case MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT:
399 				producer_type_mask =
400 				    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM;
401 			break;
402 			default:
403 				IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
404 				    ibmf_saa_send_informinfo, IBMF_TNF_ERROR,
405 				    "", "ibmf_saa_send_informinfo: %s, "
406 				    "producer_type = 0x%x\n",
407 				    tnf_string, msg, "Unknown producer type",
408 				    tnf_opaque, producer_type, producer_type);
409 
410 				ASSERT(0);
411 				producer_type_mask = 0;
412 			break;
413 		}
414 
415 		saa_portp->saa_pt_event_sub_arrive_mask |= producer_type_mask;
416 
417 		mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
418 	}
419 
420 bail:
421 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
422 	    ibmf_saa_send_informinfo_end, IBMF_TNF_TRACE, "",
423 	    "ibmf_saa_send_informinfo() exit: result = 0x%x\n",
424 	    tnf_opaque, result, res);
425 
426 	return (res);
427 }
428 
429 /*
430  * ibmf_saa_informinfo_cb:
431  *
432  * Called when the asynchronous informinfo request receives its response.
433  * Checks the status (whether the ibmf_saa was able to subscribe with the SA for
434  * events) and updates the status mask for the specific producer.  If all four
435  * producer types have arrived then the event clients are notified if there has
436  * been a change in the status.
437  *
438  * Input Arguments
439  * arg		user-specified pointer (points to the current port data)
440  * length	length of payload returned (should be size of informinfo_rec)
441  * buffer	pointer to informinfo response returned (should not be null)
442  * status	status of sa access request
443  * producer_type for subscriptions, indicates the notice producer type that was
444  * 		requested; ignored for unsubscribes
445  *
446  * Output Arguments
447  * none
448  *
449  * Returns void
450  */
451 static void
ibmf_saa_informinfo_cb(void * arg,size_t length,char * buffer,int status,uint32_t producer_type)452 ibmf_saa_informinfo_cb(void *arg, size_t length, char *buffer,
453     int status, uint32_t producer_type)
454 {
455 	saa_port_t			*saa_portp;
456 	uint8_t				producer_type_mask;
457 	boolean_t			notify_clients;
458 	uint8_t				event_status_mask;
459 	ibmf_saa_event_details_t	event_details;
460 
461 	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_saa_informinfo_cb_start,
462 	    IBMF_TNF_TRACE, "", "ibmf_saa_informinfo_cb() enter: producer_type "
463 	    "= 0x%x, status = %d\n", tnf_opaque, producer_type, producer_type,
464 	    tnf_int, status, status);
465 
466 	saa_portp = (saa_port_t *)arg;
467 
468 	notify_clients = B_FALSE;
469 
470 	/* if producer type is 0 this was an unsubscribe */
471 	if (producer_type == 0) {
472 
473 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
474 		    ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
475 		    "ibmf_saa_informinfo_cb(): %s",
476 		    tnf_string, msg, "handling unsubscribe");
477 
478 		if (buffer != NULL)
479 			kmem_free(buffer, length);
480 
481 		goto bail;
482 	}
483 
484 	/* determine which event it was */
485 	switch (producer_type) {
486 
487 		case MAD_INFORMINFO_NODETYPE_CA:
488 			producer_type_mask =
489 			    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_CA;
490 		break;
491 		case MAD_INFORMINFO_NODETYPE_SWITCH:
492 			producer_type_mask =
493 			    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SWITCH;
494 		break;
495 		case MAD_INFORMINFO_NODETYPE_ROUTER:
496 			producer_type_mask =
497 			    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_ROUTER;
498 		break;
499 		case MAD_INFORMINFO_NODETYPE_SUBNET_MANAGEMENT:
500 			producer_type_mask =
501 			    IBMF_SAA_EVENT_STATUS_MASK_PRODUCER_SM;
502 		break;
503 
504 		default:
505 			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
506 			    ibmf_saa_send_informinfo_cb, IBMF_TNF_ERROR,
507 			    "", "ibmf_saa_informinfo_cb: %s, "
508 			    "producer_type = 0x%x\n",
509 			    tnf_string, msg, "Unknown producer type",
510 			    tnf_opaque, producer_type, producer_type);
511 
512 			producer_type_mask = 0;
513 		break;
514 	}
515 
516 	mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
517 
518 	if (saa_portp->saa_pt_event_sub_arrive_mask & producer_type_mask) {
519 
520 		mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
521 
522 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
523 		    ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
524 		    "ibmf_saa_informinfo_cb(): %s, prod_type_mask = 0x%x",
525 		    tnf_string, msg, "Received duplicate response",
526 		    tnf_opaque, producer_type_mask, producer_type_mask);
527 
528 		if (buffer != NULL)
529 			kmem_free(buffer, length);
530 
531 		goto bail;
532 	}
533 
534 	saa_portp->saa_pt_event_sub_arrive_mask |= producer_type_mask;
535 
536 	/* process response */
537 	if ((status != IBMF_SUCCESS) || (buffer == NULL)) {
538 
539 		IBMF_TRACE_4(IBMF_TNF_NODEBUG, DPRINT_L1,
540 		    ibmf_saa_informinfo_cb, IBMF_TNF_ERROR, "",
541 		    "ibmf_saa_informinfo_cb: %s, status = %d,"
542 		    " buffer = 0x%p, length = %d\n",
543 		    tnf_string, msg, "could not get informinfo resp",
544 		    tnf_int, status, status, tnf_opaque, buffer, buffer,
545 		    tnf_uint, length, length);
546 
547 	} else if (buffer != NULL) {
548 
549 		kmem_free(buffer, length);
550 		saa_portp->saa_pt_event_sub_success_mask |= producer_type_mask;
551 	}
552 
553 	/* if all four InformInfo responses have arrived */
554 	if (saa_portp->saa_pt_event_sub_arrive_mask ==
555 	    IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE) {
556 
557 		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
558 		    ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
559 		    "ibmf_saa_informinfo_cb(): %s, success mask = 0x%x,"
560 		    " last success mask = 0x%x\n",
561 		    tnf_string, msg, "all informinfo responses have arrived",
562 		    tnf_opaque, success_mask,
563 		    saa_portp->saa_pt_event_sub_success_mask,
564 		    tnf_opaque, last_success_mask,
565 		    saa_portp->saa_pt_event_sub_last_success_mask);
566 
567 		mutex_enter(&saa_portp->saa_pt_mutex);
568 
569 		/*
570 		 * Sending the four InformInfos is treated as one port client
571 		 * reference.  Now that all have returned decrement the
572 		 * reference count.
573 		 */
574 		ASSERT(saa_portp->saa_pt_reference_count > 0);
575 		saa_portp->saa_pt_reference_count--;
576 
577 		mutex_exit(&saa_portp->saa_pt_mutex);
578 
579 		if (saa_portp->saa_pt_event_sub_last_success_mask !=
580 		    saa_portp->saa_pt_event_sub_success_mask) {
581 
582 			IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L2,
583 			    ibmf_saa_informinfo_cb, IBMF_TNF_TRACE, "",
584 			    "ibmf_saa_informinfo_cb(): %s\n",
585 			    tnf_string, msg,
586 			    "success mask different - notifying clients");
587 
588 			/*
589 			 * save status mask to give to clients and update last
590 			 * mask for next set of subscription requests
591 			 */
592 			event_status_mask =
593 			    saa_portp->saa_pt_event_sub_last_success_mask =
594 			    saa_portp->saa_pt_event_sub_success_mask;
595 
596 			notify_clients = B_TRUE;
597 		}
598 	}
599 
600 	mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
601 
602 	if (notify_clients == B_TRUE) {
603 
604 		bzero(&event_details, sizeof (ibmf_saa_event_details_t));
605 
606 		event_details.ie_producer_event_status_mask =
607 		    event_status_mask;
608 
609 		ibmf_saa_notify_event_clients(saa_portp, &event_details,
610 		    IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, NULL);
611 	}
612 
613 bail:
614 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
615 	    ibmf_saa_informinfo_cb_end,
616 	    IBMF_TNF_TRACE, "", "ibmf_saa_informinfo_cb() exit\n");
617 }
618 
619 /*
620  * ibmf_saa_notify_event_client_task
621  *
622  * Calls the event notification callback for a registered saa client.  Called
623  * from ibmf_saa_notify_event_clients() for each client that has registered for
624  * events.  ibmf_saa_notify_event_clients() will dispatch this task on the
625  * saa_event_taskq so the client's callback can be invoked directly.
626  *
627  * Input Arguments
628  * args			pointer to ibmf_saa_event_taskq_args_t
629  *			this function will free memory associated with args
630  *
631  * Output Arguments
632  * none
633  *
634  * Returns
635  * void
636  */
637 static void
ibmf_saa_notify_event_client_task(void * args)638 ibmf_saa_notify_event_client_task(void *args)
639 {
640 	ibmf_saa_event_taskq_args_t	*event_taskq_args;
641 	saa_client_data_t		*client;
642 
643 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L3,
644 	    ibmf_saa_notify_event_client_task_start,
645 	    IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_client_task() enter\n");
646 
647 	event_taskq_args = (ibmf_saa_event_taskq_args_t *)args;
648 
649 	client = event_taskq_args->et_client;
650 
651 	/* call client's callback (client pointer is ibmf_saa_handle) */
652 	(event_taskq_args->et_callback)((ibmf_saa_handle_t)client,
653 	    event_taskq_args->et_subnet_event,
654 	    event_taskq_args->et_event_details,
655 	    event_taskq_args->et_callback_arg);
656 
657 	kmem_free(event_taskq_args->et_event_details,
658 	    sizeof (ibmf_saa_event_details_t));
659 
660 	kmem_free(event_taskq_args, sizeof (ibmf_saa_event_taskq_args_t));
661 
662 	/* decrement the callback count and signal a waiting client */
663 	mutex_enter(&client->saa_client_mutex);
664 
665 	client->saa_client_event_cb_num_active--;
666 
667 	if (client->saa_client_event_cb_num_active == 0) {
668 
669 		cv_signal(&client->saa_client_event_cb_cv);
670 
671 	}
672 
673 	mutex_exit(&client->saa_client_mutex);
674 
675 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L3,
676 	    ibmf_saa_notify_event_client_task_end,
677 	    IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_client_task() exit\n");
678 }
679 
680 /*
681  * ibmf_saa_process_subnet_event:
682  *
683  * Called when the ibmf_saa is notified of a forwarded notice.  Converts the
684  * notice into an ibmf_saa_event_details structure and calls
685  * ibmf_saa_notify_event_clients() which will notify each interested client.
686  *
687  * Input Arguments
688  * saa_portp		pointer to saa_port data
689  * notice		notice that was forwarded from SA
690  *
691  * Output Arguments
692  * none
693  *
694  * Returns
695  * void
696  */
697 static void
ibmf_saa_process_subnet_event(saa_port_t * saa_portp,ib_mad_notice_t * notice)698 ibmf_saa_process_subnet_event(saa_port_t *saa_portp, ib_mad_notice_t *notice)
699 {
700 	ibmf_saa_event_details_t	event_details;
701 	sm_trap_64_t			trap_data_details;
702 	sm_trap_144_t			cap_mask_trap_data_details;
703 	sm_trap_145_t			sys_img_trap_data_details;
704 	ibmf_saa_subnet_event_t		subnet_event;
705 
706 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
707 	    ibmf_saa_process_subnet_event_start,
708 	    IBMF_TNF_TRACE, "", "ibmf_saa_process_subnet_event() enter: "
709 	    "trap_number = 0x%x\n",
710 	    tnf_opaque, trap_number, notice->TrapNumber_DeviceID);
711 
712 	bzero(&event_details, sizeof (ibmf_saa_event_details_t));
713 
714 	/*
715 	 * fill in the appropriate fields of event_details depending on
716 	 * the trap number
717 	 */
718 	switch (notice->TrapNumber_DeviceID) {
719 
720 		case SM_GID_IN_SERVICE_TRAP:
721 
722 			ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
723 			    &trap_data_details);
724 
725 			event_details.ie_gid = trap_data_details.GIDADDR;
726 
727 			subnet_event = IBMF_SAA_EVENT_GID_AVAILABLE;
728 		break;
729 
730 
731 		case SM_GID_OUT_OF_SERVICE_TRAP:
732 
733 			ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
734 			    &trap_data_details);
735 
736 			event_details.ie_gid = trap_data_details.GIDADDR;
737 
738 			subnet_event = IBMF_SAA_EVENT_GID_UNAVAILABLE;
739 		break;
740 
741 		case SM_MGID_CREATED_TRAP:
742 
743 			ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
744 			    &trap_data_details);
745 
746 			event_details.ie_gid = trap_data_details.GIDADDR;
747 
748 			subnet_event = IBMF_SAA_EVENT_MCG_CREATED;
749 		break;
750 
751 		case SM_MGID_DESTROYED_TRAP:
752 
753 			ibmf_saa_gid_trap_parse_buffer(notice->DataDetails,
754 			    &trap_data_details);
755 
756 			event_details.ie_gid = trap_data_details.GIDADDR;
757 
758 			subnet_event = IBMF_SAA_EVENT_MCG_DELETED;
759 		break;
760 
761 		case SM_CAP_MASK_CHANGED_TRAP:
762 
763 			ibmf_saa_capmask_chg_trap_parse_buffer(
764 			    notice->DataDetails, &cap_mask_trap_data_details);
765 
766 			event_details.ie_lid =
767 			    cap_mask_trap_data_details.LIDADDR;
768 			event_details.ie_capability_mask =
769 			    cap_mask_trap_data_details.CAPABILITYMASK;
770 
771 			subnet_event = IBMF_SAA_EVENT_CAP_MASK_CHG;
772 		break;
773 
774 		case SM_SYS_IMG_GUID_CHANGED_TRAP:
775 
776 			ibmf_saa_sysimg_guid_chg_trap_parse_buffer(
777 			    notice->DataDetails, &sys_img_trap_data_details);
778 
779 			event_details.ie_lid =
780 			    sys_img_trap_data_details.LIDADDR;
781 			event_details.ie_sysimg_guid =
782 			    sys_img_trap_data_details.SYSTEMIMAGEGUID;
783 
784 			subnet_event = IBMF_SAA_EVENT_SYS_IMG_GUID_CHG;
785 		break;
786 
787 		default:
788 			/*
789 			 * do nothing if it's not one of the traps we care about
790 			 */
791 			IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
792 			    ibmf_saa_process_subnet_event_end,
793 			    IBMF_TNF_TRACE, "",
794 			    "ibmf_saa_process_subnet_event() exit: %s\n",
795 			    tnf_string, msg,
796 			    "not one of the six ibmf_saa subnet events");
797 
798 			return;
799 	}
800 
801 	ibmf_saa_notify_event_clients(saa_portp, &event_details, subnet_event,
802 	    NULL);
803 
804 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
805 	    ibmf_saa_process_subnet_event_end,
806 	    IBMF_TNF_TRACE, "", "ibmf_saa_process_subnet_event() exit\n");
807 }
808 
809 /*
810  * ibmf_saa_notify_event_clients:
811  *
812  * Called when a trap for one of the six saa subnet events arrives or there is a
813  * change in the status of event subscriptions.  Searches the list of clients
814  * with callbacks and dispatches a taskq thread to notify the client that the
815  * event occured.
816  *
817  * If some subscription request fails and a subsequent client registers for
818  * events that client needs to know that it may not receive all events.  To
819  * facilitate this, notify_event_clients() takes an optional parameter which
820  * specifies a specific client.  If registering_client is non-NULL only this
821  * client is notified.  If the parameter is NULL, all clients in the list are
822  * notified.
823  *
824  * Input Arguments
825  * saa_portp		pointer to saa_port data
826  * event_details	pointer to ibmf_saa_event_details_t for this event
827  * subnet_event		type of event that occured
828  * registering_client	pointer to client_data_t if notification should go to a
829  *			specific client; NULL if notification should go to all
830  *			clients which subscribed for events
831  *
832  * Output Arguments
833  * none
834  *
835  * Returns
836  * none
837  */
838 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)839 ibmf_saa_notify_event_clients(saa_port_t *saa_portp,
840     ibmf_saa_event_details_t *event_details,
841     ibmf_saa_subnet_event_t subnet_event, saa_client_data_t *registering_client)
842 {
843 	saa_client_data_t		*client;
844 	ibmf_saa_event_taskq_args_t	*event_taskq_args;
845 	int				status;
846 
847 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
848 	    ibmf_saa_notify_event_clients_start,
849 	    IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_clients() enter\n");
850 
851 	mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
852 
853 	if (registering_client != NULL)
854 		client = registering_client;
855 	else
856 		client = saa_portp->saa_pt_event_sub_client_list;
857 
858 	while (client != NULL) {
859 
860 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*event_taskq_args))
861 
862 		event_taskq_args = kmem_zalloc(
863 		    sizeof (ibmf_saa_event_taskq_args_t), KM_NOSLEEP);
864 		if (event_taskq_args == NULL) {
865 
866 			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
867 			    ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
868 			    "", "ibmf_saa_notify_event_clients: %s, client = "
869 			    "0x%x\n", tnf_string, msg,
870 			    "could not allocate memory for taskq args",
871 			    tnf_opaque, client, client);
872 
873 			/*
874 			 * if a particular client was not specified continue
875 			 * processing the client list
876 			 */
877 			if (registering_client == NULL)
878 				client = client->next;
879 			else
880 				client = NULL;
881 
882 			continue;
883 		}
884 
885 		/*
886 		 * each task needs its own pointer, the task will free
887 		 * up this memory
888 		 */
889 		event_taskq_args->et_event_details = kmem_zalloc(
890 		    sizeof (ibmf_saa_event_details_t), KM_NOSLEEP);
891 		if (event_taskq_args->et_event_details == NULL) {
892 
893 			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
894 			    ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
895 			    "", "ibmf_saa_notify_event_clients: %s, client = "
896 			    "0x%x\n", tnf_string, msg,
897 			    "could not allocate memory for taskq event details",
898 			    tnf_opaque, client, client);
899 
900 			kmem_free(event_taskq_args,
901 			    sizeof (ibmf_saa_event_taskq_args_t));
902 
903 			/*
904 			 * if a particular client was not specified continue
905 			 * processing the client list
906 			 */
907 			client =
908 			    (registering_client == NULL) ? client->next: NULL;
909 
910 			continue;
911 		}
912 
913 		mutex_enter(&client->saa_client_mutex);
914 
915 		/*
916 		 * don't generate callbacks if client is not active
917 		 * (it's probably closing the session)
918 		 */
919 		if (client->saa_client_state != SAA_CLIENT_STATE_ACTIVE) {
920 
921 			IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L2,
922 			    ibmf_saa_notify_event_clients, IBMF_TNF_TRACE,
923 			    "", "ibmf_saa_notify_event_clients: %s, client = "
924 			    "0x%x, state = 0x%x\n", tnf_string, msg,
925 			    "client state not active",
926 			    tnf_opaque, client, client,
927 			    tnf_opaque, state, client->saa_client_state);
928 
929 			mutex_exit(&client->saa_client_mutex);
930 
931 			kmem_free(event_taskq_args->et_event_details,
932 			    sizeof (ibmf_saa_event_details_t));
933 
934 			kmem_free(event_taskq_args,
935 			    sizeof (ibmf_saa_event_taskq_args_t));
936 
937 			/*
938 			 * if a particular client was not specified continue
939 			 * processing the client list
940 			 */
941 			client =
942 			    (registering_client == NULL) ? client->next: NULL;
943 
944 			continue;
945 		}
946 
947 		/*
948 		 * increment the callback count so the client cannot close the
949 		 * session while callbacks are active
950 		 */
951 		client->saa_client_event_cb_num_active++;
952 
953 		mutex_exit(&client->saa_client_mutex);
954 
955 		event_taskq_args->et_client = client;
956 		event_taskq_args->et_subnet_event = subnet_event;
957 
958 		bcopy(event_details, event_taskq_args->et_event_details,
959 		    sizeof (ibmf_saa_event_details_t));
960 
961 		event_taskq_args->et_callback = client->saa_client_event_cb;
962 		event_taskq_args->et_callback_arg =
963 		    client->saa_client_event_cb_arg;
964 
965 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*event_taskq_args))
966 
967 		/* dispatch taskq thread to notify client */
968 		status = taskq_dispatch(saa_statep->saa_event_taskq,
969 		    ibmf_saa_notify_event_client_task, event_taskq_args,
970 		    KM_NOSLEEP);
971 		if (status == TASKQID_INVALID) {
972 
973 			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
974 			    ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
975 			    "", "ibmf_saa_notify_event_clients: %s, client = "
976 			    "0x%x\n",
977 			    tnf_string, msg, "Could not dispatch event taskq",
978 			    tnf_opaque, client, client);
979 
980 			kmem_free(event_taskq_args->et_event_details,
981 			    sizeof (ibmf_saa_event_details_t));
982 
983 			kmem_free(event_taskq_args,
984 			    sizeof (ibmf_saa_event_taskq_args_t));
985 
986 			/*
987 			 * decrement the callback count and signal a waiting
988 			 * client
989 			 */
990 			mutex_enter(&client->saa_client_mutex);
991 
992 			client->saa_client_event_cb_num_active--;
993 
994 			if (client->saa_client_event_cb_num_active == 0) {
995 
996 				cv_signal(&client->saa_client_event_cb_cv);
997 
998 			}
999 
1000 			mutex_exit(&client->saa_client_mutex);
1001 
1002 		} else {
1003 
1004 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
1005 			    ibmf_saa_notify_event_clients_err, IBMF_TNF_ERROR,
1006 			    "", "ibmf_saa_notify_event_clients: %s, client = "
1007 			    "0x%x\n",
1008 			    tnf_string, msg, "Dispatched task to notify client",
1009 			    tnf_opaque, client, client);
1010 		}
1011 
1012 
1013 		/*
1014 		 * if a particular client was not specified continue processing
1015 		 * the client list
1016 		 */
1017 		client = (registering_client == NULL) ? client->next: NULL;
1018 	}
1019 
1020 	mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
1021 
1022 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
1023 	    ibmf_saa_notify_event_clients_end,
1024 	    IBMF_TNF_TRACE, "", "ibmf_saa_notify_event_clients() exit\n");
1025 }
1026 
1027 /*
1028  * ibmf_saa_report_cb:
1029  *
1030  * Called when a forwarded notice Report is received by ibmf_saa from the SA.
1031  * Converts the Report into an ib_mad_notice_t and calls
1032  * ibmf_saa_notify_event_clients() which will notify each subscribed ibmf_saa
1033  * client.  Also sends a response to the report to acknowledge to the SA that
1034  * this port is still up.
1035  *
1036  * This is the registered async callback with ibmf.  Only Reports should come
1037  * through this interface as all other transactions with ibmf_saa are sequenced
1038  * (ibmf_saa makes the initial request).
1039  *
1040  * This function cannot block since it is called from an ibmf callback.
1041  *
1042  * Input Arguments
1043  * ibmf_handle 			ibmf handle
1044  * msgp				pointer to ibmf_msg_t
1045  * args				pointer to saa_port data
1046  *
1047  * Output Arguments
1048  * none
1049  *
1050  * Returns
1051  * none
1052  */
1053 void
ibmf_saa_report_cb(ibmf_handle_t ibmf_handle,ibmf_msg_t * msgp,void * args)1054 ibmf_saa_report_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
1055     void *args)
1056 {
1057 	ib_mad_hdr_t		*req_mad_hdr, *resp_mad_hdr;
1058 	saa_port_t		*saa_portp, *saa_port_list_entry;
1059 	ibmf_retrans_t		ibmf_retrans;
1060 	int			ibmf_status;
1061 	ib_mad_notice_t		*notice_report;
1062 	saa_impl_trans_info_t	*trans_info;
1063 	boolean_t		port_valid;
1064 	uint16_t		mad_status;
1065 	uint16_t		attr_id;
1066 	boolean_t		response_sent = B_FALSE;
1067 	size_t			length;
1068 	int			status;
1069 
1070 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
1071 	    ibmf_saa_report_cb_start,
1072 	    IBMF_TNF_TRACE, "", "ibmf_saa_report_cb() enter\n");
1073 
1074 	_NOTE(ASSUMING_PROTECTED(*msgp))
1075 
1076 	saa_portp = (saa_port_t *)args;
1077 
1078 	port_valid = B_FALSE;
1079 
1080 	/* check whether this portp is still valid */
1081 	mutex_enter(&saa_statep->saa_port_list_mutex);
1082 
1083 	saa_port_list_entry = saa_statep->saa_port_list;
1084 	while (saa_port_list_entry != NULL) {
1085 
1086 		if (saa_port_list_entry == saa_portp) {
1087 
1088 			port_valid = ibmf_saa_is_valid(saa_portp, B_FALSE);
1089 
1090 			break;
1091 		}
1092 		saa_port_list_entry = saa_port_list_entry->next;
1093 	}
1094 
1095 	mutex_exit(&saa_statep->saa_port_list_mutex);
1096 
1097 	if (port_valid == B_FALSE) {
1098 
1099 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
1100 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1101 		    "ibmf_saa_report_cb: %s, saa_port = 0x%p\n",
1102 		    tnf_string, msg, "port no longer valid",
1103 		    tnf_opaque, saa_portp, saa_portp);
1104 
1105 		goto bail;
1106 	}
1107 
1108 	req_mad_hdr = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
1109 
1110 	/* drop packet if status is bad */
1111 	if ((msgp->im_msg_status != IBMF_SUCCESS) ||
1112 	    (req_mad_hdr == NULL) ||
1113 	    ((mad_status = b2h16(req_mad_hdr->Status)) != SA_STATUS_NO_ERROR)) {
1114 
1115 		IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L1,
1116 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1117 		    "ibmf_saa_report_cb: %s, msg_status = 0x%x,"
1118 		    " req_mad_hdr = 0x%p, mad_status = 0x%x\n",
1119 		    tnf_string, msg, "Bad ibmf status",
1120 		    tnf_opaque, msg_status, msgp->im_msg_status,
1121 		    tnf_opaque, req_mad_hdr, req_mad_hdr,
1122 		    tnf_opaque, mad_status,
1123 		    (req_mad_hdr == NULL ? 0 : mad_status));
1124 
1125 		goto bail;
1126 	}
1127 
1128 	/* drop packet if class version is not correct */
1129 	if (req_mad_hdr->ClassVersion != SAA_MAD_CLASS_VERSION) {
1130 
1131 		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L1,
1132 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1133 		    "ibmf_saa_report_cb: %s, msg_class_ver = 0x%x,"
1134 		    " ibmf_saa_class_ver = 0x%x\n",
1135 		    tnf_string, msg, "Bad class version",
1136 		    tnf_opaque, msg_class_ver, req_mad_hdr->ClassVersion,
1137 		    tnf_opaque, ibmf_saa_class_ver, SAA_MAD_CLASS_VERSION);
1138 
1139 		goto bail;
1140 	}
1141 
1142 
1143 	/*
1144 	 * only care about notice reports(); should not get any other type
1145 	 * of method or attribute
1146 	 */
1147 	if (((attr_id = b2h16(req_mad_hdr->AttributeID)) != SA_NOTICE_ATTRID) ||
1148 	    (req_mad_hdr->R_Method != SA_SUBN_ADM_REPORT)) {
1149 
1150 		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L2,
1151 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1152 		    "ibmf_saa_report_cb: %s, attr_id = 0x%x, "
1153 		    "method = 0x%x\n",
1154 		    tnf_string, msg, "Unsolicited message not notice report",
1155 		    tnf_opaque, attr_id, attr_id,
1156 		    tnf_opaque, method, req_mad_hdr->R_Method);
1157 
1158 		goto bail;
1159 	}
1160 
1161 	/*
1162 	 * unpack the data into a ib_mad_notice_t; the data details are left
1163 	 * as packed data and will be unpacked by process_subnet_event()
1164 	 * is_get_resp parameter is set to B_TRUE since cl_data_len will
1165 	 * probably be set to 200 bytes by ibmf (it's not an RMPP trans)
1166 	 */
1167 	status = ibmf_saa_utils_unpack_payload(
1168 	    msgp->im_msgbufs_recv.im_bufs_cl_data,
1169 	    msgp->im_msgbufs_recv.im_bufs_cl_data_len, SA_NOTICE_ATTRID,
1170 	    (void **)&notice_report, &length, 0, B_TRUE, KM_NOSLEEP);
1171 	if (status != IBMF_SUCCESS) {
1172 
1173 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
1174 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1175 		    "ibmf_saa_report_cb: %s, status = %d",
1176 		    tnf_string, msg, "Could not unpack data",
1177 		    tnf_int, status, status);
1178 
1179 		goto bail;
1180 	}
1181 
1182 	ASSERT(length == sizeof (ib_mad_notice_t));
1183 
1184 	ibmf_saa_process_subnet_event(saa_portp, notice_report);
1185 
1186 	kmem_free(notice_report, length);
1187 
1188 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*resp_mad_hdr))
1189 
1190 	/* send ReportResp */
1191 	resp_mad_hdr = kmem_zalloc(sizeof (ib_mad_hdr_t), KM_SLEEP);
1192 
1193 	bcopy(req_mad_hdr, resp_mad_hdr, sizeof (ib_mad_hdr_t));
1194 
1195 	resp_mad_hdr->R_Method = SA_SUBN_ADM_REPORT_RESP;
1196 
1197 	msgp->im_msgbufs_send.im_bufs_mad_hdr = resp_mad_hdr;
1198 	msgp->im_msgbufs_send.im_bufs_cl_hdr = kmem_zalloc(
1199 	    msgp->im_msgbufs_recv.im_bufs_cl_hdr_len, KM_SLEEP);
1200 	msgp->im_msgbufs_send.im_bufs_cl_hdr_len =
1201 	    msgp->im_msgbufs_recv.im_bufs_cl_hdr_len;
1202 
1203 	/* only headers are needed */
1204 	msgp->im_msgbufs_send.im_bufs_cl_data = NULL;
1205 	msgp->im_msgbufs_send.im_bufs_cl_data_len = 0;
1206 
1207 	/*
1208 	 * report_cb cannot block because it's in the context of an ibmf
1209 	 * callback.  So the response needs to be sent asynchronously.
1210 	 * ibmf_saa_async_cb is an appropriate callback to use for the response.
1211 	 * Set up a trans_info structure as saa_async_cb expects.  But don't use
1212 	 * ibmf_saa_impl_send_request() to send the response since that function
1213 	 * does unncessary steps in this case (like allocating a new ibmf msg).
1214 	 * Only the si_trans_port field needs to be filled in.
1215 	 */
1216 	trans_info = kmem_zalloc(sizeof (saa_impl_trans_info_t), KM_NOSLEEP);
1217 	if (trans_info == NULL) {
1218 
1219 		IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
1220 		    ibmf_saa_report_cb_err, IBMF_TNF_TRACE, "",
1221 		    "ibmf_saa_report_cb: %s",
1222 		    tnf_string, msg, "could not allocate trans_info structure");
1223 
1224 		goto bail;
1225 	}
1226 
1227 	trans_info->si_trans_port = saa_portp;
1228 
1229 	mutex_enter(&saa_portp->saa_pt_mutex);
1230 
1231 	bcopy(&saa_portp->saa_pt_ibmf_retrans, &ibmf_retrans,
1232 	    sizeof (ibmf_retrans_t));
1233 
1234 	saa_portp->saa_pt_num_outstanding_trans++;
1235 
1236 	mutex_exit(&saa_portp->saa_pt_mutex);
1237 
1238 	ASSERT(ibmf_handle == saa_portp->saa_pt_ibmf_handle);
1239 
1240 	ibmf_status = ibmf_msg_transport(ibmf_handle,
1241 	    saa_portp->saa_pt_qp_handle, msgp, &ibmf_retrans, ibmf_saa_async_cb,
1242 	    trans_info, 0);
1243 	if (ibmf_status != IBMF_SUCCESS) {
1244 
1245 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L1,
1246 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1247 		    "ibmf_saa_report_cb: %s, msg_status = 0x%x\n",
1248 		    tnf_string, msg, "Could not send report response",
1249 		    tnf_int, ibmf_status, ibmf_status);
1250 
1251 		mutex_enter(&saa_portp->saa_pt_mutex);
1252 
1253 		ASSERT(saa_portp->saa_pt_num_outstanding_trans > 0);
1254 		saa_portp->saa_pt_num_outstanding_trans--;
1255 
1256 		mutex_exit(&saa_portp->saa_pt_mutex);
1257 
1258 		kmem_free(trans_info, sizeof (saa_impl_trans_info_t));
1259 
1260 	} else {
1261 
1262 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
1263 		    ibmf_saa_report_cb, IBMF_TNF_TRACE, "",
1264 		    "ibmf_saa_report_cb: %s\n",
1265 		    tnf_string, msg, "Asynchronous Report response sent");
1266 
1267 		response_sent = B_TRUE;
1268 	}
1269 
1270 bail:
1271 	if (response_sent == B_FALSE) {
1272 		ibmf_status = ibmf_free_msg(ibmf_handle, &msgp);
1273 		ASSERT(ibmf_status == IBMF_SUCCESS);
1274 	}
1275 
1276 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
1277 	    ibmf_saa_informinfo_cb_end, IBMF_TNF_TRACE, "",
1278 	    "ibmf_saa_report_cb() exit\n");
1279 }
1280 
1281 /*
1282  * ibmf_saa_add_event_subscriber:
1283  *
1284  * Adds an interested client to the list of subscribers for events for a port.
1285  * If it's the first client, generates the subscription requests.
1286  * This function must only be called if event_args is not null
1287  *
1288  * Input Arguments
1289  *
1290  * client		pointer to client data (client->saa_port should be set)
1291  * event_args		pointer to event_args passed in from client (non-NULL)
1292  *
1293  * Output Arguments
1294  * none
1295  *
1296  * Returns
1297  * void
1298  */
1299 void
ibmf_saa_add_event_subscriber(saa_client_data_t * client,ibmf_saa_subnet_event_args_t * event_args)1300 ibmf_saa_add_event_subscriber(saa_client_data_t *client,
1301     ibmf_saa_subnet_event_args_t *event_args)
1302 {
1303 	saa_port_t			*saa_portp;
1304 	boolean_t			first_client;
1305 	uint8_t				producer_status_mask;
1306 	ibmf_saa_event_details_t	event_details;
1307 
1308 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
1309 	    ibmf_saa_add_event_subscriber_start, IBMF_TNF_TRACE, "",
1310 	    "ibmf_saa_add_event_subscriber() enter\n");
1311 
1312 	/* event_args should be checked before calling this function */
1313 	ASSERT(event_args != NULL);
1314 
1315 	/* don't add client if no callback function is specified */
1316 	if (event_args->is_event_callback == NULL)
1317 		return;
1318 
1319 	saa_portp = client->saa_client_port;
1320 
1321 	client->saa_client_event_cb = event_args->is_event_callback;
1322 	client->saa_client_event_cb_arg = event_args->is_event_callback_arg;
1323 
1324 	/*
1325 	 * insert this client onto the list; this list is used when a
1326 	 * Report arrives to call each client's callback
1327 	 */
1328 	mutex_enter(&saa_portp->saa_pt_event_sub_mutex);
1329 
1330 	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
1331 	    ibmf_sa_session_open, IBMF_TNF_TRACE, "",
1332 	    "ibmf_saa_add_event_subscriber: %s, client = 0x%x\n",
1333 	    tnf_string, msg, "Adding client to event subscriber list",
1334 	    tnf_opaque, client, client);
1335 
1336 	if (saa_portp->saa_pt_event_sub_client_list == NULL)
1337 		first_client = B_TRUE;
1338 	else {
1339 		first_client = B_FALSE;
1340 		producer_status_mask =
1341 		    saa_portp->saa_pt_event_sub_last_success_mask;
1342 	}
1343 
1344 	client->next = saa_portp->saa_pt_event_sub_client_list;
1345 	saa_portp->saa_pt_event_sub_client_list = client;
1346 
1347 	mutex_exit(&saa_portp->saa_pt_event_sub_mutex);
1348 
1349 	if (first_client == B_TRUE) {
1350 
1351 		/*
1352 		 * increment the reference count by one to account for
1353 		 * the subscription requests.  All four InformInfo's are
1354 		 * sent as one port client reference.
1355 		 */
1356 		mutex_enter(&saa_portp->saa_pt_mutex);
1357 
1358 		saa_portp->saa_pt_reference_count++;
1359 
1360 		mutex_exit(&saa_portp->saa_pt_mutex);
1361 
1362 		/* subscribe for subnet events */
1363 		ibmf_saa_subscribe_events(saa_portp, B_TRUE, B_FALSE);
1364 
1365 	} else if (producer_status_mask != IBMF_SAA_PORT_EVENT_SUB_ALL_ARRIVE) {
1366 
1367 		/*
1368 		 * if this is not the first client and the producer status mask
1369 		 * is not all success, generate a callback to indicate to the
1370 		 * client that not all events will be forwarded
1371 		 */
1372 		bzero(&event_details, sizeof (ibmf_saa_event_details_t));
1373 
1374 		event_details.ie_producer_event_status_mask =
1375 		    producer_status_mask;
1376 
1377 		ibmf_saa_notify_event_clients(saa_portp, &event_details,
1378 		    IBMF_SAA_EVENT_SUBSCRIBER_STATUS_CHG, client);
1379 	}
1380 
1381 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
1382 	    ibmf_saa_add_event_subscriber_end, IBMF_TNF_TRACE, "",
1383 	    "ibmf_saa_add_event_subscriber() exit\n");
1384 }
1385