]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/scsi/libfc/fc_rport.c
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[net-next-2.6.git] / drivers / scsi / libfc / fc_rport.c
index 2aa599340106e62bc090a2c2aaf7457d7634f284..b9f2286fe0cbc9ef849cb8e2a713d9d0b11776f7 100644 (file)
@@ -60,6 +60,7 @@
 
 struct workqueue_struct *rport_event_queue;
 
+static void fc_rport_enter_flogi(struct fc_rport_priv *);
 static void fc_rport_enter_plogi(struct fc_rport_priv *);
 static void fc_rport_enter_prli(struct fc_rport_priv *);
 static void fc_rport_enter_rtv(struct fc_rport_priv *);
@@ -67,14 +68,10 @@ static void fc_rport_enter_ready(struct fc_rport_priv *);
 static void fc_rport_enter_logo(struct fc_rport_priv *);
 static void fc_rport_enter_adisc(struct fc_rport_priv *);
 
-static void fc_rport_recv_plogi_req(struct fc_lport *,
-                                   struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prli_req(struct fc_rport_priv *,
-                                  struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prlo_req(struct fc_rport_priv *,
-                                  struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_logo_req(struct fc_lport *,
-                                  struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *);
+static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *);
 static void fc_rport_timeout(struct work_struct *);
 static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
 static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
@@ -82,27 +79,29 @@ static void fc_rport_work(struct work_struct *);
 
 static const char *fc_rport_state_names[] = {
        [RPORT_ST_INIT] = "Init",
+       [RPORT_ST_FLOGI] = "FLOGI",
+       [RPORT_ST_PLOGI_WAIT] = "PLOGI_WAIT",
        [RPORT_ST_PLOGI] = "PLOGI",
        [RPORT_ST_PRLI] = "PRLI",
        [RPORT_ST_RTV] = "RTV",
        [RPORT_ST_READY] = "Ready",
-       [RPORT_ST_LOGO] = "LOGO",
        [RPORT_ST_ADISC] = "ADISC",
        [RPORT_ST_DELETE] = "Delete",
-       [RPORT_ST_RESTART] = "Restart",
 };
 
 /**
  * fc_rport_lookup() - Lookup a remote port by port_id
  * @lport:   The local port to lookup the remote port on
  * @port_id: The remote port ID to look up
+ *
+ * The caller must hold either disc_mutex or rcu_read_lock().
  */
 static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
                                             u32 port_id)
 {
        struct fc_rport_priv *rdata;
 
-       list_for_each_entry(rdata, &lport->disc.rports, peers)
+       list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
                if (rdata->ids.port_id == port_id)
                        return rdata;
        return NULL;
@@ -126,7 +125,7 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
        if (rdata)
                return rdata;
 
-       rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+       rdata = kzalloc(sizeof(*rdata) + lport->rport_priv_size, GFP_KERNEL);
        if (!rdata)
                return NULL;
 
@@ -147,10 +146,22 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
        INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
        INIT_WORK(&rdata->event_work, fc_rport_work);
        if (port_id != FC_FID_DIR_SERV)
-               list_add(&rdata->peers, &lport->disc.rports);
+               list_add_rcu(&rdata->peers, &lport->disc.rports);
        return rdata;
 }
 
+/**
+ * fc_rport_free_rcu() - Free a remote port
+ * @rcu: The rcu_head structure inside the remote port
+ */
+static void fc_rport_free_rcu(struct rcu_head *rcu)
+{
+       struct fc_rport_priv *rdata;
+
+       rdata = container_of(rcu, struct fc_rport_priv, rcu);
+       kfree(rdata);
+}
+
 /**
  * fc_rport_destroy() - Free a remote port after last reference is released
  * @kref: The remote port's kref
@@ -160,7 +171,7 @@ static void fc_rport_destroy(struct kref *kref)
        struct fc_rport_priv *rdata;
 
        rdata = container_of(kref, struct fc_rport_priv, kref);
-       kfree(rdata);
+       call_rcu(&rdata->rcu, fc_rport_free_rcu);
 }
 
 /**
@@ -194,7 +205,7 @@ EXPORT_SYMBOL(fc_set_rport_loss_tmo);
 /**
  * fc_plogi_get_maxframe() - Get the maximum payload from the common service
  *                          parameters in a FLOGI frame
- * @flp:    The FLOGI payload
+ * @flp:    The FLOGI or PLOGI payload
  * @maxval: The maximum frame size upper limit; this may be less than what
  *         is in the service parameters
  */
@@ -246,7 +257,6 @@ static void fc_rport_work(struct work_struct *work)
        struct fc_rport_operations *rport_ops;
        struct fc_rport_identifiers ids;
        struct fc_rport *rport;
-       int restart = 0;
 
        mutex_lock(&rdata->rp_mutex);
        event = rdata->event;
@@ -259,6 +269,7 @@ static void fc_rport_work(struct work_struct *work)
        case RPORT_EV_READY:
                ids = rdata->ids;
                rdata->event = RPORT_EV_NONE;
+               rdata->major_retries = 0;
                kref_get(&rdata->kref);
                mutex_unlock(&rdata->rp_mutex);
 
@@ -298,24 +309,6 @@ static void fc_rport_work(struct work_struct *work)
                port_id = rdata->ids.port_id;
                mutex_unlock(&rdata->rp_mutex);
 
-               if (port_id != FC_FID_DIR_SERV) {
-                       /*
-                        * We must drop rp_mutex before taking disc_mutex.
-                        * Re-evaluate state to allow for restart.
-                        * A transition to RESTART state must only happen
-                        * while disc_mutex is held and rdata is on the list.
-                        */
-                       mutex_lock(&lport->disc.disc_mutex);
-                       mutex_lock(&rdata->rp_mutex);
-                       if (rdata->rp_state == RPORT_ST_RESTART)
-                               restart = 1;
-                       else
-                               list_del(&rdata->peers);
-                       rdata->event = RPORT_EV_NONE;
-                       mutex_unlock(&rdata->rp_mutex);
-                       mutex_unlock(&lport->disc.disc_mutex);
-               }
-
                if (rport_ops && rport_ops->event_callback) {
                        FC_RPORT_DBG(rdata, "callback ev %d\n", event);
                        rport_ops->event_callback(lport, rdata, event);
@@ -336,13 +329,37 @@ static void fc_rport_work(struct work_struct *work)
                        mutex_unlock(&rdata->rp_mutex);
                        fc_remote_port_delete(rport);
                }
-               if (restart) {
-                       mutex_lock(&rdata->rp_mutex);
-                       FC_RPORT_DBG(rdata, "work restart\n");
-                       fc_rport_enter_plogi(rdata);
+
+               mutex_lock(&lport->disc.disc_mutex);
+               mutex_lock(&rdata->rp_mutex);
+               if (rdata->rp_state == RPORT_ST_DELETE) {
+                       if (port_id == FC_FID_DIR_SERV) {
+                               rdata->event = RPORT_EV_NONE;
+                               mutex_unlock(&rdata->rp_mutex);
+                       } else if ((rdata->flags & FC_RP_STARTED) &&
+                                  rdata->major_retries <
+                                  lport->max_rport_retry_count) {
+                               rdata->major_retries++;
+                               rdata->event = RPORT_EV_NONE;
+                               FC_RPORT_DBG(rdata, "work restart\n");
+                               fc_rport_enter_flogi(rdata);
+                               mutex_unlock(&rdata->rp_mutex);
+                       } else {
+                               FC_RPORT_DBG(rdata, "work delete\n");
+                               list_del_rcu(&rdata->peers);
+                               mutex_unlock(&rdata->rp_mutex);
+                               kref_put(&rdata->kref, lport->tt.rport_destroy);
+                       }
+               } else {
+                       /*
+                        * Re-open for events.  Reissue READY event if ready.
+                        */
+                       rdata->event = RPORT_EV_NONE;
+                       if (rdata->rp_state == RPORT_ST_READY)
+                               fc_rport_enter_ready(rdata);
                        mutex_unlock(&rdata->rp_mutex);
-               } else
-                       kref_put(&rdata->kref, lport->tt.rport_destroy);
+               }
+               mutex_unlock(&lport->disc.disc_mutex);
                break;
 
        default:
@@ -367,20 +384,18 @@ int fc_rport_login(struct fc_rport_priv *rdata)
 {
        mutex_lock(&rdata->rp_mutex);
 
+       rdata->flags |= FC_RP_STARTED;
        switch (rdata->rp_state) {
        case RPORT_ST_READY:
                FC_RPORT_DBG(rdata, "ADISC port\n");
                fc_rport_enter_adisc(rdata);
                break;
-       case RPORT_ST_RESTART:
-               break;
        case RPORT_ST_DELETE:
                FC_RPORT_DBG(rdata, "Restart deleted port\n");
-               fc_rport_state_enter(rdata, RPORT_ST_RESTART);
                break;
        default:
                FC_RPORT_DBG(rdata, "Login to port\n");
-               fc_rport_enter_plogi(rdata);
+               fc_rport_enter_flogi(rdata);
                break;
        }
        mutex_unlock(&rdata->rp_mutex);
@@ -431,15 +446,12 @@ int fc_rport_logoff(struct fc_rport_priv *rdata)
 
        FC_RPORT_DBG(rdata, "Remove port\n");
 
+       rdata->flags &= ~FC_RP_STARTED;
        if (rdata->rp_state == RPORT_ST_DELETE) {
                FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
                goto out;
        }
-
-       if (rdata->rp_state == RPORT_ST_RESTART)
-               FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n");
-       else
-               fc_rport_enter_logo(rdata);
+       fc_rport_enter_logo(rdata);
 
        /*
         * Change the state to Delete so that we discard
@@ -485,6 +497,9 @@ static void fc_rport_timeout(struct work_struct *work)
        mutex_lock(&rdata->rp_mutex);
 
        switch (rdata->rp_state) {
+       case RPORT_ST_FLOGI:
+               fc_rport_enter_flogi(rdata);
+               break;
        case RPORT_ST_PLOGI:
                fc_rport_enter_plogi(rdata);
                break;
@@ -494,16 +509,13 @@ static void fc_rport_timeout(struct work_struct *work)
        case RPORT_ST_RTV:
                fc_rport_enter_rtv(rdata);
                break;
-       case RPORT_ST_LOGO:
-               fc_rport_enter_logo(rdata);
-               break;
        case RPORT_ST_ADISC:
                fc_rport_enter_adisc(rdata);
                break;
+       case RPORT_ST_PLOGI_WAIT:
        case RPORT_ST_READY:
        case RPORT_ST_INIT:
        case RPORT_ST_DELETE:
-       case RPORT_ST_RESTART:
                break;
        }
 
@@ -525,8 +537,9 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
                     fc_rport_state(rdata), rdata->retries);
 
        switch (rdata->rp_state) {
+       case RPORT_ST_FLOGI:
        case RPORT_ST_PLOGI:
-       case RPORT_ST_LOGO:
+               rdata->flags &= ~FC_RP_STARTED;
                fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
                break;
        case RPORT_ST_RTV:
@@ -536,8 +549,8 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
        case RPORT_ST_ADISC:
                fc_rport_enter_logo(rdata);
                break;
+       case RPORT_ST_PLOGI_WAIT:
        case RPORT_ST_DELETE:
-       case RPORT_ST_RESTART:
        case RPORT_ST_READY:
        case RPORT_ST_INIT:
                break;
@@ -579,7 +592,250 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses
+ * fc_rport_login_complete() - Handle parameters and completion of p-mp login.
+ * @rdata:  The remote port which we logged into or which logged into us.
+ * @fp:     The FLOGI or PLOGI request or response frame
+ *
+ * Returns non-zero error if a problem is detected with the frame.
+ * Does not free the frame.
+ *
+ * This is only used in point-to-multipoint mode for FIP currently.
+ */
+static int fc_rport_login_complete(struct fc_rport_priv *rdata,
+                                  struct fc_frame *fp)
+{
+       struct fc_lport *lport = rdata->local_port;
+       struct fc_els_flogi *flogi;
+       unsigned int e_d_tov;
+       u16 csp_flags;
+
+       flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+       if (!flogi)
+               return -EINVAL;
+
+       csp_flags = ntohs(flogi->fl_csp.sp_features);
+
+       if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+               if (csp_flags & FC_SP_FT_FPORT) {
+                       FC_RPORT_DBG(rdata, "Fabric bit set in FLOGI\n");
+                       return -EINVAL;
+               }
+       } else {
+
+               /*
+                * E_D_TOV is not valid on an incoming FLOGI request.
+                */
+               e_d_tov = ntohl(flogi->fl_csp.sp_e_d_tov);
+               if (csp_flags & FC_SP_FT_EDTR)
+                       e_d_tov /= 1000000;
+               if (e_d_tov > rdata->e_d_tov)
+                       rdata->e_d_tov = e_d_tov;
+       }
+       rdata->maxframe_size = fc_plogi_get_maxframe(flogi, lport->mfs);
+       return 0;
+}
+
+/**
+ * fc_rport_flogi_resp() - Handle response to FLOGI request for p-mp mode
+ * @sp:            The sequence that the FLOGI was on
+ * @fp:            The FLOGI response frame
+ * @rp_arg: The remote port that received the FLOGI response
+ */
+void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+                        void *rp_arg)
+{
+       struct fc_rport_priv *rdata = rp_arg;
+       struct fc_lport *lport = rdata->local_port;
+       struct fc_els_flogi *flogi;
+       unsigned int r_a_tov;
+
+       FC_RPORT_DBG(rdata, "Received a FLOGI %s\n", fc_els_resp_type(fp));
+
+       if (fp == ERR_PTR(-FC_EX_CLOSED))
+               return;
+
+       mutex_lock(&rdata->rp_mutex);
+
+       if (rdata->rp_state != RPORT_ST_FLOGI) {
+               FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state "
+                            "%s\n", fc_rport_state(rdata));
+               if (IS_ERR(fp))
+                       goto err;
+               goto out;
+       }
+
+       if (IS_ERR(fp)) {
+               fc_rport_error(rdata, fp);
+               goto err;
+       }
+
+       if (fc_frame_payload_op(fp) != ELS_LS_ACC)
+               goto bad;
+       if (fc_rport_login_complete(rdata, fp))
+               goto bad;
+
+       flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+       if (!flogi)
+               goto bad;
+       r_a_tov = ntohl(flogi->fl_csp.sp_r_a_tov);
+       if (r_a_tov > rdata->r_a_tov)
+               rdata->r_a_tov = r_a_tov;
+
+       if (rdata->ids.port_name < lport->wwpn)
+               fc_rport_enter_plogi(rdata);
+       else
+               fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+out:
+       fc_frame_free(fp);
+err:
+       mutex_unlock(&rdata->rp_mutex);
+       kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+       return;
+bad:
+       FC_RPORT_DBG(rdata, "Bad FLOGI response\n");
+       fc_rport_error_retry(rdata, fp);
+       goto out;
+}
+
+/**
+ * fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp
+ * @rdata: The remote port to send a FLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
+{
+       struct fc_lport *lport = rdata->local_port;
+       struct fc_frame *fp;
+
+       if (!lport->point_to_multipoint)
+               return fc_rport_enter_plogi(rdata);
+
+       FC_RPORT_DBG(rdata, "Entered FLOGI state from %s state\n",
+                    fc_rport_state(rdata));
+
+       fc_rport_state_enter(rdata, RPORT_ST_FLOGI);
+
+       fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+       if (!fp)
+               return fc_rport_error_retry(rdata, fp);
+
+       if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_FLOGI,
+                                 fc_rport_flogi_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
+       else
+               kref_get(&rdata->kref);
+}
+
+/**
+ * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode
+ * @lport: The local port that received the PLOGI request
+ * @rx_fp: The PLOGI request frame
+ */
+static void fc_rport_recv_flogi_req(struct fc_lport *lport,
+                                   struct fc_frame *rx_fp)
+{
+       struct fc_disc *disc;
+       struct fc_els_flogi *flp;
+       struct fc_rport_priv *rdata;
+       struct fc_frame *fp = rx_fp;
+       struct fc_seq_els_data rjt_data;
+       u32 sid;
+
+       sid = fc_frame_sid(fp);
+
+       FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
+
+       disc = &lport->disc;
+       mutex_lock(&disc->disc_mutex);
+
+       if (!lport->point_to_multipoint) {
+               rjt_data.reason = ELS_RJT_UNSUP;
+               rjt_data.explan = ELS_EXPL_NONE;
+               goto reject;
+       }
+
+       flp = fc_frame_payload_get(fp, sizeof(*flp));
+       if (!flp) {
+               rjt_data.reason = ELS_RJT_LOGIC;
+               rjt_data.explan = ELS_EXPL_INV_LEN;
+               goto reject;
+       }
+
+       rdata = lport->tt.rport_lookup(lport, sid);
+       if (!rdata) {
+               rjt_data.reason = ELS_RJT_FIP;
+               rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
+               goto reject;
+       }
+       mutex_lock(&rdata->rp_mutex);
+
+       FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n",
+                    fc_rport_state(rdata));
+
+       switch (rdata->rp_state) {
+       case RPORT_ST_INIT:
+       case RPORT_ST_DELETE:
+               mutex_unlock(&rdata->rp_mutex);
+               rjt_data.reason = ELS_RJT_FIP;
+               rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
+               goto reject;
+       case RPORT_ST_FLOGI:
+       case RPORT_ST_PLOGI_WAIT:
+       case RPORT_ST_PLOGI:
+               break;
+       case RPORT_ST_PRLI:
+       case RPORT_ST_RTV:
+       case RPORT_ST_READY:
+       case RPORT_ST_ADISC:
+               /*
+                * Set the remote port to be deleted and to then restart.
+                * This queues work to be sure exchanges are reset.
+                */
+               fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+               mutex_unlock(&rdata->rp_mutex);
+               rjt_data.reason = ELS_RJT_BUSY;
+               rjt_data.explan = ELS_EXPL_NONE;
+               goto reject;
+       }
+       if (fc_rport_login_complete(rdata, fp)) {
+               mutex_unlock(&rdata->rp_mutex);
+               rjt_data.reason = ELS_RJT_LOGIC;
+               rjt_data.explan = ELS_EXPL_NONE;
+               goto reject;
+       }
+
+       fp = fc_frame_alloc(lport, sizeof(*flp));
+       if (!fp)
+               goto out;
+
+       fc_flogi_fill(lport, fp);
+       flp = fc_frame_payload_get(fp, sizeof(*flp));
+       flp->fl_cmd = ELS_LS_ACC;
+
+       fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
+
+       if (rdata->ids.port_name < lport->wwpn)
+               fc_rport_enter_plogi(rdata);
+       else
+               fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+out:
+       mutex_unlock(&rdata->rp_mutex);
+       mutex_unlock(&disc->disc_mutex);
+       fc_frame_free(rx_fp);
+       return;
+
+reject:
+       mutex_unlock(&disc->disc_mutex);
+       lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+       fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_plogi_resp() - Handler for ELS PLOGI responses
  * @sp:               The sequence the PLOGI is on
  * @fp:               The PLOGI response frame
  * @rdata_arg: The remote port that sent the PLOGI response
