1) removes fc_rport_lock/unlock and replaces with spin_lock_irqsave/restore
2) re-implements rport locking using the following scheme

Action Funtions (called without lock, will lock, call enter_* function and 
unlock)
 fc_rport_gpn_id_resp
 fc_rport_gnn_id_resp
 fc_rport_plogi_resp
 fc_rport_prli_resp
 fc_rport_rtv_resp
 fc_rport_logo_resp

 fc_rport_login
 fc_rport_logout
 fc_rport_reset
 fc_rport_timeout

 fc_rport_recv_req
        - Calls one of the request handlers with the lock held

Request Handlers (need lock held before calling)
 fc_rport_recv_plogi_req
 fc_rport_recv_prli_req
 fc_rport_recv_prlo_req
 fc_rport_recv_logo_req

Enter Funtions (need lock held before calling)
 fc_rport_enter_gpn_id
 fc_rport_enter_gnn_id
 fc_rport_enter_plogi
 fc_rport_enter_prli
 fc_rport_enter_rtv
 fc_rport_enter_logo

Helpers (Lock required)
 fc_rport_state_enter
 - Changing the state so the lock must be held
 fc_rport_error
        - Always called by an action or enter function

Helpers (Lock not required)
 fc_rport_create_dummy
 fc_rport_destroy_dummy
 fc_rport_lookup
 fc_remote_port_create
 fc_rport_lock
 fc_rport_unlock
 fc_plogi_get_maxframe
 fc_lport_plogi_fill
 fc_rport_init
 fc_rport_reset_list

Unknown
 fc_rport_ns_error
        - Merge with fc_rport_error
 fc_rport_retry
        - Merge with fc_rport_error

Signed-off-by: Robert Love <[EMAIL PROTECTED]>
---

 drivers/scsi/libfc/fc_rport.c |  856 +++++++++++++++++++++--------------------
 include/scsi/libfc/libfc.h    |    6 
 2 files changed, 445 insertions(+), 417 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 27f485a..159c365 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -68,7 +68,6 @@ static const char *fc_rport_state_names[] = {
        [RPORT_ST_GPN_ID] = "GPN_ID",
        [RPORT_ST_GNN_ID] = "GNN_ID",
        [RPORT_ST_PLOGI] = "PLOGI",
-       [RPORT_ST_PLOGI_RECV] = "PLOGI recv",
        [RPORT_ST_PRLI] = "PRLI",
        [RPORT_ST_RTV] = "RTV",
        [RPORT_ST_READY] = "Ready",
@@ -173,18 +172,6 @@ static struct fc_rport *fc_remote_port_create(struct 
fc_lport *lp,
        return rport;
 }
 
-static inline void fc_rport_lock(struct fc_rport *rport)
-{
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       spin_lock_bh(&rp->rp_lock);
-}
-
-static inline void fc_rport_unlock(struct fc_rport *rport)
-{
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       spin_unlock_bh(&rp->rp_lock);
-}
-
 static void fc_rport_state_enter(struct fc_rport *rport,
                                 enum fc_rport_state new)
 {
@@ -214,40 +201,37 @@ static void fc_rport_enter_ready(struct fc_rport *rp)
 
 /**
  * fc_rport_enter_gpn_id - Send Get Port Name by ID (GPN_ID) request
- * @work: The work member of the fc_ns_port structure
+ * @rp: The fc_rport
  *
- * XXX - this the following statement still valid?
- * The remote port is held by the caller for us.
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 void fc_rport_enter_gpn_id(struct fc_rport *rp)
 {
-       struct fc_rport_libfc_priv *rpp;
-       struct fc_lport *lp;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame *fp;
        struct req {
                struct fc_ct_hdr ct;
                struct fc_ns_fid fid;
        } *cp;
 
-       rpp = rp->dd_data;
-       lp = rpp->local_port;
-
        fc_rport_state_enter(rp, RPORT_ST_GPN_ID);
 
        if (fc_rp_debug)
                FC_DBG("Entered GPN_ID for port (%6x)\n", rp->port_id);
 
        fp = fc_frame_alloc(lp, sizeof(*cp));
-       if (!fp)
+       if (!fp) {
+               fc_rport_error(rp, fp);
                return;
+       }
 
        cp = fc_frame_payload_get(fp, sizeof(*cp));
        fc_fill_dns_hdr(lp, &cp->ct, FC_NS_GPN_ID, sizeof(cp->fid));
        hton24(cp->fid.fp_fid, rp->port_id);
-
-       WARN_ON(!fc_lport_test_ready(lp));
-
        fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_gpn_id_resp,
                                  rp, lp->e_d_tov,
@@ -262,30 +246,43 @@ void fc_rport_enter_gpn_id(struct fc_rport *rp)
  * @sp: Current sequence of GPN_ID exchange
  * @fp: response frame
  * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ *
+ * Locking Note: This is an action function so we lock and then call
+ *               the next _enter_* function, unlock when it returns.
  */
 static void fc_rport_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
-                             void *rp_arg)
+                                void *rp_arg)
 {
        struct fc_rport *rp = rp_arg;
        struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       unsigned long flags;
 
-       struct fc_lport *lp;
        struct resp {
                struct fc_ct_hdr ct;
                __be64 wwn;
        } *cp;
        unsigned int cmd;
 
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_GPN_ID) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a GPN_ID response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
+               return;
+       }
+
        if (IS_ERR(fp)) {
                fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                return;
        }
 
-       lp = rpp->local_port;
        WARN_ON(!fc_frame_is_linear(fp));       /* buffer must be contiguous */
 
        cp = fc_frame_payload_get(fp, sizeof(cp->ct));
-       if (cp == NULL) {
+       if (!cp) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                FC_DBG("GPN_ID response too short, len %d\n", fr_len(fp));
                return;
        }
