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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
21 * Copyright (c) 2002-2006 Neterion, Inc.
22 */
23
24#ifdef XGE_DEBUG_FP
25#include "xgehal-channel.h"
26#endif
27
28__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
29__hal_channel_dtr_alloc(xge_hal_channel_h channelh,	xge_hal_dtr_h *dtrh)
30{
31	void **tmp_arr;
32	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
33#if	defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
34	unsigned long flags	= 0;
35#endif
36	if (channel->terminating) {
37		return XGE_HAL_FAIL;
38	}
39
40	if (channel->reserve_length	- channel->reserve_top >
41						channel->reserve_threshold)	{
42
43_alloc_after_swap:
44		*dtrh =	channel->reserve_arr[--channel->reserve_length];
45
46		xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" allocated,	"
47				   "channel	%d:%d:%d, reserve_idx %d",
48				   (unsigned long long)(ulong_t)*dtrh,
49				   channel->type, channel->post_qid,
50				   channel->compl_qid, channel->reserve_length);
51
52		return XGE_HAL_OK;
53	}
54
55#if	defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
56	xge_os_spin_lock_irq(&channel->free_lock, flags);
57#elif defined(XGE_HAL_RX_MULTI_FREE) ||	defined(XGE_HAL_TX_MULTI_FREE)
58	xge_os_spin_lock(&channel->free_lock);
59#endif
60
61	/* switch between empty	and	full arrays	*/
62
63	/* the idea	behind such	a design is	that by	having free	and	reserved
64	 * arrays separated	we basically separated irq and non-irq parts.
65	 * i.e.	no additional lock need	to be done when	we free	a resource */
66
67	if (channel->reserve_initial - channel->free_length	>
68					channel->reserve_threshold)	{
69
70		tmp_arr	= channel->reserve_arr;
71		channel->reserve_arr = channel->free_arr;
72		channel->reserve_length	= channel->reserve_initial;
73		channel->free_arr =	tmp_arr;
74		channel->reserve_top = channel->free_length;
75		channel->free_length = channel->reserve_initial;
76
77		channel->stats.reserve_free_swaps_cnt++;
78
79		xge_debug_channel(XGE_TRACE,
80			   "switch on channel %d:%d:%d,	reserve_length %d, "
81			   "free_length	%d", channel->type,	channel->post_qid,
82			   channel->compl_qid, channel->reserve_length,
83			   channel->free_length);
84
85#if	defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
86		xge_os_spin_unlock_irq(&channel->free_lock,	flags);
87#elif defined(XGE_HAL_RX_MULTI_FREE) ||	defined(XGE_HAL_TX_MULTI_FREE)
88		xge_os_spin_unlock(&channel->free_lock);
89#endif
90
91		goto _alloc_after_swap;
92	}
93
94#if	defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
95	xge_os_spin_unlock_irq(&channel->free_lock,	flags);
96#elif defined(XGE_HAL_RX_MULTI_FREE) ||	defined(XGE_HAL_TX_MULTI_FREE)
97	xge_os_spin_unlock(&channel->free_lock);
98#endif
99
100	xge_debug_channel(XGE_TRACE, "channel %d:%d:%d is empty!",
101			   channel->type, channel->post_qid,
102			   channel->compl_qid);
103
104	channel->stats.full_cnt++;
105
106	*dtrh =	NULL;
107	return XGE_HAL_INF_OUT_OF_DESCRIPTORS;
108}
109
110__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
111__hal_channel_dtr_restore(xge_hal_channel_h	channelh, xge_hal_dtr_h	dtrh,
112			  int offset)
113{
114	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
115
116	/* restore a previously	allocated dtrh at current offset and update
117	 * the available reserve length	accordingly. If	dtrh is	null just
118	 * update the reserve length, only */
119
120	if (dtrh) {
121		channel->reserve_arr[channel->reserve_length + offset] = dtrh;
122		xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" restored for "
123			"channel %d:%d:%d, offset %d at	reserve	index %d, ",
124			(unsigned long long)(ulong_t)dtrh, channel->type,
125			channel->post_qid, channel->compl_qid, offset,
126			channel->reserve_length	+ offset);
127	}
128	else {
129		channel->reserve_length	+= offset;
130		xge_debug_channel(XGE_TRACE, "channel %d:%d:%d,	restored "
131			"for offset	%d,	new	reserve_length %d, free	length %d",
132			channel->type, channel->post_qid, channel->compl_qid,
133			offset,	channel->reserve_length, channel->free_length);
134	}
135}
136
137__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
138__hal_channel_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
139{
140	xge_hal_channel_t *channel	  =	(xge_hal_channel_t*)channelh;
141
142	xge_assert(channel->work_arr[channel->post_index] == NULL);
143
144	channel->work_arr[channel->post_index++] = dtrh;
145
146		/* wrap-around */
147	if (channel->post_index	== channel->length)
148		channel->post_index	= 0;
149}
150
151__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
152__hal_channel_dtr_try_complete(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
153{
154	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
155
156	xge_assert(channel->work_arr);
157	xge_assert(channel->compl_index	< channel->length);
158
159	*dtrh =	channel->work_arr[channel->compl_index];
160}
161
162__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
163__hal_channel_dtr_complete(xge_hal_channel_h channelh)
164{
165	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
166
167	channel->work_arr[channel->compl_index]	= NULL;
168
169	/* wrap-around */
170	if (++channel->compl_index == channel->length)
171		channel->compl_index = 0;
172
173	channel->stats.total_compl_cnt++;
174}
175
176__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
177__hal_channel_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
178{
179	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
180
181	channel->free_arr[--channel->free_length] =	dtrh;
182
183	xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" freed,	"
184			   "channel	%d:%d:%d, new free_length %d",
185			   (unsigned long long)(ulong_t)dtrh,
186			   channel->type, channel->post_qid,
187			   channel->compl_qid, channel->free_length);
188}
189
190/**
191 * xge_hal_channel_dtr_count
192 * @channelh: Channel handle. Obtained via xge_hal_channel_open().
193 *
194 * Retreive number of DTRs available. This function can not be called
195 * from data path.
196 */
197__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
198xge_hal_channel_dtr_count(xge_hal_channel_h channelh)
199{
200	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
201
202	return ((channel->reserve_length - channel->reserve_top) +
203		(channel->reserve_initial - channel->free_length) -
204						channel->reserve_threshold);
205}
206
207/**
208 * xge_hal_channel_userdata	- Get user-specified channel context.
209 * @channelh: Channel handle. Obtained via xge_hal_channel_open().
210 *
211 * Returns:	per-channel	"user data", which can be any ULD-defined context.
212 * The %userdata "gets"	into the channel at	open time
213 * (see	xge_hal_channel_open()).
214 *
215 * See also: xge_hal_channel_open().
216 */
217__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void*
218xge_hal_channel_userdata(xge_hal_channel_h channelh)
219{
220	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
221
222	return channel->userdata;
223}
224
225/**
226 * xge_hal_channel_id -	Get	channel	ID.
227 * @channelh: Channel handle. Obtained via xge_hal_channel_open().
228 *
229 * Returns:	channel	ID.	For	link layer channel id is the number
230 * in the range	from 0 to 7	that identifies	hardware ring or fifo,
231 * depending on	the	channel	type.
232 */
233__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
234xge_hal_channel_id(xge_hal_channel_h channelh)
235{
236	xge_hal_channel_t *channel = (xge_hal_channel_t	*)channelh;
237
238	return channel->post_qid;
239}
240
241/**
242 * xge_hal_check_alignment - Check buffer alignment	and	calculate the
243 * "misaligned"	portion.
244 * @dma_pointer: DMA address of	the	buffer.
245 * @size: Buffer size, in bytes.
246 * @alignment: Alignment "granularity" (see	below),	in bytes.
247 * @copy_size: Maximum number of bytes to "extract"	from the buffer
248 * (in order to	spost it as	a separate scatter-gather entry). See below.
249 *
250 * Check buffer	alignment and calculate	"misaligned" portion, if exists.
251 * The buffer is considered	aligned	if its address is multiple of
252 * the specified @alignment. If	this is	the	case,
253 * xge_hal_check_alignment() returns zero.
254 * Otherwise, xge_hal_check_alignment()	uses the last argument,
255 * @copy_size,
256 * to calculate	the	size to	"extract" from the buffer. The @copy_size
257 * may or may not be equal @alignment. The difference between these	two
258 * arguments is	that the @alignment	is used	to make	the	decision: aligned
259 * or not aligned. While the @copy_size	is used	to calculate the portion
260 * of the buffer to	"extract", i.e.	to post	as a separate entry	in the
261 * transmit	descriptor.	For	example, the combination
262 * @alignment=8	and	@copy_size=64 will work	okay on	AMD	Opteron	boxes.
263 *
264 * Note: @copy_size	should be a	multiple of	@alignment.	In many	practical
265 * cases @copy_size	and	@alignment will	probably be	equal.
266 *
267 * See also: xge_hal_fifo_dtr_buffer_set_aligned().
268 */
269__HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
270xge_hal_check_alignment(dma_addr_t dma_pointer,	int	size, int alignment,
271		int	copy_size)
272{
273	int	misaligned_size;
274
275	misaligned_size	= (int)(dma_pointer	& (alignment - 1));
276	if (!misaligned_size) {
277		return 0;
278	}
279
280	if (size > copy_size) {
281		misaligned_size	= (int)(dma_pointer	& (copy_size - 1));
282		misaligned_size	= copy_size	- misaligned_size;
283	} else {
284		misaligned_size	= size;
285	}
286
287	return misaligned_size;
288}
289
290