1 /*-
2  * Copyright (c) 2002-2007 Neterion, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: stable/10/sys/dev/nxge/xgehal/xgehal-channel-fp.c 173139 2007-10-29 14:19:32Z rwatson $
27  */
28 
29 #ifdef XGE_DEBUG_FP
30 #include <dev/nxge/include/xgehal-channel.h>
31 #endif
32 
33 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL xge_hal_status_e
__hal_channel_dtr_alloc(xge_hal_channel_h channelh,xge_hal_dtr_h * dtrh)34 __hal_channel_dtr_alloc(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
35 {
36 	void **tmp_arr;
37 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
38 #if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
39 	unsigned long flags = 0;
40 #endif
41 	if (channel->terminating) {
42 	    return XGE_HAL_FAIL;
43 	}
44 
45 	if (channel->reserve_length - channel->reserve_top >
46 	                    channel->reserve_threshold) {
47 
48 _alloc_after_swap:
49 	    *dtrh = channel->reserve_arr[--channel->reserve_length];
50 
51 	    xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" allocated, "
52 	               "channel %d:%d:%d, reserve_idx %d",
53 	               (unsigned long long)(ulong_t)*dtrh,
54 	               channel->type, channel->post_qid,
55 	               channel->compl_qid, channel->reserve_length);
56 
57 	    return XGE_HAL_OK;
58 	}
59 
60 #if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
61 	xge_os_spin_lock_irq(&channel->free_lock, flags);
62 #elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
63 	xge_os_spin_lock(&channel->free_lock);
64 #endif
65 
66 	/* switch between empty and full arrays */
67 
68 	/* the idea behind such a design is that by having free and reserved
69 	 * arrays separated we basically separated irq and non-irq parts.
70 	 * i.e. no additional lock need to be done when we free a resource */
71 
72 	if (channel->reserve_initial - channel->free_length >
73 	                channel->reserve_threshold) {
74 
75 	    tmp_arr = channel->reserve_arr;
76 	    channel->reserve_arr = channel->free_arr;
77 	    channel->reserve_length = channel->reserve_initial;
78 	    channel->free_arr = tmp_arr;
79 	    channel->reserve_top = channel->free_length;
80 	    channel->free_length = channel->reserve_initial;
81 
82 	    channel->stats.reserve_free_swaps_cnt++;
83 
84 	    xge_debug_channel(XGE_TRACE,
85 	           "switch on channel %d:%d:%d, reserve_length %d, "
86 	           "free_length %d", channel->type, channel->post_qid,
87 	           channel->compl_qid, channel->reserve_length,
88 	           channel->free_length);
89 
90 #if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
91 	    xge_os_spin_unlock_irq(&channel->free_lock, flags);
92 #elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
93 	    xge_os_spin_unlock(&channel->free_lock);
94 #endif
95 
96 	    goto _alloc_after_swap;
97 	}
98 
99 #if defined(XGE_HAL_RX_MULTI_FREE_IRQ) || defined(XGE_HAL_TX_MULTI_FREE_IRQ)
100 	xge_os_spin_unlock_irq(&channel->free_lock, flags);
101 #elif defined(XGE_HAL_RX_MULTI_FREE) || defined(XGE_HAL_TX_MULTI_FREE)
102 	xge_os_spin_unlock(&channel->free_lock);
103 #endif
104 
105 	xge_debug_channel(XGE_TRACE, "channel %d:%d:%d is empty!",
106 	           channel->type, channel->post_qid,
107 	           channel->compl_qid);
108 
109 	channel->stats.full_cnt++;
110 
111 	*dtrh = NULL;
112 	return XGE_HAL_INF_OUT_OF_DESCRIPTORS;
113 }
114 
115 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_channel_dtr_restore(xge_hal_channel_h channelh,xge_hal_dtr_h dtrh,int offset)116 __hal_channel_dtr_restore(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh,
117 	          int offset)
118 {
119 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
120 
121 	/* restore a previously allocated dtrh at current offset and update
122 	 * the available reserve length accordingly. If dtrh is null just
123 	 * update the reserve length, only */
124 
125 	if (dtrh) {
126 	    channel->reserve_arr[channel->reserve_length + offset] = dtrh;
127 	    xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" restored for "
128 	        "channel %d:%d:%d, offset %d at reserve index %d, ",
129 	        (unsigned long long)(ulong_t)dtrh, channel->type,
130 	        channel->post_qid, channel->compl_qid, offset,
131 	        channel->reserve_length + offset);
132 	}
133 	else {
134 	    channel->reserve_length += offset;
135 	    xge_debug_channel(XGE_TRACE, "channel %d:%d:%d, restored "
136 	        "for offset %d, new reserve_length %d, free length %d",
137 	        channel->type, channel->post_qid, channel->compl_qid,
138 	        offset, channel->reserve_length, channel->free_length);
139 	}
140 }
141 
142 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_channel_dtr_post(xge_hal_channel_h channelh,xge_hal_dtr_h dtrh)143 __hal_channel_dtr_post(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
144 {
145 	xge_hal_channel_t *channel    = (xge_hal_channel_t*)channelh;
146 
147 	xge_assert(channel->work_arr[channel->post_index] == NULL);
148 
149 	channel->work_arr[channel->post_index++] = dtrh;
150 
151 	    /* wrap-around */
152 	if (channel->post_index == channel->length)
153 	    channel->post_index = 0;
154 }
155 
156 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_channel_dtr_try_complete(xge_hal_channel_h channelh,xge_hal_dtr_h * dtrh)157 __hal_channel_dtr_try_complete(xge_hal_channel_h channelh, xge_hal_dtr_h *dtrh)
158 {
159 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
160 
161 	xge_assert(channel->work_arr);
162 	xge_assert(channel->compl_index < channel->length);
163 
164 	*dtrh = channel->work_arr[channel->compl_index];
165 }
166 
167 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_channel_dtr_complete(xge_hal_channel_h channelh)168 __hal_channel_dtr_complete(xge_hal_channel_h channelh)
169 {
170 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
171 
172 	channel->work_arr[channel->compl_index] = NULL;
173 
174 	/* wrap-around */
175 	if (++channel->compl_index == channel->length)
176 	    channel->compl_index = 0;
177 
178 	channel->stats.total_compl_cnt++;
179 }
180 
181 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void
__hal_channel_dtr_free(xge_hal_channel_h channelh,xge_hal_dtr_h dtrh)182 __hal_channel_dtr_free(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh)
183 {
184 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
185 
186 	channel->free_arr[--channel->free_length] = dtrh;
187 
188 	xge_debug_channel(XGE_TRACE, "dtrh 0x"XGE_OS_LLXFMT" freed, "
189 	           "channel %d:%d:%d, new free_length %d",
190 	           (unsigned long long)(ulong_t)dtrh,
191 	           channel->type, channel->post_qid,
192 	           channel->compl_qid, channel->free_length);
193 }
194 
195 /**
196  * xge_hal_channel_dtr_count
197  * @channelh: Channel handle. Obtained via xge_hal_channel_open().
198  *
199  * Retreive number of DTRs available. This function can not be called
200  * from data path.
201  */
202 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
xge_hal_channel_dtr_count(xge_hal_channel_h channelh)203 xge_hal_channel_dtr_count(xge_hal_channel_h channelh)
204 {
205 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
206 
207 	return ((channel->reserve_length - channel->reserve_top) +
208 	    (channel->reserve_initial - channel->free_length) -
209 	                    channel->reserve_threshold);
210 }
211 
212 /**
213  * xge_hal_channel_userdata - Get user-specified channel context.
214  * @channelh: Channel handle. Obtained via xge_hal_channel_open().
215  *
216  * Returns: per-channel "user data", which can be any ULD-defined context.
217  * The %userdata "gets" into the channel at open time
218  * (see xge_hal_channel_open()).
219  *
220  * See also: xge_hal_channel_open().
221  */
222 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL void*
xge_hal_channel_userdata(xge_hal_channel_h channelh)223 xge_hal_channel_userdata(xge_hal_channel_h channelh)
224 {
225 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
226 
227 	return channel->userdata;
228 }
229 
230 /**
231  * xge_hal_channel_id - Get channel ID.
232  * @channelh: Channel handle. Obtained via xge_hal_channel_open().
233  *
234  * Returns: channel ID. For link layer channel id is the number
235  * in the range from 0 to 7 that identifies hardware ring or fifo,
236  * depending on the channel type.
237  */
238 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
xge_hal_channel_id(xge_hal_channel_h channelh)239 xge_hal_channel_id(xge_hal_channel_h channelh)
240 {
241 	xge_hal_channel_t *channel = (xge_hal_channel_t *)channelh;
242 
243 	return channel->post_qid;
244 }
245 
246 /**
247  * xge_hal_check_alignment - Check buffer alignment and calculate the
248  * "misaligned" portion.
249  * @dma_pointer: DMA address of the buffer.
250  * @size: Buffer size, in bytes.
251  * @alignment: Alignment "granularity" (see below), in bytes.
252  * @copy_size: Maximum number of bytes to "extract" from the buffer
253  * (in order to spost it as a separate scatter-gather entry). See below.
254  *
255  * Check buffer alignment and calculate "misaligned" portion, if exists.
256  * The buffer is considered aligned if its address is multiple of
257  * the specified @alignment. If this is the case,
258  * xge_hal_check_alignment() returns zero.
259  * Otherwise, xge_hal_check_alignment() uses the last argument,
260  * @copy_size,
261  * to calculate the size to "extract" from the buffer. The @copy_size
262  * may or may not be equal @alignment. The difference between these two
263  * arguments is that the @alignment is used to make the decision: aligned
264  * or not aligned. While the @copy_size is used to calculate the portion
265  * of the buffer to "extract", i.e. to post as a separate entry in the
266  * transmit descriptor. For example, the combination
267  * @alignment=8 and @copy_size=64 will work okay on AMD Opteron boxes.
268  *
269  * Note: @copy_size should be a multiple of @alignment. In many practical
270  * cases @copy_size and @alignment will probably be equal.
271  *
272  * See also: xge_hal_fifo_dtr_buffer_set_aligned().
273  */
274 __HAL_STATIC_CHANNEL __HAL_INLINE_CHANNEL int
xge_hal_check_alignment(dma_addr_t dma_pointer,int size,int alignment,int copy_size)275 xge_hal_check_alignment(dma_addr_t dma_pointer, int size, int alignment,
276 	    int copy_size)
277 {
278 	int misaligned_size;
279 
280 	misaligned_size = (int)(dma_pointer & (alignment - 1));
281 	if (!misaligned_size) {
282 	    return 0;
283 	}
284 
285 	if (size > copy_size) {
286 	    misaligned_size = (int)(dma_pointer & (copy_size - 1));
287 	    misaligned_size = copy_size - misaligned_size;
288 	} else {
289 	    misaligned_size = size;
290 	}
291 
292 	return misaligned_size;
293 }
294 
295