]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - drivers/scsi/libfc/fc_lport.c
Merge tag 'isci-for-3.5' into misc
[linux-3.10.git] / drivers / scsi / libfc / fc_lport.c
index ef32b065a47f66c022e8a8585cc2658bad4e7d23..c1402fb499ab3c3f3038163c907aa2a7538091b2 100644 (file)
@@ -52,7 +52,7 @@
  * while making the callback. To ensure that the rport is not free'd while
  * processing the callback the rport callbacks are serialized through a
  * single-threaded workqueue. An rport would never be free'd while in a
- * callback handler becuase no other rport work in this queue can be executed
+ * callback handler because no other rport work in this queue can be executed
  * at the same time.
  *
  * When discovery succeeds or fails a callback is made to the lport as
@@ -88,6 +88,8 @@
  */
 
 #include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <asm/unaligned.h>
 
@@ -114,6 +116,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
 static void fc_lport_enter_scr(struct fc_lport *);
 static void fc_lport_enter_ready(struct fc_lport *);
 static void fc_lport_enter_logo(struct fc_lport *);
+static void fc_lport_enter_fdmi(struct fc_lport *lport);
+static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state);
 
 static const char *fc_lport_state_names[] = {
        [LPORT_ST_DISABLED] = "disabled",
@@ -124,6 +128,11 @@ static const char *fc_lport_state_names[] = {
        [LPORT_ST_RSPN_ID] =  "RSPN_ID",
        [LPORT_ST_RFT_ID] =   "RFT_ID",
        [LPORT_ST_RFF_ID] =   "RFF_ID",
+       [LPORT_ST_FDMI] =     "FDMI",
+       [LPORT_ST_RHBA] =     "RHBA",
+       [LPORT_ST_RPA] =      "RPA",
+       [LPORT_ST_DHBA] =     "DHBA",
+       [LPORT_ST_DPRT] =     "DPRT",
        [LPORT_ST_SCR] =      "SCR",
        [LPORT_ST_READY] =    "Ready",
        [LPORT_ST_LOGO] =     "LOGO",
@@ -163,7 +172,7 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
  * fc_lport_rport_callback() - Event handler for rport events
  * @lport: The lport which is receiving the event
  * @rdata: private remote port data
- * @event: The event that occured
+ * @event: The event that occurred
  *
  * Locking Note: The rport lock should not be held when calling
  *              this function.
@@ -181,11 +190,14 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
                if (lport->state == LPORT_ST_DNS) {
                        lport->dns_rdata = rdata;
                        fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
+               } else if (lport->state == LPORT_ST_FDMI) {
+                       lport->ms_rdata = rdata;
+                       fc_lport_enter_ms(lport, LPORT_ST_DHBA);
                } else {
                        FC_LPORT_DBG(lport, "Received an READY event "
                                     "on port (%6.6x) for the directory "
                                     "server, but the lport is not "
-                                    "in the DNS state, it's in the "
+                                    "in the DNS or FDMI state, it's in the "
                                     "%d state", rdata->ids.port_id,
                                     lport->state);
                        lport->tt.rport_logoff(rdata);
@@ -194,7 +206,10 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
        case RPORT_EV_LOGO:
        case RPORT_EV_FAILED:
        case RPORT_EV_STOP:
-               lport->dns_rdata = NULL;
+               if (rdata->ids.port_id == FC_FID_DIR_SERV)
+                       lport->dns_rdata = NULL;
+               else if (rdata->ids.port_id == FC_FID_MGMT_SERV)
+                       lport->ms_rdata = NULL;
                break;
        case RPORT_EV_NONE:
                break;
@@ -288,6 +303,8 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
        struct fc_lport *lport = shost_priv(shost);
        struct timespec v0, v1;
        unsigned int cpu;
+       u64 fcp_in_bytes = 0;
+       u64 fcp_out_bytes = 0;
 
        fcoe_stats = &lport->host_stats;
        memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
@@ -310,10 +327,12 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
                fcoe_stats->fcp_input_requests += stats->InputRequests;
                fcoe_stats->fcp_output_requests += stats->OutputRequests;
                fcoe_stats->fcp_control_requests += stats->ControlRequests;
-               fcoe_stats->fcp_input_megabytes += stats->InputMegabytes;
-               fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes;
+               fcp_in_bytes += stats->InputBytes;
+               fcp_out_bytes += stats->OutputBytes;
                fcoe_stats->link_failure_count += stats->LinkFailureCount;
        }
+       fcoe_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000);
+       fcoe_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000);
        fcoe_stats->lip_count = -1;
        fcoe_stats->nos_count = -1;
        fcoe_stats->loss_of_sync_count = -1;