@@ -594,7 +850,6 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
        struct fc_rport_priv *rdata = rdata_arg;
        struct fc_lport *lport = rdata->local_port;
        struct fc_els_flogi *plp = NULL;
-       unsigned int tov;
        u16 csp_seq;
        u16 cssp_seq;
        u8 op;
@@ -622,11 +877,8 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
                rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
                rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
 
-               tov = ntohl(plp->fl_csp.sp_e_d_tov);
-               if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
-                       tov /= 1000000;
-               if (tov > rdata->e_d_tov)
-                       rdata->e_d_tov = tov;
+               if (lport->point_to_multipoint)
+                       fc_rport_login_complete(rdata, fp);
                csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
                cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
                if (cssp_seq < csp_seq)
@@ -664,6 +916,7 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
        rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
        fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
        if (!fp) {
+               FC_RPORT_DBG(rdata, "%s frame alloc failed\n", __func__);
                fc_rport_error_retry(rdata, fp);
                return;
        }
@@ -698,6 +951,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
        u32 roles = FC_RPORT_ROLE_UNKNOWN;
        u32 fcp_parm = 0;
        u8 op;
+       u8 resp_code = 0;
 
        mutex_lock(&rdata->rp_mutex);
 
@@ -722,11 +976,25 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
        op = fc_frame_payload_op(fp);
        if (op == ELS_LS_ACC) {
                pp = fc_frame_payload_get(fp, sizeof(*pp));
-               if (pp && pp->prli.prli_spp_len >= sizeof(pp->spp)) {
-                       fcp_parm = ntohl(pp->spp.spp_params);
-                       if (fcp_parm & FCP_SPPF_RETRY)
-                               rdata->flags |= FC_RP_FLAGS_RETRY;
+               if (!pp)
+                       goto out;
+
+               resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);
+               FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n",
+                            pp->spp.spp_flags);
+               if (resp_code != FC_SPP_RESP_ACK) {
+                       if (resp_code == FC_SPP_RESP_CONF)
+                               fc_rport_error(rdata, fp);
+                       else
+                               fc_rport_error_retry(rdata, fp);
+                       goto out;
                }
