[SCSI] ipr: add workaround for MSI interrupts on P7
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_iscsi.c
index 75c9297..ad897df 100644 (file)
 
 #define ISCSI_TRANSPORT_VERSION "2.0-870"
 
+static int dbg_session;
+module_param_named(debug_session, dbg_session, int,
+                  S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_session,
+                "Turn on debugging for sessions in scsi_transport_iscsi "
+                "module. Set to 1 to turn on, and zero to turn off. Default "
+                "is off.");
+
+static int dbg_conn;
+module_param_named(debug_conn, dbg_conn, int,
+                  S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_conn,
+                "Turn on debugging for connections in scsi_transport_iscsi "
+                "module. Set to 1 to turn on, and zero to turn off. Default "
+                "is off.");
+
+#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)             \
+       do {                                                            \
+               if (dbg_session)                                        \
+                       iscsi_cls_session_printk(KERN_INFO, _session,   \
+                                                "%s: " dbg_fmt,        \
+                                                __func__, ##arg);      \
+       } while (0);
+
+#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)                   \
+       do {                                                            \
+               if (dbg_conn)                                           \
+                       iscsi_cls_conn_printk(KERN_INFO, _conn,         \
+                                             "%s: " dbg_fmt,           \
+                                             __func__, ##arg); \
+       } while (0);
+
 struct iscsi_internal {
-       int daemon_pid;
        struct scsi_transport_template t;
        struct iscsi_transport *iscsi_transport;
        struct list_head list;
@@ -246,30 +277,13 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
        memset(ihost, 0, sizeof(*ihost));
        atomic_set(&ihost->nr_scans, 0);
        mutex_init(&ihost->mutex);
-
-       snprintf(ihost->scan_workq_name, sizeof(ihost->scan_workq_name),
-                "iscsi_scan_%d", shost->host_no);
-       ihost->scan_workq = create_singlethread_workqueue(
-                                               ihost->scan_workq_name);
-       if (!ihost->scan_workq)
-               return -ENOMEM;
-       return 0;
-}
-
-static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
-                            struct device *cdev)
-{
-       struct Scsi_Host *shost = dev_to_shost(dev);
-       struct iscsi_cls_host *ihost = shost->shost_data;
-
-       destroy_workqueue(ihost->scan_workq);
        return 0;
 }
 
 static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
                               "iscsi_host",
                               iscsi_setup_host,
-                              iscsi_remove_host,
+                              NULL,
                               NULL);
 
 static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
@@ -374,7 +388,7 @@ int iscsi_session_chkready(struct iscsi_cls_session *session)
                err = 0;
                break;
        case ISCSI_SESSION_FAILED:
-               err = DID_TRANSPORT_DISRUPTED << 16;
+               err = DID_IMM_RETRY << 16;
                break;
        case ISCSI_SESSION_FREE:
                err = DID_TRANSPORT_FAILFAST << 16;
@@ -395,6 +409,7 @@ static void iscsi_session_release(struct device *dev)
 
        shost = iscsi_session_to_shost(session);
        scsi_host_put(shost);
+       ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
        kfree(session);
 }
 
@@ -459,6 +474,9 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
                return 0;
 
        session = iscsi_dev_to_session(dev);
+
+       ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
+
        shost = iscsi_session_to_shost(session);
        ihost = shost->shost_data;
 
@@ -466,8 +484,7 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
        spin_lock_irqsave(&session->lock, flags);
        if (session->state != ISCSI_SESSION_LOGGED_IN) {
                spin_unlock_irqrestore(&session->lock, flags);
-               mutex_unlock(&ihost->mutex);
-               return 0;
+               goto user_scan_exit;
        }
        id = session->target_id;
        spin_unlock_irqrestore(&session->lock, flags);
@@ -480,7 +497,10 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
                        scsi_scan_target(&session->dev, 0, id,
                                         scan_data->lun, 1);
        }
+
+user_scan_exit:
        mutex_unlock(&ihost->mutex);
+       ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
        return 0;
 }
 
@@ -540,7 +560,9 @@ static void session_recovery_timedout(struct work_struct *work)
        if (session->transport->session_recovery_timedout)
                session->transport->session_recovery_timedout(session);
 
+       ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
        scsi_target_unblock(&session->dev);