@@ -375,41 +394,36 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
 
 /**
  * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
- * @sp:           The sequence in the RLIR exchange
+ * @lport: Fibre Channel local port receiving the RLIR
  * @fp:           The RLIR request frame
- * @lport: Fibre Channel local port recieving the RLIR
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
-                                  struct fc_lport *lport)
+static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)
 {
        FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
                     fc_lport_state(lport));
 
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+       lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
        fc_frame_free(fp);
 }
 
 /**
  * fc_lport_recv_echo_req() - Handle received ECHO request
- * @sp:           The sequence in the ECHO exchange
+ * @lport: The local port receiving the ECHO
  * @fp:           ECHO request frame
- * @lport: The local port recieving the ECHO
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
-                                  struct fc_lport *lport)
+static void fc_lport_recv_echo_req(struct fc_lport *lport,
+                                  struct fc_frame *in_fp)
 {
        struct fc_frame *fp;
-       struct fc_exch *ep = fc_seq_exch(sp);
        unsigned int len;
        void *pp;
        void *dp;
-       u32 f_ctl;
 
        FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
                     fc_lport_state(lport));
@@ -425,29 +439,24 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
                dp = fc_frame_payload_get(fp, len);
                memcpy(dp, pp, len);
                *((__be32 *)dp) = htonl(ELS_LS_ACC << 24);
-               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, in_fp, FC_RCTL_ELS_REP, 0);
+               lport->tt.frame_send(lport, fp);
        }
        fc_frame_free(in_fp);
 }
 
 /**
  * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
- * @sp:           The sequence in the RNID exchange
+ * @lport: The local port receiving the RNID
  * @fp:           The RNID request frame
- * @lport: The local port recieving the RNID
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
-                                  struct fc_lport *lport)
+static void fc_lport_recv_rnid_req(struct fc_lport *lport,
+                                  struct fc_frame *in_fp)
 {
        struct fc_frame *fp;
-       struct fc_exch *ep = fc_seq_exch(sp);
        struct fc_els_rnid *req;
        struct {
                struct fc_els_rnid_resp rnid;
@@ -457,17 +466,15 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
        struct fc_seq_els_data rjt_data;
        u8 fmt;
        size_t len;
-       u32 f_ctl;
 
        FC_LPORT_DBG(lport, "Received RNID request while in state %s\n",
                     fc_lport_state(lport));
 
        req = fc_frame_payload_get(in_fp, sizeof(*req));
        if (!req) {
-               rjt_data.fp = NULL;
                rjt_data.reason = ELS_RJT_LOGIC;
                rjt_data.explan = ELS_EXPL_NONE;
-               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);
        } else {
                fmt = req->rnid_fmt;
                len = sizeof(*rp);
@@ -490,12 +497,8 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
                                memcpy(&rp->gen, &lport->rnid_gen,
                                       sizeof(rp->gen));
                        }
-                       sp = lport->tt.seq_start_next(sp);
-                       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
-                       f_ctl |= 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);
                }
        }
        fc_frame_free(in_fp);
@@ -503,17 +506,15 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 
 /**
  * fc_lport_recv_logo_req() - Handle received fabric LOGO request
- * @sp:           The sequence in the LOGO exchange
+ * @lport: The local port receiving the LOGO
  * @fp:           The LOGO request frame
- * @lport: The local port recieving the LOGO
  *
  * Locking Note: The lport lock is exected to be held before calling
  * this function.
  */
