]>
Commit | Line | Data |
---|---|---|
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 |