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 fullfill 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 fullfill our need
368 * try to take away just enough to fullfill 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,