-static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
-                                  struct fc_lport *lport)
+static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
 {
-       lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+       lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
        fc_lport_enter_reset(lport);
        fc_frame_free(fp);
 }
@@ -647,6 +648,8 @@ int fc_lport_destroy(struct fc_lport *lport)
        lport->tt.fcp_abort_io(lport);
        lport->tt.disc_stop_final(lport);
        lport->tt.exch_mgr_reset(lport, 0, 0);
+       cancel_delayed_work_sync(&lport->retry_work);
+       fc_fc4_del_lport(lport);
        return 0;
 }
 EXPORT_SYMBOL(fc_lport_destroy);
@@ -688,7 +691,8 @@ EXPORT_SYMBOL(fc_set_mfs);
  * @lport: The local port receiving the event
  * @event: The discovery event
  */
-void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
+static void fc_lport_disc_callback(struct fc_lport *lport,
+                                  enum fc_disc_event event)
 {
        switch (event) {
        case DISC_EV_SUCCESS:
@@ -754,11 +758,38 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
                lport->tt.lport_set_port_id(lport, port_id, fp);
 }
 
+/**
+ * fc_lport_set_port_id() - set the local port Port ID for point-to-multipoint
+ * @lport: The local port which will have its Port ID set.
+ * @port_id: The new port ID.
+ *
+ * Called by the lower-level driver when transport sets the local port_id.
+ * This is used in VN_port to VN_port mode for FCoE, and causes FLOGI and
+ * discovery to be skipped.
+ */
+void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
+{
+       mutex_lock(&lport->lp_mutex);
+
+       fc_lport_set_port_id(lport, port_id, NULL);
+
+       switch (lport->state) {
+       case LPORT_ST_RESET:
+       case LPORT_ST_FLOGI:
+               if (port_id)
+                       fc_lport_enter_ready(lport);
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_lport_set_local_id);
+
 /**
  * fc_lport_recv_flogi_req() - Receive a FLOGI request
- * @sp_in: The sequence the FLOGI is on
+ * @lport: The local port that received the request
  * @rx_fp: The FLOGI frame
- * @lport: The local port that recieved the request
  *
  * A received FLOGI request indicates a point-to-point connection.
  * Accept it with the common service parameters indicating our N port.
@@ -767,26 +798,21 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
-                                   struct fc_frame *rx_fp,
-                                   struct fc_lport *lport)
+static void fc_lport_recv_flogi_req(struct fc_lport *lport,
+                                   struct fc_frame *rx_fp)
 {
        struct fc_frame *fp;
        struct fc_frame_header *fh;
-       struct fc_seq *sp;
-       struct fc_exch *ep;
        struct fc_els_flogi *flp;
        struct fc_els_flogi *new_flp;
        u64 remote_wwpn;
        u32 remote_fid;
        u32 local_fid;
-       u32 f_ctl;
 
        FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n",
                     fc_lport_state(lport));
 
-       fh = fc_frame_header_get(rx_fp);
-       remote_fid = ntoh24(fh->fh_s_id);
+       remote_fid = fc_frame_sid(rx_fp);
        flp = fc_frame_payload_get(rx_fp, sizeof(*flp));
        if (!flp)
                goto out;
@@ -817,7 +843,6 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 
        fp = fc_frame_alloc(lport, sizeof(*flp));
        if (fp) {
-               sp = lport->tt.seq_start_next(fr_seq(rx_fp));
                new_flp = fc_frame_payload_get(fp, sizeof(*flp));
                fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI);
                new_flp->fl_cmd = (u8) ELS_LS_ACC;
@@ -826,40 +851,36 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
                 * Send the response.  If this fails, the originator should
                 * repeat the sequence.
                 */
-               f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-               ep = fc_seq_exch(sp);
-               fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, remote_fid, local_fid,
-                              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);
+               fh = fc_frame_header_get(fp);
+               hton24(fh->fh_s_id, local_fid);
+               hton24(fh->fh_d_id, remote_fid);
+               lport->tt.frame_send(lport, fp);
 
        } else {
                fc_lport_error(lport, fp);
        }
        fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
                           get_unaligned_be64(&flp->fl_wwnn));
