aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc/fc_disc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libfc/fc_disc.c')
-rw-r--r--drivers/scsi/libfc/fc_disc.c523
1 files changed, 201 insertions, 322 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 6fabf66972b9..c48799e9dd8e 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -43,47 +43,14 @@
#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 *,
- 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 int fc_disc_single(struct fc_lport *, 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,70 +60,17 @@ 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
* @fp: RSCN Frame
@@ -169,8 +83,6 @@ 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_els_rscn *rp;
struct fc_els_rscn_page *pp;
struct fc_seq_els_data rjt_data;
@@ -224,10 +136,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
break;
}
dp->lp = lport;
- dp->ids.port_id = ntoh24(pp->rscn_fid);
- dp->ids.port_name = -1;
- dp->ids.node_name = -1;
- dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ dp->port_id = ntoh24(pp->rscn_fid);
list_add_tail(&dp->peers, &disc_ports);
break;
case ELS_ADDR_FMT_AREA:
@@ -240,6 +149,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
}
}
lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+
+ /*
+ * If not doing a complete rediscovery, do GPN_ID on
+ * the individual ports mentioned in the list.
+ * If any of these get an error, do a full rediscovery.
+ * In any case, go through the list and free the entries.
+ */
+ list_for_each_entry_safe(dp, next, &disc_ports, peers) {
+ list_del(&dp->peers);
+ if (!redisc)
+ redisc = fc_disc_single(lport, dp);
+ kfree(dp);
+ }
if (redisc) {
FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
fc_disc_restart(disc);
@@ -247,16 +169,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
"redisc %d state %d in_prog %d\n",
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);
- }
- fc_disc_single(disc, dp);
- }
}
fc_frame_free(fp);
return;
@@ -308,35 +220,34 @@ 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;
+ if (!disc->disc_callback)
+ return;
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;
+ disc->retry_count = 0;
+ fc_disc_gpn_ft_req(disc);
}
/**
* fc_disc_start() - Fibre Channel Target discovery
* @lport: FC local port
- *
- * Returns non-zero if discovery cannot be started.
+ * @disc_callback: function to be called when discovery is complete
*/
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_disc *disc = &lport->disc;
/*
@@ -345,145 +256,47 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
* and send the GPN_FT request.
*/
mutex_lock(&disc->disc_mutex);
-
disc->disc_callback = disc_callback;
-
- /*
- * If not ready, or already running discovery, just set request flag.
- */
- disc->requested = 1;
-
- if (disc->pending) {
- mutex_unlock(&disc->disc_mutex);
- return;
- }
-
- /*
- * 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);
- }
- put_device(&rport->dev);
- } else {
- fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
- }
-
+ fc_disc_restart(disc);
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)
- * @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_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) {
- /*
- * 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) {
- /*
- * This is a new port with the same FCID as
- * a previously-discovered port. Presumably the old
- * port logged out and a new port logged in and was
- * 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;
- }
- }
- 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)
- 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);
- }
- }
- 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 +335,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 +365,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 +375,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 +390,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.
@@ -591,6 +403,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
plen = len;
np = (struct fc_gpn_ft_resp *)bp;
tlen = disc->buf_len;
+ disc->buf_len = 0;
if (tlen) {
WARN_ON(tlen >= sizeof(*np));
plen = sizeof(*np) - tlen;
@@ -621,31 +434,25 @@ 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);
+
+ if (ids.port_id != fc_host_port_id(lport->host) &&
+ ids.port_name != lport->wwpn) {
+ rdata = lport->tt.rport_create(lport, ids.port_id);
+ if (rdata) {
+ rdata->ids.port_name = ids.port_name;
+ 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);
+ error = -ENOMEM;
+ }
}
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;
}
@@ -665,8 +472,6 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
memcpy(&disc->partial_buf, np, len);
}
disc->buf_len = (unsigned char) len;
- } else {
- disc->buf_len = 0;
}
return error;
}
@@ -683,8 +488,7 @@ static void fc_disc_timeout(struct work_struct *work)
struct fc_disc,
disc_work.work);
mutex_lock(&disc->disc_mutex);
- if (disc->requested && !disc->pending)
- fc_disc_gpn_ft_req(disc);
+ fc_disc_gpn_ft_req(disc);
mutex_unlock(&disc->disc_mutex);
}
@@ -703,10 +507,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,77 +525,158 @@ 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",
fr_len(fp));
+ event = DISC_EV_FAILED;
} 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;
+ if (cp->ct_reason == FC_FS_RJT_UNABL &&
+ cp->ct_explan == FC_FS_EXP_FTNR)
+ event = DISC_EV_SUCCESS;
} else {
FC_DISC_DBG(disc, "GPN_FT unexpected response code "
"%x\n", ntohs(cp->ct_cmd));
+ event = DISC_EV_FAILED;
}
- } 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));
+ event = DISC_EV_FAILED;
}
- 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);
}
/**
- * fc_disc_single() - Discover the directory information for a single target
- * @lport: FC local port
- * @dp: The port to rediscover
+ * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
+ * @sp: exchange sequence
+ * @fp: response frame
+ * @rdata_arg: remote port private data
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * Locking Note: This function is called without disc mutex held.
*/
-static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
+static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_rport_priv *new_rdata;
struct fc_lport *lport;
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *rdata;
+ struct fc_disc *disc;
+ struct fc_ct_hdr *cp;
+ struct fc_ns_gid_pn *pn;
+ u64 port_name;
- lport = disc->lport;
+ lport = rdata->local_port;
+ disc = &lport->disc;
- if (dp->ids.port_id == fc_host_port_id(lport->host))
+ mutex_lock(&disc->disc_mutex);
+ if (PTR_ERR(fp) == -FC_EX_CLOSED)
goto out;
-
- new_rport = lport->tt.rport_create(dp);
- if (new_rport) {
- rdata = new_rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- kfree(dp);
- list_add_tail(&rdata->peers, &disc->rogue_rports);
- lport->tt.rport_login(new_rport);
+ if (IS_ERR(fp))
+ goto redisc;
+
+ cp = fc_frame_payload_get(fp, sizeof(*cp));
+ if (!cp)
+ goto redisc;
+ if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
+ if (fr_len(fp) < sizeof(struct fc_frame_header) +
+ sizeof(*cp) + sizeof(*pn))
+ goto redisc;
+ pn = (struct fc_ns_gid_pn *)(cp + 1);
+ port_name = get_unaligned_be64(&pn->fn_wwpn);
+ if (rdata->ids.port_name == -1)
+ rdata->ids.port_name = port_name;
+ else if (rdata->ids.port_name != port_name) {
+ FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
+ "Port-id %x wwpn %llx\n",
+ rdata->ids.port_id, port_name);
+ lport->tt.rport_logoff(rdata);
+
+ new_rdata = lport->tt.rport_create(lport,
+ rdata->ids.port_id);
+ if (new_rdata) {
+ new_rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(new_rdata);
+ }
+ goto out;
+ }
+ rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(rdata);
+ } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
+ FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
+ cp->ct_reason, cp->ct_explan);
+ lport->tt.rport_logoff(rdata);
+ } else {
+ FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
+ ntohs(cp->ct_cmd));
+redisc:
+ fc_disc_restart(disc);
}
- return;
out:
- kfree(dp);
+ mutex_unlock(&disc->disc_mutex);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+}
+
+/**
+ * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
+ * @lport: local port
+ * @rdata: remote port private data
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ * On failure, an error code is returned.
+ */
+static int fc_disc_gpn_id_req(struct fc_lport *lport,
+ struct fc_rport_priv *rdata)
+{
+ struct fc_frame *fp;
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_fid));
+ if (!fp)
+ return -ENOMEM;
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
+ fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+ return -ENOMEM;
+ kref_get(&rdata->kref);
+ return 0;
+}
+
+/**
+ * fc_disc_single() - Discover the directory information for a single target
+ * @lport: local port
+ * @dp: The port to rediscover
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
+{
+ struct fc_rport_priv *rdata;
+
+ rdata = lport->tt.rport_create(lport, dp->port_id);
+ if (!rdata)
+ return -ENOMEM;
+ rdata->disc_id = 0;
+ return fc_disc_gpn_id_req(lport, rdata);
}
/**
@@ -841,18 +726,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;
}