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