[SCSI] libfc: rearrange code in fc_disc_gpn_ft_resp()
[deliverable/linux.git] / drivers / scsi / libfc / fc_disc.c
index 5f839b625e509696a060f124170bc1177146e921..819ec6256a53619a6d7cd4205fbcd77faa17301e 100644 (file)
 #define FC_DISC_RETRY_LIMIT    3       /* max retries */
 #define FC_DISC_RETRY_DELAY    500UL   /* (msecs) delay */
 
-#define        FC_DISC_DELAY           3
-
 static void fc_disc_gpn_ft_req(struct fc_disc *);
 static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
 static int fc_disc_new_target(struct fc_disc *, struct fc_rport_priv *,
                              struct fc_rport_identifiers *);
-static void fc_disc_done(struct fc_disc *);
+static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
 static void fc_disc_timeout(struct work_struct *);
 static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
 static void fc_disc_restart(struct fc_disc *);
 
-/**
- * fc_disc_lookup_rport() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
- */
-struct fc_rport_priv *fc_disc_lookup_rport(const struct fc_lport *lport,
-                                          u32 port_id)
-{
-       const struct fc_disc *disc = &lport->disc;
-       struct fc_rport_priv *rdata;
-
-       list_for_each_entry(rdata, &disc->rports, peers) {
-               if (rdata->ids.port_id == port_id)
-                       return rdata;
-       }
-       return NULL;
-}
-
 /**
  * fc_disc_stop_rports() - delete all the remote ports associated with the lport
  * @disc: The discovery job to stop rports on
@@ -87,60 +67,11 @@ void fc_disc_stop_rports(struct fc_disc *disc)
        lport = disc->lport;
 
        mutex_lock(&disc->disc_mutex);
-       list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
-               list_del(&rdata->peers);
-               lport->tt.rport_logoff(rdata);
-       }
-
-       list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
+       list_for_each_entry_safe(rdata, next, &disc->rports, peers)
                lport->tt.rport_logoff(rdata);
-       }
-
        mutex_unlock(&disc->disc_mutex);
 }
 
-/**
- * fc_disc_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
- *
- * Locking Note: The rport lock should not be held when calling
- *              this function.
- */
-static void fc_disc_rport_callback(struct fc_lport *lport,
-                                  struct fc_rport_priv *rdata,
-                                  enum fc_rport_event event)
-{
-       struct fc_disc *disc = &lport->disc;
-
-       FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event,
-                   rdata->ids.port_id);
-
-       switch (event) {
-       case RPORT_EV_CREATED:
-               if (disc) {
-                       mutex_lock(&disc->disc_mutex);
-                       list_add_tail(&rdata->peers, &disc->rports);
-                       mutex_unlock(&disc->disc_mutex);
-               }
-               break;
-       case RPORT_EV_LOGO:
-       case RPORT_EV_FAILED:
-       case RPORT_EV_STOP:
-               mutex_lock(&disc->disc_mutex);
-               mutex_lock(&rdata->rp_mutex);
-               if (rdata->trans_state == FC_PORTSTATE_ROGUE)
-                       list_del(&rdata->peers);
-               mutex_unlock(&rdata->rp_mutex);
-               mutex_unlock(&disc->disc_mutex);
-               break;
-       default:
-               break;
-       }
-
-}
-
 /**
  * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
  * @sp: Current sequence of the RSCN exchange
@@ -235,7 +166,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
                        list_del(&dp->peers);
                        rdata = lport->tt.rport_lookup(lport, dp->ids.port_id);
                        if (rdata) {
-                               list_del(&rdata->peers);
                                lport->tt.rport_logoff(rdata);
                        }
                        fc_disc_single(disc, dp);
@@ -291,19 +221,19 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
  */
 static void fc_disc_restart(struct fc_disc *disc)
 {
-       struct fc_rport_priv *rdata, *next;
-       struct fc_lport *lport = disc->lport;
-
        FC_DISC_DBG(disc, "Restarting discovery\n");
 
-       list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
-               list_del(&rdata->peers);
-               lport->tt.rport_logoff(rdata);
-       }
-
        disc->requested = 1;
