aboutsummaryrefslogtreecommitdiffstats
path: root/net/packet
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2014-02-16 09:55:22 -0500
committerDavid S. Miller <davem@davemloft.net>2014-02-17 00:36:34 -0500
commit0fd5d57ba3456c4d0b77d1ae64be4818b47d7545 (patch)
treed5656ccfe39dba0d596334d4806e705cf35d0d03 /net/packet
parentb9507bdaf40e91fea2b1c0c1ee7dc627c8ee6fd6 (diff)
packet: check for ndo_select_queue during queue selection
Mathias reported that on an AMD Geode LX embedded board (ALiX) with ath9k driver PACKET_QDISC_BYPASS, introduced in commit d346a3fae3ff ("packet: introduce PACKET_QDISC_BYPASS socket option"), triggers a WARN_ON() coming from the driver itself via 066dae93bdf ("ath9k: rework tx queue selection and fix queue stopping/waking"). The reason why this happened is that ndo_select_queue() call is not invoked from direct xmit path i.e. for ieee80211 subsystem that sets queue and TID (similar to 802.1d tag) which is being put into the frame through 802.11e (WMM, QoS). If that is not set, pending frame counter for e.g. ath9k can get messed up. So the WARN_ON() in ath9k is absolutely legitimate. Generally, the hw queue selection in ieee80211 depends on the type of traffic, and priorities are set according to ieee80211_ac_numbers mapping; working in a similar way as DiffServ only on a lower layer, so that the AP can favour frames that have "real-time" requirements like voice or video data frames. Therefore, check for presence of ndo_select_queue() in netdev ops and, if available, invoke it with a fallback handler to __packet_pick_tx_queue(), so that driver such as bnx2x, ixgbe, or mlx4 can still select a hw queue for transmission in relation to the current CPU while e.g. ieee80211 subsystem can make their own choices. Reported-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de> Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Cc: Jesper Dangaard Brouer <brouer@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet')
-rw-r--r--net/packet/af_packet.c24
1 files changed, 21 insertions, 3 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 6a2bb37506c5..b5dc1168f98a 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -308,11 +308,27 @@ static bool packet_use_direct_xmit(const struct packet_sock *po)
308 return po->xmit == packet_direct_xmit; 308 return po->xmit == packet_direct_xmit;
309} 309}
310 310
311static u16 packet_pick_tx_queue(struct net_device *dev) 311static u16 __packet_pick_tx_queue(struct net_device *dev, struct sk_buff *skb)
312{ 312{
313 return (u16) raw_smp_processor_id() % dev->real_num_tx_queues; 313 return (u16) raw_smp_processor_id() % dev->real_num_tx_queues;
314} 314}
315 315
316static void packet_pick_tx_queue(struct net_device *dev, struct sk_buff *skb)
317{
318 const struct net_device_ops *ops = dev->netdev_ops;
319 u16 queue_index;
320
321 if (ops->ndo_select_queue) {
322 queue_index = ops->ndo_select_queue(dev, skb, NULL,
323 __packet_pick_tx_queue);
324 queue_index = netdev_cap_txqueue(dev, queue_index);
325 } else {
326 queue_index = __packet_pick_tx_queue(dev, skb);
327 }
328
329 skb_set_queue_mapping(skb, queue_index);
330}
331
316/* register_prot_hook must be invoked with the po->bind_lock held, 332/* register_prot_hook must be invoked with the po->bind_lock held,
317 * or from a context in which asynchronous accesses to the packet 333 * or from a context in which asynchronous accesses to the packet
318 * socket is not possible (packet_create()). 334 * socket is not possible (packet_create()).
@@ -2285,7 +2301,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
2285 } 2301 }
2286 } 2302 }
2287 2303
2288 skb_set_queue_mapping(skb, packet_pick_tx_queue(dev)); 2304 packet_pick_tx_queue(dev, skb);
2305
2289 skb->destructor = tpacket_destruct_skb; 2306 skb->destructor = tpacket_destruct_skb;
2290 __packet_set_status(po, ph, TP_STATUS_SENDING); 2307 __packet_set_status(po, ph, TP_STATUS_SENDING);
2291 packet_inc_pending(&po->tx_ring); 2308 packet_inc_pending(&po->tx_ring);
@@ -2499,7 +2516,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
2499 skb->dev = dev; 2516 skb->dev = dev;
2500 skb->priority = sk->sk_priority; 2517 skb->priority = sk->sk_priority;
2501 skb->mark = sk->sk_mark; 2518 skb->mark = sk->sk_mark;
2502 skb_set_queue_mapping(skb, packet_pick_tx_queue(dev)); 2519
2520 packet_pick_tx_queue(dev, skb);
2503 2521
2504 if (po->has_vnet_hdr) { 2522 if (po->has_vnet_hdr) {
2505 if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { 2523 if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {