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