-
 out:
-       sp = fr_seq(rx_fp);
        fc_frame_free(rx_fp);
 }
 
 /**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv_els_req() - The generic lport ELS request handler
  * @lport: The local port that received the request
- * @sp:           The sequence the request is on
  * @fp:           The request frame
  *
  * This function will see if the lport handles the request or
  * if an rport should handle the request.
  *
  * Locking Note: This function should not be called with the lport
- *              lock held becuase it will grab the lock.
+ *              lock held because it will grab the lock.
  */
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
-                             struct fc_frame *fp)
+static void fc_lport_recv_els_req(struct fc_lport *lport,
+                                 struct fc_frame *fp)
 {
-       struct fc_frame_header *fh = fc_frame_header_get(fp);
-       void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
+       void (*recv)(struct fc_lport *, struct fc_frame *);
 
        mutex_lock(&lport->lp_mutex);
 
@@ -870,19 +891,18 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
         */
        if (!lport->link_up)
                fc_frame_free(fp);
-       else if (fh->fh_type == FC_TYPE_ELS &&
-                fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+       else {
                /*
                 * Check opcode.
                 */
                recv = lport->tt.rport_recv_req;
                switch (fc_frame_payload_op(fp)) {
                case ELS_FLOGI:
-                       recv = fc_lport_recv_flogi_req;
+                       if (!lport->point_to_multipoint)
+                               recv = fc_lport_recv_flogi_req;
                        break;
                case ELS_LOGO:
-                       fh = fc_frame_header_get(fp);
-                       if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI)
+                       if (fc_frame_sid(fp) == FC_FID_FLOGI)
                                recv = fc_lport_recv_logo_req;
                        break;
                case ELS_RSCN:
@@ -899,18 +919,60 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
                        break;
                }
 
-               recv(sp, fp, lport);
-       } else {
-               FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
-                            fr_eof(fp));
-               fc_frame_free(fp);
+               recv(lport, fp);
        }
        mutex_unlock(&lport->lp_mutex);
+}
+
+static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
+                            const struct fc_els_spp *spp_in,
+                            struct fc_els_spp *spp_out)
+{
+       return FC_SPP_RESP_INVL;
+}
+
+struct fc4_prov fc_lport_els_prov = {
+       .prli = fc_lport_els_prli,
+       .recv = fc_lport_recv_els_req,
+};
+
+/**
+ * fc_lport_recv_req() - The generic lport request handler
+ * @lport: The lport that received the request
+ * @fp: The frame the request is in
+ *
+ * Locking Note: This function should not be called with the lport
+ *              lock held because it may grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport,
+                             struct fc_frame *fp)
+{
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+       struct fc_seq *sp = fr_seq(fp);
+       struct fc4_prov *prov;
 
        /*
-        *  The common exch_done for all request may not be good
-        *  if any request requires longer hold on exhange. XXX
+        * Use RCU read lock and module_lock to be sure module doesn't
+        * deregister and get unloaded while we're calling it.
+        * try_module_get() is inlined and accepts a NULL parameter.
+        * Only ELSes and FCP target ops should come through here.
+        * The locking is unfortunate, and a better scheme is being sought.
         */
+
+       rcu_read_lock();
+       if (fh->fh_type >= FC_FC4_PROV_SIZE)
+               goto drop;
+       prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
+       if (!prov || !try_module_get(prov->module))
+               goto drop;
+       rcu_read_unlock();
+       prov->recv(lport, fp);
+       module_put(prov->module);
+       return;
+drop:
+       rcu_read_unlock();
+       FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
+       fc_frame_free(fp);
        lport->tt.exch_done(sp);
 }
 
@@ -954,7 +1016,7 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
        lport->tt.exch_mgr_reset(lport, 0, 0);
        fc_host_fabric_name(lport->host) = 0;
 
-       if (lport->port_id)
+       if (lport->port_id && (!lport->point_to_multipoint || !lport->link_up))
                fc_lport_set_port_id(lport, 0, NULL);
 }
 
@@ -980,6 +1042,8 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
                        fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
        }
        fc_lport_state_enter(lport, LPORT_ST_RESET);
