[SCSI] libfc: rearrange code in fc_disc_gpn_ft_resp()
[deliverable/linux.git] / drivers / scsi / libfc / fc_disc.c
index 6fabf66972b92c2a529c407ecb1ffda350e491b1..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 *,
+static int fc_disc_new_target(struct fc_disc *, struct fc_rport_priv *,
                              struct fc_rport_identifiers *);
-static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
-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 *fc_disc_lookup_rport(const struct fc_lport *lport,
-                                     u32 port_id)
-{
-       const struct fc_disc *disc = &lport->disc;
-       struct fc_rport *rport, *found = NULL;
-       struct fc_rport_libfc_priv *rdata;
-       int disc_found = 0;
-
-       list_for_each_entry(rdata, &disc->rports, peers) {
-               rport = PRIV_TO_RPORT(rdata);
-               if (rport->port_id == port_id) {
-                       disc_found = 1;
-                       found = rport;
-                       break;
-               }
-       }
-
-       if (!disc_found)
-               found = NULL;
-
-       return found;
-}
-
 /**
  * fc_disc_stop_rports() - delete all the remote ports associated with the lport
  * @disc: The discovery job to stop rports on
@@ -93,69 +62,16 @@ struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
 void fc_disc_stop_rports(struct fc_disc *disc)
 {
        struct fc_lport *lport;
-       struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rdata, *next;
+       struct fc_rport_priv *rdata, *next;
 
        lport = disc->lport;
 
        mutex_lock(&disc->disc_mutex);
-       list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
-               rport = PRIV_TO_RPORT(rdata);
-               list_del(&rdata->peers);
-               lport->tt.rport_logoff(rport);
-       }
-
-       list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
-               rport = PRIV_TO_RPORT(rdata);
-               lport->tt.rport_logoff(rport);
-       }
-
+       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
- * @rport: The rport which the event has occured on
- * @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 *rport,
-                                  enum fc_rport_event event)
-{
-       struct fc_rport_libfc_priv *rdata = rport->dd_data;
-       struct fc_disc *disc = &lport->disc;
-
-       FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event,
-                   rport->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
@@ -169,8 +85,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
                                  struct fc_disc *disc)
 {
        struct fc_lport *lport;
-       struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rdata;
+       struct fc_rport_priv *rdata;
        struct fc_els_rscn *rp;
        struct fc_els_rscn_page *pp;
        struct fc_seq_els_data rjt_data;
@@ -249,11 +164,9 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
                            redisc, lport->state, disc->pending);
                list_for_each_entry_safe(dp, next, &disc_ports, peers) {
                        list_del(&dp->peers);
-                       rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
-                       if (rport) {
-                               rdata = rport->dd_data;
-                               list_del(&rdata->peers);
-                               lport->tt.rport_logoff(rport);
+                       rdata = lport->tt.rport_lookup(lport, dp->ids.port_id);
+                       if (rdata) {
+                               lport->tt.rport_logoff(rdata);
                        }
                        fc_disc_single(disc, dp);
                }
@@ -308,21 +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 *rport;
-       struct fc_rport_libfc_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) {
-               rport = PRIV_TO_RPORT(rdata);
-               list_del(&rdata->peers);
-               lport->tt.rport_logoff(rport);
-       }
-
        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);
 }
 
 /**
@@ -335,8 +246,7 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
                                                enum fc_disc_event),
                          struct fc_lport *lport)
 {
-       struct fc_rport *rport;
-       struct fc_rport_identifiers ids;
+       struct fc_rport_priv *rdata;
        struct fc_disc *disc = &lport->disc;
 
        /*
@@ -362,55 +272,45 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
         * Handle point-to-point mode as a simple discovery
         * of the remote port. Yucky, yucky, yuck, yuck!
         */
