Use sk_buff->cb for storing necessary data only. This allows ieee80211_rx_status and ieee80211_tx_control structures to be larger than cb size. Drivers have to copy the whole ieee80211_tx_control structure passed to their tx method to ieee80211_tx_status. It is somewhat simpler for them, prevents some bugs (it was so easy to forget to copy one of items from ieee80211_tx_control to ieee80211_tx_status), makes future extensions easier and as a bonus driver is no longer required to preserve sk_buff->cb. Signed-off-by: Jiri Benc Index: netdev/include/net/ieee80211.h =================================================================== --- netdev.orig/include/net/ieee80211.h 2006-01-13 16:44:08.000000000 +0100 +++ netdev/include/net/ieee80211.h 2006-01-13 16:44:22.000000000 +0100 @@ -141,9 +141,6 @@ struct ieee80211_tx_control { unsigned int retry_limit:8; /* duration field for RTS/CTS frame */ unsigned int rts_cts_duration:16; - /* TODO: change these bit flags to use one unsigned int variable and - * defines with BIT(n). These are copied to TX status structure and - * this will make the code faster and smaller. */ unsigned int req_tx_status:1; /* request TX status callback for this * frame */ unsigned int do_not_encrypt:1; /* send this frame without encryption; @@ -177,21 +174,24 @@ struct ieee80211_tx_control { unsigned int sw_retry_attempt:4; /* no. of times hw has tried to * transmit frame (not incl. hw retries) */ - int rateidx; /* internal 80211.o rateidx, to be copied to tx_status */ + int rateidx; /* internal 80211.o rateidx */ int alt_retry_rate; /* retry rate for the last retries, given as the * hw specific value for the rate (from * struct ieee80211_rate). To be used to limit * packet dropping when probing higher rates, if hw * supports multiple retry rates. -1 = not used */ + struct ieee80211_sub_if_data *sdata; /* internal */ }; -#define IEEE80211_CB_MAGIC 0xAAB80211 - +/* Stored in sk_buff->cb */ struct ieee80211_tx_packet_data { - unsigned int magic; - struct ieee80211_tx_control control; - unsigned long jiffies; struct ieee80211_sub_if_data *sdata; + unsigned long jiffies; + unsigned int req_tx_status:1; + unsigned int do_not_encrypt:1; + unsigned int pkt_probe_resp:1; + unsigned int requeue:1; + unsigned int queue:4; }; #define RX_FLAG_MMIC_ERROR 0x1 @@ -200,9 +200,7 @@ struct ieee80211_tx_packet_data { /* Receive status. The low-level driver should provide this information * (the subset supported by hardware) to the 802.11 code with each received - * frame. - * Current implementation copies this into skb->cb, so it must be less than - * 48 bytes. */ + * frame. */ struct ieee80211_rx_status { u64 hosttime; u64 mactime; @@ -219,24 +217,18 @@ struct ieee80211_rx_status { * (the subset supported by hardware) to the 802.11 code for each transmit * frame. */ struct ieee80211_tx_status { - /* flags copied from struct ieee80211_tx_control) */ - unsigned int req_tx_status:1; /* whether TX status was explicitly - * requested */ - unsigned int rate_ctrl_probe:1; /* whether this was a probe packet from - * rate control */ - unsigned int tx_filtered:1; + /* copied ieee80211_tx_control structure */ + struct ieee80211_tx_control control; - /* following three fields are only used with Atheros Super A/G */ - unsigned int turbo_prime_notify:1; /* notify HostAPd - CTS for Turbo - * Prime is sent */ - int queue_length; /* information about TX queue */ - int queue_number; - - int ack; /* whether the TX frame was ACKed */ + unsigned int tx_filtered:1; + unsigned int ack:1; /* whether the TX frame was ACKed */ int ack_signal; /* measured signal strength of the ACK frame */ int excessive_retries; int retry_count; - int rateidx; /* internal 80211.o rateidx, to be copied to tx_status */ + + /* following two fields are only used with Atheros Super A/G */ + int queue_length; /* information about TX queue */ + int queue_number; }; Index: netdev/net/ieee80211/ieee80211.c =================================================================== --- netdev.orig/net/ieee80211/ieee80211.c 2006-01-13 16:44:08.000000000 +0100 +++ netdev/net/ieee80211/ieee80211.c 2006-01-13 16:44:22.000000000 +0100 @@ -1212,7 +1212,7 @@ static int ieee80211_master_start_xmit(s struct ieee80211_tx_control control; struct ieee80211_tx_packet_data *pkt_data; struct ieee80211_sub_if_data *sdata; - int ret = 1; + int ret; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1220,18 +1220,16 @@ static int ieee80211_master_start_xmit(s * copy control out of the skb so other people can use skb->cb */ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; - if (unlikely(pkt_data->magic != IEEE80211_CB_MAGIC)) { - printk(KERN_WARNING "%s: Someone messed with our skb->cb\n", - dev->name); - dev_kfree_skb(skb); - return 0; - } - memcpy(&control, &pkt_data->control, - sizeof(struct ieee80211_tx_control)); + memset(&control, 0, sizeof(struct ieee80211_tx_control)); + control.sdata = pkt_data->sdata; + control.req_tx_status = pkt_data->req_tx_status; + control.do_not_encrypt = pkt_data->do_not_encrypt; + control.pkt_type = pkt_data->pkt_probe_resp ? PKT_PROBE_RESP : PKT_NORMAL; + control.requeue = pkt_data->requeue; + control.queue = pkt_data->queue; ret = ieee80211_tx(dev, skb, &control, - pkt_data->sdata->type == - IEEE80211_SUB_IF_TYPE_MGMT); + control.sdata->type == IEEE80211_SUB_IF_TYPE_MGMT); return ret; } @@ -1415,9 +1413,8 @@ static int ieee80211_subif_start_xmit(st pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); - pkt_data->magic = IEEE80211_CB_MAGIC; pkt_data->sdata = sdata; - pkt_data->control.do_not_encrypt = no_encrypt; + pkt_data->do_not_encrypt = no_encrypt; skb->dev = sdata->master; sdata->stats.tx_packets++; @@ -1468,12 +1465,11 @@ ieee80211_mgmt_start_xmit(struct sk_buff pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); - pkt_data->magic = IEEE80211_CB_MAGIC; pkt_data->sdata = sdata; if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) - pkt_data->control.pkt_type = PKT_PROBE_RESP; + pkt_data->pkt_probe_resp = 1; skb->priority = 20; /* use hardcode priority for mgmt TX queue */ skb->dev = sdata->master; @@ -1483,14 +1479,14 @@ ieee80211_mgmt_start_xmit(struct sk_buff * to request TX callback for hostapd. BIT(1) is checked. */ if ((fc & BIT(1)) == BIT(1)) { - pkt_data->control.req_tx_status = 1; + pkt_data->req_tx_status = 1; fc &= ~BIT(1); hdr->frame_control = cpu_to_le16(fc); } - pkt_data->control.do_not_encrypt = !(fc & WLAN_FC_ISWEP); + pkt_data->do_not_encrypt = !(fc & WLAN_FC_ISWEP); sdata->stats.tx_packets++; sdata->stats.tx_bytes += skb->len; @@ -2405,7 +2401,7 @@ static int ap_sta_ps_end(struct net_devi while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; sent++; - pkt_data->control.requeue = 1; + pkt_data->requeue = 1; dev_queue_xmit(skb); } while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { @@ -2417,7 +2413,7 @@ static int ap_sta_ps_end(struct net_devi "since STA not sleeping anymore\n", dev->name, MAC2STR(sta->addr), sta->aid); #endif /* IEEE80211_VERBOSE_DEBUG_PS */ - pkt_data->control.requeue = 1; + pkt_data->requeue = 1; dev_queue_xmit(skb); } @@ -3493,9 +3489,13 @@ void ieee80211_rx_irqsafe(struct net_dev struct ieee80211_rx_status *status) { struct ieee80211_local *local = dev->priv; + struct ieee80211_rx_status *saved; skb->dev = dev; - memcpy(skb->cb, status, sizeof(struct ieee80211_rx_status)); + saved = kmalloc(sizeof(struct ieee80211_rx_status), GFP_ATOMIC); + if (saved) + memcpy(saved, status, sizeof(struct ieee80211_rx_status)); + memcpy(skb->cb, &saved, sizeof(saved)); skb->pkt_type = ieee80211_rx_msg; skb_queue_tail(&local->skb_queue, skb); tasklet_schedule(&local->tasklet); @@ -3506,43 +3506,23 @@ void ieee80211_tx_status_irqsafe(struct struct ieee80211_tx_status *status) { struct ieee80211_local *local = dev->priv; + struct ieee80211_tx_status *saved; int tmp; - if (status->tx_filtered || status->excessive_retries) { - /* Need to save a copy of skb->cb somewhere. Storing it in the - * end of the data might not be the most efficient way of doing - * this (since it may require reallocation of packet data), but - * should be good enough for now since tx_filtered or - * excessive_retries should not be triggered that often. */ - if (skb_is_nonlinear(skb)) { - if (skb_linearize(skb, GFP_ATOMIC)) { - printk(KERN_DEBUG "%s: Failed to linearize " - "skb\n", dev->name); - dev_kfree_skb_irq(skb); - return; - } - } - if (skb_tailroom(skb) < sizeof(skb->cb) && - pskb_expand_head(skb, 0, sizeof(skb->cb), GFP_ATOMIC)) { - printk(KERN_DEBUG "%s: Failed to store skb->cb " - "in skb->data for TX filtered frame\n", - dev->name); - dev_kfree_skb_irq(skb); - return; - } - memcpy(skb_put(skb, sizeof(skb->cb)), skb->cb, - sizeof(skb->cb)); - } - skb->dev = dev; - memcpy(skb->cb, status, sizeof(struct ieee80211_tx_status)); + saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC); + if (saved) + memcpy(saved, status, sizeof(struct ieee80211_tx_status)); + memcpy(skb->cb, &saved, sizeof(saved)); skb->pkt_type = ieee80211_tx_status_msg; - skb_queue_tail(status->req_tx_status ? + skb_queue_tail(status->control.req_tx_status ? &local->skb_queue : &local->skb_queue_unreliable, skb); tmp = skb_queue_len(&local->skb_queue) + skb_queue_len(&local->skb_queue_unreliable); while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && (skb = skb_dequeue(&local->skb_queue_unreliable))) { + memcpy(&saved, skb->cb, sizeof(saved)); + kfree(saved); dev_kfree_skb_irq(skb); tmp--; I802_DEBUG_INC(local->tx_status_drop); @@ -3555,36 +3535,36 @@ static void ieee80211_tasklet_handler(un { struct ieee80211_local *local = (struct ieee80211_local *) data; struct sk_buff *skb; - struct ieee80211_rx_status rx_status; - struct ieee80211_tx_status tx_status; + struct ieee80211_rx_status *rx_status; + struct ieee80211_tx_status *tx_status; while ((skb = skb_dequeue(&local->skb_queue)) || (skb = skb_dequeue(&local->skb_queue_unreliable))) { switch (skb->pkt_type) { case ieee80211_rx_msg: - /* Make a copy of the RX status because the original - * skb may be freed during processing. Clear skb->type - * in order to not confuse kernel netstack. */ memcpy(&rx_status, skb->cb, sizeof(rx_status)); + if (!rx_status) { + if (net_ratelimit()) + printk(KERN_WARNING "%s: Not enough memory, dropping packet", + skb->dev->name); + dev_kfree_skb(skb); + return; + } + /* Clear skb->type in order to not confuse kernel + * netstack. */ skb->pkt_type = 0; - ieee80211_rx(skb->dev, skb, &rx_status); + ieee80211_rx(skb->dev, skb, rx_status); + kfree(rx_status); break; case ieee80211_tx_status_msg: - /* Make a copy of the TX status because the original - * skb may be freed during processing. */ memcpy(&tx_status, skb->cb, sizeof(tx_status)); - skb->pkt_type = 0; - if ((tx_status.tx_filtered || - tx_status.excessive_retries) && - skb->len >= sizeof(skb->cb)) { - /* Restore skb->cb from the copy that was made - * in ieee80211_tx_status_irqsafe() */ - memcpy(skb->cb, - skb->data + skb->len - sizeof(skb->cb), - sizeof(skb->cb)); - skb_trim(skb, skb->len - sizeof(skb->cb)); + if (!tx_status) { + dev_kfree_skb(skb); + return; } - ieee80211_tx_status(skb->dev, skb, &tx_status); + skb->pkt_type = 0; + ieee80211_tx_status(skb->dev, skb, tx_status); + kfree(tx_status); break; default: /* should never get here! */ printk(KERN_ERR "%s: Unknown message type (%d)\n", @@ -3598,12 +3578,23 @@ static void ieee80211_tasklet_handler(un /* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to * make a prepared TX frame (one that has been given to hw) to look like brand - * new IEEE 802.11 frame that is ready to go through TX processing again. */ + * new IEEE 802.11 frame that is ready to go through TX processing again. + * Also, tx_packet_data in cb is restored from tx_control. */ static void ieee80211_remove_tx_extra(struct ieee80211_local *local, struct ieee80211_key *key, - struct sk_buff *skb) + struct sk_buff *skb, + struct ieee80211_tx_control *control) { int hdrlen, iv_len, mic_len; + struct ieee80211_tx_packet_data *pkt_data; + + pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + pkt_data->sdata = control->sdata; + pkt_data->req_tx_status = control->req_tx_status; + pkt_data->do_not_encrypt = control->do_not_encrypt; + pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP); + pkt_data->requeue = control->requeue; + pkt_data->queue = control->queue; if (key == NULL) return; @@ -3653,8 +3644,6 @@ void ieee80211_tx_status(struct net_devi struct sk_buff *skb2; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = dev->priv; - struct ieee80211_tx_packet_data *pkt_data = - (struct ieee80211_tx_packet_data *) skb->cb; u16 frag, type; u32 msg_type; @@ -3705,14 +3694,14 @@ void ieee80211_tx_status(struct net_devi skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) { ieee80211_remove_tx_extra(local, sta->key, - skb); + skb, &status->control); skb_queue_tail(&sta->tx_filtered, skb); } else if (!(sta->flags & WLAN_STA_PS) && - !pkt_data->control.requeue) { + !status->control.requeue) { /* Software retry the packet once */ - pkt_data->control.requeue = 1; + status->control.requeue = 1; ieee80211_remove_tx_extra(local, sta->key, - skb); + skb, &status->control); dev_queue_xmit(skb); } else { if (net_ratelimit()) { @@ -3770,7 +3759,7 @@ void ieee80211_tx_status(struct net_devi local->dot11FailedCount++; } - if (!status->req_tx_status) { + if (!status->control.req_tx_status) { dev_kfree_skb(skb); return; } @@ -4695,6 +4684,9 @@ void ieee80211_unregister_hw(struct net_ ieee80211_proc_deinit_interface(local); + if (skb_queue_len(&local->skb_queue) + || skb_queue_len(&local->skb_queue_unreliable)) + printk(KERN_WARNING "%s: skb_queue not empty", dev->name); skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue_unreliable); @@ -4834,13 +4826,6 @@ static int __init ieee80211_init(void) (int) sizeof(skb->cb)); return -EINVAL; } - if (sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)) { - printk("80211: ieee80211_rx_status is bigger " - "than the skb->cb (%d > %d)\n", - (int) sizeof(struct ieee80211_rx_status), - (int) sizeof(skb->cb)); - return -EINVAL; - } ieee80211_proc_init(); { Index: netdev/net/ieee80211/ieee80211_sta.c =================================================================== --- netdev.orig/net/ieee80211/ieee80211_sta.c 2006-01-13 16:44:08.000000000 +0100 +++ netdev/net/ieee80211/ieee80211_sta.c 2006-01-13 16:44:22.000000000 +0100 @@ -399,11 +399,10 @@ static void ieee80211_sta_tx(struct net_ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); - pkt_data->magic = IEEE80211_CB_MAGIC; pkt_data->sdata = sdata; - pkt_data->control.do_not_encrypt = !encrypt; + pkt_data->do_not_encrypt = !encrypt; if (probe_resp) - pkt_data->control.pkt_type = PKT_PROBE_RESP; + pkt_data->pkt_probe_resp = 1; dev_queue_xmit(skb); } Index: netdev/net/ieee80211/sta_info.c =================================================================== --- netdev.orig/net/ieee80211/sta_info.c 2006-01-13 16:44:08.000000000 +0100 +++ netdev/net/ieee80211/sta_info.c 2006-01-13 16:44:08.000000000 +0100 @@ -258,10 +258,6 @@ static inline int sta_info_buffer_expire * beacon interval */ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; - - if (pkt_data->magic != IEEE80211_CB_MAGIC) - return 1; - return time_after(jiffies, pkt_data->jiffies + STA_TX_BUFFER_EXPIRE); } Index: netdev/net/ieee80211/wme.c =================================================================== --- netdev.orig/net/ieee80211/wme.c 2006-01-13 16:43:48.000000000 +0100 +++ netdev/net/ieee80211/wme.c 2006-01-13 16:44:08.000000000 +0100 @@ -185,9 +185,6 @@ static inline int classify80211(struct s int qos; const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; - if (unlikely(pkt_data->magic != IEEE80211_CB_MAGIC)) - return -1; - /* see if frame is data or non data frame */ if (unlikely(WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA)) { /* management frames go on AC_VO queue, but are sent @@ -239,14 +236,13 @@ static int wme_qdiscop_enqueue(struct sk struct ieee80211_sched_data *q = qdisc_priv(qd); struct ieee80211_tx_packet_data *pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; - struct ieee80211_tx_control *control = &pkt_data->control; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; unsigned short fc = le16_to_cpu(hdr->frame_control); struct Qdisc *qdisc; int err, queue; - if (control->requeue) { - skb_queue_tail(&q->requeued[control->queue], skb); + if (pkt_data->requeue) { + skb_queue_tail(&q->requeued[pkt_data->queue], skb); return 0; } @@ -278,7 +274,7 @@ static int wme_qdiscop_enqueue(struct sk kfree_skb(skb); err = NET_XMIT_DROP; } else { - control->queue = (unsigned int) queue; + pkt_data->queue = (unsigned int) queue; qdisc = q->queues[queue]; err = qdisc->enqueue(skb, qdisc); if (err == NET_XMIT_SUCCESS) { @@ -305,7 +301,7 @@ static int wme_qdiscop_requeue(struct sk int err; /* we recorded which queue to use earlier! */ - qdisc = q->queues[pkt_data->control.queue]; + qdisc = q->queues[pkt_data->queue]; if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) { qd->q.qlen++;