Skip to contentSkip to conversation

REPOST: Multiqueue network device support

PATCH In review Peter P Waskiewicz Jr 4 participants 9 messages Apr 9, 2007 – Apr 10, 2007

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover REPOST: Multiqueue network device support

Reposting due to the patches getting bounced by majordomo:

This is a redesign and repost of the multiqueue network device support patches. The new API for base drivers allows multiqueue-capable devices to manage their individual queues in the network stack. The stack now handles both non-multiqueue and multiqueue devices on the same codepath. Also, allocation and deallocation of the queues is handled by the kernel instead of the driver.

Documentation is also included describing in more detail how this works, as well as how a base driver can use the API to implement multiple queues.

These patches can also be pulled from my git repository at:

        git-pull git://lost.foo-projects.org/~ppwaskie/git/net-2.6.22 mq

--
Peter P. Waskiewicz Jr.
[email protected]

#1 lore raw

PATCH 1/2 NET: Multiqueue network device support documentation.

From: Peter P Waskiewicz Jr <[email protected]>

Adding documentation for the new multiqueue API.

---

 Documentation/networking/multiqueue.txt |   97 +++++++++++++++++++++++++++++++
 1 files changed, 97 insertions(+), 0 deletions(-)
Documentation/networking/multiqueue.txt new file +97 -0
new file mode 100644
index 0000000..c32ed83
@@ -0,0 +1,97 @@
+
+		HOWTO for multiqueue network device support
+		===========================================
+
+Section 1: Base driver requirements for implementing multiqueue support
+Section 2: Qdisc support for multiqueue devices
+Section 3: Brief howto using PRIO for multiqueue devices
+
+
+Intro: Kernel support for multiqueue devices
+---------------------------------------------------------
+
+Kernel support for multiqueue devices is only an API that is presented to the
+netdevice layer for base drivers to implement.  This feature is part of the
+core networking stack, and all network devices will be running on the
+multiqueue-aware stack.  If a base driver only has one queue, then these
+changes are transparent to that driver.
+
+
+Section 2: Base driver requirements for implementing multiqueue support
+-----------------------------------------------------------------------
+
+Base drivers are required to use the new alloc_etherdev_mq() or
+alloc_netdev_mq() functions to allocate the subqueues for the device.  The
+underlying kernel API will take care of the allocation and deallocation of
+the subqueue memory, as well as netdev configuration of where the queues
+exist in memory.
+
+The base driver will also need to manage the queues as it does the global
+netdev->queue_lock today.  Therefore base drivers should use the
+netif_{start|stop|wake}_subqueue() functions to manage each queue while the
+device is still operational.  netdev->queue_lock is still used when the device
+comes online or when it's completely shut down (unregister_netdev(), etc.).
+
+Finally, the base driver should indicate that it is a multiqueue device.  The
+feature flag NETIF_F_MULTI_QUEUE should be added to the netdev->features
+bitmap on device initialization.  Below is an example from e1000:
+
+#ifdef CONFIG_E1000_MQ
+	if ( (adapter->hw.mac.type == e1000_82571) ||
+	     (adapter->hw.mac.type == e1000_82572) ||
+	     (adapter->hw.mac.type == e1000_80003es2lan))
+		netdev->features |= NETIF_F_MULTI_QUEUE;
+#endif
+
+
+Section 3: Qdisc support for multiqueue devices
+-----------------------------------------------
+
+Currently two qdiscs support multiqueue devices.  The default qdisc, pfifo_fast,
+and the PRIO qdisc.  The qdisc is responsible for classifying the skb's to
+bands and queues, and will store the queue mapping into skb->queue_mapping.
+Use this field in the base driver to determine which queue to send the skb
+to.
+
+pfifo_fast, being the default qdisc when a device is brought online, will not
+assign a queue mapping, therefore the skb will have a value of zero.  We
+cannot assume anything about the device itself, how many queues it really has,
+etc.  Therefore sending all traffic to queue 0 is the safest thing to do here.
+
+The PRIO qdisc naturally plugs into a multiqueue device.  Upon load of the
+qdisc, PRIO will make a best-effort assignment of queue to PRIO band to evenly
+distribute traffic flows.  The algorithm can be found in prio_tune() in
+net/sched/sch_prio.c.  Once the association is made, any skb that is
+classified will have skb->queue_mapping set, which will allow the driver to
+properly queue skb's to multiple queues.
+
+
+Section 4: Brief howto using PRIO for multiqueue devices
+--------------------------------------------------------
+
+The userspace command 'tc,' part of the iproute2 package, is used to configure
+qdiscs.  To add the PRIO qdisc to your network device, assuming the device is
+called eth0, run the following command:
+
+# tc qdisc add dev eth0 root handle 1: prio
+
+This will create 3 bands, 0 being highest priority, and associate those bands
+to the queues on your NIC.  Assuming eth0 has 2 Tx queues, the band mapping
+would look like:
+
+band 0 => queue 0
+band 1 => queue 1
+band 2 => queue 1
+
+Traffic will begin flowing through each queue if your TOS values are assigning
+traffic across the various bands.  For example, ssh traffic will always try to
+go out band 0 based on TOS -> Linux priority conversion (realtime traffic),
+so it will be sent out queue 0.  ICMP traffic (pings) fall into the "normal"
+traffic classification, which is band 1.  Therefore pings will be send out
+queue 1 on the NIC.
+
+The behavior of tc filters remains the same, where it will override TOS priority
+classification.
+
+
+Author: Peter P. Waskiewicz Jr. <[email protected]>
#2 lore raw

