diff options
author | Ursula Braun <ursula.braun@de.ibm.com> | 2009-06-16 04:30:44 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-16 04:31:19 -0400 |
commit | c23cad923bfebd295ec49dc9265569993903488d (patch) | |
tree | 263bb2b6ed99f250bb8ed9f5fdeed1918cceb45e | |
parent | 1175b257c8a2cb384823621cad0c1e0945f74300 (diff) |
[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: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | net/iucv/af_iucv.c | 147 |
1 files changed, 144 insertions, 3 deletions
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index a9b3a6f9ea95..656cbd195825 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c | |||
@@ -1,11 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * linux/net/iucv/af_iucv.c | ||
3 | * | ||
4 | * IUCV protocol stack for Linux on zSeries | 2 | * IUCV protocol stack for Linux on zSeries |
5 | * | 3 | * |
6 | * Copyright 2006 IBM Corporation | 4 | * Copyright IBM Corp. 2006, 2009 |
7 | * | 5 | * |
8 | * Author(s): Jennifer Hunt <jenhunt@us.ibm.com> | 6 | * Author(s): Jennifer Hunt <jenhunt@us.ibm.com> |
7 | * Hendrik Brueckner <brueckner@linux.vnet.ibm.com> | ||
8 | * PM functions: | ||
9 | * Ursula Braun <ursula.braun@de.ibm.com> | ||
9 | */ | 10 | */ |
10 | 11 | ||
11 | #define KMSG_COMPONENT "af_iucv" | 12 | #define KMSG_COMPONENT "af_iucv" |
@@ -90,6 +91,122 @@ static inline void low_nmcpy(unsigned char *dst, char *src) | |||
90 | memcpy(&dst[8], src, 8); | 91 | memcpy(&dst[8], src, 8); |
91 | } | 92 | } |
92 | 93 | ||
94 | static int afiucv_pm_prepare(struct device *dev) | ||
95 | { | ||
96 | #ifdef CONFIG_PM_DEBUG | ||
97 | printk(KERN_WARNING "afiucv_pm_prepare\n"); | ||
98 | #endif | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static void afiucv_pm_complete(struct device *dev) | ||
103 | { | ||
104 | #ifdef CONFIG_PM_DEBUG | ||
105 | printk(KERN_WARNING "afiucv_pm_complete\n"); | ||
106 | #endif | ||
107 | return; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * afiucv_pm_freeze() - Freeze PM callback | ||
112 | * @dev: AFIUCV dummy device | ||
113 | * | ||
114 | * Sever all established IUCV communication pathes | ||
115 | */ | ||
116 | static int afiucv_pm_freeze(struct device *dev) | ||
117 | { | ||
118 | struct iucv_sock *iucv; | ||
119 | struct sock *sk; | ||
120 | struct hlist_node *node; | ||
121 | int err = 0; | ||
122 | |||
123 | #ifdef CONFIG_PM_DEBUG | ||
124 | printk(KERN_WARNING "afiucv_pm_freeze\n"); | ||
125 | #endif | ||
126 | read_lock(&iucv_sk_list.lock); | ||
127 | sk_for_each(sk, node, &iucv_sk_list.head) { | ||
128 | iucv = iucv_sk(sk); | ||
129 | skb_queue_purge(&iucv->send_skb_q); | ||
130 | skb_queue_purge(&iucv->backlog_skb_q); | ||
131 | switch (sk->sk_state) { | ||
132 | case IUCV_SEVERED: | ||
133 | case IUCV_DISCONN: | ||
134 | case IUCV_CLOSING: | ||
135 | case IUCV_CONNECTED: | ||
136 | if (iucv->path) { | ||
137 | err = iucv_path_sever(iucv->path, NULL); | ||
138 | iucv_path_free(iucv->path); | ||
139 | iucv->path = NULL; | ||
140 | } | ||
141 | break; | ||
142 | case IUCV_OPEN: | ||
143 | case IUCV_BOUND: | ||
144 | case IUCV_LISTEN: | ||
145 | case IUCV_CLOSED: | ||
146 | default: | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | read_unlock(&iucv_sk_list.lock); | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * afiucv_pm_restore_thaw() - Thaw and restore PM callback | ||
156 | * @dev: AFIUCV dummy device | ||
157 | * | ||
158 | * socket clean up after freeze | ||
159 | */ | ||
160 | static int afiucv_pm_restore_thaw(struct device *dev) | ||
161 | { | ||
162 | struct iucv_sock *iucv; | ||
163 | struct sock *sk; | ||
164 | struct hlist_node *node; | ||
165 | |||
166 | #ifdef CONFIG_PM_DEBUG | ||
167 | printk(KERN_WARNING "afiucv_pm_restore_thaw\n"); | ||
168 | #endif | ||
169 | read_lock(&iucv_sk_list.lock); | ||
170 | sk_for_each(sk, node, &iucv_sk_list.head) { | ||
171 | iucv = iucv_sk(sk); | ||
172 | switch (sk->sk_state) { | ||
173 | case IUCV_CONNECTED: | ||
174 | sk->sk_err = EPIPE; | ||
175 | sk->sk_state = IUCV_DISCONN; | ||
176 | sk->sk_state_change(sk); | ||
177 | break; | ||
178 | case IUCV_DISCONN: | ||
179 | case IUCV_SEVERED: | ||
180 | case IUCV_CLOSING: | ||
181 | case IUCV_LISTEN: | ||
182 | case IUCV_BOUND: | ||
183 | case IUCV_OPEN: | ||
184 | default: | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | read_unlock(&iucv_sk_list.lock); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static struct dev_pm_ops afiucv_pm_ops = { | ||
193 | .prepare = afiucv_pm_prepare, | ||
194 | .complete = afiucv_pm_complete, | ||
195 | .freeze = afiucv_pm_freeze, | ||
196 | .thaw = afiucv_pm_restore_thaw, | ||
197 | .restore = afiucv_pm_restore_thaw, | ||
198 | }; | ||
199 | |||
200 | static struct device_driver af_iucv_driver = { | ||
201 | .owner = THIS_MODULE, | ||
202 | .name = "afiucv", | ||
203 | .bus = &iucv_bus, | ||
204 | .pm = &afiucv_pm_ops, | ||
205 | }; | ||
206 | |||
207 | /* dummy device used as trigger for PM functions */ | ||
208 | static struct device *af_iucv_dev; | ||
209 | |||
93 | /** | 210 | /** |
94 | * iucv_msg_length() - Returns the length of an iucv message. | 211 | * iucv_msg_length() - Returns the length of an iucv message. |
95 | * @msg: Pointer to struct iucv_message, MUST NOT be NULL | 212 | * @msg: Pointer to struct iucv_message, MUST NOT be NULL |
@@ -1556,8 +1673,30 @@ static int __init afiucv_init(void) | |||
1556 | err = sock_register(&iucv_sock_family_ops); | 1673 | err = sock_register(&iucv_sock_family_ops); |
1557 | if (err) | 1674 | if (err) |
1558 | goto out_proto; | 1675 | goto out_proto; |
1676 | /* establish dummy device */ | ||
1677 | err = driver_register(&af_iucv_driver); | ||
1678 | if (err) | ||
1679 | goto out_sock; | ||
1680 | af_iucv_dev = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
1681 | if (!af_iucv_dev) { | ||
1682 | err = -ENOMEM; | ||
1683 | goto out_driver; | ||
1684 | } | ||
1685 | dev_set_name(af_iucv_dev, "af_iucv"); | ||
1686 | af_iucv_dev->bus = &iucv_bus; | ||
1687 | af_iucv_dev->parent = iucv_root; | ||
1688 | af_iucv_dev->release = (void (*)(struct device *))kfree; | ||
1689 | af_iucv_dev->driver = &af_iucv_driver; | ||
1690 | err = device_register(af_iucv_dev); | ||
1691 | if (err) | ||
1692 | goto out_driver; | ||
1693 | |||
1559 | return 0; | 1694 | return 0; |
1560 | 1695 | ||
1696 | out_driver: | ||
1697 | driver_unregister(&af_iucv_driver); | ||
1698 | out_sock: | ||
1699 | sock_unregister(PF_IUCV); | ||
1561 | out_proto: | 1700 | out_proto: |
1562 | proto_unregister(&iucv_proto); | 1701 | proto_unregister(&iucv_proto); |
1563 | out_iucv: | 1702 | out_iucv: |
@@ -1568,6 +1707,8 @@ out: | |||
1568 | 1707 | ||
1569 | static void __exit afiucv_exit(void) | 1708 | static void __exit afiucv_exit(void) |
1570 | { | 1709 | { |
1710 | device_unregister(af_iucv_dev); | ||
1711 | driver_unregister(&af_iucv_driver); | ||
1571 | sock_unregister(PF_IUCV); | 1712 | sock_unregister(PF_IUCV); |
1572 | proto_unregister(&iucv_proto); | 1713 | proto_unregister(&iucv_proto); |
1573 | iucv_unregister(&af_iucv_handler, 0); | 1714 | iucv_unregister(&af_iucv_handler, 0); |