@@ -293,20 +290,22 @@ static void fc_rport_gpn_id_resp(struct fc_seq *sp, 
struct fc_frame *fp,
        switch (cmd) {
        case FC_FS_ACC:
                cp = fc_frame_payload_get(fp, sizeof(*cp));
-               if (cp == NULL) {
+               if (!cp) {
+                       spin_unlock_irqrestore(&rpp->rp_lock, flags);
                        FC_DBG("GPN_ID response payload too short, len %d\n",
                               fr_len(fp));
                        break;
                }
                rp->port_name = ntohll(cp->wwn);
-
                fc_rport_enter_gnn_id(rp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                break;
        case FC_FS_RJT:
-               lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
-               fc_rport_destroy_dummy(rp);
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                break;
        default:
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                FC_DBG("GPN_ID unexpected CT response cmd %x\n", cmd);
                break;
        }
@@ -315,41 +314,37 @@ static void fc_rport_gpn_id_resp(struct fc_seq *sp, 
struct fc_frame *fp,
 
 /**
  * fc_rport_enter_gnn_id - Send Get Node Name by ID (GNN_ID) request
- * @lp: Fibre Channel host port instance
- * @dp: Temporary discovery port for holding IDs and world wide names
+ * @rp: The fc_rport object
  *
- * XXX- Is the following statement still true?
- * The remote port is held by the caller for us.
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 void fc_rport_enter_gnn_id(struct fc_rport *rp)
 {
-       struct fc_rport_libfc_priv *rpp;
-       struct fc_lport *lp;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame *fp;
        struct req {
                struct fc_ct_hdr ct;
                struct fc_ns_fid fid;
        } *cp;
 
-       rpp = rp->dd_data;
-       lp = rpp->local_port;
+       fc_rport_state_enter(rp, RPORT_ST_GNN_ID);
 
        if (fc_rp_debug)
                FC_DBG("Entered GNN_ID for port (%6x)\n", rp->port_id);
 
-       fc_rport_state_enter(rp, RPORT_ST_GNN_ID);
-
        fp = fc_frame_alloc(lp, sizeof(*cp));
-       if (!fp)
+       if (!fp) {
+               fc_rport_error(rp, fp);
                return;
+       }
 
        cp = fc_frame_payload_get(fp, sizeof(*cp));
        fc_fill_dns_hdr(lp, &cp->ct, FC_NS_GNN_ID, sizeof(cp->fid));
        hton24(cp->fid.fp_fid, rp->port_id);
-
-       WARN_ON(!fc_lport_test_ready(lp));
-
        fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_gnn_id_resp,
                                  rp, lp->e_d_tov,
@@ -364,14 +359,21 @@ void fc_rport_enter_gnn_id(struct fc_rport *rp)
  * @sp: Current sequence of GNN_ID exchange
  * @fp: response frame
  * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ *
+ * Locking Note: This is an action function so we hold the lock and then
+ * call an _enter_* function. This method is special though becuase this
+ * is where we transition from the dummy rport to the one the the
+ * transport class is aware of. We must transition the lock, state, WWNN,
+ * WWPN and FID from the dummy to the new rport.
  */
 static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
                                 void *rp_arg)
 {
        struct fc_rport *rp = rp_arg;
-       struct fc_rport_libfc_priv *rpp;
-       struct fc_lport *lp;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_rport_identifiers ids;
+       unsigned long flags;
 
        struct resp {
                struct fc_ct_hdr ct;
@@ -379,19 +381,27 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, 
struct fc_frame *fp,
        } *cp;
        unsigned int cmd;
 
-       rpp = rp->dd_data;
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_GNN_ID) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a GNN_ID response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
+               return;
+       }
 
        if (IS_ERR(fp)) {
                fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                return;
        }
 
-       lp = rpp->local_port;
-       WARN_ON(!fc_frame_is_linear(fp));       /* buffer must be contiguous */
+       WARN_ON(!fc_frame_is_linear(fp));      /* buffer must be contiguous */
 
        cp = fc_frame_payload_get(fp, sizeof(cp->ct));
-       if (cp == NULL) {
-               FC_DBG("GNN_ID response too short,  len %d\n", fr_len(fp));
+       if (!cp) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("GNN_ID response too short, len %d\n", fr_len(fp));
                return;
        }
 
@@ -399,7 +409,8 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct 
fc_frame *fp,
        switch (cmd) {
        case FC_FS_ACC:
                cp = fc_frame_payload_get(fp, sizeof(*cp));
-               if (cp == NULL) {
+               if (!cp) {
+                       spin_unlock_irqrestore(&rpp->rp_lock, flags);
                        FC_DBG("GNN_ID response payload too short, len %d\n",
                               fr_len(fp));
                        break;
@@ -411,31 +422,52 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, 
struct fc_frame *fp,
                ids.node_name = rp->node_name;
                ids.roles = rp->roles;
 
-               fc_rport_destroy_dummy(rp);
-
                if ((ids.port_name != -1) && (ids.port_id != -1) &&
                    (ids.port_id != lp->fid) && (ids.port_name != lp->wwpn)) {
-                       rp = lp->tt.rport_lookup(lp, ids.port_id);
-                       if (!rp)
-                               rp = lp->tt.rport_create(lp, &ids);
+                       struct fc_rport *new_rp;
+                       new_rp = lp->tt.rport_lookup(lp, ids.port_id);
+                       if (!new_rp) {
+                               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+                               new_rp = lp->tt.rport_create(lp, &ids);
+                               spin_lock_irqsave(&rpp->rp_lock, flags);
+                       }
+
+                       if (new_rp) {
+                               /*
+                                * Transition locking and state from
+                                * the dummy rport to the real rport
+                                */
+                               unsigned long new_flags;
+                               struct fc_rport_libfc_priv *new_rpp =
+                                       new_rp->dd_data;
+
+                               spin_lock_irqsave(&new_rpp->rp_lock, new_flags);
+                               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+
+                               fc_rport_destroy_dummy(rp);
+                               fc_rport_state_enter(new_rp, RPORT_ST_GNN_ID);
+                               fc_rport_enter_plogi(new_rp);
+                               spin_unlock_irqrestore(&new_rpp->rp_lock,
+                                                      new_flags);
+                       } else {
+                               lp->tt.lport_event_callback(lp, rp,
+                                                           
LPORT_EV_RPORT_FAILED);
 
-                       if (!rp)
+                               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+                               fc_rport_destroy_dummy(rp);
                                FC_DBG("Could not create a remote port, "
                                       "WWNN (%llux), WWNN (%llux), "
                                       "FID (%6x)\n", ids.node_name,
                                       ids.port_name, ids.port_id);
-                       else {
-                               rpp = rp->dd_data;
-                               rpp->rp_state = RPORT_ST_GNN_ID;
-                               fc_rport_enter_plogi(rp);
                        }
                }
                break;
        case FC_FS_RJT:
-               lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
-               fc_rport_destroy_dummy(rp);
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                break;
        default:
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                FC_DBG("GNN_ID unexpected CT response cmd %x\n", cmd);
                break;
        }
@@ -506,115 +538,76 @@ fc_lport_plogi_fill(struct fc_lport *lp,
 /**
  * fc_rport_login - Start the remote port login state machine
  * @work: member of the fc_rport_libfc_priv structure
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
 void fc_rport_login(struct work_struct *work)
 {
-       struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport *rp;
+       struct fc_rport_libfc_priv *rpp;
        struct fc_lport *lp;
+       unsigned long flags;
 
-       rp = container_of(work,
+       rpp = container_of(work,
                          struct fc_rport_libfc_priv,
                          login_work);
+       rp = (((void *)rpp) - sizeof(struct fc_rport));
+       lp = rpp->local_port;
 
-       rport = (((void *)rp) - sizeof(struct fc_rport));
-       lp = rp->local_port;
+       spin_lock_irqsave(&rpp->rp_lock, flags);
 
-       fc_rport_lock(rport);
-       if (rp->rp_state == RPORT_ST_ERROR) {
-               fc_rport_state_enter(rport, RPORT_ST_INIT);
-               fc_rport_unlock(rport);
-               if (fc_rp_debug)
-                       FC_DBG("remote %6x closed\n", rport->port_id);
+       /*
+        * This should be the only non _enter_* function
+        * that sets the state.
+        */
+       fc_rport_state_enter(rp, RPORT_ST_INIT);
 
-               lp->tt.lport_event_callback(lp, rport, LPORT_EV_RPORT_FAILED);
-       } else {
-               fc_rport_unlock(rport);
-               if (rport->port_id == FC_FID_DIR_SERV)
-                       fc_rport_enter_plogi(rport);
-               else if (rport->port_name == -1)
-                       fc_rport_enter_gpn_id(rport);
-               else if (rport->node_name == -1)
-                       fc_rport_enter_gnn_id(rport);
-       }
+       if (rp->port_id == FC_FID_DIR_SERV)
+               fc_rport_enter_plogi(rp);
+       else if (rp->port_name == -1)
+               fc_rport_enter_gpn_id(rp);
+       else if (rp->node_name == -1)
+               fc_rport_enter_gnn_id(rp);
+
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
-/*
- * Stop the session - log it off.
+/**
+ * fc_rport_logout - Stop the remote port state machine
+ * @rp: The fc_rport to logout
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
-int fc_rport_logout(struct fc_rport *rport)
+void fc_rport_logout(struct fc_rport *rp)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp = rp->local_port;
-
-       fc_rport_lock(rport);
-       switch (rp->rp_state) {
-       case RPORT_ST_PRLI:
-       case RPORT_ST_RTV:
-       case RPORT_ST_READY:
-               fc_rport_enter_logo(rport);
-               fc_rport_unlock(rport);
-               break;
-       default:
-               fc_rport_state_enter(rport, RPORT_ST_INIT);
-               fc_rport_unlock(rport);
-               if (fc_rp_debug)
-                       FC_DBG("remote %6x closed\n", rport->port_id);
-               if (rport == lp->dns_rp &&
-                   lp->state != LPORT_ST_RESET) {
-                       fc_lport_lock(lp);
-                       del_timer(&lp->state_timer);
-                       lp->dns_rp = NULL;
-
-                       if (lp->state == LPORT_ST_DNS_STOP) {
-                               fc_lport_unlock(lp);
-                               lp->tt.lport_logout(lp);
-                       } else {
-                               lp->tt.lport_login(lp);
-                               fc_lport_unlock(lp);
-                       }
-
-                       fc_remote_port_delete(rport);
-               }
-               break;
-       }
-
-       return 0;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       unsigned long flags;
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+       fc_rport_enter_logo(rp);
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
-/*
- * Reset the session - assume it is logged off.         Used after fabric 
logoff.
- * The local port code takes care of resetting the exchange manager.
+/**
+ * fc_rport_reset - Stop and then start the remote port state machine
+ * @rp: The fc_rport to reset
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
-void fc_rport_reset(struct fc_rport *rport)
+void fc_rport_reset(struct fc_rport *rp)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       unsigned long flags;
+       spin_lock_irqsave(&rpp->rp_lock, flags);
 
        if (fc_rp_debug)
-               FC_DBG("sess to %6x reset\n", rport->port_id);
-       fc_rport_lock(rport);
-
-       lp = rp->local_port;
-       fc_rport_state_enter(rport, RPORT_ST_INIT);
-       fc_rport_unlock(rport);
+               FC_DBG("Port %6x reset\n", rp->port_id);
 
-       if (fc_rp_debug)
-               FC_DBG("remote %6x closed\n", rport->port_id);
-       if (rport == lp->dns_rp &&
-           lp->state != LPORT_ST_RESET) {
-               fc_lport_lock(lp);
-               del_timer(&lp->state_timer);
-               lp->dns_rp = NULL;
-               if (lp->state == LPORT_ST_DNS_STOP) {
-                       fc_lport_unlock(lp);
-                       lp->tt.lport_logout(lp);
-               } else {
-                       lp->tt.lport_login(lp);
-                       fc_lport_unlock(lp);
-               }
-               fc_remote_port_delete(rport);
-       }
+       fc_rport_state_enter(rp, RPORT_ST_INIT);
+       fc_rport_enter_gpn_id(rp);
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
 /*
@@ -689,7 +682,6 @@ static void fc_rport_reject(struct fc_rport *rport)
        case RPORT_ST_NONE:
        case RPORT_ST_READY:
        case RPORT_ST_ERROR:
-       case RPORT_ST_PLOGI_RECV:
        case RPORT_ST_INIT:
                BUG();
                break;
@@ -700,41 +692,51 @@ static void fc_rport_reject(struct fc_rport *rport)
 /*
  * Timeout handler for retrying after allocation failures or exchange timeout.
  */
+
+/**
+ * fc_rport_timeout - Handler for the retry_work timer.
+ *  Simply determine what should be done next.
+ * @work: The work struct of the fc_rport_libfc_priv
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
+ */
 static void fc_rport_timeout(struct work_struct *work)
 {
-       struct fc_rport_libfc_priv *rp =
+       struct fc_rport_libfc_priv *rpp =
                container_of(work, struct fc_rport_libfc_priv, retry_work.work);
-       struct fc_rport *rport = (((void *)rp) - sizeof(struct fc_rport));
+       struct fc_rport *rp = (((void *)rpp) - sizeof(struct fc_rport));
+       unsigned long flags;
 
-       switch (rp->rp_state) {
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+       switch (rpp->rp_state) {
        case RPORT_ST_GPN_ID:
-               fc_rport_enter_gpn_id(rport);
+               fc_rport_enter_gpn_id(rp);
                break;
        case RPORT_ST_GNN_ID:
-               fc_rport_enter_gnn_id(rport);
+               fc_rport_enter_gnn_id(rp);
                break;
        case RPORT_ST_PLOGI:
-               fc_rport_enter_plogi(rport);
+               fc_rport_enter_plogi(rp);
                break;
        case RPORT_ST_PRLI:
-               fc_rport_enter_prli(rport);
+               fc_rport_enter_prli(rp);
                break;
        case RPORT_ST_RTV:
-               fc_rport_enter_rtv(rport);
+               fc_rport_enter_rtv(rp);
                break;
        case RPORT_ST_LOGO:
-               fc_rport_enter_logo(rport);
+               fc_rport_enter_logo(rp);
                break;
        case RPORT_ST_READY:
        case RPORT_ST_ERROR:
        case RPORT_ST_INIT:
-               break;
        case RPORT_ST_NONE:
-       case RPORT_ST_PLOGI_RECV:
                BUG();
                break;
        }
-       put_device(&rport->dev);
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
+       put_device(&rp->dev);
 }
 
 /*
@@ -764,89 +766,103 @@ static void fc_rport_error(struct fc_rport *rport, 
struct fc_frame *fp)
 static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
                                void *rp_arg)
 {
-       struct fc_els_ls_rjt *rjp;
+       struct fc_rport *rp = rp_arg;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+
        struct fc_els_flogi *plp;
        u64 wwpn, wwnn;
        unsigned int tov;
        u16 csp_seq;
        u16 cssp_seq;
        u8 op;
-       struct fc_rport *rport = rp_arg;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       unsigned long flags;
 
-       if (!IS_ERR(fp)) {
-               op = fc_frame_payload_op(fp);
-               fc_rport_lock(rport);
-               if (op == ELS_LS_ACC &&
-                   (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
-                       wwpn = get_unaligned_be64(&plp->fl_wwpn);
-                       wwnn = get_unaligned_be64(&plp->fl_wwnn);
-
-                       fc_rport_set_name(rport, wwpn, wwnn);
-                       tov = ntohl(plp->fl_csp.sp_e_d_tov);
-                       if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
-                               tov /= 1000;
-                       if (tov > rp->e_d_tov)
-                               rp->e_d_tov = tov;
-                       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)
-                               csp_seq = cssp_seq;
-                       rp->max_seq = csp_seq;
-                       rport->maxframe_size =
-                               fc_plogi_get_maxframe(plp, rp->local_port->mfs);
-                       if (rp->rp_state == RPORT_ST_PLOGI)
-                               fc_rport_enter_prli(rport);
-               } else {
-                       if (fc_rp_debug)
-                               FC_DBG("bad PLOGI response\n");
-
-                       rjp = fc_frame_payload_get(fp, sizeof(*rjp));
-                       if (op == ELS_LS_RJT && rjp != NULL &&
-                           rjp->er_reason == ELS_RJT_INPROG)
-                               fc_rport_error(rport, fp);    /* try again */
-                       else
-                               fc_rport_reject(rport);   /* error */
-               }
-               fc_rport_unlock(rport);
-               fc_frame_free(fp);
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_PLOGI) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a PLOGI response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
+               return;
+       }
+
+       if (IS_ERR(fp)) {
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+       }
+
+       op = fc_frame_payload_op(fp);
+       if (op == ELS_LS_ACC &&
+           (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
+               wwpn = get_unaligned_be64(&plp->fl_wwpn);
+               wwnn = get_unaligned_be64(&plp->fl_wwnn);
+               fc_rport_set_name(rp, wwpn, wwnn);
+               tov = ntohl(plp->fl_csp.sp_e_d_tov);
+
+               if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
+                       tov /= 1000;
+               if (tov > rpp->e_d_tov)
+                       rpp->e_d_tov = tov;
+
+               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)
+                       csp_seq = cssp_seq;
+
+               rpp->max_seq = csp_seq;
+               rp->maxframe_size =
+                       fc_plogi_get_maxframe(plp, rpp->local_port->mfs);
+
+               fc_rport_enter_prli(rp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
        } else {
-               fc_rport_error(rport, fp);
+               if (fc_rp_debug)
+                       FC_DBG("Bad PLOGI response\n");
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
        }
+       fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer
- * @rport: Fibre Channel remote port to send PLOGI to
+ * @rp: Fibre Channel remote port to send PLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
-static void fc_rport_enter_plogi(struct fc_rport *rport)
+static void fc_rport_enter_plogi(struct fc_rport *rp)
 {
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame *fp;
        struct fc_els_flogi *plogi;
-       struct fc_lport *lp;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+
+       fc_rport_state_enter(rp, RPORT_ST_PLOGI);
 
        if (fc_rp_debug)
-               FC_DBG("Entered PLOGI for port (%6x)\n", rport->port_id);
+               FC_DBG("Entered PLOGI for port (%6x)\n", rp->port_id);
+
+       rp->maxframe_size = FC_MIN_MAX_PAYLOAD;
 
-       lp = rp->local_port;
-       fc_rport_state_enter(rport, RPORT_ST_PLOGI);
-       rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
        fp = fc_frame_alloc(lp, sizeof(*plogi));
-       if (!fp)
-               return fc_rport_error(rport, fp);
+       if (!fp) {
+               fc_rport_error(rp, fp);
+               return;
+       }
+
        plogi = fc_frame_payload_get(fp, sizeof(*plogi));
-       WARN_ON(!plogi);
-       fc_lport_plogi_fill(rp->local_port, plogi, ELS_PLOGI);
-       rp->e_d_tov = lp->e_d_tov;
+       fc_lport_plogi_fill(lp, plogi, ELS_PLOGI);
+       rpp->e_d_tov = lp->e_d_tov;
        fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_plogi_resp,
-                                 rport, lp->e_d_tov,
-                                 rp->local_port->fid,
-                                 rport->port_id,
+                                 rp, lp->e_d_tov,
+                                 lp->fid, rp->port_id,
                                  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-               fc_rport_error(rport, fp);
+               fc_rport_error(rp, fp);
 }
 
 /**
@@ -854,13 +870,15 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
  * @sp: current sequence in the PRLI exchange
  * @fp: response frame
  * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This is an action function, grab the lock and
+ * call the next _enter_* function.
  */
 static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
                               void *rp_arg)
 {
-       struct fc_rport *rport = rp_arg;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp = rp->local_port;
+       struct fc_rport *rp = rp_arg;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
        struct {
                struct fc_els_prli prli;
                struct fc_els_spp spp;
@@ -868,49 +886,45 @@ 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;
+       unsigned long flags;
 
-       if (IS_ERR(fp)) {
-               fc_rport_error(rport, fp);
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_PRLI) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a PRLI response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
                return;
        }
 
-       fc_rport_lock(rport);
+       if (IS_ERR(fp)) {
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+       }
+
        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)
-                               rp->flags |= FC_RP_FLAGS_RETRY;
+                               rpp->flags |= FC_RP_FLAGS_RETRY;
                }
 
-               rport->supported_classes = FC_COS_CLASS3;
+               rp->supported_classes = FC_COS_CLASS3;
                if (fcp_parm & FCP_SPPF_INIT_FCN)
                        roles |= FC_RPORT_ROLE_FCP_INITIATOR;
                if (fcp_parm & FCP_SPPF_TARG_FCN)
                        roles |= FC_RPORT_ROLE_FCP_TARGET;
 
-               fc_rport_enter_rtv(rport);
-               fc_rport_unlock(rport);
-
-               fc_remote_port_rolechg(rport, roles);
+               fc_rport_enter_rtv(rp);
+               fc_remote_port_rolechg(rp, roles);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
        } else {
-               FC_DBG("bad ELS response\n");
-               fc_rport_state_enter(rport, RPORT_ST_ERROR);
-               fc_rport_unlock(rport);
-               if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) {
-                       fc_lport_lock(lp);
-                       del_timer(&lp->state_timer);
-                       lp->dns_rp = NULL;
-                       if (lp->state == LPORT_ST_DNS_STOP) {
-                               fc_lport_unlock(lp);
-                               lp->tt.lport_logout(lp);
-                       } else {
-                               lp->tt.lport_login(lp);
-                               fc_lport_unlock(lp);
-                       }
-                       fc_remote_port_delete(rport);
-               }
+               if (fc_rp_debug)
+                       FC_DBG("Bad PRLI response\n");
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
        }
 
        fc_frame_free(fp);