+       fc_host_post_event(lport->host, fc_get_event_number(),
+                          FCH_EVT_LIPRESET, 0);
        fc_vports_linkchange(lport);
        fc_lport_reset_locked(lport);
        if (lport->link_up)
@@ -1019,38 +1083,24 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
                     PTR_ERR(fp), fc_lport_state(lport),
                     lport->retry_count);
 
-       if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
-               /*
-                * Memory allocation failure, or the exchange timed out.
-                *  Retry after delay
-                */
-               if (lport->retry_count < lport->max_retry_count) {
-                       lport->retry_count++;
-                       if (!fp)
-                               delay = msecs_to_jiffies(500);
-                       else
-                               delay = msecs_to_jiffies(lport->e_d_tov);
+       if (PTR_ERR(fp) == -FC_EX_CLOSED)
+               return;
 
-                       schedule_delayed_work(&lport->retry_work, delay);
-               } else {
-                       switch (lport->state) {
-                       case LPORT_ST_DISABLED:
-                       case LPORT_ST_READY:
-                       case LPORT_ST_RESET:
-                       case LPORT_ST_RNN_ID:
-                       case LPORT_ST_RSNN_NN:
-                       case LPORT_ST_RSPN_ID:
-                       case LPORT_ST_RFT_ID:
-                       case LPORT_ST_RFF_ID:
-                       case LPORT_ST_SCR:
-                       case LPORT_ST_DNS:
-                       case LPORT_ST_FLOGI:
-                       case LPORT_ST_LOGO:
-                               fc_lport_enter_reset(lport);
-                               break;
-                       }
-               }
-       }
+       /*
+        * Memory allocation failure, or the exchange timed out
+        * or we received LS_RJT.
+        * Retry after delay
+        */
+       if (lport->retry_count < lport->max_retry_count) {
+               lport->retry_count++;
+               if (!fp)
+                       delay = msecs_to_jiffies(500);
+               else
+                       delay = msecs_to_jiffies(lport->e_d_tov);
+
+               schedule_delayed_work(&lport->retry_work, delay);
+       } else
+               fc_lport_enter_reset(lport);
 }
 
 /**
@@ -1112,7 +1162,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
                        fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
                        break;
                case LPORT_ST_RFF_ID:
-                       fc_lport_enter_scr(lport);
+                       if (lport->fdmi_enabled)
+                               fc_lport_enter_fdmi(lport);
+                       else
+                               fc_lport_enter_scr(lport);
                        break;
                default:
                        /* should have already been caught by state checks */
@@ -1126,6 +1179,85 @@ err:
        mutex_unlock(&lport->lp_mutex);
 }
 