+               if (pp->prli.prli_spp_len < sizeof(pp->spp))
+                       goto out;
+
+               fcp_parm = ntohl(pp->spp.spp_params);
+               if (fcp_parm & FCP_SPPF_RETRY)
+                       rdata->flags |= FC_RP_FLAGS_RETRY;
 
                rdata->supported_classes = FC_COS_CLASS3;
                if (fcp_parm & FCP_SPPF_INIT_FCN)
@@ -739,55 +1007,9 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 
        } else {
                FC_RPORT_DBG(rdata, "Bad ELS response for PRLI command\n");
-               fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
-       }
-
-out:
-       fc_frame_free(fp);
-err:
-       mutex_unlock(&rdata->rp_mutex);
-       kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
-}
-
-/**
- * fc_rport_logo_resp() - Handler for logout (LOGO) responses
- * @sp:               The sequence the LOGO was on
- * @fp:               The LOGO response frame
- * @rdata_arg: The remote port that sent the LOGO response
- *
- * Locking Note: This function will be called without the rport lock
- * held, but it will lock, call an _enter_* function or fc_rport_error
- * and then unlock the rport.
- */
-static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
-                              void *rdata_arg)
-{
-       struct fc_rport_priv *rdata = rdata_arg;
-       u8 op;
-
-       mutex_lock(&rdata->rp_mutex);
-
-       FC_RPORT_DBG(rdata, "Received a LOGO %s\n", fc_els_resp_type(fp));
-
-       if (rdata->rp_state != RPORT_ST_LOGO) {
-               FC_RPORT_DBG(rdata, "Received a LOGO response, but in state "
-                            "%s\n", fc_rport_state(rdata));
-               if (IS_ERR(fp))
-                       goto err;
-               goto out;
-       }
-
-       if (IS_ERR(fp)) {
                fc_rport_error_retry(rdata, fp);
-               goto err;
        }
 
-       op = fc_frame_payload_op(fp);
-       if (op != ELS_LS_ACC)
-               FC_RPORT_DBG(rdata, "Bad ELS response op %x for LOGO command\n",
-                            op);
-       fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
-
 out:
        fc_frame_free(fp);
 err:
@@ -936,6 +1158,24 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
                kref_get(&rdata->kref);
 }
 
