1 //------------------------------------------------------------------------------
2 // <copyright file="credit_dist.c" company="Atheros">
3 // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved.
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Author(s): ="Atheros"
22 //==============================================================================
28 #define ATH_MODULE_NAME misc
31 #include "common_drv.h"
33 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
35 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
36 #define CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS 1
39 #define DATA_SVCS_USED 3
41 #define DATA_SVCS_USED 4
44 static void RedistributeCredits(struct common_credit_state_info
*pCredInfo
,
45 struct htc_endpoint_credit_dist
*pEPDistList
);
47 static void SeekCredits(struct common_credit_state_info
*pCredInfo
,
48 struct htc_endpoint_credit_dist
*pEPDistList
);
50 /* reduce an ep's credits back to a set limit */
51 static INLINE
void ReduceCredits(struct common_credit_state_info
*pCredInfo
,
52 struct htc_endpoint_credit_dist
*pEpDist
,
57 /* set the new limit */
58 pEpDist
->TxCreditsAssigned
= Limit
;
60 if (pEpDist
->TxCredits
<= Limit
) {
64 /* figure out how much to take away */
65 credits
= pEpDist
->TxCredits
- Limit
;
67 pEpDist
->TxCredits
-= credits
;
68 pCredInfo
->CurrentFreeCredits
+= credits
;
71 /* give an endpoint some credits from the free credit pool */
72 #define GiveCredits(pCredInfo,pEpDist,credits) \
74 (pEpDist)->TxCredits += (credits); \
75 (pEpDist)->TxCreditsAssigned += (credits); \
76 (pCredInfo)->CurrentFreeCredits -= (credits); \
80 /* default credit init callback.
81 * This function is called in the context of HTCStart() to setup initial (application-specific)
82 * credit distributions */
83 static void ar6000_credit_init(void *Context
,
84 struct htc_endpoint_credit_dist
*pEPList
,
87 struct htc_endpoint_credit_dist
*pCurEpDist
;
89 struct common_credit_state_info
*pCredInfo
= (struct common_credit_state_info
*)Context
;
91 pCredInfo
->CurrentFreeCredits
= TotalCredits
;
92 pCredInfo
->TotalAvailableCredits
= TotalCredits
;
96 /* run through the list and initialize */
97 while (pCurEpDist
!= NULL
) {
99 /* set minimums for each endpoint */
100 pCurEpDist
->TxCreditsMin
= pCurEpDist
->TxCreditsPerMaxMsg
;
102 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
104 if (TotalCredits
> 4)
106 if ((pCurEpDist
->ServiceID
== WMI_DATA_BK_SVC
) || (pCurEpDist
->ServiceID
== WMI_DATA_BE_SVC
)){
107 /* assign at least min credits to lower than VO priority services */
108 GiveCredits(pCredInfo
,pCurEpDist
,pCurEpDist
->TxCreditsMin
);
110 SET_EP_ACTIVE(pCurEpDist
);
116 if (pCurEpDist
->ServiceID
== WMI_CONTROL_SVC
) {
117 /* give control service some credits */
118 GiveCredits(pCredInfo
,pCurEpDist
,pCurEpDist
->TxCreditsMin
);
119 /* control service is always marked active, it never goes inactive EVER */
120 SET_EP_ACTIVE(pCurEpDist
);
121 } else if (pCurEpDist
->ServiceID
== WMI_DATA_BK_SVC
) {
122 /* this is the lowest priority data endpoint, save this off for easy access */
123 pCredInfo
->pLowestPriEpDist
= pCurEpDist
;
126 /* Streams have to be created (explicit | implicit)for all kinds
127 * of traffic. BE endpoints are also inactive in the beginning.
128 * When BE traffic starts it creates implicit streams that
129 * redistributes credits.
132 /* note, all other endpoints have minimums set but are initially given NO credits.
133 * Credits will be distributed as traffic activity demands */
134 pCurEpDist
= pCurEpDist
->pNext
;
137 if (pCredInfo
->CurrentFreeCredits
<= 0) {
138 AR_DEBUG_PRINTF(ATH_LOG_INF
, ("Not enough credits (%d) to do credit distributions \n", TotalCredits
));
144 pCurEpDist
= pEPList
;
145 /* now run through the list and set max operating credit limits for everyone */
146 while (pCurEpDist
!= NULL
) {
147 if (pCurEpDist
->ServiceID
== WMI_CONTROL_SVC
) {
148 /* control service max is just 1 max message */
149 pCurEpDist
->TxCreditsNorm
= pCurEpDist
->TxCreditsPerMaxMsg
;
151 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
153 * We use a simple calculation here, we take the remaining credits and
154 * determine how many max messages this can cover and then set each endpoint's
155 * normal value equal to 3/4 this amount.
157 count
= (pCredInfo
->CurrentFreeCredits
/pCurEpDist
->TxCreditsPerMaxMsg
) * pCurEpDist
->TxCreditsPerMaxMsg
;
158 count
= (count
* 3) >> 2;
159 count
= max(count
,pCurEpDist
->TxCreditsPerMaxMsg
);
161 pCurEpDist
->TxCreditsNorm
= count
;
164 pCurEpDist
= pCurEpDist
->pNext
;
170 /* default credit distribution callback
171 * This callback is invoked whenever endpoints require credit distributions.
172 * A lock is held while this function is invoked, this function shall NOT block.
173 * The pEPDistList is a list of distribution structures in prioritized order as
174 * defined by the call to the HTCSetCreditDistribution() api.
177 static void ar6000_credit_distribute(void *Context
,
178 struct htc_endpoint_credit_dist
*pEPDistList
,
179 HTC_CREDIT_DIST_REASON Reason
)
181 struct htc_endpoint_credit_dist
*pCurEpDist
;
182 struct common_credit_state_info
*pCredInfo
= (struct common_credit_state_info
*)Context
;
185 case HTC_CREDIT_DIST_SEND_COMPLETE
:
186 pCurEpDist
= pEPDistList
;
187 /* we are given the start of the endpoint distribution list.
188 * There may be one or more endpoints to service.
189 * Run through the list and distribute credits */
190 while (pCurEpDist
!= NULL
) {
192 if (pCurEpDist
->TxCreditsToDist
> 0) {
193 /* return the credits back to the endpoint */
194 pCurEpDist
->TxCredits
+= pCurEpDist
->TxCreditsToDist
;
195 /* always zero out when we are done */
196 pCurEpDist
->TxCreditsToDist
= 0;
198 if (pCurEpDist
->TxCredits
> pCurEpDist
->TxCreditsAssigned
) {
199 /* reduce to the assigned limit, previous credit reductions
200 * could have caused the limit to change */
201 ReduceCredits(pCredInfo
, pCurEpDist
, pCurEpDist
->TxCreditsAssigned
);
204 if (pCurEpDist
->TxCredits
> pCurEpDist
->TxCreditsNorm
) {
205 /* oversubscribed endpoints need to reduce back to normal */
206 ReduceCredits(pCredInfo
, pCurEpDist
, pCurEpDist
->TxCreditsNorm
);
209 if (!IS_EP_ACTIVE(pCurEpDist
)) {
210 /* endpoint is inactive, now check for messages waiting for credits */
211 if (pCurEpDist
->TxQueueDepth
== 0) {
212 /* EP is inactive and there are no pending messages,
213 * reduce credits back to zero to recover credits */
214 ReduceCredits(pCredInfo
, pCurEpDist
, 0);
219 pCurEpDist
= pCurEpDist
->pNext
;
224 case HTC_CREDIT_DIST_ACTIVITY_CHANGE
:
225 RedistributeCredits(pCredInfo
,pEPDistList
);
227 case HTC_CREDIT_DIST_SEEK_CREDITS
:
228 SeekCredits(pCredInfo
,pEPDistList
);
230 case HTC_DUMP_CREDIT_STATE
:
231 AR_DEBUG_PRINTF(ATH_DEBUG_ERR
, ("Credit Distribution, total : %d, free : %d\n",
232 pCredInfo
->TotalAvailableCredits
, pCredInfo
->CurrentFreeCredits
));
239 /* sanity checks done after each distribution action */
240 A_ASSERT(pCredInfo
->CurrentFreeCredits
<= pCredInfo
->TotalAvailableCredits
);
241 A_ASSERT(pCredInfo
->CurrentFreeCredits
>= 0);
245 /* redistribute credits based on activity change */
246 static void RedistributeCredits(struct common_credit_state_info
*pCredInfo
,
247 struct htc_endpoint_credit_dist
*pEPDistList
)
249 struct htc_endpoint_credit_dist
*pCurEpDist
= pEPDistList
;
251 /* walk through the list and remove credits from inactive endpoints */
252 while (pCurEpDist
!= NULL
) {
254 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
256 if ((pCurEpDist
->ServiceID
== WMI_DATA_BK_SVC
) || (pCurEpDist
->ServiceID
== WMI_DATA_BE_SVC
)) {
257 /* force low priority streams to always be active to retain their minimum credit distribution */
258 SET_EP_ACTIVE(pCurEpDist
);
262 if (pCurEpDist
->ServiceID
!= WMI_CONTROL_SVC
) {
263 if (!IS_EP_ACTIVE(pCurEpDist
)) {
264 if (pCurEpDist
->TxQueueDepth
== 0) {
265 /* EP is inactive and there are no pending messages, reduce credits back to zero */
266 ReduceCredits(pCredInfo
, pCurEpDist
, 0);
268 /* we cannot zero the credits assigned to this EP, but to keep
269 * the credits available for these leftover packets, reduce to
271 ReduceCredits(pCredInfo
, pCurEpDist
, pCurEpDist
->TxCreditsMin
);
276 /* NOTE in the active case, we do not need to do anything further,
277 * when an EP goes active and needs credits, HTC will call into
278 * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */
280 pCurEpDist
= pCurEpDist
->pNext
;
285 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */
286 static void SeekCredits(struct common_credit_state_info
*pCredInfo
,
287 struct htc_endpoint_credit_dist
*pEPDist
)
289 struct htc_endpoint_credit_dist
*pCurEpDist
;
295 if (pEPDist
->ServiceID
== WMI_CONTROL_SVC
) {
296 /* we never oversubscribe on the control service, this is not
297 * a high performance path and the target never holds onto control
298 * credits for too long */
302 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
303 if (pEPDist
->ServiceID
== WMI_DATA_VI_SVC
) {
304 if ((pEPDist
->TxCreditsAssigned
>= pEPDist
->TxCreditsNorm
)) {
305 /* limit VI service from oversubscribing */
310 if (pEPDist
->ServiceID
== WMI_DATA_VO_SVC
) {
311 if ((pEPDist
->TxCreditsAssigned
>= pEPDist
->TxCreditsNorm
)) {
312 /* limit VO service from oversubscribing */
317 if (pEPDist
->ServiceID
== WMI_DATA_VI_SVC
) {
318 if ((pEPDist
->TxCreditsAssigned
>= pEPDist
->TxCreditsNorm
) ||
319 (pCredInfo
->CurrentFreeCredits
<= pEPDist
->TxCreditsPerMaxMsg
)) {
320 /* limit VI service from oversubscribing */
321 /* at least one free credit will not be used by VI */
326 if (pEPDist
->ServiceID
== WMI_DATA_VO_SVC
) {
327 if ((pEPDist
->TxCreditsAssigned
>= pEPDist
->TxCreditsNorm
) ||
328 (pCredInfo
->CurrentFreeCredits
<= pEPDist
->TxCreditsPerMaxMsg
)) {
329 /* limit VO service from oversubscribing */
330 /* at least one free credit will not be used by VO */
336 /* for all other services, we follow a simple algorithm of
337 * 1. checking the free pool for credits
338 * 2. checking lower priority endpoints for credits to take */
340 /* give what we can */
341 credits
= min(pCredInfo
->CurrentFreeCredits
,pEPDist
->TxCreditsSeek
);
343 if (credits
>= pEPDist
->TxCreditsSeek
) {
344 /* we found some to fulfill the seek request */
348 /* we don't have enough in the free pool, try taking away from lower priority services
350 * The rule for taking away credits:
351 * 1. Only take from lower priority endpoints
352 * 2. Only take what is allocated above the minimum (never starve an endpoint completely)
353 * 3. Only take what you need.
357 /* starting at the lowest priority */
358 pCurEpDist
= pCredInfo
->pLowestPriEpDist
;
360 /* work backwards until we hit the endpoint again */
361 while (pCurEpDist
!= pEPDist
) {
362 /* calculate how many we need so far */
363 need
= pEPDist
->TxCreditsSeek
- pCredInfo
->CurrentFreeCredits
;
365 if ((pCurEpDist
->TxCreditsAssigned
- need
) >= pCurEpDist
->TxCreditsMin
) {
366 /* the current one has been allocated more than it's minimum and it
367 * has enough credits assigned above it's minimum to fulfill our need
368 * try to take away just enough to fulfill our need */
369 ReduceCredits(pCredInfo
,
371 pCurEpDist
->TxCreditsAssigned
- need
);
373 if (pCredInfo
->CurrentFreeCredits
>= pEPDist
->TxCreditsSeek
) {
379 pCurEpDist
= pCurEpDist
->pPrev
;
382 /* return what we can get */
383 credits
= min(pCredInfo
->CurrentFreeCredits
,pEPDist
->TxCreditsSeek
);
387 /* did we find some credits? */
389 /* give what we can */
390 GiveCredits(pCredInfo
, pEPDist
, credits
);
395 /* initialize and setup credit distribution */
396 int ar6000_setup_credit_dist(HTC_HANDLE HTCHandle
, struct common_credit_state_info
*pCredInfo
)
398 HTC_SERVICE_ID servicepriority
[5];
400 A_MEMZERO(pCredInfo
,sizeof(struct common_credit_state_info
));
402 servicepriority
[0] = WMI_CONTROL_SVC
; /* highest */
403 servicepriority
[1] = WMI_DATA_VO_SVC
;
404 servicepriority
[2] = WMI_DATA_VI_SVC
;
405 servicepriority
[3] = WMI_DATA_BE_SVC
;
406 servicepriority
[4] = WMI_DATA_BK_SVC
; /* lowest */
408 /* set callbacks and priority list */
409 HTCSetCreditDistribution(HTCHandle
,
411 ar6000_credit_distribute
,