Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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"); |