+/**
+ * fc_lport_ms_resp() - Handle response to a management server
+ *                     exchange
+ * @sp:            current sequence in exchange
+ * @fp:            response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
+ * and then unlock the lport.
+ */
+static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp,
+                            void *lp_arg)
+{
+       struct fc_lport *lport = lp_arg;
+       struct fc_frame_header *fh;
+       struct fc_ct_hdr *ct;
+
+       FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp));
+
+       if (fp == ERR_PTR(-FC_EX_CLOSED))
+               return;
+
+       mutex_lock(&lport->lp_mutex);
+
+       if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) {
+               FC_LPORT_DBG(lport, "Received a management server response, "
+                            "but in state %s\n", fc_lport_state(lport));
+               if (IS_ERR(fp))
+                       goto err;
+               goto out;
+       }
+
+       if (IS_ERR(fp)) {
+               fc_lport_error(lport, fp);
+               goto err;
+       }
+
+       fh = fc_frame_header_get(fp);
+       ct = fc_frame_payload_get(fp, sizeof(*ct));
+
+       if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+           ct->ct_fs_type == FC_FST_MGMT &&
+           ct->ct_fs_subtype == FC_FDMI_SUBTYPE) {
+               FC_LPORT_DBG(lport, "Received a management server response, "
+                                   "reason=%d explain=%d\n",
+                                   ct->ct_reason,
+                                   ct->ct_explan);
+
+               switch (lport->state) {
+               case LPORT_ST_RHBA:
+                       if (ntohs(ct->ct_cmd) == FC_FS_ACC)
+                               fc_lport_enter_ms(lport, LPORT_ST_RPA);
+                       else /* Error Skip RPA */
+                               fc_lport_enter_scr(lport);
+                       break;
+               case LPORT_ST_RPA:
+                       fc_lport_enter_scr(lport);
+                       break;
+               case LPORT_ST_DPRT:
+                       fc_lport_enter_ms(lport, LPORT_ST_RHBA);
+                       break;
+               case LPORT_ST_DHBA:
+                       fc_lport_enter_ms(lport, LPORT_ST_DPRT);
+                       break;
+               default:
+                       /* should have already been caught by state checks */
+                       break;
+               }
+       } else {
+               /* Invalid Frame? */
+               fc_lport_error(lport, fp);
+       }
+out:
+       fc_frame_free(fp);
+err:
+       mutex_unlock(&lport->lp_mutex);
+}
+
 /**
  * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
  * @sp:            current sequence in SCR exchange
@@ -1302,6 +1434,123 @@ err:
        fc_lport_error(lport, NULL);
 }
 
+/**
+ * fc_lport_enter_ms() - management server commands
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state)
+{
+       struct fc_frame *fp;
+       enum fc_fdmi_req cmd;
+       int size = sizeof(struct fc_ct_hdr);
+       size_t len;
+       int numattrs;
+
+       FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+                    fc_lport_state_names[state],
+                    fc_lport_state(lport));
+
+       fc_lport_state_enter(lport, state);
+
+       switch (state) {
+       case LPORT_ST_RHBA:
+               cmd = FC_FDMI_RHBA;
+               /* Number of HBA Attributes */
+               numattrs = 10;
+               len = sizeof(struct fc_fdmi_rhba);
+               len -= sizeof(struct fc_fdmi_attr_entry);
+               len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+               len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
+               len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
+               len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
+               len += FC_FDMI_HBA_ATTR_MODEL_LEN;
+               len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
+               len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
+               len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
+               len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
+               len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
+               len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
+
+               size += len;
+               break;
+       case LPORT_ST_RPA:
+               cmd = FC_FDMI_RPA;
+               /* Number of Port Attributes */
+               numattrs = 6;
+               len = sizeof(struct fc_fdmi_rpa);
+               len -= sizeof(struct fc_fdmi_attr_entry);
+               len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+               len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
+               len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
+               len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
+               len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
+               len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
+               len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
+
+               size += len;
+               break;
+       case LPORT_ST_DPRT:
+               cmd = FC_FDMI_DPRT;
+               len = sizeof(struct fc_fdmi_dprt);
+               size += len;
+               break;
+       case LPORT_ST_DHBA:
+               cmd = FC_FDMI_DHBA;
+               len = sizeof(struct fc_fdmi_dhba);
+               size += len;
+               break;
+       default:
+               fc_lport_error(lport, NULL);
+               return;
+       }
+
+       FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n",
+                            cmd, (int)len, size);
+       fp = fc_frame_alloc(lport, size);
+       if (!fp) {
+               fc_lport_error(lport, fp);
+               return;
+       }
+
+       if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd,
+                                 fc_lport_ms_resp,
+                                 lport, 3 * lport->r_a_tov))
+               fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_rport_enter_fdmi() - Create a fc_rport for the management server
+ * @lport: The local port requesting a remote port for the management server
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_fdmi(struct fc_lport *lport)
+{
+       struct fc_rport_priv *rdata;
+
+       FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n",
+                    fc_lport_state(lport));
+
+       fc_lport_state_enter(lport, LPORT_ST_FDMI);
+
+       mutex_lock(&lport->disc.disc_mutex);
+       rdata = lport->tt.rport_create(lport, FC_FID_MGMT_SERV);
+       mutex_unlock(&lport->disc.disc_mutex);
+       if (!rdata)
+               goto err;
+
+       rdata->ops = &fc_lport_rport_ops;
+       lport->tt.rport_login(rdata);
+       return;
+
+err:
+       fc_lport_error(lport, NULL);
+}
+
 /**
  * fc_lport_timeout() - Handler for the retry_work timer
  * @work: The work struct of the local port
@@ -1316,10 +1565,8 @@ static void fc_lport_timeout(struct work_struct *work)
 
        switch (lport->state) {
        case LPORT_ST_DISABLED:
-               WARN_ON(1);
                break;
        case LPORT_ST_READY:
-               WARN_ON(1);
                break;
        case LPORT_ST_RESET:
                break;
@@ -1336,6 +1583,15 @@ static void fc_lport_timeout(struct work_struct *work)
        case LPORT_ST_RFF_ID:
                fc_lport_enter_ns(lport, lport->state);
                break;
+       case LPORT_ST_FDMI:
+               fc_lport_enter_fdmi(lport);
+               break;
+       case LPORT_ST_RHBA:
+       case LPORT_ST_RPA:
+       case LPORT_ST_DHBA:
+       case LPORT_ST_DPRT:
+               fc_lport_enter_ms(lport, lport->state);
+               break;
        case LPORT_ST_SCR:
                fc_lport_enter_scr(lport);
                break;
@@ -1469,48 +1725,65 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
        }
 
        fh = fc_frame_header_get(fp);
-       did = ntoh24(fh->fh_d_id);
-       if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
-               flp = fc_frame_payload_get(fp, sizeof(*flp));
-               if (flp) {
-                       mfs = ntohs(flp->fl_csp.sp_bb_data) &
-                               FC_SP_BB_DATA_MASK;
-                       if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
-                           mfs < lport->mfs)
-                               lport->mfs = mfs;
-                       csp_flags = ntohs(flp->fl_csp.sp_features);
-                       r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
-                       e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
-                       if (csp_flags & FC_SP_FT_EDTR)
-                               e_d_tov /= 1000000;
-
-                       lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
-
-                       if ((csp_flags & FC_SP_FT_FPORT) == 0) {
-                               if (e_d_tov > lport->e_d_tov)
-                                       lport->e_d_tov = e_d_tov;
-                               lport->r_a_tov = 2 * e_d_tov;
-                               fc_lport_set_port_id(lport, did, fp);
-                               printk(KERN_INFO "host%d: libfc: "
-                                      "Port (%6.6x) entered "
-                                      "point-to-point mode\n",
-                                      lport->host->host_no, did);
-                               fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
-                                                  get_unaligned_be64(
-                                                          &flp->fl_wwpn),
-                                                  get_unaligned_be64(
-                                                          &flp->fl_wwnn));
-                       } else {
-                               lport->e_d_tov = e_d_tov;
-                               lport->r_a_tov = r_a_tov;
-                               fc_host_fabric_name(lport->host) =
-                                       get_unaligned_be64(&flp->fl_wwnn);
-                               fc_lport_set_port_id(lport, did, fp);
-                               fc_lport_enter_dns(lport);
-                       }
-               }
+       did = fc_frame_did(fp);
+       if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 ||
+           fc_frame_payload_op(fp) != ELS_LS_ACC) {
+               FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");
+               fc_lport_error(lport, fp);
+               goto err;
+       }
+
+       flp = fc_frame_payload_get(fp, sizeof(*flp));
+       if (!flp) {
+               FC_LPORT_DBG(lport, "FLOGI bad response\n");
+               fc_lport_error(lport, fp);
+               goto err;
+       }
+
+       mfs = ntohs(flp->fl_csp.sp_bb_data) &
+               FC_SP_BB_DATA_MASK;
+
+       if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) {
+               FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
+                            "lport->mfs:%hu\n", mfs, lport->mfs);
+               fc_lport_error(lport, fp);
+               goto err;
+       }
+
+       if (mfs <= lport->mfs) {
+               lport->mfs = mfs;
+               fc_host_maxframe_size(lport->host) = mfs;
+       }
+
+       csp_flags = ntohs(flp->fl_csp.sp_features);
+       r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
+       e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
+       if (csp_flags & FC_SP_FT_EDTR)
+               e_d_tov /= 1000000;
+
+       lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
+
+       if ((csp_flags & FC_SP_FT_FPORT) == 0) {
+               if (e_d_tov > lport->e_d_tov)
+                       lport->e_d_tov = e_d_tov;
+               lport->r_a_tov = 2 * e_d_tov;
+               fc_lport_set_port_id(lport, did, fp);
+               printk(KERN_INFO "host%d: libfc: "
+                      "Port (%6.6x) entered "
+                      "point-to-point mode\n",
+                      lport->host->host_no, did);
+               fc_lport_ptp_setup(lport, fc_frame_sid(fp),
+                                  get_unaligned_be64(
+                                          &flp->fl_wwpn),
+                                  get_unaligned_be64(
+                                          &flp->fl_wwnn));
        } else {
-               FC_LPORT_DBG(lport, "Bad FLOGI response\n");
+               lport->e_d_tov = e_d_tov;
+               lport->r_a_tov = r_a_tov;
+               fc_host_fabric_name(lport->host) =
+                       get_unaligned_be64(&flp->fl_wwnn);
+               fc_lport_set_port_id(lport, did, fp);
+               fc_lport_enter_dns(lport);
        }
 
 out:
@@ -1527,7 +1800,7 @@ EXPORT_SYMBOL(fc_lport_flogi_resp);
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
  */
