Commit c23cad92 authored by Ursula Braun's avatar Ursula Braun Committed by Martin Schwidefsky

[S390] PM: af_iucv power management callbacks.

Patch establishes a dummy afiucv-device to make sure af_iucv is
notified as iucv-bus device about suspend/resume.

The PM freeze callback severs all iucv pathes of connected af_iucv sockets.
The PM thaw/restore callback switches the state of all previously connected
sockets to IUCV_DISCONN.
Signed-off-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 1175b257
/* /*
* linux/net/iucv/af_iucv.c
*
* IUCV protocol stack for Linux on zSeries * IUCV protocol stack for Linux on zSeries
* *
* Copyright 2006 IBM Corporation * Copyright IBM Corp. 2006, 2009
* *
* Author(s): Jennifer Hunt <jenhunt@us.ibm.com> * 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" #define KMSG_COMPONENT "af_iucv"
...@@ -90,6 +91,122 @@ static inline void low_nmcpy(unsigned char *dst, char *src) ...@@ -90,6 +91,122 @@ static inline void low_nmcpy(unsigned char *dst, char *src)
memcpy(&dst[8], src, 8); 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. * iucv_msg_length() - Returns the length of an iucv message.
* @msg: Pointer to struct iucv_message, MUST NOT be NULL * @msg: Pointer to struct iucv_message, MUST NOT be NULL
...@@ -1556,8 +1673,30 @@ static int __init afiucv_init(void) ...@@ -1556,8 +1673,30 @@ static int __init afiucv_init(void)
err = sock_register(&iucv_sock_family_ops); err = sock_register(&iucv_sock_family_ops);
if (err) if (err)
goto out_proto; 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; return 0;
out_driver:
driver_unregister(&af_iucv_driver);
out_sock:
sock_unregister(PF_IUCV);
out_proto: out_proto:
proto_unregister(&iucv_proto); proto_unregister(&iucv_proto);
out_iucv: out_iucv:
...@@ -1568,6 +1707,8 @@ out: ...@@ -1568,6 +1707,8 @@ out:
static void __exit afiucv_exit(void) static void __exit afiucv_exit(void)
{ {
device_unregister(af_iucv_dev);
driver_unregister(&af_iucv_driver);
sock_unregister(PF_IUCV); sock_unregister(PF_IUCV);
proto_unregister(&iucv_proto); proto_unregister(&iucv_proto);
iucv_unregister(&af_iucv_handler, 0); iucv_unregister(&af_iucv_handler, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment