diff options
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 69 | ||||
-rw-r--r-- | include/scsi/libfc.h | 1 |
2 files changed, 51 insertions, 19 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 49abb839a223..324e156b5d07 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c | |||
@@ -86,6 +86,7 @@ static const char *fc_rport_state_names[] = { | |||
86 | [RPORT_ST_LOGO] = "LOGO", | 86 | [RPORT_ST_LOGO] = "LOGO", |
87 | [RPORT_ST_ADISC] = "ADISC", | 87 | [RPORT_ST_ADISC] = "ADISC", |
88 | [RPORT_ST_DELETE] = "Delete", | 88 | [RPORT_ST_DELETE] = "Delete", |
89 | [RPORT_ST_RESTART] = "Restart", | ||
89 | }; | 90 | }; |
90 | 91 | ||
91 | /** | 92 | /** |
@@ -99,8 +100,7 @@ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport, | |||
99 | struct fc_rport_priv *rdata; | 100 | struct fc_rport_priv *rdata; |
100 | 101 | ||
101 | list_for_each_entry(rdata, &lport->disc.rports, peers) | 102 | list_for_each_entry(rdata, &lport->disc.rports, peers) |
102 | if (rdata->ids.port_id == port_id && | 103 | if (rdata->ids.port_id == port_id) |
103 | rdata->rp_state != RPORT_ST_DELETE) | ||
104 | return rdata; | 104 | return rdata; |
105 | return NULL; | 105 | return NULL; |
106 | } | 106 | } |
@@ -235,6 +235,7 @@ static void fc_rport_work(struct work_struct *work) | |||
235 | struct fc_rport_operations *rport_ops; | 235 | struct fc_rport_operations *rport_ops; |
236 | struct fc_rport_identifiers ids; | 236 | struct fc_rport_identifiers ids; |
237 | struct fc_rport *rport; | 237 | struct fc_rport *rport; |
238 | int restart = 0; | ||
238 | 239 | ||
239 | mutex_lock(&rdata->rp_mutex); | 240 | mutex_lock(&rdata->rp_mutex); |
240 | event = rdata->event; | 241 | event = rdata->event; |
@@ -287,8 +288,19 @@ static void fc_rport_work(struct work_struct *work) | |||
287 | mutex_unlock(&rdata->rp_mutex); | 288 | mutex_unlock(&rdata->rp_mutex); |
288 | 289 | ||
289 | if (port_id != FC_FID_DIR_SERV) { | 290 | if (port_id != FC_FID_DIR_SERV) { |
291 | /* | ||
292 | * We must drop rp_mutex before taking disc_mutex. | ||
293 | * Re-evaluate state to allow for restart. | ||
294 | * A transition to RESTART state must only happen | ||
295 | * while disc_mutex is held and rdata is on the list. | ||
296 | */ | ||
290 | mutex_lock(&lport->disc.disc_mutex); | 297 | mutex_lock(&lport->disc.disc_mutex); |
291 | list_del(&rdata->peers); | 298 | mutex_lock(&rdata->rp_mutex); |
299 | if (rdata->rp_state == RPORT_ST_RESTART) | ||
300 | restart = 1; | ||
301 | else | ||
302 | list_del(&rdata->peers); | ||
303 | mutex_unlock(&rdata->rp_mutex); | ||
292 | mutex_unlock(&lport->disc.disc_mutex); | 304 | mutex_unlock(&lport->disc.disc_mutex); |
293 | } | 305 | } |
294 | 306 | ||
@@ -312,7 +324,13 @@ static void fc_rport_work(struct work_struct *work) | |||
312 | mutex_unlock(&rdata->rp_mutex); | 324 | mutex_unlock(&rdata->rp_mutex); |
313 | fc_remote_port_delete(rport); | 325 | fc_remote_port_delete(rport); |
314 | } | 326 | } |
315 | kref_put(&rdata->kref, lport->tt.rport_destroy); | 327 | if (restart) { |
328 | mutex_lock(&rdata->rp_mutex); | ||
329 | FC_RPORT_DBG(rdata, "work restart\n"); | ||
330 | fc_rport_enter_plogi(rdata); | ||
331 | mutex_unlock(&rdata->rp_mutex); | ||
332 | } else | ||
333 | kref_put(&rdata->kref, lport->tt.rport_destroy); | ||
316 | break; | 334 | break; |
317 | 335 | ||
318 | default: | 336 | default: |
@@ -342,6 +360,12 @@ int fc_rport_login(struct fc_rport_priv *rdata) | |||
342 | FC_RPORT_DBG(rdata, "ADISC port\n"); | 360 | FC_RPORT_DBG(rdata, "ADISC port\n"); |
343 | fc_rport_enter_adisc(rdata); | 361 | fc_rport_enter_adisc(rdata); |
344 | break; | 362 | break; |
363 | case RPORT_ST_RESTART: | ||
364 | break; | ||
365 | case RPORT_ST_DELETE: | ||
366 | FC_RPORT_DBG(rdata, "Restart deleted port\n"); | ||
367 | fc_rport_state_enter(rdata, RPORT_ST_RESTART); | ||
368 | break; | ||
345 | default: | 369 | default: |
346 | FC_RPORT_DBG(rdata, "Login to port\n"); | 370 | FC_RPORT_DBG(rdata, "Login to port\n"); |
347 | fc_rport_enter_plogi(rdata); | 371 | fc_rport_enter_plogi(rdata); |
@@ -397,20 +421,21 @@ int fc_rport_logoff(struct fc_rport_priv *rdata) | |||
397 | 421 | ||
398 | if (rdata->rp_state == RPORT_ST_DELETE) { | 422 | if (rdata->rp_state == RPORT_ST_DELETE) { |
399 | FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n"); | 423 | FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n"); |
400 | mutex_unlock(&rdata->rp_mutex); | ||
401 | goto out; | 424 | goto out; |
402 | } | 425 | } |
403 | 426 | ||
404 | fc_rport_enter_logo(rdata); | 427 | if (rdata->rp_state == RPORT_ST_RESTART) |
428 | FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n"); | ||
429 | else | ||
430 | fc_rport_enter_logo(rdata); | ||
405 | 431 | ||
406 | /* | 432 | /* |
407 | * Change the state to Delete so that we discard | 433 | * Change the state to Delete so that we discard |
408 | * the response. | 434 | * the response. |
409 | */ | 435 | */ |
410 | fc_rport_enter_delete(rdata, RPORT_EV_STOP); | 436 | fc_rport_enter_delete(rdata, RPORT_EV_STOP); |
411 | mutex_unlock(&rdata->rp_mutex); | ||
412 | |||
413 | out: | 437 | out: |
438 | mutex_unlock(&rdata->rp_mutex); | ||
414 | return 0; | 439 | return 0; |
415 | } | 440 | } |
416 | 441 | ||
@@ -466,6 +491,7 @@ static void fc_rport_timeout(struct work_struct *work) | |||
466 | case RPORT_ST_READY: | 491 | case RPORT_ST_READY: |
467 | case RPORT_ST_INIT: | 492 | case RPORT_ST_INIT: |
468 | case RPORT_ST_DELETE: | 493 | case RPORT_ST_DELETE: |
494 | case RPORT_ST_RESTART: | ||
469 | break; | 495 | break; |
470 | } | 496 | } |
471 | 497 | ||
@@ -499,6 +525,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) | |||
499 | fc_rport_enter_logo(rdata); | 525 | fc_rport_enter_logo(rdata); |
500 | break; | 526 | break; |
501 | case RPORT_ST_DELETE: | 527 | case RPORT_ST_DELETE: |
528 | case RPORT_ST_RESTART: | ||
502 | case RPORT_ST_READY: | 529 | case RPORT_ST_READY: |
503 | case RPORT_ST_INIT: | 530 | case RPORT_ST_INIT: |
504 | break; | 531 | break; |
@@ -1248,6 +1275,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, | |||
1248 | } | 1275 | } |
1249 | break; | 1276 | break; |
1250 | case RPORT_ST_PRLI: | 1277 | case RPORT_ST_PRLI: |
1278 | case RPORT_ST_RTV: | ||
1251 | case RPORT_ST_READY: | 1279 | case RPORT_ST_READY: |
1252 | case RPORT_ST_ADISC: | 1280 | case RPORT_ST_ADISC: |
1253 | FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d " | 1281 | FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d " |
@@ -1255,11 +1283,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, | |||
1255 | /* XXX TBD - should reset */ | 1283 | /* XXX TBD - should reset */ |
1256 | break; | 1284 | break; |
1257 | case RPORT_ST_DELETE: | 1285 | case RPORT_ST_DELETE: |
1258 | default: | 1286 | case RPORT_ST_LOGO: |
1259 | FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n", | 1287 | case RPORT_ST_RESTART: |
1260 | rdata->rp_state); | 1288 | FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n", |
1261 | fc_frame_free(rx_fp); | 1289 | fc_rport_state(rdata)); |
1262 | goto out; | 1290 | mutex_unlock(&rdata->rp_mutex); |
1291 | rjt_data.reason = ELS_RJT_BUSY; | ||
1292 | rjt_data.explan = ELS_EXPL_NONE; | ||
1293 | goto reject; | ||
1263 | } | 1294 | } |
1264 | 1295 | ||
1265 | /* | 1296 | /* |
@@ -1510,14 +1541,14 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport, | |||
1510 | FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", | 1541 | FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n", |
1511 | fc_rport_state(rdata)); | 1542 | fc_rport_state(rdata)); |
1512 | 1543 | ||
1544 | fc_rport_enter_delete(rdata, RPORT_EV_LOGO); | ||
1545 | |||
1513 | /* | 1546 | /* |
1514 | * If the remote port was created due to discovery, | 1547 | * If the remote port was created due to discovery, set state |
1515 | * log back in. It may have seen a stale RSCN about us. | 1548 | * to log back in. It may have seen a stale RSCN about us. |
1516 | */ | 1549 | */ |
1517 | if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id) | 1550 | if (rdata->disc_id) |
1518 | fc_rport_enter_plogi(rdata); | 1551 | fc_rport_state_enter(rdata, RPORT_ST_RESTART); |
1519 | else | ||
1520 | fc_rport_enter_delete(rdata, RPORT_EV_LOGO); | ||
1521 | mutex_unlock(&rdata->rp_mutex); | 1552 | mutex_unlock(&rdata->rp_mutex); |
1522 | } else | 1553 | } else |
1523 | FC_RPORT_ID_DBG(lport, sid, | 1554 | FC_RPORT_ID_DBG(lport, sid, |
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 4ff148580562..1662d73d85a7 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h | |||
@@ -145,6 +145,7 @@ enum fc_rport_state { | |||
145 | RPORT_ST_LOGO, /* port logout sent */ | 145 | RPORT_ST_LOGO, /* port logout sent */ |
146 | RPORT_ST_ADISC, /* Discover Address sent */ | 146 | RPORT_ST_ADISC, /* Discover Address sent */ |
147 | RPORT_ST_DELETE, /* port being deleted */ | 147 | RPORT_ST_DELETE, /* port being deleted */ |
148 | RPORT_ST_RESTART, /* remote port being deleted and will restart */ | ||
148 | }; | 149 | }; |
149 | 150 | ||
150 | /** | 151 | /** |