-       rport = disc->lport->ptp_rp;
-       if (rport) {
-               ids.port_id = rport->port_id;
-               ids.port_name = rport->port_name;
-               ids.node_name = rport->node_name;
-               ids.roles = FC_RPORT_ROLE_UNKNOWN;
-               get_device(&rport->dev);
-
-               if (!fc_disc_new_target(disc, rport, &ids)) {
-                       disc->event = DISC_EV_SUCCESS;
-                       fc_disc_done(disc);
+       rdata = disc->lport->ptp_rp;
+       if (rdata) {
+               kref_get(&rdata->kref);
+               if (!fc_disc_new_target(disc, rdata, &rdata->ids)) {
+                       fc_disc_done(disc, DISC_EV_SUCCESS);
                }
-               put_device(&rport->dev);
+               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
- * @rport: The previous FC remote port (NULL if new remote port)
+ * @rdata: The previous FC remote port priv (NULL if new remote port)
  * @ids: Identifiers for the new FC remote port
  *
  * Locking Note: This function expects that the disc_mutex is locked
  *              before it is called.
  */
 static int fc_disc_new_target(struct fc_disc *disc,
-                             struct fc_rport *rport,
+                             struct fc_rport_priv *rdata,
                              struct fc_rport_identifiers *ids)
 {
        struct fc_lport *lport = disc->lport;
-       struct fc_rport_libfc_priv *rdata;
        int error = 0;
 
-       if (rport && ids->port_name) {
-               if (rport->port_name == -1) {
+       if (rdata && ids->port_name) {
+               if (rdata->ids.port_name == -1) {
                        /*
                         * Set WWN and fall through to notify of create.
                         */
-                       fc_rport_set_name(rport, ids->port_name,
-                                         rport->node_name);
-               } else if (rport->port_name != ids->port_name) {
+                       rdata->ids.port_name = ids->port_name;
+                       rdata->ids.node_name = ids->node_name;
+               } else if (rdata->ids.port_name != ids->port_name) {
                        /*
                         * This is a new port with the same FCID as
                         * a previously-discovered port.  Presumably the old
@@ -418,72 +318,60 @@ 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.
                         */
-                       fc_disc_del_target(disc, rport);
-                       rport = NULL;
+                       lport->tt.rport_logoff(rdata);
+                       rdata = NULL;
                }
        }
        if (((ids->port_name != -1) || (ids->port_id != -1)) &&
            ids->port_id != fc_host_port_id(lport->host) &&
            ids->port_name != lport->wwpn) {
-               if (!rport) {
-                       rport = lport->tt.rport_lookup(lport, ids->port_id);
-                       if (!rport) {
-                               struct fc_disc_port dp;
-                               dp.lp = lport;
-                               dp.ids.port_id = ids->port_id;
-                               dp.ids.port_name = ids->port_name;
-                               dp.ids.node_name = ids->node_name;
-                               dp.ids.roles = ids->roles;
-                               rport = lport->tt.rport_create(&dp);
-                       }
-                       if (!rport)
+               if (!rdata) {
+                       rdata = lport->tt.rport_create(lport, ids);
+                       if (!rdata)
                                error = -ENOMEM;
                }
-               if (rport) {
-                       rdata = rport->dd_data;
-                       rdata->ops = &fc_disc_rport_ops;
-                       rdata->rp_state = RPORT_ST_INIT;
-                       list_add_tail(&rdata->peers, &disc->rogue_rports);
-                       lport->tt.rport_login(rport);
-               }
+               if (rdata)
+                       lport->tt.rport_login(rdata);
        }
        return error;
 }
 
-/**
- * fc_disc_del_target() - Delete a target
- * @disc: FC discovery context
- * @rport: The remote port to be removed
- */
-static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
-{
-       struct fc_lport *lport = disc->lport;
-       struct fc_rport_libfc_priv *rdata = rport->dd_data;
-       list_del(&rdata->peers);
-       lport->tt.rport_logoff(rport);
-}
-
 /**
  * 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);
@@ -522,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);
        }
 }
 
@@ -555,7 +440,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
        if (!fp)
                goto err;
 
-       if (lport->tt.elsct_send(lport, NULL, fp,
+       if (lport->tt.elsct_send(lport, 0, fp,
                                 FC_NS_GPN_FT,
                                 fc_disc_gpn_ft_resp,
                                 disc, lport->e_d_tov))
@@ -565,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)
 {
@@ -578,11 +465,11 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
        size_t plen;
        size_t tlen;
        int error = 0;
-       struct fc_disc_port dp;
-       struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rdata;
+       struct fc_rport_identifiers ids;
+       struct fc_rport_priv *rdata;
 
        lport = disc->lport;
+       disc->seq_count++;
 
        /*
         * Handle partial name record left over from previous call.
@@ -621,31 +508,24 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
         * After the first time through the loop, things return to "normal".
         */
        while (plen >= sizeof(*np)) {
-               dp.lp = lport;
-               dp.ids.port_id = ntoh24(np->fp_fid);
-               dp.ids.port_name = ntohll(np->fp_wwpn);
-               dp.ids.node_name = -1;
-               dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
-               if ((dp.ids.port_id != fc_host_port_id(lport->host)) &&
-                   (dp.ids.port_name != lport->wwpn)) {
-                       rport = lport->tt.rport_create(&dp);
-                       if (rport) {
-                               rdata = rport->dd_data;
-                               rdata->ops = &fc_disc_rport_ops;
-                               rdata->local_port = lport;
-                               list_add_tail(&rdata->peers,
-                                             &disc->rogue_rports);
-                               lport->tt.rport_login(rport);
-                       } else
+               ids.port_id = ntoh24(np->fp_fid);
+               ids.port_name = ntohll(np->fp_wwpn);
+               ids.node_name = -1;
+               ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
+               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->disc_id = disc->disc_id;
+                       else
                                printk(KERN_WARNING "libfc: Failed to allocate "
                                       "memory for the newly discovered port "
-                                      "(%6x)\n", dp.ids.port_id);
+                                      "(%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;
                }
@@ -703,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");
@@ -721,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",
@@ -730,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);
 }
 
@@ -773,21 +646,18 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
 {
        struct fc_lport *lport;
-       struct fc_rport *new_rport;
-       struct fc_rport_libfc_priv *rdata;
+       struct fc_rport_priv *rdata;
 
        lport = disc->lport;
 
        if (dp->ids.port_id == fc_host_port_id(lport->host))
                goto out;
 
-       new_rport = lport->tt.rport_create(dp);
-       if (new_rport) {
-               rdata = new_rport->dd_data;
-               rdata->ops = &fc_disc_rport_ops;
+       rdata = lport->tt.rport_create(lport, &dp->ids);
+       if (rdata) {
+               rdata->disc_id = disc->disc_id;
                kfree(dp);
-               list_add_tail(&rdata->peers, &disc->rogue_rports);
-               lport->tt.rport_login(new_rport);
+               lport->tt.rport_login(rdata);
        }
        return;
 out:
@@ -841,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.030307 seconds and 5 git commands to generate.