@@ -5265,11 +5265,49 @@ static void netdev_init_one_queue(struct net_device *dev,
queue->dev = dev;
}
-static void netdev_init_queues(struct net_device *dev)
+/**
+ * realloc_netdev_mq - (re)allocate network subqueues
+ * @dev: device
+ * @queue_count: the number of subqueues to (re)allocate
+ *
+ * (Re)allocates and initializes subqueue structs for each queue.
+ * It is allowed to use only until register_netdev().
+ * On error previous structs are intact.
+ */
+int realloc_netdev_mq(struct net_device *dev, unsigned int queue_count)
{
- netdev_init_one_queue(dev, &dev->rx_queue, NULL);
+ struct netdev_queue *tx;
+
+ tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);
+ if (!tx) {
+ printk(KERN_ERR "alloc_netdev: Unable to allocate "
+ "tx qdiscs.\n");
+ return -ENOMEM;
+ }
+
+ kfree(dev->_tx);
+
+ dev->_tx = tx;
+ dev->num_tx_queues = queue_count;
+ dev->real_num_tx_queues = queue_count;
+
netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL(realloc_netdev_mq);
+
+static int netdev_init_queues(struct net_device *dev, unsigned int queue_count)
+{
+ int err = realloc_netdev_mq(dev, queue_count);
+
+ if (err)
+ return err;
+
+ netdev_init_one_queue(dev, &dev->rx_queue, NULL);
spin_lock_init(&dev->tx_global_lock);
+
+ return 0;
}
/**
@@ -5280,13 +5318,12 @@ static void netdev_init_queues(struct net_device *dev)
* @queue_count: the number of subqueues to allocate
*
* Allocates a struct net_device with private data area for driver use
- * and performs basic initialization. Also allocates subquue structs
- * for each queue on the device at the end of the netdevice.
+ * and performs basic initialization. Also allocates subqueue structs
+ * for each queue on the device.
*/
struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
void (*setup)(struct net_device *), unsigned int queue_count)
{
- struct netdev_queue *tx;
struct net_device *dev;
size_t alloc_size;
struct net_device *p;
@@ -5308,16 +5345,12 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
return NULL;
}
- tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);
- if (!tx) {
- printk(KERN_ERR "alloc_netdev: Unable to allocate "
- "tx qdiscs.\n");
- goto free_p;
- }
-
dev = PTR_ALIGN(p, NETDEV_ALIGN);
dev->padded = (char *)dev - (char *)p;
+ if (netdev_init_queues(dev, queue_count))
+ goto free_p;
+
if (dev_addr_init(dev))
goto free_tx;
@@ -5325,14 +5358,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
dev_net_set(dev, &init_net);
- dev->_tx = tx;
- dev->num_tx_queues = queue_count;
- dev->real_num_tx_queues = queue_count;
-
dev->gso_max_size = GSO_MAX_SIZE;
- netdev_init_queues(dev);
-
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
INIT_LIST_HEAD(&dev->link_watch_list);
@@ -5342,7 +5369,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
return dev;
free_tx:
- kfree(tx);
+ kfree(dev->_tx);
free_p:
kfree(p);