diff mbox series

[net-next,v4,1/2] virtio-net: convert rx mode setting to use workqueue

Message ID 20230720083839.481487-2-jasowang@redhat.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series virtio-net: don't busy poll for cvq command | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
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 success Errors and warnings before: 1342 this patch: 1342
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 1365 this patch: 1365
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 success Errors and warnings before: 1365 this patch: 1365
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 138 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Jason Wang July 20, 2023, 8:38 a.m. UTC
This patch convert rx mode setting to be done in a workqueue, this is
a must for allow to sleep when waiting for the cvq command to
response since current code is executed under addr spin lock.

Note that we need to disable and flush the workqueue during freeze,
this means the rx mode setting is lost after resuming. This is not the
bug of this patch as we never try to restore rx mode setting during
resume.

Signed-off-by: Jason Wang <jasowang@redhat.com>
---
 drivers/net/virtio_net.c | 55 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 52 insertions(+), 3 deletions(-)

Comments

Nelson, Shannon July 20, 2023, 8:25 p.m. UTC | #1
On 7/20/23 1:38 AM, Jason Wang wrote:
> 
> This patch convert rx mode setting to be done in a workqueue, this is
> a must for allow to sleep when waiting for the cvq command to
> response since current code is executed under addr spin lock.
> 
> Note that we need to disable and flush the workqueue during freeze,
> this means the rx mode setting is lost after resuming. This is not the
> bug of this patch as we never try to restore rx mode setting during
> resume.
> 
> Signed-off-by: Jason Wang <jasowang@redhat.com>

Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>