+       ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
 }
 
 static void __iscsi_unblock_session(struct work_struct *work)
@@ -552,6 +574,7 @@ static void __iscsi_unblock_session(struct work_struct *work)
        struct iscsi_cls_host *ihost = shost->shost_data;
        unsigned long flags;
 
+       ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
        /*
         * The recovery and unblock work get run from the same workqueue,
         * so try to cancel it if it was going to run after this unblock.
@@ -568,9 +591,10 @@ static void __iscsi_unblock_session(struct work_struct *work)
         * scanning from userspace).
         */
        if (shost->hostt->scan_finished) {
-               if (queue_work(ihost->scan_workq, &session->scan_work))
+               if (scsi_queue_work(shost, &session->scan_work))
                        atomic_inc(&ihost->nr_scans);
        }
+       ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
 }
 
 /**
@@ -597,10 +621,12 @@ static void __iscsi_block_session(struct work_struct *work)
                                     block_work);
        unsigned long flags;
 
+       ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
        spin_lock_irqsave(&session->lock, flags);
        session->state = ISCSI_SESSION_FAILED;
        spin_unlock_irqrestore(&session->lock, flags);
        scsi_target_block(&session->dev);
+       ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
        queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
                           session->recovery_tmo * HZ);
 }
@@ -620,6 +646,8 @@ static void __iscsi_unbind_session(struct work_struct *work)
        struct iscsi_cls_host *ihost = shost->shost_data;
        unsigned long flags;
 
+       ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
+
        /* Prevent new scans and make sure scanning is not in progress */
        mutex_lock(&ihost->mutex);
        spin_lock_irqsave(&session->lock, flags);
@@ -634,14 +662,7 @@ static void __iscsi_unbind_session(struct work_struct *work)
 
        scsi_remove_target(&session->dev);
        iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
-}
-
-static int iscsi_unbind_session(struct iscsi_cls_session *session)
-{
-       struct Scsi_Host *shost = iscsi_session_to_shost(session);
-       struct iscsi_cls_host *ihost = shost->shost_data;
-
-       return queue_work(ihost->scan_workq, &session->unbind_work);
+       ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
 }
 
 struct iscsi_cls_session *
@@ -673,6 +694,8 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
        device_initialize(&session->dev);
        if (dd_size)
                session->dd_data = &session[1];
+
+       ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
        return session;
 }
 EXPORT_SYMBOL_GPL(iscsi_alloc_session);
@@ -718,6 +741,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
                                                 "Too many iscsi targets. Max "
                                                 "number of targets is %d.\n",
                                                 ISCSI_MAX_TARGET - 1);
+                       err = -EOVERFLOW;
                        goto release_host;
                }
        }
@@ -737,6 +761,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
        spin_unlock_irqrestore(&sesslock, flags);
 
        iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
+       ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
        return 0;
 
 release_host:
@@ -777,6 +802,7 @@ static void iscsi_conn_release(struct device *dev)
        struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
        struct device *parent = conn->dev.parent;
 
+       ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
        kfree(conn);
        put_device(parent);
 }
@@ -796,10 +822,11 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
 void iscsi_remove_session(struct iscsi_cls_session *session)
 {
        struct Scsi_Host *shost = iscsi_session_to_shost(session);
-       struct iscsi_cls_host *ihost = shost->shost_data;
        unsigned long flags;
        int err;
 
+       ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");
+
        spin_lock_irqsave(&sesslock, flags);
        list_del(&session->sess_list);
        spin_unlock_irqrestore(&sesslock, flags);
@@ -821,7 +848,7 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
 
        scsi_target_unblock(&session->dev);
        /* flush running scans then delete devices */
-       flush_workqueue(ihost->scan_workq);
+       scsi_flush_work(shost);
        __iscsi_unbind_session(&session->unbind_work);
 
        /* hw iscsi may not have removed all connections from session */
@@ -833,12 +860,15 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
                                         "for session. Error %d.\n", err);
 
        transport_unregister_device(&session->dev);
+
+       ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
        device_del(&session->dev);
 }
 EXPORT_SYMBOL_GPL(iscsi_remove_session);
 
 void iscsi_free_session(struct iscsi_cls_session *session)
 {
+       ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
        iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
        put_device(&session->dev);
 }
