diff options
| author | David S. Miller <davem@davemloft.net> | 2014-10-06 01:04:21 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-10-06 01:04:21 -0400 |
| commit | 4e62ccd901062c532673f4fda16c484de2c3c8fc (patch) | |
| tree | 33befd15a97dcb8bb8b3a78bc6d355caf2d37f59 | |
| parent | f2600cf02b5b59aaee082c3485b7f01fc7f7b70c (diff) | |
| parent | 1556b8746e52501fdfaadd65837baaa63a9fa937 (diff) | |
Merge branch 'mlx4-next'
Amir Vadai says:
====================
net/mlx4_en: Optimizations to TX flow
This patchset contains optimizations to TX flow in mlx4_en driver. It also introduce
setting/getting tx copybreak, to enable controlling inline threshold dynamically.
TX flow optimizations was authored and posted to the mailing list by Eric
Dumazet [1] as a single patch. I splitted this patch to smaller patches,
Reviewed it and tested.
Changed from original patch:
- s/iowrite32be/iowrite32/, since ring->doorbell_qpn is stored as be32
The tx copybreak patch was also suggested by Eric Dumazet, and was edited and
reviewed by me. User space patch will be sent after kernel code is ready.
I am sending this patchset now since the merge window is near and don't want to
miss it.
More work need to do:
- Disable BF when xmit_more is in use
- Make TSO use xmit_more too. Maybe by splitting small TSO packets in the
driver itself, to avoid extra cpu/memory costs of GSO before the driver
- Fix mlx4_en_xmit buggy handling of queue full in the middle of a burst
partially posted to send queue using xmit_more
Eric, I edited the patches to have you as the Author and the first
signed-off-by. I hope it is ok with you (I wasn't sure if it is ok to sign by
you), anyway all the credit to those changes should go to you.
Patchset was tested and applied over commit 1e203c1 "(net: sched:
suspicious RCU usage in qdisc_watchdog")
[1] - https://patchwork.ozlabs.org/patch/394256/
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 44 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_tx.c | 330 | ||||
| -rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 90 | ||||
| -rw-r--r-- | include/linux/mlx4/device.h | 2 | ||||
| -rw-r--r-- | include/uapi/linux/ethtool.h | 1 | ||||
| -rw-r--r-- | net/core/ethtool.c | 1 |
6 files changed, 290 insertions, 178 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 42c9f8b09a6e..ae83da9cd18a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | |||
| @@ -1267,6 +1267,48 @@ static u32 mlx4_en_get_priv_flags(struct net_device *dev) | |||
| 1267 | return priv->pflags; | 1267 | return priv->pflags; |
| 1268 | } | 1268 | } |
| 1269 | 1269 | ||
| 1270 | static int mlx4_en_get_tunable(struct net_device *dev, | ||
| 1271 | const struct ethtool_tunable *tuna, | ||
| 1272 | void *data) | ||
| 1273 | { | ||
| 1274 | const struct mlx4_en_priv *priv = netdev_priv(dev); | ||
| 1275 | int ret = 0; | ||
| 1276 | |||
| 1277 | switch (tuna->id) { | ||
| 1278 | case ETHTOOL_TX_COPYBREAK: | ||
| 1279 | *(u32 *)data = priv->prof->inline_thold; | ||
| 1280 | break; | ||
| 1281 | default: | ||
| 1282 | ret = -EINVAL; | ||
| 1283 | break; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | return ret; | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | static int mlx4_en_set_tunable(struct net_device *dev, | ||
| 1290 | const struct ethtool_tunable *tuna, | ||
| 1291 | const void *data) | ||
| 1292 | { | ||
| 1293 | struct mlx4_en_priv *priv = netdev_priv(dev); | ||
| 1294 | int val, ret = 0; | ||
| 1295 | |||
| 1296 | switch (tuna->id) { | ||
| 1297 | case ETHTOOL_TX_COPYBREAK: | ||
| 1298 | val = *(u32 *)data; | ||
| 1299 | if (val < MIN_PKT_LEN || val > MAX_INLINE) | ||
| 1300 | ret = -EINVAL; | ||
| 1301 | else | ||
| 1302 | priv->prof->inline_thold = val; | ||
| 1303 | break; | ||
| 1304 | default: | ||
| 1305 | ret = -EINVAL; | ||
| 1306 | break; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | return ret; | ||
| 1310 | } | ||
| 1311 | |||
| 1270 | 1312 | ||
| 1271 | const struct ethtool_ops mlx4_en_ethtool_ops = { | 1313 | const struct ethtool_ops mlx4_en_ethtool_ops = { |
| 1272 | .get_drvinfo = mlx4_en_get_drvinfo, | 1314 | .get_drvinfo = mlx4_en_get_drvinfo, |
| @@ -1297,6 +1339,8 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { | |||
| 1297 | .get_ts_info = mlx4_en_get_ts_info, | 1339 | .get_ts_info = mlx4_en_get_ts_info, |
| 1298 | .set_priv_flags = mlx4_en_set_priv_flags, | 1340 | .set_priv_flags = mlx4_en_set_priv_flags, |
| 1299 | .get_priv_flags = mlx4_en_get_priv_flags, | 1341 | .get_priv_flags = mlx4_en_get_priv_flags, |
| 1342 | .get_tunable = mlx4_en_get_tunable, | ||
| 1343 | .set_tunable = mlx4_en_set_tunable, | ||
| 1300 | }; | 1344 | }; |
| 1301 | 1345 | ||
| 1302 | 1346 | ||
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 0c501253fdab..92a7cf46d9af 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | #include <linux/mlx4/qp.h> | 37 | #include <linux/mlx4/qp.h> |
| 38 | #include <linux/skbuff.h> | 38 | #include <linux/skbuff.h> |
| 39 | #include <linux/if_vlan.h> | 39 | #include <linux/if_vlan.h> |
| 40 | #include <linux/prefetch.h> | ||
| 40 | #include <linux/vmalloc.h> | 41 | #include <linux/vmalloc.h> |
| 41 | #include <linux/tcp.h> | 42 | #include <linux/tcp.h> |
| 42 | #include <linux/ip.h> | 43 | #include <linux/ip.h> |
| @@ -65,10 +66,9 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, | |||
| 65 | ring->size = size; | 66 | ring->size = size; |
| 66 | ring->size_mask = size - 1; | 67 | ring->size_mask = size - 1; |
| 67 | ring->stride = stride; | 68 | ring->stride = stride; |
| 68 | ring->inline_thold = priv->prof->inline_thold; | ||
| 69 | 69 | ||
| 70 | tmp = size * sizeof(struct mlx4_en_tx_info); | 70 | tmp = size * sizeof(struct mlx4_en_tx_info); |
| 71 | ring->tx_info = vmalloc_node(tmp, node); | 71 | ring->tx_info = kmalloc_node(tmp, GFP_KERNEL | __GFP_NOWARN, node); |
| 72 | if (!ring->tx_info) { | 72 | if (!ring->tx_info) { |
| 73 | ring->tx_info = vmalloc(tmp); | 73 | ring->tx_info = vmalloc(tmp); |
| 74 | if (!ring->tx_info) { | 74 | if (!ring->tx_info) { |
| @@ -151,7 +151,7 @@ err_bounce: | |||
| 151 | kfree(ring->bounce_buf); | 151 | kfree(ring->bounce_buf); |
| 152 | ring->bounce_buf = NULL; | 152 | ring->bounce_buf = NULL; |
| 153 | err_info: | 153 | err_info: |
| 154 | vfree(ring->tx_info); | 154 | kvfree(ring->tx_info); |
| 155 | ring->tx_info = NULL; | 155 | ring->tx_info = NULL; |
| 156 | err_ring: | 156 | err_ring: |
| 157 | kfree(ring); | 157 | kfree(ring); |
| @@ -174,7 +174,7 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, | |||
| 174 | mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); | 174 | mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); |
| 175 | kfree(ring->bounce_buf); | 175 | kfree(ring->bounce_buf); |
| 176 | ring->bounce_buf = NULL; | 176 | ring->bounce_buf = NULL; |
| 177 | vfree(ring->tx_info); | 177 | kvfree(ring->tx_info); |
| 178 | ring->tx_info = NULL; | 178 | ring->tx_info = NULL; |
| 179 | kfree(ring); | 179 | kfree(ring); |
| 180 | *pring = NULL; | 180 | *pring = NULL; |
| @@ -191,12 +191,12 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, | |||
| 191 | ring->prod = 0; | 191 | ring->prod = 0; |
| 192 | ring->cons = 0xffffffff; | 192 | ring->cons = 0xffffffff; |
| 193 | ring->last_nr_txbb = 1; | 193 | ring->last_nr_txbb = 1; |
| 194 | ring->poll_cnt = 0; | ||
| 195 | memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info)); | 194 | memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info)); |
| 196 | memset(ring->buf, 0, ring->buf_size); | 195 | memset(ring->buf, 0, ring->buf_size); |
| 197 | 196 | ||
| 198 | ring->qp_state = MLX4_QP_STATE_RST; | 197 | ring->qp_state = MLX4_QP_STATE_RST; |
| 199 | ring->doorbell_qpn = ring->qp.qpn << 8; | 198 | ring->doorbell_qpn = cpu_to_be32(ring->qp.qpn << 8); |
| 199 | ring->mr_key = cpu_to_be32(mdev->mr.key); | ||
| 200 | 200 | ||
| 201 | mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn, | 201 | mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn, |
| 202 | ring->cqn, user_prio, &ring->context); | 202 | ring->cqn, user_prio, &ring->context); |
| @@ -259,38 +259,45 @@ static u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, | |||
| 259 | struct mlx4_en_tx_ring *ring, | 259 | struct mlx4_en_tx_ring *ring, |
| 260 | int index, u8 owner, u64 timestamp) | 260 | int index, u8 owner, u64 timestamp) |
| 261 | { | 261 | { |
| 262 | struct mlx4_en_dev *mdev = priv->mdev; | ||
| 263 | struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; | 262 | struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; |
| 264 | struct mlx4_en_tx_desc *tx_desc = ring->buf + index * TXBB_SIZE; | 263 | struct mlx4_en_tx_desc *tx_desc = ring->buf + index * TXBB_SIZE; |
| 265 | struct mlx4_wqe_data_seg *data = (void *) tx_desc + tx_info->data_offset; | 264 | struct mlx4_wqe_data_seg *data = (void *) tx_desc + tx_info->data_offset; |
| 266 | struct sk_buff *skb = tx_info->skb; | ||
| 267 | struct skb_frag_struct *frag; | ||
| 268 | void *end = ring->buf + ring->buf_size; | 265 | void *end = ring->buf + ring->buf_size; |
| 269 | int frags = skb_shinfo(skb)->nr_frags; | 266 | struct sk_buff *skb = tx_info->skb; |
| 267 | int nr_maps = tx_info->nr_maps; | ||
| 270 | int i; | 268 | int i; |
| 271 | struct skb_shared_hwtstamps hwts; | ||
| 272 | 269 | ||
| 273 | if (timestamp) { | 270 | /* We do not touch skb here, so prefetch skb->users location |
| 274 | mlx4_en_fill_hwtstamps(mdev, &hwts, timestamp); | 271 | * to speedup consume_skb() |
| 272 | */ | ||
| 273 | prefetchw(&skb->users); | ||
| 274 | |||
| 275 | if (unlikely(timestamp)) { | ||
| 276 | struct skb_shared_hwtstamps hwts; | ||
| 277 | |||
| 278 | mlx4_en_fill_hwtstamps(priv->mdev, &hwts, timestamp); | ||
| 275 | skb_ | ||
