From 0753677f452b5bc42d6cba311df78dbf93fa978e Mon Sep 17 00:00:00 2001
From: Munehisa Kamata <kamatam@amazon.com>
Date: Mon, 9 Jan 2017 23:36:52 +0000
Subject: xen-netfront: add callbacks for PM suspend and hibernation support
Add freeze and restore callbacks for PM suspend and hibernation support.
The freeze handler simply disconnects the frotnend from the backend and
frees resources associated with queues after disabling the net_device
from the system. The restore handler just changes the frontend state and
let the xenbus handler to re-allocate the resources and re-connect to the
backend. This can be performed transparently to the rest of the system.
The handlers are used for both PM suspend and hibernation so that we can
keep the existing suspend/resume callbacks for Xen suspend without
modification.
Signed-off-by: Munehisa Kamata <kamatam@amazon.com>
Reviewed-by: Vallish Vaidyeshwara <vallish@amazon.com>
Reviewed-by: Eduardo Valentin <eduval@amazon.com>
Reviewed-by: Anchal Agarwal <anchalag@amazon.com>
CR: https://cr.amazon.com/r/7367683/
---
drivers/net/xen-netfront.c | 93 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 92 insertions(+), 1 deletion(-)
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 8d498a9..7e5233e 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -43,6 +43,7 @@
#include <linux/moduleparam.h>
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/completion.h>
#include <net/ip.h>
#include <xen/xen.h>
@@ -56,6 +57,12 @@
#include <xen/interface/memory.h>
#include <xen/interface/grant_table.h>
+enum netif_freeze_state {
+ NETIF_FREEZE_STATE_UNFROZEN,
+ NETIF_FREEZE_STATE_FREEZING,
+ NETIF_FREEZE_STATE_FROZEN,
+};
+
/* Module parameters */
static unsigned int xennet_max_queues;
module_param_named(max_queues, xennet_max_queues, uint, 0644);
@@ -157,6 +164,10 @@ struct netfront_info {
struct netfront_stats __percpu *tx_stats;
atomic_t rx_gso_checksum_fixup;
+
+ int freeze_state;
+
+ struct completion wait_backend_disconnected;
};
struct netfront_rx_info {
@@ -717,6 +728,21 @@ static int xennet_close(struct net_device *dev)
return 0;
}
+static int xennet_disable_interrupts(struct net_device *dev)
+{
+ struct netfront_info *np = netdev_priv(dev);
+ unsigned int num_queues = dev->real_num_tx_queues;
+ unsigned int i;
+ struct netfront_queue *queue;
+
+ for (i = 0; i < num_queues; ++i) {
+ queue = &np->queues[i];
+ disable_irq(queue->tx_irq);
+ disable_irq(queue->rx_irq);
+ }
+ return 0;
+}
+
static void xennet_move_rx_slot(struct netfront_queue *queue, struct sk_buff *skb,
grant_ref_t ref)
{
@@ -1312,6 +1338,8 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
np->queues = NULL;
+ init_completion(&np->wait_backend_disconnected);
+
err = -ENOMEM;
np->rx_stats = netdev_alloc_pcpu_stats(struct netfront_stats);
if (np->rx_stats == NULL)
@@ -1814,6 +1842,51 @@ static int xennet_create_queues(struct netfront_info *info,
return 0;
}
+static int netfront_freeze(struct xenbus_device *dev)
+{
+ struct netfront_info *info = dev_get_drvdata(&dev->dev);
+ /* This would be reasonable timeout as used in xenbus_dev_shutdown() */
+ unsigned long timeout = 5 * HZ;
+ int err = 0;
+
+ xennet_disable_interrupts(info->netdev);
+
+ netif_device_detach(info->netdev);
+
+ info->freeze_state = NETIF_FREEZE_STATE_FREEZING;
+
+ /* Kick the backend to disconnect */
+ xenbus_switch_state(dev, XenbusStateClosing);
+
+ /* We don't want to move forward before the frontend is diconnected
+ * from the backend cleanly.
+ */
+ timeout = wait_for_completion_timeout(&info->wait_backend_disconnected,
+ timeout);
+ if (!timeout) {
+ err = -EBUSY;
+ xenbus_dev_error(dev, err, "Freezing timed out;"
+ "the device may become inconsistent state");
+ return err;
+ }
+
+ /* Tear down queues */
+ xennet_disconnect_backend(info);
+ xennet_destroy_queues(info);
+
+ info->freeze_state = NETIF_FREEZE_STATE_FROZEN;
+
+ return err;
+}
+
+static int netfront_restore(struct xenbus_device *dev)
+{
+ /* Kick the backend to re-connect */
+ xenbus_switch_state(dev, XenbusStateInitialising);
+
+ return 0;
+}
+
/* Common code used when first setting up, and when resuming. */
static int talk_to_netback(struct xenbus_device *dev,
struct netfront_info *info)
@@ -2016,6 +2089,8 @@ static int xennet_connect(struct net_device *dev)
spin_unlock_bh(&queue->rx_lock);
}
+ np->freeze_state = NETIF_FREEZE_STATE_UNFROZEN;
+
return 0;
}
@@ -2054,11 +2129,25 @@ static void netback_changed(struct xenbus_device *dev,
case XenbusStateClosed:
wake_up_all(&module_unload_q);
- if (dev->state == XenbusStateClosed)
+ if (dev->state == XenbusStateClosed) {
+ /* dpm context is waiting for the backend */
+ if (np->freeze_state == NETIF_FREEZE_STATE_FREEZING)
+ complete(&np->wait_backend_disconnected);
break;
+ }
/* Missed the backend's CLOSING state -- fallthrough */
case XenbusStateClosing:
wake_up_all(&module_unload_q);
+
+ /* We may see unexpected Closed or Closing from the backend.
+ * Just ignore it not to prevent the frontend from being
+ * re-connected in the case of PM suspend or hibernation.
+ */
+ if (np->freeze_state == NETIF_FREEZE_STATE_FROZEN &&
+ dev->state == XenbusStateInitialising) {
+ break;
+ }
+
xenbus_frontend_closed(dev);
break;
}
@@ -2181,6 +2269,9 @@ static struct xenbus_driver netfront_driver = {
.probe = netfront_probe,
.remove = xennet_remove,
.resume = netfront_resume,
+ .freeze = netfront_freeze,
+ .thaw = netfront_restore,
+ .restore = netfront_restore,
.otherend_changed = netback_changed,
};
--
2.7.5