@@ -854,6 +884,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_session);
 int iscsi_destroy_session(struct iscsi_cls_session *session)
 {
        iscsi_remove_session(session);
+       ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n");
        iscsi_free_session(session);
        return 0;
 }
@@ -911,6 +942,8 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
        list_add(&conn->conn_list, &connlist);
        conn->active = 1;
        spin_unlock_irqrestore(&connlock, flags);
+
+       ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
        return conn;
 
 release_parent_ref:
@@ -938,6 +971,7 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
        spin_unlock_irqrestore(&connlock, flags);
 
        transport_unregister_device(&conn->dev);
+       ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n");
        device_unregister(&conn->dev);
        return 0;
 }
@@ -964,31 +998,9 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt)
 }
 
 static int
-iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
+iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
 {
-       int rc;
-
-       rc = netlink_broadcast(nls, skb, 0, 1, gfp);
-       if (rc < 0) {
-               printk(KERN_ERR "iscsi: can not broadcast skb (%d)\n", rc);
-               return rc;
-       }
-
-       return 0;
-}
-
-static int
-iscsi_unicast_skb(struct sk_buff *skb, int pid)
-{
-       int rc;
-
-       rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT);
-       if (rc < 0) {
-               printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc);
-               return rc;
-       }
-
-       return 0;
+       return nlmsg_multicast(nls, skb, 0, group, gfp);
 }
 
 int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
@@ -1014,7 +1026,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        memset(ev, 0, sizeof(*ev));
        ev->transport_handle = iscsi_handle(conn->transport);
@@ -1025,10 +1037,45 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
        memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
        memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
 
-       return iscsi_unicast_skb(skb, priv->daemon_pid);
+       return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
 }
 EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
 
+int iscsi_offload_mesg(struct Scsi_Host *shost,
+                      struct iscsi_transport *transport, uint32_t type,
+                      char *data, uint16_t data_size)
+{
+       struct nlmsghdr *nlh;
+       struct sk_buff *skb;
+       struct iscsi_uevent *ev;
+       int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+       skb = alloc_skb(len, GFP_ATOMIC);
+       if (!skb) {
+               printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
+               return -ENOMEM;
+       }
+
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+       ev = NLMSG_DATA(nlh);
+       memset(ev, 0, sizeof(*ev));
+       ev->type = type;
+       ev->transport_handle = iscsi_handle(transport);
+       switch (type) {
+       case ISCSI_KEVENT_PATH_REQ:
+               ev->r.req_path.host_no = shost->host_no;
+               break;
+       case ISCSI_KEVENT_IF_DOWN:
+               ev->r.notify_if_down.host_no = shost->host_no;
+               break;
+       }
+
+       memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+       return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
+}
+EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
+
 void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
 {
        struct nlmsghdr *nlh;
@@ -1048,7 +1095,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
                return;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        ev->transport_handle = iscsi_handle(conn->transport);
        ev->type = ISCSI_KEVENT_CONN_ERROR;
@@ -1056,7 +1103,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
        ev->r.connerror.cid = conn->cid;
        ev->r.connerror.sid = iscsi_conn_get_sid(conn);
 
-       iscsi_broadcast_skb(skb, GFP_ATOMIC);
+       iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
 
        iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
                              error);
@@ -1064,8 +1111,8 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
 EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
 
 static int
-iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
-                     void *payload, int size)
+iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
+                   void *payload, int size)
 {
        struct sk_buff  *skb;
        struct nlmsghdr *nlh;
@@ -1079,10 +1126,10 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
        nlh->nlmsg_flags = flags;
        memcpy(NLMSG_DATA(nlh), payload, size);
-       return iscsi_unicast_skb(skb, pid);
+       return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
 }
 
 static int
@@ -1119,7 +1166,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                        return -ENOMEM;
                }
 
-               nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0,
+               nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
                                      (len - sizeof(*nlhstat)), 0);
                evstat = NLMSG_DATA(nlhstat);
                memset(evstat, 0, sizeof(*evstat));
@@ -1143,7 +1190,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
                skb_trim(skbstat, NLMSG_ALIGN(actual_size));
                nlhstat->nlmsg_len = actual_size;
 
