diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_ccw.c')
-rw-r--r-- | drivers/s390/scsi/zfcp_ccw.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c new file mode 100644 index 000000000000..0fc46381fc22 --- /dev/null +++ b/drivers/s390/scsi/zfcp_ccw.c | |||
@@ -0,0 +1,312 @@ | |||
1 | /* | ||
2 | * linux/drivers/s390/scsi/zfcp_ccw.c | ||
3 | * | ||
4 | * FCP adapter driver for IBM eServer zSeries | ||
5 | * | ||
6 | * CCW driver related routines | ||
7 | * | ||
8 | * (C) Copyright IBM Corp. 2003, 2004 | ||
9 | * | ||
10 | * Authors: | ||
11 | * Martin Peschke <mpeschke@de.ibm.com> | ||
12 | * Heiko Carstens <heiko.carstens@de.ibm.com> | ||
13 | * Andreas Herrmann <aherrman@de.ibm.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2, or (at your option) | ||
18 | * any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
28 | */ | ||
29 | |||
30 | #define ZFCP_CCW_C_REVISION "$Revision: 1.58 $" | ||
31 | |||
32 | #include "zfcp_ext.h" | ||
33 | |||
34 | #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG | ||
35 | |||
36 | static int zfcp_ccw_probe(struct ccw_device *); | ||
37 | static void zfcp_ccw_remove(struct ccw_device *); | ||
38 | static int zfcp_ccw_set_online(struct ccw_device *); | ||
39 | static int zfcp_ccw_set_offline(struct ccw_device *); | ||
40 | static int zfcp_ccw_notify(struct ccw_device *, int); | ||
41 | static void zfcp_ccw_shutdown(struct device *); | ||
42 | |||
43 | static struct ccw_device_id zfcp_ccw_device_id[] = { | ||
44 | {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, | ||
45 | ZFCP_CONTROL_UNIT_MODEL, | ||
46 | ZFCP_DEVICE_TYPE, | ||
47 | ZFCP_DEVICE_MODEL)}, | ||
48 | {CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE, | ||
49 | ZFCP_CONTROL_UNIT_MODEL, | ||
50 | ZFCP_DEVICE_TYPE, | ||
51 | ZFCP_DEVICE_MODEL_PRIV)}, | ||
52 | {}, | ||
53 | }; | ||
54 | |||
55 | static struct ccw_driver zfcp_ccw_driver = { | ||
56 | .owner = THIS_MODULE, | ||
57 | .name = ZFCP_NAME, | ||
58 | .ids = zfcp_ccw_device_id, | ||
59 | .probe = zfcp_ccw_probe, | ||
60 | .remove = zfcp_ccw_remove, | ||
61 | .set_online = zfcp_ccw_set_online, | ||
62 | .set_offline = zfcp_ccw_set_offline, | ||
63 | .notify = zfcp_ccw_notify, | ||
64 | .driver = { | ||
65 | .shutdown = zfcp_ccw_shutdown, | ||
66 | }, | ||
67 | }; | ||
68 | |||
69 | MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); | ||
70 | |||
71 | /** | ||
72 | * zfcp_ccw_probe - probe function of zfcp driver | ||
73 | * @ccw_device: pointer to belonging ccw device | ||
74 | * | ||
75 | * This function gets called by the common i/o layer and sets up the initial | ||
76 | * data structures for each fcp adapter, which was detected by the system. | ||
77 | * Also the sysfs files for this adapter will be created by this function. | ||
78 | * In addition the nameserver port will be added to the ports of the adapter | ||
79 | * and its sysfs representation will be created too. | ||
80 | */ | ||
81 | static int | ||
82 | zfcp_ccw_probe(struct ccw_device *ccw_device) | ||
83 | { | ||
84 | struct zfcp_adapter *adapter; | ||
85 | int retval = 0; | ||
86 | |||
87 | down(&zfcp_data.config_sema); | ||
88 | adapter = zfcp_adapter_enqueue(ccw_device); | ||
89 | if (!adapter) | ||
90 | retval = -EINVAL; | ||
91 | else | ||
92 | ZFCP_LOG_DEBUG("Probed adapter %s\n", | ||
93 | zfcp_get_busid_by_adapter(adapter)); | ||
94 | up(&zfcp_data.config_sema); | ||
95 | return retval; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * zfcp_ccw_remove - remove function of zfcp driver | ||
100 | * @ccw_device: pointer to belonging ccw device | ||
101 | * | ||
102 | * This function gets called by the common i/o layer and removes an adapter | ||
103 | * from the system. Task of this function is to get rid of all units and | ||
104 | * ports that belong to this adapter. And in addition all resources of this | ||
105 | * adapter will be freed too. | ||
106 | */ | ||
107 | static void | ||
108 | zfcp_ccw_remove(struct ccw_device *ccw_device) | ||
109 | { | ||
110 | struct zfcp_adapter *adapter; | ||
111 | struct zfcp_port *port, *p; | ||
112 | struct zfcp_unit *unit, *u; | ||
113 | |||
114 | ccw_device_set_offline(ccw_device); | ||
115 | down(&zfcp_data.config_sema); | ||
116 | adapter = dev_get_drvdata(&ccw_device->dev); | ||
117 | |||
118 | ZFCP_LOG_DEBUG("Removing adapter %s\n", | ||
119 | zfcp_get_busid_by_adapter(adapter)); | ||
120 | write_lock_irq(&zfcp_data.config_lock); | ||
121 | list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { | ||
122 | list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { | ||
123 | list_move(&unit->list, &port->unit_remove_lh); | ||
124 | atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, | ||
125 | &unit->status); | ||
126 | } | ||
127 | list_move(&port->list, &adapter->port_remove_lh); | ||
128 | atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); | ||
129 | } | ||
130 | atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); | ||
131 | write_unlock_irq(&zfcp_data.config_lock); | ||
132 | |||
133 | list_for_each_entry_safe(port, p, &adapter->port_remove_lh, list) { | ||
134 | list_for_each_entry_safe(unit, u, &port->unit_remove_lh, list) { | ||
135 | zfcp_unit_dequeue(unit); | ||
136 | } | ||
137 | zfcp_port_dequeue(port); | ||
138 | } | ||
139 | zfcp_adapter_wait(adapter); | ||
140 | zfcp_adapter_dequeue(adapter); | ||
141 | |||
142 | up(&zfcp_data.config_sema); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * zfcp_ccw_set_online - set_online function of zfcp driver | ||
147 | * @ccw_device: pointer to belonging ccw device | ||
148 | * | ||
149 | * This function gets called by the common i/o layer and sets an adapter | ||
150 | * into state online. Setting an fcp device online means that it will be | ||
151 | * registered with the SCSI stack, that the QDIO queues will be set up | ||
152 | * and that the adapter will be opened (asynchronously). | ||
153 | */ | ||
154 | static int | ||
155 | zfcp_ccw_set_online(struct ccw_device *ccw_device) | ||
156 | { | ||
157 | struct zfcp_adapter *adapter; | ||
158 | int retval; | ||
159 | |||
160 | down(&zfcp_data.config_sema); | ||
161 | adapter = dev_get_drvdata(&ccw_device->dev); | ||
162 | |||
163 | retval = zfcp_adapter_debug_register(adapter); | ||
164 | if (retval) | ||
165 | goto out; | ||
166 | retval = zfcp_erp_thread_setup(adapter); | ||
167 | if (retval) { | ||
168 | ZFCP_LOG_INFO("error: start of error recovery thread for " | ||
169 | "adapter %s failed\n", | ||
170 | zfcp_get_busid_by_adapter(adapter)); | ||
171 | goto out_erp_thread; | ||
172 | } | ||
173 | |||
174 | retval = zfcp_adapter_scsi_register(adapter); | ||
175 | if (retval) | ||
176 | goto out_scsi_register; | ||
177 | zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING, | ||
178 | ZFCP_SET); | ||
179 | zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED); | ||
180 | zfcp_erp_wait(adapter); | ||
181 | goto out; | ||
182 | |||
183 | out_scsi_register: | ||
184 | zfcp_erp_thread_kill(adapter); | ||
185 | out_erp_thread: | ||
186 | zfcp_adapter_debug_unregister(adapter); | ||
187 | out: | ||
188 | up(&zfcp_data.config_sema); | ||
189 | return retval; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * zfcp_ccw_set_offline - set_offline function of zfcp driver | ||
194 | * @ccw_device: pointer to belonging ccw device | ||
195 | * | ||
196 | * This function gets called by the common i/o layer and sets an adapter | ||
197 | * into state offline. Setting an fcp device offline means that it will be | ||
198 | * unregistered from the SCSI stack and that the adapter will be shut down | ||
199 | * asynchronously. | ||
200 | */ | ||
201 | static int | ||
202 | zfcp_ccw_set_offline(struct ccw_device *ccw_device) | ||
203 | { | ||
204 | struct zfcp_adapter *adapter; | ||
205 | |||
206 | down(&zfcp_data.config_sema); | ||
207 | adapter = dev_get_drvdata(&ccw_device->dev); | ||
208 | zfcp_erp_adapter_shutdown(adapter, 0); | ||
209 | zfcp_erp_wait(adapter); | ||
210 | zfcp_adapter_scsi_unregister(adapter); | ||
211 | zfcp_erp_thread_kill(adapter); | ||
212 | zfcp_adapter_debug_unregister(adapter); | ||
213 | up(&zfcp_data.config_sema); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * zfcp_ccw_notify | ||
219 | * @ccw_device: pointer to belonging ccw device | ||
220 | * @event: indicates if adapter was detached or attached | ||
221 | * | ||
222 | * This function gets called by the common i/o layer if an adapter has gone | ||
223 | * or reappeared. | ||
224 | */ | ||
225 | static int | ||
226 | zfcp_ccw_notify(struct ccw_device *ccw_device, int event) | ||
227 | { | ||
228 | struct zfcp_adapter *adapter; | ||
229 | |||
230 | down(&zfcp_data.config_sema); | ||
231 | adapter = dev_get_drvdata(&ccw_device->dev); | ||
232 | switch (event) { | ||
233 | case CIO_GONE: | ||
234 | ZFCP_LOG_NORMAL("adapter %s: device gone\n", | ||
235 | zfcp_get_busid_by_adapter(adapter)); | ||
236 | debug_text_event(adapter->erp_dbf,1,"dev_gone"); | ||
237 | zfcp_erp_adapter_shutdown(adapter, 0); | ||
238 | break; | ||
239 | case CIO_NO_PATH: | ||
240 | ZFCP_LOG_NORMAL("adapter %s: no path\n", | ||
241 | zfcp_get_busid_by_adapter(adapter)); | ||
242 | debug_text_event(adapter->erp_dbf,1,"no_path"); | ||
243 | zfcp_erp_adapter_shutdown(adapter, 0); | ||
244 | break; | ||
245 | case CIO_OPER: | ||
246 | ZFCP_LOG_NORMAL("adapter %s: operational again\n", | ||
247 | zfcp_get_busid_by_adapter(adapter)); | ||
248 | debug_text_event(adapter->erp_dbf,1,"dev_oper"); | ||
249 | zfcp_erp_modify_adapter_status(adapter, | ||
250 | ZFCP_STATUS_COMMON_RUNNING, | ||
251 | ZFCP_SET); | ||
252 | zfcp_erp_adapter_reopen(adapter, | ||
253 | ZFCP_STATUS_COMMON_ERP_FAILED); | ||
254 | break; | ||
255 | } | ||
256 | zfcp_erp_wait(adapter); | ||
257 | up(&zfcp_data.config_sema); | ||
258 | return 1; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * zfcp_ccw_register - ccw register function | ||
263 | * | ||
264 | * Registers the driver at the common i/o layer. This function will be called | ||
265 | * at module load time/system start. | ||
266 | */ | ||
267 | int __init | ||
268 | zfcp_ccw_register(void) | ||
269 | { | ||
270 | int retval; | ||
271 | |||
272 | retval = ccw_driver_register(&zfcp_ccw_driver); | ||
273 | if (retval) | ||
274 | goto out; | ||
275 | retval = zfcp_sysfs_driver_create_files(&zfcp_ccw_driver.driver); | ||
276 | if (retval) | ||
277 | ccw_driver_unregister(&zfcp_ccw_driver); | ||
278 | out: | ||
279 | return retval; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * zfcp_ccw_unregister - ccw unregister function | ||
284 | * | ||
285 | * Unregisters the driver from common i/o layer. Function will be called at | ||
286 | * module unload/system shutdown. | ||
287 | */ | ||
288 | void __exit | ||
289 | zfcp_ccw_unregister(void) | ||
290 | { | ||
291 | zfcp_sysfs_driver_remove_files(&zfcp_ccw_driver.driver); | ||
292 | ccw_driver_unregister(&zfcp_ccw_driver); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * zfcp_ccw_shutdown - gets called on reboot/shutdown | ||
297 | * | ||
298 | * Makes sure that QDIO queues are down when the system gets stopped. | ||
299 | */ | ||
300 | static void | ||
301 | zfcp_ccw_shutdown(struct device *dev) | ||
302 | { | ||
303 | struct zfcp_adapter *adapter; | ||
304 | |||
305 | down(&zfcp_data.config_sema); | ||
306 | adapter = dev_get_drvdata(dev); | ||
307 | zfcp_erp_adapter_shutdown(adapter, 0); | ||
308 | zfcp_erp_wait(adapter); | ||
309 | up(&zfcp_data.config_sema); | ||
310 | } | ||
311 | |||
312 | #undef ZFCP_LOG_AREA | ||