|
@@ -1,11 +1,12 @@
|
|
|
/*
|
|
|
- * linux/net/iucv/af_iucv.c
|
|
|
- *
|
|
|
* IUCV protocol stack for Linux on zSeries
|
|
|
*
|
|
|
- * Copyright 2006 IBM Corporation
|
|
|
+ * Copyright IBM Corp. 2006, 2009
|
|
|
*
|
|
|
* Author(s): Jennifer Hunt <jenhunt@us.ibm.com>
|
|
|
+ * Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
|
|
|
+ * PM functions:
|
|
|
+ * Ursula Braun <ursula.braun@de.ibm.com>
|
|
|
*/
|
|
|
|
|
|
#define KMSG_COMPONENT "af_iucv"
|
|
@@ -90,6 +91,122 @@ static inline void low_nmcpy(unsigned char *dst, char *src)
|
|
|
memcpy(&dst[8], src, 8);
|
|
|
}
|
|
|
|
|
|
+static int afiucv_pm_prepare(struct device *dev)
|
|
|
+{
|
|
|
+#ifdef CONFIG_PM_DEBUG
|
|
|
+ printk(KERN_WARNING "afiucv_pm_prepare\n");
|
|
|
+#endif
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void afiucv_pm_complete(struct device *dev)
|
|
|
+{
|
|
|
+#ifdef CONFIG_PM_DEBUG
|
|
|
+ printk(KERN_WARNING "afiucv_pm_complete\n");
|
|
|
+#endif
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * afiucv_pm_freeze() - Freeze PM callback
|
|
|
+ * @dev: AFIUCV dummy device
|
|
|
+ *
|
|
|
+ * Sever all established IUCV communication pathes
|
|
|
+ */
|
|
|
+static int afiucv_pm_freeze(struct device *dev)
|
|
|
+{
|
|
|
+ struct iucv_sock *iucv;
|
|
|
+ struct sock *sk;
|
|
|
+ struct hlist_node *node;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+#ifdef CONFIG_PM_DEBUG
|
|
|
+ printk(KERN_WARNING "afiucv_pm_freeze\n");
|
|
|
+#endif
|
|
|
+ read_lock(&iucv_sk_list.lock);
|
|
|
+ sk_for_each(sk, node, &iucv_sk_list.head) {
|
|
|
+ iucv = iucv_sk(sk);
|
|
|
+ skb_queue_purge(&iucv->send_skb_q);
|
|
|
+ skb_queue_purge(&iucv->backlog_skb_q);
|
|
|
+ switch (sk->sk_state) {
|
|
|
+ case IUCV_SEVERED:
|
|
|
+ case IUCV_DISCONN:
|
|
|
+ case IUCV_CLOSING:
|
|
|
+ case IUCV_CONNECTED:
|
|
|
+ if (iucv->path) {
|
|
|
+ err = iucv_path_sever(iucv->path, NULL);
|
|
|
+ iucv_path_free(iucv->path);
|
|
|
+ iucv->path = NULL;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case IUCV_OPEN:
|
|
|
+ case IUCV_BOUND:
|
|
|
+ case IUCV_LISTEN:
|
|
|
+ case IUCV_CLOSED:
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ read_unlock(&iucv_sk_list.lock);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * afiucv_pm_restore_thaw() - Thaw and restore PM callback
|
|
|
+ * @dev: AFIUCV dummy device
|
|
|
+ *
|
|
|
+ * socket clean up after freeze
|
|
|
+ */
|
|
|
+static int afiucv_pm_restore_thaw(struct device *dev)
|
|
|
+{
|
|
|
+ struct iucv_sock *iucv;
|
|
|
+ struct sock *sk;
|
|
|
+ struct hlist_node *node;
|
|
|
+
|
|
|
+#ifdef CONFIG_PM_DEBUG
|
|
|
+ printk(KERN_WARNING "afiucv_pm_restore_thaw\n");
|
|
|
+#endif
|
|
|
+ read_lock(&iucv_sk_list.lock);
|
|
|
+ sk_for_each(sk, node, &iucv_sk_list.head) {
|
|
|
+ iucv = iucv_sk(sk);
|
|
|
+ switch (sk->sk_state) {
|
|
|
+ case IUCV_CONNECTED:
|
|
|
+ sk->sk_err = EPIPE;
|
|
|
+ sk->sk_state = IUCV_DISCONN;
|
|
|
+ sk->sk_state_change(sk);
|
|
|
+ break;
|
|
|
+ case IUCV_DISCONN:
|
|
|
+ case IUCV_SEVERED:
|
|
|
+ case IUCV_CLOSING:
|
|
|
+ case IUCV_LISTEN:
|
|
|
+ case IUCV_BOUND:
|
|
|
+ case IUCV_OPEN:
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ read_unlock(&iucv_sk_list.lock);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct dev_pm_ops afiucv_pm_ops = {
|
|
|
+ .prepare = afiucv_pm_prepare,
|
|
|
+ .complete = afiucv_pm_complete,
|
|
|
+ .freeze = afiucv_pm_freeze,
|
|
|
+ .thaw = afiucv_pm_restore_thaw,
|
|
|
+ .restore = afiucv_pm_restore_thaw,
|
|
|
+};
|
|
|
+
|
|
|
+static struct device_driver af_iucv_driver = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .name = "afiucv",
|
|
|
+ .bus = &iucv_bus,
|
|
|
+ .pm = &afiucv_pm_ops,
|
|
|
+};
|
|
|
+
|
|
|
+/* dummy device used as trigger for PM functions */
|
|
|
+static struct device *af_iucv_dev;
|
|
|
+
|
|
|
/**
|
|
|
* iucv_msg_length() - Returns the length of an iucv message.
|
|
|
* @msg: Pointer to struct iucv_message, MUST NOT be NULL
|
|
@@ -1556,8 +1673,30 @@ static int __init afiucv_init(void)
|
|
|
err = sock_register(&iucv_sock_family_ops);
|
|
|
if (err)
|
|
|
goto out_proto;
|
|
|
+ /* establish dummy device */
|
|
|
+ err = driver_register(&af_iucv_driver);
|
|
|
+ if (err)
|
|
|
+ goto out_sock;
|
|
|
+ af_iucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
|
|
+ if (!af_iucv_dev) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto out_driver;
|
|
|
+ }
|
|
|
+ dev_set_name(af_iucv_dev, "af_iucv");
|
|
|
+ af_iucv_dev->bus = &iucv_bus;
|
|
|
+ af_iucv_dev->parent = iucv_root;
|
|
|
+ af_iucv_dev->release = (void (*)(struct device *))kfree;
|
|
|
+ af_iucv_dev->driver = &af_iucv_driver;
|
|
|
+ err = device_register(af_iucv_dev);
|
|
|
+ if (err)
|
|
|
+ goto out_driver;
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
+out_driver:
|
|
|
+ driver_unregister(&af_iucv_driver);
|
|
|
+out_sock:
|
|
|
+ sock_unregister(PF_IUCV);
|
|
|
out_proto:
|
|
|
proto_unregister(&iucv_proto);
|
|
|
out_iucv:
|
|
@@ -1568,6 +1707,8 @@ out:
|
|
|
|
|
|
static void __exit afiucv_exit(void)
|
|
|
{
|
|
|
+ device_unregister(af_iucv_dev);
|
|
|
+ driver_unregister(&af_iucv_driver);
|
|
|
sock_unregister(PF_IUCV);
|
|
|
proto_unregister(&iucv_proto);
|
|
|
iucv_unregister(&af_iucv_handler, 0);
|