> ---
>   drivers/net/virtio_net.c | 55 +++++++++++++++++++++++++++++++++++++---
>   1 file changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index d67b36fdba0d..9f3b1d6ac33d 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -274,6 +274,12 @@ struct virtnet_info {
>          /* Work struct for config space updates */
>          struct work_struct config_work;
> 
> +       /* Work struct for setting rx mode */
> +       struct work_struct rx_mode_work;
> +
> +       /* OK to queue work setting RX mode? */
> +       bool rx_mode_work_enabled;
> +
>          /* Does the affinity hint is set for virtqueues? */
>          bool affinity_hint_set;
> 
> @@ -397,6 +403,20 @@ static void disable_delayed_refill(struct virtnet_info *vi)
>          spin_unlock_bh(&vi->refill_lock);
>   }
> 
> +static void enable_rx_mode_work(struct virtnet_info *vi)
> +{
> +       rtnl_lock();
> +       vi->rx_mode_work_enabled = true;
> +       rtnl_unlock();
> +}
> +
> +static void disable_rx_mode_work(struct virtnet_info *vi)
> +{
> +       rtnl_lock();
> +       vi->rx_mode_work_enabled = false;
> +       rtnl_unlock();
> +}
> +
>   static void virtqueue_napi_schedule(struct napi_struct *napi,
>                                      struct virtqueue *vq)
>   {
> @@ -2448,9 +2468,11 @@ static int virtnet_close(struct net_device *dev)
>          return 0;
>   }
> 
> -static void virtnet_set_rx_mode(struct net_device *dev)
> +static void virtnet_rx_mode_work(struct work_struct *work)
>   {
> -       struct virtnet_info *vi = netdev_priv(dev);
> +       struct virtnet_info *vi =
> +               container_of(work, struct virtnet_info, rx_mode_work);
> +       struct net_device *dev = vi->dev;
>          struct scatterlist sg[2];
>          struct virtio_net_ctrl_mac *mac_data;
>          struct netdev_hw_addr *ha;
> @@ -2463,6 +2485,8 @@ static void virtnet_set_rx_mode(struct net_device *dev)
>          if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX))
>                  return;
> 
> +       rtnl_lock();
> +
>          vi->ctrl->promisc = ((dev->flags & IFF_PROMISC) != 0);
>          vi->ctrl->allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
> 
> @@ -2480,14 +2504,19 @@ static void virtnet_set_rx_mode(struct net_device *dev)
>                  dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n",
>                           vi->ctrl->allmulti ? "en" : "dis");
> 
> +       netif_addr_lock_bh(dev);
> +
>          uc_count = netdev_uc_count(dev);
>          mc_count = netdev_mc_count(dev);
>          /* MAC filter - use one buffer for both lists */
>          buf = kzalloc(((uc_count + mc_count) * ETH_ALEN) +
>                        (2 * sizeof(mac_data->entries)), GFP_ATOMIC);
>          mac_data = buf;
> -       if (!buf)
> +       if (!buf) {
> +               netif_addr_unlock_bh(dev);
> +               rtnl_unlock();
>                  return;
> +       }
> 
>          sg_init_table(sg, 2);
> 
> @@ -2508,6 +2537,8 @@ static void virtnet_set_rx_mode(struct net_device *dev)
>          netdev_for_each_mc_addr(ha, dev)
>                  memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN);
> 
> +       netif_addr_unlock_bh(dev);
> +
>          sg_set_buf(&sg[1], mac_data,
>                     sizeof(mac_data->entries) + (mc_count * ETH_ALEN));
> 
> @@ -2515,9 +2546,19 @@ static void virtnet_set_rx_mode(struct net_device *dev)
>                                    VIRTIO_NET_CTRL_MAC_TABLE_SET, sg))
>                  dev_warn(&dev->dev, "Failed to set MAC filter table.\n");
> 
> +       rtnl_unlock();
> +
>          kfree(buf);
>   }
> 
> +static void virtnet_set_rx_mode(struct net_device *dev)
> +{
> +       struct virtnet_info *vi = netdev_priv(dev);
> +
> +       if (vi->rx_mode_work_enabled)
> +               schedule_work(&vi->rx_mode_work);
> +}
> +
>   static int virtnet_vlan_rx_add_vid(struct net_device *dev,
>                                     __be16 proto, u16 vid)
>   {
> @@ -3286,6 +3327,8 @@ static void virtnet_freeze_down(struct virtio_device *vdev)
> 
>          /* Make sure no work handler is accessing the device */
>          flush_work(&vi->config_work);
> +       disable_rx_mode_work(vi);
> +       flush_work(&vi->rx_mode_work);
> 
>          netif_tx_lock_bh(vi->dev);
>          netif_device_detach(vi->dev);
> @@ -3308,6 +3351,7 @@ static int virtnet_restore_up(struct virtio_device *vdev)
>          virtio_device_ready(vdev);
> 
>          enable_delayed_refill(vi);
> +       enable_rx_mode_work(vi);
> 
>          if (netif_running(vi->dev)) {
>                  err = virtnet_open(vi->dev);
> @@ -4121,6 +4165,7 @@ static int virtnet_probe(struct virtio_device *vdev)
>          vdev->priv = vi;
> 
>          INIT_WORK(&vi->config_work, virtnet_config_changed_work);
> +       INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work);
>          spin_lock_init(&vi->refill_lock);
> 
>          if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) {
> @@ -4229,6 +4274,8 @@ static int virtnet_probe(struct virtio_device *vdev)
>          if (vi->has_rss || vi->has_rss_hash_report)
>                  virtnet_init_default_rss(vi);
> 
> +       enable_rx_mode_work(vi);
> +
>          /* serialize netdev register + virtio_device_ready() with ndo_open() */
>          rtnl_lock();
> 
> @@ -4326,6 +4373,8 @@ static void virtnet_remove(struct virtio_device *vdev)
> 
>          /* Make sure no work handler is accessing the device. */
>          flush_work(&vi->config_work);
> +       disable_rx_mode_work(vi);
> +       flush_work(&vi->rx_mode_work);
> 
>          unregister_netdev(vi->dev);
> 
> --
> 2.39.3
> 
> _______________________________________________
> Virtualization mailing list
> Virtualization@lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/virtualization
diff mbox series

Patch

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index d67b36fdba0d..9f3b1d6ac33d 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -274,6 +274,12 @@  struct virtnet_info {
 	/* Work struct for config space updates */
 	struct work_struct config_work;
 
+	/* Work struct for setting rx mode */
+	struct work_struct rx_mode_work;
+
+	/* OK to queue work setting RX mode? */
+	bool rx_mode_work_enabled;
+
 	/* Does the affinity hint is set for virtqueues? */
 	bool affinity_hint_set;
 
@@ -397,6 +403,20 @@  static void disable_delayed_refill(struct virtnet_info *vi)
 	spin_unlock_bh(&vi->refill_lock);
 }
 
+static void enable_rx_mode_work(struct virtnet_info *vi)
+{
+	rtnl_lock();
+	vi->rx_mode_work_enabled = true;
+	rtnl_unlock();
+}
+
+static void disable_rx_mode_work(struct virtnet_info *vi)
+{
+	rtnl_lock();
+	vi->rx_mode_work_enabled = false;
+	rtnl_unlock();
+}
+
 static void virtqueue_napi_schedule(struct napi_struct *napi,
 				    struct virtqueue *vq)
 {
@@ -2448,9 +2468,11 @@  static int virtnet_close(struct net_device *dev)
 	return 0;
 }
 
-static void virtnet_set_rx_mode(struct net_device *dev)
+static void virtnet_rx_mode_work(struct work_struct *work)
 {
-	struct virtnet_info *vi = netdev_priv(dev);
+	struct virtnet_info *vi =
+		container_of(work, struct virtnet_info, rx_mode_work);
+	struct net_device *dev = vi->dev;
 	struct scatterlist sg[2];
 	struct virtio_net_ctrl_mac *mac_data;
 	struct netdev_hw_addr *ha;
@@ -2463,6 +2485,8 @@  static void virtnet_set_rx_mode(struct net_device *dev)
 	if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX))
 		return;
 