-void fc_lport_enter_flogi(struct fc_lport *lport)
+static void fc_lport_enter_flogi(struct fc_lport *lport)
 {
        struct fc_frame *fp;
 
@@ -1536,6 +1809,12 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
 
        fc_lport_state_enter(lport, LPORT_ST_FLOGI);
 
+       if (lport->point_to_multipoint) {
+               if (lport->port_id)
+                       fc_lport_enter_ready(lport);
+               return;
+       }
+
        fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
        if (!fp)
                return fc_lport_error(lport, fp);
@@ -1561,6 +1840,7 @@ int fc_lport_config(struct fc_lport *lport)
 
        fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
        fc_lport_add_fc4_type(lport, FC_TYPE_CT);
+       fc_fc4_conf_lport_params(lport, FC_TYPE_FCP);
 
        return 0;
 }
@@ -1598,6 +1878,7 @@ int fc_lport_init(struct fc_lport *lport)
                fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
        if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
                fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+       fc_fc4_add_lport(lport);
 
        return 0;
 }
@@ -1648,7 +1929,7 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
 
        job->reply->reply_payload_rcv_len +=
                fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
-                                        &info->offset, KM_BIO_SRC_IRQ, NULL);
+                                        &info->offset, NULL);
 
        if (fr_eof(fp) == FC_EOF_T &&
            (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
@@ -1701,8 +1982,7 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
        hton24(fh->fh_d_id, did);
        hton24(fh->fh_s_id, lport->port_id);
        fh->fh_type = FC_TYPE_ELS;
-       hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
-              FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+       hton24(fh->fh_f_ctl, FC_FCTL_REQ);
        fh->fh_cs_ctl = 0;
        fh->fh_df_ctl = 0;
        fh->fh_parm_offset = 0;
@@ -1720,8 +2000,10 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
        info->sg = job->reply_payload.sg_list;
 
        if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
-                                    NULL, info, tov))
+                                    NULL, info, tov)) {
+               kfree(info);
                return -ECOMM;
+       }
        return 0;
 }
 
@@ -1761,8 +2043,7 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
        hton24(fh->fh_d_id, did);
        hton24(fh->fh_s_id, lport->port_id);
        fh->fh_type = FC_TYPE_CT;
-       hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
-              FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+       hton24(fh->fh_f_ctl, FC_FCTL_REQ);
        fh->fh_cs_ctl = 0;
        fh->fh_df_ctl = 0;
        fh->fh_parm_offset = 0;
@@ -1780,8 +2061,10 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
        info->sg = job->reply_payload.sg_list;
 
        if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
-                                    NULL, info, tov))
+                                    NULL, info, tov)) {
+               kfree(info);
                return -ECOMM;
+       }
        return 0;
 }