diff mbox series

[RFC,net-next,v3] net: virtio_net: notifications coalescing support

Message ID 20220714142027.3684622-1-alvaro.karsz@solid-run.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [RFC,net-next,v3] net: virtio_net: notifications coalescing support | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 14 this patch: 14
netdev/cc_maintainers warning 1 maintainers not CCed: virtualization@lists.linux-foundation.org
netdev/build_clang success Errors and warnings before: 6 this patch: 6
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
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: 14 this patch: 14
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Alvaro Karsz July 14, 2022, 2:20 p.m. UTC
New VirtIO network feature: VIRTIO_NET_F_NOTF_COAL.

Control a Virtio network device notifications coalescing parameters
using the control virtqueue.

A device that supports this fetature can receive
VIRTIO_NET_CTRL_NOTF_COAL control commands.

- VIRTIO_NET_CTRL_NOTF_COAL_TX_SET:
  Ask the network device to change the following parameters:
  - tx_usecs: Maximum number of usecs to delay a TX notification.
  - tx_max_packets: Maximum number of packets to send before a
    TX notification.

- VIRTIO_NET_CTRL_NOTF_COAL_RX_SET:
  Ask the network device to change the following parameters:
  - rx_usecs: Maximum number of usecs to delay a RX notification.
  - rx_max_packets: Maximum number of packets to receive before a
    RX notification.

VirtIO spec. patch:
https://lists.oasis-open.org/archives/virtio-comment/202206/msg00100.html

Signed-off-by: Alvaro Karsz <alvaro.karsz@solid-run.com>
---
v2:
	- Fix type assignments warnings found with sparse.
	- Fix a few typos.

v3:
  - Change the coalescing parameters in a dedicated function.
  - Return -EBUSY from the set coalescing function when the device's
    link is up, even if the notifications coalescing feature is negotiated.

---
 drivers/net/virtio_net.c        | 127 +++++++++++++++++++++++++++-----
 include/uapi/linux/virtio_net.h |  34 ++++++++-
 2 files changed, 140 insertions(+), 21 deletions(-)

--
2.32.0

Comments

Alvaro Karsz July 17, 2022, 6:24 a.m. UTC | #1
If VIRTIO_NET_F_NOTF_COAL is negotiated, and we try to change the
coalescing parameters when the link is up,
the control commands will be sent to the device, but the
virtnet_set_coalesce function will return -EBUSY anyway.

I'll fix it in the next version by checking if the link is up before
sending the control commands.

BTW, the VirtIO patch was merged, so the next version won't be tagged as RFC.
Dave Taht July 17, 2022, 2:09 p.m. UTC | #2
On Thu, Jul 14, 2022 at 7:29 AM Alvaro Karsz <alvaro.karsz@solid-run.com> wrote:
>
> New VirtIO network feature: VIRTIO_NET_F_NOTF_COAL.
>
> Control a Virtio network device notifications coalescing parameters
> using the control virtqueue.

What are a typical range of settings for these?


> A device that supports this fetature can receive
> VIRTIO_NET_CTRL_NOTF_COAL control commands.
>
> - VIRTIO_NET_CTRL_NOTF_COAL_TX_SET:
>   Ask the network device to change the following parameters:
>   - tx_usecs: Maximum number of usecs to delay a TX notification.
>   - tx_max_packets: Maximum number of packets to send before a
>     TX notification.
>
> - VIRTIO_NET_CTRL_NOTF_COAL_RX_SET:
>   Ask the network device to change the following parameters:
>   - rx_usecs: Maximum number of usecs to delay a RX notification.
>   - rx_max_packets: Maximum number of packets to receive before a
>     RX notification.

Bytes = time.  Packets nowadays have a dynamic range of 64-64k bytes,
and with big TCP even more. would there be any way to use
bytes rather than packets?