+	rtnl_lock();
+
 	vi->ctrl->promisc = ((dev->flags & IFF_PROMISC) != 0);
 	vi->ctrl->allmulti = ((dev->flags & IFF_ALLMULTI) != 0);
 
@@ -2480,14 +2504,19 @@  static void virtnet_set_rx_mode(struct net_device *dev)
 		dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n",
 			 vi->ctrl->allmulti ? "en" : "dis");
 
+	netif_addr_lock_bh(dev);
+
 	uc_count = netdev_uc_count(dev);
 	mc_count = netdev_mc_count(dev);
 	/* MAC filter - use one buffer for both lists */
 	buf = kzalloc(((uc_count + mc_count) * ETH_ALEN) +
 		      (2 * sizeof(mac_data->entries)), GFP_ATOMIC);
 	mac_data = buf;
-	if (!buf)
+	if (!buf) {
+		netif_addr_unlock_bh(dev);
+		rtnl_unlock();
 		return;
+	}
 
 	sg_init_table(sg, 2);
 
@@ -2508,6 +2537,8 @@  static void virtnet_set_rx_mode(struct net_device *dev)
 	netdev_for_each_mc_addr(ha, dev)
 		memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN);
 
+	netif_addr_unlock_bh(dev);
+
 	sg_set_buf(&sg[1], mac_data,
 		   sizeof(mac_data->entries) + (mc_count * ETH_ALEN));
 
@@ -2515,9 +2546,19 @@  static void virtnet_set_rx_mode(struct net_device *dev)
 				  VIRTIO_NET_CTRL_MAC_TABLE_SET, sg))
 		dev_warn(&dev->dev, "Failed to set MAC filter table.\n");
 
+	rtnl_unlock();
+
 	kfree(buf);
 }
 
+static void virtnet_set_rx_mode(struct net_device *dev)
+{
+	struct virtnet_info *vi = netdev_priv(dev);
+
+	if (vi->rx_mode_work_enabled)
+		schedule_work(&vi->rx_mode_work);
+}
+
 static int virtnet_vlan_rx_add_vid(struct net_device *dev,
 				   __be16 proto, u16 vid)
 {
@@ -3286,6 +3327,8 @@  static void virtnet_freeze_down(struct virtio_device *vdev)
 
 	/* Make sure no work handler is accessing the device */
 	flush_work(&vi->config_work);
+	disable_rx_mode_work(vi);
+	flush_work(&vi->rx_mode_work);
 
 	netif_tx_lock_bh(vi->dev);
 	netif_device_detach(vi->dev);
@@ -3308,6 +3351,7 @@  static int virtnet_restore_up(struct virtio_device *vdev)
 	virtio_device_ready(vdev);
 
 	enable_delayed_refill(vi);
+	enable_rx_mode_work(vi);
 
 	if (netif_running(vi->dev)) {
 		err = virtnet_open(vi->dev);
@@ -4121,6 +4165,7 @@  static int virtnet_probe(struct virtio_device *vdev)
 	vdev->priv = vi;
 
 	INIT_WORK(&vi->config_work, virtnet_config_changed_work);
+	INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work);
 	spin_lock_init(&vi->refill_lock);
 
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) {
@@ -4229,6 +4274,8 @@  static int virtnet_probe(struct virtio_device *vdev)
 	if (vi->has_rss || vi->has_rss_hash_report)
 		virtnet_init_default_rss(vi);
 
+	enable_rx_mode_work(vi);
+
 	/* serialize netdev register + virtio_device_ready() with ndo_open() */
 	rtnl_lock();
 
@@ -4326,6 +4373,8 @@  static void virtnet_remove(struct virtio_device *vdev)
 
 	/* Make sure no work handler is accessing the device. */
 	flush_work(&vi->config_work);
+	disable_rx_mode_work(vi);
+	flush_work(&vi->rx_mode_work);
 
 	unregister_netdev(vi->dev);