@@ -921,95 +935,91 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct 
fc_frame *fp,
  * @sp: current sequence in the LOGO exchange
  * @fp: response frame
  * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This is an action function, but all we care about
+ * is retrying or removing the rport that we just logged out of.
  */
 static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
                               void *rp_arg)
 {
-       struct fc_rport *rport = rp_arg;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp = rp->local_port;
-       u8 op;
+       struct fc_rport *rp = rp_arg;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       unsigned long flags;
 
-       if (IS_ERR(fp)) {
-               fc_rport_error(rport, fp);
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_LOGO) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a LOGO response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
                return;
        }
 
-       fc_rport_lock(rport);
-       op = fc_frame_payload_op(fp);
-       if (op == ELS_LS_ACC) {
-               fc_rport_enter_rtv(rport);
-               fc_rport_unlock(rport);
-       } else {
-               FC_DBG("bad ELS response\n");
-               fc_rport_state_enter(rport, RPORT_ST_ERROR);
-               fc_rport_unlock(rport);
-               if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) {
-                       fc_lport_lock(lp);
-                       del_timer(&lp->state_timer);
-                       lp->dns_rp = NULL;
-                       if (lp->state == LPORT_ST_DNS_STOP) {
-                               fc_lport_unlock(lp);
-                               lp->tt.lport_logout(lp);
-                       } else {
-                               lp->tt.lport_login(lp);
-                               fc_lport_unlock(lp);
-                       }
-                       fc_remote_port_delete(rport);
-               }
+       if (IS_ERR(fp)) {
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
        }
 
