aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc
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
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')
-rw-r--r--drivers/scsi/libfc/fc_disc.c36
-rw-r--r--drivers/scsi/libfc/fc_rport.c28
2 files changed, 53 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;
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index eef70b4b7b92..5bf7a949f051 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -267,6 +267,10 @@ static void fc_rport_work(struct work_struct *work)
267 "(%6x).\n", ids.port_id); 267 "(%6x).\n", ids.port_id);
268 event = RPORT_EV_FAILED; 268 event = RPORT_EV_FAILED;
269 } 269 }
270 if (rport->port_id != FC_FID_DIR_SERV)
271 if (rport_ops->event_callback)
272 rport_ops->event_callback(lport, rport,
273 RPORT_EV_FAILED);
270 put_device(&rport->dev); 274 put_device(&rport->dev);
271 rport = new_rport; 275 rport = new_rport;
272 rdata = new_rport->dd_data; 276 rdata = new_rport->dd_data;
@@ -325,11 +329,20 @@ int fc_rport_login(struct fc_rport *rport)
325int fc_rport_logoff(struct fc_rport *rport) 329int fc_rport_logoff(struct fc_rport *rport)
326{ 330{
327 struct fc_rport_libfc_priv *rdata = rport->dd_data; 331 struct fc_rport_libfc_priv *rdata = rport->dd_data;
332 struct fc_lport *lport = rdata->local_port;
328 333
329 mutex_lock(&rdata->rp_mutex); 334 mutex_lock(&rdata->rp_mutex);
330 335
331 FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id); 336 FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
332 337
338 if (rdata->rp_state == RPORT_ST_NONE) {
339 FC_DEBUG_RPORT("(%6x): Port (%6x) in NONE state,"
340 " not removing", fc_host_port_id(lport->host),
341 rport->port_id);
342 mutex_unlock(&rdata->rp_mutex);
343 goto out;
344 }
345
333 fc_rport_enter_logo(rport); 346 fc_rport_enter_logo(rport);
334 347
335 /* 348 /*
@@ -349,6 +362,7 @@ int fc_rport_logoff(struct fc_rport *rport)
349 362
350 mutex_unlock(&rdata->rp_mutex); 363 mutex_unlock(&rdata->rp_mutex);
351 364
365out:
352 return 0; 366 return 0;
353} 367}
354 368
@@ -1015,6 +1029,8 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
1015 default: 1029 default:
1016 FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected " 1030 FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected "
1017 "state %d\n", sid, rdata->rp_state); 1031 "state %d\n", sid, rdata->rp_state);
1032 fc_frame_free(fp);
1033 return;
1018 break; 1034 break;
1019 } 1035 }
1020 1036
@@ -1106,6 +1122,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
1106 reason = ELS_RJT_NONE; 1122 reason = ELS_RJT_NONE;
1107 break; 1123 break;
1108 default: 1124 default:
1125 fc_frame_free(rx_fp);
1126 return;
1109 break; 1127 break;
1110 } 1128 }
1111 len = fr_len(rx_fp) - sizeof(*fh); 1129 len = fr_len(rx_fp) - sizeof(*fh);
@@ -1235,6 +1253,11 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
1235 "while in state %s\n", ntoh24(fh->fh_s_id), 1253 "while in state %s\n", ntoh24(fh->fh_s_id),
1236 fc_rport_state(rport)); 1254 fc_rport_state(rport));
1237 1255
1256 if (rdata->rp_state == RPORT_ST_NONE) {
1257 fc_frame_free(fp);
1258 return;
1259 }
1260
1238 rjt_data.fp = NULL; 1261 rjt_data.fp = NULL;
1239 rjt_data.reason = ELS_RJT_UNAB; 1262 rjt_data.reason = ELS_RJT_UNAB;
1240 rjt_data.explan = ELS_EXPL_NONE; 1263 rjt_data.explan = ELS_EXPL_NONE;
@@ -1264,6 +1287,11 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
1264 "while in state %s\n", ntoh24(fh->fh_s_id), 1287 "while in state %s\n", ntoh24(fh->fh_s_id),
1265 fc_rport_state(rport)); 1288 fc_rport_state(rport));
1266 1289
1290 if (rdata->rp_state == RPORT_ST_NONE) {
1291 fc_frame_free(fp);
1292 return;
1293 }
1294
1267 rdata->event = RPORT_EV_LOGO; 1295 rdata->event = RPORT_EV_LOGO;
1268 queue_work(rport_event_queue, &rdata->event_work); 1296 queue_work(rport_event_queue, &rdata->event_work);
1269 1297