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 
22 /*
23  *  Copyright (c) 2002-2005 Neterion, Inc.
24  *  All right Reserved.
25  *
26  *  FileName :    xgehal-channel-fp.c
27  *
28  *  Description:  HAL channel object functionality (fast path)
29  *
30  *  Created:      10 June 2004
31  */
32 
33 #ifdef XGE_DEBUG_FP
34 #include "xgehal-channel.h"
35 #endif
36 
37 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
38 __hal_channel_dtr_alloc(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
39 {
40 	void **tmp_arr;
41 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
42 	unsigned long flags = 0;
43 
44 	if (channel->reserve_length - channel->reserve_top >
45 						channel->reserve_threshold) {
46 
47 _alloc_after_swap:
48 		*dtrh = channel->reserve_arr[--channel->reserve_length];
49 
50 		xge_debug_channel(XGE_TRACE, "dtrh 0x%llx allocated, "
51 				   "channel %d:%d:%d, reserve_idx %d",
52 				   (unsigned long long)(ulong_t)*dtrh,
53 				   channel->type, channel->post_qid,
54 				   channel->compl_qid, channel->reserve_length);
55 
56 		return XGE_HAL_OK;
57 	}
58 
59 	xge_os_spin_lock_irq(&channel->free_lock, flags);
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 		xge_os_spin_unlock_irq(&channel->free_lock, flags);
86 
87 		goto _alloc_after_swap;
88 	}
89 
90 	xge_os_spin_unlock_irq(&channel->free_lock, flags);
91 
92 	xge_debug_channel(XGE_TRACE, "channel %d:%d:%d is empty!",
93 			   channel->type, channel->post_qid,
94 			   channel->compl_qid);
95 
96 	channel->stats.out_of_dtrs_cnt++;
97 
98 	*dtrh = NULL;
99 	return XGE_HAL_INF_OUT_OF_DESCRIPTORS;
100 }
101 
102 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
103 __hal_channel_dtr_restore(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
104 			  int offset)
105 {
106 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
107 
108 	/* restore a previously allocated dtrh at current offset and update
109 	 * the available reserve length accordingly. If dtrh is null just
110 	 * update the reserve length, only */
111 
112 	if (dtrh) {
113 		channel->reserve_arr[channel->reserve_length + offset] = dtrh;
114 		xge_debug_channel(XGE_TRACE, "dtrh 0x%llx restored for "
115 			"channel %d:%d:%d, offset %d at reserve index %d, ",
116 			(unsigned long long)(ulong_t)dtrh, channel->type,
117 			channel->post_qid, channel->compl_qid, offset,
118 			channel->reserve_length + offset);
119 	}
120 	else {
121 		channel->reserve_length += offset;
122 		xge_debug_channel(XGE_TRACE, "channel %d:%d:%d, restored "
123 			"for offset %d, new reserve_length %d, free length %d",
124 			channel->type, channel->post_qid, channel->compl_qid,
125 			offset, channel->reserve_length, channel->free_length);
126 	}
127 }
128 
129 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
130 __hal_channel_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
131 {
132 	xge_hal_channel_t *channel    = (xge_hal_channel_t*)channelh;
133 
134 	xge_assert(channel->work_arr[channel->post_index] == NULL);
135 
136 	channel->work_arr[channel->post_index++] = dtrh;
137 
138         /* wrap-around */
139 	if (channel->post_index == channel->length)
140 		channel->post_index = 0;
141 }
142 
143 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
144 __hal_channel_dtr_try_complete(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
145 {
146 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
147 
148 	xge_assert(channel->work_arr);
149 	xge_assert(channel->compl_index < channel->length);
150 
151 	*dtrh = channel->work_arr[channel->compl_index];
152 }
153 
154 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
155 __hal_channel_dtr_complete(xge_hal_channel_h channelh)
156 {
157 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
158 
159 	channel->work_arr[channel->compl_index] = NULL;
160 
161 	/* wrap-around */
162 	if (++channel->compl_index == channel->length)
163 		channel->compl_index = 0;
164 
165 	channel->stats.total_compl_cnt++;
166 }
167 
168 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
169 __hal_channel_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
170 {
171 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
172 
173 	channel->free_arr[--channel->free_length] = dtrh;
174 
175 	xge_debug_channel(XGE_TRACE, "dtrh 0x%llx freed, "
176 			   "channel %d:%d:%d, new free_length %d",
177 			   (unsigned long long)(ulong_t)dtrh,
178 			   channel->type, channel->post_qid,
179 			   channel->compl_qid, channel->free_length);
180 }
181 
182 /**
183  * xge_hal_channel_userdata - Get user-specified channel context.
184  * @channelh: Channel handle. Obtained via xge_hal_channel_open().
185  *
186  * Returns: per-channel "user data", which can be any ULD-defined context.
187  * The %userdata "gets" into the channel at open time
188  * (see xge_hal_channel_open()).
189  *
190  * See also: xge_hal_channel_open().
191  */
192 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void*
193 xge_hal_channel_userdata(xge_hal_channel_h channelh)
194 {
195 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
196 
197 	return channel->userdata;
198 }
199 
200 /**
201  * xge_hal_channel_id - Get channel ID.
202  * @channelh: Channel handle. Obtained via xge_hal_channel_open().
203  *
204  * Returns: channel ID. For link layer channel id is the number
205  * in the range from 0 to 7 that identifies hardware ring or fifo,
206  * depending on the channel type.
207  */
208 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
209 xge_hal_channel_id(xge_hal_channel_h channelh)
210 {
211 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
212 
213 	return channel->post_qid;
214 }
215 
216 /**
217  * xge_hal_check_alignment - Check buffer alignment and calculate the
218  * "misaligned" portion.
219  * @dma_pointer: DMA address of the buffer.
220  * @size: Buffer size, in bytes.
221  * @alignment: Alignment "granularity" (see below), in bytes.
222  * @copy_size: Maximum number of bytes to "extract" from the buffer
223  * (in order to spost it as a separate scatter-gather entry). See below.
224  *
225  * Check buffer alignment and calculate "misaligned" portion, if exists.
226  * The buffer is considered aligned if its address is multiple of
227  * the specified @alignment. If this is the case,
228  * xge_hal_check_alignment() returns zero.
229  * Otherwise, xge_hal_check_alignment() uses the last argument,
230  * @copy_size,
231  * to calculate the size to "extract" from the buffer. The @copy_size
232  * may or may not be equal @alignment. The difference between these two
233  * arguments is that the @alignment is used to make the decision: aligned
234  * or not aligned. While the @copy_size is used to calculate the portion
235  * of the buffer to "extract", i.e. to post as a separate entry in the
236  * transmit descriptor. For example, the combination
237  * @alignment=8 and @copy_size=64 will work okay on AMD Opteron boxes.
238  *
239  * Note: @copy_size should be a multiple of @alignment. In many practical
240  * cases @copy_size and @alignment will probably be equal.
241  *
242  * See also: xge_hal_fifo_dtr_buffer_set_aligned().
243  */
244 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
245 xge_hal_check_alignment(dma_addr_t dma_pointer, int size, int alignment,
246 		int copy_size)
247 {
248 	int misaligned_size;
249 
250 	misaligned_size = (int)(dma_pointer & (alignment - 1));
251 	if (!misaligned_size) {
252 		return 0;
253 	}
254 
255 	if (size > copy_size) {
256 		misaligned_size = (int)(dma_pointer & (copy_size - 1));
257 		misaligned_size = copy_size - misaligned_size;
258 	} else {
259 		misaligned_size = size;
260 	}
261 
262 	return misaligned_size;
263 }
264