+       if (fc_rp_debug)
+               FC_DBG("Logged out of port (%6x)\n", rp->port_id);
+       fc_remote_port_delete(rp);
        fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_prli - Send Process Login (PRLI) request to peer
- * @rport: Fibre Channel remote port to send PRLI to
+ * @rp: Fibre Channel remote port to send PRLI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
-static void fc_rport_enter_prli(struct fc_rport *rport)
+static void fc_rport_enter_prli(struct fc_rport *rp)
 {
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
+
        struct {
                struct fc_els_prli prli;
                struct fc_els_spp spp;
        } *pp;
        struct fc_frame *fp;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp = rp->local_port;
 
-       if (fc_rp_debug)
-               FC_DBG("Entered PRLI for port (%6x)\n", rport->port_id);
+       fc_rport_state_enter(rp, RPORT_ST_PRLI);
 
-       fc_rport_state_enter(rport, RPORT_ST_PRLI);
+       if (fc_rp_debug)
+               FC_DBG("Entered PRLI for port (%6x)\n", rp->port_id);
 
        /*
-        * Special case if session is for name server or any other
-        * well-known address:  Skip the PRLI step.
-        * This should be made more general, possibly moved to the FCP layer.
+        * Special case if the RP is a well-known address then skip PRLI
         */
-       if (rport->port_id >= FC_FID_DOM_MGR) {
-               fc_rport_enter_ready(rport);
+
+       if (rp->port_id >= FC_FID_DOM_MGR) {
+               fc_rport_enter_ready(rp);
                return;
        }
+
        fp = fc_frame_alloc(lp, sizeof(*pp));
-       if (!fp)
-               return fc_rport_error(rport, fp);
+       if (!fp) {
+               fc_rport_error(rp, fp);
+               return;
+       }
+
        pp = fc_frame_payload_get(fp, sizeof(*pp));
-       WARN_ON(!pp);
        memset(pp, 0, sizeof(*pp));
        pp->prli.prli_cmd = ELS_PRLI;
        pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
        pp->prli.prli_len = htons(sizeof(*pp));
        pp->spp.spp_type = FC_TYPE_FCP;
        pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
-       pp->spp.spp_params = htonl(rp->local_port->service_params);
+       pp->spp.spp_params = htonl(lp->service_params);
        fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_prli_resp,
-                                 rport, lp->e_d_tov,
-                                 rp->local_port->fid,
-                                 rport->port_id,
+                                 rp, lp->e_d_tov,
+                                 lp->fid, rp->port_id,
                                  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-               fc_rport_error(rport, fp);
+               fc_rport_error(rp, fp);
 }
 
 /**
@@ -1023,16 +1033,26 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
 static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
                              void *rp_arg)
 {
-       struct fc_rport *rport = rp_arg;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport *rp = rp_arg;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
        u8 op;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rpp->rp_lock, flags);
+
+       if (rpp->rp_state != RPORT_ST_RTV) {
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
+               FC_DBG("Recieved a RTV response, but port (%6x) is in "
+                      "the %s state\n", rp->port_id, fc_rport_state(rp));
+               return;
+       }
 
        if (IS_ERR(fp)) {
-               fc_rport_error(rport, fp);
+               fc_rport_error(rp, fp);
+               spin_unlock_irqrestore(&rpp->rp_lock, flags);
                return;
        }
 
-       fc_rport_lock(rport);
        op = fc_frame_payload_op(fp);
        if (op == ELS_LS_ACC) {
                struct fc_els_rtv_acc *rtv;
@@ -1045,106 +1065,119 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, 
struct fc_frame *fp,
                        tov = ntohl(rtv->rtv_r_a_tov);
                        if (tov == 0)
                                tov = 1;
-                       rp->r_a_tov = tov;
+                       rpp->r_a_tov = tov;
                        tov = ntohl(rtv->rtv_e_d_tov);
                        if (toq & FC_ELS_RTV_EDRES)
                                tov /= 1000000;
                        if (tov == 0)
                                tov = 1;
-                       rp->e_d_tov = tov;
+                       rpp->e_d_tov = tov;
                }
        }
-       fc_rport_state_enter(rport, RPORT_ST_READY);
-       fc_rport_unlock(rport);
-       if (fc_rp_debug)
-               FC_DBG("remote %6x ready\n", rport->port_id);
+       fc_rport_enter_ready(rp);
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
        fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_rtv - Send Request Timeout Value (RTV) request to peer
- * @rport: Fibre Channel remote port to send RTV to
+ * @rp: Fibre Channel remote port to send RTV to
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_enter_rtv(struct fc_rport *rport)
+static void fc_rport_enter_rtv(struct fc_rport *rp)
 {
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_els_rtv *rtv;
        struct fc_frame *fp;
-       struct fc_lport *lp;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
 
-       lp = rp->local_port;
+       fc_rport_state_enter(rp, RPORT_ST_RTV);
 
        if (fc_rp_debug)
-               FC_DBG("Entered RTV for port (%6x)\n", rport->port_id);
+               FC_DBG("Entered RTV for port (%6x)\n", rp->port_id);
 
-       fc_rport_state_enter(rport, RPORT_ST_RTV);
 
        fp = fc_frame_alloc(lp, sizeof(*rtv));
-       if (!fp)
-               return fc_rport_error(rport, fp);
+       if (!fp) {
+               fc_rport_error(rp, fp);
+               return;
+       }
+
        rtv = fc_frame_payload_get(fp, sizeof(*rtv));
-       WARN_ON(!rtv);
        memset(rtv, 0, sizeof(*rtv));
        rtv->rtv_cmd = ELS_RTV;
        fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_rtv_resp,
-                                 rport, lp->e_d_tov,
-                                 rp->local_port->fid,
-                                 rport->port_id,
+                                 rp, lp->e_d_tov,
+                                 lp->fid, rp->port_id,
                                  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-               fc_rport_error(rport, fp);
+               fc_rport_error(rp, fp);
 }
 
 /**
  * fc_rport_enter_logo - Send Logout (LOGO) request to peer
- * @rport: Fibre Channel remote port to send LOGO to
+ * @rp: Fibre Channel remote port to send LOGO to
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_enter_logo(struct fc_rport *rport)
+static void fc_rport_enter_logo(struct fc_rport *rp)
 {
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame *fp;
        struct fc_els_logo *logo;
-       struct fc_lport *lp;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
 
-       if (fc_rp_debug)
-               FC_DBG("Entered LOGO for port (%6x)\n", rport->port_id);
+       fc_rport_state_enter(rp, RPORT_ST_LOGO);
 
-       fc_rport_state_enter(rport, RPORT_ST_LOGO);
+       if (fc_rp_debug)
+               FC_DBG("Entered LOGO for port (%6x)\n", rp->port_id);
 
-       lp = rp->local_port;
        fp = fc_frame_alloc(lp, sizeof(*logo));
-       if (!fp)
-               return fc_rport_error(rport, fp);
+       if (!fp) {
+               fc_rport_error(rp, fp);
+               return;
+       }
+
        logo = fc_frame_payload_get(fp, sizeof(*logo));
        memset(logo, 0, sizeof(*logo));
        logo->fl_cmd = ELS_LOGO;
        hton24(logo->fl_n_port_id, lp->fid);
        logo->fl_n_port_wwn = htonll(lp->wwpn);
-
        fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
        if (!lp->tt.exch_seq_send(lp, fp,
                                  fc_rport_logo_resp,
-                                 rport, lp->e_d_tov,
-                                 rp->local_port->fid,
-                                 rport->port_id,
+                                 rp, lp->e_d_tov,
+                                 lp->fid, rp->port_id,
                                  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-               fc_rport_error(rport, fp);
+               fc_rport_error(rp, fp);
 }
 
-/*
- * Handle a request received by the exchange manager for the session.
- * This may be an entirely new session, or a PLOGI or LOGO for an existing one.
- * This will free the frame.
+/**
+ * fc_rport_recv_req - Handle an incoming request
+ * @sp: A pointer to the sequence
+ * @fp: A pointer to the frame
+ * @rp_arg: A void pointer to the fc_rport object
+ *
+ * Locking Note: This function calls one of the request handlers. We need
+ * to lock, call the request handler and then unlock when it returns to us.
  */
 void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg)
 {
-       struct fc_rport *rport = rp_arg;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport *rp = rp_arg;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame_header *fh;
-       struct fc_lport *lp = rp->local_port;
        struct fc_seq_els_data els_data;
        u8 op;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rpp->rp_lock, flags);
 
        els_data.fp = NULL;
        els_data.explan = ELS_EXPL_NONE;
