Linux-2.6.12-rc2
[linux-2.6.git] / 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 <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");