+/**
+ * fc_rport_logo_resp() - Handler for logout (LOGO) responses
+ * @sp:               The sequence the LOGO was on
+ * @fp:               The LOGO response frame
+ * @lport_arg: The local port
+ */
+static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+                              void *lport_arg)
+{
+       struct fc_lport *lport = lport_arg;
+
+       FC_RPORT_ID_DBG(lport, fc_seq_exch(sp)->did,
+                       "Received a LOGO %s\n", fc_els_resp_type(fp));
+       if (IS_ERR(fp))
+               return;
+       fc_frame_free(fp);
+}
+
 /**
  * fc_rport_enter_logo() - Send a logout (LOGO) request
  * @rdata: The remote port to send the LOGO request to
@@ -948,23 +1188,14 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
        struct fc_lport *lport = rdata->local_port;
        struct fc_frame *fp;
 
-       FC_RPORT_DBG(rdata, "Port entered LOGO state from %s state\n",
+       FC_RPORT_DBG(rdata, "Port sending LOGO from %s state\n",
                     fc_rport_state(rdata));
 
-       fc_rport_state_enter(rdata, RPORT_ST_LOGO);
-
        fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
-       if (!fp) {
-               fc_rport_error_retry(rdata, fp);
+       if (!fp)
                return;
-       }
-
-       if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
-                                 fc_rport_logo_resp, rdata,
-                                 2 * lport->r_a_tov))
-               fc_rport_error_retry(rdata, NULL);
-       else
-               kref_get(&rdata->kref);
+       (void)lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+                                  fc_rport_logo_resp, lport, 0);
 }
 
 /**
@@ -1013,7 +1244,7 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
            get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
            get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
                FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
-               fc_rport_enter_plogi(rdata);
+               fc_rport_enter_flogi(rdata);
        } else {
                FC_RPORT_DBG(rdata, "ADISC OK\n");
                fc_rport_enter_ready(rdata);
@@ -1058,29 +1289,25 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
 /**
  * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
  * @rdata: The remote port that sent the ADISC request
- * @sp:           The sequence the ADISC request was on
  * @in_fp: The ADISC request frame
  *
  * Locking Note:  Called with the lport and rport locks held.
  */
 static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
