aboutsummaryrefslogtreecommitdiff
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.c92
1 files changed, 53 insertions, 39 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 9b0a5192a96..880a9068ca1 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -33,7 +33,9 @@
*/
#include <linux/timer.h>
+#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <asm/unaligned.h>
#include <scsi/fc/fc_gs.h>
@@ -59,30 +61,28 @@ static void fc_disc_restart(struct fc_disc *);
* Locking Note: This function expects that the lport mutex is locked before
* calling it.
*/
-void fc_disc_stop_rports(struct fc_disc *disc)
+static void fc_disc_stop_rports(struct fc_disc *disc)
{
struct fc_lport *lport;
- struct fc_rport_priv *rdata, *next;
+ struct fc_rport_priv *rdata;
- lport = disc->lport;
+ lport = fc_disc_lport(disc);
mutex_lock(&disc->disc_mutex);
- list_for_each_entry_safe(rdata, next, &disc->rports, peers)
+ list_for_each_entry_rcu(rdata, &disc->rports, peers)
lport->tt.rport_logoff(rdata);
mutex_unlock(&disc->disc_mutex);
}
/**
* fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
- * @sp: The sequence of the RSCN exchange
+ * @disc: The discovery object to which the RSCN applies
* @fp: The RSCN frame
- * @lport: The local port that the request will be sent on
*
* Locking Note: This function expects that the disc_mutex is locked
* before it is called.
*/
-static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
- struct fc_disc *disc)
+static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
{
struct fc_lport *lport;
struct fc_els_rscn *rp;
@@ -95,7 +95,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
LIST_HEAD(disc_ports);
struct fc_disc_port *dp, *next;
- lport = disc->lport;
+ lport = fc_disc_lport(disc);
FC_DISC_DBG(disc, "Received an RSCN event\n");
@@ -131,7 +131,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
switch (fmt) {
case ELS_ADDR_FMT_PORT:
FC_DISC_DBG(disc, "Port address format for port "
- "(%6x)\n", ntoh24(pp->rscn_fid));
+ "(%6.6x)\n", ntoh24(pp->rscn_fid));
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (!dp) {
redisc = 1;
@@ -150,7 +150,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
break;
}
}
- lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
/*
* If not doing a complete rediscovery, do GPN_ID on
@@ -176,25 +176,22 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
return;
reject:
FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
- 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(fp, ELS_LS_RJT, &rjt_data);
fc_frame_free(fp);
}
/**
* fc_disc_recv_req() - Handle incoming requests
- * @sp: The sequence of the request exchange
- * @fp: The request frame
* @lport: The local port receiving the request
+ * @fp: The request frame
*
* Locking Note: This function is called from the EM and will lock
* the disc_mutex before calling the handler for the
* request.
*/
-static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
- struct fc_lport *lport)
+static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
{
u8 op;
struct fc_disc *disc = &lport->disc;
@@ -203,12 +200,13 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
switch (op) {
case ELS_RSCN:
mutex_lock(&disc->disc_mutex);
- fc_disc_recv_rscn_req(sp, fp, disc);
+ fc_disc_recv_rscn_req(disc, fp);
mutex_unlock(&disc->disc_mutex);
break;
default:
FC_DISC_DBG(disc, "Received an unsupported request, "
"the opcode is (%x)\n", op);
+ fc_frame_free(fp);
break;
}
}
@@ -274,7 +272,7 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
*/
static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
{
- struct fc_lport *lport = disc->lport;
+ struct fc_lport *lport = fc_disc_lport(disc);
struct fc_rport_priv *rdata;
FC_DISC_DBG(disc, "Discovery complete\n");
@@ -291,7 +289,7 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
* 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) {
+ list_for_each_entry_rcu(rdata, &disc->rports, peers) {
if (!rdata->disc_id)
continue;
if (rdata->disc_id == disc->disc_id)
@@ -312,7 +310,7 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
*/
static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
{
- struct fc_lport *lport = disc->lport;
+ struct fc_lport *lport = fc_disc_lport(disc);
unsigned long delay = 0;
FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
@@ -339,6 +337,13 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
schedule_delayed_work(&disc->disc_work, delay);
} else
fc_disc_done(disc, DISC_EV_FAILED);
+ } else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
+ /*
+ * if discovery fails due to lport reset, clear
+ * pending flag so that subsequent discovery can
+ * continue
+ */
+ disc->pending = 0;
}
}
@@ -352,7 +357,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
static void fc_disc_gpn_ft_req(struct fc_disc *disc)
{
struct fc_frame *fp;
- struct fc_lport *lport = disc->lport;
+ struct fc_lport *lport = fc_disc_lport(disc);
WARN_ON(!fc_lport_test_ready(lport));
@@ -395,7 +400,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
struct fc_rport_identifiers ids;
struct fc_rport_priv *rdata;
- lport = disc->lport;
+ lport = fc_disc_lport(disc);
disc->seq_count++;
/*
@@ -439,7 +444,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
ids.port_id = ntoh24(np->fp_fid);
ids.port_name = ntohll(np->fp_wwpn);
- if (ids.port_id != fc_host_port_id(lport->host) &&
+ if (ids.port_id != lport->port_id &&
ids.port_name != lport->wwpn) {
rdata = lport->tt.rport_create(lport, ids.port_id);
if (rdata) {
@@ -448,7 +453,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
} else {
printk(KERN_WARNING "libfc: Failed to allocate "
"memory for the newly discovered port "
- "(%6x)\n", ids.port_id);
+ "(%6.6x)\n", ids.port_id);
error = -ENOMEM;
}
}
@@ -606,7 +611,7 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
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",
+ "Port-id %6.6x wwpn %16.16llx\n",
rdata->ids.port_id, port_name);
lport->tt.rport_logoff(rdata);
@@ -684,14 +689,13 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
* fc_disc_stop() - Stop discovery for a given lport
* @lport: The local port that discovery should stop on
*/
-void fc_disc_stop(struct fc_lport *lport)
+static void fc_disc_stop(struct fc_lport *lport)
{
struct fc_disc *disc = &lport->disc;
- if (disc) {
+ if (disc->pending)
cancel_delayed_work_sync(&disc->disc_work);
- fc_disc_stop_rports(disc);
- }
+ fc_disc_stop_rports(disc);
}
/**
@@ -701,19 +705,20 @@ void fc_disc_stop(struct fc_lport *lport)
* This function will block until discovery has been
* completely stopped and all rports have been deleted.
*/
-void fc_disc_stop_final(struct fc_lport *lport)
+static void fc_disc_stop_final(struct fc_lport *lport)
{
fc_disc_stop(lport);
lport->tt.rport_flush_queue();
}
/**
- * fc_disc_init() - Initialize the discovery layer for a local port
- * @lport: The local port that needs the discovery layer to be initialized
+ * fc_disc_config() - Configure the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be configured
+ * @priv: Private data structre for users of the discovery layer
*/
-int fc_disc_init(struct fc_lport *lport)
+void fc_disc_config(struct fc_lport *lport, void *priv)
{
- struct fc_disc *disc;
+ struct fc_disc *disc = &lport->disc;
if (!lport->tt.disc_start)
lport->tt.disc_start = fc_disc_start;
@@ -728,12 +733,21 @@ int fc_disc_init(struct fc_lport *lport)
lport->tt.disc_recv_req = fc_disc_recv_req;
disc = &lport->disc;
+
+ disc->priv = priv;
+}
+EXPORT_SYMBOL(fc_disc_config);
+
+/**
+ * fc_disc_init() - Initialize the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be initialized
+ */
+void fc_disc_init(struct fc_lport *lport)
+{
+ struct fc_disc *disc = &lport->disc;
+
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
-
- disc->lport = lport;
-
- return 0;
}
EXPORT_SYMBOL(fc_disc_init);