]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/scsi/scsi_transport_iscsi.c
[SCSI] pm8001: fix endian issue with code optimization.
[mirror_ubuntu-bionic-kernel.git] / drivers / scsi / scsi_transport_iscsi.c
index 96029e6d027fd654ad4a1f17272b74e0a215565c..a20f1813cb514bc1ff79f6b77a09b03b631fac2f 100644 (file)
@@ -328,7 +328,7 @@ iscsi_iface_net_attr(iface, vlan_enabled, ISCSI_NET_PARAM_VLAN_ENABLED);
 iscsi_iface_net_attr(iface, mtu, ISCSI_NET_PARAM_MTU);
 iscsi_iface_net_attr(iface, port, ISCSI_NET_PARAM_PORT);
 
-static mode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
                                          struct attribute *attr, int i)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
@@ -1030,6 +1030,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
                return NULL;
 
        session->transport = transport;
+       session->creator = -1;
        session->recovery_tmo = 120;
        session->state = ISCSI_SESSION_FREE;
        INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
@@ -1475,6 +1476,66 @@ void iscsi_conn_login_event(struct iscsi_cls_conn *conn,
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_login_event);
 
+void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
+                          enum iscsi_host_event_code code, uint32_t data_size,
+                          uint8_t *data)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       struct iscsi_uevent *ev;
+       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb) {
+               printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n",
+                      host_no, code);
+               return;
+       }
+
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+       ev = NLMSG_DATA(nlh);
+       ev->transport_handle = iscsi_handle(transport);
+       ev->type = ISCSI_KEVENT_HOST_EVENT;
+       ev->r.host_event.host_no = host_no;
+       ev->r.host_event.code = code;
+       ev->r.host_event.data_size = data_size;
+
+       if (data_size)
+               memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(iscsi_post_host_event);
+
+void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
+                          uint32_t status, uint32_t pid, uint32_t data_size,
+                          uint8_t *data)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       struct iscsi_uevent *ev;
+       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb) {
+               printk(KERN_ERR "gracefully ignored ping comp: OOM\n");
+               return;
+       }
+
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+       ev = NLMSG_DATA(nlh);
+       ev->transport_handle = iscsi_handle(transport);
+       ev->type = ISCSI_KEVENT_PING_COMP;
+       ev->r.ping_comp.host_no = host_no;
+       ev->r.ping_comp.status = status;
+       ev->r.ping_comp.pid = pid;
+       ev->r.ping_comp.data_size = data_size;
+       memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
+
 static int
 iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
                    void *payload, int size)
@@ -1634,8 +1695,9 @@ EXPORT_SYMBOL_GPL(iscsi_session_event);
 
 static int
 iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
-                       struct iscsi_uevent *ev, uint32_t initial_cmdsn,
-                       uint16_t cmds_max, uint16_t queue_depth)
+                       struct iscsi_uevent *ev, pid_t pid,
+                       uint32_t initial_cmdsn, uint16_t cmds_max,
+                       uint16_t queue_depth)
 {
        struct iscsi_transport *transport = priv->iscsi_transport;
        struct iscsi_cls_session *session;
@@ -1646,6 +1708,7 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
        if (!session)
                return -ENOMEM;
 
+       session->creator = pid;
        shost = iscsi_session_to_shost(session);
        ev->r.c_session_ret.host_no = shost->host_no;
        ev->r.c_session_ret.sid = session->sid;
@@ -1911,6 +1974,33 @@ iscsi_set_iface_params(struct iscsi_transport *transport,
        return err;
 }
 
+static int
+iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+       struct Scsi_Host *shost;
+       struct sockaddr *dst_addr;
+       int err;
+
+       if (!transport->send_ping)
+               return -ENOSYS;
+
+       shost = scsi_host_lookup(ev->u.iscsi_ping.host_no);
+       if (!shost) {
+               printk(KERN_ERR "iscsi_ping could not find host no %u\n",
+                      ev->u.iscsi_ping.host_no);
+               return -ENODEV;
+       }
+
+       dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev));
+       err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num,
+                                  ev->u.iscsi_ping.iface_type,
+                                  ev->u.iscsi_ping.payload_size,
+                                  ev->u.iscsi_ping.pid,
+                                  dst_addr);
+       scsi_host_put(shost);
+       return err;
+}
+
 static int
 iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 {
@@ -1938,6 +2028,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
        switch (nlh->nlmsg_type) {
        case ISCSI_UEVENT_CREATE_SESSION:
                err = iscsi_if_create_session(priv, ep, ev,
+                                             NETLINK_CB(skb).pid,
                                              ev->u.c_session.initial_cmdsn,
                                              ev->u.c_session.cmds_max,
                                              ev->u.c_session.queue_depth);
@@ -1950,6 +2041,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
                }
 
                err = iscsi_if_create_session(priv, ep, ev,
+                                       NETLINK_CB(skb).pid,
                                        ev->u.c_bound_session.initial_cmdsn,
                                        ev->u.c_bound_session.cmds_max,
                                        ev->u.c_bound_session.queue_depth);
@@ -2054,6 +2146,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
                err = iscsi_set_iface_params(transport, ev,
                                             nlmsg_attrlen(nlh, sizeof(*ev)));
                break;
+       case ISCSI_UEVENT_PING:
+               err = iscsi_send_ping(transport, ev);
+               break;
        default:
                err = -ENOSYS;
                break;
@@ -2105,7 +2200,7 @@ iscsi_if_rx(struct sk_buff *skb)
                                break;
                        err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
                                nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
-               } while (err < 0 && err != -ECONNREFUSED);
+               } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH);
                skb_pull(skb, rlen);
        }
        mutex_unlock(&rx_queue_mutex);