-                                   struct fc_seq *sp, struct fc_frame *in_fp)
+                                   struct fc_frame *in_fp)
 {
        struct fc_lport *lport = rdata->local_port;
        struct fc_frame *fp;
-       struct fc_exch *ep = fc_seq_exch(sp);
        struct fc_els_adisc *adisc;
        struct fc_seq_els_data rjt_data;
-       u32 f_ctl;
 
        FC_RPORT_DBG(rdata, "Received ADISC request\n");
 
        adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
        if (!adisc) {
-               rjt_data.fp = NULL;
                rjt_data.reason = ELS_RJT_PROT;
                rjt_data.explan = ELS_EXPL_INV_LEN;
-               lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+               lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
                goto drop;
        }
 
@@ -1090,11 +1317,8 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
        fc_adisc_fill(lport, fp);
        adisc = fc_frame_payload_get(fp, sizeof(*adisc));
        adisc->adisc_cmd = ELS_LS_ACC;
-       sp = lport->tt.seq_start_next(sp);
-       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-                      FC_TYPE_ELS, f_ctl, 0);
-       lport->tt.seq_send(lport, sp, fp);
+       fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
 drop:
        fc_frame_free(in_fp);
 }
@@ -1102,25 +1326,22 @@ drop:
 /**
  * fc_rport_recv_rls_req() - Handle received Read Link Status request
  * @rdata: The remote port that sent the RLS request
- * @sp:        The sequence that the RLS was on
  * @rx_fp: The PRLI request frame
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this function.
  */
 static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