@@ -1156,16 +1189,16 @@ void fc_rport_recv_req(struct fc_seq *sp, struct 
fc_frame *fp, void *rp_arg)
                op = fc_frame_payload_op(fp);
                switch (op) {
                case ELS_PLOGI:
-                       fc_rport_recv_plogi_req(rport, sp, fp);
+                       fc_rport_recv_plogi_req(rp, sp, fp);
                        break;
                case ELS_PRLI:
-                       fc_rport_recv_prli_req(rport, sp, fp);
+                       fc_rport_recv_prli_req(rp, sp, fp);
                        break;
                case ELS_PRLO:
-                       fc_rport_recv_prlo_req(rport, sp, fp);
+                       fc_rport_recv_prlo_req(rp, sp, fp);
                        break;
                case ELS_LOGO:
-                       fc_rport_recv_logo_req(rport, sp, fp);
+                       fc_rport_recv_logo_req(rp, sp, fp);
                        break;
                case ELS_RRQ:
                        els_data.fp = fp;
@@ -1184,27 +1217,31 @@ void fc_rport_recv_req(struct fc_seq *sp, struct 
fc_frame *fp, void *rp_arg)
        } else {
                fc_frame_free(fp);
        }
+       spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
 /**
  * fc_rport_recv_plogi_req - Handle incoming Port Login (PLOGI) request
- * @rport: Fibre Channel remote port that initiated PLOGI
+ * @rp: Fibre Channel remote port that initiated PLOGI
  * @sp: current sequence in the PLOGI exchange
  * @fp: PLOGI request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+static void fc_rport_recv_plogi_req(struct fc_rport *rp,
                                    struct fc_seq *sp, struct fc_frame *rx_fp)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_frame *fp = rx_fp;
        struct fc_frame_header *fh;
-       struct fc_lport *lp;
        struct fc_els_flogi *pl;
        struct fc_seq_els_data rjt_data;
+       enum fc_els_rjt_reason reject = 0;
        u32 sid;
        u64 wwpn;
        u64 wwnn;
-       enum fc_els_rjt_reason reject = 0;
        u32 f_ctl;
 
        rjt_data.fp = NULL;
@@ -1220,8 +1257,6 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
        }
        wwpn = get_unaligned_be64(&pl->fl_wwpn);
        wwnn = get_unaligned_be64(&pl->fl_wwnn);
-       fc_rport_lock(rport);
-       lp = rp->local_port;
 
        /*
         * If the session was just created, possibly due to the incoming PLOGI,
@@ -1234,7 +1269,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
         * XXX TBD: If the session was ready before, the PLOGI should result in
         * all outstanding exchanges being reset.
         */