-       if (!disc->pending)
-               fc_disc_gpn_ft_req(disc);
+       if (disc->pending)
+               return;
+
+       /*
+        * Advance disc_id.  This is an arbitrary non-zero number that will
+        * match the value in the fc_rport_priv after discovery for all
+        * freshly-discovered remote ports.  Avoid wrapping to zero.
+        */
+       disc->disc_id = (disc->disc_id + 2) | 1;
+       fc_disc_gpn_ft_req(disc);
 }
 
 /**
@@ -346,21 +276,17 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
        if (rdata) {
                kref_get(&rdata->kref);
                if (!fc_disc_new_target(disc, rdata, &rdata->ids)) {
-                       disc->event = DISC_EV_SUCCESS;
-                       fc_disc_done(disc);
+                       fc_disc_done(disc, DISC_EV_SUCCESS);
                }
                kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
        } else {
+               disc->disc_id = (disc->disc_id + 2) | 1;
                fc_disc_gpn_ft_req(disc);       /* get ports by FC-4 type */
        }
 
        mutex_unlock(&disc->disc_mutex);
 }
 
-static struct fc_rport_operations fc_disc_rport_ops = {
-       .event_callback = fc_disc_rport_callback,
-};
-
 /**
  * fc_disc_new_target() - Handle new target found by discovery
  * @lport: FC local port
@@ -392,7 +318,6 @@ static int fc_disc_new_target(struct fc_disc *disc,
                         * assigned the same FCID.  This should be rare.
                         * Delete the old one and fall thru to re-create.
                         */
-                       list_del(&rdata->peers);
                        lport->tt.rport_logoff(rdata);
                        rdata = NULL;
                }
@@ -401,19 +326,12 @@ static int fc_disc_new_target(struct fc_disc *disc,
            ids->port_id != fc_host_port_id(lport->host) &&
            ids->port_name != lport->wwpn) {
                if (!rdata) {
-                       rdata = lport->tt.rport_lookup(lport, ids->port_id);
-                       if (!rdata) {
-                               rdata = lport->tt.rport_create(lport, ids);
-                               if (!rdata)
-                                       error = -ENOMEM;
-                       }
+                       rdata = lport->tt.rport_create(lport, ids);
+                       if (!rdata)
+                               error = -ENOMEM;
                }
-               if (rdata) {
-                       rdata->ops = &fc_disc_rport_ops;
-                       rdata->rp_state = RPORT_ST_INIT;
-                       list_add_tail(&rdata->peers, &disc->rogue_rports);
+               if (rdata)
                        lport->tt.rport_login(rdata);
-               }
        }
        return error;
 }
@@ -421,24 +339,39 @@ static int fc_disc_new_target(struct fc_disc *disc,
 /**
  * fc_disc_done() - Discovery has been completed
  * @disc: FC discovery context
+ * @event: discovery completion status
+ *
  * Locking Note: This function expects that the disc mutex is locked before
  * it is called. The discovery callback is then made with the lock released,
  * and the lock is re-taken before returning from this function
  */
-static void fc_disc_done(struct fc_disc *disc)
+static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 {
        struct fc_lport *lport = disc->lport;
-       enum fc_disc_event event;
+       struct fc_rport_priv *rdata;
 
        FC_DISC_DBG(disc, "Discovery complete\n");
 
-       event = disc->event;
-       disc->event = DISC_EV_NONE;
+       disc->pending = 0;
+       if (disc->requested) {
+               fc_disc_restart(disc);
+               return;
+       }
 
-       if (disc->requested)
-               fc_disc_gpn_ft_req(disc);
-       else
-               disc->pending = 0;
+       /*
+        * Go through all remote ports.  If they were found in the latest
+        * discovery, reverify or log them in.  Otherwise, log them out.
+        * Skip ports which were never discovered.  These are the dNS port
+        * and ports which were created by PLOGI.
+        */
+       list_for_each_entry(rdata, &disc->rports, peers) {
+               if (!rdata->disc_id)
+                       continue;
+               if (rdata->disc_id == disc->disc_id)
+                       lport->tt.rport_login(rdata);
+               else
+                       lport->tt.rport_logoff(rdata);
+       }
 
        mutex_unlock(&disc->disc_mutex);
        disc->disc_callback(lport, event);
@@ -477,11 +410,8 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
                        }
                        disc->retry_count++;
                        schedule_delayed_work(&disc->disc_work, delay);
-               } else {
-                       /* exceeded retries */
-                       disc->event = DISC_EV_FAILED;
-                       fc_disc_done(disc);
-               }
+               } else
+                       fc_disc_done(disc, DISC_EV_FAILED);
        }
 }
 
