diff options
author | David S. Miller <davem@davemloft.net> | 2012-07-20 14:07:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-20 14:07:37 -0400 |
commit | aac3942cedc339b1e7b6bad28f3abe4ceb15bcc3 (patch) | |
tree | 49abe1f74a0a3290cf611878a704914a9f5e6577 | |
parent | 6f458dfb409272082c9bfa412f77ff2fc21c626f (diff) | |
parent | 6c85f2bdda2086d804e198a3f31b685bc2f86b04 (diff) |
Merge branch 'team_multiq'
Jiri Pirko says:
====================
This patchset represents the way I walked when I was adding multiqueue
support for team driver.
Jiri Pirko (6):
net: honour netif_set_real_num_tx_queues() retval
rtnl: allow to specify different num for rx and tx queue count
rtnl: allow to specify number of rx and tx queues on device creation
net: rename bond_queue_mapping to slave_dev_queue_mapping
bond_sysfs: use ream_num_tx_queues rather than params.tx_queue
team: add multiqueue support
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/bonding/bond_main.c | 20 | ||||
-rw-r--r-- | drivers/net/bonding/bond_sysfs.c | 2 | ||||
-rw-r--r-- | drivers/net/team/team.c | 65 | ||||
-rw-r--r-- | include/linux/if_link.h | 2 | ||||
-rw-r--r-- | include/linux/if_team.h | 8 | ||||
-rw-r--r-- | include/linux/netdevice.h | 7 | ||||
-rw-r--r-- | include/net/rtnetlink.h | 10 | ||||
-rw-r--r-- | include/net/sch_generic.h | 2 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 27 |
9 files changed, 114 insertions, 29 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3960b1b26178..6fae5f3ec7f6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c | |||
@@ -395,8 +395,8 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, | |||
395 | skb->dev = slave_dev; | 395 | skb->dev = slave_dev; |
396 | 396 | ||
397 | BUILD_BUG_ON(sizeof(skb->queue_mapping) != | 397 | BUILD_BUG_ON(sizeof(skb->queue_mapping) != |
398 | sizeof(qdisc_skb_cb(skb)->bond_queue_mapping)); | 398 | sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); |
399 | skb->queue_mapping = qdisc_skb_cb(skb)->bond_queue_mapping; | 399 | skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping; |
400 | 400 | ||
401 | if (unlikely(netpoll_tx_running(slave_dev))) | 401 | if (unlikely(netpoll_tx_running(slave_dev))) |
402 | bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); | 402 | bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb); |
@@ -4184,7 +4184,7 @@ static u16 bond_select_queue(struct net_device *dev, struct sk_buff *skb) | |||
4184 | /* | 4184 | /* |
4185 | * Save the original txq to restore before passing to the driver | 4185 | * Save the original txq to restore before passing to the driver |
4186 | */ | 4186 | */ |
4187 | qdisc_skb_cb(skb)->bond_queue_mapping = skb->queue_mapping; | 4187 | qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; |
4188 | 4188 | ||
4189 | if (unlikely(txq >= dev->real_num_tx_queues)) { | 4189 | if (unlikely(txq >= dev->real_num_tx_queues)) { |
4190 | do { | 4190 | do { |
@@ -4845,17 +4845,19 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
4845 | return 0; | 4845 | return 0; |
4846 | } | 4846 | } |
4847 | 4847 | ||
4848 | static int bond_get_tx_queues(struct net *net, struct nlattr *tb[]) | 4848 | static unsigned int bond_get_num_tx_queues(void) |
4849 | { | 4849 | { |
4850 | return tx_queues; | 4850 | return tx_queues; |
4851 | } | 4851 | } |
4852 | 4852 | ||
4853 | static struct rtnl_link_ops bond_link_ops __read_mostly = { | 4853 | static struct rtnl_link_ops bond_link_ops __read_mostly = { |
4854 | .kind = "bond", | 4854 | .kind = "bond", |
4855 | .priv_size = sizeof(struct bonding), | 4855 | .priv_size = sizeof(struct bonding), |
4856 | .setup = bond_setup, | 4856 | .setup = bond_setup, |
4857 | .validate = bond_validate, | 4857 | .validate = bond_validate, |
4858 | .get_tx_queues = bond_get_tx_queues, | 4858 | .get_num_tx_queues = bond_get_num_tx_queues, |
4859 | .get_num_rx_queues = bond_get_num_tx_queues, /* Use the same number | ||
4860 | as for TX queues */ | ||
4859 | }; | 4861 | }; |
4860 | 4862 | ||
4861 | /* Create a new bond based on the specified name and bonding parameters. | 4863 | /* Create a new bond based on the specified name and bonding parameters. |
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 485bedb8278c..dc15d248443f 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c | |||
@@ -1495,7 +1495,7 @@ static ssize_t bonding_store_queue_id(struct device *d, | |||
1495 | /* Check buffer length, valid ifname and queue id */ | 1495 | /* Check buffer length, valid ifname and queue id */ |
1496 | if (strlen(buffer) > IFNAMSIZ || | 1496 | if (strlen(buffer) > IFNAMSIZ || |
1497 | !dev_valid_name(buffer) || | 1497 | !dev_valid_name(buffer) || |
1498 | qid > bond->params.tx_queues) | 1498 | qid > bond->dev->real_num_tx_queues) |
1499 | goto err_no_cmd; | 1499 | goto err_no_cmd; |
1500 | 1500 | ||
1501 | /* Get the pointer to that interface if it exists */ | 1501 | /* Get the pointer to that interface if it exists */ |
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 813e1319095f..b104c05225f7 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <net/rtnetlink.h> | 27 | #include <net/rtnetlink.h> |
28 | #include <net/genetlink.h> | 28 | #include <net/genetlink.h> |
29 | #include <net/netlink.h> | 29 | #include <net/netlink.h> |
30 | #include <net/sch_generic.h> | ||
30 | #include <linux/if_team.h> | 31 | #include <linux/if_team.h> |
31 | 32 | ||
32 | #define DRV_NAME "team" | 33 | #define DRV_NAME "team" |
@@ -1121,6 +1122,22 @@ static const struct team_option team_options[] = { | |||
1121 | }, | 1122 | }, |
1122 | }; | 1123 | }; |
1123 | 1124 | ||
1125 | static struct lock_class_key team_netdev_xmit_lock_key; | ||
1126 | static struct lock_class_key team_netdev_addr_lock_key; | ||
1127 | |||
1128 | static void team_set_lockdep_class_one(struct net_device *dev, | ||
1129 | struct netdev_queue *txq, | ||
1130 | void *unused) | ||
1131 | { | ||
1132 | lockdep_set_class(&txq->_xmit_lock, &team_netdev_xmit_lock_key); | ||
1133 | } | ||
1134 | |||
1135 | static void team_set_lockdep_class(struct net_device *dev) | ||
1136 | { | ||
1137 | lockdep_set_class(&dev->addr_list_lock, &team_netdev_addr_lock_key); | ||
1138 | netdev_for_each_tx_queue(dev, team_set_lockdep_class_one, NULL); | ||
1139 | } | ||
1140 | |||
1124 | static int team_init(struct net_device *dev) | 1141 | static int team_init(struct net_device *dev) |
1125 | { | 1142 | { |
1126 | struct team *team = netdev_priv(dev); | 1143 | struct team *team = netdev_priv(dev); |
@@ -1148,6 +1165,8 @@ static int team_init(struct net_device *dev) | |||
1148 | goto err_options_register; | 1165 | goto err_options_register; |
1149 | netif_carrier_off(dev); | 1166 | netif_carrier_off(dev); |
1150 | 1167 | ||
1168 | team_set_lockdep_class(dev); | ||
1169 | |||
1151 | return 0; | 1170 | return 0; |
1152 | 1171 | ||
1153 | err_options_register: | 1172 | err_options_register: |
@@ -1216,6 +1235,29 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1216 | return NETDEV_TX_OK; | 1235 | return NETDEV_TX_OK; |
1217 | } | 1236 | } |
1218 | 1237 | ||
1238 | static u16 team_select_queue(struct net_device *dev, struct sk_buff *skb) | ||
1239 | { | ||
1240 | /* | ||
1241 | * This helper function exists to help dev_pick_tx get the correct | ||
1242 | * destination queue. Using a helper function skips a call to | ||
1243 | * skb_tx_hash and will put the skbs in the queue we expect on their | ||
1244 | * way down to the team driver. | ||
1245 | */ | ||
1246 | u16 txq = skb_rx_queue_recorded(skb) ? skb_get_rx_queue(skb) : 0; | ||
1247 | |||
1248 | /* | ||
1249 | * Save the original txq to restore before passing to the driver | ||
1250 | */ | ||
1251 | qdisc_skb_cb(skb)->slave_dev_queue_mapping = skb->queue_mapping; | ||
1252 | |||
1253 | if (unlikely(txq >= dev->real_num_tx_queues)) { | ||
1254 | do { | ||
1255 | txq -= dev->real_num_tx_queues; | ||
1256 | } while (txq >= dev->real_num_tx_queues); | ||
1257 | } | ||
1258 | return txq; | ||
1259 | } | ||
1260 | |||
1219 | static void team_change_rx_flags(struct net_device *dev, int change) | 1261 | static void team_change_rx_flags(struct net_device *dev, int change) |
1220 | { | 1262 | { |
1221 | struct team *team = netdev_priv(dev); | 1263 | struct team *team = netdev_priv(dev); |
@@ -1469,6 +1511,7 @@ static const struct net_device_ops team_netdev_ops = { | |||
1469 | .ndo_open = team_open, | 1511 | .ndo_open = team_open, |
1470 | .ndo_stop = team_close, | 1512 | .ndo_stop = team_close, |
1471 | .ndo_start_xmit = team_xmit, | 1513 | .ndo_start_xmit = team_xmit, |
1514 | .ndo_select_queue = team_select_queue, | ||
1472 | .ndo_change_rx_flags = team_change_rx_flags, | 1515 | .ndo_change_rx_flags = team_change_rx_flags, |
1473 | .ndo_set_rx_mode = team_set_rx_mode, | 1516 | .ndo_set_rx_mode = team_set_rx_mode, |
1474 | .ndo_set_mac_address = team_set_mac_address, | 1517 | .ndo_set_mac_address = team_set_mac_address, |
@@ -1543,12 +1586,24 @@ static int team_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
1543 | return 0; | 1586 | return 0; |
1544 | } | 1587 | } |
1545 | 1588 | ||
1589 | static unsigned int team_get_num_tx_queues(void) | ||
1590 | { | ||
1591 | return TEAM_DEFAULT_NUM_TX_QUEUES; | ||
1592 | } | ||
1593 | |||
1594 | static unsigned int team_get_num_rx_queues(void) | ||
1595 | { | ||
1596 | return TEAM_DEFAULT_NUM_RX_QUEUES; | ||
1597 | } | ||
1598 | |||
1546 | static struct rtnl_link_ops team_link_ops __read_mostly = { | 1599 | static struct rtnl_link_ops team_link_ops __read_mostly = { |
1547 | .kind = DRV_NAME, | 1600 | .kind = DRV_NAME, |
1548 | .priv_size = sizeof(struct team), | 1601 | .priv_size = sizeof(struct team), |
1549 | .setup = team_setup, | 1602 | .setup = team_setup, |
1550 | .newlink = team_newlink, | 1603 | .newlink = team_newlink, |
1551 | .validate = team_validate, | 1604 | .validate = team_validate, |
1605 | .get_num_tx_queues = team_get_num_tx_queues, | ||
1606 | .get_num_rx_queues = team_get_num_rx_queues, | ||
1552 | }; | 1607 | }; |
1553 | 1608 | ||
1554 | 1609 | ||
diff --git a/include/linux/if_link.h b/include/linux/if_link.h index f715750d0b87..ac173bd2ab65 100644 --- a/include/linux/if_link.h +++ b/include/linux/if_link.h | |||
@@ -140,6 +140,8 @@ enum { | |||
140 | IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ | 140 | IFLA_EXT_MASK, /* Extended info mask, VFs, etc */ |
141 | IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ | 141 | IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */ |
142 | #define IFLA_PROMISCUITY IFLA_PROMISCUITY | 142 | #define IFLA_PROMISCUITY IFLA_PROMISCUITY |
143 | IFLA_NUM_TX_QUEUES, | ||
144 | IFLA_NUM_RX_QUEUES, | ||
143 | __IFLA_MAX | 145 | __IFLA_MAX |
144 | }; | 146 | }; |
145 | 147 | ||
diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 7fd0cdeb9444..6960fc1841a7 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #ifdef __KERNEL__ | 14 | #ifdef __KERNEL__ |
15 | 15 | ||
16 | #include <linux/netpoll.h> | 16 | #include <linux/netpoll.h> |
17 | #include <net/sch_generic.h> | ||
17 | 18 | ||
18 | struct team_pcpu_stats { | 19 | struct team_pcpu_stats { |
19 | u64 rx_packets; | 20 | u64 rx_packets; |
@@ -98,6 +99,10 @@ static inline void team_netpoll_send_skb(struct team_port *port, | |||
98 | static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, | 99 | static inline int team_dev_queue_xmit(struct team *team, struct team_port *port, |
99 | struct sk_buff *skb) | 100 | struct sk_buff *skb) |
100 | { | 101 | { |
102 | BUILD_BUG_ON(sizeof(skb->queue_mapping) != | ||
103 | sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping)); | ||
104 | skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping); | ||
105 | |||
101 | skb->dev = port->dev; | 106 | skb->dev = port->dev; |
102 | if (unlikely(netpoll_tx_running(port->dev))) { | 107 | if (unlikely(netpoll_tx_running(port->dev))) { |
103 | team_netpoll_send_skb(port, skb); | 108 | team_netpoll_send_skb(port, skb); |
@@ -236,6 +241,9 @@ extern void team_options_unregister(struct team *team, | |||
236 | extern int team_mode_register(const struct team_mode *mode); | 241 | extern int team_mode_register(const struct team_mode *mode); |
237 | extern void team_mode_unregister(const struct team_mode *mode); | 242 | extern void team_mode_unregister(const struct team_mode *mode); |
238 | 243 | ||
244 | #define TEAM_DEFAULT_NUM_TX_QUEUES 16 | ||
245 | #define TEAM_DEFAULT_NUM_RX_QUEUES 16 | ||
246 | |||
239 | #endif /* __KERNEL__ */ | 247 | #endif /* __KERNEL__ */ |
240 | 248 | ||
241 | #define TEAM_STRING_MAX_LEN 32 | 249 | #define TEAM_STRING_MAX_LEN 32 |
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ab0251d541ab..eb06e58bed0b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -2110,7 +2110,12 @@ static inline int netif_set_real_num_rx_queues(struct net_device *dev, | |||
2110 | static inline int netif_copy_real_num_queues(struct net_device *to_dev, | 2110 | static inline int netif_copy_real_num_queues(struct net_device *to_dev, |
2111 | const struct net_device *from_dev) | 2111 | const struct net_device *from_dev) |
2112 | { | 2112 | { |
2113 | netif_set_real_num_tx_queues(to_dev, from_dev->real_num_tx_queues); | 2113 | int err; |
2114 | |||
2115 | err = netif_set_real_num_tx_queues(to_dev, | ||
2116 | from_dev->real_num_tx_queues); | ||
2117 | if (err) | ||
2118 | return err; | ||
2114 | #ifdef CONFIG_RPS | 2119 | #ifdef CONFIG_RPS |
2115 | return netif_set_real_num_rx_queues(to_dev, | 2120 | return netif_set_real_num_rx_queues(to_dev, |
2116 | from_dev->real_num_rx_queues); | 2121 | from_dev->real_num_rx_queues); |
diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index bbcfd0993432..6b00c4fc4291 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h | |||
@@ -44,8 +44,10 @@ static inline int rtnl_msg_family(const struct nlmsghdr *nlh) | |||
44 | * @get_xstats_size: Function to calculate required room for dumping device | 44 | * @get_xstats_size: Function to calculate required room for dumping device |
45 | * specific statistics | 45 | * specific statistics |
46 | * @fill_xstats: Function to dump device specific statistics | 46 | * @fill_xstats: Function to dump device specific statistics |
47 | * @get_tx_queues: Function to determine number of transmit queues to create when | 47 | * @get_num_tx_queues: Function to determine number of transmit queues |
48 | * creating a new device. | 48 | * to create when creating a new device. |
49 | * @get_num_rx_queues: Function to determine number of receive queues | ||
50 | * to create when creating a new device. | ||
49 | */ | 51 | */ |
50 | struct rtnl_link_ops { | 52 | struct rtnl_link_ops { |
51 | struct list_head list; | 53 | struct list_head list; |
@@ -77,8 +79,8 @@ struct rtnl_link_ops { | |||
77 | size_t (*get_xstats_size)(const struct net_device *dev); | 79 | size_t (*get_xstats_size)(const struct net_device *dev); |
78 | int (*fill_xstats)(struct sk_buff *skb, | 80 | int (*fill_xstats)(struct sk_buff *skb, |
79 | const struct net_device *dev); | 81 | const struct net_device *dev); |
80 | int (*get_tx_queues)(struct net *net, | 82 | unsigned int (*get_num_tx_queues)(void); |
81 | struct nlattr *tb[]); | 83 | unsigned int (*get_num_rx_queues)(void); |
82 | }; | 84 | }; |
83 | 85 | ||
84 | extern int __rtnl_link_register(struct rtnl_link_ops *ops); | 86 | extern int __rtnl_link_register(struct rtnl_link_ops *ops); |
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 9d7d54a00e63..d9611e032418 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
@@ -220,7 +220,7 @@ struct tcf_proto { | |||
220 | 220 | ||
221 | struct qdisc_skb_cb { | 221 | struct qdisc_skb_cb { |
222 | unsigned int pkt_len; | 222 | unsigned int pkt_len; |
223 | u16 bond_queue_mapping; | 223 | u16 slave_dev_queue_mapping; |
224 | u16 _pad; | 224 | u16 _pad; |
225 | unsigned char data[20]; | 225 | unsigned char data[20]; |
226 | }; | 226 | }; |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 045db8ad87c8..5bb1ebca2eb0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -771,6 +771,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, | |||
771 | + nla_total_size(4) /* IFLA_LINK */ | 771 | + nla_total_size(4) /* IFLA_LINK */ |
772 | + nla_total_size(4) /* IFLA_MASTER */ | 772 | + nla_total_size(4) /* IFLA_MASTER */ |
773 | + nla_total_size(4) /* IFLA_PROMISCUITY */ | 773 | + nla_total_size(4) /* IFLA_PROMISCUITY */ |
774 | + nla_total_size(4) /* IFLA_NUM_TX_QUEUES */ | ||
775 | + nla_total_size(4) /* IFLA_NUM_RX_QUEUES */ | ||
774 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 776 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
775 | + nla_total_size(1) /* IFLA_LINKMODE */ | 777 | + nla_total_size(1) /* IFLA_LINKMODE */ |
776 | + nla_total_size(ext_filter_mask | 778 | + nla_total_size(ext_filter_mask |
@@ -889,6 +891,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
889 | nla_put_u32(skb, IFLA_MTU, dev->mtu) || | 891 | nla_put_u32(skb, IFLA_MTU, dev->mtu) || |
890 | nla_put_u32(skb, IFLA_GROUP, dev->group) || | 892 | nla_put_u32(skb, IFLA_GROUP, dev->group) || |
891 | nla_put_u32(skb, IFLA_PROMISCUITY, dev->promiscuity) || | 893 | nla_put_u32(skb, IFLA_PROMISCUITY, dev->promiscuity) || |
894 | nla_put_u32(skb, IFLA_NUM_TX_QUEUES, dev->num_tx_queues) || | ||
895 | nla_put_u32(skb, IFLA_NUM_RX_QUEUES, dev->num_rx_queues) || | ||
892 | (dev->ifindex != dev->iflink && | 896 | (dev->ifindex != dev->iflink && |
893 | nla_put_u32(skb, IFLA_LINK, dev->iflink)) || | 897 | nla_put_u32(skb, IFLA_LINK, dev->iflink)) || |
894 | (dev->master && | 898 | (dev->master && |
@@ -1106,6 +1110,8 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
1106 | [IFLA_AF_SPEC] = { .type = NLA_NESTED }, | 1110 | [IFLA_AF_SPEC] = { .type = NLA_NESTED }, |
1107 | [IFLA_EXT_MASK] = { .type = NLA_U32 }, | 1111 | [IFLA_EXT_MASK] = { .type = NLA_U32 }, |
1108 | [IFLA_PROMISCUITY] = { .type = NLA_U32 }, | 1112 | [IFLA_PROMISCUITY] = { .type = NLA_U32 }, |
1113 | [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, | ||
1114 | [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, | ||
1109 | }; | 1115 | }; |
1110 | EXPORT_SYMBOL(ifla_policy); | 1116 | EXPORT_SYMBOL(ifla_policy); |
1111 | 1117 | ||
@@ -1624,17 +1630,22 @@ struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | |||
1624 | { | 1630 | { |
1625 | int err; | 1631 | int err; |
1626 | struct net_device *dev; | 1632 | struct net_device *dev; |
1627 | unsigned int num_queues = 1; | 1633 | unsigned int num_tx_queues = 1; |
1634 | unsigned int num_rx_queues = 1; | ||
1628 | 1635 | ||
1629 | if (ops->get_tx_queues) { | 1636 | if (tb[IFLA_NUM_TX_QUEUES]) |
1630 | err = ops->get_tx_queues(src_net, tb); | 1637 | num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]); |
1631 | if (err < 0) | 1638 | else if (ops->get_num_tx_queues) |
1632 | goto err; | 1639 | num_tx_queues = ops->get_num_tx_queues(); |
1633 | num_queues = err; | 1640 | |
1634 | } | 1641 | if (tb[IFLA_NUM_RX_QUEUES]) |
1642 | num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]); | ||
1643 | else if (ops->get_num_rx_queues) | ||
1644 | num_rx_queues = ops->get_num_rx_queues(); | ||
1635 | 1645 | ||
1636 | err = -ENOMEM; | 1646 | err = -ENOMEM; |
1637 | dev = alloc_netdev_mq(ops->priv_size, ifname, ops->setup, num_queues); | 1647 | dev = alloc_netdev_mqs(ops->priv_size, ifname, ops->setup, |
1648 | num_tx_queues, num_rx_queues); | ||
1638 | if (!dev) | 1649 | if (!dev) |
1639 | goto err; | 1650 | goto err; |
1640 | 1651 | ||