-       switch (rp->rp_state) {
+       switch (rpp->rp_state) {
        case RPORT_ST_INIT:
                if (fc_rp_debug)
                        FC_DBG("incoming PLOGI from %6x wwpn %llx state INIT "
@@ -1244,7 +1279,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
        case RPORT_ST_PLOGI:
                if (fc_rp_debug)
                        FC_DBG("incoming PLOGI from %x in PLOGI state %d\n",
-                              sid, rp->rp_state);
+                              sid, rpp->rp_state);
                if (wwpn < lp->wwpn)
                        reject = ELS_RJT_INPROG;
                break;
@@ -1253,14 +1288,14 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
        case RPORT_ST_READY:
                if (fc_rp_debug)
                        FC_DBG("incoming PLOGI from %x in logged - in state %d "
-                              "- ignored for now\n", sid, rp->rp_state);
+                              "- ignored for now\n", sid, rpp->rp_state);
                /* XXX TBD - should reset */
                break;
        case RPORT_ST_NONE:
        default:
                if (fc_rp_debug)
                        FC_DBG("incoming PLOGI from %x in unexpected "
-                              "state %d\n", sid, rp->rp_state);
+                              "state %d\n", sid, rpp->rp_state);
                break;
        }
 
@@ -1271,7 +1306,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
                fc_frame_free(fp);
        } else {
                fp = fc_frame_alloc(lp, sizeof(*pl));
-               if (fp == NULL) {
+               if (!fp) {
                        fp = rx_fp;
                        rjt_data.reason = ELS_RJT_UNAB;
                        rjt_data.explan = ELS_EXPL_NONE;
@@ -1280,12 +1315,12 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
                } else {
                        sp = lp->tt.seq_start_next(sp);
                        WARN_ON(!sp);
-                       fc_rport_set_name(rport, wwpn, wwnn);
+                       fc_rport_set_name(rp, wwpn, wwnn);
 
                        /*
                         * Get session payload size from incoming PLOGI.
                         */
-                       rport->maxframe_size =
+                       rp->maxframe_size =
                                fc_plogi_get_maxframe(pl, lp->mfs);
                        fc_frame_free(rx_fp);
                        pl = fc_frame_payload_get(fp, sizeof(*pl));
@@ -1299,14 +1334,11 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
                        f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
                        fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
                        lp->tt.seq_send(lp, sp, fp, f_ctl);
-                       if (rp->rp_state == RPORT_ST_PLOGI)
-                               fc_rport_enter_prli(rport);
-                       else
-                               fc_rport_state_enter(rport,
-                                                    RPORT_ST_PLOGI_RECV);
+                       if (rpp->rp_state == RPORT_ST_PLOGI)
+                               fc_rport_enter_prli(rp);
                }
        }
-       fc_rport_unlock(rport);
+
 }
 
 /**
@@ -1314,11 +1346,14 @@ static void fc_rport_recv_plogi_req(struct fc_rport 
*rport,
  * @rport: Fibre Channel remote port that initiated PRLI
  * @sp: current sequence in the PRLI exchange
  * @fp: PRLI request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_prli_req(struct fc_rport *rport,
+static void fc_rport_recv_prli_req(struct fc_rport *rp,
                                   struct fc_seq *sp, struct fc_frame *rx_fp)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
        struct fc_frame *fp;
        struct fc_frame_header *fh;
        struct fc_lport *lp;
@@ -1340,9 +1375,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 
        rjt_data.fp = NULL;
        fh = fc_frame_header_get(rx_fp);
-       lp = rp->local_port;
-       switch (rp->rp_state) {
-       case RPORT_ST_PLOGI_RECV:
+       lp = rpp->local_port;
+       switch (rpp->rp_state) {
        case RPORT_ST_PRLI:
        case RPORT_ST_READY:
                reason = ELS_RJT_NONE;
@@ -1406,16 +1440,16 @@ static void fc_rport_recv_prli_req(struct fc_rport 
*rport,
                        case FC_TYPE_FCP:
                                fcp_parm = ntohl(rspp->spp_params);
                                if (fcp_parm * FCP_SPPF_RETRY)
-                                       rp->flags |= FC_RP_FLAGS_RETRY;
-                               rport->supported_classes = FC_COS_CLASS3;
+                                       rpp->flags |= FC_RP_FLAGS_RETRY;
+                               rp->supported_classes = FC_COS_CLASS3;
                                if (fcp_parm & FCP_SPPF_INIT_FCN)
                                        roles |= FC_RPORT_ROLE_FCP_INITIATOR;
                                if (fcp_parm & FCP_SPPF_TARG_FCN)
                                        roles |= FC_RPORT_ROLE_FCP_TARGET;
 
-                               fc_remote_port_rolechg(rport, roles);
+                               fc_remote_port_rolechg(rp, roles);
                                spp->spp_params =
-                                       htonl(rp->local_port->service_params);
+                                       htonl(rpp->local_port->service_params);
                                break;
                        default:
                                resp = FC_SPP_RESP_INVL;
@@ -1437,14 +1471,12 @@ static void fc_rport_recv_prli_req(struct fc_rport 
*rport,
                /*
                 * Get lock and re-check state.
                 */