-                                 struct fc_seq *sp, struct fc_frame *rx_fp)
+                                 struct fc_frame *rx_fp)
 
 {
        struct fc_lport *lport = rdata->local_port;
        struct fc_frame *fp;
-       struct fc_exch *ep = fc_seq_exch(sp);
        struct fc_els_rls *rls;
        struct fc_els_rls_resp *rsp;
        struct fc_els_lesb *lesb;
        struct fc_seq_els_data rjt_data;
        struct fc_host_statistics *hst;
-       u32 f_ctl;
 
        FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
                     fc_rport_state(rdata));
@@ -1157,16 +1378,12 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
                lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
        }
 
-       sp = lport->tt.seq_start_next(sp);
-       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-                      FC_TYPE_ELS, f_ctl, 0);
-       lport->tt.seq_send(lport, sp, fp);
+       fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
        goto out;
 
 out_rjt:
-       rjt_data.fp = NULL;
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+       lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 out:
        fc_frame_free(rx_fp);
 }
@@ -1174,7 +1391,6 @@ out:
 /**
  * fc_rport_recv_els_req() - Handler for validated ELS requests
  * @lport: The local port that received the ELS request
- * @sp:           The sequence that the ELS request was on
  * @fp:           The ELS request frame
  *
  * Handle incoming ELS requests that require port login.
@@ -1182,21 +1398,13 @@ out:
  *
  * Locking Note: Called with the lport lock held.
  */
-static void fc_rport_recv_els_req(struct fc_lport *lport,
-                                 struct fc_seq *sp, struct fc_frame *fp)
+static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
 {
        struct fc_rport_priv *rdata;
-       struct fc_frame_header *fh;
        struct fc_seq_els_data els_data;
 
-       els_data.fp = NULL;
-       els_data.reason = ELS_RJT_UNAB;
-       els_data.explan = ELS_EXPL_PLOGI_REQD;
-
-       fh = fc_frame_header_get(fp);
-
        mutex_lock(&lport->disc.disc_mutex);
-       rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id));
+       rdata = lport->tt.rport_lookup(lport, fc_frame_sid(fp));
        if (!rdata) {
                mutex_unlock(&lport->disc.disc_mutex);
                goto reject;
@@ -1217,24 +1425,24 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
 
        switch (fc_frame_payload_op(fp)) {
        case ELS_PRLI:
-               fc_rport_recv_prli_req(rdata, sp, fp);
+               fc_rport_recv_prli_req(rdata, fp);
                break;
        case ELS_PRLO:
-               fc_rport_recv_prlo_req(rdata, sp, fp);
+               fc_rport_recv_prlo_req(rdata, fp);
                break;
        case ELS_ADISC:
-               fc_rport_recv_adisc_req(rdata, sp, fp);
+               fc_rport_recv_adisc_req(rdata, fp);
                break;
        case ELS_RRQ:
-               els_data.fp = fp;
-               lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+               lport->tt.seq_els_rsp_send(fp, ELS_RRQ, NULL);
+               fc_frame_free(fp);
                break;
        case ELS_REC:
-               els_data.fp = fp;
-               lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+               lport->tt.seq_els_rsp_send(fp, ELS_REC, NULL);
+               fc_frame_free(fp);
                break;
        case ELS_RLS:
-               fc_rport_recv_rls_req(rdata, sp, fp);
+               fc_rport_recv_rls_req(rdata, fp);
                break;
        default:
                fc_frame_free(fp);      /* can't happen */
@@ -1245,35 +1453,38 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
        return;
 
 reject:
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+       els_data.reason = ELS_RJT_UNAB;
+       els_data.explan = ELS_EXPL_PLOGI_REQD;
+       lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
        fc_frame_free(fp);
 }
 
 /**
  * fc_rport_recv_req() - Handler for requests
- * @sp:           The sequence the request was on
- * @fp:           The request frame
  * @lport: The local port that received the request
+ * @fp:           The request frame
  *
  * Locking Note: Called with the lport lock held.
  */
-void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
-                      struct fc_lport *lport)
+void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 {
        struct fc_seq_els_data els_data;
 
        /*
-        * Handle PLOGI and LOGO requests separately, since they
+        * Handle FLOGI, PLOGI and LOGO requests separately, since they
         * don't require prior login.
         * Check for unsupported opcodes first and reject them.
         * For some ops, it would be incorrect to reject with "PLOGI required".
         */
        switch (fc_frame_payload_op(fp)) {
+       case ELS_FLOGI:
+               fc_rport_recv_flogi_req(lport, fp);
+               break;
        case ELS_PLOGI:
-               fc_rport_recv_plogi_req(lport, sp, fp);
+               fc_rport_recv_plogi_req(lport, fp);
                break;
        case ELS_LOGO:
-               fc_rport_recv_logo_req(lport, sp, fp);
+               fc_rport_recv_logo_req(lport, fp);
                break;
        case ELS_PRLI:
        case ELS_PRLO:
@@ -1281,14 +1492,13 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
        case ELS_RRQ:
        case ELS_REC:
        case ELS_RLS:
-               fc_rport_recv_els_req(lport, sp, fp);
+               fc_rport_recv_els_req(lport, fp);
                break;
        default:
-               fc_frame_free(fp);
-               els_data.fp = NULL;
                els_data.reason = ELS_RJT_UNSUP;
                els_data.explan = ELS_EXPL_NONE;
-               lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+               lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
+               fc_frame_free(fp);
                break;
        }
 }
