NET: [UPDATED] Multiqueue network device support implementation.
Subsystems (3)
PATCH 1/1 NET: [UPDATED] Multiqueue network device support implementation.
From: Peter P Waskiewicz Jr <[email protected]>
Update: Fixed a typecast in free_netdev() for the egress_subqueue list.
Added an API and associated supporting routines for multiqueue network devices. This allows network devices supporting multiple TX queues to configure each queue within the netdevice and manage each queue independantly. Changes to the PRIO Qdisc also allow a user to map multiple flows to individual TX queues, taking advantage of each queue on the device.
---
include/linux/etherdevice.h | 3 +- include/linux/netdevice.h | 66 ++++++++++++++++++++++++++++++++++++++++++- include/linux/skbuff.h | 2 + net/core/dev.c | 25 +++++++++++++--- net/core/skbuff.c | 3 ++ net/ethernet/eth.c | 9 +++--- net/sched/sch_generic.c | 4 +-- net/sched/sch_prio.c | 55 +++++++++++++++++++++++++++++++----- 8 files changed, 146 insertions(+), 21 deletions(-)
include/linux/etherdevice.h +2 -1
@@ -39,7 +39,8 @@ extern void eth_header_cache_update(struct hh_cache *hh, struct net_device *devextern int eth_header_cache (struct neighbour *neigh,struct hh_cache *hh); -extern struct net_device *alloc_etherdev (int sizeof_priv); +extern struct net_device *alloc_etherdev_mq (int sizeof_priv,int queue_count); +#define alloc_etherdev (sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)static inline void eth_copy_and_sum (struct sk_buff *dest,const unsigned char *src,int len,int base)
include/linux/netdevice.h +64 -2
96 lines not on this page · raw message
include/linux/skbuff.h +2 -0
@@ -212,6 +212,7 @@ typedef unsigned char *sk_buff_data_t;* @pkt_type: Packet class * @fclone: skbuff clone status * @ip_summed: Driver fed us an IP checksum +* @queue_mapping: Queue mapping for multiqueue devices * @priority: Packet queueing priority * @users: User count - see {datagram,tcp}.c * @protocol: Packet protocol from driver
@@ -264,6 +265,7 @@ struct sk_buff {__wsum csum;__u32 csum_offset; }; +__u16 queue_mapping;__u32 priority; __u8 local_df:1, cloned:1,
net/core/dev.c +20 -5
@@ -3292,16 +3292,18 @@ static struct net_device_stats *maybe_internal_stats(struct net_device *dev) }/** -* alloc_netdev - allocate network device +* alloc_netdev_mq - allocate network device * @sizeof_priv: size of private data to allocate space for * @name: device name format string * @setup: callback to initialize device +* @queue_count: the number of subqueues to allocate * * Allocates a struct net_device with private data area for driver use -* and performs basic initialization. +* and performs basic initialization. Also allocates subqueue structs +* for each queue on the device. */ -struct net_device *alloc_netdev (int sizeof_priv,const char *name, -void (*setup)(struct net_device *)) +struct net_device *alloc_netdev_mq (int sizeof_priv,const char *name, +void (*setup)(struct net_device *),int queue_count) {void *p;struct net_device *dev;
@@ -3326,12 +3328,23 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *name,if (sizeof_priv) dev->priv =netdev_priv (dev); + alloc_size = (sizeof (struct net_device_subqueue ) * queue_count); + + p =kzalloc (alloc_size,GFP_KERNEL ); +if (!p) { +printk (KERN_ERR "alloc_netdev: Unable to allocate queues.\n" ); +return NULL ; + } + + dev->egress_subqueue = p; + dev->egress_subqueue_count = queue_count; + dev->get_stats = maybe_internal_stats;setup (dev);strcpy (dev->name, name);return dev; } -EXPORT_SYMBOL (alloc_netdev); +EXPORT_SYMBOL (alloc_netdev_mq);/** * free_netdev - free network device
@@ -3345,6 +3358,7 @@ void free_netdev(struct net_device *dev) {#ifdef CONFIG_SYSFS /* Compatibility with error handling in drivers */ +kfree (dev->egress_subqueue);if (dev->reg_state ==NETREG_UNINITIALIZED ) {kfree ((char *)dev - dev->padded);return ;
@@ -3356,6 +3370,7 @@ void free_netdev(struct net_device *dev)/* will free via device release */ put_device (&dev->dev);#else +kfree (dev->egress_subqueue);kfree ((char *)dev - dev->padded);#endif }
net/core/skbuff.c +3 -0
@@ -474,6 +474,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) n->nohdr =0 ;C (pkt_type);C (ip_summed); +C (queue_mapping);C (priority);#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE )C (ipvs_property);
@@ -516,6 +517,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)#endif new->sk =NULL ; new->dev = old->dev; + new->queue_mapping = old->queue_mapping; new->priority = old->priority; new->protocol = old->protocol; new->dst =dst_clone (old->dst);
@@ -1974,6 +1976,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features) tail = nskb; nskb->dev = skb->dev; + nskb->queue_mapping = skb->queue_mapping; nskb->priority = skb->priority; nskb->protocol = skb->protocol; nskb->dst =dst_clone (skb->dst);
net/ethernet/eth.c +5 -4
@@ -316,9 +316,10 @@ void ether_setup(struct net_device *dev)EXPORT_SYMBOL (ether_setup);/** -* alloc_etherdev - Allocates and sets up an Ethernet device +* alloc_etherdev_mq - Allocates and sets up an Ethernet device * @sizeof_priv: Size of additional driver-private structure to be allocated * for this Ethernet device +* @queue_count: The number of queues this device has. * * Fill in the fields of the device structure with Ethernet-generic * values. Basically does everything except registering the device.
@@ -328,8 +329,8 @@ EXPORT_SYMBOL(ether_setup);* this private data area. */ -struct net_device *alloc_etherdev (int sizeof_priv) +struct net_device *alloc_etherdev_mq (int sizeof_priv,int queue_count) { -return alloc_netdev (sizeof_priv,"eth%d" , ether_setup); +return alloc_netdev_mq (sizeof_priv,"eth%d" , ether_setup, queue_count); } -EXPORT_SYMBOL (alloc_etherdev); +EXPORT_SYMBOL (alloc_etherdev_mq);
net/sched/sch_generic.c +2 -2
@@ -133,7 +133,8 @@ static inline int qdisc_restart(struct net_device *dev)/* And release queue */ spin_unlock (&dev->queue_lock); -if (!netif_queue_stopped (dev)) { +if (!netif_queue_stopped (dev) && + !netif_subqueue_stopped (dev, skb->queue_mapping)) {int ret; ret =dev_hard_start_xmit (skb, dev);
@@ -149,7 +150,6 @@ static inline int qdisc_restart(struct net_device *dev) goto collision; } } -/* NETDEV_TX_BUSY - we need to requeue */ /* Release the driver */ if (!nolock) {
net/sched/sch_prio.c +48 -7
97 lines not on this page · raw message
Quoted 9 lines · Peter P Waskiewicz Jr
From: Peter P Waskiewicz Jr <[email protected]>
Update: Fixed a typecast in
free_netdev()for theegress_subqueuelist.Added an API and associated supporting routines for multiqueue network devices. This allows network devices supporting multiple TX queues to configure each queue within the netdevice and manage each queue independantly. Changes to the PRIO Qdisc also allow a user to map multiple flows to individual TX queues, taking advantage of each queue on the device.
This indeed looks a lot better than the first patch. I'm too tired to fully review this now, but could you please post the corresponding e1000 patch? From a quick look I'm guessing that this patch changes the behaviour of the prio qdisc from strict priority to whatever scheduling mechanism e1000 uses for its queues when the multiqueue config option is enabled, which might surprise people.
Quoted 8 lines
This indeed looks a lot better than the first patch. I'm too tired to fully review this now, but could you please post the corresponding e1000 patch? From a quick look I'm guessing that this patch changes the behaviour of the prio qdisc from strict priority to whatever scheduling mechanism e1000 uses for its queues when the multiqueue config option is enabled, which might surprise people.
Thanks Pat for the initial feedback. I can post a set of patches to e1000 using the new API; I'll try to get them out asap (need to apply to this kernel tree). However, the PRIO qdisc still uses the priority in the bands for dequeueing priority, and will feed the queues on the NIC. The e1000, and any other multiqueue NIC, will schedule Tx based on how the PRIO qdisc feeds the queues. So the only priority here is the dequeuing priority from the kernel. The e1000 will use the new API for starting/stopping the individual queues based on the descriptors available, much like it does today for the global queue.
-PJ Waskiewicz
Quoted 3 lines · Peter P
Thanks Pat for the initial feedback. I can post a set of patches to e1000 using the new API; I'll try to get them out asap (need to apply to this kernel tree).
Thanks.
Quoted 7 lines
However, the PRIO qdisc still uses the priority in
the bands for dequeueing priority, and will feed the queues on the NIC. The e1000, and any other multiqueue NIC, will schedule Tx based on how the PRIO qdisc feeds the queues. So the only priority here is the dequeuing priority from the kernel. The e1000 will use the new API for starting/stopping the individual queues based on the descriptors available, much like it does today for the global queue.
Packets will only be dequeued from a band if the associated subqueue is active, which moves the decision from prio to the driver, no? What policy does e1000 use for scheduling its internal queues?
Quoted 9 lines
Thanks.
Quoted 3 lines
However, the PRIO qdisc still uses the priority in the bands for dequeueing priority, and will feed the queues on the NIC. The e1000, and any other multiqueue NIC, will schedule Tx
based on how
Quoted 3 lines
the PRIO qdisc feeds the queues. So the only priority here is the dequeuing priority from the kernel. The e1000 will use the new API for starting/stopping the individual queues based on the
Quoted 9 lines
descriptors
Quoted 1 line
available, much like it does today for the global queue.
Packets will only be dequeued from a band if the associated subqueue is active, which moves the decision from prio to the driver, no?
What policy does e1000 use for scheduling its internal queues?
E1000 is handed the skb's from PRIO to whichever queue the PRIO flows dictate (based on band2queue mapping, tc filters, or TOS to skb->priority filtering). Once the skb hits the e1000, the internal policy is round-robin on the hardware queues to dequeue and transmit on the wire. I agree the NIC does influence the decision of which band an skb will be dequeued from based on available descriptors, etc., but that's one of the goals of multiqueue: don't allow another traffic flow in one queue stop or impact another separate flow.
Once NICs start using hardware-based priority scheduling (wireless?), we can use a round-robin type qdisc in the kernel to dequeue, and let the hardware directly decide how important one flow is over another.
Hope this answers your question.
Cheers,
-PJ Waskiewicz
Quoted 7 lines · Peter P
Quoted 5 lines
Packets will only be dequeued from a band if the associated subqueue is active, which moves the decision from prio to the driver, no?
What policy does e1000 use for scheduling its internal queues?
Quoted 8 lines
E1000 is handed the skb's from PRIO to whichever queue the PRIO flows dictate (based on band2queue mapping, tc filters, or TOS to
skb->priorityfiltering). Once the skb hits the e1000, the internal policy is round-robin on the hardware queues to dequeue and transmit on the wire. I agree the NIC does influence the decision of which band an skb will be dequeued from based on available descriptors, etc., but that's one of the goals of multiqueue: don't allow another traffic flow in one queue stop or impact another separate flow.
Yes, I'm not saying its wrong, but it will surprise users that put a prio qdisc on a multiqueue device without knowing anything about the multiqueue capabilities and suddenly don't have strict priority anymore. If it changes the behaviour, it should be explicitly enabled by the user in the prio qdisc configuration. This would also allow to move the band2queue mapping policy to userspace.
Quoted 3 lines
Once NICs start using hardware-based priority scheduling (wireless?), we can use a round-robin type qdisc in the kernel to dequeue, and let the hardware directly decide how important one flow is over another.
You bring up a good point, it would be good to hear the opinion from one of the wireless people on this since they have their own multiqueue scheduler in the wireless-dev tree.
Quoted 4 lines · Patrick McHardy
You bring up a good point, it would be good to hear the opinion from one of the wireless people on this since they have their own multiqueue scheduler in the wireless-dev tree.
The one in the wireless-dev is pretty much like this one. It existed only because there was not such a multiqueue aware qdisc available at that time.
The requirement for wireless is the same as the strict PRIO with an addition that the dequeued SKB's corresponding NIC hardware queue must be active (this is also true for other devices I think, otherwise it has to be requeued which leads a busy or dead loop in the end). In other words, the dequeue method should select the SKB with the highest priority from all the ACTIVE hardware queues (not all queues). The wireless hardware then schedules all the packets from its 4 hardware TX queues based on the priority and network environment.
Thanks,
-yi
include/linux/netdevice.h struct net_device · line 549
Quoted 3 lines · Peter P Waskiewicz Jr
include/linux/netdevice.h +3
-0+/* To retrieve statistics per subqueue - FOR FUTURE USE */ +struct net_device_stats * (*get_subqueue_stats)(struct net_device *dev, +int queue_index);
Please no future use stuff, just add it when you need it.
Quoted 5 lines
net/core/dev.c +0
-0
net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name, · line 3336
Quoted 9 lines
net/core/dev.c +6
-0@@ -3326,12 +3328,23 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *name,if (sizeof_priv) dev->priv =netdev_priv (dev); + alloc_size = (sizeof (struct net_device_subqueue ) * queue_count); + + p =kzalloc (alloc_size,GFP_KERNEL ); +if (!p) { +printk (KERN_ERR "alloc_netdev: Unable to allocate queues.\n" ); +return NULL ;
This leaks the device. You treat every single-queue device as having a single subqueue. If it doesn't get too ugly it would be nice to avoid this and only allocate the subqueue states for real multiqueue devices.
Quoted 13 lines
net/sched/sch_generic.c +2
-1@@ -133,7 +133,8 @@ static inline int qdisc_restart(struct net_device *dev)/* And release queue */ spin_unlock (&dev->queue_lock); -if (!netif_queue_stopped (dev)) { +if (!netif_queue_stopped (dev) && + !netif_subqueue_stopped (dev, skb->queue_mapping)) {int ret; ret =dev_hard_start_xmit (skb, dev);
net/sched/sch_generic.c static inline int qdisc_restart(struct net_device *dev) · line 152
Quoted 4 lines
net/sched/sch_generic.c +0
-1@@ -149,7 +150,6 @@ static inline int qdisc_restart(struct net_device *dev) goto collision; } } -
Unrelated whitespace change.
Quoted 16 lines
net/sched/sch_generic.c +0
-0/* NETDEV_TX_BUSY - we need to requeue */ /* Release the driver */ if (!nolock) {net/sched/sch_prio.c +1
-0@@ -43,6 +43,7 @@ struct prio_sched_datastruct tcf_proto *filter_list;u8 prio2band[TC_PRIO_MAX +1 ];struct Qdisc *queues[TCQ_PRIO_BANDS ]; +u16 band2queue[TC_PRIO_MAX +1 ]; };
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · line 66
Quoted 4 lines
net/sched/sch_prio.c +0
-1@@ -63,20 +64,26 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)case TC_ACT_SHOT :return NULL ; }; -
Same here
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · line 75
Quoted 9 lines
net/sched/sch_prio.c +3
-0if (!q->filter_list ) {#else if (!q->filter_list ||tc_classify (skb, q->filter_list, &res)) {#endif if (TC_H_MAJ (band)) band =0 ; + skb->queue_mapping = + q->prio2band[q->band2queue[band&TC_PRIO_MAX ]]; +
Does this needs to be cleared at some point again? TC actions might redirect or mirror packets to other (multiqueue) devices.
Quoted 18 lines
net/sched/sch_prio.c +14
-0@@ -242,6 +259,30 @@ static int prio_tune(struct Qdisc *sch, struct rtattr *opt) } } } +/* setup queue to band mapping */ +if (q->bands < sch->dev->egress_subqueue_count) { + qmapoffset =1 ; + mod =0 ; + }else { + mod = q->bands % sch->dev->egress_subqueue_count; + qmapoffset = q->bands / sch->dev->egress_subqueue_count + + ((mod) ?1 :0 ); + } + + queue =0 ; + offset =0 ; +for (i =0 ; i < q->bands; i++) { + q->band2queue[i] = queue;
net/sched/sch_prio.c static int prio_tune(struct Qdisc *sch, struct rtattr *opt) · line 285
Quoted 10 lines
net/sched/sch_prio.c +10
-0+if ( ((i +1 ) - offset) == qmapoffset) { + queue++; + offset += qmapoffset; +if (mod) + mod--; + qmapoffset = q->bands / + sch->dev->egress_subqueue_count + + ((mod) ?1 :0 ); + } + }
Besides being quite ugly, I don't think this does what you want. For bands < queues we get band2queue[0] = 0, all others map to 1.
Quoted 9 lines
Review
Quoted 2 lines · Peter P Waskiewicz Jr
(quoted diff) +2
-0+ /* To retrieve statistics per subqueue - FOR FUTURE USE */ + struct net_device_stats* (*get_subqueue_stats)(struct
net_device*dev,Review
Quoted 1 line
(quoted diff) +1
-0+ int
queue_index);Please no future use stuff, just add it when you need it.
Gotcha. I'll remove this.
Quoted 15 lines
net/core/dev.c struct net_device *alloc_netdev(int
Quoted 6 lines
net/core/dev.c index 219a57f..c11c8fa renamed from net/core/dev.c +0
-0100644
net/core/dev.c +0
-0@@ -3326,12 +3328,23 @@ struct net_device *alloc_netdev(int
sizeof_priv, const char *name,net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name,
Quoted 8 lines
net/core/dev.c +5
-0if (sizeof_priv) dev->priv =netdev_priv (dev); + alloc_size = (sizeof (struct net_device_subqueue ) * queue_count); + + p =kzalloc (alloc_size,GFP_KERNEL ); + if (!p) { + printk(KERN_ERR "alloc_netdev: Unable to
Quoted 8 lines
allocate queues.\n");
net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name, · line 3336
Quoted 1 line
net/core/dev.c +1
-0+return NULL ;This leaks the device. You treat every single-queue device as having a single subqueue. If it doesn't get too ugly it would be nice to avoid this and only allocate the subqueue states for real multiqueue devices.
We went back and forth on this. The reason we allocate a queue in every case, even on single-queue devices, was to make the stack not have branching for multiqueue and non-multiqueue devices. If we don't have at least one queue on a device, then we can't have netif_subqueue_stopped() in the hotpath unless we check if a device is multiqueue before. The original patches I released had this branching, and I was asked to not do that. I'd also like to see all queue-related stuff be pulled from net_device and put into net_device_subqueue at some point, even for single-queue devices. Thoughts?
Quoted 16 lines
net/sched/sch_generic.c static inline int qdisc_restart(struct
Quoted 4 lines
net/sched/sch_generic.c +0
-0@@ -133,7 +133,8 @@ static inline int qdisc_restart(struct
net_device*dev)net/sched/sch_generic.c static inline int qdisc_restart(struct net_device *dev)
Quoted 6 lines
net/sched/sch_generic.c +2
-1/* And release queue */ spin_unlock (&dev->queue_lock); -if (!netif_queue_stopped (dev)) { +if (!netif_queue_stopped (dev) && + !netif_subqueue_stopped(dev,
skb->queue_mapping)) {net/sched/sch_generic.c static inline int qdisc_restart(struct net_device *dev)
Quoted 3 lines
net/sched/sch_generic.c +0
-0int ret; ret = dev_hard_start_xmit(skb,dev); @@ -149,7 +150,6 @@ static
Quoted 8 lines
net/sched/sch_generic.c static inline int qdisc_restart(struct net_device *dev) · line 152
Quoted 5 lines
inline int
qdisc_restart(struct net_device *dev)net/sched/sch_generic.c +0
-1goto collision; } } -Unrelated whitespace change.
I'll fix that.
Quoted 16 lines
Quoted 16 lines
net/sched/sch_generic.c +0
-0/* NETDEV_TX_BUSY - we need to requeue */ /* Release the driver */ if (!nolock) {net/sched/sch_prio.c index renamed from net/sched/sch_prio.c +0
-05cfe60b..7365621 100644
net/sched/sch_prio.c +1
-0@@ -43,6 +43,7 @@ struct prio_sched_datastruct tcf_proto *filter_list;u8 prio2band[TC_PRIO_MAX +1 ];struct Qdisc *queues[TCQ_PRIO_BANDS ]; +u16 band2queue[TC_PRIO_MAX +1 ]; };
Quoted 8 lines
net/sched/sch_prio.c prio_classify(struct sk_buff *skb,
Quoted 1 line
net/sched/sch_prio.c +0
-0@@ -63,20 +64,26 @@ prio_classify(struct sk_buff *skb,struct Qdisc *sch, int *qerr)
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · line 66
Quoted 4 lines
net/sched/sch_prio.c +0
-1case TC_ACT_SHOT :return NULL ; }; -Same here
I'll fix that too.
Quoted 11 lines
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
Quoted 4 lines
net/sched/sch_prio.c +0
-0if (!q->filter_list ) { #else if (!q->filter_list || tc_classify(skb,
q->filter_list, &res)) {net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · line 75
Quoted 5 lines
net/sched/sch_prio.c +2
-0#endif if (TC_H_MAJ (band)) band =0 ; + skb->queue_mapping = +q->prio2band[q->band2queue[band&TC_PRIO_MAX]];
Quoted 5 lines
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, · line 75
Quoted 1 line
net/sched/sch_prio.c +1
-0+Does this needs to be cleared at some point again? TC actions might redirect or mirror packets to other (multiqueue) devices.
If an skb is redirected to another device, the skb should be filtered through that device's qdisc, yes?
Quoted 27 lines
net/sched/sch_prio.c static int prio_tune(struct Qdisc
Quoted 2 lines
net/sched/sch_prio.c +0
-0@@ -242,6 +259,30 @@ static int prio_tune(struct Qdisc*sch, struct rtattr *opt)
net/sched/sch_prio.c static int prio_tune(struct Qdisc *sch, struct rtattr *opt) · line 281
Quoted 10 lines
net/sched/sch_prio.c +7
-0} } } +/* setup queue to band mapping */ +if (q->bands < sch->dev->egress_subqueue_count) { + qmapoffset =1 ; + mod =0 ; + }else { + mod = q->bands % sch->dev->egress_subqueue_count; + qmapoffset = q->bands /
sch->dev->egress_subqueue_count+Quoted 13 lines
net/sched/sch_prio.c +13
-0+ ((mod) ?1 :0 ); + } + + queue =0 ; + offset =0 ; +for (i =0 ; i < q->bands; i++) { + q->band2queue[i] = queue; +if ( ((i +1 ) - offset) == qmapoffset) { + queue++; + offset += qmapoffset; +if (mod) + mod--; + qmapoffset = q->bands /
Quoted 9 lines
net/sched/sch_prio.c static int prio_tune(struct Qdisc *sch, struct rtattr *opt) · line 285
Quoted 4 lines
net/sched/sch_prio.c +4
-0+ sch->dev->egress_subqueue_count + + ((mod) ?1 :0 ); + } + }Besides being quite ugly, I don't think this does what you want. For bands < queues we get band2queue[0] = 0, all others map to 1.
I see what's wrong with this. If I removed "mod = 0;", the algorithm works as intended. We also did go through a number of iterations of the best way to map queues to bands, using a simple hash, a complex assigment, etc., and this algorithm gives the best distribution given any combination of queues / bands (minus that small bug). I'll fix this and resubmit.
Thanks for the feedback,
-PJ Waskiewicz
Quoted 6 lines · Peter P
Quoted 4 lines
This leaks the device. You treat every single-queue device as having a single subqueue. If it doesn't get too ugly it would be nice to avoid this and only allocate the subqueue states for real multiqueue devices.
Quoted 7 lines
We went back and forth on this. The reason we allocate a queue in every case, even on single-queue devices, was to make the stack not have branching for multiqueue and non-multiqueue devices. If we don't have at least one queue on a device, then we can't have
netif_subqueue_stopped()in the hotpath unless we check if a device is multiqueue before. The original patches I released had this branching, and I was asked to not do that.
OK, thanks for the explanation.
Quoted 10 lines
Quoted 6 lines
net/sched/sch_prio.c prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) · line 74
Quoted 2 lines
net/sched/sch_prio.c +2
-0+ skb->queue_mapping = + q->prio2band[q->band2queue[band&TC_PRIO_MAX ]];Does this needs to be cleared at some point again? TC actions might redirect or mirror packets to other (multiqueue) devices.
If an skb is redirected to another device, the skb should be filtered through that device's qdisc, yes?
Yes, but the device might not have a queue or use something different than prio, so the value would stay the same. I think you need to clear it before enqueueing a packet or alternatively when redirecting in the mirred action.
Quoted 10 lines
Quoted 2 lines
Quoted 2 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ skb->queue_mapping = +q->prio2band[q->band2queue[band&TC_PRIO_MAX]];
Quoted 7 lines
Quoted 4 lines
Does this needs to be cleared at some point again? TC actions might redirect or mirror packets to other (multiqueue) devices.
If an skb is redirected to another device, the skb should
Quoted 9 lines
be filtered
Quoted 1 line
through that device's qdisc, yes?
Yes, but the device might not have a queue or use something different than prio, so the value would stay the same. I think you need to clear it before enqueueing a packet or alternatively when redirecting in the mirred action.
I see what you're saying now - I'll set the queue_mapping to zero before the enqueue occurs. Thanks for the feedback.
-PJ Waskiewicz