diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 212a8d846748..4d5e64f429e7 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -270,6 +270,185 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) } EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); +/* + * Interface to display network param to sysfs + */ + +static void iscsi_iface_release(struct device *dev) +{ + struct iscsi_iface *iface = iscsi_dev_to_iface(dev); + struct device *parent = iface->dev.parent; + + kfree(iface); + put_device(parent); +} + + +static struct class iscsi_iface_class = { + .name = "iscsi_iface", + .dev_release = iscsi_iface_release, +}; + +#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store) \ +struct device_attribute dev_attr_##_prefix##_##_name = \ + __ATTR(_name, _mode, _show, _store) + +/* iface attrs show */ +#define iscsi_iface_attr_show(type, name, param_type, param) \ +static ssize_t \ +show_##type##_##name(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct iscsi_iface *iface = iscsi_dev_to_iface(dev); \ + struct iscsi_transport *t = iface->transport; \ + return t->get_iface_param(iface, param_type, param, buf); \ +} \ + +#define iscsi_iface_net_attr(type, name, param) \ + iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param) \ +static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL); + +/* generic read only ipvi4 attribute */ +iscsi_iface_net_attr(ipv4_iface, ipaddress, ISCSI_NET_PARAM_IPV4_ADDR); +iscsi_iface_net_attr(ipv4_iface, gateway, ISCSI_NET_PARAM_IPV4_GW); +iscsi_iface_net_attr(ipv4_iface, subnet, ISCSI_NET_PARAM_IPV4_SUBNET); +iscsi_iface_net_attr(ipv4_iface, bootproto, ISCSI_NET_PARAM_IPV4_BOOTPROTO); + +/* generic read only ipv6 attribute */ +iscsi_iface_net_attr(ipv6_iface, ipaddress, ISCSI_NET_PARAM_IPV6_ADDR); +iscsi_iface_net_attr(ipv6_iface, link_local_addr, ISCSI_NET_PARAM_IPV6_LINKLOCAL); +iscsi_iface_net_attr(ipv6_iface, router_addr, ISCSI_NET_PARAM_IPV6_ROUTER); +iscsi_iface_net_attr(ipv6_iface, ipaddr_autocfg, + ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG); +iscsi_iface_net_attr(ipv6_iface, linklocal_autocfg, + ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG); + +/* common read only iface attribute */ +iscsi_iface_net_attr(iface, enabled, ISCSI_NET_PARAM_IFACE_ENABLE); +iscsi_iface_net_attr(iface, vlan, ISCSI_NET_PARAM_VLAN_ID); + +static mode_t iscsi_iface_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct iscsi_iface *iface = iscsi_dev_to_iface(dev); + struct iscsi_transport *t = iface->transport; + + if (attr == &dev_attr_iface_enabled.attr) + return (t->iface_param_mask & ISCSI_NET_IFACE_ENABLE) ? + S_IRUGO : 0; + else if (attr == &dev_attr_iface_vlan.attr) + return (t->iface_param_mask & ISCSI_NET_VLAN_ID) ? S_IRUGO : 0; + + if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { + if (attr == &dev_attr_ipv4_iface_ipaddress.attr) + return (t->iface_param_mask & ISCSI_NET_IPV4_ADDR) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv4_iface_gateway.attr) + return (t->iface_param_mask & ISCSI_NET_IPV4_GW) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv4_iface_subnet.attr) + return (t->iface_param_mask & ISCSI_NET_IPV4_SUBNET) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv4_iface_bootproto.attr) + return (t->iface_param_mask & ISCSI_NET_IPV4_BOOTPROTO) ? + S_IRUGO : 0; + } else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) { + if (attr == &dev_attr_ipv6_iface_ipaddress.attr) + return (t->iface_param_mask & ISCSI_NET_IPV6_ADDR) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr) + return (t->iface_param_mask & ISCSI_NET_IPV6_LINKLOCAL) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv6_iface_router_addr.attr) + return (t->iface_param_mask & ISCSI_NET_IPV6_ROUTER) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr) + return (t->iface_param_mask & ISCSI_NET_IPV6_ADDR_AUTOCFG) ? + S_IRUGO : 0; + else if (attr == &dev_attr_ipv6_iface_linklocal_autocfg.attr) + return (t->iface_param_mask & ISCSI_NET_IPV6_LINKLOCAL_AUTOCFG) ? + S_IRUGO : 0; + } + + return 0; +} + +static struct attribute *iscsi_iface_attrs[] = { + &dev_attr_iface_enabled.attr, + &dev_attr_iface_vlan.attr, + &dev_attr_ipv4_iface_ipaddress.attr, + &dev_attr_ipv4_iface_gateway.attr, + &dev_attr_ipv4_iface_subnet.attr, + &dev_attr_ipv4_iface_bootproto.attr, + &dev_attr_ipv6_iface_ipaddress.attr, + &dev_attr_ipv6_iface_link_local_addr.attr, + &dev_attr_ipv6_iface_router_addr.attr, + &dev_attr_ipv6_iface_ipaddr_autocfg.attr, + &dev_attr_ipv6_iface_linklocal_autocfg.attr, + NULL, +}; + +static struct attribute_group iscsi_iface_group = { + .attrs = iscsi_iface_attrs, + .is_visible = iscsi_iface_attr_is_visible, +}; + +struct iscsi_iface * +iscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *transport, + uint32_t iface_type, uint32_t iface_num, int dd_size) +{ + struct iscsi_iface *iface; + int err; + + iface = kzalloc(sizeof(*iface) + dd_size, GFP_KERNEL); + if (!iface) + return NULL; + + iface->transport = transport; + iface->iface_type = iface_type; + iface->iface_num = iface_num; + iface->dev.release = iscsi_iface_release; + iface->dev.class = &iscsi_iface_class; + /* parent reference released in iscsi_iface_release */ + iface->dev.parent = get_device(&shost->shost_gendev); + if (iface_type == ISCSI_IFACE_TYPE_IPV4) + dev_set_name(&iface->dev, "ipv4-iface-%u-%u", shost->host_no, + iface_num); + else + dev_set_name(&iface->dev, "ipv6-iface-%u-%u", shost->host_no, + iface_num); + + err = device_register(&iface->dev); + if (err) + goto free_iface; + + err = sysfs_create_group(&iface->dev.kobj, &iscsi_iface_group); + if (err) + goto unreg_iface; + + if (dd_size) + iface->dd_data = &iface[1]; + return iface; + +unreg_iface: + device_unregister(&iface->dev); + return NULL; + +free_iface: + put_device(iface->dev.parent); + kfree(iface); + return NULL; +} +EXPORT_SYMBOL_GPL(iscsi_create_iface); + +void iscsi_destroy_iface(struct iscsi_iface *iface) +{ + sysfs_remove_group(&iface->dev.kobj, &iscsi_iface_group); + device_unregister(&iface->dev); +} +EXPORT_SYMBOL_GPL(iscsi_destroy_iface); + static int iscsi_setup_host(struct transport_container *tc, struct device *dev, struct device *cdev) { @@ -2175,6 +2354,7 @@ iscsi_register_transport(struct iscsi_transport *tt) BUG_ON(count > ISCSI_SESSION_ATTRS); priv->session_attrs[count] = NULL; + count = 0; spin_lock_irqsave(&iscsi_transport_lock, flags); list_add(&priv->list, &iscsi_transports); @@ -2237,10 +2417,14 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_transport_class; - err = transport_class_register(&iscsi_host_class); + err = class_register(&iscsi_iface_class); if (err) goto unregister_endpoint_class; + err = transport_class_register(&iscsi_host_class); + if (err) + goto unregister_iface_class; + err = transport_class_register(&iscsi_connection_class); if (err) goto unregister_host_class; @@ -2270,6 +2454,8 @@ unregister_conn_class: transport_class_unregister(&iscsi_connection_class); unregister_host_class: transport_class_unregister(&iscsi_host_class); +unregister_iface_class: + class_unregister(&iscsi_iface_class); unregister_endpoint_class: class_unregister(&iscsi_endpoint_class); unregister_transport_class: @@ -2285,6 +2471,7 @@ static void __exit iscsi_transport_exit(void) transport_class_unregister(&iscsi_session_class); transport_class_unregister(&iscsi_host_class); class_unregister(&iscsi_endpoint_class); + class_unregister(&iscsi_iface_class); class_unregister(&iscsi_transport_class); } diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index e93831e87878..a563753d578b 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -296,10 +296,27 @@ enum iscsi_net_param { ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG = 11, ISCSI_NET_PARAM_IFACE_ENABLE = 12, ISCSI_NET_PARAM_VLAN_ID = 13, - ISCSI_NET_IFACE_TYPE = 14, - ISCSI_NET_IFACE_NAME = 15, + ISCSI_NET_PARAM_IFACE_TYPE = 14, + ISCSI_NET_PARAM_IFACE_NAME = 15, }; +#define ISCSI_NET_IPV4_ADDR (1ULL << ISCSI_NET_PARAM_IPV4_ADDR) +#define ISCSI_NET_IPV4_SUBNET (1ULL << ISCSI_NET_PARAM_IPV4_SUBNET) +#define ISCSI_NET_IPV4_GW (1ULL << ISCSI_NET_PARAM_IPV4_GW) +#define ISCSI_NET_IPV4_BOOTPROTO (1ULL << ISCSI_NET_PARAM_IPV4_BOOTPROTO) +#define ISCSI_NET_MAC (1ULL << ISCSI_NET_PARAM_MAC) +#define ISCSI_NET_IPV6_LINKLOCAL (1ULL << ISCSI_NET_PARAM_IPV6_LINKLOCAL) +#define ISCSI_NET_IPV6_ADDR (1ULL << ISCSI_NET_PARAM_IPV6_ADDR) +#define ISCSI_NET_IPV6_ROUTER (1ULL << ISCSI_NET_PARAM_IPV6_ROUTER) +#define ISCSI_NET_IPV6_ADDR_AUTOCFG \ + (1ULL << ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG) +#define ISCSI_NET_IPV6_LINKLOCAL_AUTOCFG \ + (1ULL << ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG) +#define ISCSI_NET_IPV6_ROUTER_AUTOCFG \ + (1ULL << ISCSI_NET_PARAM_IPV6_ROUTER_AUTOCFG) +#define ISCSI_NET_IFACE_ENABLE (1ULL << ISCSI_NET_PARAM_IFACE_ENABLE) +#define ISCSI_NET_VLAN_ID (1ULL << ISCSI_NET_PARAM_VLAN_ID) + /* * Common error codes */ diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 9fcce7cd3673..e1f210a173a5 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -37,6 +37,7 @@ struct iscsi_cls_conn; struct iscsi_conn; struct iscsi_task; struct sockaddr; +struct iscsi_iface; /** * struct iscsi_transport - iSCSI Transport template @@ -87,6 +88,8 @@ struct iscsi_transport { /* LLD sets this to indicate what values it can export to sysfs */ uint64_t param_mask; uint64_t host_param_mask; + uint64_t iface_param_mask; + struct iscsi_cls_session *(*create_session) (struct iscsi_endpoint *ep, uint16_t cmds_max, uint16_t qdepth, uint32_t sn); @@ -138,6 +141,9 @@ struct iscsi_transport { uint32_t enable, struct sockaddr *dst_addr); int (*set_path) (struct Scsi_Host *shost, struct iscsi_path *params); int (*set_iface_param) (struct Scsi_Host *shost, char *data, int count); + int (*get_iface_param) (struct iscsi_iface *iface, + enum iscsi_param_type param_type, + int param, char *buf); }; /* @@ -229,6 +235,20 @@ struct iscsi_endpoint { struct iscsi_cls_conn *conn; }; +struct iscsi_iface { + struct device dev; + struct iscsi_transport *transport; + uint32_t iface_type; /* IPv4 or IPv6 */ + uint32_t iface_num; /* iface number, 0 - n */ + void *dd_data; /* LLD private data */ +}; + +#define iscsi_dev_to_iface(_dev) \ + container_of(_dev, struct iscsi_iface, dev) + +#define iscsi_iface_to_shost(_iface) \ + dev_to_shost(_iface->dev.parent) + /* * session and connection functions that can be used by HW iSCSI LLDs */ @@ -262,5 +282,11 @@ extern struct iscsi_endpoint *iscsi_create_endpoint(int dd_size); extern void iscsi_destroy_endpoint(struct iscsi_endpoint *ep); extern struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle); extern int iscsi_block_scsi_eh(struct scsi_cmnd *cmd); +extern struct iscsi_iface *iscsi_create_iface(struct Scsi_Host *shost, + struct iscsi_transport *t, + uint32_t iface_type, + uint32_t iface_num, int dd_size); +extern void iscsi_destroy_iface(struct iscsi_iface *iface); +extern struct iscsi_iface *iscsi_lookup_iface(int handle); #endif