diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/s390/scsi/zfcp_unit.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/s390/scsi/zfcp_unit.c')
-rw-r--r-- | drivers/s390/scsi/zfcp_unit.c | 244 |
1 files changed, 244 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..20796ebc33ce --- /dev/null +++ b/drivers/s390/scsi/zfcp_unit.c | |||
@@ -0,0 +1,244 @@ | |||
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 | INIT_WORK(&unit->scsi_work, zfcp_unit_scsi_scan_work); | ||
138 | |||
139 | if (dev_set_name(&unit->dev, "0x%016llx", | ||
140 | (unsigned long long) fcp_lun)) { | ||
141 | kfree(unit); | ||
142 | return -ENOMEM; | ||
143 | } | ||
144 | |||
145 | get_device(&port->dev); | ||
146 | |||
147 | if (device_register(&unit->dev)) { | ||
148 | put_device(&unit->dev); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | |||
152 | if (sysfs_create_group(&unit->dev.kobj, &zfcp_sysfs_unit_attrs)) { | ||
153 | device_unregister(&unit->dev); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | write_lock_irq(&port->unit_list_lock); | ||
158 | list_add_tail(&unit->list, &port->unit_list); | ||
159 | write_unlock_irq(&port->unit_list_lock); | ||
160 | |||
161 | zfcp_unit_scsi_scan(unit); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * zfcp_unit_sdev - Return SCSI device for zfcp_unit | ||
168 | * @unit: The zfcp_unit where to get the SCSI device for | ||
169 | * | ||
170 | * Returns: scsi_device pointer on success, NULL if there is no SCSI | ||
171 | * device for this zfcp_unit | ||
172 | * | ||
173 | * On success, the caller also holds a reference to the SCSI device | ||
174 | * that must be released with scsi_device_put. | ||
175 | */ | ||
176 | struct scsi_device *zfcp_unit_sdev(struct zfcp_unit *unit) | ||
177 | { | ||
178 | struct Scsi_Host *shost; | ||
179 | struct zfcp_port *port; | ||
180 | unsigned int lun; | ||
181 | |||
182 | lun = scsilun_to_int((struct scsi_lun *) &unit->fcp_lun); | ||
183 | port = unit->port; | ||
184 | shost = port->adapter->scsi_host; | ||
185 | return scsi_device_lookup(shost, 0, port->starget_id, lun); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * zfcp_unit_sdev_status - Return zfcp LUN status for SCSI device | ||
190 | * @unit: The unit to lookup the SCSI device for | ||
191 | * | ||
192 | * Returns the zfcp LUN status field of the SCSI device if the SCSI device | ||
193 | * for the zfcp_unit exists, 0 otherwise. | ||
194 | */ | ||
195 | unsigned int zfcp_unit_sdev_status(struct zfcp_unit *unit) | ||
196 | { | ||
197 | unsigned int status = 0; | ||
198 | struct scsi_device *sdev; | ||
199 | struct zfcp_scsi_dev *zfcp_sdev; | ||
200 | |||
201 | sdev = zfcp_unit_sdev(unit); | ||
202 | if (sdev) { | ||
203 | zfcp_sdev = sdev_to_zfcp(sdev); | ||
204 | status = atomic_read(&zfcp_sdev->status); | ||
205 | scsi_device_put(sdev); | ||
206 | } | ||
207 | |||
208 | return status; | ||
209 | } | ||
210 | |||
211 | /** | ||
212 | * zfcp_unit_remove - Remove entry from list of configured units | ||
213 | * @port: The port where to remove the unit from the configuration | ||
214 | * @fcp_lun: The 64 bit LUN of the unit to remove | ||
215 | * | ||
216 | * Returns: -EINVAL if a unit with the specified LUN does not exist, | ||
217 | * 0 on success. | ||
218 | */ | ||
219 | int zfcp_unit_remove(struct zfcp_port *port, u64 fcp_lun) | ||
220 | { | ||
221 | struct zfcp_unit *unit; | ||
222 | struct scsi_device *sdev; | ||
223 | |||
224 | write_lock_irq(&port->unit_list_lock); | ||
225 | unit = _zfcp_unit_find(port, fcp_lun); | ||
226 | if (unit) | ||
227 | list_del(&unit->list); | ||
228 | write_unlock_irq(&port->unit_list_lock); | ||
229 | |||
230 | if (!unit) | ||
231 | return -EINVAL; | ||
232 | |||
233 | sdev = zfcp_unit_sdev(unit); | ||
234 | if (sdev) { | ||
235 | scsi_remove_device(sdev); | ||
236 | scsi_device_put(sdev); | ||
237 | } | ||
238 | |||
239 | put_device(&unit->dev); | ||
240 | |||
241 | zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs); | ||
242 | |||
243 | return 0; | ||
244 | } | ||