https://lwn.net/Articles/469652/
>
> VirtIO spec. patch:
> https://lists.oasis-open.org/archives/virtio-comment/202206/msg00100.html
>
> Signed-off-by: Alvaro Karsz <alvaro.karsz@solid-run.com>
> ---
> v2:
>         - Fix type assignments warnings found with sparse.
>         - Fix a few typos.
>
> v3:
>   - Change the coalescing parameters in a dedicated function.
>   - Return -EBUSY from the set coalescing function when the device's
>     link is up, even if the notifications coalescing feature is negotiated.
>
> ---
>  drivers/net/virtio_net.c        | 127 +++++++++++++++++++++++++++-----
>  include/uapi/linux/virtio_net.h |  34 ++++++++-
>  2 files changed, 140 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 356cf8dd416..00905f2e2f2 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -261,6 +261,12 @@ struct virtnet_info {
>         u8 duplex;
>         u32 speed;
>
> +       /* Interrupt coalescing settings */
> +       u32 tx_usecs;
> +       u32 rx_usecs;
> +       u32 tx_max_packets;
> +       u32 rx_max_packets;
> +
>         unsigned long guest_offloads;
>         unsigned long guest_offloads_capable;
>
> @@ -2587,44 +2593,115 @@ static int virtnet_get_link_ksettings(struct net_device *dev,
>         return 0;
>  }
>
> -static int virtnet_set_coalesce(struct net_device *dev,
> -                               struct ethtool_coalesce *ec,
> -                               struct kernel_ethtool_coalesce *kernel_coal,
> -                               struct netlink_ext_ack *extack)
> +static int virtnet_update_napi_weight(struct net_device *dev,
> +                                     int napi_weight)
>  {
>         struct virtnet_info *vi = netdev_priv(dev);
> -       int i, napi_weight;
> -
> -       if (ec->tx_max_coalesced_frames > 1 ||
> -           ec->rx_max_coalesced_frames != 1)
> -               return -EINVAL;
> +       int i;
>
> -       napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
>         if (napi_weight ^ vi->sq[0].napi.weight) {
>                 if (dev->flags & IFF_UP)
>                         return -EBUSY;
>                 for (i = 0; i < vi->max_queue_pairs; i++)
>                         vi->sq[i].napi.weight = napi_weight;
>         }
> -
>         return 0;
>  }
>
> +static int virtnet_set_notf_coal(struct net_device *dev,
> +                                struct ethtool_coalesce *ec)
> +{
> +       struct virtnet_info *vi = netdev_priv(dev);
> +       struct scatterlist sgs_tx, sgs_rx;
> +       struct virtio_net_ctrl_coal_tx coal_tx;
> +       struct virtio_net_ctrl_coal_rx coal_rx;
> +       int ret, napi_weight;
> +
> +       coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs);
> +       coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames);
> +       sg_init_one(&sgs_tx, &coal_tx, sizeof(coal_tx));
> +
> +       if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL,
> +                                 VIRTIO_NET_CTRL_NOTF_COAL_TX_SET,
> +                                 &sgs_tx))
> +               return -EINVAL;
> +
> +       /* Save parameters */
> +       vi->tx_usecs = ec->tx_coalesce_usecs;
> +       vi->tx_max_packets = ec->tx_max_coalesced_frames;
> +
> +       coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs);
> +       coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames);
> +       sg_init_one(&sgs_rx, &coal_rx, sizeof(coal_rx));
> +
> +       if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL,
> +                                 VIRTIO_NET_CTRL_NOTF_COAL_RX_SET,
> +                                 &sgs_rx))
> +               return -EINVAL;
> +
> +       /* Save parameters */
> +       vi->rx_usecs = ec->rx_coalesce_usecs;
> +       vi->rx_max_packets = ec->rx_max_coalesced_frames;
> +
> +       napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
> +       ret = virtnet_update_napi_weight(dev, napi_weight);
> +       return ret;
> +}
> +
> +static int virtnet_set_notf_coal_napi(struct net_device *dev,
> +                                     struct ethtool_coalesce *ec)
> +{
> +       int ret, napi_weight;
> +
> +       /* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL
> +        * feature is negotiated.
> +        */
> +       if (ec->rx_coalesce_usecs || ec->tx_coalesce_usecs)
> +               return -EOPNOTSUPP;
> +
> +       if (ec->tx_max_coalesced_frames > 1 ||
> +           ec->rx_max_coalesced_frames != 1)
> +               return -EINVAL;
> +
> +       napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
> +       ret = virtnet_update_napi_weight(dev, napi_weight);
> +       return ret;
> +}
> +
> +static int virtnet_set_coalesce(struct net_device *dev,
> +                               struct ethtool_coalesce *ec,
> +                               struct kernel_ethtool_coalesce *kernel_coal,
> +                               struct netlink_ext_ack *extack)
> +{
> +       struct virtnet_info *vi = netdev_priv(dev);
> +       int ret;
> +
> +       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL))
> +               ret = virtnet_set_notf_coal(dev, ec);
> +       else
> +               ret = virtnet_set_notf_coal_napi(dev, ec);
> +
> +       return ret;
> +}
> +
>  static int virtnet_get_coalesce(struct net_device *dev,
>                                 struct ethtool_coalesce *ec,
>                                 struct kernel_ethtool_coalesce *kernel_coal,
>                                 struct netlink_ext_ack *extack)
>  {
> -       struct ethtool_coalesce ec_default = {
> -               .cmd = ETHTOOL_GCOALESCE,
> -               .rx_max_coalesced_frames = 1,
> -       };
>         struct virtnet_info *vi = netdev_priv(dev);
>
> -       memcpy(ec, &ec_default, sizeof(ec_default));
> +       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) {
> +               ec->rx_coalesce_usecs = vi->rx_usecs;
> +               ec->tx_coalesce_usecs = vi->tx_usecs;
> +               ec->tx_max_coalesced_frames = vi->tx_max_packets;
> +               ec->rx_max_coalesced_frames = vi->rx_max_packets;
> +       } else {
> +               ec->rx_max_coalesced_frames = 1;
>
> -       if (vi->sq[0].napi.weight)
> -               ec->tx_max_coalesced_frames = 1;
> +               if (vi->sq[0].napi.weight)
> +                       ec->tx_max_coalesced_frames = 1;
> +       }
>
>         return 0;
>  }
> @@ -2743,7 +2820,8 @@ static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
>  }
>
>  static const struct ethtool_ops virtnet_ethtool_ops = {
> -       .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES,
> +       .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
> +               ETHTOOL_COALESCE_USECS,
>         .get_drvinfo = virtnet_get_drvinfo,
>         .get_link = ethtool_op_get_link,
>         .get_ringparam = virtnet_get_ringparam,
> @@ -3411,6 +3489,8 @@ static bool virtnet_validate_features(struct virtio_device *vdev)
>              VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_RSS,
>                              "VIRTIO_NET_F_CTRL_VQ") ||
>              VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_HASH_REPORT,
> +                            "VIRTIO_NET_F_CTRL_VQ") ||
> +            VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_NOTF_COAL,
>                              "VIRTIO_NET_F_CTRL_VQ"))) {
>                 return false;
>         }
> @@ -3546,6 +3626,13 @@ static int virtnet_probe(struct virtio_device *vdev)
>         if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
>                 vi->mergeable_rx_bufs = true;
>
> +       if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) {
> +               vi->rx_usecs = 0;
> +               vi->tx_usecs = 0;
> +               vi->tx_max_packets = 0;
> +               vi->rx_max_packets = 0;
> +       }
> +
>         if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT))
>                 vi->has_rss_hash_report = true;
>
> @@ -3780,7 +3867,7 @@ static struct virtio_device_id id_table[] = {
>         VIRTIO_NET_F_CTRL_MAC_ADDR, \
>         VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \
>         VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \
> -       VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT
> +       VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL
>
>  static unsigned int features[] = {
>         VIRTNET_FEATURES,
> diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
> index 3f55a4215f1..29ced55514d 100644
> --- a/include/uapi/linux/virtio_net.h
> +++ b/include/uapi/linux/virtio_net.h
> @@ -56,7 +56,7 @@
>  #define VIRTIO_NET_F_MQ        22      /* Device supports Receive Flow
>                                          * Steering */
>  #define VIRTIO_NET_F_CTRL_MAC_ADDR 23  /* Set MAC address */
> -
> +#define VIRTIO_NET_F_NOTF_COAL 53      /* Guest can handle notifications coalescing */
>  #define VIRTIO_NET_F_HASH_REPORT  57   /* Supports hash report */
>  #define VIRTIO_NET_F_RSS         60    /* Supports RSS RX steering */
>  #define VIRTIO_NET_F_RSC_EXT     61    /* extended coalescing info */
> @@ -355,4 +355,36 @@ struct virtio_net_hash_config {
>  #define VIRTIO_NET_CTRL_GUEST_OFFLOADS   5
>  #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET        0
>
> +/*
> + * Control notifications coalescing.
> + *
> + * Request the device to change the notifications coalescing parameters.
> + *
> + * Available with the VIRTIO_NET_F_NOTF_COAL feature bit.
> + */
> +#define VIRTIO_NET_CTRL_NOTF_COAL              6
> +/*
> + * Set the tx-usecs/tx-max-packets patameters.
> + * tx-usecs - Maximum number of usecs to delay a TX notification.
> + * tx-max-packets - Maximum number of packets to send before a TX notification.
> + */
> +struct virtio_net_ctrl_coal_tx {
> +       __le32 tx_max_packets;
> +       __le32 tx_usecs;
> +};
> +
> +#define VIRTIO_NET_CTRL_NOTF_COAL_TX_SET               0
> +
> +/*
> + * Set the rx-usecs/rx-max-packets patameters.
> + * rx-usecs - Maximum number of usecs to delay a RX notification.
> + * rx-max-frames - Maximum number of packets to receive before a RX notification.
> + */
> +struct virtio_net_ctrl_coal_rx {
> +       __le32 rx_max_packets;
> +       __le32 rx_usecs;
> +};
> +
> +#define VIRTIO_NET_CTRL_NOTF_COAL_RX_SET               1
> +
>  #endif /* _UAPI_LINUX_VIRTIO_NET_H */
> --
> 2.32.0
Alvaro Karsz July 17, 2022, 3 p.m. UTC | #3
Hi Dave,

> What are a typical range of settings for these?


This entirely depends on the user, and the values are set using ethtool.
$  ethtool -C <interface>  tx-frames X rx-frames Y rx-usecs Z tx-usecs W

> Bytes = time.  Packets nowadays have a dynamic range of 64-64k bytes,
> and with big TCP even more. would there be any way to use
> bytes rather than packets?


This feature should follow the VirtIO spec.
The parameters are set with ethtool, so we should use the defined
ethtool parameters.

The parameters are set with the virtnet_set_coalesce function, which
is the ethtool_ops set_coalesce callback.
diff mbox series

Patch

diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 356cf8dd416..00905f2e2f2 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -261,6 +261,12 @@  struct virtnet_info {
 	u8 duplex;
 	u32 speed;

+	/* Interrupt coalescing settings */
+	u32 tx_usecs;
+	u32 rx_usecs;
+	u32 tx_max_packets;
+	u32 rx_max_packets;
+
 	unsigned long guest_offloads;
 	unsigned long guest_offloads_capable;

@@ -2587,44 +2593,115 @@  static int virtnet_get_link_ksettings(struct net_device *dev,
 	return 0;
 }

-static int virtnet_set_coalesce(struct net_device *dev,
-				struct ethtool_coalesce *ec,
-				struct kernel_ethtool_coalesce *kernel_coal,
-				struct netlink_ext_ack *extack)
+static int virtnet_update_napi_weight(struct net_device *dev,
+				      int napi_weight)
 {
 	struct virtnet_info *vi = netdev_priv(dev);
-	int i, napi_weight;
-
-	if (ec->tx_max_coalesced_frames > 1 ||
-	    ec->rx_max_coalesced_frames != 1)
-		return -EINVAL;
+	int i;

-	napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
 	if (napi_weight ^ vi->sq[0].napi.weight) {
 		if (dev->flags & IFF_UP)
 			return -EBUSY;
 		for (i = 0; i < vi->max_queue_pairs; i++)
 			vi->sq[i].napi.weight = napi_weight;
 	}
-
 	return 0;
 }

+static int virtnet_set_notf_coal(struct net_device *dev,
+				 struct ethtool_coalesce *ec)
+{
+	struct virtnet_info *vi = netdev_priv(dev);
+	struct scatterlist sgs_tx, sgs_rx;
+	struct virtio_net_ctrl_coal_tx coal_tx;
+	struct virtio_net_ctrl_coal_rx coal_rx;
+	int ret, napi_weight;
+
+	coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs);
+	coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames);
+	sg_init_one(&sgs_tx, &coal_tx, sizeof(coal_tx));
+
+	if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL,
+				  VIRTIO_NET_CTRL_NOTF_COAL_TX_SET,
+				  &sgs_tx))
+		return -EINVAL;
+
+	/* Save parameters */
+	vi->tx_usecs = ec->tx_coalesce_usecs;
+	vi->tx_max_packets = ec->tx_max_coalesced_frames;
+
+	coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs);
+	coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames);
+	sg_init_one(&sgs_rx, &coal_rx, sizeof(coal_rx));
+
+	if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL,
+				  VIRTIO_NET_CTRL_NOTF_COAL_RX_SET,
+				  &sgs_rx))
+		return -EINVAL;
+
+	/* Save parameters */
+	vi->rx_usecs = ec->rx_coalesce_usecs;
+	vi->rx_max_packets = ec->rx_max_coalesced_frames;
+
+	napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
+	ret = virtnet_update_napi_weight(dev, napi_weight);
+	return ret;
+}
+
+static int virtnet_set_notf_coal_napi(struct net_device *dev,
+				      struct ethtool_coalesce *ec)
+{
+	int ret, napi_weight;
+
+	/* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL
+	 * feature is negotiated.
+	 */
+	if (ec->rx_coalesce_usecs || ec->tx_coalesce_usecs)
+		return -EOPNOTSUPP;
+
+	if (ec->tx_max_coalesced_frames > 1 ||
+	    ec->rx_max_coalesced_frames != 1)
+		return -EINVAL;
+
+	napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0;
+	ret = virtnet_update_napi_weight(dev, napi_weight);
+	return ret;
+}
+
+static int virtnet_set_coalesce(struct net_device *dev,
+				struct ethtool_coalesce *ec,
+				struct kernel_ethtool_coalesce *kernel_coal,
+				struct netlink_ext_ack *extack)
+{
+	struct virtnet_info *vi = netdev_priv(dev);
+	int ret;
+
+	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL))
+		ret = virtnet_set_notf_coal(dev, ec);
+	else
+		ret = virtnet_set_notf_coal_napi(dev, ec);
+
+	return ret;
+}
+
 static int virtnet_get_coalesce(struct net_device *dev,
 				struct ethtool_coalesce *ec,
 				struct kernel_ethtool_coalesce *kernel_coal,
 				struct netlink_ext_ack *extack)
 {
-	struct ethtool_coalesce ec_default = {
-		.cmd = ETHTOOL_GCOALESCE,
-		.rx_max_coalesced_frames = 1,
-	};
 	struct virtnet_info *vi = netdev_priv(dev);

-	memcpy(ec, &ec_default, sizeof(ec_default));
+	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) {
+		ec->rx_coalesce_usecs = vi->rx_usecs;
+		ec->tx_coalesce_usecs = vi->tx_usecs;
+		ec->tx_max_coalesced_frames = vi->tx_max_packets;
+		ec->rx_max_coalesced_frames = vi->rx_max_packets;
+	} else {
+		ec->rx_max_coalesced_frames = 1;

-	if (vi->sq[0].napi.weight)
-		ec->tx_max_coalesced_frames = 1;
+		if (vi->sq[0].napi.weight)
+			ec->tx_max_coalesced_frames = 1;
+	}

 	return 0;
 }