-               fc_rport_lock(rport);
-               switch (rp->rp_state) {
-               case RPORT_ST_PLOGI_RECV:
+               switch (rpp->rp_state) {
                case RPORT_ST_PRLI:
-                       fc_rport_state_enter(rport, RPORT_ST_READY);
+                       fc_rport_state_enter(rp, RPORT_ST_READY);
                        if (fc_rp_debug)
-                               FC_DBG("remote %6x ready\n", rport->port_id);
-                       if (rport == lp->dns_rp &&
+                               FC_DBG("remote %6x ready\n", rp->port_id);
+                       if (rp == lp->dns_rp &&
                            lp->state == LPORT_ST_DNS) {
                                fc_lport_lock(lp);
                                del_timer(&lp->state_timer);
@@ -1457,28 +1489,31 @@ static void fc_rport_recv_prli_req(struct fc_rport 
*rport,
                default:
                        break;
                }
-               fc_rport_unlock(rport);
+
        }
        fc_frame_free(rx_fp);
 }
 
 /**
  * fc_rport_recv_prlo_req - Handle incoming Process Logout (PRLO) request
- * @rport: Fibre Channel remote port that initiated PRLO
+ * @rp: Fibre Channel remote port that initiated PRLO
  * @sp: current sequence in the PRLO exchange
  * @fp: PRLO request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_prlo_req(struct fc_rport *rp, struct fc_seq *sp,
                                   struct fc_frame *fp)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
        struct fc_frame_header *fh;
-       struct fc_lport *lp = rp->local_port;
+       struct fc_lport *lp = rpp->local_port;
        struct fc_seq_els_data rjt_data;
 
        fh = fc_frame_header_get(fp);
        FC_DBG("incoming PRLO from %x state %d\n",
-              ntoh24(fh->fh_s_id), rp->rp_state);
+              ntoh24(fh->fh_s_id), rpp->rp_state);
        rjt_data.fp = NULL;
        rjt_data.reason = ELS_RJT_UNAB;
        rjt_data.explan = ELS_EXPL_NONE;
@@ -1491,34 +1526,27 @@ static void fc_rport_recv_prlo_req(struct fc_rport 
*rport, struct fc_seq *sp,
  * @rport: Fibre Channel remote port that initiated LOGO
  * @sp: current sequence in the LOGO exchange
  * @fp: LOGO request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_logo_req(struct fc_rport *rp, struct fc_seq *sp,
                                   struct fc_frame *fp)
 {
        struct fc_frame_header *fh;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lp = rp->local_port;
+       struct fc_rport_libfc_priv *rpp = rp->dd_data;
+       struct fc_lport *lp = rpp->local_port;
 
-       fh = fc_frame_header_get(fp);
-       fc_rport_lock(rport);
-       fc_rport_state_enter(rport, RPORT_ST_INIT);
-       fc_rport_unlock(rport);
        if (fc_rp_debug)
-               FC_DBG("remote %6x closed\n", rport->port_id);
-       if (rport == lp->dns_rp &&
-           lp->state != LPORT_ST_RESET) {
-               fc_lport_lock(lp);
-               del_timer(&lp->state_timer);
-               lp->dns_rp = NULL;
-               if (lp->state == LPORT_ST_DNS_STOP) {
-                       fc_lport_unlock(lp);
-                       lp->tt.lport_logout(lp);
-               } else {
-                       lp->tt.lport_login(lp);
-                       fc_lport_unlock(lp);
-               }
-               fc_remote_port_delete(rport);
-       }
+               FC_DBG("Received LOGO request for port (%6x)\n", rp->port_id);
+
+       fh = fc_frame_header_get(fp);
+
+       if (rp->port_id == FC_FID_DIR_SERV)
+               lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_LOGO);
+
+       fc_remote_port_delete(rp);
+
        lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
        fc_frame_free(fp);
 }
diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h
index ef0bb8e..61a7d59 100644
--- a/include/scsi/libfc/libfc.h
+++ b/include/scsi/libfc/libfc.h
@@ -103,7 +103,8 @@ enum fc_lport_state {
 
 enum fc_lport_event {
        LPORT_EV_RPORT_CREATED = 0,
-       LPORT_EV_RPORT_FAILED
+       LPORT_EV_RPORT_FAILED,
+       LPORT_EV_RPORT_LOGO,
 };
 
 enum fc_rport_state {
@@ -112,7 +113,6 @@ enum fc_rport_state {
        RPORT_ST_GPN_ID,        /* get the WWPN from the name server */
        RPORT_ST_GNN_ID,        /* get the WWNN from the name server */
        RPORT_ST_PLOGI,         /* waiting for PLOGI completion */
-       RPORT_ST_PLOGI_RECV,    /* received PLOGI (as target) */
        RPORT_ST_PRLI,          /* waiting for PRLI completion */
        RPORT_ST_RTV,           /* waiting for RTV completion */
        RPORT_ST_ERROR,         /* error */
@@ -361,7 +361,7 @@ struct libfc_function_template {
         * Logs the specified local port out of a N_Port identified
         * by the ID provided.
         */
-       int (*rport_logout)(struct fc_rport *rport);
+       void (*rport_logout)(struct fc_rport *rport);
 
        void (*rport_recv_req)(struct fc_seq *, struct fc_frame *, void *);
 

_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel

Reply via email to