diff options
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 122 | ||||
-rw-r--r-- | include/scsi/fc_encode.h | 21 | ||||
-rw-r--r-- | include/scsi/libfc.h | 1 |
3 files changed, 139 insertions, 5 deletions
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index b5bc8724e1a0..c33e25851082 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c | |||
@@ -62,6 +62,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *); | |||
62 | static void fc_rport_enter_rtv(struct fc_rport_priv *); | 62 | static void fc_rport_enter_rtv(struct fc_rport_priv *); |
63 | static void fc_rport_enter_ready(struct fc_rport_priv *); | 63 | static void fc_rport_enter_ready(struct fc_rport_priv *); |
64 | static void fc_rport_enter_logo(struct fc_rport_priv *); | 64 | static void fc_rport_enter_logo(struct fc_rport_priv *); |
65 | static void fc_rport_enter_adisc(struct fc_rport_priv *); | ||
65 | 66 | ||
66 | static void fc_rport_recv_plogi_req(struct fc_lport *, | 67 | static void fc_rport_recv_plogi_req(struct fc_lport *, |
67 | struct fc_seq *, struct fc_frame *); | 68 | struct fc_seq *, struct fc_frame *); |
@@ -83,6 +84,7 @@ static const char *fc_rport_state_names[] = { | |||
83 | [RPORT_ST_RTV] = "RTV", | 84 | [RPORT_ST_RTV] = "RTV", |
84 | [RPORT_ST_READY] = "Ready", | 85 | [RPORT_ST_READY] = "Ready", |
85 | [RPORT_ST_LOGO] = "LOGO", | 86 | [RPORT_ST_LOGO] = "LOGO", |
87 | [RPORT_ST_ADISC] = "ADISC", | ||
86 | [RPORT_ST_DELETE] = "Delete", | 88 | [RPORT_ST_DELETE] = "Delete", |
87 | }; | 89 | }; |
88 | 90 | ||
@@ -326,15 +328,25 @@ static void fc_rport_work(struct work_struct *work) | |||
326 | * Locking Note: Called without the rport lock held. This | 328 | * Locking Note: Called without the rport lock held. This |
327 | * function will hold the rport lock, call an _enter_* | 329 | * function will hold the rport lock, call an _enter_* |
328 | * function and then unlock the rport. | 330 | * function and then unlock the rport. |
331 | * | ||
332 | * This indicates the intent to be logged into the remote port. | ||
333 | * If it appears we are already logged in, ADISC is used to verify | ||
334 | * the setup. | ||
329 | */ | 335 | */ |
330 | int fc_rport_login(struct fc_rport_priv *rdata) | 336 | int fc_rport_login(struct fc_rport_priv *rdata) |
331 | { | 337 | { |
332 | mutex_lock(&rdata->rp_mutex); | 338 | mutex_lock(&rdata->rp_mutex); |
333 | 339 | ||
334 | FC_RPORT_DBG(rdata, "Login to port\n"); | 340 | switch (rdata->rp_state) { |
335 | 341 | case RPORT_ST_READY: | |
336 | fc_rport_enter_plogi(rdata); | 342 | FC_RPORT_DBG(rdata, "ADISC port\n"); |
337 | 343 | fc_rport_enter_adisc(rdata); | |
344 | break; | ||
345 | default: | ||
346 | FC_RPORT_DBG(rdata, "Login to port\n"); | ||
347 | fc_rport_enter_plogi(rdata); | ||
348 | break; | ||
349 | } | ||
338 | mutex_unlock(&rdata->rp_mutex); | 350 | mutex_unlock(&rdata->rp_mutex); |
339 | 351 | ||
340 | return 0; | 352 | return 0; |
@@ -448,6 +460,9 @@ static void fc_rport_timeout(struct work_struct *work) | |||
448 | case RPORT_ST_LOGO: | 460 | case RPORT_ST_LOGO: |
449 | fc_rport_enter_logo(rdata); | 461 | fc_rport_enter_logo(rdata); |
450 | break; | 462 | break; |
463 | case RPORT_ST_ADISC: | ||
464 | fc_rport_enter_adisc(rdata); | ||
465 | break; | ||
451 | case RPORT_ST_READY: | 466 | case RPORT_ST_READY: |
452 | case RPORT_ST_INIT: | 467 | case RPORT_ST_INIT: |
453 | case RPORT_ST_DELETE: | 468 | case RPORT_ST_DELETE: |
@@ -473,13 +488,16 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) | |||
473 | 488 | ||
474 | switch (rdata->rp_state) { | 489 | switch (rdata->rp_state) { |
475 | case RPORT_ST_PLOGI: | 490 | case RPORT_ST_PLOGI: |
476 | case RPORT_ST_PRLI: | ||
477 | case RPORT_ST_LOGO: | 491 | case RPORT_ST_LOGO: |
478 | fc_rport_enter_delete(rdata, RPORT_EV_FAILED); | 492 | fc_rport_enter_delete(rdata, RPORT_EV_FAILED); |
479 | break; | 493 | break; |
480 | case RPORT_ST_RTV: | 494 | case RPORT_ST_RTV: |
481 | fc_rport_enter_ready(rdata); | 495 | fc_rport_enter_ready(rdata); |
482 | break; | 496 | break; |
497 | case RPORT_ST_PRLI: | ||
498 | case RPORT_ST_ADISC: | ||
499 | fc_rport_enter_logo(rdata); | ||
500 | break; | ||
483 | case RPORT_ST_DELETE: | 501 | case RPORT_ST_DELETE: |
484 | case RPORT_ST_READY: | 502 | case RPORT_ST_READY: |
485 | case RPORT_ST_INIT: | 503 | case RPORT_ST_INIT: |
@@ -907,6 +925,93 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata) | |||
907 | } | 925 | } |
908 | 926 | ||
909 | /** | 927 | /** |
928 | * fc_rport_els_adisc_resp() - Address Discovery response handler | ||
929 | * @sp: current sequence in the ADISC exchange | ||
930 | * @fp: response frame | ||
931 | * @rdata_arg: remote port private. | ||
932 | * | ||
933 | * Locking Note: This function will be called without the rport lock | ||
934 | * held, but it will lock, call an _enter_* function or fc_rport_error | ||
935 | * and then unlock the rport. | ||
936 | */ | ||
937 | static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp, | ||
938 | void *rdata_arg) | ||
939 | { | ||
940 | struct fc_rport_priv *rdata = rdata_arg; | ||
941 | struct fc_els_adisc *adisc; | ||
942 | u8 op; | ||
943 | |||
944 | mutex_lock(&rdata->rp_mutex); | ||
945 | |||
946 | FC_RPORT_DBG(rdata, "Received a ADISC response\n"); | ||
947 | |||
948 | if (rdata->rp_state != RPORT_ST_ADISC) { | ||
949 | FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n", | ||
950 | fc_rport_state(rdata)); | ||
951 | if (IS_ERR(fp)) | ||
952 | goto err; | ||
953 | goto out; | ||
954 | } | ||
955 | |||
956 | if (IS_ERR(fp)) { | ||
957 | fc_rport_error(rdata, fp); | ||
958 | goto err; | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * If address verification failed. Consider us logged out of the rport. | ||
963 | * Since the rport is still in discovery, we want to be | ||
964 | * logged in, so go to PLOGI state. Otherwise, go back to READY. | ||
965 | */ | ||
966 | op = fc_frame_payload_op(fp); | ||
967 | adisc = fc_frame_payload_get(fp, sizeof(*adisc)); | ||
968 | if (op != ELS_LS_ACC || !adisc || | ||
969 | ntoh24(adisc->adisc_port_id) != rdata->ids.port_id || | ||
970 | get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name || | ||
971 | get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) { | ||
972 | FC_RPORT_DBG(rdata, "ADISC error or mismatch\n"); | ||
973 | fc_rport_enter_plogi(rdata); | ||
974 | } else { | ||
975 | FC_RPORT_DBG(rdata, "ADISC OK\n"); | ||
976 | fc_rport_enter_ready(rdata); | ||
977 | } | ||
978 | out: | ||
979 | fc_frame_free(fp); | ||
980 | err: | ||
981 | mutex_unlock(&rdata->rp_mutex); | ||
982 | kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy); | ||
983 | } | ||
984 | |||
985 | /** | ||
986 | * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer | ||
987 | * @rdata: remote port private data | ||
988 | * | ||
989 | * Locking Note: The rport lock is expected to be held before calling | ||
990 | * this routine. | ||
991 | */ | ||
992 | static void fc_rport_enter_adisc(struct fc_rport_priv *rdata) | ||
993 | { | ||
994 | struct fc_lport *lport = rdata->local_port; | ||
995 | struct fc_frame *fp; | ||
996 | |||
997 | FC_RPORT_DBG(rdata, "sending ADISC from %s state\n", | ||
998 | fc_rport_state(rdata)); | ||
999 | |||
1000 | fc_rport_state_enter(rdata, RPORT_ST_ADISC); | ||
1001 | |||
1002 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc)); | ||
1003 | if (!fp) { | ||
1004 | fc_rport_error_retry(rdata, fp); | ||
1005 | return; | ||
1006 | } | ||
1007 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC, | ||
1008 | fc_rport_adisc_resp, rdata, lport->e_d_tov)) | ||
1009 | fc_rport_error_retry(rdata, fp); | ||
1010 | else | ||
1011 | kref_get(&rdata->kref); | ||
1012 | } | ||
1013 | |||
1014 | /** | ||
910 | * fc_rport_recv_els_req() - handle a validated ELS request. | 1015 | * fc_rport_recv_els_req() - handle a validated ELS request. |
911 | * @lport: Fibre Channel local port | 1016 | * @lport: Fibre Channel local port |
912 | * @sp: current sequence in the PLOGI exchange | 1017 | * @sp: current sequence in the PLOGI exchange |
@@ -943,6 +1048,7 @@ static void fc_rport_recv_els_req(struct fc_lport *lport, | |||
943 | case RPORT_ST_PRLI: | 1048 | case RPORT_ST_PRLI: |
944 | case RPORT_ST_RTV: | 1049 | case RPORT_ST_RTV: |
945 | case RPORT_ST_READY: | 1050 | case RPORT_ST_READY: |
1051 | case RPORT_ST_ADISC: | ||
946 | break; | 1052 | break; |
947 | default: | 1053 | default: |
948 | mutex_unlock(&rdata->rp_mutex); | 1054 | mutex_unlock(&rdata->rp_mutex); |
@@ -1095,6 +1201,10 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport, | |||
1095 | break; | 1201 | break; |
1096 | case RPORT_ST_PRLI: | 1202 | case RPORT_ST_PRLI: |
1097 | case RPORT_ST_READY: | 1203 | case RPORT_ST_READY: |
1204 | case RPORT_ST_ADISC: | ||
1205 | FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d " | ||
1206 | "- ignored for now\n", rdata->rp_state); | ||
1207 | /* XXX TBD - should reset */ | ||
1098 | break; | 1208 | break; |
1099 | case RPORT_ST_DELETE: | 1209 | case RPORT_ST_DELETE: |
1100 | default: | 1210 | default: |
@@ -1178,6 +1288,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, | |||
1178 | case RPORT_ST_PRLI: | 1288 | case RPORT_ST_PRLI: |
1179 | case RPORT_ST_RTV: | 1289 | case RPORT_ST_RTV: |
1180 | case RPORT_ST_READY: | 1290 | case RPORT_ST_READY: |
1291 | case RPORT_ST_ADISC: | ||
1181 | reason = ELS_RJT_NONE; | 1292 | reason = ELS_RJT_NONE; |
1182 | break; | 1293 | break; |
1183 | default: | 1294 | default: |
@@ -1283,6 +1394,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, | |||
1283 | fc_rport_enter_ready(rdata); | 1394 | fc_rport_enter_ready(rdata); |
1284 | break; | 1395 | break; |
1285 | case RPORT_ST_READY: | 1396 | case RPORT_ST_READY: |
1397 | case RPORT_ST_ADISC: | ||
1286 | break; | 1398 | break; |
1287 | default: | 1399 | default: |
1288 | break; | 1400 | break; |
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index 24bf764f9884..c5ee6bb79e05 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h | |||
@@ -57,6 +57,23 @@ static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl, | |||
57 | } | 57 | } |
58 | 58 | ||
59 | /** | 59 | /** |
60 | * fc_adisc_fill() - Fill in adisc request frame | ||
61 | * @lport: local port. | ||
62 | * @fp: fc frame where payload will be placed. | ||
63 | */ | ||
64 | static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp) | ||
65 | { | ||
66 | struct fc_els_adisc *adisc; | ||
67 | |||
68 | adisc = fc_frame_payload_get(fp, sizeof(*adisc)); | ||
69 | memset(adisc, 0, sizeof(*adisc)); | ||
70 | adisc->adisc_cmd = ELS_ADISC; | ||
71 | put_unaligned_be64(lport->wwpn, &adisc->adisc_wwpn); | ||
72 | put_unaligned_be64(lport->wwnn, &adisc->adisc_wwnn); | ||
73 | hton24(adisc->adisc_port_id, fc_host_port_id(lport->host)); | ||
74 | } | ||
75 | |||
76 | /** | ||
60 | * fc_ct_hdr_fill- fills ct header and reset ct payload | 77 | * fc_ct_hdr_fill- fills ct header and reset ct payload |
61 | * returns pointer to ct request. | 78 | * returns pointer to ct request. |
62 | */ | 79 | */ |
@@ -255,6 +272,10 @@ static inline int fc_els_fill(struct fc_lport *lport, | |||
255 | enum fc_rctl *r_ctl, enum fc_fh_type *fh_type) | 272 | enum fc_rctl *r_ctl, enum fc_fh_type *fh_type) |
256 | { | 273 | { |
257 | switch (op) { | 274 | switch (op) { |
275 | case ELS_ADISC: | ||
276 | fc_adisc_fill(lport, fp); | ||
277 | break; | ||
278 | |||
258 | case ELS_PLOGI: | 279 | case ELS_PLOGI: |
259 | fc_plogi_fill(lport, fp, ELS_PLOGI); | 280 | fc_plogi_fill(lport, fp, ELS_PLOGI); |
260 | break; | 281 | break; |
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index e18e5ce5af51..65dc9aacbf70 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h | |||
@@ -143,6 +143,7 @@ enum fc_rport_state { | |||
143 | RPORT_ST_RTV, /* waiting for RTV completion */ | 143 | RPORT_ST_RTV, /* waiting for RTV completion */ |
144 | RPORT_ST_READY, /* ready for use */ | 144 | RPORT_ST_READY, /* ready for use */ |
145 | RPORT_ST_LOGO, /* port logout sent */ | 145 | RPORT_ST_LOGO, /* port logout sent */ |
146 | RPORT_ST_ADISC, /* Discover Address sent */ | ||
146 | RPORT_ST_DELETE, /* port being deleted */ | 147 | RPORT_ST_DELETE, /* port being deleted */ |
147 | }; | 148 | }; |
148 | 149 | ||