Commit | Line | Data |
---|---|---|
30295c89 VM |
1 | //------------------------------------------------------------------------------ |
2 | // <copyright file="credit_dist.c" company="Atheros"> | |
3 | // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved. | |
4 | // | |
5 | // | |
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. | |
9 | // | |
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. | |
17 | // | |
18 | // | |
19 | //------------------------------------------------------------------------------ | |
20 | //============================================================================== | |
21 | // Author(s): ="Atheros" | |
22 | //============================================================================== | |
23 | ||
24 | #include "a_config.h" | |
25 | #include "athdefs.h" | |
26 | #include "a_types.h" | |
27 | #include "a_osapi.h" | |
28 | #define ATH_MODULE_NAME misc | |
29 | #include "a_debug.h" | |
30 | #include "htc_api.h" | |
31 | #include "common_drv.h" | |
32 | ||
33 | /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/ | |
34 | ||
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 | |
37 | ||
38 | #ifdef NO_VO_SERVICE | |
39 | #define DATA_SVCS_USED 3 | |
40 | #else | |
41 | #define DATA_SVCS_USED 4 | |
42 | #endif | |
43 | ||
84fd335f | 44 | static void RedistributeCredits(struct common_credit_state_info *pCredInfo, |
57c9d5b3 | 45 | struct htc_endpoint_credit_dist *pEPDistList); |
30295c89 | 46 | |
84fd335f | 47 | static void SeekCredits(struct common_credit_state_info *pCredInfo, |
57c9d5b3 | 48 | struct htc_endpoint_credit_dist *pEPDistList); |
30295c89 VM |
49 | |
50 | /* reduce an ep's credits back to a set limit */ | |
84fd335f | 51 | static INLINE void ReduceCredits(struct common_credit_state_info *pCredInfo, |
57c9d5b3 | 52 | struct htc_endpoint_credit_dist *pEpDist, |
30295c89 VM |
53 | int Limit) |
54 | { | |
55 | int credits; | |
56 | ||
57 | /* set the new limit */ | |
58 | pEpDist->TxCreditsAssigned = Limit; | |
59 | ||
60 | if (pEpDist->TxCredits <= Limit) { | |
61 | return; | |
62 | } | |
63 | ||
64 | /* figure out how much to take away */ | |
65 | credits = pEpDist->TxCredits - Limit; | |
66 | /* take them away */ | |
67 | pEpDist->TxCredits -= credits; | |
68 | pCredInfo->CurrentFreeCredits += credits; | |
69 | } | |
70 | ||
71 | /* give an endpoint some credits from the free credit pool */ | |
72 | #define GiveCredits(pCredInfo,pEpDist,credits) \ | |
73 | { \ | |
74 | (pEpDist)->TxCredits += (credits); \ | |
75 | (pEpDist)->TxCreditsAssigned += (credits); \ | |
76 | (pCredInfo)->CurrentFreeCredits -= (credits); \ | |
77 | } | |
78 | ||
79 | ||
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, | |
57c9d5b3 | 84 | struct htc_endpoint_credit_dist *pEPList, |
30295c89 VM |
85 | int TotalCredits) |
86 | { | |
57c9d5b3 | 87 | struct htc_endpoint_credit_dist *pCurEpDist; |
30295c89 | 88 | int count; |
84fd335f | 89 | struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context; |
30295c89 VM |
90 | |
91 | pCredInfo->CurrentFreeCredits = TotalCredits; | |
92 | pCredInfo->TotalAvailableCredits = TotalCredits; | |
93 | ||
94 | pCurEpDist = pEPList; | |
95 | ||
96 | /* run through the list and initialize */ | |
97 | while (pCurEpDist != NULL) { | |
98 | ||
99 | /* set minimums for each endpoint */ | |
100 | pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg; | |
101 | ||
102 | #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS | |
103 | ||
104 | if (TotalCredits > 4) | |
105 | { | |
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); | |
109 | /* force active */ | |
110 | SET_EP_ACTIVE(pCurEpDist); | |
111 | } | |
112 | } | |
113 | ||
114 | #endif | |
115 | ||
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; | |
124 | } | |
125 | ||
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. | |
130 | */ | |
131 | ||
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; | |
135 | } | |
136 | ||
137 | if (pCredInfo->CurrentFreeCredits <= 0) { | |
138 | AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits)); | |
1071a134 | 139 | A_ASSERT(false); |
30295c89 VM |
140 | return; |
141 | } | |
142 | ||
143 | /* reset list */ | |
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; | |
150 | } else { | |
151 | /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are | |
152 | * the same. | |
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. | |
156 | * */ | |
157 | count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg; | |
158 | count = (count * 3) >> 2; | |
159 | count = max(count,pCurEpDist->TxCreditsPerMaxMsg); | |
160 | /* set normal */ | |
161 | pCurEpDist->TxCreditsNorm = count; | |
162 | ||
163 | } | |
164 | pCurEpDist = pCurEpDist->pNext; | |
165 | } | |
166 | ||
167 | } | |
168 | ||
169 | ||
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. | |
175 | * | |
176 | */ | |
177 | static void ar6000_credit_distribute(void *Context, | |
57c9d5b3 | 178 | struct htc_endpoint_credit_dist *pEPDistList, |
30295c89 VM |
179 | HTC_CREDIT_DIST_REASON Reason) |
180 | { | |
57c9d5b3 | 181 | struct htc_endpoint_credit_dist *pCurEpDist; |
84fd335f | 182 | struct common_credit_state_info *pCredInfo = (struct common_credit_state_info *)Context; |
30295c89 VM |
183 | |
184 | switch (Reason) { | |
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) { | |
191 | ||
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; | |
197 | ||
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); | |
202 | } | |
203 | ||
204 | if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) { | |
205 | /* oversubscribed endpoints need to reduce back to normal */ | |
206 | ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm); | |
207 | } | |
208 | ||
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); | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | pCurEpDist = pCurEpDist->pNext; | |
220 | } | |
221 | ||
222 | break; | |
223 | ||
224 | case HTC_CREDIT_DIST_ACTIVITY_CHANGE : | |
225 | RedistributeCredits(pCredInfo,pEPDistList); | |
226 | break; | |
227 | case HTC_CREDIT_DIST_SEEK_CREDITS : | |
228 | SeekCredits(pCredInfo,pEPDistList); | |
229 | break; | |
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)); | |
233 | break; | |
234 | default: | |
235 | break; | |
236 | ||
237 | } | |
238 | ||
239 | /* sanity checks done after each distribution action */ | |
240 | A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits); | |
241 | A_ASSERT(pCredInfo->CurrentFreeCredits >= 0); | |
242 | ||
243 | } | |
244 | ||
245 | /* redistribute credits based on activity change */ | |
84fd335f | 246 | static void RedistributeCredits(struct common_credit_state_info *pCredInfo, |
57c9d5b3 | 247 | struct htc_endpoint_credit_dist *pEPDistList) |
30295c89 | 248 | { |
57c9d5b3 | 249 | struct htc_endpoint_credit_dist *pCurEpDist = pEPDistList; |
30295c89 VM |
250 | |
251 | /* walk through the list and remove credits from inactive endpoints */ | |
252 | while (pCurEpDist != NULL) { | |
253 | ||
254 | #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS | |
255 | ||
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); | |
259 | } | |
260 | #endif | |
261 | ||
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); | |
267 | } else { | |
268 | /* we cannot zero the credits assigned to this EP, but to keep | |
269 | * the credits available for these leftover packets, reduce to | |
270 | * a minimum */ | |
271 | ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin); | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
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 */ | |
279 | ||
280 | pCurEpDist = pCurEpDist->pNext; | |
281 | } | |
282 | ||
283 | } | |
284 | ||
285 | /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */ | |
84fd335f | 286 | static void SeekCredits(struct common_credit_state_info *pCredInfo, |
57c9d5b3 | 287 | struct htc_endpoint_credit_dist *pEPDist) |
30295c89 | 288 | { |
57c9d5b3 | 289 | struct htc_endpoint_credit_dist *pCurEpDist; |
30295c89 VM |
290 | int credits = 0; |
291 | int need; | |
292 | ||
293 | do { | |
294 | ||
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 */ | |
299 | break; | |
300 | } | |
301 | ||
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 */ | |
306 | break; | |
307 | } | |
308 | } | |
309 | ||
310 | if (pEPDist->ServiceID == WMI_DATA_VO_SVC) { | |
311 | if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) { | |
312 | /* limit VO service from oversubscribing */ | |
313 | break; | |
314 | } | |
315 | } | |
316 | #else | |
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 */ | |
322 | break; | |
323 | } | |
324 | } | |
325 | ||
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 */ | |
331 | break; | |
332 | } | |
333 | } | |
334 | #endif | |
335 | ||
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 */ | |
339 | ||
340 | /* give what we can */ | |
341 | credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); | |
342 | ||
343 | if (credits >= pEPDist->TxCreditsSeek) { | |
25985edc | 344 | /* we found some to fulfill the seek request */ |
30295c89 VM |
345 | break; |
346 | } | |
347 | ||
348 | /* we don't have enough in the free pool, try taking away from lower priority services | |
349 | * | |
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. | |
354 | * | |
355 | * */ | |
356 | ||
357 | /* starting at the lowest priority */ | |
358 | pCurEpDist = pCredInfo->pLowestPriEpDist; | |
359 | ||
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; | |
364 | ||
365 | if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) { | |
366 | /* the current one has been allocated more than it's minimum and it | |
25985edc LDM |
367 | * has enough credits assigned above it's minimum to fulfill our need |
368 | * try to take away just enough to fulfill our need */ | |
30295c89 VM |
369 | ReduceCredits(pCredInfo, |
370 | pCurEpDist, | |
371 | pCurEpDist->TxCreditsAssigned - need); | |
372 | ||
373 | if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) { | |
374 | /* we have enough */ | |
375 | break; | |
376 | } | |
377 | } | |
378 | ||
379 | pCurEpDist = pCurEpDist->pPrev; | |
380 | } | |
381 | ||
382 | /* return what we can get */ | |
383 | credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek); | |
384 | ||
1071a134 | 385 | } while (false); |
30295c89 VM |
386 | |
387 | /* did we find some credits? */ | |
388 | if (credits) { | |
389 | /* give what we can */ | |
390 | GiveCredits(pCredInfo, pEPDist, credits); | |
391 | } | |
392 | ||
393 | } | |
394 | ||
395 | /* initialize and setup credit distribution */ | |
84fd335f | 396 | int ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, struct common_credit_state_info *pCredInfo) |
30295c89 VM |
397 | { |
398 | HTC_SERVICE_ID servicepriority[5]; | |
399 | ||
84fd335f | 400 | A_MEMZERO(pCredInfo,sizeof(struct common_credit_state_info)); |
30295c89 VM |
401 | |
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 */ | |
407 | ||
408 | /* set callbacks and priority list */ | |
409 | HTCSetCreditDistribution(HTCHandle, | |
410 | pCredInfo, | |
411 | ar6000_credit_distribute, | |
412 | ar6000_credit_init, | |
413 | servicepriority, | |
414 | 5); | |
415 | ||
4f69cef0 | 416 | return 0; |
30295c89 VM |
417 | } |
418 |