aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorChristof Schmitt <christof.schmitt@de.ibm.com>2010-09-08 08:39:52 -0400
committerJames Bottomley <James.Bottomley@suse.de>2010-09-16 22:54:15 -0400
commit1daa4eb50fa5cd4c8f9c55452606e786fd42053b (patch)
tree2c299aacb948264e716ca6651c2f4ef234fc310a /drivers/s390
parent57c237731b92fadc7d44824276313ec330b1989b (diff)
[SCSI] zfcp: Move code for managing zfcp_unit devices to new file
Move the code for managing zfcp_unit devices to the new file zfcp_unit.c. This is in preparation for the change that zfcp_unit will only track the LUNs configured via unit_add, other data will be moved from zfcp_unit to the new struct zfcp_scsi_dev. Reviewed-by: Swen Schillig <swen@vnet.ibm.com> Signed-off-by: Christof Schmitt <christof.schmitt@de.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/scsi/Makefile5
-rw-r--r--drivers/s390/scsi/zfcp_aux.c122
-rw-r--r--drivers/s390/scsi/zfcp_ext.h13
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c41
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c56
-rw-r--r--drivers/s390/scsi/zfcp_unit.c255
6 files changed, 279 insertions, 213 deletions
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
index cb301cc6178c..c454ffebb63e 100644
--- a/drivers/s390/scsi/Makefile
+++ b/drivers/s390/scsi/Makefile
@@ -2,7 +2,8 @@
2# Makefile for the S/390 specific device drivers 2# Makefile for the S/390 specific device drivers
3# 3#
4 4
5zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \ 5zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_cfdc.o zfcp_dbf.o zfcp_erp.o \
6 zfcp_fsf.o zfcp_dbf.o zfcp_sysfs.o zfcp_fc.o zfcp_cfdc.o 6 zfcp_fc.o zfcp_fsf.o zfcp_qdio.o zfcp_scsi.o zfcp_sysfs.o \
7 zfcp_unit.o
7 8
8obj-$(CONFIG_ZFCP) += zfcp.o 9obj-$(CONFIG_ZFCP) += zfcp.o
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 5c4b874591ea..044fb22718d2 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -56,7 +56,6 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
56 struct ccw_device *cdev; 56 struct ccw_device *cdev;
57 struct zfcp_adapter *adapter; 57 struct zfcp_adapter *adapter;
58 struct zfcp_port *port; 58 struct zfcp_port *port;
59 struct zfcp_unit *unit;
60 59
61 cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); 60 cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
62 if (!cdev) 61 if (!cdev)
@@ -72,18 +71,11 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
72 port = zfcp_get_port_by_wwpn(adapter, wwpn); 71 port = zfcp_get_port_by_wwpn(adapter, wwpn);
73 if (!port) 72 if (!port)
74 goto out_port; 73 goto out_port;
75
76 flush_work(&port->rport_work); 74 flush_work(&port->rport_work);
77 unit = zfcp_unit_enqueue(port, lun);
78 if (IS_ERR(unit))
79 goto out_unit;
80
81 zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL);
82 zfcp_erp_wait(adapter);
83 zfcp_scsi_scan(unit);
84 75
85out_unit: 76 zfcp_unit_add(port, lun);
86 put_device(&port->dev); 77 put_device(&port->dev);
78
87out_port: 79out_port:
88 zfcp_ccw_adapter_put(adapter); 80 zfcp_ccw_adapter_put(adapter);
89out_ccw_device: 81out_ccw_device:
@@ -215,30 +207,6 @@ static void __exit zfcp_module_exit(void)
215module_exit(zfcp_module_exit); 207module_exit(zfcp_module_exit);
216 208
217/** 209/**
218 * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
219 * @port: pointer to port to search for unit
220 * @fcp_lun: FCP LUN to search for
221 *
222 * Returns: pointer to zfcp_unit or NULL
223 */
224struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
225{
226 unsigned long flags;
227 struct zfcp_unit *unit;
228
229 read_lock_irqsave(&port->unit_list_lock, flags);
230 list_for_each_entry(unit, &port->unit_list, list)
231 if (unit->fcp_lun == fcp_lun) {
232 if (!get_device(&unit->dev))
233 unit = NULL;
234 read_unlock_irqrestore(&port->unit_list_lock, flags);
235 return unit;
236 }
237 read_unlock_irqrestore(&port->unit_list_lock, flags);
238 return NULL;
239}
240
241/**
242 * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn 210 * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn
243 * @adapter: pointer to adapter to search for port 211 * @adapter: pointer to adapter to search for port
244 * @wwpn: wwpn to search for 212 * @wwpn: wwpn to search for
@@ -263,92 +231,6 @@ struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
263 return NULL; 231 return NULL;
264} 232}
265 233
266/**
267 * zfcp_unit_release - dequeue unit
268 * @dev: pointer to device
269 *
270 * waits until all work is done on unit and removes it then from the unit->list
271 * of the associated port.
272 */
273static void zfcp_unit_release(struct device *dev)
274{
275 struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
276
277 put_device(&unit->port->dev);
278 kfree(unit);
279}
280
281/**
282 * zfcp_unit_enqueue - enqueue unit to unit list of a port.
283 * @port: pointer to port where unit is added
284 * @fcp_lun: FCP LUN of unit to be enqueued
285 * Returns: pointer to enqueued unit on success, ERR_PTR on error
286 *
287 * Sets up some unit internal structures and creates sysfs entry.
288 */
289struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
290{
291 struct zfcp_unit *unit;
292 int retval = -ENOMEM;
293
294 get_device(&port->dev);
295
296 unit = zfcp_get_unit_by_lun(port, fcp_lun);
297 if (unit) {
298 put_device(&unit->dev);
299 retval = -EEXIST;
300 goto err_out;
301 }
302
303 unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
304 if (!unit)
305 goto err_out;
306
307 unit->port = port;
308 unit->fcp_lun = fcp_lun;
309 unit->dev.parent = &port->dev;
310 unit->dev.release = zfcp_unit_release;
311
312 if (dev_set_name(&unit->dev, "0x%016llx",
313 (unsigned long long) fcp_lun)) {
314 kfree(unit);
315 goto err_out;
316 }
317 retval = -EINVAL;
318
319 INIT_WORK(&unit->scsi_work, zfcp_scsi_scan_work);
320
321 spin_lock_init(&unit->latencies.lock);
322 unit->latencies.write.channel.min = 0xFFFFFFFF;
323 unit->latencies.write.fabric.min = 0xFFFFFFFF;
324 unit->latencies.read.channel.min = 0xFFFFFFFF;
325 unit->latencies.read.fabric.min = 0xFFFFFFFF;
326 unit->latencies.cmd.channel.min = 0xFFFFFFFF;
327 unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
328
329 if (device_register(&unit->dev)) {
330 put_device(&unit->dev);
331 goto err_out;
332 }
333
334 if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs))
335 goto err_out_put;
336
337 write_lock_irq(&port->unit_list_lock);
338 list_add_tail(&unit->list, &port->unit_list);
339 write_unlock_irq(&port->unit_list_lock);
340
341 atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
342
343 return unit;
344
345err_out_put:
346 device_unregister(&unit->dev);
347err_out:
348 put_device(&port->dev);
349 return ERR_PTR(retval);
350}
351
352static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) 234static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
353{ 235{
354 adapter->pool.erp_req = 236 adapter->pool.erp_req =
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 3b93239c6f69..5c3966eaca51 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -15,12 +15,10 @@
15#include "zfcp_fc.h" 15#include "zfcp_fc.h"
16 16
17/* zfcp_aux.c */ 17/* zfcp_aux.c */
18extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, u64);
19extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64); 18extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64);
20extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *); 19extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
21extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32, 20extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
22 u32); 21 u32);
23extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64);
24extern void zfcp_sg_free_table(struct scatterlist *, int); 22extern void zfcp_sg_free_table(struct scatterlist *, int);
25extern int zfcp_sg_setup_table(struct scatterlist *, int); 23extern int zfcp_sg_setup_table(struct scatterlist *, int);
26extern void zfcp_device_unregister(struct device *, 24extern void zfcp_device_unregister(struct device *,
@@ -163,8 +161,6 @@ extern void zfcp_scsi_rport_work(struct work_struct *);
163extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *); 161extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
164extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); 162extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
165extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); 163extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);
166extern void zfcp_scsi_scan(struct zfcp_unit *);
167extern void zfcp_scsi_scan_work(struct work_struct *);
168extern void zfcp_scsi_set_prot(struct zfcp_adapter *); 164extern void zfcp_scsi_set_prot(struct zfcp_adapter *);
169extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int); 165extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int);
170 166
@@ -175,4 +171,13 @@ extern struct attribute_group zfcp_sysfs_port_attrs;
175extern struct device_attribute *zfcp_sysfs_sdev_attrs[]; 171extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
176extern struct device_attribute *zfcp_sysfs_shost_attrs[]; 172extern struct device_attribute *zfcp_sysfs_shost_attrs[];
177 173
174/* zfcp_unit.c */
175extern int zfcp_unit_add(struct zfcp_port *, u64);
176extern int zfcp_unit_remove(struct zfcp_port *, u64);
177extern struct zfcp_unit *zfcp_unit_find(struct zfcp_port *, u64);
178extern struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit);
179extern void zfcp_unit_scsi_scan(struct zfcp_unit *);
180extern void zfcp_unit_queue_scsi_scan(struct zfcp_port *);
181extern unsigned int zfcp_unit_sdev_status(struct zfcp_unit *);
182
178#endif /* ZFCP_EXT_H */ 183#endif /* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index cb000c9833bb..03837797c45e 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -144,7 +144,7 @@ static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
144 list_for_each_entry(port, &adapter->port_list, list) { 144 list_for_each_entry(port, &adapter->port_list, list) {
145 if (!port->rport || (id != port->rport->scsi_target_id)) 145 if (!port->rport || (id != port->rport->scsi_target_id))
146 continue; 146 continue;
147 unit = zfcp_get_unit_by_lun(port, lun); 147 unit = zfcp_unit_find(port, lun);
148 if (unit) 148 if (unit)
149 break; 149 break;
150 } 150 }
@@ -534,20 +534,6 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
534 } 534 }
535} 535}
536 536
537static void zfcp_scsi_queue_unit_register(struct zfcp_port *port)
538{
539 struct zfcp_unit *unit;
540
541 read_lock_irq(&port->unit_list_lock);
542 list_for_each_entry(unit, &port->unit_list, list) {
543 get_device(&unit->dev);
544 if (scsi_queue_work(port->adapter->scsi_host,
545 &unit->scsi_work) <= 0)
546 put_device(&unit->dev);
547 }
548 read_unlock_irq(&port->unit_list_lock);
549}
550
551static void zfcp_scsi_rport_register(struct zfcp_port *port) 537static void zfcp_scsi_rport_register(struct zfcp_port *port)
552{ 538{
553 struct fc_rport_identifiers ids; 539 struct fc_rport_identifiers ids;
@@ -574,7 +560,7 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
574 port->rport = rport; 560 port->rport = rport;
575 port->starget_id = rport->scsi_target_id; 561 port->starget_id = rport->scsi_target_id;
576 562
577 zfcp_scsi_queue_unit_register(port); 563 zfcp_unit_queue_scsi_scan(port);
578} 564}
579 565
580static void zfcp_scsi_rport_block(struct zfcp_port *port) 566static void zfcp_scsi_rport_block(struct zfcp_port *port)
@@ -638,29 +624,6 @@ void zfcp_scsi_rport_work(struct work_struct *work)
638} 624}
639 625
640/** 626/**
641 * zfcp_scsi_scan - Register LUN with SCSI midlayer
642 * @unit: The LUN/unit to register
643 */
644void zfcp_scsi_scan(struct zfcp_unit *unit)
645{
646 struct fc_rport *rport = unit->port->rport;
647
648 if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
649 scsi_scan_target(&rport->dev, 0, rport->scsi_target_id,
650 scsilun_to_int((struct scsi_lun *)
651 &unit->fcp_lun), 0);
652}
653
654void zfcp_scsi_scan_work(struct work_struct *work)
655{
656 struct zfcp_unit *unit = container_of(work, struct zfcp_unit,
657 scsi_work);
658
659 zfcp_scsi_scan(unit);
660 put_device(&unit->dev);
661}
662
663/**
664 * zfcp_scsi_set_prot - Configure DIF/DIX support in scsi_host 627 * zfcp_scsi_set_prot - Configure DIF/DIX support in scsi_host
665 * @adapter: The adapter where to configure DIF/DIX for the SCSI host 628 * @adapter: The adapter where to configure DIF/DIX for the SCSI host
666 */ 629 */
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index b4561c86e230..56c46e09d2d4 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -257,28 +257,15 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
257 const char *buf, size_t count) 257 const char *buf, size_t count)
258{ 258{
259 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); 259 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
260 struct zfcp_unit *unit;
261 u64 fcp_lun; 260 u64 fcp_lun;
262 int retval = -EINVAL;
263
264 if (!(port && get_device(&port->dev)))
265 return -EBUSY;
266 261
267 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) 262 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
268 goto out; 263 return -EINVAL;
269 264
270 unit = zfcp_unit_enqueue(port, fcp_lun); 265 if (zfcp_unit_add(port, fcp_lun))
271 if (IS_ERR(unit)) 266 return -EINVAL;
272 goto out;
273 else
274 retval = 0;
275 267
276 zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL); 268 return count;
277 zfcp_erp_wait(unit->port->adapter);
278 zfcp_scsi_scan(unit);
279out:
280 put_device(&port->dev);
281 return retval ? retval : (ssize_t) count;
282} 269}
283static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store); 270static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
284 271
@@ -287,42 +274,15 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
287 const char *buf, size_t count) 274 const char *buf, size_t count)
288{ 275{
289 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); 276 struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
290 struct zfcp_unit *unit;
291 u64 fcp_lun; 277 u64 fcp_lun;
292 int retval = -EINVAL;
293 struct scsi_device *sdev;
294
295 if (!(port && get_device(&port->dev)))
296 return -EBUSY;
297 278
298 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) 279 if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
299 goto out; 280 return -EINVAL;
300
301 unit = zfcp_get_unit_by_lun(port, fcp_lun);
302 if (!unit)
303 goto out;
304 else
305 retval = 0;
306 281
307 sdev = scsi_device_lookup(port->adapter->scsi_host, 0, 282 if (zfcp_unit_remove(port, fcp_lun))
308 port->starget_id, 283 return -EINVAL;
309 scsilun_to_int((struct scsi_lun *)&fcp_lun));
310 if (sdev) {
311 scsi_remove_device(sdev);
312 scsi_device_put(sdev);
313 }
314 284
315 write_lock_irq(&port->unit_list_lock); 285 return count;
316 list_del(&unit->list);
317 write_unlock_irq(&port->unit_list_lock);
318
319 put_device(&unit->dev);
320
321 zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
322 zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs);
323out:
324 put_device(&port->dev);
325 return retval ? retval : (ssize_t) count;
326} 286}
327static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store); 287static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
328 288
diff --git a/drivers/s390/scsi/zfcp_unit.c b/drivers/s390/scsi/zfcp_unit.c
new file mode 100644
index 000000000000..e210c41ee389
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_unit.c
@@ -0,0 +1,255 @@
1/*
2 * zfcp device driver
3 *
4 * Tracking of manually configured LUNs and helper functions to
5 * register the LUNs with the SCSI midlayer.
6 *
7 * Copyright IBM Corporation 2010
8 */
9
10#include "zfcp_def.h"
11#include "zfcp_ext.h"
12
13/**
14 * zfcp_unit_scsi_scan - Register LUN with SCSI midlayer
15 * @unit: The zfcp LUN/unit to register
16 *
17 * When the SCSI midlayer is not allowed to automatically scan and
18 * attach SCSI devices, zfcp has to register the single devices with
19 * the SCSI midlayer.
20 */
21void zfcp_unit_scsi_scan(struct zfcp_unit *unit)
22{
23 struct fc_rport *rport = unit->port->rport;
24 unsigned int lun;
25
26 lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun);
27
28 if (rport && rport->port_state == FC_PORTSTATE_ONLINE)
29 scsi_scan_target(&rport->dev, 0, rport->scsi_target_id, lun, 1);
30}
31
32static void zfcp_unit_scsi_scan_work(struct work_struct *work)
33{
34 struct zfcp_unit *unit = container_of(work, struct zfcp_unit,
35 scsi_work);
36
37 zfcp_unit_scsi_scan(unit);
38 put_device(&unit->dev);
39}
40
41/**
42 * zfcp_unit_queue_scsi_scan - Register configured units on port
43 * @port: The zfcp_port where to register units
44 *
45 * After opening a port, all units configured on this port have to be
46 * registered with the SCSI midlayer. This function should be called
47 * after calling fc_remote_port_add, so that the fc_rport is already
48 * ONLINE and the call to scsi_scan_target runs the same way as the
49 * call in the FC transport class.
50 */
51void zfcp_unit_queue_scsi_scan(struct zfcp_port *port)
52{
53 struct zfcp_unit *unit;
54
55 read_lock_irq(&port->unit_list_lock);
56 list_for_each_entry(unit, &port->unit_list, list) {
57 get_device(&unit->dev);
58 if (scsi_queue_work(port->adapter->scsi_host,
59 &unit->scsi_work) <= 0)
60 put_device(&unit->dev);
61 }
62 read_unlock_irq(&port->unit_list_lock);
63}
64
65static struct zfcp_unit *_zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun)
66{
67 struct zfcp_unit *unit;
68
69 list_for_each_entry(unit, &port->unit_list, list)
70 if (unit->fcp_lun == fcp_lun) {
71 get_device(&unit->dev);
72 return unit;
73 }
74
75 return NULL;
76}
77
78/**
79 * zfcp_unit_find - Find and return zfcp_unit with specified FCP LUN
80 * @port: zfcp_port where to look for the unit
81 * @fcp_lun: 64 Bit FCP LUN used to identify the zfcp_unit
82 *
83 * If zfcp_unit is found, a reference is acquired that has to be
84 * released later.
85 *
86 * Returns: Pointer to the zfcp_unit, or NULL if there is no zfcp_unit
87 * with the specified FCP LUN.
88 */
89struct zfcp_unit *zfcp_unit_find(struct zfcp_port *port, u64 fcp_lun)
90{
91 struct zfcp_unit *unit;
92
93 read_lock_irq(&port->unit_list_lock);
94 unit = _zfcp_unit_find(port, fcp_lun);
95 read_unlock_irq(&port->unit_list_lock);
96 return unit;
97}
98
99/**
100 * zfcp_unit_release - Drop reference to zfcp_port and free memory of zfcp_unit.
101 * @dev: pointer to device in zfcp_unit
102 */
103static void zfcp_unit_release(struct device *dev)
104{
105 struct zfcp_unit *unit = container_of(dev, struct zfcp_unit, dev);
106
107 put_device(&unit->port->dev);
108 kfree(unit);
109}
110
111/**
112 * zfcp_unit_enqueue - enqueue unit to unit list of a port.
113 * @port: pointer to port where unit is added
114 * @fcp_lun: FCP LUN of unit to be enqueued
115 * Returns: 0 success
116 *
117 * Sets up some unit internal structures and creates sysfs entry.
118 */
119int zfcp_unit_add(struct zfcp_port *port, u64 fcp_lun)
120{
121 struct zfcp_unit *unit;
122
123 unit = zfcp_unit_find(port, fcp_lun);
124 if (unit) {
125 put_device(&unit->dev);
126 return -EEXIST;
127 }
128
129 unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
130 if (!unit)
131 return -ENOMEM;
132
133 unit->port = port;
134 unit->fcp_lun = fcp_lun;
135 unit->dev.parent = &port->dev;
136 unit->dev.release = zfcp_unit_release;
137 unit->latencies.write.channel.min = 0xFFFFFFFF;
138 unit->latencies.write.fabric.min = 0xFFFFFFFF;
139 unit->latencies.read.channel.min = 0xFFFFFFFF;
140 unit->latencies.read.fabric.min = 0xFFFFFFFF;
141 unit->latencies.cmd.channel.min = 0xFFFFFFFF;
142 unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
143 INIT_WORK(&unit->scsi_work, zfcp_unit_scsi_scan_work);
144 spin_lock_init(&unit->latencies.lock);
145
146 if (dev_set_name(&unit->dev, "0x%016llx",
147 (unsigned long long) fcp_lun)) {
148 kfree(unit);
149 return -ENOMEM;
150 }
151
152 if (device_register(&unit->dev)) {
153 put_device(&unit->dev);
154 return -ENOMEM;
155 }
156
157 if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) {
158 device_unregister(&unit->dev);
159 return -EINVAL;
160 }
161
162 get_device(&port->dev);
163
164 write_lock_irq(&port->unit_list_lock);
165 list_add_tail(&unit->list, &port->unit_list);
166 write_unlock_irq(&port->unit_list_lock);
167
168 atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
169 zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL);
170 zfcp_erp_wait(unit->port->adapter);
171 zfcp_unit_scsi_scan(unit);
172
173 return 0;
174}
175
176/**
177 * zfcp_unit_sdev - Return SCSI device for zfcp_unit
178 * @unit: The zfcp_unit where to get the SCSI device for
179 *
180 * Returns: scsi_device pointer on success, NULL if there is no SCSI
181 * device for this zfcp_unit
182 *
183 * On success, the caller also holds a reference to the SCSI device
184 * that must be released with scsi_device_put.
185 */
186struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit)
187{
188 struct Scsi_Host *shost;
189 struct zfcp_port *port;
190 unsigned int lun;
191
192 lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun);
193 port = unit->port;
194 shost = port->adapter->scsi_host;
195 return scsi_device_lookup(shost, 0, port->starget_id, lun);
196}
197
198/**
199 * zfcp_unit_sdev_status - Return zfcp LUN status for SCSI device
200 * @unit: The unit to lookup the SCSI device for
201 *
202 * Returns the zfcp LUN status field of the SCSI device if the SCSI device
203 * for the zfcp_unit exists, 0 otherwise.
204 */
205unsigned int zfcp_unit_sdev_status(struct zfcp_unit *unit)
206{
207 unsigned int status = 0;
208 struct scsi_device *sdev;
209 struct zfcp_scsi_dev *zfcp_sdev;
210
211 sdev = zfcp_unit_sdev(unit);
212 if (sdev) {
213 zfcp_sdev = sdev_to_zfcp(sdev);
214 status = atomic_read(&zfcp_sdev->status);
215 scsi_device_put(sdev);
216 }
217
218 return status;
219}
220
221/**
222 * zfcp_unit_remove - Remove entry from list of configured units
223 * @port: The port where to remove the unit from the configuration
224 * @fcp_lun: The 64 bit LUN of the unit to remove
225 *
226 * Returns: -EINVAL if a unit with the specified LUN does not exist,
227 * 0 on success.
228 */
229int zfcp_unit_remove(struct zfcp_port *port, u64 fcp_lun)
230{
231 struct zfcp_unit *unit;
232 struct scsi_device *sdev;
233
234 write_lock_irq(&port->unit_list_lock);
235 unit = _zfcp_unit_find(port, fcp_lun);
236 if (unit)
237 list_del(&unit->list);
238 write_unlock_irq(&port->unit_list_lock);
239
240 if (!unit)
241 return -EINVAL;
242
243 sdev = zfcp_unit_sdev(unit);
244 if (sdev) {
245 scsi_remove_device(sdev);
246 scsi_device_put(sdev);
247 }
248
249 put_device(&unit->dev);
250
251 zfcp_erp_unit_shutdown(unit, 0, "unrem_1", NULL);
252 zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs);
253
254 return 0;
255}