aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc/fc_rport.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libfc/fc_rport.c')
-rw-r--r--drivers/scsi/libfc/fc_rport.c1144
1 files changed, 696 insertions, 448 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 7162385f52eb..03ea6748e7ee 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -57,94 +57,114 @@
struct workqueue_struct *rport_event_queue;
-static void fc_rport_enter_plogi(struct fc_rport *);
-static void fc_rport_enter_prli(struct fc_rport *);
-static void fc_rport_enter_rtv(struct fc_rport *);
-static void fc_rport_enter_ready(struct fc_rport *);
-static void fc_rport_enter_logo(struct fc_rport *);
-
-static void fc_rport_recv_plogi_req(struct fc_rport *,
+static void fc_rport_enter_plogi(struct fc_rport_priv *);
+static void fc_rport_enter_prli(struct fc_rport_priv *);
+static void fc_rport_enter_rtv(struct fc_rport_priv *);
+static void fc_rport_enter_ready(struct fc_rport_priv *);
+static void fc_rport_enter_logo(struct fc_rport_priv *);
+static void fc_rport_enter_adisc(struct fc_rport_priv *);
+
+static void fc_rport_recv_plogi_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prli_req(struct fc_rport *,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prlo_req(struct fc_rport *,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_logo_req(struct fc_rport *,
+static void fc_rport_recv_logo_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *);
-static void fc_rport_error(struct fc_rport *, struct fc_frame *);
-static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *);
+static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = {
- [RPORT_ST_NONE] = "None",
[RPORT_ST_INIT] = "Init",
[RPORT_ST_PLOGI] = "PLOGI",
[RPORT_ST_PRLI] = "PRLI",
[RPORT_ST_RTV] = "RTV",
[RPORT_ST_READY] = "Ready",
[RPORT_ST_LOGO] = "LOGO",
+ [RPORT_ST_ADISC] = "ADISC",
+ [RPORT_ST_DELETE] = "Delete",
};
-static void fc_rport_rogue_destroy(struct device *dev)
+/**
+ * fc_rport_lookup() - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport = dev_to_rport(dev);
- FC_RPORT_DBG(rport, "Destroying rogue rport\n");
- kfree(rport);
+ struct fc_rport_priv *rdata;
+
+ list_for_each_entry(rdata, &lport->disc.rports, peers)
+ if (rdata->ids.port_id == port_id &&
+ rdata->rp_state != RPORT_ST_DELETE)
+ return rdata;
+ return NULL;
}
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
+/**
+ * fc_rport_create() - Create a new remote port
+ * @lport: The local port that the new remote port is for
+ * @port_id: The port ID for the new remote port
+ *
+ * Locking note: must be called with the disc_mutex held.
+ */
+static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
- rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL);
+ struct fc_rport_priv *rdata;
- if (!rport)
- return NULL;
+ rdata = lport->tt.rport_lookup(lport, port_id);
+ if (rdata)
+ return rdata;
- rdata = RPORT_TO_PRIV(rport);
+ rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return NULL;
- rport->dd_data = rdata;
- rport->port_id = dp->ids.port_id;
- rport->port_name = dp->ids.port_name;
- rport->node_name = dp->ids.node_name;
- rport->roles = dp->ids.roles;
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
- /*
- * Note: all this libfc rogue rport code will be removed for
- * upstream so it fine that this is really ugly and hacky right now.
- */
- device_initialize(&rport->dev);
- rport->dev.release = fc_rport_rogue_destroy;
+ rdata->ids.node_name = -1;
+ rdata->ids.port_name = -1;
+ rdata->ids.port_id = port_id;
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ kref_init(&rdata->kref);
mutex_init(&rdata->rp_mutex);
- rdata->local_port = dp->lp;
- rdata->trans_state = FC_PORTSTATE_ROGUE;
+ rdata->local_port = lport;
rdata->rp_state = RPORT_ST_INIT;
rdata->event = RPORT_EV_NONE;
rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- rdata->ops = NULL;
- rdata->e_d_tov = dp->lp->e_d_tov;
- rdata->r_a_tov = dp->lp->r_a_tov;
+ rdata->e_d_tov = lport->e_d_tov;
+ rdata->r_a_tov = lport->r_a_tov;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
INIT_WORK(&rdata->event_work, fc_rport_work);
- /*
- * For good measure, but not necessary as we should only
- * add REAL rport to the lport list.
- */
- INIT_LIST_HEAD(&rdata->peers);
+ if (port_id != FC_FID_DIR_SERV)
+ list_add(&rdata->peers, &lport->disc.rports);
+ return rdata;
+}
+
+/**
+ * fc_rport_destroy() - free a remote port after last reference is released.
+ * @kref: pointer to kref inside struct fc_rport_priv
+ */
+static void fc_rport_destroy(struct kref *kref)
+{
+ struct fc_rport_priv *rdata;
- return rport;
+ rdata = container_of(kref, struct fc_rport_priv, kref);
+ kfree(rdata);
}
/**
* fc_rport_state() - return a string for the state the rport is in
- * @rport: The rport whose state we want to get a string for
+ * @rdata: remote port private data
*/
-static const char *fc_rport_state(struct fc_rport *rport)
+static const char *fc_rport_state(struct fc_rport_priv *rdata)
{
const char *cp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
cp = fc_rport_state_names[rdata->rp_state];
if (!cp)
@@ -191,15 +211,14 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
/**
* fc_rport_state_enter() - Change the rport's state
- * @rport: The rport whose state should change
+ * @rdata: The rport whose state should change
* @new: The new state of the rport
*
* Locking Note: Called with the rport lock held
*/
-static void fc_rport_state_enter(struct fc_rport *rport,
+static void fc_rport_state_enter(struct fc_rport_priv *rdata,
enum fc_rport_state new)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
if (rdata->rp_state != new)
rdata->retries = 0;
rdata->rp_state = new;
@@ -208,147 +227,187 @@ static void fc_rport_state_enter(struct fc_rport *rport,
static void fc_rport_work(struct work_struct *work)
{
u32 port_id;
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, event_work);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, event_work);
+ struct fc_rport_libfc_priv *rp;
enum fc_rport_event event;
- enum fc_rport_trans_state trans_state;
struct fc_lport *lport = rdata->local_port;
struct fc_rport_operations *rport_ops;
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
rport_ops = rdata->ops;
+ rport = rdata->rport;
- if (event == RPORT_EV_CREATED) {
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *new_rdata;
- struct fc_rport_identifiers ids;
+ FC_RPORT_DBG(rdata, "work event %u\n", event);
- ids.port_id = rport->port_id;
- ids.roles = rport->roles;
- ids.port_name = rport->port_name;
- ids.node_name = rport->node_name;
+ switch (event) {
+ case RPORT_EV_READY:
+ ids = rdata->ids;
+ rdata->event = RPORT_EV_NONE;
+ kref_get(&rdata->kref);
+ mutex_unlock(&rdata->rp_mutex);
+ if (!rport)
+ rport = fc_remote_port_add(lport->host, 0, &ids);
+ if (!rport) {
+ FC_RPORT_DBG(rdata, "Failed to add the rport\n");
+ lport->tt.rport_logoff(rdata);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ return;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->rport)
+ FC_RPORT_DBG(rdata, "rport already allocated\n");
+ rdata->rport = rport;
+ rport->maxframe_size = rdata->maxframe_size;
+ rport->supported_classes = rdata->supported_classes;
+
+ rp = rport->dd_data;
+ rp->local_port = lport;
+ rp->rp_state = rdata->rp_state;
+ rp->flags = rdata->flags;
+ rp->e_d_tov = rdata->e_d_tov;
+ rp->r_a_tov = rdata->r_a_tov;
mutex_unlock(&rdata->rp_mutex);
- new_rport = fc_remote_port_add(lport->host, 0, &ids);
- if (new_rport) {
- /*
- * Switch from the rogue rport to the rport
- * returned by the FC class.
- */
- new_rport->maxframe_size = rport->maxframe_size;
-
- new_rdata = new_rport->dd_data;
- new_rdata->e_d_tov = rdata->e_d_tov;
- new_rdata->r_a_tov = rdata->r_a_tov;
- new_rdata->ops = rdata->ops;
- new_rdata->local_port = rdata->local_port;
- new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- new_rdata->trans_state = FC_PORTSTATE_REAL;
- mutex_init(&new_rdata->rp_mutex);
- INIT_DELAYED_WORK(&new_rdata->retry_work,
- fc_rport_timeout);
- INIT_LIST_HEAD(&new_rdata->peers);
- INIT_WORK(&new_rdata->event_work, fc_rport_work);
-
- fc_rport_state_enter(new_rport, RPORT_ST_READY);
- } else {
- printk(KERN_WARNING "libfc: Failed to allocate "
- " memory for rport (%6x)\n", ids.port_id);
- event = RPORT_EV_FAILED;
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
}
- if (rport->port_id != FC_FID_DIR_SERV)
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport,
- RPORT_EV_FAILED);
- put_device(&rport->dev);
- rport = new_rport;
- rdata = new_rport->dd_data;
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- } else if ((event == RPORT_EV_FAILED) ||
- (event == RPORT_EV_LOGO) ||
- (event == RPORT_EV_STOP)) {
- trans_state = rdata->trans_state;
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ case RPORT_EV_FAILED:
+ case RPORT_EV_LOGO:
+ case RPORT_EV_STOP:
+ port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- if (trans_state == FC_PORTSTATE_ROGUE)
- put_device(&rport->dev);
- else {
- port_id = rport->port_id;
+
+ if (port_id != FC_FID_DIR_SERV) {
+ mutex_lock(&lport->disc.disc_mutex);
+ list_del(&rdata->peers);
+ mutex_unlock(&lport->disc.disc_mutex);
+ }
+
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
+ }
+ cancel_delayed_work_sync(&rdata->retry_work);
+
+ /*
+ * Reset any outstanding exchanges before freeing rport.
+ */
+ lport->tt.exch_mgr_reset(lport, 0, port_id);
+ lport->tt.exch_mgr_reset(lport, port_id, 0);
+
+ if (rport) {
+ rp = rport->dd_data;
+ rp->rp_state = RPORT_ST_DELETE;
+ mutex_lock(&rdata->rp_mutex);
+ rdata->rport = NULL;
+ mutex_unlock(&rdata->rp_mutex);
fc_remote_port_delete(rport);
- lport->tt.exch_mgr_reset(lport, 0, port_id);
- lport->tt.exch_mgr_reset(lport, port_id, 0);
}
- } else
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ default:
mutex_unlock(&rdata->rp_mutex);
+ break;
+ }
}
/**
* fc_rport_login() - Start the remote port login state machine
- * @rport: Fibre Channel remote port
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
+ *
+ * This indicates the intent to be logged into the remote port.
+ * If it appears we are already logged in, ADISC is used to verify
+ * the setup.
*/
-int fc_rport_login(struct fc_rport *rport)
+int fc_rport_login(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Login to port\n");
-
- fc_rport_enter_plogi(rport);
-
+ switch (rdata->rp_state) {
+ case RPORT_ST_READY:
+ FC_RPORT_DBG(rdata, "ADISC port\n");
+ fc_rport_enter_adisc(rdata);
+ break;
+ default:
+ FC_RPORT_DBG(rdata, "Login to port\n");
+ fc_rport_enter_plogi(rdata);
+ break;
+ }
mutex_unlock(&rdata->rp_mutex);
return 0;
}
/**
+ * fc_rport_enter_delete() - schedule a remote port to be deleted.
+ * @rdata: private remote port
+ * @event: event to report as the reason for deletion
+ *
+ * Locking Note: Called with the rport lock held.
+ *
+ * Allow state change into DELETE only once.
+ *
+ * Call queue_work only if there's no event already pending.
+ * Set the new event so that the old pending event will not occur.
+ * Since we have the mutex, even if fc_rport_work() is already started,
+ * it'll see the new event.
+ */
+static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
+ enum fc_rport_event event)
+{
+ if (rdata->rp_state == RPORT_ST_DELETE)
+ return;
+
+ FC_RPORT_DBG(rdata, "Delete port\n");
+
+ fc_rport_state_enter(rdata, RPORT_ST_DELETE);
+
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = event;
+}
+
+/**
* fc_rport_logoff() - Logoff and remove an rport
- * @rport: Fibre Channel remote port to be removed
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
*/
-int fc_rport_logoff(struct fc_rport *rport)
+int fc_rport_logoff(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Remove port\n");
+ FC_RPORT_DBG(rdata, "Remove port\n");
- if (rdata->rp_state == RPORT_ST_NONE) {
- FC_RPORT_DBG(rport, "Port in NONE state, not removing\n");
+ if (rdata->rp_state == RPORT_ST_DELETE) {
+ FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
mutex_unlock(&rdata->rp_mutex);
goto out;
}
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
/*
- * Change the state to NONE so that we discard
+ * Change the state to Delete so that we discard
* the response.
*/
- fc_rport_state_enter(rport, RPORT_ST_NONE);
-
- mutex_unlock(&rdata->rp_mutex);
-
- cancel_delayed_work_sync(&rdata->retry_work);
-
- mutex_lock(&rdata->rp_mutex);
-
- rdata->event = RPORT_EV_STOP;
- queue_work(rport_event_queue, &rdata->event_work);
-
+ fc_rport_enter_delete(rdata, RPORT_EV_STOP);
mutex_unlock(&rdata->rp_mutex);
out:
@@ -357,26 +416,25 @@ out:
/**
* fc_rport_enter_ready() - The rport is ready
- * @rport: Fibre Channel remote port that is ready
+ * @rdata: private remote port
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_ready(struct fc_rport *rport)
+static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- fc_rport_state_enter(rport, RPORT_ST_READY);
+ fc_rport_state_enter(rdata, RPORT_ST_READY);
- FC_RPORT_DBG(rport, "Port is Ready\n");
+ FC_RPORT_DBG(rdata, "Port is Ready\n");
- rdata->event = RPORT_EV_CREATED;
- queue_work(rport_event_queue, &rdata->event_work);
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = RPORT_EV_READY;
}
/**
* fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_libfc_priv
+ * @work: The work struct of the fc_rport_priv
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -384,63 +442,63 @@ static void fc_rport_enter_ready(struct fc_rport *rport)
*/
static void fc_rport_timeout(struct work_struct *work)
{
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, retry_work.work);
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, retry_work.work);
mutex_lock(&rdata->rp_mutex);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- fc_rport_enter_plogi(rport);
+ fc_rport_enter_plogi(rdata);
break;
case RPORT_ST_PRLI:
- fc_rport_enter_prli(rport);
+ fc_rport_enter_prli(rdata);
break;
case RPORT_ST_RTV:
- fc_rport_enter_rtv(rport);
+ fc_rport_enter_rtv(rdata);
break;
case RPORT_ST_LOGO:
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_ADISC:
+ fc_rport_enter_adisc(rdata);
break;
case RPORT_ST_READY:
case RPORT_ST_INIT:
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
break;
}
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
}
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
- * @rport: The fc_rport object
+ * @rdata: private remote port
* @fp: The frame pointer
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- FC_RPORT_DBG(rport, "Error %ld in state %s, retries %d\n",
- PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n",
+ IS_ERR(fp) ? -PTR_ERR(fp) : 0,
+ fc_rport_state(rdata), rdata->retries);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- case RPORT_ST_PRLI:
case RPORT_ST_LOGO:
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue,
- &rdata->event_work);
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
break;
case RPORT_ST_RTV:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_ADISC:
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_DELETE:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
@@ -449,7 +507,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
/**
* fc_rport_error_retry() - Error handler when retries are desired
- * @rport: The fc_rport object
+ * @rdata: private remote port data
* @fp: The frame pointer
*
* If the error was an exchange timeout retry immediately,
@@ -458,45 +516,43 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error_retry(struct fc_rport_priv *rdata,
+ struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
unsigned long delay = FC_DEF_E_D_TOV;
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
if (PTR_ERR(fp) == -FC_EX_CLOSED)
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
if (rdata->retries < rdata->local_port->max_rport_retry_count) {
- FC_RPORT_DBG(rport, "Error %ld in state %s, retrying\n",
- PTR_ERR(fp), fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
+ PTR_ERR(fp), fc_rport_state(rdata));
rdata->retries++;
/* no additional delay on exchange timeouts */
if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
delay = 0;
- get_device(&rport->dev);
schedule_delayed_work(&rdata->retry_work, delay);
return;
}
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
}
/**
* fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
* @sp: current sequence in the PLOGI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct fc_lport *lport = rdata->local_port;
struct fc_els_flogi *plp = NULL;
unsigned int tov;
@@ -506,26 +562,26 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PLOGI response\n");
+ FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PLOGI) {
- FC_RPORT_DBG(rport, "Received a PLOGI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
- rport->port_name = get_unaligned_be64(&plp->fl_wwpn);
- rport->node_name = get_unaligned_be64(&plp->fl_wwnn);
+ rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
tov = ntohl(plp->fl_csp.sp_e_d_tov);
if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
@@ -537,75 +593,64 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
if (cssp_seq < csp_seq)
csp_seq = cssp_seq;
rdata->max_seq = csp_seq;
- rport->maxframe_size =
- fc_plogi_get_maxframe(plp, lport->mfs);
-
- /*
- * If the rport is one of the well known addresses
- * we skip PRLI and RTV and go straight to READY.
- */
- if (rport->port_id >= FC_FID_DOM_MGR)
- fc_rport_enter_ready(rport);
- else
- fc_rport_enter_prli(rport);
+ rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
+ fc_rport_enter_prli(rdata);
} else
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rport: Fibre Channel remote port to send PLOGI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_plogi(struct fc_rport *rport)
+static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PLOGI state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI);
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
rdata->e_d_tov = lport->e_d_tov;
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
- fc_rport_plogi_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_prli_resp() - Process Login (PRLI) response handler
* @sp: current sequence in the PRLI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -616,21 +661,24 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PRLI response\n");
+ FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PRLI) {
- FC_RPORT_DBG(rport, "Received a PRLI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PRLI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -640,90 +688,82 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
rdata->flags |= FC_RP_FLAGS_RETRY;
}
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
- fc_rport_enter_rtv(rport);
+ rdata->ids.roles = roles;
+ fc_rport_enter_rtv(rdata);
} else {
- FC_RPORT_DBG(rport, "Bad ELS response for PRLI command\n");
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ FC_RPORT_DBG(rdata, "Bad ELS response for PRLI command\n");
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
}
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_logo_resp() - Logout (LOGO) response handler
* @sp: current sequence in the LOGO exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a LOGO response\n");
+ FC_RPORT_DBG(rdata, "Received a LOGO %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_LOGO) {
- FC_RPORT_DBG(rport, "Received a LOGO response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a LOGO response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
- if (op == ELS_LS_ACC) {
- fc_rport_enter_rtv(rport);
- } else {
- FC_RPORT_DBG(rport, "Bad ELS response for LOGO command\n");
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
- }
+ if (op != ELS_LS_ACC)
+ FC_RPORT_DBG(rdata, "Bad ELS response op %x for LOGO command\n",
+ op);
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rport: Fibre Channel remote port to send PRLI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_prli(struct fc_rport *rport)
+static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct {
struct fc_els_prli prli;
@@ -731,29 +771,38 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
} *pp;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PRLI state from %s state\n",
- fc_rport_state(rport));
+ /*
+ * If the rport is one of the well known addresses
+ * we skip PRLI and RTV and go straight to READY.
+ */
+ if (rdata->ids.port_id >= FC_FID_DOM_MGR) {
+ fc_rport_enter_ready(rdata);
+ return;
+ }
+
+ FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PRLI);
+ fc_rport_state_enter(rdata, RPORT_ST_PRLI);
fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
- fc_rport_prli_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
+ fc_rport_prli_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_els_rtv_resp() - Request Timeout Value response handler
* @sp: current sequence in the RTV exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Many targets don't seem to support this.
*
@@ -762,26 +811,25 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
* and then unlock the rport.
*/
static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a RTV response\n");
+ FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_RTV) {
- FC_RPORT_DBG(rport, "Received a RTV response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a RTV response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error(rdata, fp);
goto err;
}
@@ -807,184 +855,376 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
}
}
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rport: Fibre Channel remote port to send RTV to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_rtv(struct fc_rport *rport)
+static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
{
struct fc_frame *fp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- FC_RPORT_DBG(rport, "Port entered RTV state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_RTV);
+ fc_rport_state_enter(rdata, RPORT_ST_RTV);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
- fc_rport_rtv_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
+ fc_rport_rtv_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rport: Fibre Channel remote port to send LOGO to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_logo(struct fc_rport *rport)
+static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered LOGO state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered LOGO state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_LOGO);
+ fc_rport_state_enter(rdata, RPORT_ST_LOGO);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
- fc_rport_logo_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+ fc_rport_logo_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
-
/**
- * fc_rport_recv_req() - Receive a request from a rport
- * @sp: current sequence in the PLOGI exchange
+ * fc_rport_els_adisc_resp() - Address Discovery response handler
+ * @sp: current sequence in the ADISC exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: remote port private.
*
- * Locking Note: Called without the rport lock held. This
- * function will hold the rport lock, call an _enter_*
- * function and then unlock the rport.
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
*/
-void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
- struct fc_rport *rport)
+static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
+{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_els_adisc *adisc;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_RPORT_DBG(rdata, "Received a ADISC response\n");
+
+ if (rdata->rp_state != RPORT_ST_ADISC) {
+ FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n",
+ fc_rport_state(rdata));
+ if (IS_ERR(fp))
+ goto err;
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rdata, fp);
+ goto err;
+ }
+
+ /*
+ * If address verification failed. Consider us logged out of the rport.
+ * Since the rport is still in discovery, we want to be
+ * logged in, so go to PLOGI state. Otherwise, go back to READY.
+ */
+ op = fc_frame_payload_op(fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ if (op != ELS_LS_ACC || !adisc ||
+ ntoh24(adisc->adisc_port_id) != rdata->ids.port_id ||
+ get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
+ get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
+ FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
+ fc_rport_enter_plogi(rdata);
+ } else {
+ FC_RPORT_DBG(rdata, "ADISC OK\n");
+ fc_rport_enter_ready(rdata);
+ }
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+}
+
+/**
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
+ * @rdata: remote port private data
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_RPORT_DBG(rdata, "sending ADISC from %s state\n",
+ fc_rport_state(rdata));
+ fc_rport_state_enter(rdata, RPORT_ST_ADISC);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc));
+ if (!fp) {
+ fc_rport_error_retry(rdata, fp);
+ return;
+ }
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
+ fc_rport_adisc_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
+ else
+ kref_get(&rdata->kref);
+}
+
+/**
+ * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
+ * @rdata: remote port private
+ * @sp: current sequence in the ADISC exchange
+ * @in_fp: ADISC request frame
+ *
+ * Locking Note: Called with the lport and rport locks held.
+ */
+static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp, struct fc_frame *in_fp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_adisc *adisc;
+ struct fc_seq_els_data rjt_data;
+ u32 f_ctl;
+
+ FC_RPORT_DBG(rdata, "Received ADISC request\n");
+
+ adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
+ if (!adisc) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ goto drop;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(*adisc));
+ if (!fp)
+ goto drop;
+ fc_adisc_fill(lport, fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_LS_ACC;
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | 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);
+drop:
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - handle a validated ELS request.
+ * @lport: Fibre Channel local port
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ *
+ * Handle incoming ELS requests that require port login.
+ * The ELS opcode has already been validated by the caller.
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+static void fc_rport_recv_els_req(struct fc_lport *lport,
+ struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_rport_priv *rdata;
struct fc_frame_header *fh;
struct fc_seq_els_data els_data;
- u8 op;
-
- mutex_lock(&rdata->rp_mutex);
els_data.fp = NULL;
- els_data.explan = ELS_EXPL_NONE;
- els_data.reason = ELS_RJT_NONE;
+ els_data.reason = ELS_RJT_UNAB;
+ els_data.explan = ELS_EXPL_PLOGI_REQD;
fh = fc_frame_header_get(fp);
- if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) {
- op = fc_frame_payload_op(fp);
- switch (op) {
- case ELS_PLOGI:
- fc_rport_recv_plogi_req(rport, sp, fp);
- break;
- case ELS_PRLI:
- fc_rport_recv_prli_req(rport, sp, fp);
- break;
- case ELS_PRLO:
- fc_rport_recv_prlo_req(rport, sp, fp);
- break;
- case ELS_LOGO:
- fc_rport_recv_logo_req(rport, sp, fp);
- break;
- case ELS_RRQ:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
- break;
- case ELS_REC:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
- break;
- default:
- els_data.reason = ELS_RJT_UNSUP;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
- break;
- }
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id));
+ if (!rdata) {
+ mutex_unlock(&lport->disc.disc_mutex);
+ goto reject;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&lport->disc.disc_mutex);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
+ case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
+ break;
+ default:
+ mutex_unlock(&rdata->rp_mutex);
+ goto reject;
+ }
+
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PRLI:
+ fc_rport_recv_prli_req(rdata, sp, fp);
+ break;
+ case ELS_PRLO:
+ fc_rport_recv_prlo_req(rdata, sp, fp);
+ break;
+ case ELS_ADISC:
+ fc_rport_recv_adisc_req(rdata, sp, fp);
+ break;
+ case ELS_RRQ:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+ break;
+ case ELS_REC:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+ break;
+ default:
+ fc_frame_free(fp); /* can't happen */
+ break;
}
mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_recv_req() - Handle a received ELS request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @lport: Fibre Channel local port
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ struct fc_seq_els_data els_data;
+
+ /*
+ * Handle PLOGI and LOGO requests separately, since they
+ * don't require prior login.
+ * Check for unsupported opcodes first and reject them.
+ * For some ops, it would be incorrect to reject with "PLOGI required".
+ */
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PLOGI:
+ fc_rport_recv_plogi_req(lport, sp, fp);
+ break;
+ case ELS_LOGO:
+ fc_rport_recv_logo_req(lport, sp, fp);
+ break;
+ case ELS_PRLI:
+ case ELS_PRLO:
+ case ELS_ADISC:
+ case ELS_RRQ:
+ case ELS_REC:
+ fc_rport_recv_els_req(lport, sp, fp);
+ break;
+ default:
+ fc_frame_free(fp);
+ els_data.fp = NULL;
+ els_data.reason = ELS_RJT_UNSUP;
+ els_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ break;
+ }
}
/**
* fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @rport: Fibre Channel remote port that initiated PLOGI
+ * @lport: local port
* @sp: current sequence in the PLOGI exchange
* @fp: PLOGI request frame
*
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
+ * Locking Note: The rport lock is held before calling this function.
*/
-static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_disc *disc;
+ struct fc_rport_priv *rdata;
struct fc_frame *fp = rx_fp;
struct fc_exch *ep;
struct fc_frame_header *fh;
struct fc_els_flogi *pl;
struct fc_seq_els_data rjt_data;
- u32 sid;
- u64 wwpn;
- u64 wwnn;
- enum fc_els_rjt_reason reject = 0;
- u32 f_ctl;
- rjt_data.fp = NULL;
+ u32 sid, f_ctl;
+ rjt_data.fp = NULL;
fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- FC_RPORT_DBG(rport, "Received PLOGI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
- sid = ntoh24(fh->fh_s_id);
pl = fc_frame_payload_get(fp, sizeof(*pl));
if (!pl) {
- FC_RPORT_DBG(rport, "Received PLOGI too short\n");
- WARN_ON(1);
- /* XXX TBD: send reject? */
- fc_frame_free(fp);
- return;
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n");
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ goto reject;
+ }
+
+ disc = &lport->disc;
+ mutex_lock(&disc->disc_mutex);
+ rdata = lport->tt.rport_create(lport, sid);
+ if (!rdata) {
+ mutex_unlock(&disc->disc_mutex);
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto reject;
}
- wwpn = get_unaligned_be64(&pl->fl_wwpn);
- wwnn = get_unaligned_be64(&pl->fl_wwnn);
+
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&disc->disc_mutex);
+
+ rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&pl->fl_wwnn);
/*
- * If the session was just created, possibly due to the incoming PLOGI,
+ * If the rport was just created, possibly due to the incoming PLOGI,
* set the state appropriately and accept the PLOGI.
*
* If we had also sent a PLOGI, and if the received PLOGI is from a
@@ -996,86 +1236,76 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_INIT:
- FC_RPORT_DBG(rport, "Received PLOGI, wwpn %llx state INIT "
- "- reject\n", (unsigned long long)wwpn);
- reject = ELS_RJT_UNSUP;
+ FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
break;
case RPORT_ST_PLOGI:
- FC_RPORT_DBG(rport, "Received PLOGI in PLOGI state %d\n",
- rdata->rp_state);
- if (wwpn < lport->wwpn)
- reject = ELS_RJT_INPROG;
+ FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
+ if (rdata->ids.port_name < lport->wwpn) {
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_INPROG;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
+ }
break;
case RPORT_ST_PRLI:
case RPORT_ST_READY:
- FC_RPORT_DBG(rport, "Received PLOGI in logged-in state %d "
+ case RPORT_ST_ADISC:
+ FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
"- ignored for now\n", rdata->rp_state);
/* XXX TBD - should reset */
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
default:
- FC_RPORT_DBG(rport, "Received PLOGI in unexpected "
- "state %d\n", rdata->rp_state);
- fc_frame_free(fp);
- return;
- break;
+ FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
+ rdata->rp_state);
+ fc_frame_free(rx_fp);
+ goto out;
}
- if (reject) {
- rjt_data.reason = reject;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- fp = fc_frame_alloc(lport, sizeof(*pl));
- if (fp == NULL) {
- fp = rx_fp;
- rjt_data.reason = ELS_RJT_UNAB;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- sp = lport->tt.seq_start_next(sp);
- WARN_ON(!sp);
- fc_rport_set_name(rport, wwpn, wwnn);
-
- /*
- * Get session payload size from incoming PLOGI.
- */
- rport->maxframe_size =
- fc_plogi_get_maxframe(pl, lport->mfs);
- fc_frame_free(rx_fp);
- fc_plogi_fill(lport, fp, ELS_LS_ACC);
-
- /*
- * Send LS_ACC. If this fails,
- * the originator should retry.
- */
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- ep = fc_seq_exch(sp);
- 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);
- if (rdata->rp_state == RPORT_ST_PLOGI)
- fc_rport_enter_prli(rport);
- }
- }
+ /*
+ * Get session payload size from incoming PLOGI.
+ */
+ rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
+ fc_frame_free(rx_fp);
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ sp = lport->tt.seq_start_next(sp);
+ if (!sp)
+ goto out;
+ fp = fc_frame_alloc(lport, sizeof(*pl));
+ if (!fp)
+ goto out;
+
+ fc_plogi_fill(lport, fp, ELS_LS_ACC);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ 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_rport_enter_prli(rdata);
+out:
+ mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
}
/**
* fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rport: Fibre Channel remote port that initiated PRLI
+ * @rdata: private remote port data
* @sp: current sequence in the PRLI exchange
* @fp: PRLI request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prli_req(struct fc_rport *rport,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_exch *ep;
struct fc_frame *fp;
@@ -1099,12 +1329,14 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fh = fc_frame_header_get(rx_fp);
- FC_RPORT_DBG(rport, "Received PRLI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
+ fc_rport_state(rdata));
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
reason = ELS_RJT_NONE;
break;
default:
@@ -1149,6 +1381,9 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
@@ -1169,12 +1404,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fcp_parm = ntohl(rspp->spp_params);
if (fcp_parm * FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
+ rdata->ids.roles = roles;
spp->spp_params =
htonl(lport->service_params);
@@ -1204,9 +1439,10 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
break;
default:
break;
@@ -1217,17 +1453,17 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
/**
* fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rport: Fibre Channel remote port that initiated PRLO
+ * @rdata: private remote port data
* @sp: current sequence in the PRLO exchange
* @fp: PRLO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame_header *fh;
@@ -1235,13 +1471,8 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
fh = fc_frame_header_get(fp);
- FC_RPORT_DBG(rport, "Received PRLO request while in state %s\n",
- fc_rport_state(rport));
-
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
+ fc_rport_state(rdata));
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
@@ -1252,35 +1483,46 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
/**
* fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @rport: Fibre Channel remote port that initiated LOGO
+ * @lport: local port.
* @sp: current sequence in the LOGO exchange
* @fp: LOGO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_logo_req(struct fc_lport *lport,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
struct fc_frame_header *fh;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
-
- fh = fc_frame_header_get(fp);
+ struct fc_rport_priv *rdata;
+ u32 sid;
- FC_RPORT_DBG(rport, "Received LOGO request while in state %s\n",
- fc_rport_state(rport));
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, sid);
+ if (rdata) {
+ mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
+ fc_rport_state(rdata));
- lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ /*
+ * If the remote port was created due to discovery,
+ * log back in. It may have seen a stale RSCN about us.
+ */
+ if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
+ fc_rport_enter_plogi(rdata);
+ else
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ mutex_unlock(&rdata->rp_mutex);
+ } else
+ FC_RPORT_ID_DBG(lport, sid,
+ "Received LOGO from non-logged-in port\n");
+ mutex_unlock(&lport->disc.disc_mutex);
fc_frame_free(fp);
}
@@ -1291,8 +1533,11 @@ static void fc_rport_flush_queue(void)
int fc_rport_init(struct fc_lport *lport)
{
+ if (!lport->tt.rport_lookup)
+ lport->tt.rport_lookup = fc_rport_lookup;
+
if (!lport->tt.rport_create)
- lport->tt.rport_create = fc_rport_rogue_create;
+ lport->tt.rport_create = fc_rport_create;
if (!lport->tt.rport_login)
lport->tt.rport_login = fc_rport_login;
@@ -1306,6 +1551,9 @@ int fc_rport_init(struct fc_lport *lport)
if (!lport->tt.rport_flush_queue)
lport->tt.rport_flush_queue = fc_rport_flush_queue;
+ if (!lport->tt.rport_destroy)
+ lport->tt.rport_destroy = fc_rport_destroy;
+
return 0;
}
EXPORT_SYMBOL(fc_rport_init);
@@ -1327,8 +1575,8 @@ EXPORT_SYMBOL(fc_destroy_rport);
void fc_rport_terminate_io(struct fc_rport *rport)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_lport *lport = rp->local_port;
lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);