@@ -2743,7 +2820,8 @@  static int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
 }

 static const struct ethtool_ops virtnet_ethtool_ops = {
-	.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES,
+	.supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES |
+		ETHTOOL_COALESCE_USECS,
 	.get_drvinfo = virtnet_get_drvinfo,
 	.get_link = ethtool_op_get_link,
 	.get_ringparam = virtnet_get_ringparam,
@@ -3411,6 +3489,8 @@  static bool virtnet_validate_features(struct virtio_device *vdev)
 	     VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_RSS,
 			     "VIRTIO_NET_F_CTRL_VQ") ||
 	     VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_HASH_REPORT,
+			     "VIRTIO_NET_F_CTRL_VQ") ||
+	     VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_NOTF_COAL,
 			     "VIRTIO_NET_F_CTRL_VQ"))) {
 		return false;
 	}
@@ -3546,6 +3626,13 @@  static int virtnet_probe(struct virtio_device *vdev)
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF))
 		vi->mergeable_rx_bufs = true;

+	if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) {
+		vi->rx_usecs = 0;
+		vi->tx_usecs = 0;
+		vi->tx_max_packets = 0;
+		vi->rx_max_packets = 0;
+	}
+
 	if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT))
 		vi->has_rss_hash_report = true;

@@ -3780,7 +3867,7 @@  static struct virtio_device_id id_table[] = {
 	VIRTIO_NET_F_CTRL_MAC_ADDR, \
 	VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \
 	VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \
-	VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT
+	VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL

 static unsigned int features[] = {
 	VIRTNET_FEATURES,
diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h
index 3f55a4215f1..29ced55514d 100644
--- a/include/uapi/linux/virtio_net.h
+++ b/include/uapi/linux/virtio_net.h
@@ -56,7 +56,7 @@ 
 #define VIRTIO_NET_F_MQ	22	/* Device supports Receive Flow
 					 * Steering */
 #define VIRTIO_NET_F_CTRL_MAC_ADDR 23	/* Set MAC address */
-
+#define VIRTIO_NET_F_NOTF_COAL	53	/* Guest can handle notifications coalescing */
 #define VIRTIO_NET_F_HASH_REPORT  57	/* Supports hash report */
 #define VIRTIO_NET_F_RSS	  60	/* Supports RSS RX steering */
 #define VIRTIO_NET_F_RSC_EXT	  61	/* extended coalescing info */
@@ -355,4 +355,36 @@  struct virtio_net_hash_config {
 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS   5
 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET        0

+/*
+ * Control notifications coalescing.
+ *
+ * Request the device to change the notifications coalescing parameters.
+ *
+ * Available with the VIRTIO_NET_F_NOTF_COAL feature bit.
+ */
+#define VIRTIO_NET_CTRL_NOTF_COAL		6
+/*
+ * Set the tx-usecs/tx-max-packets patameters.
+ * tx-usecs - Maximum number of usecs to delay a TX notification.
+ * tx-max-packets - Maximum number of packets to send before a TX notification.
+ */
+struct virtio_net_ctrl_coal_tx {
+	__le32 tx_max_packets;
+	__le32 tx_usecs;
+};
+
+#define VIRTIO_NET_CTRL_NOTF_COAL_TX_SET		0
+
+/*
+ * Set the rx-usecs/rx-max-packets patameters.
+ * rx-usecs - Maximum number of usecs to delay a RX notification.
+ * rx-max-frames - Maximum number of packets to receive before a RX notification.
+ */
+struct virtio_net_ctrl_coal_rx {
+	__le32 rx_max_packets;
+	__le32 rx_usecs;
+};
+
+#define VIRTIO_NET_CTRL_NOTF_COAL_RX_SET		1
+
 #endif /* _UAPI_LINUX_VIRTIO_NET_H */