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 //==============================================================================
27 #define ATH_MODULE_NAME misc
30 #include "common_drv.h"
32 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
34 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
35 #define CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS 1
38 #define DATA_SVCS_USED 3
40 #define DATA_SVCS_USED 4
43 static void RedistributeCredits(struct common_credit_state_info *pCredInfo,
44 struct htc_endpoint_credit_dist *pEPDistList);
46 static void SeekCredits(struct common_credit_state_info *pCredInfo,
47 struct htc_endpoint_credit_dist *pEPDistList);
49 /* reduce an ep's credits back to a set limit */
50 static INLINE void ReduceCredits(struct common_credit_state_info *pCredInfo,
51 struct htc_endpoint_credit_dist *pEpDist,
56 /* set the new limit */
57 pEpDist->TxCreditsAssigned = Limit;
59 if (pEpDist->TxCredits <= Limit) {
63 /* figure out how much to take away */
64 credits = pEpDist->TxCredits - Limit;
66 pEpDist->TxCredits -= credits;
67 pCredInfo->CurrentFreeCredits += credits;
70 /* give an endpoint some credits from the free credit pool */
71 #define GiveCredits(pCredInfo,pEpDist,credits) \
73 (pEpDist)->TxCredits += (credits); \
74 (pEpDist)->TxCreditsAssigned += (credits); \
75 (pCredInfo)->CurrentFreeCredits -= (credits); \
79 /* default credit init callback.
80 * This function is called in the context of HTCStart() to setup initial (application-specific)
81 * credit distributions */
82 static void ar6000_credit_init(void *Context,
83 struct htc_endpoint_credit_dist *pEPList,
86 struct htc_endpoint_credit_dist *pCurEpDist;
88 struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context;
90 pCredInfo->CurrentFreeCredits = TotalCredits;
91 pCredInfo->TotalAvailableCredits = TotalCredits;
95 /* run through the list and initialize */
96 while (pCurEpDist != NULL) {
98 /* set minimums for each endpoint */
99 pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
101 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
103 if (TotalCredits > 4)
105 if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC) || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)){
106 /* assign at least min credits to lower than VO priority services */
107 GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
109 SET_EP_ACTIVE(pCurEpDist);
115 if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
116 /* give control service some credits */
117 GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
118 /* control service is always marked active, it never goes inactive EVER */
119 SET_EP_ACTIVE(pCurEpDist);
120 } else if (pCurEpDist->ServiceID == WMI_DATA_BK_SVC) {
121 /* this is the lowest priority data endpoint, save this off for easy access */
122 pCredInfo->pLowestPriEpDist = pCurEpDist;
125 /* Streams have to be created (explicit | implicit)for all kinds
126 * of traffic. BE endpoints are also inactive in the beginning.
127 * When BE traffic starts it creates implicit streams that
128 * redistributes credits.
131 /* note, all other endpoints have minimums set but are initially given NO credits.
132 * Credits will be distributed as traffic activity demands */
133 pCurEpDist = pCurEpDist->pNext;
136 if (pCredInfo->CurrentFreeCredits <= 0) {
137 AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits));
143 pCurEpDist = pEPList;
144 /* now run through the list and set max operating credit limits for everyone */
145 while (pCurEpDist != NULL) {
146 if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
147 /* control service max is just 1 max message */
148 pCurEpDist->TxCreditsNorm = pCurEpDist->TxCreditsPerMaxMsg;
150 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
152 * We use a simple calculation here, we take the remaining credits and
153 * determine how many max messages this can cover and then set each endpoint's
154 * normal value equal to 3/4 this amount.
156 count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg;
157 count = (count * 3) >> 2;
158 count = max(count,pCurEpDist->TxCreditsPerMaxMsg);
160 pCurEpDist->TxCreditsNorm = count;
163 pCurEpDist = pCurEpDist->pNext;
169 /* default credit distribution callback
170 * This callback is invoked whenever endpoints require credit distributions.
171 * A lock is held while this function is invoked, this function shall NOT block.
172 * The pEPDistList is a list of distribution structures in prioritized order as
173 * defined by the call to the HTCSetCreditDistribution() api.
176 static void ar6000_credit_distribute(void *Context,
177 struct htc_endpoint_credit_dist *pEPDistList,
178 HTC_CREDIT_DIST_REASON Reason)
180 struct htc_endpoint_credit_dist *pCurEpDist;
181 struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context;
184 case HTC_CREDIT_DIST_SEND_COMPLETE :
185 pCurEpDist = pEPDistList;
186 /* we are given the start of the endpoint distribution list.
187 * There may be one or more endpoints to service.
188 * Run through the list and distribute credits */
189 while (pCurEpDist != NULL) {
191 if (pCurEpDist->TxCreditsToDist > 0) {
192 /* return the credits back to the endpoint */
193 pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
194 /* always zero out when we are done */
195 pCurEpDist->TxCreditsToDist = 0;
197 if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) {
198 /* reduce to the assigned limit, previous credit reductions
199 * could have caused the limit to change */
200 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned);
203 if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) {
204 /* oversubscribed endpoints need to reduce back to normal */
205 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm);
208 if (!IS_EP_ACTIVE(pCurEpDist)) {
209 /* endpoint is inactive, now check for messages waiting for credits */
210 if (pCurEpDist->TxQueueDepth == 0) {
211 /* EP is inactive and there are no pending messages,
212 * reduce credits back to zero to recover credits */
213 ReduceCredits(pCredInfo, pCurEpDist, 0);
218 pCurEpDist = pCurEpDist->pNext;
223 case HTC_CREDIT_DIST_ACTIVITY_CHANGE :
224 RedistributeCredits(pCredInfo,pEPDistList);
226 case HTC_CREDIT_DIST_SEEK_CREDITS :
227 SeekCredits(pCredInfo,pEPDistList);
229 case HTC_DUMP_CREDIT_STATE :
230 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, free : %d\n",
231 pCredInfo->TotalAvailableCredits, pCredInfo->CurrentFreeCredits));
238 /* sanity checks done after each distribution action */
239 A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits);
240 A_ASSERT(pCredInfo->CurrentFreeCredits >= 0);
244 /* redistribute credits based on activity change */
245 static void RedistributeCredits(struct common_credit_state_info *pCredInfo,
246 struct htc_endpoint_credit_dist *pEPDistList)
248 struct htc_endpoint_credit_dist *pCurEpDist = pEPDistList;
250 /* walk through the list and remove credits from inactive endpoints */
251 while (pCurEpDist != NULL) {
253 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
255 if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC) || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)) {
256 /* force low priority streams to always be active to retain their minimum credit distribution */
257 SET_EP_ACTIVE(pCurEpDist);
261 if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) {
262 if (!IS_EP_ACTIVE(pCurEpDist)) {
263 if (pCurEpDist->TxQueueDepth == 0) {
264 /* EP is inactive and there are no pending messages, reduce credits back to zero */
265 ReduceCredits(pCredInfo, pCurEpDist, 0);
267 /* we cannot zero the credits assigned to this EP, but to keep
268 * the credits available for these leftover packets, reduce to
270 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin);
275 /* NOTE in the active case, we do not need to do anything further,
276 * when an EP goes active and needs credits, HTC will call into
277 * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */
279 pCurEpDist = pCurEpDist->pNext;
284 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */
285 static void SeekCredits(struct common_credit_state_info *pCredInfo,
286 struct htc_endpoint_credit_dist *pEPDist)
288 struct htc_endpoint_credit_dist *pCurEpDist;
294 if (pEPDist->ServiceID == WMI_CONTROL_SVC) {
295 /* we never oversubscribe on the control service, this is not
296 * a high performance path and the target never holds onto control
297 * credits for too long */
301 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
302 if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
303 if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
304 /* limit VI service from oversubscribing */
309 if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
310 if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
311 /* limit VO service from oversubscribing */
316 if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
317 if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
318 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
319 /* limit VI service from oversubscribing */
320 /* at least one free credit will not be used by VI */
325 if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
326 if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
327 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
328 /* limit VO service from oversubscribing */
329 /* at least one free credit will not be used by VO */
335 /* for all other services, we follow a simple algorithm of
336 * 1. checking the free pool for credits
337 * 2. checking lower priority endpoints for credits to take */
339 /* give what we can */
340 credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
342 if (credits >= pEPDist->TxCreditsSeek) {
343 /* we found some to fulfill the seek request */
347 /* we don't have enough in the free pool, try taking away from lower priority services
349 * The rule for taking away credits:
350 * 1. Only take from lower priority endpoints
351 * 2. Only take what is allocated above the minimum (never starve an endpoint completely)
352 * 3. Only take what you need.
356 /* starting at the lowest priority */
357 pCurEpDist = pCredInfo->pLowestPriEpDist;
359 /* work backwards until we hit the endpoint again */
360 while (pCurEpDist != pEPDist) {
361 /* calculate how many we need so far */
362 need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits;
364 if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) {
365 /* the current one has been allocated more than it's minimum and it
366 * has enough credits assigned above it's minimum to fulfill our need
367 * try to take away just enough to fulfill our need */
368 ReduceCredits(pCredInfo,
370 pCurEpDist->TxCreditsAssigned - need);
372 if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) {
378 pCurEpDist = pCurEpDist->pPrev;
381 /* return what we can get */
382 credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
386 /* did we find some credits? */
388 /* give what we can */
389 GiveCredits(pCredInfo, pEPDist, credits);
394 /* initialize and setup credit distribution */
395 int ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, struct common_credit_state_info *pCredInfo)
397 HTC_SERVICE_ID servicepriority[5];
399 A_MEMZERO(pCredInfo,sizeof(struct common_credit_state_info));
401 servicepriority[0] = WMI_CONTROL_SVC; /* highest */
402 servicepriority[1] = WMI_DATA_VO_SVC;
403 servicepriority[2] = WMI_DATA_VI_SVC;
404 servicepriority[3] = WMI_DATA_BE_SVC;
405 servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */
407 /* set callbacks and priority list */
408 HTCSetCreditDistribution(HTCHandle,
410 ar6000_credit_distribute,