diff mbox series

[net-next,v3,5/6] virtio_net: Add a lock for per queue RX coalesce

Message ID 20240412195309.737781-6-danielj@nvidia.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series Remove RTNL lock protection of CVQ | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 927 this patch: 930
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/build_clang fail Errors and warnings before: 937 this patch: 16
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 938 this patch: 941
netdev/checkpatch warning CHECK: spinlock_t definition without comment WARNING: line length of 101 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Dan Jurgens April 12, 2024, 7:53 p.m. UTC
Once the RTNL locking around the control buffer is removed there can be
contention on the per queue RX interrupt coalescing data. Use a spin
lock per queue.

Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
---
 drivers/net/virtio_net.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

Comments

Jakub Kicinski April 13, 2024, 2:21 a.m. UTC | #1
On Fri, 12 Apr 2024 14:53:08 -0500 Daniel Jurgens wrote:
> Once the RTNL locking around the control buffer is removed there can be
> contention on the per queue RX interrupt coalescing data. Use a spin
> lock per queue.

Does not compile on Clang.

> +			scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> +				err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> +								       vi->intr_coal_rx.max_usecs,
> +								       vi->intr_coal_rx.max_packets);
> +				if (err)
> +					return err;
> +			}

Do you really think this needs a scoped guard and 4th indentation level,
instead of just:

			..lock(..);
			err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
							       vi->intr_coal_rx.max_usecs,
							       vi->intr_coal_rx.max_packets);
			..unlock(..);
			if (err)
				return err;

> +		scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> +			vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
> +			vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames;
> +		}

