diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/scsi_transport_iscsi.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/scsi_transport_iscsi.c')
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c new file mode 100644 index 000000000000..8bb8222ea589 --- /dev/null +++ b/drivers/scsi/scsi_transport_iscsi.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * iSCSI transport class definitions | ||
3 | * | ||
4 | * Copyright (C) IBM Corporation, 2004 | ||
5 | * Copyright (C) Mike Christie, 2004 | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | */ | ||
21 | #include <linux/module.h> | ||
22 | #include <scsi/scsi.h> | ||
23 | #include <scsi/scsi_host.h> | ||
24 | #include <scsi/scsi_device.h> | ||
25 | #include <scsi/scsi_transport.h> | ||
26 | #include <scsi/scsi_transport_iscsi.h> | ||
27 | |||
28 | #define ISCSI_SESSION_ATTRS 20 | ||
29 | #define ISCSI_HOST_ATTRS 2 | ||
30 | |||
31 | struct iscsi_internal { | ||
32 | struct scsi_transport_template t; | ||
33 | struct iscsi_function_template *fnt; | ||
34 | /* | ||
35 | * We do not have any private or other attrs. | ||
36 | */ | ||
37 | struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1]; | ||
38 | struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1]; | ||
39 | }; | ||
40 | |||
41 | #define to_iscsi_internal(tmpl) container_of(tmpl, struct iscsi_internal, t) | ||
42 | |||
43 | static DECLARE_TRANSPORT_CLASS(iscsi_transport_class, | ||
44 | "iscsi_transport", | ||
45 | NULL, | ||
46 | NULL, | ||
47 | NULL); | ||
48 | |||
49 | static DECLARE_TRANSPORT_CLASS(iscsi_host_class, | ||
50 | "iscsi_host", | ||
51 | NULL, | ||
52 | NULL, | ||
53 | NULL); | ||
54 | /* | ||
55 | * iSCSI target and session attrs | ||
56 | */ | ||
57 | #define iscsi_session_show_fn(field, format) \ | ||
58 | \ | ||
59 | static ssize_t \ | ||
60 | show_session_##field(struct class_device *cdev, char *buf) \ | ||
61 | { \ | ||
62 | struct scsi_target *starget = transport_class_to_starget(cdev); \ | ||
63 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ | ||
64 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ | ||
65 | \ | ||
66 | if (i->fnt->get_##field) \ | ||
67 | i->fnt->get_##field(starget); \ | ||
68 | return snprintf(buf, 20, format"\n", iscsi_##field(starget)); \ | ||
69 | } | ||
70 | |||
71 | #define iscsi_session_rd_attr(field, format) \ | ||
72 | iscsi_session_show_fn(field, format) \ | ||
73 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL); | ||
74 | |||
75 | iscsi_session_rd_attr(tpgt, "%hu"); | ||
76 | iscsi_session_rd_attr(tsih, "%2x"); | ||
77 | iscsi_session_rd_attr(max_recv_data_segment_len, "%u"); | ||
78 | iscsi_session_rd_attr(max_burst_len, "%u"); | ||
79 | iscsi_session_rd_attr(first_burst_len, "%u"); | ||
80 | iscsi_session_rd_attr(def_time2wait, "%hu"); | ||
81 | iscsi_session_rd_attr(def_time2retain, "%hu"); | ||
82 | iscsi_session_rd_attr(max_outstanding_r2t, "%hu"); | ||
83 | iscsi_session_rd_attr(erl, "%d"); | ||
84 | |||
85 | |||
86 | #define iscsi_session_show_bool_fn(field) \ | ||
87 | \ | ||
88 | static ssize_t \ | ||
89 | show_session_bool_##field(struct class_device *cdev, char *buf) \ | ||
90 | { \ | ||
91 | struct scsi_target *starget = transport_class_to_starget(cdev); \ | ||
92 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ | ||
93 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ | ||
94 | \ | ||
95 | if (i->fnt->get_##field) \ | ||
96 | i->fnt->get_##field(starget); \ | ||
97 | \ | ||
98 | if (iscsi_##field(starget)) \ | ||
99 | return sprintf(buf, "Yes\n"); \ | ||
100 | return sprintf(buf, "No\n"); \ | ||
101 | } | ||
102 | |||
103 | #define iscsi_session_rd_bool_attr(field) \ | ||
104 | iscsi_session_show_bool_fn(field) \ | ||
105 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL); | ||
106 | |||
107 | iscsi_session_rd_bool_attr(initial_r2t); | ||
108 | iscsi_session_rd_bool_attr(immediate_data); | ||
109 | iscsi_session_rd_bool_attr(data_pdu_in_order); | ||
110 | iscsi_session_rd_bool_attr(data_sequence_in_order); | ||
111 | |||
112 | #define iscsi_session_show_digest_fn(field) \ | ||
113 | \ | ||
114 | static ssize_t \ | ||
115 | show_##field(struct class_device *cdev, char *buf) \ | ||
116 | { \ | ||
117 | struct scsi_target *starget = transport_class_to_starget(cdev); \ | ||
118 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ | ||
119 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ | ||
120 | \ | ||
121 | if (i->fnt->get_##field) \ | ||
122 | i->fnt->get_##field(starget); \ | ||
123 | \ | ||
124 | if (iscsi_##field(starget)) \ | ||
125 | return sprintf(buf, "CRC32C\n"); \ | ||
126 | return sprintf(buf, "None\n"); \ | ||
127 | } | ||
128 | |||
129 | #define iscsi_session_rd_digest_attr(field) \ | ||
130 | iscsi_session_show_digest_fn(field) \ | ||
131 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); | ||
132 | |||
133 | iscsi_session_rd_digest_attr(header_digest); | ||
134 | iscsi_session_rd_digest_attr(data_digest); | ||
135 | |||
136 | static ssize_t | ||
137 | show_port(struct class_device *cdev, char *buf) | ||
138 | { | ||
139 | struct scsi_target *starget = transport_class_to_starget(cdev); | ||
140 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
141 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); | ||
142 | |||
143 | if (i->fnt->get_port) | ||
144 | i->fnt->get_port(starget); | ||
145 | |||
146 | return snprintf(buf, 20, "%hu\n", ntohs(iscsi_port(starget))); | ||
147 | } | ||
148 | static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL); | ||
149 | |||
150 | static ssize_t | ||
151 | show_ip_address(struct class_device *cdev, char *buf) | ||
152 | { | ||
153 | struct scsi_target *starget = transport_class_to_starget(cdev); | ||
154 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
155 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); | ||
156 | |||
157 | if (i->fnt->get_ip_address) | ||
158 | i->fnt->get_ip_address(starget); | ||
159 | |||
160 | if (iscsi_addr_type(starget) == AF_INET) | ||
161 | return sprintf(buf, "%u.%u.%u.%u\n", | ||
162 | NIPQUAD(iscsi_sin_addr(starget))); | ||
163 | else if(iscsi_addr_type(starget) == AF_INET6) | ||
164 | return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", | ||
165 | NIP6(iscsi_sin6_addr(starget))); | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL); | ||
169 | |||
170 | static ssize_t | ||
171 | show_isid(struct class_device *cdev, char *buf) | ||
172 | { | ||
173 | struct scsi_target *starget = transport_class_to_starget(cdev); | ||
174 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||
175 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); | ||
176 | |||
177 | if (i->fnt->get_isid) | ||
178 | i->fnt->get_isid(starget); | ||
179 | |||
180 | return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n", | ||
181 | iscsi_isid(starget)[0], iscsi_isid(starget)[1], | ||
182 | iscsi_isid(starget)[2], iscsi_isid(starget)[3], | ||
183 | iscsi_isid(starget)[4], iscsi_isid(starget)[5]); | ||
184 | } | ||
185 | static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL); | ||
186 | |||
187 | /* | ||
188 | * This is used for iSCSI names. Normally, we follow | ||
189 | * the transport class convention of having the lld | ||
190 | * set the field, but in these cases the value is | ||
191 | * too large. | ||
192 | */ | ||
193 | #define iscsi_session_show_str_fn(field) \ | ||
194 | \ | ||
195 | static ssize_t \ | ||
196 | show_session_str_##field(struct class_device *cdev, char *buf) \ | ||
197 | { \ | ||
198 | ssize_t ret = 0; \ | ||
199 | struct scsi_target *starget = transport_class_to_starget(cdev); \ | ||
200 | struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ | ||
201 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ | ||
202 | \ | ||
203 | if (i->fnt->get_##field) \ | ||
204 | ret = i->fnt->get_##field(starget, buf, PAGE_SIZE); \ | ||
205 | return ret; \ | ||
206 | } | ||
207 | |||
208 | #define iscsi_session_rd_str_attr(field) \ | ||
209 | iscsi_session_show_str_fn(field) \ | ||
210 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL); | ||
211 | |||
212 | iscsi_session_rd_str_attr(target_name); | ||
213 | iscsi_session_rd_str_attr(target_alias); | ||
214 | |||
215 | /* | ||
216 | * iSCSI host attrs | ||
217 | */ | ||
218 | |||
219 | /* | ||
220 | * Again, this is used for iSCSI names. Normally, we follow | ||
221 | * the transport class convention of having the lld set | ||
222 | * the field, but in these cases the value is too large. | ||
223 | */ | ||
224 | #define iscsi_host_show_str_fn(field) \ | ||
225 | \ | ||
226 | static ssize_t \ | ||
227 | show_host_str_##field(struct class_device *cdev, char *buf) \ | ||
228 | { \ | ||
229 | int ret = 0; \ | ||
230 | struct Scsi_Host *shost = transport_class_to_shost(cdev); \ | ||
231 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \ | ||
232 | \ | ||
233 | if (i->fnt->get_##field) \ | ||
234 | ret = i->fnt->get_##field(shost, buf, PAGE_SIZE); \ | ||
235 | return ret; \ | ||
236 | } | ||
237 | |||
238 | #define iscsi_host_rd_str_attr(field) \ | ||
239 | iscsi_host_show_str_fn(field) \ | ||
240 | static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL); | ||
241 | |||
242 | iscsi_host_rd_str_attr(initiator_name); | ||
243 | iscsi_host_rd_str_attr(initiator_alias); | ||
244 | |||
245 | #define SETUP_SESSION_RD_ATTR(field) \ | ||
246 | if (i->fnt->show_##field) { \ | ||
247 | i->session_attrs[count] = &class_device_attr_##field; \ | ||
248 | count++; \ | ||
249 | } | ||
250 | |||
251 | #define SETUP_HOST_RD_ATTR(field) \ | ||
252 | if (i->fnt->show_##field) { \ | ||
253 | i->host_attrs[count] = &class_device_attr_##field; \ | ||
254 | count++; \ | ||
255 | } | ||
256 | |||
257 | static int iscsi_host_match(struct attribute_container *cont, | ||
258 | struct device *dev) | ||
259 | { | ||
260 | struct Scsi_Host *shost; | ||
261 | struct iscsi_internal *i; | ||
262 | |||
263 | if (!scsi_is_host_device(dev)) | ||
264 | return 0; | ||
265 | |||
266 | shost = dev_to_shost(dev); | ||
267 | if (!shost->transportt || shost->transportt->host_attrs.ac.class | ||
268 | != &iscsi_host_class.class) | ||
269 | return 0; | ||
270 | |||
271 | i = to_iscsi_internal(shost->transportt); | ||
272 | |||
273 | return &i->t.host_attrs.ac == cont; | ||
274 | } | ||
275 | |||
276 | static int iscsi_target_match(struct attribute_container *cont, | ||
277 | struct device *dev) | ||
278 | { | ||
279 | struct Scsi_Host *shost; | ||
280 | struct iscsi_internal *i; | ||
281 | |||
282 | if (!scsi_is_target_device(dev)) | ||
283 | return 0; | ||
284 | |||
285 | shost = dev_to_shost(dev->parent); | ||
286 | if (!shost->transportt || shost->transportt->host_attrs.ac.class | ||
287 | != &iscsi_host_class.class) | ||
288 | return 0; | ||
289 | |||
290 | i = to_iscsi_internal(shost->transportt); | ||
291 | |||
292 | return &i->t.target_attrs.ac == cont; | ||
293 | } | ||
294 | |||
295 | struct scsi_transport_template * | ||
296 | iscsi_attach_transport(struct iscsi_function_template *fnt) | ||
297 | { | ||
298 | struct iscsi_internal *i = kmalloc(sizeof(struct iscsi_internal), | ||
299 | GFP_KERNEL); | ||
300 | int count = 0; | ||
301 | |||
302 | if (unlikely(!i)) | ||
303 | return NULL; | ||
304 | |||
305 | memset(i, 0, sizeof(struct iscsi_internal)); | ||
306 | i->fnt = fnt; | ||
307 | |||
308 | i->t.target_attrs.ac.attrs = &i->session_attrs[0]; | ||
309 | i->t.target_attrs.ac.class = &iscsi_transport_class.class; | ||
310 | i->t.target_attrs.ac.match = iscsi_target_match; | ||
311 | transport_container_register(&i->t.target_attrs); | ||
312 | i->t.target_size = sizeof(struct iscsi_class_session); | ||
313 | |||
314 | SETUP_SESSION_RD_ATTR(tsih); | ||
315 | SETUP_SESSION_RD_ATTR(isid); | ||
316 | SETUP_SESSION_RD_ATTR(header_digest); | ||
317 | SETUP_SESSION_RD_ATTR(data_digest); | ||
318 | SETUP_SESSION_RD_ATTR(target_name); | ||
319 | SETUP_SESSION_RD_ATTR(target_alias); | ||
320 | SETUP_SESSION_RD_ATTR(port); | ||
321 | SETUP_SESSION_RD_ATTR(tpgt); | ||
322 | SETUP_SESSION_RD_ATTR(ip_address); | ||
323 | SETUP_SESSION_RD_ATTR(initial_r2t); | ||
324 | SETUP_SESSION_RD_ATTR(immediate_data); | ||
325 | SETUP_SESSION_RD_ATTR(max_recv_data_segment_len); | ||
326 | SETUP_SESSION_RD_ATTR(max_burst_len); | ||
327 | SETUP_SESSION_RD_ATTR(first_burst_len); | ||
328 | SETUP_SESSION_RD_ATTR(def_time2wait); | ||
329 | SETUP_SESSION_RD_ATTR(def_time2retain); | ||
330 | SETUP_SESSION_RD_ATTR(max_outstanding_r2t); | ||
331 | SETUP_SESSION_RD_ATTR(data_pdu_in_order); | ||
332 | SETUP_SESSION_RD_ATTR(data_sequence_in_order); | ||
333 | SETUP_SESSION_RD_ATTR(erl); | ||
334 | |||
335 | BUG_ON(count > ISCSI_SESSION_ATTRS); | ||
336 | i->session_attrs[count] = NULL; | ||
337 | |||
338 | i->t.host_attrs.ac.attrs = &i->host_attrs[0]; | ||
339 | i->t.host_attrs.ac.class = &iscsi_host_class.class; | ||
340 | i->t.host_attrs.ac.match = iscsi_host_match; | ||
341 | transport_container_register(&i->t.host_attrs); | ||
342 | i->t.host_size = 0; | ||
343 | |||
344 | count = 0; | ||
345 | SETUP_HOST_RD_ATTR(initiator_name); | ||
346 | SETUP_HOST_RD_ATTR(initiator_alias); | ||
347 | |||
348 | BUG_ON(count > ISCSI_HOST_ATTRS); | ||
349 | i->host_attrs[count] = NULL; | ||
350 | |||
351 | return &i->t; | ||
352 | } | ||
353 | |||
354 | EXPORT_SYMBOL(iscsi_attach_transport); | ||
355 | |||
356 | void iscsi_release_transport(struct scsi_transport_template *t) | ||
357 | { | ||
358 | struct iscsi_internal *i = to_iscsi_internal(t); | ||
359 | |||
360 | transport_container_unregister(&i->t.target_attrs); | ||
361 | transport_container_unregister(&i->t.host_attrs); | ||
362 | |||
363 | kfree(i); | ||
364 | } | ||
365 | |||
366 | EXPORT_SYMBOL(iscsi_release_transport); | ||
367 | |||
368 | static __init int iscsi_transport_init(void) | ||
369 | { | ||
370 | int err = transport_class_register(&iscsi_transport_class); | ||
371 | |||
372 | if (err) | ||
373 | return err; | ||
374 | return transport_class_register(&iscsi_host_class); | ||
375 | } | ||
376 | |||
377 | static void __exit iscsi_transport_exit(void) | ||
378 | { | ||
379 | transport_class_unregister(&iscsi_host_class); | ||
380 | transport_class_unregister(&iscsi_transport_class); | ||
381 | } | ||
382 | |||
383 | module_init(iscsi_transport_init); | ||
384 | module_exit(iscsi_transport_exit); | ||
385 | |||
386 | MODULE_AUTHOR("Mike Christie"); | ||
387 | MODULE_DESCRIPTION("iSCSI Transport Attributes"); | ||
388 | MODULE_LICENSE("GPL"); | ||