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