@@ -1296,26 +1506,21 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 /**
  * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
  * @lport: The local port that received the PLOGI request
- * @sp:           The sequence that the PLOGI request was on
  * @rx_fp: The PLOGI request frame
  *
  * Locking Note: The rport lock is held before calling this function.
  */
 static void fc_rport_recv_plogi_req(struct fc_lport *lport,
-                                   struct fc_seq *sp, struct fc_frame *rx_fp)
+                                   struct fc_frame *rx_fp)
 {
        struct fc_disc *disc;
        struct fc_rport_priv *rdata;
        struct fc_frame *fp = rx_fp;
-       struct fc_exch *ep;
-       struct fc_frame_header *fh;
        struct fc_els_flogi *pl;
        struct fc_seq_els_data rjt_data;
-       u32 sid, f_ctl;
+       u32 sid;
 
-       rjt_data.fp = NULL;
-       fh = fc_frame_header_get(fp);
-       sid = ntoh24(fh->fh_s_id);
+       sid = fc_frame_sid(fp);
 
        FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
 
@@ -1358,6 +1563,9 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
        case RPORT_ST_INIT:
                FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
                break;
+       case RPORT_ST_PLOGI_WAIT:
+               FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI_WAIT state\n");
+               break;
        case RPORT_ST_PLOGI:
                FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
                if (rdata->ids.port_name < lport->wwpn) {
@@ -1375,9 +1583,8 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
                             "- ignored for now\n", rdata->rp_state);
                /* XXX TBD - should reset */
                break;
+       case RPORT_ST_FLOGI:
        case RPORT_ST_DELETE:
-       case RPORT_ST_LOGO:
-       case RPORT_ST_RESTART:
                FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
                             fc_rport_state(rdata));
                mutex_unlock(&rdata->rp_mutex);
@@ -1390,50 +1597,41 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
         * Get session payload size from incoming PLOGI.
         */
        rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
-       fc_frame_free(rx_fp);
 
        /*
         * Send LS_ACC.  If this fails, the originator should retry.
         */
-       sp = lport->tt.seq_start_next(sp);
-       if (!sp)
-               goto out;
        fp = fc_frame_alloc(lport, sizeof(*pl));
        if (!fp)
                goto out;
 
        fc_plogi_fill(lport, fp, ELS_LS_ACC);
-       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-       ep = fc_seq_exch(sp);
-       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-                      FC_TYPE_ELS, f_ctl, 0);
-       lport->tt.seq_send(lport, sp, fp);
+       fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
        fc_rport_enter_prli(rdata);
 out:
        mutex_unlock(&rdata->rp_mutex);
