mac80211: support paged rx SKBs

Mac80211 drivers can now pass paged SKBs to mac80211 via
ieee80211_rx{_irqsafe}. The implementation currently use
skb_linearize() in a few places i.e. management frame
handling, software decryption, defragmentation and A-MSDU
process. We will optimize them one by one later.

Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Cc: Kalle Valo <kalle.valo@iki.fi>
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Zhu Yi 2010-03-29 17:35:07 +08:00 committed by John W. Linville
parent e58b1253f1
commit e3cf8b3f7b
3 changed files with 54 additions and 11 deletions

View File

@ -1822,7 +1822,10 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
* ieee80211_rx - receive frame * ieee80211_rx - receive frame
* *
* Use this function to hand received frames to mac80211. The receive * Use this function to hand received frames to mac80211. The receive
* buffer in @skb must start with an IEEE 802.11 header. * buffer in @skb must start with an IEEE 802.11 header. In case of a
* paged @skb is used, the driver is recommended to put the ieee80211
* header of the frame on the linear part of the @skb to avoid memory
* allocation and/or memcpy by the stack.
* *
* This function may not be called in IRQ context. Calls to this function * This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls to * for a single hardware must be synchronized against each other. Calls to

View File

@ -38,7 +38,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
{ {
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN)) if (likely(skb->len > FCS_LEN))
skb_trim(skb, skb->len - FCS_LEN); __pskb_trim(skb, skb->len - FCS_LEN);
else { else {
/* driver bug */ /* driver bug */
WARN_ON(1); WARN_ON(1);
@ -227,6 +227,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN; present_fcs_len = FCS_LEN;
/* make sure hdr->frame_control is on the linear part */
if (!pskb_may_pull(origskb, 2)) {
dev_kfree_skb(origskb);
return NULL;
}
if (!local->monitors) { if (!local->monitors) {
if (should_drop_frame(origskb, present_fcs_len)) { if (should_drop_frame(origskb, present_fcs_len)) {
dev_kfree_skb(origskb); dev_kfree_skb(origskb);
@ -931,6 +937,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
} }
if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE;
/* Check for weak IVs if possible */ /* Check for weak IVs if possible */
if (rx->sta && rx->key->conf.alg == ALG_WEP && if (rx->sta && rx->key->conf.alg == ALG_WEP &&
ieee80211_is_data(hdr->frame_control) && ieee80211_is_data(hdr->frame_control) &&
@ -1231,6 +1240,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
} }
I802_DEBUG_INC(rx->local->rx_handlers_fragments); I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
return RX_DROP_UNUSABLE;
seq = (sc & IEEE80211_SCTL_SEQ) >> 4; seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
if (frag == 0) { if (frag == 0) {
@ -1588,6 +1600,9 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
skb->dev = dev; skb->dev = dev;
__skb_queue_head_init(&frame_list); __skb_queue_head_init(&frame_list);
if (skb_linearize(skb))
return RX_DROP_UNUSABLE;
ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
rx->sdata->vif.type, rx->sdata->vif.type,
rx->local->hw.extra_tx_headroom); rx->local->hw.extra_tx_headroom);
@ -2357,29 +2372,42 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
__le16 fc;
struct ieee80211_rx_data rx; struct ieee80211_rx_data rx;
int prepares; int prepares;
struct ieee80211_sub_if_data *prev = NULL; struct ieee80211_sub_if_data *prev = NULL;
struct sk_buff *skb_new; struct sk_buff *skb_new;
struct sta_info *sta, *tmp; struct sta_info *sta, *tmp;
bool found_sta = false; bool found_sta = false;
int err = 0;
hdr = (struct ieee80211_hdr *)skb->data; fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
memset(&rx, 0, sizeof(rx)); memset(&rx, 0, sizeof(rx));
rx.skb = skb; rx.skb = skb;
rx.local = local; rx.local = local;
if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_mgmt(hdr->frame_control)) if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
local->dot11ReceivedFragmentCount++; local->dot11ReceivedFragmentCount++;
if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
test_bit(SCAN_OFF_CHANNEL, &local->scanning))) test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
rx.flags |= IEEE80211_RX_IN_SCAN; rx.flags |= IEEE80211_RX_IN_SCAN;
if (ieee80211_is_mgmt(fc))
err = skb_linearize(skb);
else
err = !pskb_may_pull(skb, ieee80211_hdrlen(fc));
if (err) {
dev_kfree_skb(skb);
return;
}
hdr = (struct ieee80211_hdr *)skb->data;
ieee80211_parse_qos(&rx); ieee80211_parse_qos(&rx);
ieee80211_verify_alignment(&rx); ieee80211_verify_alignment(&rx);
if (ieee80211_is_data(hdr->frame_control)) { if (ieee80211_is_data(fc)) {
for_each_sta_info(local, hdr->addr2, sta, tmp) { for_each_sta_info(local, hdr->addr2, sta, tmp) {
rx.sta = sta; rx.sta = sta;
found_sta = true; found_sta = true;

View File

@ -330,11 +330,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
if (iftype == NL80211_IFTYPE_MESH_POINT) { if (iftype == NL80211_IFTYPE_MESH_POINT) {
struct ieee80211s_hdr *meshdr = struct ieee80211s_hdr *meshdr =
(struct ieee80211s_hdr *) (skb->data + hdrlen); (struct ieee80211s_hdr *) (skb->data + hdrlen);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr); /* make sure meshdr->flags is on the linear part */
if (!pskb_may_pull(skb, hdrlen + 1))
return -1;
if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
memcpy(dst, meshdr->eaddr1, ETH_ALEN); skb_copy_bits(skb, hdrlen +
memcpy(src, meshdr->eaddr2, ETH_ALEN); offsetof(struct ieee80211s_hdr, eaddr1),
dst, ETH_ALEN);
skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr2),
src, ETH_ALEN);
} }
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
} }
break; break;
case cpu_to_le16(IEEE80211_FCTL_FROMDS): case cpu_to_le16(IEEE80211_FCTL_FROMDS):
@ -346,9 +353,14 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
if (iftype == NL80211_IFTYPE_MESH_POINT) { if (iftype == NL80211_IFTYPE_MESH_POINT) {
struct ieee80211s_hdr *meshdr = struct ieee80211s_hdr *meshdr =
(struct ieee80211s_hdr *) (skb->data + hdrlen); (struct ieee80211s_hdr *) (skb->data + hdrlen);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr); /* make sure meshdr->flags is on the linear part */
if (!pskb_may_pull(skb, hdrlen + 1))
return -1;
if (meshdr->flags & MESH_FLAGS_AE_A4) if (meshdr->flags & MESH_FLAGS_AE_A4)
memcpy(src, meshdr->eaddr1, ETH_ALEN); skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr1),
src, ETH_ALEN);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
} }
break; break;
case cpu_to_le16(0): case cpu_to_le16(0):
@ -357,7 +369,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
break; break;
} }
if (unlikely(skb->len - hdrlen < 8)) if (!pskb_may_pull(skb, hdrlen + 8))
return -1; return -1;
payload = skb->data + hdrlen; payload = skb->data + hdrlen;