File Index
Files and directories
Sign in
FILE

agg-rx.c

File preview with syntax highlighting.

linux-6.19.14/net/mac80211/agg-rx.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * HT handling
  4 *
  5 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
  6 * Copyright 2002-2005, Instant802 Networks, Inc.
  7 * Copyright 2005-2006, Devicescape Software, Inc.
  8 * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
  9 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
 10 * Copyright 2007-2010, Intel Corporation
 11 * Copyright(c) 2015-2017 Intel Deutschland GmbH
 12 * Copyright (C) 2018-2025 Intel Corporation
 13 */
 14
 15/**
 16 * DOC: RX A-MPDU aggregation
 17 *
 18 * Aggregation on the RX side requires only implementing the
 19 * @ampdu_action callback that is invoked to start/stop any
 20 * block-ack sessions for RX aggregation.
 21 *
 22 * When RX aggregation is started by the peer, the driver is
 23 * notified via @ampdu_action function, with the
 24 * %IEEE80211_AMPDU_RX_START action, and may reject the request
 25 * in which case a negative response is sent to the peer, if it
 26 * accepts it a positive response is sent.
 27 *
 28 * While the session is active, the device/driver are required
 29 * to de-aggregate frames and pass them up one by one to mac80211,
 30 * which will handle the reorder buffer.
 31 *
 32 * When the aggregation session is stopped again by the peer or
 33 * ourselves, the driver's @ampdu_action function will be called
 34 * with the action %IEEE80211_AMPDU_RX_STOP. In this case, the
 35 * call must not fail.
 36 */
 37
 38#include <linux/ieee80211.h>
 39#include <linux/slab.h>
 40#include <linux/export.h>
 41#include <net/mac80211.h>
 42#include "ieee80211_i.h"
 43#include "driver-ops.h"
 44
 45static void ieee80211_free_tid_rx(struct rcu_head *h)
 46{
 47	struct tid_ampdu_rx *tid_rx =
 48		container_of(h, struct tid_ampdu_rx, rcu_head);
 49	int i;
 50
 51	for (i = 0; i < tid_rx->buf_size; i++)
 52		__skb_queue_purge(&tid_rx->reorder_buf[i]);
 53	kfree(tid_rx->reorder_buf);
 54	kfree(tid_rx->reorder_time);
 55	kfree(tid_rx);
 56}
 57
 58void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 59				    u16 initiator, u16 reason, bool tx)
 60{
 61	struct ieee80211_local *local = sta->local;
 62	struct tid_ampdu_rx *tid_rx;
 63	struct ieee80211_ampdu_params params = {
 64		.sta = &sta->sta,
 65		.action = IEEE80211_AMPDU_RX_STOP,
 66		.tid = tid,
 67		.amsdu = false,
 68		.timeout = 0,
 69		.ssn = 0,
 70	};
 71
 72	lockdep_assert_wiphy(sta->local->hw.wiphy);
 73
 74	tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid],
 75					lockdep_is_held(&sta->local->hw.wiphy->mtx));
 76
 77	if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid))
 78		return;
 79
 80	RCU_INIT_POINTER(sta->ampdu_mlme.tid_rx[tid], NULL);
 81	__clear_bit(tid, sta->ampdu_mlme.agg_session_valid);
 82
 83	ht_dbg(sta->sdata,
 84	       "Rx BA session stop requested for %pM tid %u %s reason: %d\n",
 85	       sta->sta.addr, tid,
 86	       initiator == WLAN_BACK_RECIPIENT ? "recipient" : "initiator",
 87	       (int)reason);
 88
 89	if (drv_ampdu_action(local, sta->sdata, &params))
 90		sdata_info(sta->sdata,
 91			   "HW problem - can not stop rx aggregation for %pM tid %d\n",
 92			   sta->sta.addr, tid);
 93
 94	/* check if this is a self generated aggregation halt */
 95	if (initiator == WLAN_BACK_RECIPIENT && tx)
 96		ieee80211_send_delba(sta->sdata, sta->sta.addr,
 97				     tid, WLAN_BACK_RECIPIENT, reason);
 98
 99	/*
100	 * return here in case tid_rx is not assigned - which will happen if
101	 * IEEE80211_HW_SUPPORTS_REORDERING_BUFFER is set.
102	 */
103	if (!tid_rx)
104		return;
105
106	timer_delete_sync(&tid_rx->session_timer);
107
108	/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
109	spin_lock_bh(&tid_rx->reorder_lock);
110	tid_rx->removed = true;
111	spin_unlock_bh(&tid_rx->reorder_lock);
112	timer_delete_sync(&tid_rx->reorder_timer);
113
114	call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
115}
116
117void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
118				  const u8 *addr)
119{
120	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
121	struct sta_info *sta;
122	int i;
123
124	rcu_read_lock();
125	sta = sta_info_get_bss(sdata, addr);
126	if (!sta) {
127		rcu_read_unlock();
128		return;
129	}
130
131	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
132		if (ba_rx_bitmap & BIT(i))
133			set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
134
135	wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work);
136	rcu_read_unlock();
137}
138EXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
139
140/*
141 * After accepting the AddBA Request we activated a timer,
142 * resetting it after each frame that arrives from the originator.
143 */
144static void sta_rx_agg_session_timer_expired(struct timer_list *t)
145{
146	struct tid_ampdu_rx *tid_rx = timer_container_of(tid_rx, t,
147							 session_timer);
148	struct sta_info *sta = tid_rx->sta;
149	u8 tid = tid_rx->tid;
150	unsigned long timeout;
151
152	timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);
153	if (time_is_after_jiffies(timeout)) {
154		mod_timer(&tid_rx->session_timer, timeout);
155		return;
156	}
157
158	ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
159	       sta->sta.addr, tid);
160
161	set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
162	wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work);
163}
164
165static void sta_rx_agg_reorder_timer_expired(struct timer_list *t)
166{
167	struct tid_ampdu_rx *tid_rx = timer_container_of(tid_rx, t,
168							 reorder_timer);
169
170	rcu_read_lock();
171	ieee80211_release_reorder_timeout(tid_rx->sta, tid_rx->tid);
172	rcu_read_unlock();
173}
174
175void ieee80211_add_addbaext(struct sk_buff *skb,
176			    const u8 req_addba_ext_data,
177			    u16 buf_size)
178{
179	struct ieee80211_addba_ext_ie *addba_ext;
180	u8 *pos;
181
182	pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
183	*pos++ = WLAN_EID_ADDBA_EXT;
184	*pos++ = sizeof(struct ieee80211_addba_ext_ie);
185	addba_ext = (struct ieee80211_addba_ext_ie *)pos;
186
187	addba_ext->data = IEEE80211_ADDBA_EXT_NO_FRAG;
188	if (req_addba_ext_data)
189		addba_ext->data &= req_addba_ext_data;
190
191	addba_ext->data |=
192		u8_encode_bits(buf_size >> IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT,
193			       IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
194}
195
196u8 ieee80211_retrieve_addba_ext_data(struct sta_info *sta,
197				     const void *elem_data, ssize_t elem_len,
198				     u16 *buf_size)
199{
200	struct ieee802_11_elems *elems;
201	u8 buf_size_1k, data = 0;
202
203	if (!sta->sta.deflink.he_cap.has_he)
204		return 0;
205
206	if (elem_len <= 0)
207		return 0;
208
209	elems = ieee802_11_parse_elems(elem_data, elem_len,
210				       IEEE80211_FTYPE_MGMT |
211				       IEEE80211_STYPE_ACTION,
212				       NULL);
213
214	if (!elems || elems->parse_error || !elems->addba_ext_ie)
215		goto free;
216
217	data = elems->addba_ext_ie->data;
218
219	if (buf_size &&
220	    (sta->sta.valid_links || sta->sta.deflink.eht_cap.has_eht)) {
221		buf_size_1k = u8_get_bits(elems->addba_ext_ie->data,
222					  IEEE80211_ADDBA_EXT_BUF_SIZE_MASK);
223		*buf_size |= (u16)buf_size_1k <<
224			     IEEE80211_ADDBA_EXT_BUF_SIZE_SHIFT;
225	}
226
227free:
228	kfree(elems);
229
230	return data;
231}
232
233static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid,
234				      u8 dialog_token, u16 status, u16 policy,
235				      u16 buf_size, u16 timeout,
236				      const u8 req_addba_ext_data)
237{
238	struct ieee80211_sub_if_data *sdata = sta->sdata;
239	struct ieee80211_local *local = sdata->local;
240	struct sk_buff *skb;
241	struct ieee80211_mgmt *mgmt;
242	bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
243	u16 capab;
244
245	skb = dev_alloc_skb(sizeof(*mgmt) +
246		    2 + sizeof(struct ieee80211_addba_ext_ie) +
247		    local->hw.extra_tx_headroom);
248	if (!skb)
249		return;
250
251	skb_reserve(skb, local->hw.extra_tx_headroom);
252	mgmt = ieee80211_mgmt_ba(skb, da, sdata);
253
254	skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp));
255	mgmt->u.action.category = WLAN_CATEGORY_BACK;
256	mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP;
257	mgmt->u.action.u.addba_resp.dialog_token = dialog_token;
258
259	capab = u16_encode_bits(amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK);
260	capab |= u16_encode_bits(policy, IEEE80211_ADDBA_PARAM_POLICY_MASK);
261	capab |= u16_encode_bits(tid, IEEE80211_ADDBA_PARAM_TID_MASK);
262	capab |= u16_encode_bits(buf_size, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK);
263
264	mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
265	mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
266	mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
267
268	if (sta->sta.valid_links || sta->sta.deflink.he_cap.has_he)
269		ieee80211_add_addbaext(skb, req_addba_ext_data, buf_size);
270
271	ieee80211_tx_skb(sdata, skb);
272}
273
274void __ieee80211_start_rx_ba_session(struct sta_info *sta,
275				     u8 dialog_token, u16 timeout,
276				     u16 start_seq_num, u16 ba_policy, u16 tid,
277				     u16 buf_size, bool tx, bool auto_seq,
278				     const u8 addba_ext_data)
279{
280	struct ieee80211_local *local = sta->sdata->local;
281	struct tid_ampdu_rx *tid_agg_rx;
282	struct ieee80211_ampdu_params params = {
283		.sta = &sta->sta,
284		.action = IEEE80211_AMPDU_RX_START,
285		.tid = tid,
286		.amsdu = false,
287		.timeout = timeout,
288		.ssn = start_seq_num,
289	};
290	int i, ret = -EOPNOTSUPP;
291	u16 status = WLAN_STATUS_REQUEST_DECLINED;
292	u16 max_buf_size;
293
294	lockdep_assert_wiphy(sta->local->hw.wiphy);
295
296	if (tid >= IEEE80211_FIRST_TSPEC_TSID) {
297		ht_dbg(sta->sdata,
298		       "STA %pM requests BA session on unsupported tid %d\n",
299		       sta->sta.addr, tid);
300		goto end;
301	}
302
303	if (!sta->sta.valid_links &&
304	    !sta->sta.deflink.ht_cap.ht_supported &&
305	    !sta->sta.deflink.he_cap.has_he &&
306	    !sta->sta.deflink.s1g_cap.s1g) {
307		ht_dbg(sta->sdata,
308		       "STA %pM erroneously requests BA session on tid %d w/o HT\n",
309		       sta->sta.addr, tid);
310		/* send a response anyway, it's an error case if we get here */
311		goto end;
312	}
313
314	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
315		ht_dbg(sta->sdata,
316		       "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
317		       sta->sta.addr, tid);
318		goto end;
319	}
320
321	if (sta->sta.valid_links || sta->sta.deflink.eht_cap.has_eht)
322		max_buf_size = IEEE80211_MAX_AMPDU_BUF_EHT;
323	else if (sta->sta.deflink.he_cap.has_he)
324		max_buf_size = IEEE80211_MAX_AMPDU_BUF_HE;
325	else
326		max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
327
328	/* sanity check for incoming parameters:
329	 * check if configuration can support the BA policy
330	 * and if buffer size does not exceeds max value */
331	/* XXX: check own ht delayed BA capability?? */
332	if (((ba_policy != 1) &&
333	     (sta->sta.valid_links ||
334	      !(sta->sta.deflink.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA) ||
335	      !(sta->sta.deflink.s1g_cap.cap[3] & S1G_CAP3_HT_DELAYED_BA))) ||
336	    (buf_size > max_buf_size)) {
337		status = WLAN_STATUS_INVALID_QOS_PARAM;
338		ht_dbg_ratelimited(sta->sdata,
339				   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
340				   sta->sta.addr, tid, ba_policy, buf_size);
341		goto end;
342	}
343	/* determine default buffer size */
344	if (buf_size == 0)
345		buf_size = max_buf_size;
346
347	/* make sure the size doesn't exceed the maximum supported by the hw */
348	if (buf_size > sta->sta.max_rx_aggregation_subframes)
349		buf_size = sta->sta.max_rx_aggregation_subframes;
350	params.buf_size = buf_size;
351
352	ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n",
353	       buf_size, sta->sta.addr);
354
355	if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) {
356		if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) {
357			struct tid_ampdu_rx *tid_rx;
358
359			ht_dbg_ratelimited(sta->sdata,
360					   "updated AddBA Req from %pM on tid %u\n",
361					   sta->sta.addr, tid);
362			/* We have no API to update the timeout value in the
363			 * driver so reject the timeout update if the timeout
364			 * changed. If it did not change, i.e., no real update,
365			 * just reply with success.
366			 */
367			rcu_read_lock();
368			tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
369			if (tid_rx && tid_rx->timeout == timeout)
370				status = WLAN_STATUS_SUCCESS;
371			else
372				status = WLAN_STATUS_REQUEST_DECLINED;
373			rcu_read_unlock();
374			goto end;
375		}
376
377		ht_dbg_ratelimited(sta->sdata,
378				   "unexpected AddBA Req from %pM on tid %u\n",
379				   sta->sta.addr, tid);
380
381		/* delete existing Rx BA session on the same tid */
382		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
383					       WLAN_STATUS_UNSPECIFIED_QOS,
384					       false);
385	}
386
387	if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) {
388		ret = drv_ampdu_action(local, sta->sdata, &params);
389		ht_dbg(sta->sdata,
390		       "Rx A-MPDU request on %pM tid %d result %d\n",
391		       sta->sta.addr, tid, ret);
392		if (!ret)
393			status = WLAN_STATUS_SUCCESS;
394		goto end;
395	}
396
397	/* prepare A-MPDU MLME for Rx aggregation */
398	tid_agg_rx = kzalloc(sizeof(*tid_agg_rx), GFP_KERNEL);
399	if (!tid_agg_rx)
400		goto end;
401
402	spin_lock_init(&tid_agg_rx->reorder_lock);
403
404	/* rx timer */
405	timer_setup(&tid_agg_rx->session_timer,
406		    sta_rx_agg_session_timer_expired, TIMER_DEFERRABLE);
407
408	/* rx reorder timer */
409	timer_setup(&tid_agg_rx->reorder_timer,
410		    sta_rx_agg_reorder_timer_expired, 0);
411
412	/* prepare reordering buffer */
413	tid_agg_rx->reorder_buf =
414		kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
415	tid_agg_rx->reorder_time =
416		kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
417	if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
418		kfree(tid_agg_rx->reorder_buf);
419		kfree(tid_agg_rx->reorder_time);
420		kfree(tid_agg_rx);
421		goto end;
422	}
423
424	for (i = 0; i < buf_size; i++)
425		__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
426
427	ret = drv_ampdu_action(local, sta->sdata, &params);
428	ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
429	       sta->sta.addr, tid, ret);
430	if (ret) {
431		kfree(tid_agg_rx->reorder_buf);
432		kfree(tid_agg_rx->reorder_time);
433		kfree(tid_agg_rx);
434		goto end;
435	}
436
437	/* update data */
438	tid_agg_rx->ssn = start_seq_num;
439	tid_agg_rx->head_seq_num = start_seq_num;
440	tid_agg_rx->buf_size = buf_size;
441	tid_agg_rx->timeout = timeout;
442	tid_agg_rx->stored_mpdu_num = 0;
443	tid_agg_rx->auto_seq = auto_seq;
444	tid_agg_rx->started = false;
445	tid_agg_rx->reorder_buf_filtered = 0;
446	tid_agg_rx->tid = tid;
447	tid_agg_rx->sta = sta;
448	status = WLAN_STATUS_SUCCESS;
449
450	/* activate it for RX */
451	rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
452
453	if (timeout) {
454		mod_timer(&tid_agg_rx->session_timer, TU_TO_EXP_TIME(timeout));
455		tid_agg_rx->last_rx = jiffies;
456	}
457
458end:
459	if (status == WLAN_STATUS_SUCCESS) {
460		__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
461		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
462		sta->ampdu_mlme.tid_rx_token[tid] = dialog_token;
463	}
464
465	if (tx)
466		ieee80211_send_addba_resp(sta, sta->sta.addr, tid,
467					  dialog_token, status, 1, buf_size,
468					  timeout, addba_ext_data);
469}
470
471void ieee80211_process_addba_request(struct ieee80211_local *local,
472				     struct sta_info *sta,
473				     struct ieee80211_mgmt *mgmt,
474				     size_t len)
475{
476	u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
477	u8 dialog_token, addba_ext_data;
478
479	/* extract session parameters from addba request frame */
480	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
481	timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
482	start_seq_num =
483		le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
484
485	capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
486	ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
487	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
488	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
489
490	addba_ext_data =
491		ieee80211_retrieve_addba_ext_data(sta,
492						  mgmt->u.action.u.addba_req.variable,
493						  len -
494						  offsetof(typeof(*mgmt),
495							   u.action.u.addba_req.variable),
496						  &buf_size);
497
498	__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
499					start_seq_num, ba_policy, tid,
500					buf_size, true, false, addba_ext_data);
501}
502
503void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
504				 const u8 *addr, unsigned int tid)
505{
506	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
507	struct sta_info *sta;
508
509	rcu_read_lock();
510	sta = sta_info_get_bss(sdata, addr);
511	if (!sta)
512		goto unlock;
513
514	set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl);
515	wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work);
516 unlock:
517	rcu_read_unlock();
518}
519EXPORT_SYMBOL(ieee80211_manage_rx_ba_offl);
520
521void ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif,
522				   const u8 *addr, unsigned int tid)
523{
524	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
525	struct sta_info *sta;
526
527	rcu_read_lock();
528	sta = sta_info_get_bss(sdata, addr);
529	if (!sta)
530		goto unlock;
531
532	set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
533	wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work);
534
535 unlock:
536	rcu_read_unlock();
537}
538EXPORT_SYMBOL(ieee80211_rx_ba_timer_expired);
~/ Read-only file preview.