@@ -2199,7 +2294,7 @@ static struct attribute *iscsi_conn_attrs[] = {
        NULL,
 };
 
-static mode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
                                         struct attribute *attr, int i)
 {
        struct device *cdev = container_of(kobj, struct device, kobj);
@@ -2298,6 +2393,15 @@ show_priv_session_state(struct device *dev, struct device_attribute *attr,
 }
 static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
                        NULL);
+static ssize_t
+show_priv_session_creator(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+       return sprintf(buf, "%d\n", session->creator);
+}
+static ISCSI_CLASS_ATTR(priv_sess, creator, S_IRUGO, show_priv_session_creator,
+                       NULL);
 
 #define iscsi_priv_session_attr_show(field, format)                    \
 static ssize_t                                                         \
@@ -2367,10 +2471,11 @@ static struct attribute *iscsi_session_attrs[] = {
        &dev_attr_sess_targetalias.attr,
        &dev_attr_priv_sess_recovery_tmo.attr,
        &dev_attr_priv_sess_state.attr,
+       &dev_attr_priv_sess_creator.attr,
        NULL,
 };
 
-static mode_t iscsi_session_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
                                            struct attribute *attr, int i)
 {
        struct device *cdev = container_of(kobj, struct device, kobj);
@@ -2424,6 +2529,8 @@ static mode_t iscsi_session_attr_is_visible(struct kobject *kobj,
                return S_IRUGO | S_IWUSR;
        else if (attr == &dev_attr_priv_sess_state.attr)
                return S_IRUGO;
+       else if (attr == &dev_attr_priv_sess_creator.attr)
+               return S_IRUGO;
        else {
                WARN_ONCE(1, "Invalid session attr");
                return 0;
@@ -2459,16 +2566,20 @@ iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
 iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
 iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
 iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
+iscsi_host_attr(port_state, ISCSI_HOST_PARAM_PORT_STATE);
+iscsi_host_attr(port_speed, ISCSI_HOST_PARAM_PORT_SPEED);
 
 static struct attribute *iscsi_host_attrs[] = {
        &dev_attr_host_netdev.attr,
        &dev_attr_host_hwaddress.attr,
        &dev_attr_host_ipaddress.attr,
        &dev_attr_host_initiatorname.attr,
+       &dev_attr_host_port_state.attr,
+       &dev_attr_host_port_speed.attr,
        NULL,
 };
 
-static mode_t iscsi_host_attr_is_visible(struct kobject *kobj,
+static umode_t iscsi_host_attr_is_visible(struct kobject *kobj,
                                         struct attribute *attr, int i)
 {
        struct device *cdev = container_of(kobj, struct device, kobj);
@@ -2484,6 +2595,10 @@ static mode_t iscsi_host_attr_is_visible(struct kobject *kobj,
                param = ISCSI_HOST_PARAM_IPADDRESS;
        else if (attr == &dev_attr_host_initiatorname.attr)
                param = ISCSI_HOST_PARAM_INITIATOR_NAME;
+       else if (attr == &dev_attr_host_port_state.attr)
+               param = ISCSI_HOST_PARAM_PORT_STATE;
+       else if (attr == &dev_attr_host_port_speed.attr)
+               param = ISCSI_HOST_PARAM_PORT_SPEED;
        else {
                WARN_ONCE(1, "Invalid host attr");
                return 0;
@@ -2497,6 +2612,61 @@ static struct attribute_group iscsi_host_group = {
        .is_visible = iscsi_host_attr_is_visible,
 };
 
+/* convert iscsi_port_speed values to ascii string name */
+static const struct {
+       enum iscsi_port_speed   value;
+       char                    *name;
+} iscsi_port_speed_names[] = {
+       {ISCSI_PORT_SPEED_UNKNOWN,      "Unknown" },
+       {ISCSI_PORT_SPEED_10MBPS,       "10 Mbps" },
+       {ISCSI_PORT_SPEED_100MBPS,      "100 Mbps" },
+       {ISCSI_PORT_SPEED_1GBPS,        "1 Gbps" },
+       {ISCSI_PORT_SPEED_10GBPS,       "10 Gbps" },
+};
+
+char *iscsi_get_port_speed_name(struct Scsi_Host *shost)
+{
+       int i;
+       char *speed = "Unknown!";
+       struct iscsi_cls_host *ihost = shost->shost_data;
+       uint32_t port_speed = ihost->port_speed;
+
+       for (i = 0; i < ARRAY_SIZE(iscsi_port_speed_names); i++) {
+               if (iscsi_port_speed_names[i].value & port_speed) {
+                       speed = iscsi_port_speed_names[i].name;
+                       break;
+               }
+       }
+       return speed;
+}
+EXPORT_SYMBOL_GPL(iscsi_get_port_speed_name);
+
+/* convert iscsi_port_state values to ascii string name */
+static const struct {
+       enum iscsi_port_state   value;
+       char                    *name;
+} iscsi_port_state_names[] = {
+       {ISCSI_PORT_STATE_DOWN,         "LINK DOWN" },
+       {ISCSI_PORT_STATE_UP,           "LINK UP" },
+};
+
+char *iscsi_get_port_state_name(struct Scsi_Host *shost)
+{
+       int i;
+       char *state = "Unknown!";
+       struct iscsi_cls_host *ihost = shost->shost_data;
+       uint32_t port_state = ihost->port_state;
+
+       for (i = 0; i < ARRAY_SIZE(iscsi_port_state_names); i++) {
+               if (iscsi_port_state_names[i].value & port_state) {
+                       state = iscsi_port_state_names[i].name;
+                       break;
+               }
+       }
+       return state;
+}
+EXPORT_SYMBOL_GPL(iscsi_get_port_state_name);
+
 static int iscsi_session_match(struct attribute_container *cont,
                           struct device *dev)
 {