:-|
Heng Qi April 15, 2024, 1:41 p.m. UTC | #2
在 2024/4/13 上午3:53, Daniel Jurgens 写道:
> Once the RTNL locking around the control buffer is removed there can be
> contention on the per queue RX interrupt coalescing data. Use a spin
> lock per queue.
>
> Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
> ---
>   drivers/net/virtio_net.c | 23 ++++++++++++++++-------
>   1 file changed, 16 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index b3aa4d2a15e9..8724caa7c2ed 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -190,6 +190,7 @@ struct receive_queue {
>   	u32 packets_in_napi;
>   
>   	struct virtnet_interrupt_coalesce intr_coal;
> +	spinlock_t intr_coal_lock;
>   
>   	/* Chain pages by the private ptr. */
>   	struct page *pages;
> @@ -3087,11 +3088,13 @@ static int virtnet_set_ringparam(struct net_device *dev,
>   				return err;
>   
>   			/* The reason is same as the transmit virtqueue reset */
> -			err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> -							       vi->intr_coal_rx.max_usecs,
> -							       vi->intr_coal_rx.max_packets);
> -			if (err)
> -				return err;
> +			scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> +				err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> +								       vi->intr_coal_rx.max_usecs,
> +								       vi->intr_coal_rx.max_packets);
> +				if (err)
> +					return err;
> +			}
>   		}
>   	}
>   
> @@ -3510,8 +3513,10 @@ static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi,
>   	vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs;
>   	vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames;
>   	for (i = 0; i < vi->max_queue_pairs; i++) {
> -		vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
> -		vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames;
> +		scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> +			vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
> +			vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames;
> +		}
>   	}
>   
>   	return 0;
> @@ -3542,6 +3547,7 @@ static int virtnet_send_rx_notf_coal_vq_cmds(struct virtnet_info *vi,
>   	u32 max_usecs, max_packets;
>   	int err;
>   
> +	guard(spinlock)(&vi->rq[queue].intr_coal_lock);
>   	max_usecs = vi->rq[queue].intr_coal.max_usecs;
>   	max_packets = vi->rq[queue].intr_coal.max_packets;
>   
> @@ -3606,6 +3612,7 @@ static void virtnet_rx_dim_work(struct work_struct *work)
>   	if (!rq->dim_enabled)
>   		goto out;

We should also protect rq->dim_enabled access, incorrect values may be 
read in
rx_dim_worker because it is modified in set_coalesce/set_per_queue_coalesce.

Thanks.

>   
> +	guard(spinlock)(&rq->intr_coal_lock);
>   	update_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
>   	if (update_moder.usec != rq->intr_coal.max_usecs ||
>   	    update_moder.pkts != rq->intr_coal.max_packets) {
> @@ -3756,6 +3763,7 @@ static int virtnet_get_per_queue_coalesce(struct net_device *dev,
>   		return -EINVAL;
>   
>   	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) {
> +		guard(spinlock)(&vi->rq[queue].intr_coal_lock);
>   		ec->rx_coalesce_usecs = vi->rq[queue].intr_coal.max_usecs;
>   		ec->tx_coalesce_usecs = vi->sq[queue].intr_coal.max_usecs;
>   		ec->tx_max_coalesced_frames = vi->sq[queue].intr_coal.max_packets;
> @@ -4501,6 +4509,7 @@ static int virtnet_alloc_queues(struct virtnet_info *vi)
>   
>   		u64_stats_init(&vi->rq[i].stats.syncp);
>   		u64_stats_init(&vi->sq[i].stats.syncp);
> +		spin_lock_init(&vi->rq[i].intr_coal_lock);
>   	}
>   
>   	return 0;
Dan Jurgens April 15, 2024, 8:24 p.m. UTC | #3
> From: Heng Qi <hengqi@linux.alibaba.com>
> Sent: Monday, April 15, 2024 8:42 AM
> To: Dan Jurgens <danielj@nvidia.com>; netdev@vger.kernel.org
> Cc: mst@redhat.com; jasowang@redhat.com; xuanzhuo@linux.alibaba.com;
> virtualization@lists.linux.dev; davem@davemloft.net;
> edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; Jiri Pirko
> <jiri@nvidia.com>
> Subject: Re: [PATCH net-next v3 5/6] virtio_net: Add a lock for per queue RX
> coalesce
> 
> 
> 
> 在 2024/4/13 上午3:53, Daniel Jurgens 写道:
> > Once the RTNL locking around the control buffer is removed there can
> > be contention on the per queue RX interrupt coalescing data. Use a
> > spin lock per queue.
> >
> > Signed-off-by: Daniel Jurgens <danielj@nvidia.com>
> > ---
> >   drivers/net/virtio_net.c | 23 ++++++++++++++++-------
> >   1 file changed, 16 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index
> > b3aa4d2a15e9..8724caa7c2ed 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -190,6 +190,7 @@ struct receive_queue {
> >   	u32 packets_in_napi;
> >
> >   	struct virtnet_interrupt_coalesce intr_coal;
> > +	spinlock_t intr_coal_lock;
> >
> >   	/* Chain pages by the private ptr. */
> >   	struct page *pages;
> > @@ -3087,11 +3088,13 @@ static int virtnet_set_ringparam(struct
> net_device *dev,
> >   				return err;
> >
> >   			/* The reason is same as the transmit virtqueue reset
> */
> > -			err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> > -							       vi-
> >intr_coal_rx.max_usecs,
> > -							       vi-
> >intr_coal_rx.max_packets);
> > -			if (err)
> > -				return err;
> > +			scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> > +				err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> > +								       vi-
> >intr_coal_rx.max_usecs,
> > +								       vi-
> >intr_coal_rx.max_packets);
> > +				if (err)
> > +					return err;
> > +			}
> >   		}
> >   	}
> >
> > @@ -3510,8 +3513,10 @@ static int
> virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi,
> >   	vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs;
> >   	vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames;
> >   	for (i = 0; i < vi->max_queue_pairs; i++) {
> > -		vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
> > -		vi->rq[i].intr_coal.max_packets = ec-
> >rx_max_coalesced_frames;
> > +		scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> > +			vi->rq[i].intr_coal.max_usecs = ec-
> >rx_coalesce_usecs;
> > +			vi->rq[i].intr_coal.max_packets = ec-
> >rx_max_coalesced_frames;
> > +		}
> >   	}
> >
> >   	return 0;
> > @@ -3542,6 +3547,7 @@ static int
> virtnet_send_rx_notf_coal_vq_cmds(struct virtnet_info *vi,
> >   	u32 max_usecs, max_packets;
> >   	int err;
> >
> > +	guard(spinlock)(&vi->rq[queue].intr_coal_lock);
> >   	max_usecs = vi->rq[queue].intr_coal.max_usecs;
> >   	max_packets = vi->rq[queue].intr_coal.max_packets;
> >
> > @@ -3606,6 +3612,7 @@ static void virtnet_rx_dim_work(struct
> work_struct *work)
> >   	if (!rq->dim_enabled)
> >   		goto out;
> 
> We should also protect rq->dim_enabled access, incorrect values may be
> read in rx_dim_worker because it is modified in
> set_coalesce/set_per_queue_coalesce.

Good point. Thanks

> 
> Thanks.
> 
> >
> > +	guard(spinlock)(&rq->intr_coal_lock);
> >   	update_moder = net_dim_get_rx_moderation(dim->mode, dim-
> >profile_ix);
> >   	if (update_moder.usec != rq->intr_coal.max_usecs ||
> >   	    update_moder.pkts != rq->intr_coal.max_packets) { @@ -3756,6
> > +3763,7 @@ static int virtnet_get_per_queue_coalesce(struct net_device
> *dev,
> >   		return -EINVAL;
> >
> >   	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) {
> > +		guard(spinlock)(&vi->rq[queue].intr_coal_lock);
> >   		ec->rx_coalesce_usecs = vi->rq[queue].intr_coal.max_usecs;
> >   		ec->tx_coalesce_usecs = vi-
> >sq[queue].intr_coal.max_usecs;
> >   		ec->tx_max_coalesced_frames = vi-
> >sq[queue].intr_coal.max_packets;
> > @@ -4501,6 +4509,7 @@ static int virtnet_alloc_queues(struct
> > virtnet_info *vi)
> >
> >   		u64_stats_init(&vi->rq[i].stats.syncp);
> >   		u64_stats_init(&vi->sq[i].stats.syncp);
> > +		spin_lock_init(&vi->rq[i].intr_coal_lock);
> >   	}
> >
> >   	return 0;
Dan Jurgens April 16, 2024, 3:15 a.m. UTC | #4
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Friday, April 12, 2024 9:21 PM
> To: Dan Jurgens <danielj@nvidia.com>
> Cc: netdev@vger.kernel.org; mst@redhat.com; jasowang@redhat.com;
> xuanzhuo@linux.alibaba.com; virtualization@lists.linux.dev;
> davem@davemloft.net; edumazet@google.com; pabeni@redhat.com; Jiri
> Pirko <jiri@nvidia.com>
> Subject: Re: [PATCH net-next v3 5/6] virtio_net: Add a lock for per queue RX
> coalesce
> 
> On Fri, 12 Apr 2024 14:53:08 -0500 Daniel Jurgens wrote:
> > Once the RTNL locking around the control buffer is removed there can
> > be contention on the per queue RX interrupt coalescing data. Use a
> > spin lock per queue.
> 
> Does not compile on Clang.

Which version? It compiles for me with:
$ clang -v
clang version 15.0.7 (Fedora 15.0.7-2.fc37)

> 
> > +			scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> > +				err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> > +								       vi-
> >intr_coal_rx.max_usecs,
> > +								       vi-
> >intr_coal_rx.max_packets);
> > +				if (err)
> > +					return err;
> > +			}
> 
> Do you really think this needs a scoped guard and 4th indentation level,
> instead of just:
> 
> 			..lock(..);
> 			err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
> 							       vi-
> >intr_coal_rx.max_usecs,
> 							       vi-
> >intr_coal_rx.max_packets);
> 			..unlock(..);

