diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_unit.c')
-rw-r--r-- | drivers/s390/scsi/zfcp_unit.c | 255 |
1 files changed, 255 insertions, 0 deletions
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 | */ | ||
21 | void 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 | |||
32 | static 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 | */ | ||
51 | void 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 | |||
65 | static 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 | */ | ||
89 | struct 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 | */ | ||
103 | static 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 | */ | ||
119 | int 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 | */ | ||
186 | struct 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 | */ | ||
205 | unsigned 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 | */ | ||
229 | int 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 | } | ||