Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
553448f6 | 2 | * zfcp device driver |
1da177e4 | 3 | * |
553448f6 | 4 | * Module interface and handling of zfcp data structures. |
1da177e4 | 5 | * |
a2fa0aed | 6 | * Copyright IBM Corporation 2002, 2009 |
1da177e4 LT |
7 | */ |
8 | ||
4a9d2d8b AH |
9 | /* |
10 | * Driver authors: | |
11 | * Martin Peschke (originator of the driver) | |
12 | * Raimund Schroeder | |
13 | * Aron Zeh | |
14 | * Wolfgang Taphorn | |
15 | * Stefan Bader | |
16 | * Heiko Carstens (kernel 2.6 port of the driver) | |
17 | * Andreas Herrmann | |
18 | * Maxim Shchetynin | |
19 | * Volker Sameske | |
20 | * Ralph Wuerthner | |
553448f6 CS |
21 | * Michael Loehr |
22 | * Swen Schillig | |
23 | * Christof Schmitt | |
24 | * Martin Petermann | |
25 | * Sven Schuetz | |
4a9d2d8b AH |
26 | */ |
27 | ||
ecf39d42 CS |
28 | #define KMSG_COMPONENT "zfcp" |
29 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
30 | ||
45633fdc | 31 | #include <linux/miscdevice.h> |
bd43a42b | 32 | #include <linux/seq_file.h> |
1da177e4 | 33 | #include "zfcp_ext.h" |
dbf5dfe9 | 34 | #include "zfcp_fc.h" |
1da177e4 | 35 | |
98df67b3 KS |
36 | #define ZFCP_BUS_ID_SIZE 20 |
37 | ||
4a9d2d8b | 38 | MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com"); |
317e6b65 | 39 | MODULE_DESCRIPTION("FCP HBA driver"); |
1da177e4 LT |
40 | MODULE_LICENSE("GPL"); |
41 | ||
3623ecba CS |
42 | static char *init_device; |
43 | module_param_named(device, init_device, charp, 0400); | |
1da177e4 LT |
44 | MODULE_PARM_DESC(device, "specify initial device"); |
45 | ||
a4623c46 SS |
46 | static struct kmem_cache *zfcp_cache_hw_align(const char *name, |
47 | unsigned long size) | |
48 | { | |
49 | return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL); | |
50 | } | |
51 | ||
ca2d02c2 | 52 | static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter) |
fea9d6c7 | 53 | { |
ca2d02c2 | 54 | int idx; |
fea9d6c7 VS |
55 | |
56 | adapter->req_list = kcalloc(REQUEST_LIST_SIZE, sizeof(struct list_head), | |
57 | GFP_KERNEL); | |
fea9d6c7 VS |
58 | if (!adapter->req_list) |
59 | return -ENOMEM; | |
60 | ||
ca2d02c2 HC |
61 | for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) |
62 | INIT_LIST_HEAD(&adapter->req_list[idx]); | |
fea9d6c7 VS |
63 | return 0; |
64 | } | |
65 | ||
317e6b65 SS |
66 | /** |
67 | * zfcp_reqlist_isempty - is the request list empty | |
68 | * @adapter: pointer to struct zfcp_adapter | |
69 | * | |
70 | * Returns: true if list is empty, false otherwise | |
71 | */ | |
fea9d6c7 VS |
72 | int zfcp_reqlist_isempty(struct zfcp_adapter *adapter) |
73 | { | |
ca2d02c2 | 74 | unsigned int idx; |
fea9d6c7 | 75 | |
ca2d02c2 HC |
76 | for (idx = 0; idx < REQUEST_LIST_SIZE; idx++) |
77 | if (!list_empty(&adapter->req_list[idx])) | |
fea9d6c7 | 78 | return 0; |
fea9d6c7 VS |
79 | return 1; |
80 | } | |
81 | ||
3623ecba | 82 | static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun) |
1da177e4 | 83 | { |
de3dc572 | 84 | struct ccw_device *cdev; |
1da177e4 LT |
85 | struct zfcp_adapter *adapter; |
86 | struct zfcp_port *port; | |
87 | struct zfcp_unit *unit; | |
88 | ||
de3dc572 SS |
89 | cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); |
90 | if (!cdev) | |
c5afd81e CS |
91 | return; |
92 | ||
de3dc572 SS |
93 | if (ccw_device_set_online(cdev)) |
94 | goto out_ccw_device; | |
1da177e4 | 95 | |
de3dc572 | 96 | adapter = zfcp_ccw_adapter_by_cdev(cdev); |
317e6b65 | 97 | if (!adapter) |
de3dc572 | 98 | goto out_ccw_device; |
c5afd81e CS |
99 | |
100 | port = zfcp_get_port_by_wwpn(adapter, wwpn); | |
101 | if (!port) | |
1da177e4 | 102 | goto out_port; |
c5afd81e | 103 | |
3623ecba | 104 | unit = zfcp_unit_enqueue(port, lun); |
317e6b65 | 105 | if (IS_ERR(unit)) |
1da177e4 | 106 | goto out_unit; |
091694a5 | 107 | |
c5afd81e | 108 | zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL); |
1da177e4 | 109 | zfcp_erp_wait(adapter); |
92d5193b | 110 | flush_work(&unit->scsi_work); |
091694a5 | 111 | |
317e6b65 | 112 | out_unit: |
f3450c7b | 113 | put_device(&port->sysfs_device); |
317e6b65 | 114 | out_port: |
de3dc572 SS |
115 | zfcp_ccw_adapter_put(adapter); |
116 | out_ccw_device: | |
117 | put_device(&cdev->dev); | |
1da177e4 LT |
118 | return; |
119 | } | |
120 | ||
3623ecba CS |
121 | static void __init zfcp_init_device_setup(char *devstr) |
122 | { | |
123 | char *token; | |
d10c0858 | 124 | char *str, *str_saved; |
3623ecba CS |
125 | char busid[ZFCP_BUS_ID_SIZE]; |
126 | u64 wwpn, lun; | |
127 | ||
128 | /* duplicate devstr and keep the original for sysfs presentation*/ | |
d10c0858 HC |
129 | str_saved = kmalloc(strlen(devstr) + 1, GFP_KERNEL); |
130 | str = str_saved; | |
3623ecba CS |
131 | if (!str) |
132 | return; | |
133 | ||
134 | strcpy(str, devstr); | |
135 | ||
136 | token = strsep(&str, ","); | |
137 | if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE) | |
138 | goto err_out; | |
139 | strncpy(busid, token, ZFCP_BUS_ID_SIZE); | |
140 | ||
141 | token = strsep(&str, ","); | |
142 | if (!token || strict_strtoull(token, 0, (unsigned long long *) &wwpn)) | |
143 | goto err_out; | |
144 | ||
145 | token = strsep(&str, ","); | |
146 | if (!token || strict_strtoull(token, 0, (unsigned long long *) &lun)) | |
147 | goto err_out; | |
148 | ||
d10c0858 | 149 | kfree(str_saved); |
3623ecba CS |
150 | zfcp_init_device_configure(busid, wwpn, lun); |
151 | return; | |
152 | ||
d10c0858 HC |
153 | err_out: |
154 | kfree(str_saved); | |
3623ecba CS |
155 | pr_err("%s is not a valid SCSI device\n", devstr); |
156 | } | |
157 | ||
317e6b65 | 158 | static int __init zfcp_module_init(void) |
1da177e4 | 159 | { |
dd52e0ea | 160 | int retval = -ENOMEM; |
dd52e0ea | 161 | |
a4623c46 | 162 | zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn", |
dbf5dfe9 | 163 | sizeof(struct zfcp_fc_gpn_ft_req)); |
a4623c46 | 164 | if (!zfcp_data.gpn_ft_cache) |
dd52e0ea | 165 | goto out; |
1da177e4 | 166 | |
a4623c46 SS |
167 | zfcp_data.qtcb_cache = zfcp_cache_hw_align("zfcp_qtcb", |
168 | sizeof(struct fsf_qtcb)); | |
169 | if (!zfcp_data.qtcb_cache) | |
170 | goto out_qtcb_cache; | |
171 | ||
172 | zfcp_data.sr_buffer_cache = zfcp_cache_hw_align("zfcp_sr", | |
173 | sizeof(struct fsf_status_read_buffer)); | |
dd52e0ea HC |
174 | if (!zfcp_data.sr_buffer_cache) |
175 | goto out_sr_cache; | |
176 | ||
a4623c46 | 177 | zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid", |
dbf5dfe9 | 178 | sizeof(struct zfcp_fc_gid_pn)); |
dd52e0ea HC |
179 | if (!zfcp_data.gid_pn_cache) |
180 | goto out_gid_cache; | |
1da177e4 | 181 | |
ee744622 CS |
182 | zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc", |
183 | sizeof(struct zfcp_fc_els_adisc)); | |
184 | if (!zfcp_data.adisc_cache) | |
185 | goto out_adisc_cache; | |
186 | ||
dd52e0ea HC |
187 | zfcp_data.scsi_transport_template = |
188 | fc_attach_transport(&zfcp_transport_functions); | |
189 | if (!zfcp_data.scsi_transport_template) | |
190 | goto out_transport; | |
1da177e4 | 191 | |
1da177e4 | 192 | retval = misc_register(&zfcp_cfdc_misc); |
317e6b65 | 193 | if (retval) { |
ecf39d42 | 194 | pr_err("Registering the misc device zfcp_cfdc failed\n"); |
dd52e0ea | 195 | goto out_misc; |
1da177e4 LT |
196 | } |
197 | ||
c1fad417 | 198 | retval = ccw_driver_register(&zfcp_ccw_driver); |
1da177e4 | 199 | if (retval) { |
ecf39d42 | 200 | pr_err("The zfcp device driver could not register with " |
ff3b24fa | 201 | "the common I/O layer\n"); |
1da177e4 LT |
202 | goto out_ccw_register; |
203 | } | |
204 | ||
3623ecba CS |
205 | if (init_device) |
206 | zfcp_init_device_setup(init_device); | |
207 | return 0; | |
1da177e4 | 208 | |
317e6b65 | 209 | out_ccw_register: |
1da177e4 | 210 | misc_deregister(&zfcp_cfdc_misc); |
317e6b65 | 211 | out_misc: |
dd52e0ea | 212 | fc_release_transport(zfcp_data.scsi_transport_template); |
317e6b65 | 213 | out_transport: |
ee744622 CS |
214 | kmem_cache_destroy(zfcp_data.adisc_cache); |
215 | out_adisc_cache: | |
dd52e0ea | 216 | kmem_cache_destroy(zfcp_data.gid_pn_cache); |
317e6b65 | 217 | out_gid_cache: |
dd52e0ea | 218 | kmem_cache_destroy(zfcp_data.sr_buffer_cache); |
317e6b65 | 219 | out_sr_cache: |
a4623c46 SS |
220 | kmem_cache_destroy(zfcp_data.qtcb_cache); |
221 | out_qtcb_cache: | |
222 | kmem_cache_destroy(zfcp_data.gpn_ft_cache); | |
317e6b65 | 223 | out: |
1da177e4 LT |
224 | return retval; |
225 | } | |
226 | ||
317e6b65 | 227 | module_init(zfcp_module_init); |
1da177e4 | 228 | |
c1fad417 CS |
229 | static void __exit zfcp_module_exit(void) |
230 | { | |
231 | ccw_driver_unregister(&zfcp_ccw_driver); | |
232 | misc_deregister(&zfcp_cfdc_misc); | |
233 | fc_release_transport(zfcp_data.scsi_transport_template); | |
ee744622 | 234 | kmem_cache_destroy(zfcp_data.adisc_cache); |
c1fad417 CS |
235 | kmem_cache_destroy(zfcp_data.gid_pn_cache); |
236 | kmem_cache_destroy(zfcp_data.sr_buffer_cache); | |
237 | kmem_cache_destroy(zfcp_data.qtcb_cache); | |
238 | kmem_cache_destroy(zfcp_data.gpn_ft_cache); | |
239 | } | |
240 | ||
241 | module_exit(zfcp_module_exit); | |
242 | ||
1da177e4 LT |
243 | /** |
244 | * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN | |
245 | * @port: pointer to port to search for unit | |
246 | * @fcp_lun: FCP LUN to search for | |
317e6b65 SS |
247 | * |
248 | * Returns: pointer to zfcp_unit or NULL | |
1da177e4 | 249 | */ |
7ba58c9c | 250 | struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun) |
1da177e4 | 251 | { |
ecf0c772 | 252 | unsigned long flags; |
1da177e4 | 253 | struct zfcp_unit *unit; |
1da177e4 | 254 | |
ecf0c772 SS |
255 | read_lock_irqsave(&port->unit_list_lock, flags); |
256 | list_for_each_entry(unit, &port->unit_list, list) | |
6b183334 SS |
257 | if (unit->fcp_lun == fcp_lun) { |
258 | if (!get_device(&unit->sysfs_device)) | |
259 | unit = NULL; | |
ecf0c772 SS |
260 | read_unlock_irqrestore(&port->unit_list_lock, flags); |
261 | return unit; | |
262 | } | |
263 | read_unlock_irqrestore(&port->unit_list_lock, flags); | |
317e6b65 | 264 | return NULL; |
1da177e4 LT |
265 | } |
266 | ||
267 | /** | |
268 | * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn | |
269 | * @adapter: pointer to adapter to search for port | |
270 | * @wwpn: wwpn to search for | |
317e6b65 SS |
271 | * |
272 | * Returns: pointer to zfcp_port or NULL | |
1da177e4 | 273 | */ |
317e6b65 | 274 | struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, |
7ba58c9c | 275 | u64 wwpn) |
1da177e4 | 276 | { |
ecf0c772 | 277 | unsigned long flags; |
1da177e4 | 278 | struct zfcp_port *port; |
1da177e4 | 279 | |
ecf0c772 SS |
280 | read_lock_irqsave(&adapter->port_list_lock, flags); |
281 | list_for_each_entry(port, &adapter->port_list, list) | |
6b183334 SS |
282 | if (port->wwpn == wwpn) { |
283 | if (!get_device(&port->sysfs_device)) | |
284 | port = NULL; | |
ecf0c772 | 285 | read_unlock_irqrestore(&adapter->port_list_lock, flags); |
317e6b65 | 286 | return port; |
ecf0c772 SS |
287 | } |
288 | read_unlock_irqrestore(&adapter->port_list_lock, flags); | |
317e6b65 | 289 | return NULL; |
1da177e4 LT |
290 | } |
291 | ||
f3450c7b SS |
292 | /** |
293 | * zfcp_unit_release - dequeue unit | |
294 | * @dev: pointer to device | |
295 | * | |
296 | * waits until all work is done on unit and removes it then from the unit->list | |
297 | * of the associated port. | |
298 | */ | |
299 | static void zfcp_unit_release(struct device *dev) | |
60221920 | 300 | { |
f3450c7b SS |
301 | struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, |
302 | sysfs_device); | |
303 | ||
304 | put_device(&unit->port->sysfs_device); | |
305 | kfree(unit); | |
60221920 SS |
306 | } |
307 | ||
1da177e4 LT |
308 | /** |
309 | * zfcp_unit_enqueue - enqueue unit to unit list of a port. | |
310 | * @port: pointer to port where unit is added | |
311 | * @fcp_lun: FCP LUN of unit to be enqueued | |
317e6b65 | 312 | * Returns: pointer to enqueued unit on success, ERR_PTR on error |
1da177e4 LT |
313 | * |
314 | * Sets up some unit internal structures and creates sysfs entry. | |
315 | */ | |
7ba58c9c | 316 | struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) |
1da177e4 | 317 | { |
462b7859 | 318 | struct zfcp_unit *unit; |
f3450c7b SS |
319 | int retval = -ENOMEM; |
320 | ||
321 | get_device(&port->sysfs_device); | |
1da177e4 | 322 | |
ecf0c772 SS |
323 | unit = zfcp_get_unit_by_lun(port, fcp_lun); |
324 | if (unit) { | |
f3450c7b SS |
325 | put_device(&unit->sysfs_device); |
326 | retval = -EEXIST; | |
327 | goto err_out; | |
0fac3f47 | 328 | } |
0fac3f47 | 329 | |
317e6b65 | 330 | unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); |
1da177e4 | 331 | if (!unit) |
f3450c7b | 332 | goto err_out; |
1da177e4 LT |
333 | |
334 | unit->port = port; | |
335 | unit->fcp_lun = fcp_lun; | |
f3450c7b SS |
336 | unit->sysfs_device.parent = &port->sysfs_device; |
337 | unit->sysfs_device.release = zfcp_unit_release; | |
1da177e4 | 338 | |
0fac3f47 CS |
339 | if (dev_set_name(&unit->sysfs_device, "0x%016llx", |
340 | (unsigned long long) fcp_lun)) { | |
341 | kfree(unit); | |
f3450c7b | 342 | goto err_out; |
0fac3f47 | 343 | } |
f3450c7b | 344 | retval = -EINVAL; |
1da177e4 | 345 | |
f3450c7b SS |
346 | INIT_WORK(&unit->scsi_work, zfcp_scsi_scan); |
347 | ||
c9615858 CS |
348 | spin_lock_init(&unit->latencies.lock); |
349 | unit->latencies.write.channel.min = 0xFFFFFFFF; | |
350 | unit->latencies.write.fabric.min = 0xFFFFFFFF; | |
351 | unit->latencies.read.channel.min = 0xFFFFFFFF; | |
352 | unit->latencies.read.fabric.min = 0xFFFFFFFF; | |
353 | unit->latencies.cmd.channel.min = 0xFFFFFFFF; | |
354 | unit->latencies.cmd.fabric.min = 0xFFFFFFFF; | |
355 | ||
f4395b65 SO |
356 | if (device_register(&unit->sysfs_device)) { |
357 | put_device(&unit->sysfs_device); | |
f3450c7b | 358 | goto err_out; |
f4395b65 | 359 | } |
1da177e4 | 360 | |
60221920 | 361 | if (sysfs_create_group(&unit->sysfs_device.kobj, |
f3450c7b SS |
362 | &zfcp_sysfs_unit_attrs)) |
363 | goto err_out_put; | |
1da177e4 | 364 | |
ecf0c772 SS |
365 | write_lock_irq(&port->unit_list_lock); |
366 | list_add_tail(&unit->list, &port->unit_list); | |
367 | write_unlock_irq(&port->unit_list_lock); | |
368 | ||
1da177e4 | 369 | atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); |
317e6b65 | 370 | |
1da177e4 | 371 | return unit; |
ecf0c772 | 372 | |
f3450c7b | 373 | err_out_put: |
1da177e4 | 374 | device_unregister(&unit->sysfs_device); |
f3450c7b SS |
375 | err_out: |
376 | put_device(&port->sysfs_device); | |
377 | return ERR_PTR(retval); | |
1da177e4 LT |
378 | } |
379 | ||
317e6b65 | 380 | static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) |
1da177e4 | 381 | { |
a4623c46 SS |
382 | adapter->pool.erp_req = |
383 | mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); | |
384 | if (!adapter->pool.erp_req) | |
1da177e4 LT |
385 | return -ENOMEM; |
386 | ||
799b76d0 CS |
387 | adapter->pool.gid_pn_req = |
388 | mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); | |
389 | if (!adapter->pool.gid_pn_req) | |
390 | return -ENOMEM; | |
391 | ||
a4623c46 SS |
392 | adapter->pool.scsi_req = |
393 | mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); | |
394 | if (!adapter->pool.scsi_req) | |
1da177e4 LT |
395 | return -ENOMEM; |
396 | ||
a4623c46 SS |
397 | adapter->pool.scsi_abort = |
398 | mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req)); | |
399 | if (!adapter->pool.scsi_abort) | |
1da177e4 LT |
400 | return -ENOMEM; |
401 | ||
a4623c46 | 402 | adapter->pool.status_read_req = |
317e6b65 | 403 | mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM, |
0eaae62a | 404 | sizeof(struct zfcp_fsf_req)); |
a4623c46 SS |
405 | if (!adapter->pool.status_read_req) |
406 | return -ENOMEM; | |
407 | ||
408 | adapter->pool.qtcb_pool = | |
799b76d0 | 409 | mempool_create_slab_pool(4, zfcp_data.qtcb_cache); |
a4623c46 | 410 | if (!adapter->pool.qtcb_pool) |
1da177e4 LT |
411 | return -ENOMEM; |
412 | ||
a4623c46 | 413 | adapter->pool.status_read_data = |
317e6b65 | 414 | mempool_create_slab_pool(FSF_STATUS_READS_RECOM, |
dd52e0ea | 415 | zfcp_data.sr_buffer_cache); |
a4623c46 | 416 | if (!adapter->pool.status_read_data) |
1da177e4 LT |
417 | return -ENOMEM; |
418 | ||
dbf5dfe9 | 419 | adapter->pool.gid_pn = |
317e6b65 | 420 | mempool_create_slab_pool(1, zfcp_data.gid_pn_cache); |
dbf5dfe9 | 421 | if (!adapter->pool.gid_pn) |
1da177e4 LT |
422 | return -ENOMEM; |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
317e6b65 | 427 | static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) |
1da177e4 | 428 | { |
a4623c46 SS |
429 | if (adapter->pool.erp_req) |
430 | mempool_destroy(adapter->pool.erp_req); | |
431 | if (adapter->pool.scsi_req) | |
432 | mempool_destroy(adapter->pool.scsi_req); | |
433 | if (adapter->pool.scsi_abort) | |
434 | mempool_destroy(adapter->pool.scsi_abort); | |
435 | if (adapter->pool.qtcb_pool) | |
436 | mempool_destroy(adapter->pool.qtcb_pool); | |
437 | if (adapter->pool.status_read_req) | |
438 | mempool_destroy(adapter->pool.status_read_req); | |
439 | if (adapter->pool.status_read_data) | |
440 | mempool_destroy(adapter->pool.status_read_data); | |
dbf5dfe9 CS |
441 | if (adapter->pool.gid_pn) |
442 | mempool_destroy(adapter->pool.gid_pn); | |
1da177e4 LT |
443 | } |
444 | ||
317e6b65 SS |
445 | /** |
446 | * zfcp_status_read_refill - refill the long running status_read_requests | |
447 | * @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled | |
448 | * | |
449 | * Returns: 0 on success, 1 otherwise | |
450 | * | |
451 | * if there are 16 or more status_read requests missing an adapter_reopen | |
452 | * is triggered | |
453 | */ | |
d26ab06e SS |
454 | int zfcp_status_read_refill(struct zfcp_adapter *adapter) |
455 | { | |
456 | while (atomic_read(&adapter->stat_miss) > 0) | |
564e1c86 | 457 | if (zfcp_fsf_status_read(adapter->qdio)) { |
7afe29f7 | 458 | if (atomic_read(&adapter->stat_miss) >= 16) { |
5ffd51a5 SS |
459 | zfcp_erp_adapter_reopen(adapter, 0, "axsref1", |
460 | NULL); | |
7afe29f7 SS |
461 | return 1; |
462 | } | |
d26ab06e | 463 | break; |
7afe29f7 SS |
464 | } else |
465 | atomic_dec(&adapter->stat_miss); | |
d26ab06e SS |
466 | return 0; |
467 | } | |
468 | ||
469 | static void _zfcp_status_read_scheduler(struct work_struct *work) | |
470 | { | |
471 | zfcp_status_read_refill(container_of(work, struct zfcp_adapter, | |
472 | stat_work)); | |
473 | } | |
474 | ||
bd43a42b CS |
475 | static void zfcp_print_sl(struct seq_file *m, struct service_level *sl) |
476 | { | |
477 | struct zfcp_adapter *adapter = | |
478 | container_of(sl, struct zfcp_adapter, service_level); | |
479 | ||
480 | seq_printf(m, "zfcp: %s microcode level %x\n", | |
481 | dev_name(&adapter->ccw_device->dev), | |
482 | adapter->fsf_lic_version); | |
483 | } | |
484 | ||
4544683a SS |
485 | static int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter) |
486 | { | |
487 | char name[TASK_COMM_LEN]; | |
488 | ||
489 | snprintf(name, sizeof(name), "zfcp_q_%s", | |
490 | dev_name(&adapter->ccw_device->dev)); | |
491 | adapter->work_queue = create_singlethread_workqueue(name); | |
492 | ||
493 | if (adapter->work_queue) | |
494 | return 0; | |
495 | return -ENOMEM; | |
496 | } | |
497 | ||
498 | static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter) | |
499 | { | |
500 | if (adapter->work_queue) | |
501 | destroy_workqueue(adapter->work_queue); | |
502 | adapter->work_queue = NULL; | |
503 | ||
504 | } | |
505 | ||
317e6b65 SS |
506 | /** |
507 | * zfcp_adapter_enqueue - enqueue a new adapter to the list | |
508 | * @ccw_device: pointer to the struct cc_device | |
509 | * | |
de3dc572 | 510 | * Returns: struct zfcp_adapter* |
1da177e4 LT |
511 | * Enqueues an adapter at the end of the adapter list in the driver data. |
512 | * All adapter internal structures are set up. | |
513 | * Proc-fs entries are also created. | |
1da177e4 | 514 | */ |
de3dc572 | 515 | struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) |
1da177e4 | 516 | { |
1da177e4 LT |
517 | struct zfcp_adapter *adapter; |
518 | ||
f3450c7b | 519 | if (!get_device(&ccw_device->dev)) |
de3dc572 | 520 | return ERR_PTR(-ENODEV); |
1da177e4 | 521 | |
317e6b65 | 522 | adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL); |
f3450c7b SS |
523 | if (!adapter) { |
524 | put_device(&ccw_device->dev); | |
de3dc572 | 525 | return ERR_PTR(-ENOMEM); |
f3450c7b SS |
526 | } |
527 | ||
528 | kref_init(&adapter->ref); | |
1da177e4 LT |
529 | |
530 | ccw_device->handler = NULL; | |
1da177e4 | 531 | adapter->ccw_device = ccw_device; |
f3450c7b SS |
532 | |
533 | INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler); | |
9eae07ef | 534 | INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports); |
1da177e4 | 535 | |
d5a282a1 | 536 | if (zfcp_qdio_setup(adapter)) |
f3450c7b | 537 | goto failed; |
1da177e4 | 538 | |
00bab910 | 539 | if (zfcp_allocate_low_mem_buffers(adapter)) |
f3450c7b | 540 | goto failed; |
1da177e4 | 541 | |
317e6b65 | 542 | if (zfcp_reqlist_alloc(adapter)) |
f3450c7b | 543 | goto failed; |
317e6b65 | 544 | |
5771710b | 545 | if (zfcp_dbf_adapter_register(adapter)) |
f3450c7b | 546 | goto failed; |
317e6b65 | 547 | |
4544683a | 548 | if (zfcp_setup_adapter_work_queue(adapter)) |
f3450c7b | 549 | goto failed; |
4544683a | 550 | |
d5a282a1 | 551 | if (zfcp_fc_gs_setup(adapter)) |
f3450c7b | 552 | goto failed; |
d5a282a1 | 553 | |
ecf0c772 SS |
554 | rwlock_init(&adapter->port_list_lock); |
555 | INIT_LIST_HEAD(&adapter->port_list); | |
556 | ||
347c6a96 | 557 | init_waitqueue_head(&adapter->erp_ready_wq); |
317e6b65 | 558 | init_waitqueue_head(&adapter->erp_done_wqh); |
1da177e4 | 559 | |
317e6b65 SS |
560 | INIT_LIST_HEAD(&adapter->erp_ready_head); |
561 | INIT_LIST_HEAD(&adapter->erp_running_head); | |
1da177e4 | 562 | |
fea9d6c7 | 563 | spin_lock_init(&adapter->req_list_lock); |
c48a29d0 | 564 | |
c48a29d0 | 565 | rwlock_init(&adapter->erp_lock); |
1da177e4 LT |
566 | rwlock_init(&adapter->abort_lock); |
567 | ||
143bb6bf | 568 | if (zfcp_erp_thread_setup(adapter)) |
f3450c7b | 569 | goto failed; |
1da177e4 | 570 | |
bd43a42b CS |
571 | adapter->service_level.seq_print = zfcp_print_sl; |
572 | ||
1da177e4 LT |
573 | dev_set_drvdata(&ccw_device->dev, adapter); |
574 | ||
60221920 SS |
575 | if (sysfs_create_group(&ccw_device->dev.kobj, |
576 | &zfcp_sysfs_adapter_attrs)) | |
f3450c7b | 577 | goto failed; |
1da177e4 | 578 | |
1d3aab08 | 579 | if (!zfcp_adapter_scsi_register(adapter)) |
de3dc572 | 580 | return adapter; |
1da177e4 | 581 | |
f3450c7b | 582 | failed: |
de3dc572 SS |
583 | zfcp_adapter_unregister(adapter); |
584 | return ERR_PTR(-ENOMEM); | |
585 | } | |
586 | ||
587 | void zfcp_adapter_unregister(struct zfcp_adapter *adapter) | |
588 | { | |
589 | struct ccw_device *cdev = adapter->ccw_device; | |
590 | ||
591 | cancel_work_sync(&adapter->scan_work); | |
592 | cancel_work_sync(&adapter->stat_work); | |
593 | zfcp_destroy_adapter_work_queue(adapter); | |
594 | ||
595 | zfcp_fc_wka_ports_force_offline(adapter->gs); | |
596 | zfcp_adapter_scsi_unregister(adapter); | |
597 | sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs); | |
598 | ||
599 | zfcp_erp_thread_kill(adapter); | |
600 | zfcp_dbf_adapter_unregister(adapter->dbf); | |
601 | zfcp_qdio_destroy(adapter->qdio); | |
602 | ||
603 | zfcp_ccw_adapter_put(adapter); /* final put to release */ | |
1da177e4 LT |
604 | } |
605 | ||
317e6b65 | 606 | /** |
f3450c7b SS |
607 | * zfcp_adapter_release - remove the adapter from the resource list |
608 | * @ref: pointer to struct kref | |
1da177e4 | 609 | * locks: adapter list write lock is assumed to be held by caller |
1da177e4 | 610 | */ |
f3450c7b | 611 | void zfcp_adapter_release(struct kref *ref) |
1da177e4 | 612 | { |
f3450c7b SS |
613 | struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter, |
614 | ref); | |
de3dc572 | 615 | struct ccw_device *cdev = adapter->ccw_device; |
1da177e4 | 616 | |
f3450c7b | 617 | dev_set_drvdata(&adapter->ccw_device->dev, NULL); |
d5a282a1 | 618 | zfcp_fc_gs_destroy(adapter); |
1da177e4 | 619 | zfcp_free_low_mem_buffers(adapter); |
317e6b65 | 620 | kfree(adapter->req_list); |
f6cd94b1 AH |
621 | kfree(adapter->fc_stats); |
622 | kfree(adapter->stats_reset_data); | |
1da177e4 | 623 | kfree(adapter); |
de3dc572 | 624 | put_device(&cdev->dev); |
f3450c7b SS |
625 | } |
626 | ||
627 | /** | |
628 | * zfcp_device_unregister - remove port, unit from system | |
629 | * @dev: reference to device which is to be removed | |
630 | * @grp: related reference to attribute group | |
631 | * | |
632 | * Helper function to unregister port, unit from system | |
633 | */ | |
634 | void zfcp_device_unregister(struct device *dev, | |
635 | const struct attribute_group *grp) | |
636 | { | |
637 | sysfs_remove_group(&dev->kobj, grp); | |
638 | device_unregister(dev); | |
1da177e4 LT |
639 | } |
640 | ||
f3450c7b | 641 | static void zfcp_port_release(struct device *dev) |
60221920 | 642 | { |
f3450c7b SS |
643 | struct zfcp_port *port = container_of(dev, struct zfcp_port, |
644 | sysfs_device); | |
645 | ||
de3dc572 | 646 | zfcp_ccw_adapter_put(port->adapter); |
f3450c7b | 647 | kfree(port); |
60221920 SS |
648 | } |
649 | ||
1da177e4 LT |
650 | /** |
651 | * zfcp_port_enqueue - enqueue port to port list of adapter | |
652 | * @adapter: adapter where remote port is added | |
653 | * @wwpn: WWPN of the remote port to be enqueued | |
654 | * @status: initial status for the port | |
655 | * @d_id: destination id of the remote port to be enqueued | |
317e6b65 | 656 | * Returns: pointer to enqueued port on success, ERR_PTR on error |
1da177e4 LT |
657 | * |
658 | * All port internal structures are set up and the sysfs entry is generated. | |
659 | * d_id is used to enqueue ports with a well known address like the Directory | |
660 | * Service for nameserver lookup. | |
661 | */ | |
7ba58c9c | 662 | struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, |
317e6b65 | 663 | u32 status, u32 d_id) |
1da177e4 | 664 | { |
3859f6a2 | 665 | struct zfcp_port *port; |
f3450c7b SS |
666 | int retval = -ENOMEM; |
667 | ||
668 | kref_get(&adapter->ref); | |
0fac3f47 | 669 | |
ecf0c772 SS |
670 | port = zfcp_get_port_by_wwpn(adapter, wwpn); |
671 | if (port) { | |
f3450c7b SS |
672 | put_device(&port->sysfs_device); |
673 | retval = -EEXIST; | |
674 | goto err_out; | |
0fac3f47 | 675 | } |
1da177e4 | 676 | |
317e6b65 | 677 | port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); |
1da177e4 | 678 | if (!port) |
f3450c7b | 679 | goto err_out; |
1da177e4 | 680 | |
ecf0c772 SS |
681 | rwlock_init(&port->unit_list_lock); |
682 | INIT_LIST_HEAD(&port->unit_list); | |
683 | ||
799b76d0 | 684 | INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup); |
8fdf30d5 | 685 | INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work); |
a2fa0aed | 686 | INIT_WORK(&port->rport_work, zfcp_scsi_rport_work); |
1da177e4 LT |
687 | |
688 | port->adapter = adapter; | |
317e6b65 SS |
689 | port->d_id = d_id; |
690 | port->wwpn = wwpn; | |
a2fa0aed | 691 | port->rport_task = RPORT_NONE; |
f3450c7b SS |
692 | port->sysfs_device.parent = &adapter->ccw_device->dev; |
693 | port->sysfs_device.release = zfcp_port_release; | |
1da177e4 | 694 | |
0fac3f47 CS |
695 | if (dev_set_name(&port->sysfs_device, "0x%016llx", |
696 | (unsigned long long)wwpn)) { | |
697 | kfree(port); | |
f3450c7b | 698 | goto err_out; |
0fac3f47 | 699 | } |
f3450c7b | 700 | retval = -EINVAL; |
1da177e4 | 701 | |
f4395b65 SO |
702 | if (device_register(&port->sysfs_device)) { |
703 | put_device(&port->sysfs_device); | |
f3450c7b | 704 | goto err_out; |
f4395b65 | 705 | } |
1da177e4 | 706 | |
0fac3f47 | 707 | if (sysfs_create_group(&port->sysfs_device.kobj, |
f3450c7b SS |
708 | &zfcp_sysfs_port_attrs)) |
709 | goto err_out_put; | |
1da177e4 | 710 | |
ecf0c772 SS |
711 | write_lock_irq(&adapter->port_list_lock); |
712 | list_add_tail(&port->list, &adapter->port_list); | |
713 | write_unlock_irq(&adapter->port_list_lock); | |
714 | ||
6b183334 | 715 | atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status); |
317e6b65 | 716 | |
1da177e4 | 717 | return port; |
1da177e4 | 718 | |
f3450c7b | 719 | err_out_put: |
1da177e4 | 720 | device_unregister(&port->sysfs_device); |
f3450c7b | 721 | err_out: |
de3dc572 | 722 | zfcp_ccw_adapter_put(adapter); |
f3450c7b | 723 | return ERR_PTR(retval); |
1da177e4 LT |
724 | } |
725 | ||
317e6b65 SS |
726 | /** |
727 | * zfcp_sg_free_table - free memory used by scatterlists | |
728 | * @sg: pointer to scatterlist | |
729 | * @count: number of scatterlist which are to be free'ed | |
730 | * the scatterlist are expected to reference pages always | |
731 | */ | |
45633fdc CS |
732 | void zfcp_sg_free_table(struct scatterlist *sg, int count) |
733 | { | |
734 | int i; | |
735 | ||
736 | for (i = 0; i < count; i++, sg++) | |
737 | if (sg) | |
738 | free_page((unsigned long) sg_virt(sg)); | |
739 | else | |
740 | break; | |
741 | } | |
742 | ||
317e6b65 SS |
743 | /** |
744 | * zfcp_sg_setup_table - init scatterlist and allocate, assign buffers | |
745 | * @sg: pointer to struct scatterlist | |
746 | * @count: number of scatterlists which should be assigned with buffers | |
747 | * of size page | |
748 | * | |
749 | * Returns: 0 on success, -ENOMEM otherwise | |
750 | */ | |
45633fdc CS |
751 | int zfcp_sg_setup_table(struct scatterlist *sg, int count) |
752 | { | |
753 | void *addr; | |
754 | int i; | |
755 | ||
756 | sg_init_table(sg, count); | |
757 | for (i = 0; i < count; i++, sg++) { | |
758 | addr = (void *) get_zeroed_page(GFP_KERNEL); | |
759 | if (!addr) { | |
760 | zfcp_sg_free_table(sg, i); | |
761 | return -ENOMEM; | |
762 | } | |
763 | sg_set_buf(sg, addr, PAGE_SIZE); | |
764 | } | |
765 | return 0; | |
766 | } |