Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / drivers / scsi / scsi_transport_iscsi.c
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 <linux/string.h>
23 #include <linux/slab.h>
24
25 #include <scsi/scsi.h>
26 #include <scsi/scsi_host.h>
27 #include <scsi/scsi_device.h>
28 #include <scsi/scsi_transport.h>
29 #include <scsi/scsi_transport_iscsi.h>
30
31 #define ISCSI_SESSION_ATTRS 20
32 #define ISCSI_HOST_ATTRS 2
33
34 struct iscsi_internal {
35         struct scsi_transport_template t;
36         struct iscsi_function_template *fnt;
37         /*
38          * We do not have any private or other attrs.
39          */
40         struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
41         struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
42 };
43
44 #define to_iscsi_internal(tmpl) container_of(tmpl, struct iscsi_internal, t)
45
46 static DECLARE_TRANSPORT_CLASS(iscsi_transport_class,
47                                "iscsi_transport",
48                                NULL,
49                                NULL,
50                                NULL);
51
52 static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
53                                "iscsi_host",
54                                NULL,
55                                NULL,
56                                NULL);
57 /*
58  * iSCSI target and session attrs
59  */
60 #define iscsi_session_show_fn(field, format)                            \
61                                                                         \
62 static ssize_t                                                          \
63 show_session_##field(struct class_device *cdev, char *buf)              \
64 {                                                                       \
65         struct scsi_target *starget = transport_class_to_starget(cdev); \
66         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
67         struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
68                                                                         \
69         if (i->fnt->get_##field)                                        \
70                 i->fnt->get_##field(starget);                           \
71         return snprintf(buf, 20, format"\n", iscsi_##field(starget));   \
72 }
73
74 #define iscsi_session_rd_attr(field, format)                            \
75         iscsi_session_show_fn(field, format)                            \
76 static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL);
77
78 iscsi_session_rd_attr(tpgt, "%hu");
79 iscsi_session_rd_attr(tsih, "%2x");
80 iscsi_session_rd_attr(max_recv_data_segment_len, "%u");
81 iscsi_session_rd_attr(max_burst_len, "%u");
82 iscsi_session_rd_attr(first_burst_len, "%u");
83 iscsi_session_rd_attr(def_time2wait, "%hu");
84 iscsi_session_rd_attr(def_time2retain, "%hu");
85 iscsi_session_rd_attr(max_outstanding_r2t, "%hu");
86 iscsi_session_rd_attr(erl, "%d");
87
88
89 #define iscsi_session_show_bool_fn(field)                               \
90                                                                         \
91 static ssize_t                                                          \
92 show_session_bool_##field(struct class_device *cdev, char *buf)         \
93 {                                                                       \
94         struct scsi_target *starget = transport_class_to_starget(cdev); \
95         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
96         struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
97                                                                         \
98         if (i->fnt->get_##field)                                        \
99                 i->fnt->get_##field(starget);                           \
100                                                                         \
101         if (iscsi_##field(starget))                                     \
102                 return sprintf(buf, "Yes\n");                           \
103         return sprintf(buf, "No\n");                                    \
104 }
105
106 #define iscsi_session_rd_bool_attr(field)                               \
107         iscsi_session_show_bool_fn(field)                               \
108 static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL);
109
110 iscsi_session_rd_bool_attr(initial_r2t);
111 iscsi_session_rd_bool_attr(immediate_data);
112 iscsi_session_rd_bool_attr(data_pdu_in_order);
113 iscsi_session_rd_bool_attr(data_sequence_in_order);
114
115 #define iscsi_session_show_digest_fn(field)                             \
116                                                                         \
117 static ssize_t                                                          \
118 show_##field(struct class_device *cdev, char *buf)                      \
119 {                                                                       \
120         struct scsi_target *starget = transport_class_to_starget(cdev); \
121         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
122         struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
123                                                                         \
124         if (i->fnt->get_##field)                                        \
125                 i->fnt->get_##field(starget);                           \
126                                                                         \
127         if (iscsi_##field(starget))                                     \
128                 return sprintf(buf, "CRC32C\n");                        \
129         return sprintf(buf, "None\n");                                  \
130 }
131
132 #define iscsi_session_rd_digest_attr(field)                             \
133         iscsi_session_show_digest_fn(field)                             \
134 static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
135
136 iscsi_session_rd_digest_attr(header_digest);
137 iscsi_session_rd_digest_attr(data_digest);
138
139 static ssize_t
140 show_port(struct class_device *cdev, char *buf)
141 {
142         struct scsi_target *starget = transport_class_to_starget(cdev);
143         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
144         struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
145
146         if (i->fnt->get_port)
147                 i->fnt->get_port(starget);
148
149         return snprintf(buf, 20, "%hu\n", ntohs(iscsi_port(starget)));
150 }
151 static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
152
153 static ssize_t
154 show_ip_address(struct class_device *cdev, char *buf)
155 {
156         struct scsi_target *starget = transport_class_to_starget(cdev);
157         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
158         struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
159
160         if (i->fnt->get_ip_address)
161                 i->fnt->get_ip_address(starget);
162
163         if (iscsi_addr_type(starget) == AF_INET)
164                 return sprintf(buf, "%u.%u.%u.%u\n",
165                                NIPQUAD(iscsi_sin_addr(starget)));
166         else if(iscsi_addr_type(starget) == AF_INET6)
167                 return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
168                                NIP6(iscsi_sin6_addr(starget)));
169         return -EINVAL;
170 }
171 static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL);
172
173 static ssize_t
174 show_isid(struct class_device *cdev, char *buf)
175 {
176         struct scsi_target *starget = transport_class_to_starget(cdev);
177         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
178         struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
179
180         if (i->fnt->get_isid)
181                 i->fnt->get_isid(starget);
182
183         return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n",
184                        iscsi_isid(starget)[0], iscsi_isid(starget)[1],
185                        iscsi_isid(starget)[2], iscsi_isid(starget)[3],
186                        iscsi_isid(starget)[4], iscsi_isid(starget)[5]);
187 }
188 static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL);
189
190 /*
191  * This is used for iSCSI names. Normally, we follow
192  * the transport class convention of having the lld
193  * set the field, but in these cases the value is
194  * too large.
195  */
196 #define iscsi_session_show_str_fn(field)                                \
197                                                                         \
198 static ssize_t                                                          \
199 show_session_str_##field(struct class_device *cdev, char *buf)          \
200 {                                                                       \
201         ssize_t ret = 0;                                                \
202         struct scsi_target *starget = transport_class_to_starget(cdev); \
203         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
204         struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
205                                                                         \
206         if (i->fnt->get_##field)                                        \
207                 ret = i->fnt->get_##field(starget, buf, PAGE_SIZE);     \
208         return ret;                                                     \
209 }
210
211 #define iscsi_session_rd_str_attr(field)                                \
212         iscsi_session_show_str_fn(field)                                \
213 static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL);
214
215 iscsi_session_rd_str_attr(target_name);
216 iscsi_session_rd_str_attr(target_alias);
217
218 /*
219  * iSCSI host attrs
220  */
221
222 /*
223  * Again, this is used for iSCSI names. Normally, we follow
224  * the transport class convention of having the lld set
225  * the field, but in these cases the value is too large.
226  */
227 #define iscsi_host_show_str_fn(field)                                   \
228                                                                         \
229 static ssize_t                                                          \
230 show_host_str_##field(struct class_device *cdev, char *buf)             \
231 {                                                                       \
232         int ret = 0;                                                    \
233         struct Scsi_Host *shost = transport_class_to_shost(cdev);       \
234         struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
235                                                                         \
236         if (i->fnt->get_##field)                                        \
237                 ret = i->fnt->get_##field(shost, buf, PAGE_SIZE);       \
238         return ret;                                                     \
239 }
240
241 #define iscsi_host_rd_str_attr(field)                                   \
242         iscsi_host_show_str_fn(field)                                   \
243 static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL);
244
245 iscsi_host_rd_str_attr(initiator_name);
246 iscsi_host_rd_str_attr(initiator_alias);
247
248 #define SETUP_SESSION_RD_ATTR(field)                                    \
249         if (i->fnt->show_##field) {                                     \
250                 i->session_attrs[count] = &class_device_attr_##field;   \
251                 count++;                                                \
252         }
253
254 #define SETUP_HOST_RD_ATTR(field)                                       \
255         if (i->fnt->show_##field) {                                     \
256                 i->host_attrs[count] = &class_device_attr_##field;      \
257                 count++;                                                \
258         }
259
260 static int iscsi_host_match(struct attribute_container *cont,
261                           struct device *dev)
262 {
263         struct Scsi_Host *shost;
264         struct iscsi_internal *i;
265
266         if (!scsi_is_host_device(dev))
267                 return 0;
268
269         shost = dev_to_shost(dev);
270         if (!shost->transportt  || shost->transportt->host_attrs.ac.class
271             != &iscsi_host_class.class)
272                 return 0;
273
274         i = to_iscsi_internal(shost->transportt);
275         
276         return &i->t.host_attrs.ac == cont;
277 }
278
279 static int iscsi_target_match(struct attribute_container *cont,
280                             struct device *dev)
281 {
282         struct Scsi_Host *shost;
283         struct iscsi_internal *i;
284
285         if (!scsi_is_target_device(dev))
286                 return 0;
287
288         shost = dev_to_shost(dev->parent);
289         if (!shost->transportt  || shost->transportt->host_attrs.ac.class
290             != &iscsi_host_class.class)
291                 return 0;
292
293         i = to_iscsi_internal(shost->transportt);
294         
295         return &i->t.target_attrs.ac == cont;
296 }
297
298 struct scsi_transport_template *
299 iscsi_attach_transport(struct iscsi_function_template *fnt)
300 {
301         struct iscsi_internal *i = kmalloc(sizeof(struct iscsi_internal),
302                                            GFP_KERNEL);
303         int count = 0;
304
305         if (unlikely(!i))
306                 return NULL;
307
308         memset(i, 0, sizeof(struct iscsi_internal));
309         i->fnt = fnt;
310
311         i->t.target_attrs.ac.attrs = &i->session_attrs[0];
312         i->t.target_attrs.ac.class = &iscsi_transport_class.class;
313         i->t.target_attrs.ac.match = iscsi_target_match;
314         transport_container_register(&i->t.target_attrs);
315         i->t.target_size = sizeof(struct iscsi_class_session);
316
317         SETUP_SESSION_RD_ATTR(tsih);
318         SETUP_SESSION_RD_ATTR(isid);
319         SETUP_SESSION_RD_ATTR(header_digest);
320         SETUP_SESSION_RD_ATTR(data_digest);
321         SETUP_SESSION_RD_ATTR(target_name);
322         SETUP_SESSION_RD_ATTR(target_alias);
323         SETUP_SESSION_RD_ATTR(port);
324         SETUP_SESSION_RD_ATTR(tpgt);
325         SETUP_SESSION_RD_ATTR(ip_address);
326         SETUP_SESSION_RD_ATTR(initial_r2t);
327         SETUP_SESSION_RD_ATTR(immediate_data);
328         SETUP_SESSION_RD_ATTR(max_recv_data_segment_len);
329         SETUP_SESSION_RD_ATTR(max_burst_len);
330         SETUP_SESSION_RD_ATTR(first_burst_len);
331         SETUP_SESSION_RD_ATTR(def_time2wait);
332         SETUP_SESSION_RD_ATTR(def_time2retain);
333         SETUP_SESSION_RD_ATTR(max_outstanding_r2t);
334         SETUP_SESSION_RD_ATTR(data_pdu_in_order);
335         SETUP_SESSION_RD_ATTR(data_sequence_in_order);
336         SETUP_SESSION_RD_ATTR(erl);
337
338         BUG_ON(count > ISCSI_SESSION_ATTRS);
339         i->session_attrs[count] = NULL;
340
341         i->t.host_attrs.ac.attrs = &i->host_attrs[0];
342         i->t.host_attrs.ac.class = &iscsi_host_class.class;
343         i->t.host_attrs.ac.match = iscsi_host_match;
344         transport_container_register(&i->t.host_attrs);
345         i->t.host_size = 0;
346
347         count = 0;
348         SETUP_HOST_RD_ATTR(initiator_name);
349         SETUP_HOST_RD_ATTR(initiator_alias);
350
351         BUG_ON(count > ISCSI_HOST_ATTRS);
352         i->host_attrs[count] = NULL;
353
354         return &i->t;
355 }
356
357 EXPORT_SYMBOL(iscsi_attach_transport);
358
359 void iscsi_release_transport(struct scsi_transport_template *t)
360 {
361         struct iscsi_internal *i = to_iscsi_internal(t);
362
363         transport_container_unregister(&i->t.target_attrs);
364         transport_container_unregister(&i->t.host_attrs);
365   
366         kfree(i);
367 }
368
369 EXPORT_SYMBOL(iscsi_release_transport);
370
371 static __init int iscsi_transport_init(void)
372 {
373         int err = transport_class_register(&iscsi_transport_class);
374
375         if (err)
376                 return err;
377         return transport_class_register(&iscsi_host_class);
378 }
379
380 static void __exit iscsi_transport_exit(void)
381 {
382         transport_class_unregister(&iscsi_host_class);
383         transport_class_unregister(&iscsi_transport_class);
384 }
385
386 module_init(iscsi_transport_init);
387 module_exit(iscsi_transport_exit);
388
389 MODULE_AUTHOR("Mike Christie");
390 MODULE_DESCRIPTION("iSCSI Transport Attributes");
391 MODULE_LICENSE("GPL");