PATCH 2/2 NET: Multiqueue network device support implementation.

In reply to Peter P Waskiewicz Jr

From: Peter P Waskiewicz Jr <[email protected]>

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.

Removed typecast when assigning net_device_subqueue in alloc_etherdev_mq.

---

 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
index 745c988..446de39 100644
@@ -39,7 +39,8 @@ extern void		eth_header_cache_update(struct hh_cache *hh, struct net_device *dev
 extern 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
index 08fa5c8..96fd263 100644
@@ -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
index 219a57f..86c9a89 100644
@@ -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((char *)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((char *)dev->egress_subqueue);
 	kfree((char *)dev - dev->padded);
 #endif
 }
net/core/skbuff.c +3 -0
index e60864e..642aab9 100644
@@ -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
index 0ac2524..87a509c 100644
@@ -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
index 52eb343..8f0063a 100644
@@ -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

#3 lore raw

Re: [PATCH 2/2] NET: Multiqueue network device support implementation.

Hi,

Quoted 9 lines · Peter P Waskiewicz Jr
net/core/dev.c +1 -0
@@ -3345,6 +3358,7 @@ void free_netdev(struct net_device *dev)
 {
 #ifdef CONFIG_SYSFS
 	/*  Compatibility with error handling in drivers */
+	kfree((char *)dev->egress_subqueue);
 	if (dev->reg_state == NETREG_UNINITIALIZED) {
 		kfree((char *)dev - dev->padded);
 		return;

net/core/dev.c void free_netdev(struct net_device *dev) · line 3376

Quoted 7 lines
net/core/dev.c +1 -0
@@ -3356,6 +3370,7 @@ void free_netdev(struct net_device *dev)
 	/* will free via device release */
 	put_device(&dev->dev);
 #else
+	kfree((char *)dev->egress_subqueue);
 	kfree((char *)dev - dev->padded);
 #endif
 }

Ahem. Explain that cast.

Jan

#4 lore raw

RE: [PATCH 2/2] NET: Multiqueue network device support implementation.

Quoted 13 lines

Hi,

Quoted 9 lines · Peter P Waskiewicz Jr
net/core/dev.c +1 -0
@@ -3345,6 +3358,7 @@ void free_netdev(struct net_device *dev)  {  
 #ifdef CONFIG_SYSFS
 	/*  Compatibility with error handling in drivers */
+	kfree((char *)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 */
Quoted 8 lines

net/core/dev.c void free_netdev(struct net_device *dev) · line 3376

Quoted 6 lines
net/core/dev.c +1 -0
 	put_device(&dev->dev);
 #else
+	kfree((char *)dev->egress_subqueue);
 	kfree((char *)dev - dev->padded);
 #endif
 }

Ahem. Explain that cast.

Jan,
This can be removed if needed; however, I'm just copying what
the other kfree()'s are doing in this function. Any instance of a typecast that I introduced in these patches are just following what others have done in that section of the code. So the cast is just for consistency in this particular area. If you'd like me to remove it, I can do that.

Thanks,

-PJ Waskiewicz

#5 lore raw

Re: [PATCH 2/2] NET: Multiqueue network device support implementation.

Quoted 12 lines · Peter P
Quoted 11 lines

net/core/dev.c void free_netdev(struct net_device *dev) · line 3376

Quoted 9 lines
net/core/dev.c +1 -0
@@ -3356,6 +3370,7 @@ void free_netdev(struct net_device *dev)
    /* will free via device release */
    put_device(&dev->dev);
 #else
+    kfree((char *)dev->egress_subqueue);
    kfree((char *)dev - dev->padded);
 #endif
 }

Ahem. Explain that cast.

Quoted 6 lines

This can be removed if needed; however, I'm just copying what
the other kfree()'s are doing in this function. Any instance of a typecast that I introduced in these patches are just following what others have done in that section of the code. So the cast is just for consistency in this particular area. If you'd like me to remove it, I can do that.

The other cast is there for the subtraction, not the kfree...

Cheers,

Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
#6 lore raw

Re: [PATCH 2/2] NET: Multiqueue network device support implementation.

In reply to Peter P Waskiewicz Jr

net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name, · line 3336

Quoted 6 lines · Peter P Waskiewicz Jr
net/core/dev.c +6 -0
+ 	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;

I think you either do not want to print it, or want additional details about device...

Quoted 24 lines
net/core/dev.c +7 -1
+ 	}
+ 
+ 	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((char *)dev->egress_subqueue);
 	if (dev->reg_state == NETREG_UNINITIALIZED) {
 		kfree((char *)dev - dev->padded);
 		return;

net/core/dev.c void free_netdev(struct net_device *dev) · line 3373

Quoted 4 lines
net/core/dev.c +1 -0
@@ -3356,6 +3370,7 @@ void free_netdev(struct net_device *dev)
 	/* will free via device release */
 	put_device(&dev->dev);
 #else
+	kfree((char *)dev->egress_subqueue);

Still casting :)

Signature · 1 line
	Evgeniy Polyakov
#7 lore raw

RE: [PATCH 2/2] NET: Multiqueue network device support implementation.

Quoted 7 lines

net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name,

Quoted 5 lines · Peter P Waskiewicz Jr
net/core/dev.c +5 -0
+ 	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 5 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;

I think you either do not want to print it, or want additional details about device...

Ok. This is essentially the same output printed if the netdev itself cannot be allocated. Should I update both strings to have more device-specific information?

Quoted 14 lines
Quoted 14 lines
net/core/dev.c +6 -1
+ 	}
+ 
+ 	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);
Quoted 16 lines
Quoted 10 lines