@@ -520,10 +450,12 @@ err:
 }
 
 /**
- * fc_disc_gpn_ft_parse() - Parse the list of IDs and names resulting from a request
+ * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
  * @lport: Fibre Channel host port instance
  * @buf: GPN_FT response buffer
  * @len: size of response buffer
+ *
+ * Goes through the list of IDs and names resulting from a request.
  */
 static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 {
@@ -537,6 +469,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
        struct fc_rport_priv *rdata;
 
        lport = disc->lport;
+       disc->seq_count++;
 
        /*
         * Handle partial name record left over from previous call.
@@ -583,21 +516,16 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
                if (ids.port_id != fc_host_port_id(lport->host) &&
                    ids.port_name != lport->wwpn) {
                        rdata = lport->tt.rport_create(lport, &ids);
-                       if (rdata) {
-                               rdata->ops = &fc_disc_rport_ops;
-                               rdata->local_port = lport;
-                               list_add_tail(&rdata->peers,
-                                             &disc->rogue_rports);
-                               lport->tt.rport_login(rdata);
-                       } else
+                       if (rdata)
+                               rdata->disc_id = disc->disc_id;
+                       else
                                printk(KERN_WARNING "libfc: Failed to allocate "
                                       "memory for the newly discovered port "
                                       "(%6x)\n", ids.port_id);
                }
 
                if (np->fp_flags & FC_NS_FID_LAST) {
-                       disc->event = DISC_EV_SUCCESS;
-                       fc_disc_done(disc);
+                       fc_disc_done(disc, DISC_EV_SUCCESS);
                        len = 0;
                        break;
                }
@@ -655,10 +583,10 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
        struct fc_disc *disc = disc_arg;
        struct fc_ct_hdr *cp;
        struct fc_frame_header *fh;
+       enum fc_disc_event event = DISC_EV_NONE;
        unsigned int seq_cnt;
-       void *buf = NULL;
        unsigned int len;
-       int error;
+       int error = 0;
 
        mutex_lock(&disc->disc_mutex);
        FC_DISC_DBG(disc, "Received a GPN_FT response\n");
@@ -673,8 +601,7 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
        fh = fc_frame_header_get(fp);
        len = fr_len(fp) - sizeof(*fh);
        seq_cnt = ntohs(fh->fh_seq_cnt);
-       if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
-           disc->seq_count == 0) {
+       if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
                cp = fc_frame_payload_get(fp, sizeof(*cp));
                if (!cp) {
                        FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
@@ -682,35 +609,29 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
                } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
 
                        /* Accepted, parse the response. */
-                       buf = cp + 1;
                        len -= sizeof(*cp);
+                       error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
                } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
                        FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
                                    "(check zoning)\n", cp->ct_reason,
                                    cp->ct_explan);
-                       disc->event = DISC_EV_FAILED;
-                       fc_disc_done(disc);
+                       event = DISC_EV_FAILED;
                } else {
                        FC_DISC_DBG(disc, "GPN_FT unexpected response code "
                                    "%x\n", ntohs(cp->ct_cmd));
                }
-       } else if (fr_sof(fp) == FC_SOF_N3 &&
-                  seq_cnt == disc->seq_count) {
-               buf = fh + 1;
+       } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
+               error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
        } else {
                FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
                            "seq_cnt %x expected %x sof %x eof %x\n",
                            seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
        }
-       if (buf) {
-               error = fc_disc_gpn_ft_parse(disc, buf, len);
-               if (error)
-                       fc_disc_error(disc, fp);
-               else
-                       disc->seq_count++;
-       }
+       if (error)
+               fc_disc_error(disc, fp);
+       else if (event != DISC_EV_NONE)
+               fc_disc_done(disc, event);
        fc_frame_free(fp);
-
        mutex_unlock(&disc->disc_mutex);
 }
 
@@ -734,9 +655,8 @@ static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
 
        rdata = lport->tt.rport_create(lport, &dp->ids);
        if (rdata) {
-               rdata->ops = &fc_disc_rport_ops;
+               rdata->disc_id = disc->disc_id;
                kfree(dp);
-               list_add_tail(&rdata->peers, &disc->rogue_rports);
                lport->tt.rport_login(rdata);
        }
        return;
@@ -791,18 +711,12 @@ int fc_disc_init(struct fc_lport *lport)
        if (!lport->tt.disc_recv_req)
                lport->tt.disc_recv_req = fc_disc_recv_req;
 
-       if (!lport->tt.rport_lookup)
-               lport->tt.rport_lookup = fc_disc_lookup_rport;
-
        disc = &lport->disc;
        INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
        mutex_init(&disc->disc_mutex);
        INIT_LIST_HEAD(&disc->rports);
-       INIT_LIST_HEAD(&disc->rogue_rports);
 
        disc->lport = lport;
-       disc->delay = FC_DISC_DELAY;
-       disc->event = DISC_EV_NONE;
 
        return 0;
 }
This page took 0.029317 seconds and 5 git commands to generate.