I'll change it in the next version.

> 			if (err)
> 				return err;
> 
> > +		scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
> > +			vi->rq[i].intr_coal.max_usecs = ec-
> >rx_coalesce_usecs;
> > +			vi->rq[i].intr_coal.max_packets = ec-
> >rx_max_coalesced_frames;
> > +		}
> 
> :-|
> --
> pw-bot: cr
Jakub Kicinski April 16, 2024, 2:27 p.m. UTC | #5
On Tue, 16 Apr 2024 03:15:34 +0000 Dan Jurgens wrote:
> Which version? It compiles for me with:
> $ clang -v
> clang version 15.0.7 (Fedora 15.0.7-2.fc37)

clang version 17.0.6 (Fedora 17.0.6-2.fc39)

allmodconfig

The combination of UNIQUE() goto and guard seems to make it unhappy:

../drivers/net/virtio_net.c:3613:3: error: cannot jump from this goto
statement to its label 3613 |                 goto out; |                 ^
../drivers/net/virtio_net.c:3615:2: note: jump bypasses initialization of variable with __attribute__((cleanup))
 3615 |         guard(spinlock)(&rq->intr_coal_lock);
      |         ^
../include/linux/cleanup.h:164:15: note: expanded from macro 'guard'
  164 |         CLASS(_name, __UNIQUE_ID(guard))
      |                      ^
../include/linux/compiler.h:189:29: note: expanded from macro '__UNIQUE_ID'
  189 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
      |                             ^
./../include/linux/compiler_types.h:84:22: note: expanded from macro '__PASTE'
   84 | #define __PASTE(a,b) ___PASTE(a,b)
      |                      ^
./../include/linux/compiler_types.h:83:23: note: expanded from macro '___PASTE'
   83 | #define ___PASTE(a,b) a##b
      |                       ^
<scratch space>:18:1: note: expanded from here
   18 | __UNIQUE_ID_guard2044
      | ^
1 error generated.
Dan Jurgens April 16, 2024, 7:24 p.m. UTC | #6
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Tuesday, April 16, 2024 9:27 AM
> To: Dan Jurgens <danielj@nvidia.com>
> On Tue, 16 Apr 2024 03:15:34 +0000 Dan Jurgens wrote:
> > Which version? It compiles for me with:
> > $ clang -v
> > clang version 15.0.7 (Fedora 15.0.7-2.fc37)
> 
> clang version 17.0.6 (Fedora 17.0.6-2.fc39)
> 