-               err = iscsi_unicast_skb(skbstat, priv->daemon_pid);
+               err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
+                                         GFP_ATOMIC);
        } while (err < 0 && err != -ECONNREFUSED);
 
        return err;
@@ -1177,7 +1225,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
                return -ENOMEM;
        }
 
-       nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0);
+       nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
        ev = NLMSG_DATA(nlh);
        ev->transport_handle = iscsi_handle(session->transport);
 
@@ -1206,12 +1254,15 @@ int iscsi_session_event(struct iscsi_cls_session *session,
         * this will occur if the daemon is not up, so we just warn
         * the user and when the daemon is restarted it will handle it
         */
-       rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
-       if (rc < 0)
+       rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+       if (rc == -ESRCH)
                iscsi_cls_session_printk(KERN_ERR, session,
                                         "Cannot notify userspace of session "
                                         "event %u. Check iscsi daemon\n",
                                         event);
+
+       ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
+                               event, rc);
        return rc;
 }
 EXPORT_SYMBOL_GPL(iscsi_session_event);
@@ -1223,15 +1274,18 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
 {
        struct iscsi_transport *transport = priv->iscsi_transport;
        struct iscsi_cls_session *session;
-       uint32_t host_no;
+       struct Scsi_Host *shost;
 
        session = transport->create_session(ep, cmds_max, queue_depth,
-                                           initial_cmdsn, &host_no);
+                                           initial_cmdsn);
        if (!session)
                return -ENOMEM;
 
-       ev->r.c_session_ret.host_no = host_no;
+       shost = iscsi_session_to_shost(session);
+       ev->r.c_session_ret.host_no = shost->host_no;
        ev->r.c_session_ret.sid = session->sid;
+       ISCSI_DBG_TRANS_SESSION(session,
+                               "Completed creating transport session\n");
        return 0;
 }
 
@@ -1257,6 +1311,8 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
 
        ev->r.c_conn_ret.sid = session->sid;
        ev->r.c_conn_ret.cid = conn->cid;
+
+       ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
        return 0;
 }
 
@@ -1269,8 +1325,10 @@ iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev
        if (!conn)
                return -EINVAL;
 
+       ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
        if (transport->destroy_conn)
                transport->destroy_conn(conn);
+
        return 0;
 }
 
@@ -1301,26 +1359,54 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
        return err;
 }
 