/**
* free_netdev - free network device

net/core/dev.c +1 -0
@@ -3345,6 +3358,7 @@ void free_netdev(struct net_device *dev)  {  
 #ifdef CONFIG_SYSFS
 	/*  Compatibility with error handling in drivers */
+	kfree((char *)dev->egress_subqueue);
 	if (dev->reg_state == NETREG_UNINITIALIZED) {
 		kfree((char *)dev - dev->padded);
 		return;

net/core/dev.c void free_netdev(struct net_device *dev) · line 3373

Quoted 4 lines
net/core/dev.c +1 -0
@@ -3356,6 +3370,7 @@ void free_netdev(struct net_device *dev)
 	/* will free via device release */
 	put_device(&dev->dev);
 #else
+	kfree((char *)dev->egress_subqueue);

Still casting :)

The latest repost removes these casts.

Thanks for the feedback,

-PJ Waskiewicz

#8 lore raw

Re: [PATCH 2/2] NET: Multiqueue network device support implementation.

Quoted 8 lines · Peter P
Quoted 8 lines

net/core/dev.c struct net_device *alloc_netdev(int sizeof_priv, const char *name,

Quoted 5 lines · Peter P Waskiewicz Jr
net/core/dev.c +5 -0
+ 	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");

Quoted 8 lines
Quoted 4 lines

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;

I think you either do not want to print it, or want additional details about device...

Ok. This is essentially the same output printed if the netdev itself cannot be allocated. Should I update both strings to have more device-specific information?

I wonder, if it is ever possible with gfp_kernel...

I think different patch would be ok.

Signature · 1 line
	Evgeniy Polyakov