diff options
author | Amir Vadai <amirv@mellanox.com> | 2013-06-18 09:18:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-06-19 21:32:16 -0400 |
commit | 9e77a2b837bbd7197da966f0915e8f1ddb2ca850 (patch) | |
tree | faf28979465bc6909513170b41a3cbe5f811ea45 | |
parent | dc3d807d6fd983603c82e7bcdbaa49cdb4239691 (diff) |
net/mlx4_en: Add Low Latency Socket (LLS) support
Add basic support for LLS.
Signed-off-by: Amir Vadai <amirv@mellanox.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_cq.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 43 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/en_rx.c | 15 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 121 |
4 files changed, 178 insertions, 4 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 1e6c594d6d04..3e2d5047cdb3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c | |||
@@ -139,6 +139,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, | |||
139 | 139 | ||
140 | if (!cq->is_tx) { | 140 | if (!cq->is_tx) { |
141 | netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); | 141 | netif_napi_add(cq->dev, &cq->napi, mlx4_en_poll_rx_cq, 64); |
142 | napi_hash_add(&cq->napi); | ||
142 | napi_enable(&cq->napi); | 143 | napi_enable(&cq->napi); |
143 | } | 144 | } |
144 | 145 | ||
@@ -162,6 +163,8 @@ void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) | |||
162 | { | 163 | { |
163 | if (!cq->is_tx) { | 164 | if (!cq->is_tx) { |
164 | napi_disable(&cq->napi); | 165 | napi_disable(&cq->napi); |
166 | napi_hash_del(&cq->napi); | ||
167 | synchronize_rcu(); | ||
165 | netif_napi_del(&cq->napi); | 168 | netif_napi_del(&cq->napi); |
166 | } | 169 | } |
167 | 170 | ||
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index ade276cca0e6..ab9ec91d1f70 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <linux/hash.h> | 39 | #include <linux/hash.h> |
40 | #include <net/ip.h> | 40 | #include <net/ip.h> |
41 | #include <net/ll_poll.h> | ||
41 | 42 | ||
42 | #include <linux/mlx4/driver.h> | 43 | #include <linux/mlx4/driver.h> |
43 | #include <linux/mlx4/device.h> | 44 | #include <linux/mlx4/device.h> |
@@ -67,6 +68,30 @@ int mlx4_en_setup_tc(struct net_device *dev, u8 up) | |||
67 | return 0; | 68 | return 0; |
68 | } | 69 | } |
69 | 70 | ||
71 | #ifdef CONFIG_NET_LL_RX_POLL | ||
72 | /* must be called with local_bh_disable()d */ | ||
73 | static int mlx4_en_low_latency_recv(struct napi_struct *napi) | ||
74 | { | ||
75 | struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); | ||
76 | struct net_device *dev = cq->dev; | ||
77 | struct mlx4_en_priv *priv = netdev_priv(dev); | ||
78 | struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; | ||
79 | int done; | ||
80 | |||
81 | if (!priv->port_up) | ||
82 | return LL_FLUSH_FAILED; | ||
83 | |||
84 | if (!mlx4_en_cq_lock_poll(cq)) | ||
85 | return LL_FLUSH_BUSY; | ||
86 | |||
87 | done = mlx4_en_process_rx_cq(dev, cq, 4); | ||
88 | |||
89 | mlx4_en_cq_unlock_poll(cq); | ||
90 | |||
91 | return done; | ||
92 | } | ||
93 | #endif /* CONFIG_NET_LL_RX_POLL */ | ||
94 | |||
70 | #ifdef CONFIG_RFS_ACCEL | 95 | #ifdef CONFIG_RFS_ACCEL |
71 | 96 | ||
72 | struct mlx4_en_filter { | 97 | struct mlx4_en_filter { |
@@ -1445,6 +1470,8 @@ int mlx4_en_start_port(struct net_device *dev) | |||
1445 | for (i = 0; i < priv->rx_ring_num; i++) { | 1470 | for (i = 0; i < priv->rx_ring_num; i++) { |
1446 | cq = &priv->rx_cq[i]; | 1471 | cq = &priv->rx_cq[i]; |
1447 | 1472 | ||
1473 | mlx4_en_cq_init_lock(cq); | ||
1474 | |||
1448 | err = mlx4_en_activate_cq(priv, cq, i); | 1475 | err = mlx4_en_activate_cq(priv, cq, i); |
1449 | if (err) { | 1476 | if (err) { |
1450 | en_err(priv, "Failed activating Rx CQ\n"); | 1477 | en_err(priv, "Failed activating Rx CQ\n"); |
@@ -1694,10 +1721,19 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) | |||
1694 | 1721 | ||
1695 | /* Free RX Rings */ | 1722 | /* Free RX Rings */ |
1696 | for (i = 0; i < priv->rx_ring_num; i++) { | 1723 | for (i = 0; i < priv->rx_ring_num; i++) { |
1724 | struct mlx4_en_cq *cq = &priv->rx_cq[i]; | ||
1725 | |||
1726 | local_bh_disable(); | ||
1727 | while (!mlx4_en_cq_lock_napi(cq)) { | ||
1728 | pr_info("CQ %d locked\n", i); | ||
1729 | mdelay(1); | ||
1730 | } | ||
1731 | local_bh_enable(); | ||
1732 | |||
1697 | mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); | 1733 | mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); |
1698 | while (test_bit(NAPI_STATE_SCHED, &priv->rx_cq[i].napi.state)) | 1734 | while (test_bit(NAPI_STATE_SCHED, &cq->napi.state)) |
1699 | msleep(1); | 1735 | msleep(1); |
1700 | mlx4_en_deactivate_cq(priv, &priv->rx_cq[i]); | 1736 | mlx4_en_deactivate_cq(priv, cq); |
1701 | } | 1737 | } |
1702 | 1738 | ||
1703 | /* close port*/ | 1739 | /* close port*/ |
@@ -2090,6 +2126,9 @@ static const struct net_device_ops mlx4_netdev_ops = { | |||
2090 | #ifdef CONFIG_RFS_ACCEL | 2126 | #ifdef CONFIG_RFS_ACCEL |
2091 | .ndo_rx_flow_steer = mlx4_en_filter_rfs, | 2127 | .ndo_rx_flow_steer = mlx4_en_filter_rfs, |
2092 | #endif | 2128 | #endif |
2129 | #ifdef CONFIG_NET_LL_RX_POLL | ||
2130 | .ndo_ll_poll = mlx4_en_low_latency_recv, | ||
2131 | #endif | ||
2093 | }; | 2132 | }; |
2094 | 2133 | ||
2095 | static const struct net_device_ops mlx4_netdev_ops_master = { | 2134 | static const struct net_device_ops mlx4_netdev_ops_master = { |
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 02aee1ebd203..9c57581b021c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c | |||
@@ -31,6 +31,7 @@ | |||
31 | * | 31 | * |
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <net/ll_poll.h> | ||
34 | #include <linux/mlx4/cq.h> | 35 | #include <linux/mlx4/cq.h> |
35 | #include <linux/slab.h> | 36 | #include <linux/slab.h> |
36 | #include <linux/mlx4/qp.h> | 37 | #include <linux/mlx4/qp.h> |
@@ -656,8 +657,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud | |||
656 | * - DIX Ethernet (type interpretation) | 657 | * - DIX Ethernet (type interpretation) |
657 | * - TCP/IP (v4) | 658 | * - TCP/IP (v4) |
658 | * - without IP options | 659 | * - without IP options |
659 | * - not an IP fragment */ | 660 | * - not an IP fragment |
660 | if (dev->features & NETIF_F_GRO) { | 661 | * - no LLS polling in progress |
662 | */ | ||
663 | if (!mlx4_en_cq_ll_polling(cq) && | ||
664 | (dev->features & NETIF_F_GRO)) { | ||
661 | struct sk_buff *gro_skb = napi_get_frags(&cq->napi); | 665 | struct sk_buff *gro_skb = napi_get_frags(&cq->napi); |
662 | if (!gro_skb) | 666 | if (!gro_skb) |
663 | goto next; | 667 | goto next; |
@@ -737,6 +741,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud | |||
737 | timestamp); | 741 | timestamp); |
738 | } | 742 | } |
739 | 743 | ||
744 | skb_mark_ll(skb, &cq->napi); | ||
745 | |||
740 | /* Push it up the stack */ | 746 | /* Push it up the stack */ |
741 | netif_receive_skb(skb); | 747 | netif_receive_skb(skb); |
742 | 748 | ||
@@ -781,8 +787,13 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) | |||
781 | struct mlx4_en_priv *priv = netdev_priv(dev); | 787 | struct mlx4_en_priv *priv = netdev_priv(dev); |
782 | int done; | 788 | int done; |
783 | 789 | ||
790 | if (!mlx4_en_cq_lock_napi(cq)) | ||
791 | return budget; | ||
792 | |||
784 | done = mlx4_en_process_rx_cq(dev, cq, budget); | 793 | done = mlx4_en_process_rx_cq(dev, cq, budget); |
785 | 794 | ||
795 | mlx4_en_cq_unlock_napi(cq); | ||
796 | |||
786 | /* If we used up all the quota - we're probably not done yet... */ | 797 | /* If we used up all the quota - we're probably not done yet... */ |
787 | if (done == budget) | 798 | if (done == budget) |
788 | INC_PERF_COUNTER(priv->pstats.napi_quota); | 799 | INC_PERF_COUNTER(priv->pstats.napi_quota); |
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index b1f51c1f635c..11c862e4e69d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | |||
@@ -310,6 +310,19 @@ struct mlx4_en_cq { | |||
310 | u16 moder_cnt; | 310 | u16 moder_cnt; |
311 | struct mlx4_cqe *buf; | 311 | struct mlx4_cqe *buf; |
312 | #define MLX4_EN_OPCODE_ERROR 0x1e | 312 | #define MLX4_EN_OPCODE_ERROR 0x1e |
313 | |||
314 | #ifdef CONFIG_NET_LL_RX_POLL | ||
315 | unsigned int state; | ||
316 | #define MLX4_EN_CQ_STATE_IDLE 0 | ||
317 | #define MLX4_EN_CQ_STATE_NAPI 1 /* NAPI owns this CQ */ | ||
318 | #define MLX4_EN_CQ_STATE_POLL 2 /* poll owns this CQ */ | ||
319 | #define MLX4_CQ_LOCKED (MLX4_EN_CQ_STATE_NAPI | MLX4_EN_CQ_STATE_POLL) | ||
320 | #define MLX4_EN_CQ_STATE_NAPI_YIELD 4 /* NAPI yielded this CQ */ | ||
321 | #define MLX4_EN_CQ_STATE_POLL_YIELD 8 /* poll yielded this CQ */ | ||
322 | #define CQ_YIELD (MLX4_EN_CQ_STATE_NAPI_YIELD | MLX4_EN_CQ_STATE_POLL_YIELD) | ||
323 | #define CQ_USER_PEND (MLX4_EN_CQ_STATE_POLL | MLX4_EN_CQ_STATE_POLL_YIELD) | ||
324 | spinlock_t poll_lock; /* protects from LLS/napi conflicts */ | ||
325 | #endif /* CONFIG_NET_LL_RX_POLL */ | ||
313 | }; | 326 | }; |
314 | 327 | ||
315 | struct mlx4_en_port_profile { | 328 | struct mlx4_en_port_profile { |
@@ -562,6 +575,114 @@ struct mlx4_mac_entry { | |||
562 | struct rcu_head rcu; | 575 | struct rcu_head rcu; |
563 | }; | 576 | }; |
564 | 577 | ||
578 | #ifdef CONFIG_NET_LL_RX_POLL | ||
579 | static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) | ||
580 | { | ||
581 | spin_lock_init(&cq->poll_lock); | ||
582 | cq->state = MLX4_EN_CQ_STATE_IDLE; | ||
583 | } | ||
584 | |||
585 | /* called from the device poll rutine to get ownership of a cq */ | ||
586 | static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) | ||
587 | { | ||
588 | int rc = true; | ||
589 | spin_lock(&cq->poll_lock); | ||
590 | if (cq->state & MLX4_CQ_LOCKED) { | ||
591 | WARN_ON(cq->state & MLX4_EN_CQ_STATE_NAPI); | ||
592 | cq->state |= MLX4_EN_CQ_STATE_NAPI_YIELD; | ||
593 | rc = false; | ||
594 | } else | ||
595 | /* we don't care if someone yielded */ | ||
596 | cq->state = MLX4_EN_CQ_STATE_NAPI; | ||
597 | spin_unlock(&cq->poll_lock); | ||
598 | return rc; | ||
599 | } | ||
600 | |||
601 | /* returns true is someone tried to get the cq while napi had it */ | ||
602 | static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) | ||
603 | { | ||
604 | int rc = false; | ||
605 | spin_lock(&cq->poll_lock); | ||
606 | WARN_ON(cq->state & (MLX4_EN_CQ_STATE_POLL | | ||
607 | MLX4_EN_CQ_STATE_NAPI_YIELD)); | ||
608 | |||
609 | if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) | ||
610 | rc = true; | ||
611 | cq->state = MLX4_EN_CQ_STATE_IDLE; | ||
612 | spin_unlock(&cq->poll_lock); | ||
613 | return rc; | ||
614 | } | ||
615 | |||
616 | /* called from mlx4_en_low_latency_poll() */ | ||
617 | static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) | ||
618 | { | ||
619 | int rc = true; | ||
620 | spin_lock_bh(&cq->poll_lock); | ||
621 | if ((cq->state & MLX4_CQ_LOCKED)) { | ||
622 | struct net_device *dev = cq->dev; | ||
623 | struct mlx4_en_priv *priv = netdev_priv(dev); | ||
624 | struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; | ||
625 | |||
626 | cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD; | ||
627 | rc = false; | ||
628 | } else | ||
629 | /* preserve yield marks */ | ||
630 | cq->state |= MLX4_EN_CQ_STATE_POLL; | ||
631 | spin_unlock_bh(&cq->poll_lock); | ||
632 | return rc; | ||
633 | } | ||
634 | |||
635 | /* returns true if someone tried to get the cq while it was locked */ | ||
636 | static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) | ||
637 | { | ||
638 | int rc = false; | ||
639 | spin_lock_bh(&cq->poll_lock); | ||
640 | WARN_ON(cq->state & (MLX4_EN_CQ_STATE_NAPI)); | ||
641 | |||
642 | if (cq->state & MLX4_EN_CQ_STATE_POLL_YIELD) | ||
643 | rc = true; | ||
644 | cq->state = MLX4_EN_CQ_STATE_IDLE; | ||
645 | spin_unlock_bh(&cq->poll_lock); | ||
646 | return rc; | ||
647 | } | ||
648 | |||
649 | /* true if a socket is polling, even if it did not get the lock */ | ||
650 | static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq) | ||
651 | { | ||
652 | WARN_ON(!(cq->state & MLX4_CQ_LOCKED)); | ||
653 | return cq->state & CQ_USER_PEND; | ||
654 | } | ||
655 | #else | ||
656 | static inline void mlx4_en_cq_init_lock(struct mlx4_en_cq *cq) | ||
657 | { | ||
658 | } | ||
659 | |||
660 | static inline bool mlx4_en_cq_lock_napi(struct mlx4_en_cq *cq) | ||
661 | { | ||
662 | return true; | ||
663 | } | ||
664 | |||
665 | static inline bool mlx4_en_cq_unlock_napi(struct mlx4_en_cq *cq) | ||
666 | { | ||
667 | return false; | ||
668 | } | ||
669 | |||
670 | static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) | ||
671 | { | ||
672 | return false; | ||
673 | } | ||
674 | |||
675 | static inline bool mlx4_en_cq_unlock_poll(struct mlx4_en_cq *cq) | ||
676 | { | ||
677 | return false; | ||
678 | } | ||
679 | |||
680 | static inline bool mlx4_en_cq_ll_polling(struct mlx4_en_cq *cq) | ||
681 | { | ||
682 | return false; | ||
683 | } | ||
684 | #endif /* CONFIG_NET_LL_RX_POLL */ | ||
685 | |||
565 | #define MLX4_EN_WOL_DO_MODIFY (1ULL << 63) | 686 | #define MLX4_EN_WOL_DO_MODIFY (1ULL << 63) |
566 | 687 | ||
567 | void mlx4_en_update_loopback_state(struct net_device *dev, | 688 | void mlx4_en_update_loopback_state(struct net_device *dev, |