+static int iscsi_if_ep_connect(struct iscsi_transport *transport,
+                              struct iscsi_uevent *ev, int msg_type)
+{
+       struct iscsi_endpoint *ep;
+       struct sockaddr *dst_addr;
+       struct Scsi_Host *shost = NULL;
+       int non_blocking, err = 0;
+
+       if (!transport->ep_connect)
+               return -EINVAL;
+
+       if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
+               shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
+               if (!shost) {
+                       printk(KERN_ERR "ep connect failed. Could not find "
+                              "host no %u\n",
+                              ev->u.ep_connect_through_host.host_no);
+                       return -ENODEV;
+               }
+               non_blocking = ev->u.ep_connect_through_host.non_blocking;
+       } else
+               non_blocking = ev->u.ep_connect.non_blocking;
+
+       dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+       ep = transport->ep_connect(shost, dst_addr, non_blocking);
+       if (IS_ERR(ep)) {
+               err = PTR_ERR(ep);
+               goto release_host;
+       }
+
+       ev->r.ep_connect_ret.handle = ep->id;
+release_host:
+       if (shost)
+               scsi_host_put(shost);
+       return err;
+}
+
 static int
 iscsi_if_transport_ep(struct iscsi_transport *transport,
                      struct iscsi_uevent *ev, int msg_type)
 {
        struct iscsi_endpoint *ep;
-       struct sockaddr *dst_addr;
        int rc = 0;
 
        switch (msg_type) {
+       case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
        case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
-               if (!transport->ep_connect)
-                       return -EINVAL;
-
-               dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
-               ep = transport->ep_connect(dst_addr,
-                                          ev->u.ep_connect.non_blocking);
-               if (IS_ERR(ep))
-                       return PTR_ERR(ep);
-
-               ev->r.ep_connect_ret.handle = ep->id;
+               rc = iscsi_if_ep_connect(transport, ev, msg_type);
                break;
        case ISCSI_UEVENT_TRANSPORT_EP_POLL:
                if (!transport->ep_poll)
@@ -1398,7 +1484,31 @@ iscsi_set_host_param(struct iscsi_transport *transport,
 }
 
 static int
-iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_path *params;
+       int err;
+
+       if (!transport->set_path)
+               return -ENOSYS;
+
+       shost = scsi_host_lookup(ev->u.set_path.host_no);
+       if (!shost) {
+               printk(KERN_ERR "set path could not find host no %u\n",
+                      ev->u.set_path.host_no);
+               return -ENODEV;
+       }
+
+       params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
+       err = transport->set_path(shost, params);
+
+       scsi_host_put(shost);
+       return err;
+}
+
+static int
+iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
 {
        int err = 0;
        struct iscsi_uevent *ev = NLMSG_DATA(nlh);
@@ -1408,6 +1518,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct iscsi_cls_conn *conn;
        struct iscsi_endpoint *ep = NULL;
 
+       if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
+               *group = ISCSI_NL_GRP_UIP;
+       else
+               *group = ISCSI_NL_GRP_ISCSID;
+
        priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
        if (!priv)
                return -EINVAL;
@@ -1416,8 +1531,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (!try_module_get(transport->owner))
                return -EINVAL;
 
-       priv->daemon_pid = NETLINK_CREDS(skb)->pid;
-
        switch (nlh->nlmsg_type) {
        case ISCSI_UEVENT_CREATE_SESSION:
                err = iscsi_if_create_session(priv, ep, ev,
@@ -1447,7 +1560,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case ISCSI_UEVENT_UNBIND_SESSION:
                session = iscsi_session_lookup(ev->u.d_session.sid);
                if (session)
-                       iscsi_unbind_session(session);
+                       scsi_queue_work(iscsi_session_to_shost(session),
+                                       &session->unbind_work);
                else
                        err = -EINVAL;
                break;
@@ -1501,6 +1615,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
        case ISCSI_UEVENT_TRANSPORT_EP_POLL:
        case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
+       case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
                err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
                break;
        case ISCSI_UEVENT_TGT_DSCVR:
@@ -1509,6 +1624,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case ISCSI_UEVENT_SET_HOST_PARAM:
                err = iscsi_set_host_param(transport, ev);
                break;
+       case ISCSI_UEVENT_PATH_UPDATE:
+               err = iscsi_set_path(transport, ev);
+               break;
        default:
                err = -ENOSYS;
                break;
@@ -1531,6 +1649,7 @@ iscsi_if_rx(struct sk_buff *skb)
                uint32_t rlen;
                struct nlmsghdr *nlh;
                struct iscsi_uevent *ev;
+               uint32_t group;
 
                nlh = nlmsg_hdr(skb);
                if (nlh->nlmsg_len < sizeof(*nlh) ||
@@ -1543,7 +1662,7 @@ iscsi_if_rx(struct sk_buff *skb)
                if (rlen > skb->len)
                        rlen = skb->len;
 
-               err = iscsi_if_recv_msg(skb, nlh);
+               err = iscsi_if_recv_msg(skb, nlh, &group);
                if (err) {
                        ev->type = ISCSI_KEVENT_IF_ERROR;
                        ev->iferror = err;
@@ -1557,8 +1676,7 @@ iscsi_if_rx(struct sk_buff *skb)
                         */
                        if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
                                break;
-                       err = iscsi_if_send_reply(
-                               NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq,
+                       err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
                                nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
                } while (err < 0 && err != -ECONNREFUSED);
                skb_pull(skb, rlen);
@@ -1806,11 +1924,9 @@ iscsi_register_transport(struct iscsi_transport *tt)
        if (!priv)
                return NULL;
        INIT_LIST_HEAD(&priv->list);
-       priv->daemon_pid = -1;
        priv->iscsi_transport = tt;
        priv->t.user_scan = iscsi_user_scan;
-       if (!(tt->caps & CAP_DATA_PATH_OFFLOAD))
-               priv->t.create_work_queue = 1;
+       priv->t.create_work_queue = 1;
 
        priv->dev.class = &iscsi_transport_class;
        dev_set_name(&priv->dev, "%s", tt->name);