322-mac80211-Add-airtime-accounting-and-scheduling-to-TX.patch 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
  2. Date: Tue, 18 Dec 2018 17:02:08 -0800
  3. Subject: [PATCH] mac80211: Add airtime accounting and scheduling to TXQs
  4. MIME-Version: 1.0
  5. Content-Type: text/plain; charset=UTF-8
  6. Content-Transfer-Encoding: 8bit
  7. This adds airtime accounting and scheduling to the mac80211 TXQ
  8. scheduler. A new callback, ieee80211_sta_register_airtime(), is added
  9. that drivers can call to report airtime usage for stations.
  10. When airtime information is present, mac80211 will schedule TXQs
  11. (through ieee80211_next_txq()) in a way that enforces airtime fairness
  12. between active stations. This scheduling works the same way as the ath9k
  13. in-driver airtime fairness scheduling. If no airtime usage is reported
  14. by the driver, the scheduler will default to round-robin scheduling.
  15. For drivers that don't control TXQ scheduling in software, a new API
  16. function, ieee80211_txq_may_transmit(), is added which the driver can use
  17. to check if the TXQ is eligible for transmission, or should be throttled to
  18. enforce fairness. Calls to this function must also be enclosed in
  19. ieee80211_txq_schedule_{start,end}() calls to ensure proper locking.
  20. The API ieee80211_txq_may_transmit() also ensures that TXQ list will be
  21. aligned aginst driver's own round-robin scheduler list. i.e it rotates
  22. the TXQ list till it makes the requested node becomes the first entry
  23. in TXQ list. Thus both the TXQ list and driver's list are in sync.
  24. Co-developed-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
  25. Signed-off-by: Louie Lu <git@louie.lu>
  26. [added debugfs write op to reset airtime counter]
  27. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
  28. Signed-off-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
  29. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  30. ---
  31. --- a/include/net/mac80211.h
  32. +++ b/include/net/mac80211.h
  33. @@ -2304,6 +2304,9 @@ enum ieee80211_hw_flags {
  34. * supported by HW.
  35. * @max_nan_de_entries: maximum number of NAN DE functions supported by the
  36. * device.
  37. + *
  38. + * @weight_multipler: Driver specific airtime weight multiplier used while
  39. + * refilling deficit of each TXQ.
  40. */
  41. struct ieee80211_hw {
  42. struct ieee80211_conf conf;
  43. @@ -2339,6 +2342,7 @@ struct ieee80211_hw {
  44. u8 n_cipher_schemes;
  45. const struct ieee80211_cipher_scheme *cipher_schemes;
  46. u8 max_nan_de_entries;
  47. + u8 weight_multiplier;
  48. };
  49. static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
  50. @@ -5299,6 +5303,34 @@ void ieee80211_sta_eosp(struct ieee80211
  51. void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
  52. /**
  53. + * ieee80211_sta_register_airtime - register airtime usage for a sta/tid
  54. + *
  55. + * Register airtime usage for a given sta on a given tid. The driver can call
  56. + * this function to notify mac80211 that a station used a certain amount of
  57. + * airtime. This information will be used by the TXQ scheduler to schedule
  58. + * stations in a way that ensures airtime fairness.
  59. + *
  60. + * The reported airtime should as a minimum include all time that is spent
  61. + * transmitting to the remote station, including overhead and padding, but not
  62. + * including time spent waiting for a TXOP. If the time is not reported by the
  63. + * hardware it can in some cases be calculated from the rate and known frame
  64. + * composition. When possible, the time should include any failed transmission
  65. + * attempts.
  66. + *
  67. + * The driver can either call this function synchronously for every packet or
  68. + * aggregate, or asynchronously as airtime usage information becomes available.
  69. + * TX and RX airtime can be reported together, or separately by setting one of
  70. + * them to 0.
  71. + *
  72. + * @pubsta: the station
  73. + * @tid: the TID to register airtime for
  74. + * @tx_airtime: airtime used during TX (in usec)
  75. + * @rx_airtime: airtime used during RX (in usec)
  76. + */
  77. +void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
  78. + u32 tx_airtime, u32 rx_airtime);
  79. +
  80. +/**
  81. * ieee80211_iter_keys - iterate keys programmed into the device
  82. * @hw: pointer obtained from ieee80211_alloc_hw()
  83. * @vif: virtual interface to iterate, may be %NULL for all
  84. @@ -6042,6 +6074,33 @@ void ieee80211_txq_schedule_end(struct i
  85. __releases(txq_lock);
  86. /**
  87. + * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
  88. + *
  89. + * This function is used to check whether given txq is allowed to transmit by
  90. + * the airtime scheduler, and can be used by drivers to access the airtime
  91. + * fairness accounting without going using the scheduling order enfored by
  92. + * next_txq().
  93. + *
  94. + * Returns %true if the airtime scheduler thinks the TXQ should be allowed to
  95. + * transmit, and %false if it should be throttled. This function can also have
  96. + * the side effect of rotating the TXQ in the scheduler rotation, which will
  97. + * eventually bring the deficit to positive and allow the station to transmit
  98. + * again.
  99. + *
  100. + * The API ieee80211_txq_may_transmit() also ensures that TXQ list will be
  101. + * aligned aginst driver's own round-robin scheduler list. i.e it rotates
  102. + * the TXQ list till it makes the requested node becomes the first entry
  103. + * in TXQ list. Thus both the TXQ list and driver's list are in sync. If this
  104. + * function returns %true, the driver is expected to schedule packets
  105. + * for transmission, and then return the TXQ through ieee80211_return_txq().
  106. + *
  107. + * @hw: pointer as obtained from ieee80211_alloc_hw()
  108. + * @txq: pointer obtained from station or virtual interface
  109. + */
  110. +bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
  111. + struct ieee80211_txq *txq);
  112. +
  113. +/**
  114. * ieee80211_txq_get_depth - get pending frame/byte count of given txq
  115. *
  116. * The values are not guaranteed to be coherent with regard to each other, i.e.
  117. --- a/net/mac80211/cfg.c
  118. +++ b/net/mac80211/cfg.c
  119. @@ -1390,6 +1390,9 @@ static int sta_apply_parameters(struct i
  120. if (ieee80211_vif_is_mesh(&sdata->vif))
  121. sta_apply_mesh_params(local, sta, params);
  122. + if (params->airtime_weight)
  123. + sta->airtime_weight = params->airtime_weight;
  124. +
  125. /* set the STA state after all sta info from usermode has been set */
  126. if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
  127. set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
  128. --- a/net/mac80211/debugfs.c
  129. +++ b/net/mac80211/debugfs.c
  130. @@ -380,6 +380,9 @@ void debugfs_hw_add(struct ieee80211_loc
  131. if (local->ops->wake_tx_queue)
  132. DEBUGFS_ADD_MODE(aqm, 0600);
  133. + debugfs_create_u16("airtime_flags", 0600,
  134. + phyd, &local->airtime_flags);
  135. +
  136. statsd = debugfs_create_dir("statistics", phyd);
  137. /* if the dir failed, don't put all the other things into the root! */
  138. --- a/net/mac80211/debugfs_sta.c
  139. +++ b/net/mac80211/debugfs_sta.c
  140. @@ -178,9 +178,9 @@ static ssize_t sta_aqm_read(struct file
  141. txqi->tin.tx_bytes,
  142. txqi->tin.tx_packets,
  143. txqi->flags,
  144. - txqi->flags & (1<<IEEE80211_TXQ_STOP) ? "STOP" : "RUN",
  145. - txqi->flags & (1<<IEEE80211_TXQ_AMPDU) ? " AMPDU" : "",
  146. - txqi->flags & (1<<IEEE80211_TXQ_NO_AMSDU) ? " NO-AMSDU" : "");
  147. + test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
  148. + test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
  149. + test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "");
  150. }
  151. rcu_read_unlock();
  152. @@ -192,6 +192,64 @@ static ssize_t sta_aqm_read(struct file
  153. }
  154. STA_OPS(aqm);
  155. +static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
  156. + size_t count, loff_t *ppos)
  157. +{
  158. + struct sta_info *sta = file->private_data;
  159. + struct ieee80211_local *local = sta->sdata->local;
  160. + size_t bufsz = 200;
  161. + char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
  162. + u64 rx_airtime = 0, tx_airtime = 0;
  163. + s64 deficit[IEEE80211_NUM_ACS];
  164. + ssize_t rv;
  165. + int ac;
  166. +
  167. + if (!buf)
  168. + return -ENOMEM;
  169. +
  170. + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
  171. + spin_lock_bh(&local->active_txq_lock[ac]);
  172. + rx_airtime += sta->airtime[ac].rx_airtime;
  173. + tx_airtime += sta->airtime[ac].tx_airtime;
  174. + deficit[ac] = sta->airtime[ac].deficit;
  175. + spin_unlock_bh(&local->active_txq_lock[ac]);
  176. + }
  177. +
  178. + p += scnprintf(p, bufsz + buf - p,
  179. + "RX: %llu us\nTX: %llu us\nWeight: %u\n"
  180. + "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
  181. + rx_airtime,
  182. + tx_airtime,
  183. + sta->airtime_weight,
  184. + deficit[0],
  185. + deficit[1],
  186. + deficit[2],
  187. + deficit[3]);
  188. +
  189. + rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
  190. + kfree(buf);
  191. + return rv;
  192. +}
  193. +
  194. +static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
  195. + size_t count, loff_t *ppos)
  196. +{
  197. + struct sta_info *sta = file->private_data;
  198. + struct ieee80211_local *local = sta->sdata->local;
  199. + int ac;
  200. +
  201. + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
  202. + spin_lock_bh(&local->active_txq_lock[ac]);
  203. + sta->airtime[ac].rx_airtime = 0;
  204. + sta->airtime[ac].tx_airtime = 0;
  205. + sta->airtime[ac].deficit = sta->airtime_weight;
  206. + spin_unlock_bh(&local->active_txq_lock[ac]);
  207. + }
  208. +
  209. + return count;
  210. +}
  211. +STA_OPS_RW(airtime);
  212. +
  213. static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
  214. size_t count, loff_t *ppos)
  215. {
  216. @@ -546,6 +604,10 @@ void ieee80211_sta_debugfs_add(struct st
  217. if (local->ops->wake_tx_queue)
  218. DEBUGFS_ADD(aqm);
  219. + if (wiphy_ext_feature_isset(local->hw.wiphy,
  220. + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
  221. + DEBUGFS_ADD(airtime);
  222. +
  223. if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
  224. debugfs_create_x32("driver_buffered_tids", 0400,
  225. sta->debugfs_dir,
  226. --- a/net/mac80211/ieee80211_i.h
  227. +++ b/net/mac80211/ieee80211_i.h
  228. @@ -1136,6 +1136,8 @@ struct ieee80211_local {
  229. struct list_head active_txqs[IEEE80211_NUM_ACS];
  230. u16 schedule_round[IEEE80211_NUM_ACS];
  231. + u16 airtime_flags;
  232. +
  233. const struct ieee80211_ops *ops;
  234. /*
  235. --- a/net/mac80211/main.c
  236. +++ b/net/mac80211/main.c
  237. @@ -656,6 +656,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
  238. INIT_LIST_HEAD(&local->active_txqs[i]);
  239. spin_lock_init(&local->active_txq_lock[i]);
  240. }
  241. + local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
  242. INIT_LIST_HEAD(&local->chanctx_list);
  243. mutex_init(&local->chanctx_mtx);
  244. @@ -1142,6 +1143,9 @@ int ieee80211_register_hw(struct ieee802
  245. if (!local->hw.max_nan_de_entries)
  246. local->hw.max_nan_de_entries = IEEE80211_MAX_NAN_INSTANCE_ID;
  247. + if (!local->hw.weight_multiplier)
  248. + local->hw.weight_multiplier = 1;
  249. +
  250. result = ieee80211_wep_init(local);
  251. if (result < 0)
  252. wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
  253. --- a/net/mac80211/sta_info.c
  254. +++ b/net/mac80211/sta_info.c
  255. @@ -90,7 +90,6 @@ static void __cleanup_single_sta(struct
  256. struct tid_ampdu_tx *tid_tx;
  257. struct ieee80211_sub_if_data *sdata = sta->sdata;
  258. struct ieee80211_local *local = sdata->local;
  259. - struct fq *fq = &local->fq;
  260. struct ps_data *ps;
  261. if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
  262. @@ -115,9 +114,7 @@ static void __cleanup_single_sta(struct
  263. for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
  264. struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
  265. - spin_lock_bh(&fq->lock);
  266. ieee80211_txq_purge(local, txqi);
  267. - spin_unlock_bh(&fq->lock);
  268. }
  269. }
  270. @@ -381,9 +378,12 @@ struct sta_info *sta_info_alloc(struct i
  271. if (sta_prepare_rate_control(local, sta, gfp))
  272. goto free_txq;
  273. + sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
  274. +
  275. for (i = 0; i < IEEE80211_NUM_ACS; i++) {
  276. skb_queue_head_init(&sta->ps_tx_buf[i]);
  277. skb_queue_head_init(&sta->tx_filtered[i]);
  278. + sta->airtime[i].deficit = sta->airtime_weight;
  279. }
  280. for (i = 0; i < IEEE80211_NUM_TIDS; i++)
  281. @@ -1826,6 +1826,27 @@ void ieee80211_sta_set_buffered(struct i
  282. }
  283. EXPORT_SYMBOL(ieee80211_sta_set_buffered);
  284. +void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
  285. + u32 tx_airtime, u32 rx_airtime)
  286. +{
  287. + struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
  288. + struct ieee80211_local *local = sta->sdata->local;
  289. + u8 ac = ieee80211_ac_from_tid(tid);
  290. + u32 airtime = 0;
  291. +
  292. + if (sta->local->airtime_flags & AIRTIME_USE_TX)
  293. + airtime += tx_airtime;
  294. + if (sta->local->airtime_flags & AIRTIME_USE_RX)
  295. + airtime += rx_airtime;
  296. +
  297. + spin_lock_bh(&local->active_txq_lock[ac]);
  298. + sta->airtime[ac].tx_airtime += tx_airtime;
  299. + sta->airtime[ac].rx_airtime += rx_airtime;
  300. + sta->airtime[ac].deficit -= airtime;
  301. + spin_unlock_bh(&local->active_txq_lock[ac]);
  302. +}
  303. +EXPORT_SYMBOL(ieee80211_sta_register_airtime);
  304. +
  305. int sta_info_move_state(struct sta_info *sta,
  306. enum ieee80211_sta_state new_state)
  307. {
  308. @@ -2192,6 +2213,23 @@ void sta_set_sinfo(struct sta_info *sta,
  309. sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
  310. }
  311. + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_DURATION))) {
  312. + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
  313. + sinfo->rx_duration += sta->airtime[ac].rx_airtime;
  314. + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
  315. + }
  316. +
  317. + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_DURATION))) {
  318. + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
  319. + sinfo->tx_duration += sta->airtime[ac].tx_airtime;
  320. + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION);
  321. + }
  322. +
  323. + if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
  324. + sinfo->airtime_weight = sta->airtime_weight;
  325. + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
  326. + }
  327. +
  328. sinfo->rx_dropped_misc = sta->rx_stats.dropped;
  329. if (sta->pcpu_rx_stats) {
  330. for_each_possible_cpu(cpu) {
  331. --- a/net/mac80211/sta_info.h
  332. +++ b/net/mac80211/sta_info.h
  333. @@ -127,6 +127,16 @@ enum ieee80211_agg_stop_reason {
  334. AGG_STOP_DESTROY_STA,
  335. };
  336. +/* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
  337. +#define AIRTIME_USE_TX BIT(0)
  338. +#define AIRTIME_USE_RX BIT(1)
  339. +
  340. +struct airtime_info {
  341. + u64 rx_airtime;
  342. + u64 tx_airtime;
  343. + s64 deficit;
  344. +};
  345. +
  346. struct sta_info;
  347. /**
  348. @@ -563,6 +573,9 @@ struct sta_info {
  349. } tx_stats;
  350. u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
  351. + struct airtime_info airtime[IEEE80211_NUM_ACS];
  352. + u16 airtime_weight;
  353. +
  354. /*
  355. * Aggregation information, locked with lock.
  356. */
  357. --- a/net/mac80211/status.c
  358. +++ b/net/mac80211/status.c
  359. @@ -828,6 +828,12 @@ static void __ieee80211_tx_status(struct
  360. ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
  361. acked, info->status.tx_time);
  362. + if (info->status.tx_time &&
  363. + wiphy_ext_feature_isset(local->hw.wiphy,
  364. + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
  365. + ieee80211_sta_register_airtime(&sta->sta, tid,
  366. + info->status.tx_time, 0);
  367. +
  368. if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
  369. if (acked) {
  370. if (sta->status_stats.lost_packets)
  371. --- a/net/mac80211/tx.c
  372. +++ b/net/mac80211/tx.c
  373. @@ -1463,8 +1463,11 @@ void ieee80211_txq_purge(struct ieee8021
  374. struct fq *fq = &local->fq;
  375. struct fq_tin *tin = &txqi->tin;
  376. + spin_lock_bh(&fq->lock);
  377. fq_tin_reset(fq, tin, fq_skb_free_func);
  378. ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
  379. + spin_unlock_bh(&fq->lock);
  380. +
  381. spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
  382. list_del_init(&txqi->schedule_order);
  383. spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
  384. @@ -3631,11 +3634,28 @@ struct ieee80211_txq *ieee80211_next_txq
  385. lockdep_assert_held(&local->active_txq_lock[ac]);
  386. + begin:
  387. txqi = list_first_entry_or_null(&local->active_txqs[ac],
  388. struct txq_info,
  389. schedule_order);
  390. + if (!txqi)
  391. + return NULL;
  392. +
  393. + if (txqi->txq.sta) {
  394. + struct sta_info *sta = container_of(txqi->txq.sta,
  395. + struct sta_info, sta);
  396. +
  397. + if (sta->airtime[txqi->txq.ac].deficit < 0) {
  398. + sta->airtime[txqi->txq.ac].deficit +=
  399. + sta->airtime_weight;
  400. + list_move_tail(&txqi->schedule_order,
  401. + &local->active_txqs[txqi->txq.ac]);
  402. + goto begin;
  403. + }
  404. + }
  405. +
  406. - if (!txqi || txqi->schedule_round == local->schedule_round[ac])
  407. + if (txqi->schedule_round == local->schedule_round[ac])
  408. return NULL;
  409. list_del_init(&txqi->schedule_order);
  410. @@ -3653,12 +3673,74 @@ void ieee80211_return_txq(struct ieee802
  411. lockdep_assert_held(&local->active_txq_lock[txq->ac]);
  412. if (list_empty(&txqi->schedule_order) &&
  413. - (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets))
  414. - list_add_tail(&txqi->schedule_order,
  415. - &local->active_txqs[txq->ac]);
  416. + (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
  417. + /* If airtime accounting is active, always enqueue STAs at the
  418. + * head of the list to ensure that they only get moved to the
  419. + * back by the airtime DRR scheduler once they have a negative
  420. + * deficit. A station that already has a negative deficit will
  421. + * get immediately moved to the back of the list on the next
  422. + * call to ieee80211_next_txq().
  423. + */
  424. + if (txqi->txq.sta &&
  425. + wiphy_ext_feature_isset(local->hw.wiphy,
  426. + NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
  427. + list_add(&txqi->schedule_order,
  428. + &local->active_txqs[txq->ac]);
  429. + else
  430. + list_add_tail(&txqi->schedule_order,
  431. + &local->active_txqs[txq->ac]);
  432. + }
  433. }
  434. EXPORT_SYMBOL(ieee80211_return_txq);
  435. +bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
  436. + struct ieee80211_txq *txq)
  437. +{
  438. + struct ieee80211_local *local = hw_to_local(hw);
  439. + struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
  440. + struct sta_info *sta;
  441. + u8 ac = txq->ac;
  442. +
  443. + lockdep_assert_held(&local->active_txq_lock[ac]);
  444. +
  445. + if (!txqi->txq.sta)
  446. + goto out;
  447. +
  448. + if (list_empty(&txqi->schedule_order))
  449. + goto out;
  450. +
  451. + list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
  452. + schedule_order) {
  453. + if (iter == txqi)
  454. + break;
  455. +
  456. + if (!iter->txq.sta) {
  457. + list_move_tail(&iter->schedule_order,
  458. + &local->active_txqs[ac]);
  459. + continue;
  460. + }
  461. + sta = container_of(iter->txq.sta, struct sta_info, sta);
  462. + if (sta->airtime[ac].deficit < 0)
  463. + sta->airtime[ac].deficit += sta->airtime_weight;
  464. + list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
  465. + }
  466. +
  467. + sta = container_of(txqi->txq.sta, struct sta_info, sta);
  468. + if (sta->airtime[ac].deficit >= 0)
  469. + goto out;
  470. +
  471. + sta->airtime[ac].deficit += sta->airtime_weight;
  472. + list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
  473. +
  474. + return false;
  475. +out:
  476. + if (!list_empty(&txqi->schedule_order))
  477. + list_del_init(&txqi->schedule_order);
  478. +
  479. + return true;
  480. +}
  481. +EXPORT_SYMBOL(ieee80211_txq_may_transmit);
  482. +
  483. void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
  484. __acquires(txq_lock)
  485. {