[SCSI] iscsi class: add iface representation
authorMike Christie <michaelc@cs.wisc.edu>
Mon, 25 Jul 2011 18:48:40 +0000 (13:48 -0500)
committerJames Bottomley <JBottomley@Parallels.com>
Sat, 27 Aug 2011 14:36:00 +0000 (08:36 -0600)
A iscsi host can have multiple interfaces. This patch
adds a new iface iscsi class for this. It exports the
network settings now, and will be extended to also
export iscsi initiator port settings like the isid
and initiator name for drivers that can support multiple
initiator ports.

Based on patch from Lalit Chandivade.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/scsi_transport_iscsi.c
include/scsi/iscsi_if.h
include/scsi/scsi_transport_iscsi.h

index 212a8d846748601b332f2e047f9f08850180ba51..4d5e64f429e73ab53078338fd8d0532ccb35e4ed 100644 (file)
@@ -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);
 }
 
index e93831e87878110c0a77a17d6f96c737608dbfc8..a563753d578b031615eab9d68efd288962e01973 100644 (file)
@@ -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
  */
index 9fcce7cd36733c71b3026d2a5d51cb1b0a8a60c4..e1f210a173a5ce435a084503df37e48a3e169dc2 100644 (file)
@@ -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