OLD | NEW |
(Empty) | |
| 1 //------------------------------------------------------------------------------ |
| 2 // <copyright file="credit_dist.c" company="Atheros"> |
| 3 // Copyright (c) 2004-2008 Atheros Corporation. All rights reserved. |
| 4 // |
| 5 // This program is free software; you can redistribute it and/or modify |
| 6 // it under the terms of the GNU General Public License version 2 as |
| 7 // published by the Free Software Foundation; |
| 8 // |
| 9 // Software distributed under the License is distributed on an "AS |
| 10 // IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| 11 // implied. See the License for the specific language governing |
| 12 // rights and limitations under the License. |
| 13 // |
| 14 // |
| 15 //------------------------------------------------------------------------------ |
| 16 //============================================================================== |
| 17 // Author(s): ="Atheros" |
| 18 //============================================================================== |
| 19 #include "a_config.h" |
| 20 #include "athdefs.h" |
| 21 #include "a_types.h" |
| 22 #include "a_osapi.h" |
| 23 #define ATH_MODULE_NAME misc |
| 24 #include "a_debug.h" |
| 25 #include "htc_api.h" |
| 26 #include "common_drv.h" |
| 27 |
| 28 /********* CREDIT DISTRIBUTION FUNCTIONS ***************************************
***/ |
| 29 |
| 30 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave V
O service inactive */ |
| 31 |
| 32 #ifdef NO_VO_SERVICE |
| 33 #define DATA_SVCS_USED 3 |
| 34 #else |
| 35 #define DATA_SVCS_USED 4 |
| 36 #endif |
| 37 |
| 38 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
| 39 HTC_ENDPOINT_CREDIT_DIST *pEPDistList); |
| 40 |
| 41 static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
| 42 HTC_ENDPOINT_CREDIT_DIST *pEPDistList); |
| 43 |
| 44 /* reduce an ep's credits back to a set limit */ |
| 45 static INLINE void ReduceCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
| 46 HTC_ENDPOINT_CREDIT_DIST *pEpDist, |
| 47 int Limit) |
| 48 { |
| 49 int credits; |
| 50 |
| 51 /* set the new limit */ |
| 52 pEpDist->TxCreditsAssigned = Limit; |
| 53 |
| 54 if (pEpDist->TxCredits <= Limit) { |
| 55 return; |
| 56 } |
| 57 |
| 58 /* figure out how much to take away */ |
| 59 credits = pEpDist->TxCredits - Limit; |
| 60 /* take them away */ |
| 61 pEpDist->TxCredits -= credits; |
| 62 pCredInfo->CurrentFreeCredits += credits; |
| 63 } |
| 64 |
| 65 /* give an endpoint some credits from the free credit pool */ |
| 66 #define GiveCredits(pCredInfo,pEpDist,credits) \ |
| 67 { \ |
| 68 (pEpDist)->TxCredits += (credits); \ |
| 69 (pEpDist)->TxCreditsAssigned += (credits); \ |
| 70 (pCredInfo)->CurrentFreeCredits -= (credits); \ |
| 71 } |
| 72 |
| 73 |
| 74 /* default credit init callback. |
| 75 * This function is called in the context of HTCStart() to setup initial (applic
ation-specific) |
| 76 * credit distributions */ |
| 77 static void ar6000_credit_init(void *Context, |
| 78 HTC_ENDPOINT_CREDIT_DIST *pEPList, |
| 79 int TotalCredits) |
| 80 { |
| 81 HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
| 82 int count; |
| 83 COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context; |
| 84 |
| 85 pCredInfo->CurrentFreeCredits = TotalCredits; |
| 86 pCredInfo->TotalAvailableCredits = TotalCredits; |
| 87 |
| 88 pCurEpDist = pEPList; |
| 89 |
| 90 /* run through the list and initialize */ |
| 91 while (pCurEpDist != NULL) { |
| 92 |
| 93 /* set minimums for each endpoint */ |
| 94 pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg; |
| 95 |
| 96 if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) { |
| 97 /* give control service some credits */ |
| 98 GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin); |
| 99 /* control service is always marked active, it never goes inacti
ve EVER */ |
| 100 SET_EP_ACTIVE(pCurEpDist); |
| 101 } else if (pCurEpDist->ServiceID == WMI_DATA_BK_SVC) { |
| 102 /* this is the lowest priority data endpoint, save this off for
easy access */ |
| 103 pCredInfo->pLowestPriEpDist = pCurEpDist; |
| 104 } |
| 105 |
| 106 /* Streams have to be created (explicit | implicit)for all kinds |
| 107 * of traffic. BE endpoints are also inactive in the beginning. |
| 108 * When BE traffic starts it creates implicit streams that |
| 109 * redistributes credits. |
| 110 */ |
| 111 |
| 112 /* note, all other endpoints have minimums set but are initially given N
O credits. |
| 113 * Credits will be distributed as traffic activity demands */ |
| 114 pCurEpDist = pCurEpDist->pNext; |
| 115 } |
| 116 |
| 117 if (pCredInfo->CurrentFreeCredits <= 0) { |
| 118 AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit dist
ributions \n", TotalCredits)); |
| 119 A_ASSERT(FALSE); |
| 120 return; |
| 121 } |
| 122 |
| 123 /* reset list */ |
| 124 pCurEpDist = pEPList; |
| 125 /* now run through the list and set max operating credit limits for ever
yone */ |
| 126 while (pCurEpDist != NULL) { |
| 127 if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) { |
| 128 /* control service max is just 1 max message */ |
| 129 pCurEpDist->TxCreditsNorm = pCurEpDist->TxCreditsPerMaxMsg; |
| 130 } else { |
| 131 /* for the remaining data endpoints, we assume that each TxCredi
tsPerMaxMsg are |
| 132 * the same. |
| 133 * We use a simple calculation here, we take the remaining credi
ts and |
| 134 * determine how many max messages this can cover and then set e
ach endpoint's |
| 135 * normal value equal to 3/4 this amount. |
| 136 * */ |
| 137 count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMs
g) * pCurEpDist->TxCreditsPerMaxMsg; |
| 138 count = (count * 3) >> 2; |
| 139 count = max(count,pCurEpDist->TxCreditsPerMaxMsg); |
| 140 /* set normal */ |
| 141 pCurEpDist->TxCreditsNorm = count; |
| 142 |
| 143 } |
| 144 pCurEpDist = pCurEpDist->pNext; |
| 145 } |
| 146 |
| 147 } |
| 148 |
| 149 |
| 150 /* default credit distribution callback |
| 151 * This callback is invoked whenever endpoints require credit distributions. |
| 152 * A lock is held while this function is invoked, this function shall NOT block. |
| 153 * The pEPDistList is a list of distribution structures in prioritized order as |
| 154 * defined by the call to the HTCSetCreditDistribution() api. |
| 155 * |
| 156 */ |
| 157 static void ar6000_credit_distribute(void *Context, |
| 158 HTC_ENDPOINT_CREDIT_DIST *pEPDistList, |
| 159 HTC_CREDIT_DIST_REASON Reason) |
| 160 { |
| 161 HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
| 162 COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context; |
| 163 |
| 164 switch (Reason) { |
| 165 case HTC_CREDIT_DIST_SEND_COMPLETE : |
| 166 pCurEpDist = pEPDistList; |
| 167 /* we are given the start of the endpoint distribution list. |
| 168 * There may be one or more endpoints to service. |
| 169 * Run through the list and distribute credits */ |
| 170 while (pCurEpDist != NULL) { |
| 171 |
| 172 if (pCurEpDist->TxCreditsToDist > 0) { |
| 173 /* return the credits back to the endpoint */ |
| 174 pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist; |
| 175 /* always zero out when we are done */ |
| 176 pCurEpDist->TxCreditsToDist = 0; |
| 177 |
| 178 if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) { |
| 179 /* reduce to the assigned limit, previous credit red
uctions |
| 180 * could have caused the limit to change */ |
| 181 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCredi
tsAssigned); |
| 182 } |
| 183 |
| 184 if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) { |
| 185 /* oversubscribed endpoints need to reduce back to n
ormal */ |
| 186 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCredi
tsNorm); |
| 187 } |
| 188 |
| 189 if (!IS_EP_ACTIVE(pCurEpDist)) { |
| 190 /* endpoint is inactive, now check for messages wait
ing for credits */ |
| 191 if (pCurEpDist->TxQueueDepth == 0) { |
| 192 /* EP is inactive and there are no pending messa
ges, |
| 193 * reduce credits back to zero to recover credit
s */ |
| 194 ReduceCredits(pCredInfo, pCurEpDist, 0); |
| 195 } |
| 196 } |
| 197 } |
| 198 |
| 199 pCurEpDist = pCurEpDist->pNext; |
| 200 } |
| 201 |
| 202 break; |
| 203 |
| 204 case HTC_CREDIT_DIST_ACTIVITY_CHANGE : |
| 205 RedistributeCredits(pCredInfo,pEPDistList); |
| 206 break; |
| 207 case HTC_CREDIT_DIST_SEEK_CREDITS : |
| 208 SeekCredits(pCredInfo,pEPDistList); |
| 209 break; |
| 210 case HTC_DUMP_CREDIT_STATE : |
| 211 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, fr
ee : %d\n", |
| 212 pCredInf
o->TotalAvailableCredits, pCredInfo->CurrentFreeCredits)); |
| 213 break; |
| 214 default: |
| 215 break; |
| 216 |
| 217 } |
| 218 |
| 219 /* sanity checks done after each distribution action */ |
| 220 A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits); |
| 221 A_ASSERT(pCredInfo->CurrentFreeCredits >= 0); |
| 222 |
| 223 } |
| 224 |
| 225 /* redistribute credits based on activity change */ |
| 226 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
| 227 HTC_ENDPOINT_CREDIT_DIST *pEPDistList) |
| 228 { |
| 229 HTC_ENDPOINT_CREDIT_DIST *pCurEpDist = pEPDistList; |
| 230 |
| 231 /* walk through the list and remove credits from inactive endpoints */ |
| 232 while (pCurEpDist != NULL) { |
| 233 |
| 234 if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) { |
| 235 if (!IS_EP_ACTIVE(pCurEpDist)) { |
| 236 if (pCurEpDist->TxQueueDepth == 0) { |
| 237 /* EP is inactive and there are no pending messages, red
uce credits back to zero */ |
| 238 ReduceCredits(pCredInfo, pCurEpDist, 0); |
| 239 } else { |
| 240 /* we cannot zero the credits assigned to this EP, but t
o keep |
| 241 * the credits available for these leftover packets, red
uce to |
| 242 * a minimum */ |
| 243 ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMi
n); |
| 244 } |
| 245 } |
| 246 } |
| 247 |
| 248 /* NOTE in the active case, we do not need to do anything further, |
| 249 * when an EP goes active and needs credits, HTC will call into |
| 250 * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK
_CREDITS */ |
| 251 |
| 252 pCurEpDist = pCurEpDist->pNext; |
| 253 } |
| 254 |
| 255 } |
| 256 |
| 257 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question *
/ |
| 258 static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo, |
| 259 HTC_ENDPOINT_CREDIT_DIST *pEPDist) |
| 260 { |
| 261 HTC_ENDPOINT_CREDIT_DIST *pCurEpDist; |
| 262 int credits = 0; |
| 263 int need; |
| 264 |
| 265 do { |
| 266 |
| 267 if (pEPDist->ServiceID == WMI_CONTROL_SVC) { |
| 268 /* we never oversubscribe on the control service, this is not |
| 269 * a high performance path and the target never holds onto contr
ol |
| 270 * credits for too long */ |
| 271 break; |
| 272 } |
| 273 |
| 274 if (pEPDist->ServiceID == WMI_DATA_VI_SVC) { |
| 275 if (pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) { |
| 276 /* limit VI service from oversubscribing */ |
| 277 break; |
| 278 } |
| 279 } |
| 280 |
| 281 if (pEPDist->ServiceID == WMI_DATA_VO_SVC) { |
| 282 if (pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) { |
| 283 /* limit VO service from oversubscribing */ |
| 284 break; |
| 285 } |
| 286 } |
| 287 |
| 288 /* for all other services, we follow a simple algorithm of |
| 289 * 1. checking the free pool for credits |
| 290 * 2. checking lower priority endpoints for credits to take */ |
| 291 |
| 292 /* give what we can */ |
| 293 credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); |
| 294 |
| 295 if (credits >= pEPDist->TxCreditsSeek) { |
| 296 /* we found some to fullfill the seek request */ |
| 297 break; |
| 298 } |
| 299 |
| 300 /* we don't have enough in the free pool, try taking away from lower pri
ority services |
| 301 * |
| 302 * The rule for taking away credits: |
| 303 * 1. Only take from lower priority endpoints |
| 304 * 2. Only take what is allocated above the minimum (never starve an e
ndpoint completely) |
| 305 * 3. Only take what you need. |
| 306 * |
| 307 * */ |
| 308 |
| 309 /* starting at the lowest priority */ |
| 310 pCurEpDist = pCredInfo->pLowestPriEpDist; |
| 311 |
| 312 /* work backwards until we hit the endpoint again */ |
| 313 while (pCurEpDist != pEPDist) { |
| 314 /* calculate how many we need so far */ |
| 315 need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits; |
| 316 |
| 317 if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsM
in) { |
| 318 /* the current one has been allocated more than it's minimum
and it |
| 319 * has enough credits assigned above it's minimum to fullfil
l our need |
| 320 * try to take away just enough to fullfill our need */ |
| 321 ReduceCredits(pCredInfo, |
| 322 pCurEpDist, |
| 323 pCurEpDist->TxCreditsAssigned - need); |
| 324 |
| 325 if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) { |
| 326 /* we have enough */ |
| 327 break; |
| 328 } |
| 329 } |
| 330 |
| 331 pCurEpDist = pCurEpDist->pPrev; |
| 332 } |
| 333 |
| 334 /* return what we can get */ |
| 335 credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); |
| 336 |
| 337 } while (FALSE); |
| 338 |
| 339 /* did we find some credits? */ |
| 340 if (credits) { |
| 341 /* give what we can */ |
| 342 GiveCredits(pCredInfo, pEPDist, credits); |
| 343 } |
| 344 |
| 345 } |
| 346 |
| 347 /* initialize and setup credit distribution */ |
| 348 A_STATUS ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, COMMON_CREDIT_STATE_INFO
*pCredInfo) |
| 349 { |
| 350 HTC_SERVICE_ID servicepriority[5]; |
| 351 |
| 352 A_MEMZERO(pCredInfo,sizeof(COMMON_CREDIT_STATE_INFO)); |
| 353 |
| 354 servicepriority[0] = WMI_CONTROL_SVC; /* highest */ |
| 355 servicepriority[1] = WMI_DATA_VO_SVC; |
| 356 servicepriority[2] = WMI_DATA_VI_SVC; |
| 357 servicepriority[3] = WMI_DATA_BE_SVC; |
| 358 servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ |
| 359 |
| 360 /* set callbacks and priority list */ |
| 361 HTCSetCreditDistribution(HTCHandle, |
| 362 pCredInfo, |
| 363 ar6000_credit_distribute, |
| 364 ar6000_credit_init, |
| 365 servicepriority, |
| 366 5); |
| 367 |
| 368 return A_OK; |
| 369 } |
| 370 |
OLD | NEW |