+       fc_frame_free(rx_fp);
        return;
 
 reject:
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+       lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
        fc_frame_free(fp);
 }
 
 /**
  * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
  * @rdata: The remote port that sent the PRLI request
- * @sp:           The sequence that the PRLI was on
  * @rx_fp: The PRLI request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
-                                  struct fc_seq *sp, struct fc_frame *rx_fp)
+                                  struct fc_frame *rx_fp)
 {
        struct fc_lport *lport = rdata->local_port;
-       struct fc_exch *ep;
        struct fc_frame *fp;
-       struct fc_frame_header *fh;
        struct {
                struct fc_els_prli prli;
                struct fc_els_spp spp;
@@ -1444,17 +1642,13 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
        unsigned int plen;
        enum fc_els_spp_resp resp;
        struct fc_seq_els_data rjt_data;
-       u32 f_ctl;
        u32 fcp_parm;
        u32 roles = FC_RPORT_ROLE_UNKNOWN;
 
-       rjt_data.fp = NULL;
-       fh = fc_frame_header_get(rx_fp);
-
        FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
                     fc_rport_state(rdata));
 
-       len = fr_len(rx_fp) - sizeof(*fh);
+       len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
        pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
        if (!pp)
                goto reject_len;
@@ -1475,8 +1669,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
                rjt_data.explan = ELS_EXPL_INSUF_RES;
                goto reject;
        }
-       sp = lport->tt.seq_start_next(sp);
-       WARN_ON(!sp);
        pp = fc_frame_payload_get(fp, len);
        WARN_ON(!pp);
        memset(pp, 0, len);
@@ -1529,12 +1721,8 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
        /*
         * Send LS_ACC.  If this fails, the originator should retry.
         */
-       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
-       f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-       ep = fc_seq_exch(sp);
-       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-                      FC_TYPE_ELS, f_ctl, 0);
-       lport->tt.seq_send(lport, sp, fp);
+       fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
 
        switch (rdata->rp_state) {
        case RPORT_ST_PRLI:
@@ -1549,7 +1737,7 @@ reject_len:
        rjt_data.reason = ELS_RJT_PROT;
        rjt_data.explan = ELS_EXPL_INV_LEN;
 reject:
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+       lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 drop:
        fc_frame_free(rx_fp);
 }
@@ -1557,54 +1745,90 @@ drop:
 /**
  * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
  * @rdata: The remote port that sent the PRLO request
- * @sp:           The sequence that the PRLO was on
- * @fp:           The PRLO request frame
+ * @rx_fp: The PRLO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
-                                  struct fc_seq *sp,
-                                  struct fc_frame *fp)
+                                  struct fc_frame *rx_fp)
 {
        struct fc_lport *lport = rdata->local_port;
-
-       struct fc_frame_header *fh;
+       struct fc_frame *fp;
+       struct {
+               struct fc_els_prlo prlo;
+               struct fc_els_spp spp;
+       } *pp;
+       struct fc_els_spp *rspp;        /* request service param page */
+       struct fc_els_spp *spp;         /* response spp */
+       unsigned int len;
+       unsigned int plen;
        struct fc_seq_els_data rjt_data;
 
-       fh = fc_frame_header_get(fp);
-
        FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
                     fc_rport_state(rdata));
 
-       rjt_data.fp = NULL;
-       rjt_data.reason = ELS_RJT_UNAB;
-       rjt_data.explan = ELS_EXPL_NONE;
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
-       fc_frame_free(fp);
+       len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
+       pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
+       if (!pp)
+               goto reject_len;
+       plen = ntohs(pp->prlo.prlo_len);
+       if (plen != 20)
+               goto reject_len;
+       if (plen < len)
+               len = plen;
+
+       rspp = &pp->spp;
+
+       fp = fc_frame_alloc(lport, len);
+       if (!fp) {
+               rjt_data.reason = ELS_RJT_UNAB;
+               rjt_data.explan = ELS_EXPL_INSUF_RES;
+               goto reject;
+       }
+
+       pp = fc_frame_payload_get(fp, len);
+       WARN_ON(!pp);
+       memset(pp, 0, len);
+       pp->prlo.prlo_cmd = ELS_LS_ACC;
+       pp->prlo.prlo_obs = 0x10;
+       pp->prlo.prlo_len = htons(len);
+       spp = &pp->spp;
+       spp->spp_type = rspp->spp_type;
+       spp->spp_type_ext = rspp->spp_type_ext;
+       spp->spp_flags = FC_SPP_RESP_ACK;
+
+       fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+
+       fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+       lport->tt.frame_send(lport, fp);
+       goto drop;
+
+reject_len:
+       rjt_data.reason = ELS_RJT_PROT;
+       rjt_data.explan = ELS_EXPL_INV_LEN;
+reject:
+       lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
+drop:
+       fc_frame_free(rx_fp);
 }
 
 /**
  * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
  * @lport: The local port that received the LOGO request
- * @sp:           The sequence that the LOGO request was on
  * @fp:           The LOGO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
-static void fc_rport_recv_logo_req(struct fc_lport *lport,
-                                  struct fc_seq *sp,
-                                  struct fc_frame *fp)
+static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
 {
-       struct fc_frame_header *fh;
        struct fc_rport_priv *rdata;
        u32 sid;
 
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+       lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
 
-       fh = fc_frame_header_get(fp);
-       sid = ntoh24(fh->fh_s_id);
+       sid = fc_frame_sid(fp);
 
        mutex_lock(&lport->disc.disc_mutex);
        rdata = lport->tt.rport_lookup(lport, sid);
@@ -1614,13 +1838,6 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
                             fc_rport_state(rdata));
 
                fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
-
-               /*
-                * If the remote port was created due to discovery, set state
-                * to log back in.  It may have seen a stale RSCN about us.
-                */
-               if (rdata->disc_id)
-                       fc_rport_state_enter(rdata, RPORT_ST_RESTART);
                mutex_unlock(&rdata->rp_mutex);
        } else
                FC_RPORT_ID_DBG(lport, sid,