1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36/*
37 * Abstract:
38 *    Implementation of osm_vl15_t.
39 * This object represents the VL15 Interface object.
40 * This object is part of the opensm family of objects.
41 */
42
43#if HAVE_CONFIG_H
44#  include <config.h>
45#endif				/* HAVE_CONFIG_H */
46
47#include <string.h>
48#include <iba/ib_types.h>
49#include <complib/cl_thread.h>
50#include <vendor/osm_vendor_api.h>
51#include <opensm/osm_vl15intf.h>
52#include <opensm/osm_madw.h>
53#include <opensm/osm_log.h>
54#include <opensm/osm_helper.h>
55
56/**********************************************************************
57 **********************************************************************/
58
59static void vl15_send_mad(osm_vl15_t * p_vl, osm_madw_t * p_madw)
60{
61	ib_api_status_t status;
62
63	/*
64	   Non-response-expected mads are not throttled on the wire
65	   since we can have no confirmation that they arrived
66	   at their destination.
67	 */
68	if (p_madw->resp_expected == TRUE)
69		/*
70		   Note that other threads may not see the response MAD
71		   arrive before send() even returns.
72		   In that case, the wire count would temporarily go negative.
73		   To avoid this confusion, preincrement the counts on the
74		   assumption that send() will succeed.
75		 */
76		cl_atomic_inc(&p_vl->p_stats->qp0_mads_outstanding_on_wire);
77	else
78		cl_atomic_inc(&p_vl->p_stats->qp0_unicasts_sent);
79
80	cl_atomic_inc(&p_vl->p_stats->qp0_mads_sent);
81
82	status = osm_vendor_send(osm_madw_get_bind_handle(p_madw),
83				 p_madw, p_madw->resp_expected);
84
85	if (status == IB_SUCCESS) {
86		OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
87			"%u QP0 MADs on wire, %u outstanding, "
88			"%u unicasts sent, %u total sent\n",
89			p_vl->p_stats->qp0_mads_outstanding_on_wire,
90			p_vl->p_stats->qp0_mads_outstanding,
91			p_vl->p_stats->qp0_unicasts_sent,
92			p_vl->p_stats->qp0_mads_sent);
93		return;
94	}
95
96	OSM_LOG(p_vl->p_log, OSM_LOG_ERROR, "ERR 3E03: "
97		"MAD send failed (%s)\n", ib_get_err_str(status));
98
99	/*
100	   The MAD was never successfully sent, so
101	   fix up the pre-incremented count values.
102	 */
103
104	/* Decrement qp0_mads_sent that were incremented in the code above.
105	   qp0_mads_outstanding will be decremented by send error callback
106	   (called by osm_vendor_send() */
107	cl_atomic_dec(&p_vl->p_stats->qp0_mads_sent);
108	if (!p_madw->resp_expected)
109		cl_atomic_dec(&p_vl->p_stats->qp0_unicasts_sent);
110}
111
112static void __osm_vl15_poller(IN void *p_ptr)
113{
114	ib_api_status_t status;
115	osm_madw_t *p_madw;
116	osm_vl15_t *const p_vl = (osm_vl15_t *) p_ptr;
117	cl_qlist_t *p_fifo;
118
119	OSM_LOG_ENTER(p_vl->p_log);
120
121	if (p_vl->thread_state == OSM_THREAD_STATE_NONE)
122		p_vl->thread_state = OSM_THREAD_STATE_RUN;
123
124	while (p_vl->thread_state == OSM_THREAD_STATE_RUN) {
125		/*
126		   Start servicing the FIFOs by pulling off MAD wrappers
127		   and passing them to the transport interface.
128		   There are lots of corner cases here so tread carefully.
129
130		   The unicast FIFO has priority, since somebody is waiting
131		   for a timely response.
132		 */
133		cl_spinlock_acquire(&p_vl->lock);
134
135		if (cl_qlist_count(&p_vl->ufifo) != 0)
136			p_fifo = &p_vl->ufifo;
137		else
138			p_fifo = &p_vl->rfifo;
139
140		p_madw = (osm_madw_t *) cl_qlist_remove_head(p_fifo);
141
142		cl_spinlock_release(&p_vl->lock);
143
144		if (p_madw != (osm_madw_t *) cl_qlist_end(p_fifo)) {
145			OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
146				"Servicing p_madw = %p\n", p_madw);
147			if (osm_log_is_active(p_vl->p_log, OSM_LOG_FRAMES))
148				osm_dump_dr_smp(p_vl->p_log,
149						osm_madw_get_smp_ptr(p_madw),
150						OSM_LOG_FRAMES);
151
152			vl15_send_mad(p_vl, p_madw);
153		} else
154			/*
155			   The VL15 FIFO is empty, so we have nothing left to do.
156			 */
157			status = cl_event_wait_on(&p_vl->signal,
158						  EVENT_NO_TIMEOUT, TRUE);
159
160		while ((p_vl->p_stats->qp0_mads_outstanding_on_wire >=
161			(int32_t) p_vl->max_wire_smps) &&
162		       (p_vl->thread_state == OSM_THREAD_STATE_RUN)) {
163			status = cl_event_wait_on(&p_vl->signal,
164						  EVENT_NO_TIMEOUT, TRUE);
165			if (status != CL_SUCCESS) {
166				OSM_LOG(p_vl->p_log, OSM_LOG_ERROR, "ERR 3E02: "
167					"Event wait failed (%s)\n",
168					CL_STATUS_MSG(status));
169				break;
170			}
171		}
172	}
173
174	/*
175	   since we abort immediately when the state != OSM_THREAD_STATE_RUN
176	   we might have some mads on the queues. After the thread exits
177	   the vl15 destroy routine should put these mads back...
178	 */
179
180	OSM_LOG_EXIT(p_vl->p_log);
181}
182
183/**********************************************************************
184 **********************************************************************/
185void osm_vl15_construct(IN osm_vl15_t * const p_vl)
186{
187	memset(p_vl, 0, sizeof(*p_vl));
188	p_vl->state = OSM_VL15_STATE_INIT;
189	p_vl->thread_state = OSM_THREAD_STATE_NONE;
190	cl_event_construct(&p_vl->signal);
191	cl_spinlock_construct(&p_vl->lock);
192	cl_qlist_init(&p_vl->rfifo);
193	cl_qlist_init(&p_vl->ufifo);
194	cl_thread_construct(&p_vl->poller);
195}
196
197/**********************************************************************
198 **********************************************************************/
199void
200osm_vl15_destroy(IN osm_vl15_t * const p_vl, IN struct osm_mad_pool *p_pool)
201{
202	osm_madw_t *p_madw;
203
204	OSM_LOG_ENTER(p_vl->p_log);
205
206	/*
207	   Signal our threads that we're leaving.
208	 */
209	p_vl->thread_state = OSM_THREAD_STATE_EXIT;
210
211	/*
212	   Don't trigger unless event has been initialized.
213	   Destroy the thread before we tear down the other objects.
214	 */
215	if (p_vl->state != OSM_VL15_STATE_INIT)
216		cl_event_signal(&p_vl->signal);
217
218	cl_thread_destroy(&p_vl->poller);
219
220	/*
221	   Return the outstanding messages to the pool
222	 */
223
224	cl_spinlock_acquire(&p_vl->lock);
225
226	while (!cl_is_qlist_empty(&p_vl->rfifo)) {
227		p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->rfifo);
228		osm_mad_pool_put(p_pool, p_madw);
229	}
230	while (!cl_is_qlist_empty(&p_vl->ufifo)) {
231		p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->ufifo);
232		osm_mad_pool_put(p_pool, p_madw);
233	}
234
235	cl_spinlock_release(&p_vl->lock);
236
237	cl_event_destroy(&p_vl->signal);
238	p_vl->state = OSM_VL15_STATE_INIT;
239	cl_spinlock_destroy(&p_vl->lock);
240
241	OSM_LOG_EXIT(p_vl->p_log);
242}
243
244/**********************************************************************
245 **********************************************************************/
246ib_api_status_t
247osm_vl15_init(IN osm_vl15_t * const p_vl,
248	      IN osm_vendor_t * const p_vend,
249	      IN osm_log_t * const p_log,
250	      IN osm_stats_t * const p_stats, IN const int32_t max_wire_smps)
251{
252	ib_api_status_t status = IB_SUCCESS;
253
254	OSM_LOG_ENTER(p_log);
255
256	p_vl->p_vend = p_vend;
257	p_vl->p_log = p_log;
258	p_vl->p_stats = p_stats;
259	p_vl->max_wire_smps = max_wire_smps;
260
261	status = cl_event_init(&p_vl->signal, FALSE);
262	if (status != IB_SUCCESS)
263		goto Exit;
264
265	p_vl->state = OSM_VL15_STATE_READY;
266
267	status = cl_spinlock_init(&p_vl->lock);
268	if (status != IB_SUCCESS)
269		goto Exit;
270
271	/*
272	   Initialize the thread after all other dependent objects
273	   have been initialized.
274	 */
275	status = cl_thread_init(&p_vl->poller, __osm_vl15_poller, p_vl,
276				"opensm poller");
277	if (status != IB_SUCCESS)
278		goto Exit;
279
280Exit:
281	OSM_LOG_EXIT(p_log);
282	return (status);
283}
284
285/**********************************************************************
286 **********************************************************************/
287void osm_vl15_poll(IN osm_vl15_t * const p_vl)
288{
289	OSM_LOG_ENTER(p_vl->p_log);
290
291	CL_ASSERT(p_vl->state == OSM_VL15_STATE_READY);
292
293	/*
294	   If we have room for more VL15 MADs on the wire,
295	   then signal the poller thread.
296
297	   This is not an airtight check, since the poller thread
298	   could be just about to send another MAD as we signal
299	   the event here.  To cover this rare case, the poller
300	   thread checks for a spurious wake-up.
301	 */
302	if (p_vl->p_stats->qp0_mads_outstanding_on_wire <
303	    (int32_t) p_vl->max_wire_smps) {
304		OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
305			"Signalling poller thread\n");
306		cl_event_signal(&p_vl->signal);
307	}
308
309	OSM_LOG_EXIT(p_vl->p_log);
310}
311
312/**********************************************************************
313 **********************************************************************/
314void osm_vl15_post(IN osm_vl15_t * const p_vl, IN osm_madw_t * const p_madw)
315{
316	OSM_LOG_ENTER(p_vl->p_log);
317
318	CL_ASSERT(p_vl->state == OSM_VL15_STATE_READY);
319
320	OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG, "Posting p_madw = 0x%p\n", p_madw);
321
322	/*
323	   Determine in which fifo to place the pending madw.
324	 */
325	cl_spinlock_acquire(&p_vl->lock);
326	if (p_madw->resp_expected == TRUE) {
327		cl_qlist_insert_tail(&p_vl->rfifo, &p_madw->list_item);
328		osm_stats_inc_qp0_outstanding(p_vl->p_stats);
329	} else
330		cl_qlist_insert_tail(&p_vl->ufifo, &p_madw->list_item);
331	cl_spinlock_release(&p_vl->lock);
332
333	OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
334		"%u QP0 MADs on wire, %u QP0 MADs outstanding\n",
335		p_vl->p_stats->qp0_mads_outstanding_on_wire,
336		p_vl->p_stats->qp0_mads_outstanding);
337
338	osm_vl15_poll(p_vl);
339
340	OSM_LOG_EXIT(p_vl->p_log);
341}
342
343void
344osm_vl15_shutdown(IN osm_vl15_t * const p_vl,
345		  IN osm_mad_pool_t * const p_mad_pool)
346{
347	osm_madw_t *p_madw;
348
349	OSM_LOG_ENTER(p_vl->p_log);
350
351	/* we only should get here after the VL15 interface was initialized */
352	CL_ASSERT(p_vl->state == OSM_VL15_STATE_READY);
353
354	/* grap a lock on the object */
355	cl_spinlock_acquire(&p_vl->lock);
356
357	/* go over all outstanding MADs and retire their transactions */
358
359	/* first we handle the list of response MADs */
360	p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->ufifo);
361	while (p_madw != (osm_madw_t *) cl_qlist_end(&p_vl->ufifo)) {
362		OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
363			"Releasing Response p_madw = %p\n", p_madw);
364
365		osm_mad_pool_put(p_mad_pool, p_madw);
366
367		p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->ufifo);
368	}
369
370	/* Request MADs we send out */
371	p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->rfifo);
372	while (p_madw != (osm_madw_t *) cl_qlist_end(&p_vl->rfifo)) {
373		OSM_LOG(p_vl->p_log, OSM_LOG_DEBUG,
374			"Releasing Request p_madw = %p\n", p_madw);
375
376		osm_mad_pool_put(p_mad_pool, p_madw);
377		osm_stats_dec_qp0_outstanding(p_vl->p_stats);
378
379		p_madw = (osm_madw_t *) cl_qlist_remove_head(&p_vl->rfifo);
380	}
381
382	/* free the lock */
383	cl_spinlock_release(&p_vl->lock);
384
385	OSM_LOG_EXIT(p_vl->p_log);
386}
387