aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc/fc_disc.c
diff options
context:
space:
mode:
authorAbhijeet Joglekar <abjoglek@cisco.com>2009-04-21 19:27:04 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-04-27 11:18:57 -0400
commitb4c6f54632ad664a3d9e7f05e4ea0f1803e32755 (patch)
tree6d419c4a2cdf1e58ae8044d006c53df9cb856f07 /drivers/scsi/libfc/fc_disc.c
parent76f6804e7e7bb836cbdf4a73fe6c5485e4cc04c2 (diff)
[SCSI] libfc: Track rogue remote ports
Rogue ports are currently not tracked on any list. The only reference to them is through any outstanding exchanges pending on the rogue ports. If the module is removed while a retry is set on a rogue port (say a Plogi retry for instance), this retry is not cancelled because there is no reference to the rogue port in the discovery rports list. Thus the local port can clean itself up, delete the exchange pool, and then the rogue port timeout can fire and try to start up another exchange. This patch tracks the rogue ports in a new list disc->rogue_rports. Creating a new list instead of using the disc->rports list keeps remote port code change to a minimum. 1) Whenever a rogue port is created, it is immediately added to the disc->rogue_rports list. 2) When the rogues port goes to ready, it is removed from the rogue list and the real remote port is added to the disc->rports list 3) The removal of the rogue from the disc->rogue_rports list is done in the context of the fc_rport_work() workQ thread in discovery callback. 4) Real rports are removed from the disc->rports list like before. Lookup is done only in the real rports list. This avoids making large changes to the remote port code. 5) In fc_disc_stop_rports, the rogues list is traversed in addition to the real list to stop the rogue ports and issue logoffs on them. This way, rogue ports get cleaned up when the local port goes away. 6) rogue remote ports are not removed from the list right away, but removed late in fc_rport_work() context, multiple threads can find the same remote port in the list and call rport_logoff(). Rport_logoff() only continues with the logoff if port is not in NONE state, thus preventing multiple logoffs and multiple list deletions. 7) Since the rport is removed from the disc list at a later stage (in the disc callback), incoming frames can find the rport even if rport_logoff() has been called on the rport. When rport_logoff() is called, the rport state is set to NONE, and we are trying to cancel all exchanges and retries on that port. While in this state, if an incoming Plogi/Prli/Logo or other frames match the rport, we should not reply because the rport is in the NONE state. Just drop the frame, since the rport will be deleted soon in the disc callback (fc_rport_work) 8) In fc_disc_single(), remove rport lookup and call to fc_disc_del_target. fc_disc_single() is called from recv_rscn_req() where rport lookup and rport_logoff is already done. Signed-off-by: Abhijeet Joglekar <abjoglek@cisco.com> Signed-off-by: Robert Love <robert.w.love@intel.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/libfc/fc_disc.c')
-rw-r--r--drivers/scsi/libfc/fc_disc.c36
1 files changed, 25 insertions, 11 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 44806307f831..4c880656990b 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -113,6 +113,11 @@ void fc_disc_stop_rports(struct fc_disc *disc)
113 lport->tt.rport_logoff(rport); 113 lport->tt.rport_logoff(rport);
114 } 114 }
115 115
116 list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
117 rport = PRIV_TO_RPORT(rdata);
118 lport->tt.rport_logoff(rport);
119 }
120
116 mutex_unlock(&disc->disc_mutex); 121 mutex_unlock(&disc->disc_mutex);
117} 122}
118 123
@@ -131,23 +136,32 @@ static void fc_disc_rport_callback(struct fc_lport *lport,
131{ 136{
132 struct fc_rport_libfc_priv *rdata = rport->dd_data; 137 struct fc_rport_libfc_priv *rdata = rport->dd_data;
133 struct fc_disc *disc = &lport->disc; 138 struct fc_disc *disc = &lport->disc;
134 int found = 0;
135 139
136 FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event, 140 FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
137 rport->port_id); 141 rport->port_id);
138 142
139 if (event == RPORT_EV_CREATED) { 143 switch (event) {
144 case RPORT_EV_CREATED:
140 if (disc) { 145 if (disc) {
141 found = 1;
142 mutex_lock(&disc->disc_mutex); 146 mutex_lock(&disc->disc_mutex);
143 list_add_tail(&rdata->peers, &disc->rports); 147 list_add_tail(&rdata->peers, &disc->rports);
144 mutex_unlock(&disc->disc_mutex); 148 mutex_unlock(&disc->disc_mutex);
145 } 149 }
150 break;
151 case RPORT_EV_LOGO:
152 case RPORT_EV_FAILED:
153 case RPORT_EV_STOP:
154 mutex_lock(&disc->disc_mutex);
155 mutex_lock(&rdata->rp_mutex);
156 if (rdata->trans_state == FC_PORTSTATE_ROGUE)
157 list_del(&rdata->peers);
158 mutex_unlock(&rdata->rp_mutex);
159 mutex_unlock(&disc->disc_mutex);
160 break;
161 default:
162 break;
146 } 163 }
147 164
148 if (!found)
149 FC_DEBUG_DISC("The rport (%6x) is not maintained "
150 "by the discovery layer\n", rport->port_id);
151} 165}
152 166
153/** 167/**
@@ -439,6 +453,7 @@ static int fc_disc_new_target(struct fc_disc *disc,
439 rdata = rport->dd_data; 453 rdata = rport->dd_data;
440 rdata->ops = &fc_disc_rport_ops; 454 rdata->ops = &fc_disc_rport_ops;
441 rdata->rp_state = RPORT_ST_INIT; 455 rdata->rp_state = RPORT_ST_INIT;
456 list_add_tail(&rdata->peers, &disc->rogue_rports);
442 lport->tt.rport_login(rport); 457 lport->tt.rport_login(rport);
443 } 458 }
444 } 459 }
@@ -630,6 +645,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
630 rdata = rport->dd_data; 645 rdata = rport->dd_data;
631 rdata->ops = &fc_disc_rport_ops; 646 rdata->ops = &fc_disc_rport_ops;
632 rdata->local_port = lport; 647 rdata->local_port = lport;
648 list_add_tail(&rdata->peers,
649 &disc->rogue_rports);
633 lport->tt.rport_login(rport); 650 lport->tt.rport_login(rport);
634 } else 651 } else
635 FC_DBG("Failed to allocate memory for " 652 FC_DBG("Failed to allocate memory for "
@@ -769,7 +786,6 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
769static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) 786static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
770{ 787{
771 struct fc_lport *lport; 788 struct fc_lport *lport;
772 struct fc_rport *rport;
773 struct fc_rport *new_rport; 789 struct fc_rport *new_rport;
774 struct fc_rport_libfc_priv *rdata; 790 struct fc_rport_libfc_priv *rdata;
775 791
@@ -778,15 +794,12 @@ static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
778 if (dp->ids.port_id == fc_host_port_id(lport->host)) 794 if (dp->ids.port_id == fc_host_port_id(lport->host))
779 goto out; 795 goto out;
780 796
781 rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
782 if (rport)
783 fc_disc_del_target(disc, rport);
784
785 new_rport = lport->tt.rport_create(dp); 797 new_rport = lport->tt.rport_create(dp);
786 if (new_rport) { 798 if (new_rport) {
787 rdata = new_rport->dd_data; 799 rdata = new_rport->dd_data;
788 rdata->ops = &fc_disc_rport_ops; 800 rdata->ops = &fc_disc_rport_ops;
789 kfree(dp); 801 kfree(dp);
802 list_add_tail(&rdata->peers, &disc->rogue_rports);
790 lport->tt.rport_login(new_rport); 803 lport->tt.rport_login(new_rport);
791 } 804 }
792 return; 805 return;
@@ -848,6 +861,7 @@ int fc_disc_init(struct fc_lport *lport)
848 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout); 861 INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
849 mutex_init(&disc->disc_mutex); 862 mutex_init(&disc->disc_mutex);
850 INIT_LIST_HEAD(&disc->rports); 863 INIT_LIST_HEAD(&disc->rports);
864 INIT_LIST_HEAD(&disc->rogue_rports);
851 865
852 disc->lport = lport; 866 disc->lport = lport;
853 disc->delay = FC_DISC_DELAY; 867 disc->delay = FC_DISC_DELAY;