Thanks, I was able to see this with the newer version. The changes to address Heng's comment resolves it as well.

> allmodconfig
> 
> The combination of UNIQUE() goto and guard seems to make it unhappy:
> 
> ../drivers/net/virtio_net.c:3613:3: error: cannot jump from this goto
> statement to its label 3613 |                 goto out; |                 ^
> ../drivers/net/virtio_net.c:3615:2: note: jump bypasses initialization of
> variable with __attribute__((cleanup))
>  3615 |         guard(spinlock)(&rq->intr_coal_lock);
>       |         ^
> ../include/linux/cleanup.h:164:15: note: expanded from macro 'guard'
>   164 |         CLASS(_name, __UNIQUE_ID(guard))
>       |                      ^
> ../include/linux/compiler.h:189:29: note: expanded from macro
> '__UNIQUE_ID'
>   189 | #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_,
> prefix), __COUNTER__)
>       |                             ^
> ./../include/linux/compiler_types.h:84:22: note: expanded from macro
> '__PASTE'
>    84 | #define __PASTE(a,b) ___PASTE(a,b)
>       |                      ^
> ./../include/linux/compiler_types.h:83:23: note: expanded from macro
> '___PASTE'
>    83 | #define ___PASTE(a,b) a##b
>       |                       ^
> <scratch space>:18:1: note: expanded from here
>    18 | __UNIQUE_ID_guard2044
>       | ^
> 1 error generated.
diff mbox series

Patch

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index b3aa4d2a15e9..8724caa7c2ed 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -190,6 +190,7 @@  struct receive_queue {
 	u32 packets_in_napi;
 
 	struct virtnet_interrupt_coalesce intr_coal;
+	spinlock_t intr_coal_lock;
 
 	/* Chain pages by the private ptr. */
 	struct page *pages;
@@ -3087,11 +3088,13 @@  static int virtnet_set_ringparam(struct net_device *dev,
 				return err;
 
 			/* The reason is same as the transmit virtqueue reset */
-			err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
-							       vi->intr_coal_rx.max_usecs,
-							       vi->intr_coal_rx.max_packets);
-			if (err)
-				return err;
+			scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
+				err = virtnet_send_rx_ctrl_coal_vq_cmd(vi, i,
+								       vi->intr_coal_rx.max_usecs,
+								       vi->intr_coal_rx.max_packets);
+				if (err)
+					return err;
+			}
 		}
 	}
 
@@ -3510,8 +3513,10 @@  static int virtnet_send_rx_notf_coal_cmds(struct virtnet_info *vi,
 	vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs;
 	vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames;
 	for (i = 0; i < vi->max_queue_pairs; i++) {
-		vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
-		vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames;
+		scoped_guard(spinlock, &vi->rq[i].intr_coal_lock) {
+			vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs;
+			vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames;
+		}
 	}
 
 	return 0;
@@ -3542,6 +3547,7 @@  static int virtnet_send_rx_notf_coal_vq_cmds(struct virtnet_info *vi,
 	u32 max_usecs, max_packets;
 	int err;
 
+	guard(spinlock)(&vi->rq[queue].intr_coal_lock);
 	max_usecs = vi->rq[queue].intr_coal.max_usecs;
 	max_packets = vi->rq[queue].intr_coal.max_packets;
 
@@ -3606,6 +3612,7 @@  static void virtnet_rx_dim_work(struct work_struct *work)
 	if (!rq->dim_enabled)
 		goto out;
 
+	guard(spinlock)(&rq->intr_coal_lock);
 	update_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
 	if (update_moder.usec != rq->intr_coal.max_usecs ||
 	    update_moder.pkts != rq->intr_coal.max_packets) {
@@ -3756,6 +3763,7 @@  static int virtnet_get_per_queue_coalesce(struct net_device *dev,
 		return -EINVAL;
 
 	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) {
+		guard(spinlock)(&vi->rq[queue].intr_coal_lock);
 		ec->rx_coalesce_usecs = vi->rq[queue].intr_coal.max_usecs;
 		ec->tx_coalesce_usecs = vi->sq[queue].intr_coal.max_usecs;
 		ec->tx_max_coalesced_frames = vi->sq[queue].intr_coal.max_packets;
@@ -4501,6 +4509,7 @@  static int virtnet_alloc_queues(struct virtnet_info *vi)
 
 		u64_stats_init(&vi->rq[i].stats.syncp);
 		u64_stats_init(&vi->sq[i].stats.syncp);
+		spin_lock_init(&vi->rq[i].intr_coal_lock);
 	}
 
 	return 0;