1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2007 Intel Corporation. All rights reserved. |
4 | * |
5 | * Maintained at www.Open-FCoE.org |
6 | */ |
7 | |
8 | /* |
9 | * PORT LOCKING NOTES |
10 | * |
11 | * These comments only apply to the 'port code' which consists of the lport, |
12 | * disc and rport blocks. |
13 | * |
14 | * MOTIVATION |
15 | * |
16 | * The lport, disc and rport blocks all have mutexes that are used to protect |
17 | * those objects. The main motivation for these locks is to prevent from |
18 | * having an lport reset just before we send a frame. In that scenario the |
19 | * lport's FID would get set to zero and then we'd send a frame with an |
20 | * invalid SID. We also need to ensure that states don't change unexpectedly |
21 | * while processing another state. |
22 | * |
23 | * HIERARCHY |
24 | * |
25 | * The following hierarchy defines the locking rules. A greater lock |
26 | * may be held before acquiring a lesser lock, but a lesser lock should never |
27 | * be held while attempting to acquire a greater lock. Here is the hierarchy- |
28 | * |
29 | * lport > disc, lport > rport, disc > rport |
30 | * |
31 | * CALLBACKS |
32 | * |
33 | * The callbacks cause complications with this scheme. There is a callback |
34 | * from the rport (to either lport or disc) and a callback from disc |
35 | * (to the lport). |
36 | * |
37 | * As rports exit the rport state machine a callback is made to the owner of |
38 | * the rport to notify success or failure. Since the callback is likely to |
39 | * cause the lport or disc to grab its lock we cannot hold the rport lock |
40 | * while making the callback. To ensure that the rport is not free'd while |
41 | * processing the callback the rport callbacks are serialized through a |
42 | * single-threaded workqueue. An rport would never be free'd while in a |
43 | * callback handler because no other rport work in this queue can be executed |
44 | * at the same time. |
45 | * |
46 | * When discovery succeeds or fails a callback is made to the lport as |
47 | * notification. Currently, successful discovery causes the lport to take no |
48 | * action. A failure will cause the lport to reset. There is likely a circular |
49 | * locking problem with this implementation. |
50 | */ |
51 | |
52 | /* |
53 | * LPORT LOCKING |
54 | * |
55 | * The critical sections protected by the lport's mutex are quite broad and |
56 | * may be improved upon in the future. The lport code and its locking doesn't |
57 | * influence the I/O path, so excessive locking doesn't penalize I/O |
58 | * performance. |
59 | * |
60 | * The strategy is to lock whenever processing a request or response. Note |
61 | * that every _enter_* function corresponds to a state change. They generally |
62 | * change the lports state and then send a request out on the wire. We lock |
63 | * before calling any of these functions to protect that state change. This |
64 | * means that the entry points into the lport block manage the locks while |
65 | * the state machine can transition between states (i.e. _enter_* functions) |
66 | * while always staying protected. |
67 | * |
68 | * When handling responses we also hold the lport mutex broadly. When the |
69 | * lport receives the response frame it locks the mutex and then calls the |
70 | * appropriate handler for the particuar response. Generally a response will |
71 | * trigger a state change and so the lock must already be held. |
72 | * |
73 | * Retries also have to consider the locking. The retries occur from a work |
74 | * context and the work function will lock the lport and then retry the state |
75 | * (i.e. _enter_* function). |
76 | */ |
77 | |
78 | #include <linux/timer.h> |
79 | #include <linux/delay.h> |
80 | #include <linux/module.h> |
81 | #include <linux/slab.h> |
82 | #include <asm/unaligned.h> |
83 | |
84 | #include <scsi/fc/fc_gs.h> |
85 | |
86 | #include <scsi/libfc.h> |
87 | #include <linux/scatterlist.h> |
88 | |
89 | #include "fc_encode.h" |
90 | #include "fc_libfc.h" |
91 | |
92 | /* Fabric IDs to use for point-to-point mode, chosen on whims. */ |
93 | #define FC_LOCAL_PTP_FID_LO 0x010101 |
94 | #define FC_LOCAL_PTP_FID_HI 0x010102 |
95 | |
96 | #define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/ |
97 | #define MAX_CT_PAYLOAD 2048 |
98 | #define DISCOVERED_PORTS 4 |
99 | #define NUMBER_OF_PORTS 1 |
100 | |
101 | static void fc_lport_error(struct fc_lport *, struct fc_frame *); |
102 | |
103 | static void fc_lport_enter_reset(struct fc_lport *); |
104 | static void fc_lport_enter_flogi(struct fc_lport *); |
105 | static void fc_lport_enter_dns(struct fc_lport *); |
106 | static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state); |
107 | static void fc_lport_enter_scr(struct fc_lport *); |
108 | static void fc_lport_enter_ready(struct fc_lport *); |
109 | static void fc_lport_enter_logo(struct fc_lport *); |
110 | static void fc_lport_enter_fdmi(struct fc_lport *lport); |
111 | static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state); |
112 | |
113 | static const char *fc_lport_state_names[] = { |
114 | [LPORT_ST_DISABLED] = "disabled" , |
115 | [LPORT_ST_FLOGI] = "FLOGI" , |
116 | [LPORT_ST_DNS] = "dNS" , |
117 | [LPORT_ST_RNN_ID] = "RNN_ID" , |
118 | [LPORT_ST_RSNN_NN] = "RSNN_NN" , |
119 | [LPORT_ST_RSPN_ID] = "RSPN_ID" , |
120 | [LPORT_ST_RFT_ID] = "RFT_ID" , |
121 | [LPORT_ST_RFF_ID] = "RFF_ID" , |
122 | [LPORT_ST_FDMI] = "FDMI" , |
123 | [LPORT_ST_RHBA] = "RHBA" , |
124 | [LPORT_ST_RPA] = "RPA" , |
125 | [LPORT_ST_DHBA] = "DHBA" , |
126 | [LPORT_ST_DPRT] = "DPRT" , |
127 | [LPORT_ST_SCR] = "SCR" , |
128 | [LPORT_ST_READY] = "Ready" , |
129 | [LPORT_ST_LOGO] = "LOGO" , |
130 | [LPORT_ST_RESET] = "reset" , |
131 | }; |
132 | |
133 | /** |
134 | * struct fc_bsg_info - FC Passthrough managemet structure |
135 | * @job: The passthrough job |
136 | * @lport: The local port to pass through a command |
137 | * @rsp_code: The expected response code |
138 | * @sg: job->reply_payload.sg_list |
139 | * @nents: job->reply_payload.sg_cnt |
140 | * @offset: The offset into the response data |
141 | */ |
142 | struct fc_bsg_info { |
143 | struct bsg_job *job; |
144 | struct fc_lport *lport; |
145 | u16 rsp_code; |
146 | struct scatterlist *sg; |
147 | u32 nents; |
148 | size_t offset; |
149 | }; |
150 | |
151 | /** |
152 | * fc_frame_drop() - Dummy frame handler |
153 | * @lport: The local port the frame was received on |
154 | * @fp: The received frame |
155 | */ |
156 | static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) |
157 | { |
158 | fc_frame_free(fp); |
159 | return 0; |
160 | } |
161 | |
162 | /** |
163 | * fc_lport_rport_callback() - Event handler for rport events |
164 | * @lport: The lport which is receiving the event |
165 | * @rdata: private remote port data |
166 | * @event: The event that occurred |
167 | * |
168 | * Locking Note: The rport lock should not be held when calling |
169 | * this function. |
170 | */ |
171 | static void fc_lport_rport_callback(struct fc_lport *lport, |
172 | struct fc_rport_priv *rdata, |
173 | enum fc_rport_event event) |
174 | { |
175 | FC_LPORT_DBG(lport, "Received a %d event for port (%6.6x)\n" , event, |
176 | rdata->ids.port_id); |
177 | |
178 | mutex_lock(&lport->lp_mutex); |
179 | switch (event) { |
180 | case RPORT_EV_READY: |
181 | if (lport->state == LPORT_ST_DNS) { |
182 | lport->dns_rdata = rdata; |
183 | fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); |
184 | } else if (lport->state == LPORT_ST_FDMI) { |
185 | lport->ms_rdata = rdata; |
186 | fc_lport_enter_ms(lport, LPORT_ST_DHBA); |
187 | } else { |
188 | FC_LPORT_DBG(lport, "Received an READY event " |
189 | "on port (%6.6x) for the directory " |
190 | "server, but the lport is not " |
191 | "in the DNS or FDMI state, it's in the " |
192 | "%d state" , rdata->ids.port_id, |
193 | lport->state); |
194 | fc_rport_logoff(rdata); |
195 | } |
196 | break; |
197 | case RPORT_EV_LOGO: |
198 | case RPORT_EV_FAILED: |
199 | case RPORT_EV_STOP: |
200 | if (rdata->ids.port_id == FC_FID_DIR_SERV) |
201 | lport->dns_rdata = NULL; |
202 | else if (rdata->ids.port_id == FC_FID_MGMT_SERV) |
203 | lport->ms_rdata = NULL; |
204 | break; |
205 | case RPORT_EV_NONE: |
206 | break; |
207 | } |
208 | mutex_unlock(lock: &lport->lp_mutex); |
209 | } |
210 | |
211 | /** |
212 | * fc_lport_state() - Return a string which represents the lport's state |
213 | * @lport: The lport whose state is to converted to a string |
214 | */ |
215 | static const char *fc_lport_state(struct fc_lport *lport) |
216 | { |
217 | const char *cp; |
218 | |
219 | cp = fc_lport_state_names[lport->state]; |
220 | if (!cp) |
221 | cp = "unknown" ; |
222 | return cp; |
223 | } |
224 | |
225 | /** |
226 | * fc_lport_ptp_setup() - Create an rport for point-to-point mode |
227 | * @lport: The lport to attach the ptp rport to |
228 | * @remote_fid: The FID of the ptp rport |
229 | * @remote_wwpn: The WWPN of the ptp rport |
230 | * @remote_wwnn: The WWNN of the ptp rport |
231 | */ |
232 | static void fc_lport_ptp_setup(struct fc_lport *lport, |
233 | u32 remote_fid, u64 remote_wwpn, |
234 | u64 remote_wwnn) |
235 | { |
236 | lockdep_assert_held(&lport->lp_mutex); |
237 | |
238 | if (lport->ptp_rdata) { |
239 | fc_rport_logoff(rdata: lport->ptp_rdata); |
240 | kref_put(kref: &lport->ptp_rdata->kref, release: fc_rport_destroy); |
241 | } |
242 | mutex_lock(&lport->disc.disc_mutex); |
243 | lport->ptp_rdata = fc_rport_create(lport, remote_fid); |
244 | if (!lport->ptp_rdata) { |
245 | printk(KERN_WARNING "libfc: Failed to setup lport 0x%x\n" , |
246 | lport->port_id); |
247 | mutex_unlock(lock: &lport->disc.disc_mutex); |
248 | return; |
249 | } |
250 | kref_get(kref: &lport->ptp_rdata->kref); |
251 | lport->ptp_rdata->ids.port_name = remote_wwpn; |
252 | lport->ptp_rdata->ids.node_name = remote_wwnn; |
253 | mutex_unlock(lock: &lport->disc.disc_mutex); |
254 | |
255 | fc_rport_login(rdata: lport->ptp_rdata); |
256 | |
257 | fc_lport_enter_ready(lport); |
258 | } |
259 | |
260 | /** |
261 | * fc_get_host_port_state() - Return the port state of the given Scsi_Host |
262 | * @shost: The SCSI host whose port state is to be determined |
263 | */ |
264 | void fc_get_host_port_state(struct Scsi_Host *shost) |
265 | { |
266 | struct fc_lport *lport = shost_priv(shost); |
267 | |
268 | mutex_lock(&lport->lp_mutex); |
269 | if (!lport->link_up) |
270 | fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN; |
271 | else |
272 | switch (lport->state) { |
273 | case LPORT_ST_READY: |
274 | fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; |
275 | break; |
276 | default: |
277 | fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; |
278 | } |
279 | mutex_unlock(lock: &lport->lp_mutex); |
280 | } |
281 | EXPORT_SYMBOL(fc_get_host_port_state); |
282 | |
283 | /** |
284 | * fc_get_host_speed() - Return the speed of the given Scsi_Host |
285 | * @shost: The SCSI host whose port speed is to be determined |
286 | */ |
287 | void fc_get_host_speed(struct Scsi_Host *shost) |
288 | { |
289 | struct fc_lport *lport = shost_priv(shost); |
290 | |
291 | fc_host_speed(shost) = lport->link_speed; |
292 | } |
293 | EXPORT_SYMBOL(fc_get_host_speed); |
294 | |
295 | /** |
296 | * fc_get_host_stats() - Return the Scsi_Host's statistics |
297 | * @shost: The SCSI host whose statistics are to be returned |
298 | */ |
299 | struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) |
300 | { |
301 | struct fc_host_statistics *fc_stats; |
302 | struct fc_lport *lport = shost_priv(shost); |
303 | unsigned int cpu; |
304 | u64 fcp_in_bytes = 0; |
305 | u64 fcp_out_bytes = 0; |
306 | |
307 | fc_stats = &lport->host_stats; |
308 | memset(fc_stats, 0, sizeof(struct fc_host_statistics)); |
309 | |
310 | fc_stats->seconds_since_last_reset = (jiffies - lport->boot_time) / HZ; |
311 | |
312 | for_each_possible_cpu(cpu) { |
313 | struct fc_stats *stats; |
314 | |
315 | stats = per_cpu_ptr(lport->stats, cpu); |
316 | |
317 | fc_stats->tx_frames += READ_ONCE(stats->TxFrames); |
318 | fc_stats->tx_words += READ_ONCE(stats->TxWords); |
319 | fc_stats->rx_frames += READ_ONCE(stats->RxFrames); |
320 | fc_stats->rx_words += READ_ONCE(stats->RxWords); |
321 | fc_stats->error_frames += READ_ONCE(stats->ErrorFrames); |
322 | fc_stats->invalid_crc_count += READ_ONCE(stats->InvalidCRCCount); |
323 | fc_stats->fcp_input_requests += READ_ONCE(stats->InputRequests); |
324 | fc_stats->fcp_output_requests += READ_ONCE(stats->OutputRequests); |
325 | fc_stats->fcp_control_requests += READ_ONCE(stats->ControlRequests); |
326 | fcp_in_bytes += READ_ONCE(stats->InputBytes); |
327 | fcp_out_bytes += READ_ONCE(stats->OutputBytes); |
328 | fc_stats->fcp_packet_alloc_failures += READ_ONCE(stats->FcpPktAllocFails); |
329 | fc_stats->fcp_packet_aborts += READ_ONCE(stats->FcpPktAborts); |
330 | fc_stats->fcp_frame_alloc_failures += READ_ONCE(stats->FcpFrameAllocFails); |
331 | fc_stats->link_failure_count += READ_ONCE(stats->LinkFailureCount); |
332 | } |
333 | fc_stats->fcp_input_megabytes = div_u64(dividend: fcp_in_bytes, divisor: 1000000); |
334 | fc_stats->fcp_output_megabytes = div_u64(dividend: fcp_out_bytes, divisor: 1000000); |
335 | fc_stats->lip_count = -1; |
336 | fc_stats->nos_count = -1; |
337 | fc_stats->loss_of_sync_count = -1; |
338 | fc_stats->loss_of_signal_count = -1; |
339 | fc_stats->prim_seq_protocol_err_count = -1; |
340 | fc_stats->dumped_frames = -1; |
341 | |
342 | /* update exches stats */ |
343 | fc_exch_update_stats(lport); |
344 | |
345 | return fc_stats; |
346 | } |
347 | EXPORT_SYMBOL(fc_get_host_stats); |
348 | |
349 | /** |
350 | * fc_lport_flogi_fill() - Fill in FLOGI command for request |
351 | * @lport: The local port the FLOGI is for |
352 | * @flogi: The FLOGI command |
353 | * @op: The opcode |
354 | */ |
355 | static void fc_lport_flogi_fill(struct fc_lport *lport, |
356 | struct fc_els_flogi *flogi, |
357 | unsigned int op) |
358 | { |
359 | struct fc_els_csp *sp; |
360 | struct fc_els_cssp *cp; |
361 | |
362 | memset(flogi, 0, sizeof(*flogi)); |
363 | flogi->fl_cmd = (u8) op; |
364 | put_unaligned_be64(val: lport->wwpn, p: &flogi->fl_wwpn); |
365 | put_unaligned_be64(val: lport->wwnn, p: &flogi->fl_wwnn); |
366 | sp = &flogi->fl_csp; |
367 | sp->sp_hi_ver = 0x20; |
368 | sp->sp_lo_ver = 0x20; |
369 | sp->sp_bb_cred = htons(10); /* this gets set by gateway */ |
370 | sp->sp_bb_data = htons((u16) lport->mfs); |
371 | cp = &flogi->fl_cssp[3 - 1]; /* class 3 parameters */ |
372 | cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ); |
373 | if (op != ELS_FLOGI) { |
374 | sp->sp_features = htons(FC_SP_FT_CIRO); |
375 | sp->sp_tot_seq = htons(255); /* seq. we accept */ |
376 | sp->sp_rel_off = htons(0x1f); |
377 | sp->sp_e_d_tov = htonl(lport->e_d_tov); |
378 | |
379 | cp->cp_rdfs = htons((u16) lport->mfs); |
380 | cp->cp_con_seq = htons(255); |
381 | cp->cp_open_seq = 1; |
382 | } |
383 | } |
384 | |
385 | /** |
386 | * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port |
387 | * @lport: The local port to add a new FC-4 type to |
388 | * @type: The new FC-4 type |
389 | */ |
390 | static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) |
391 | { |
392 | __be32 *mp; |
393 | |
394 | mp = &lport->fcts.ff_type_map[type / FC_NS_BPW]; |
395 | *mp = htonl(ntohl(*mp) | 1UL << (type % FC_NS_BPW)); |
396 | } |
397 | |
398 | /** |
399 | * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report. |
400 | * @lport: Fibre Channel local port receiving the RLIR |
401 | * @fp: The RLIR request frame |
402 | */ |
403 | static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp) |
404 | { |
405 | lockdep_assert_held(&lport->lp_mutex); |
406 | |
407 | FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n" , |
408 | fc_lport_state(lport)); |
409 | |
410 | fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL); |
411 | fc_frame_free(fp); |
412 | } |
413 | |
414 | /** |
415 | * fc_lport_recv_echo_req() - Handle received ECHO request |
416 | * @lport: The local port receiving the ECHO |
417 | * @in_fp: ECHO request frame |
418 | */ |
419 | static void fc_lport_recv_echo_req(struct fc_lport *lport, |
420 | struct fc_frame *in_fp) |
421 | { |
422 | struct fc_frame *fp; |
423 | unsigned int len; |
424 | void *pp; |
425 | void *dp; |
426 | |
427 | lockdep_assert_held(&lport->lp_mutex); |
428 | |
429 | FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n" , |
430 | fc_lport_state(lport)); |
431 | |
432 | len = fr_len(in_fp) - sizeof(struct fc_frame_header); |
433 | pp = fc_frame_payload_get(fp: in_fp, len); |
434 | |
435 | if (len < sizeof(__be32)) |
436 | len = sizeof(__be32); |
437 | |
438 | fp = fc_frame_alloc(dev: lport, len); |
439 | if (fp) { |
440 | dp = fc_frame_payload_get(fp, len); |
441 | memcpy(dp, pp, len); |
442 | *((__be32 *)dp) = htonl(ELS_LS_ACC << 24); |
443 | fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
444 | lport->tt.frame_send(lport, fp); |
445 | } |
446 | fc_frame_free(fp: in_fp); |
447 | } |
448 | |
449 | /** |
450 | * fc_lport_recv_rnid_req() - Handle received Request Node ID data request |
451 | * @lport: The local port receiving the RNID |
452 | * @in_fp: The RNID request frame |
453 | */ |
454 | static void fc_lport_recv_rnid_req(struct fc_lport *lport, |
455 | struct fc_frame *in_fp) |
456 | { |
457 | struct fc_frame *fp; |
458 | struct fc_els_rnid *req; |
459 | struct { |
460 | struct fc_els_rnid_resp rnid; |
461 | struct fc_els_rnid_cid cid; |
462 | struct fc_els_rnid_gen gen; |
463 | } *rp; |
464 | struct fc_seq_els_data rjt_data; |
465 | u8 fmt; |
466 | size_t len; |
467 | |
468 | lockdep_assert_held(&lport->lp_mutex); |
469 | |
470 | FC_LPORT_DBG(lport, "Received RNID request while in state %s\n" , |
471 | fc_lport_state(lport)); |
472 | |
473 | req = fc_frame_payload_get(fp: in_fp, len: sizeof(*req)); |
474 | if (!req) { |
475 | rjt_data.reason = ELS_RJT_LOGIC; |
476 | rjt_data.explan = ELS_EXPL_NONE; |
477 | fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); |
478 | } else { |
479 | fmt = req->rnid_fmt; |
480 | len = sizeof(*rp); |
481 | if (fmt != ELS_RNIDF_GEN || |
482 | ntohl(lport->rnid_gen.rnid_atype) == 0) { |
483 | fmt = ELS_RNIDF_NONE; /* nothing to provide */ |
484 | len -= sizeof(rp->gen); |
485 | } |
486 | fp = fc_frame_alloc(dev: lport, len); |
487 | if (fp) { |
488 | rp = fc_frame_payload_get(fp, len); |
489 | memset(rp, 0, len); |
490 | rp->rnid.rnid_cmd = ELS_LS_ACC; |
491 | rp->rnid.rnid_fmt = fmt; |
492 | rp->rnid.rnid_cid_len = sizeof(rp->cid); |
493 | rp->cid.rnid_wwpn = htonll(lport->wwpn); |
494 | rp->cid.rnid_wwnn = htonll(lport->wwnn); |
495 | if (fmt == ELS_RNIDF_GEN) { |
496 | rp->rnid.rnid_sid_len = sizeof(rp->gen); |
497 | memcpy(&rp->gen, &lport->rnid_gen, |
498 | sizeof(rp->gen)); |
499 | } |
500 | fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
501 | lport->tt.frame_send(lport, fp); |
502 | } |
503 | } |
504 | fc_frame_free(fp: in_fp); |
505 | } |
506 | |
507 | /** |
508 | * fc_lport_recv_logo_req() - Handle received fabric LOGO request |
509 | * @lport: The local port receiving the LOGO |
510 | * @fp: The LOGO request frame |
511 | */ |
512 | static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) |
513 | { |
514 | lockdep_assert_held(&lport->lp_mutex); |
515 | |
516 | fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL); |
517 | fc_lport_enter_reset(lport); |
518 | fc_frame_free(fp); |
519 | } |
520 | |
521 | /** |
522 | * fc_fabric_login() - Start the lport state machine |
523 | * @lport: The local port that should log into the fabric |
524 | * |
525 | * Locking Note: This function should not be called |
526 | * with the lport lock held. |
527 | */ |
528 | int fc_fabric_login(struct fc_lport *lport) |
529 | { |
530 | int rc = -1; |
531 | |
532 | mutex_lock(&lport->lp_mutex); |
533 | if (lport->state == LPORT_ST_DISABLED || |
534 | lport->state == LPORT_ST_LOGO) { |
535 | fc_lport_state_enter(lport, state: LPORT_ST_RESET); |
536 | fc_lport_enter_reset(lport); |
537 | rc = 0; |
538 | } |
539 | mutex_unlock(lock: &lport->lp_mutex); |
540 | |
541 | return rc; |
542 | } |
543 | EXPORT_SYMBOL(fc_fabric_login); |
544 | |
545 | /** |
546 | * __fc_linkup() - Handler for transport linkup events |
547 | * @lport: The lport whose link is up |
548 | */ |
549 | void __fc_linkup(struct fc_lport *lport) |
550 | { |
551 | lockdep_assert_held(&lport->lp_mutex); |
552 | |
553 | if (!lport->link_up) { |
554 | lport->link_up = 1; |
555 | |
556 | if (lport->state == LPORT_ST_RESET) |
557 | fc_lport_enter_flogi(lport); |
558 | } |
559 | } |
560 | |
561 | /** |
562 | * fc_linkup() - Handler for transport linkup events |
563 | * @lport: The local port whose link is up |
564 | */ |
565 | void fc_linkup(struct fc_lport *lport) |
566 | { |
567 | printk(KERN_INFO "host%d: libfc: Link up on port (%6.6x)\n" , |
568 | lport->host->host_no, lport->port_id); |
569 | |
570 | mutex_lock(&lport->lp_mutex); |
571 | __fc_linkup(lport); |
572 | mutex_unlock(lock: &lport->lp_mutex); |
573 | } |
574 | EXPORT_SYMBOL(fc_linkup); |
575 | |
576 | /** |
577 | * __fc_linkdown() - Handler for transport linkdown events |
578 | * @lport: The lport whose link is down |
579 | */ |
580 | void __fc_linkdown(struct fc_lport *lport) |
581 | { |
582 | lockdep_assert_held(&lport->lp_mutex); |
583 | |
584 | if (lport->link_up) { |
585 | lport->link_up = 0; |
586 | fc_lport_enter_reset(lport); |
587 | lport->tt.fcp_cleanup(lport); |
588 | } |
589 | } |
590 | |
591 | /** |
592 | * fc_linkdown() - Handler for transport linkdown events |
593 | * @lport: The local port whose link is down |
594 | */ |
595 | void fc_linkdown(struct fc_lport *lport) |
596 | { |
597 | printk(KERN_INFO "host%d: libfc: Link down on port (%6.6x)\n" , |
598 | lport->host->host_no, lport->port_id); |
599 | |
600 | mutex_lock(&lport->lp_mutex); |
601 | __fc_linkdown(lport); |
602 | mutex_unlock(lock: &lport->lp_mutex); |
603 | } |
604 | EXPORT_SYMBOL(fc_linkdown); |
605 | |
606 | /** |
607 | * fc_fabric_logoff() - Logout of the fabric |
608 | * @lport: The local port to logoff the fabric |
609 | * |
610 | * Return value: |
611 | * 0 for success, -1 for failure |
612 | */ |
613 | int fc_fabric_logoff(struct fc_lport *lport) |
614 | { |
615 | lport->tt.disc_stop_final(lport); |
616 | mutex_lock(&lport->lp_mutex); |
617 | if (lport->dns_rdata) |
618 | fc_rport_logoff(rdata: lport->dns_rdata); |
619 | mutex_unlock(lock: &lport->lp_mutex); |
620 | fc_rport_flush_queue(); |
621 | mutex_lock(&lport->lp_mutex); |
622 | fc_lport_enter_logo(lport); |
623 | mutex_unlock(lock: &lport->lp_mutex); |
624 | cancel_delayed_work_sync(dwork: &lport->retry_work); |
625 | return 0; |
626 | } |
627 | EXPORT_SYMBOL(fc_fabric_logoff); |
628 | |
629 | /** |
630 | * fc_lport_destroy() - Unregister a fc_lport |
631 | * @lport: The local port to unregister |
632 | * |
633 | * Note: |
634 | * exit routine for fc_lport instance |
635 | * clean-up all the allocated memory |
636 | * and free up other system resources. |
637 | * |
638 | */ |
639 | int fc_lport_destroy(struct fc_lport *lport) |
640 | { |
641 | mutex_lock(&lport->lp_mutex); |
642 | lport->state = LPORT_ST_DISABLED; |
643 | lport->link_up = 0; |
644 | lport->tt.frame_send = fc_frame_drop; |
645 | mutex_unlock(lock: &lport->lp_mutex); |
646 | |
647 | lport->tt.fcp_abort_io(lport); |
648 | lport->tt.disc_stop_final(lport); |
649 | lport->tt.exch_mgr_reset(lport, 0, 0); |
650 | cancel_delayed_work_sync(dwork: &lport->retry_work); |
651 | fc_fc4_del_lport(lport); |
652 | return 0; |
653 | } |
654 | EXPORT_SYMBOL(fc_lport_destroy); |
655 | |
656 | /** |
657 | * fc_set_mfs() - Set the maximum frame size for a local port |
658 | * @lport: The local port to set the MFS for |
659 | * @mfs: The new MFS |
660 | */ |
661 | int fc_set_mfs(struct fc_lport *lport, u32 mfs) |
662 | { |
663 | unsigned int old_mfs; |
664 | int rc = -EINVAL; |
665 | |
666 | mutex_lock(&lport->lp_mutex); |
667 | |
668 | old_mfs = lport->mfs; |
669 | |
670 | if (mfs >= FC_MIN_MAX_FRAME) { |
671 | mfs &= ~3; |
672 | if (mfs > FC_MAX_FRAME) |
673 | mfs = FC_MAX_FRAME; |
674 | mfs -= sizeof(struct fc_frame_header); |
675 | lport->mfs = mfs; |
676 | rc = 0; |
677 | } |
678 | |
679 | if (!rc && mfs < old_mfs) |
680 | fc_lport_enter_reset(lport); |
681 | |
682 | mutex_unlock(lock: &lport->lp_mutex); |
683 | |
684 | return rc; |
685 | } |
686 | EXPORT_SYMBOL(fc_set_mfs); |
687 | |
688 | /** |
689 | * fc_lport_disc_callback() - Callback for discovery events |
690 | * @lport: The local port receiving the event |
691 | * @event: The discovery event |
692 | */ |
693 | static void fc_lport_disc_callback(struct fc_lport *lport, |
694 | enum fc_disc_event event) |
695 | { |
696 | switch (event) { |
697 | case DISC_EV_SUCCESS: |
698 | FC_LPORT_DBG(lport, "Discovery succeeded\n" ); |
699 | break; |
700 | case DISC_EV_FAILED: |
701 | printk(KERN_ERR "host%d: libfc: " |
702 | "Discovery failed for port (%6.6x)\n" , |
703 | lport->host->host_no, lport->port_id); |
704 | mutex_lock(&lport->lp_mutex); |
705 | fc_lport_enter_reset(lport); |
706 | mutex_unlock(lock: &lport->lp_mutex); |
707 | break; |
708 | case DISC_EV_NONE: |
709 | WARN_ON(1); |
710 | break; |
711 | } |
712 | } |
713 | |
714 | /** |
715 | * fc_lport_enter_ready() - Enter the ready state and start discovery |
716 | * @lport: The local port that is ready |
717 | */ |
718 | static void fc_lport_enter_ready(struct fc_lport *lport) |
719 | { |
720 | lockdep_assert_held(&lport->lp_mutex); |
721 | |
722 | FC_LPORT_DBG(lport, "Entered READY from state %s\n" , |
723 | fc_lport_state(lport)); |
724 | |
725 | fc_lport_state_enter(lport, state: LPORT_ST_READY); |
726 | if (lport->vport) |
727 | fc_vport_set_state(vport: lport->vport, new_state: FC_VPORT_ACTIVE); |
728 | fc_vports_linkchange(lport); |
729 | |
730 | if (!lport->ptp_rdata) |
731 | lport->tt.disc_start(fc_lport_disc_callback, lport); |
732 | } |
733 | |
734 | /** |
735 | * fc_lport_set_port_id() - set the local port Port ID |
736 | * @lport: The local port which will have its Port ID set. |
737 | * @port_id: The new port ID. |
738 | * @fp: The frame containing the incoming request, or NULL. |
739 | */ |
740 | static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id, |
741 | struct fc_frame *fp) |
742 | { |
743 | lockdep_assert_held(&lport->lp_mutex); |
744 | |
745 | if (port_id) |
746 | printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n" , |
747 | lport->host->host_no, port_id); |
748 | |
749 | lport->port_id = port_id; |
750 | |
751 | /* Update the fc_host */ |
752 | fc_host_port_id(lport->host) = port_id; |
753 | |
754 | if (lport->tt.lport_set_port_id) |
755 | lport->tt.lport_set_port_id(lport, port_id, fp); |
756 | } |
757 | |
758 | /** |
759 | * fc_lport_set_local_id() - set the local port Port ID for point-to-multipoint |
760 | * @lport: The local port which will have its Port ID set. |
761 | * @port_id: The new port ID. |
762 | * |
763 | * Called by the lower-level driver when transport sets the local port_id. |
764 | * This is used in VN_port to VN_port mode for FCoE, and causes FLOGI and |
765 | * discovery to be skipped. |
766 | */ |
767 | void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id) |
768 | { |
769 | mutex_lock(&lport->lp_mutex); |
770 | |
771 | fc_lport_set_port_id(lport, port_id, NULL); |
772 | |
773 | switch (lport->state) { |
774 | case LPORT_ST_RESET: |
775 | case LPORT_ST_FLOGI: |
776 | if (port_id) |
777 | fc_lport_enter_ready(lport); |
778 | break; |
779 | default: |
780 | break; |
781 | } |
782 | mutex_unlock(lock: &lport->lp_mutex); |
783 | } |
784 | EXPORT_SYMBOL(fc_lport_set_local_id); |
785 | |
786 | /** |
787 | * fc_lport_recv_flogi_req() - Receive a FLOGI request |
788 | * @lport: The local port that received the request |
789 | * @rx_fp: The FLOGI frame |
790 | * |
791 | * A received FLOGI request indicates a point-to-point connection. |
792 | * Accept it with the common service parameters indicating our N port. |
793 | * Set up to do a PLOGI if we have the higher-number WWPN. |
794 | */ |
795 | static void fc_lport_recv_flogi_req(struct fc_lport *lport, |
796 | struct fc_frame *rx_fp) |
797 | { |
798 | struct fc_frame *fp; |
799 | struct fc_frame_header *fh; |
800 | struct fc_els_flogi *flp; |
801 | struct fc_els_flogi *new_flp; |
802 | u64 remote_wwpn; |
803 | u32 remote_fid; |
804 | u32 local_fid; |
805 | |
806 | lockdep_assert_held(&lport->lp_mutex); |
807 | |
808 | FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n" , |
809 | fc_lport_state(lport)); |
810 | |
811 | remote_fid = fc_frame_sid(fp: rx_fp); |
812 | flp = fc_frame_payload_get(fp: rx_fp, len: sizeof(*flp)); |
813 | if (!flp) |
814 | goto out; |
815 | remote_wwpn = get_unaligned_be64(p: &flp->fl_wwpn); |
816 | if (remote_wwpn == lport->wwpn) { |
817 | printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " |
818 | "with same WWPN %16.16llx\n" , |
819 | lport->host->host_no, remote_wwpn); |
820 | goto out; |
821 | } |
822 | FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n" , remote_wwpn); |
823 | |
824 | /* |
825 | * XXX what is the right thing to do for FIDs? |
826 | * The originator might expect our S_ID to be 0xfffffe. |
827 | * But if so, both of us could end up with the same FID. |
828 | */ |
829 | local_fid = FC_LOCAL_PTP_FID_LO; |
830 | if (remote_wwpn < lport->wwpn) { |
831 | local_fid = FC_LOCAL_PTP_FID_HI; |
832 | if (!remote_fid || remote_fid == local_fid) |
833 | remote_fid = FC_LOCAL_PTP_FID_LO; |
834 | } else if (!remote_fid) { |
835 | remote_fid = FC_LOCAL_PTP_FID_HI; |
836 | } |
837 | |
838 | fc_lport_set_port_id(lport, port_id: local_fid, fp: rx_fp); |
839 | |
840 | fp = fc_frame_alloc(dev: lport, len: sizeof(*flp)); |
841 | if (fp) { |
842 | new_flp = fc_frame_payload_get(fp, len: sizeof(*flp)); |
843 | fc_lport_flogi_fill(lport, flogi: new_flp, op: ELS_FLOGI); |
844 | new_flp->fl_cmd = (u8) ELS_LS_ACC; |
845 | |
846 | /* |
847 | * Send the response. If this fails, the originator should |
848 | * repeat the sequence. |
849 | */ |
850 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
851 | fh = fc_frame_header_get(fp); |
852 | hton24(p: fh->fh_s_id, v: local_fid); |
853 | hton24(p: fh->fh_d_id, v: remote_fid); |
854 | lport->tt.frame_send(lport, fp); |
855 | |
856 | } else { |
857 | fc_lport_error(lport, fp); |
858 | } |
859 | fc_lport_ptp_setup(lport, remote_fid, remote_wwpn, |
860 | remote_wwnn: get_unaligned_be64(p: &flp->fl_wwnn)); |
861 | out: |
862 | fc_frame_free(fp: rx_fp); |
863 | } |
864 | |
865 | /** |
866 | * fc_lport_recv_els_req() - The generic lport ELS request handler |
867 | * @lport: The local port that received the request |
868 | * @fp: The request frame |
869 | * |
870 | * This function will see if the lport handles the request or |
871 | * if an rport should handle the request. |
872 | * |
873 | * Locking Note: This function should not be called with the lport |
874 | * lock held because it will grab the lock. |
875 | */ |
876 | static void fc_lport_recv_els_req(struct fc_lport *lport, |
877 | struct fc_frame *fp) |
878 | { |
879 | mutex_lock(&lport->lp_mutex); |
880 | |
881 | /* |
882 | * Handle special ELS cases like FLOGI, LOGO, and |
883 | * RSCN here. These don't require a session. |
884 | * Even if we had a session, it might not be ready. |
885 | */ |
886 | if (!lport->link_up) |
887 | fc_frame_free(fp); |
888 | else { |
889 | /* |
890 | * Check opcode. |
891 | */ |
892 | switch (fc_frame_payload_op(fp)) { |
893 | case ELS_FLOGI: |
894 | if (!lport->point_to_multipoint) |
895 | fc_lport_recv_flogi_req(lport, rx_fp: fp); |
896 | else |
897 | fc_rport_recv_req(lport, fp); |
898 | break; |
899 | case ELS_LOGO: |
900 | if (fc_frame_sid(fp) == FC_FID_FLOGI) |
901 | fc_lport_recv_logo_req(lport, fp); |
902 | else |
903 | fc_rport_recv_req(lport, fp); |
904 | break; |
905 | case ELS_RSCN: |
906 | lport->tt.disc_recv_req(lport, fp); |
907 | break; |
908 | case ELS_ECHO: |
909 | fc_lport_recv_echo_req(lport, in_fp: fp); |
910 | break; |
911 | case ELS_RLIR: |
912 | fc_lport_recv_rlir_req(lport, fp); |
913 | break; |
914 | case ELS_RNID: |
915 | fc_lport_recv_rnid_req(lport, in_fp: fp); |
916 | break; |
917 | default: |
918 | fc_rport_recv_req(lport, fp); |
919 | break; |
920 | } |
921 | } |
922 | mutex_unlock(lock: &lport->lp_mutex); |
923 | } |
924 | |
925 | static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len, |
926 | const struct fc_els_spp *spp_in, |
927 | struct fc_els_spp *spp_out) |
928 | { |
929 | return FC_SPP_RESP_INVL; |
930 | } |
931 | |
932 | struct fc4_prov fc_lport_els_prov = { |
933 | .prli = fc_lport_els_prli, |
934 | .recv = fc_lport_recv_els_req, |
935 | }; |
936 | |
937 | /** |
938 | * fc_lport_recv() - The generic lport request handler |
939 | * @lport: The lport that received the request |
940 | * @fp: The frame the request is in |
941 | * |
942 | * Locking Note: This function should not be called with the lport |
943 | * lock held because it may grab the lock. |
944 | */ |
945 | void fc_lport_recv(struct fc_lport *lport, struct fc_frame *fp) |
946 | { |
947 | struct fc_frame_header *fh = fc_frame_header_get(fp); |
948 | struct fc_seq *sp = fr_seq(fp); |
949 | struct fc4_prov *prov; |
950 | |
951 | /* |
952 | * Use RCU read lock and module_lock to be sure module doesn't |
953 | * deregister and get unloaded while we're calling it. |
954 | * try_module_get() is inlined and accepts a NULL parameter. |
955 | * Only ELSes and FCP target ops should come through here. |
956 | * The locking is unfortunate, and a better scheme is being sought. |
957 | */ |
958 | |
959 | rcu_read_lock(); |
960 | if (fh->fh_type >= FC_FC4_PROV_SIZE) |
961 | goto drop; |
962 | prov = rcu_dereference(fc_passive_prov[fh->fh_type]); |
963 | if (!prov || !try_module_get(module: prov->module)) |
964 | goto drop; |
965 | rcu_read_unlock(); |
966 | prov->recv(lport, fp); |
967 | module_put(module: prov->module); |
968 | return; |
969 | drop: |
970 | rcu_read_unlock(); |
971 | FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n" , fh->fh_type); |
972 | fc_frame_free(fp); |
973 | if (sp) |
974 | fc_exch_done(sp); |
975 | } |
976 | EXPORT_SYMBOL(fc_lport_recv); |
977 | |
978 | /** |
979 | * fc_lport_reset() - Reset a local port |
980 | * @lport: The local port which should be reset |
981 | * |
982 | * Locking Note: This functions should not be called with the |
983 | * lport lock held. |
984 | */ |
985 | int fc_lport_reset(struct fc_lport *lport) |
986 | { |
987 | cancel_delayed_work_sync(dwork: &lport->retry_work); |
988 | mutex_lock(&lport->lp_mutex); |
989 | fc_lport_enter_reset(lport); |
990 | mutex_unlock(lock: &lport->lp_mutex); |
991 | return 0; |
992 | } |
993 | EXPORT_SYMBOL(fc_lport_reset); |
994 | |
995 | /** |
996 | * fc_lport_reset_locked() - Reset the local port w/ the lport lock held |
997 | * @lport: The local port to be reset |
998 | */ |
999 | static void fc_lport_reset_locked(struct fc_lport *lport) |
1000 | { |
1001 | lockdep_assert_held(&lport->lp_mutex); |
1002 | |
1003 | if (lport->dns_rdata) { |
1004 | fc_rport_logoff(rdata: lport->dns_rdata); |
1005 | lport->dns_rdata = NULL; |
1006 | } |
1007 | |
1008 | if (lport->ptp_rdata) { |
1009 | fc_rport_logoff(rdata: lport->ptp_rdata); |
1010 | kref_put(kref: &lport->ptp_rdata->kref, release: fc_rport_destroy); |
1011 | lport->ptp_rdata = NULL; |
1012 | } |
1013 | |
1014 | lport->tt.disc_stop(lport); |
1015 | |
1016 | lport->tt.exch_mgr_reset(lport, 0, 0); |
1017 | fc_host_fabric_name(lport->host) = 0; |
1018 | |
1019 | if (lport->port_id && (!lport->point_to_multipoint || !lport->link_up)) |
1020 | fc_lport_set_port_id(lport, port_id: 0, NULL); |
1021 | } |
1022 | |
1023 | /** |
1024 | * fc_lport_enter_reset() - Reset the local port |
1025 | * @lport: The local port to be reset |
1026 | */ |
1027 | static void fc_lport_enter_reset(struct fc_lport *lport) |
1028 | { |
1029 | lockdep_assert_held(&lport->lp_mutex); |
1030 | |
1031 | FC_LPORT_DBG(lport, "Entered RESET state from %s state\n" , |
1032 | fc_lport_state(lport)); |
1033 | |
1034 | if (lport->state == LPORT_ST_DISABLED || lport->state == LPORT_ST_LOGO) |
1035 | return; |
1036 | |
1037 | if (lport->vport) { |
1038 | if (lport->link_up) |
1039 | fc_vport_set_state(vport: lport->vport, new_state: FC_VPORT_INITIALIZING); |
1040 | else |
1041 | fc_vport_set_state(vport: lport->vport, new_state: FC_VPORT_LINKDOWN); |
1042 | } |
1043 | fc_lport_state_enter(lport, state: LPORT_ST_RESET); |
1044 | fc_host_post_event(shost: lport->host, event_number: fc_get_event_number(), |
1045 | event_code: FCH_EVT_LIPRESET, event_data: 0); |
1046 | fc_vports_linkchange(lport); |
1047 | fc_lport_reset_locked(lport); |
1048 | if (lport->link_up) |
1049 | fc_lport_enter_flogi(lport); |
1050 | } |
1051 | |
1052 | /** |
1053 | * fc_lport_enter_disabled() - Disable the local port |
1054 | * @lport: The local port to be reset |
1055 | */ |
1056 | static void fc_lport_enter_disabled(struct fc_lport *lport) |
1057 | { |
1058 | lockdep_assert_held(&lport->lp_mutex); |
1059 | |
1060 | FC_LPORT_DBG(lport, "Entered disabled state from %s state\n" , |
1061 | fc_lport_state(lport)); |
1062 | |
1063 | fc_lport_state_enter(lport, state: LPORT_ST_DISABLED); |
1064 | fc_vports_linkchange(lport); |
1065 | fc_lport_reset_locked(lport); |
1066 | } |
1067 | |
1068 | /** |
1069 | * fc_lport_error() - Handler for any errors |
1070 | * @lport: The local port that the error was on |
1071 | * @fp: The error code encoded in a frame pointer |
1072 | * |
1073 | * If the error was caused by a resource allocation failure |
1074 | * then wait for half a second and retry, otherwise retry |
1075 | * after the e_d_tov time. |
1076 | */ |
1077 | static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) |
1078 | { |
1079 | unsigned long delay = 0; |
1080 | FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n" , |
1081 | IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport), |
1082 | lport->retry_count); |
1083 | |
1084 | if (PTR_ERR(ptr: fp) == -FC_EX_CLOSED) |
1085 | return; |
1086 | |
1087 | /* |
1088 | * Memory allocation failure, or the exchange timed out |
1089 | * or we received LS_RJT. |
1090 | * Retry after delay |
1091 | */ |
1092 | if (lport->retry_count < lport->max_retry_count) { |
1093 | lport->retry_count++; |
1094 | if (!fp) |
1095 | delay = msecs_to_jiffies(m: 500); |
1096 | else |
1097 | delay = msecs_to_jiffies(m: lport->e_d_tov); |
1098 | |
1099 | schedule_delayed_work(dwork: &lport->retry_work, delay); |
1100 | } else |
1101 | fc_lport_enter_reset(lport); |
1102 | } |
1103 | |
1104 | /** |
1105 | * fc_lport_ns_resp() - Handle response to a name server |
1106 | * registration exchange |
1107 | * @sp: current sequence in exchange |
1108 | * @fp: response frame |
1109 | * @lp_arg: Fibre Channel host port instance |
1110 | * |
1111 | * Locking Note: This function will be called without the lport lock |
1112 | * held, but it will lock, call an _enter_* function or fc_lport_error() |
1113 | * and then unlock the lport. |
1114 | */ |
1115 | static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp, |
1116 | void *lp_arg) |
1117 | { |
1118 | struct fc_lport *lport = lp_arg; |
1119 | struct fc_frame_header *fh; |
1120 | struct fc_ct_hdr *ct; |
1121 | |
1122 | FC_LPORT_DBG(lport, "Received a ns %s\n" , fc_els_resp_type(fp)); |
1123 | |
1124 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1125 | return; |
1126 | |
1127 | mutex_lock(&lport->lp_mutex); |
1128 | |
1129 | if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) { |
1130 | FC_LPORT_DBG(lport, "Received a name server response, " |
1131 | "but in state %s\n" , fc_lport_state(lport)); |
1132 | if (IS_ERR(ptr: fp)) |
1133 | goto err; |
1134 | goto out; |
1135 | } |
1136 | |
1137 | if (IS_ERR(ptr: fp)) { |
1138 | fc_lport_error(lport, fp); |
1139 | goto err; |
1140 | } |
1141 | |
1142 | fh = fc_frame_header_get(fp); |
1143 | ct = fc_frame_payload_get(fp, len: sizeof(*ct)); |
1144 | |
1145 | if (fh && ct && fh->fh_type == FC_TYPE_CT && |
1146 | ct->ct_fs_type == FC_FST_DIR && |
1147 | ct->ct_fs_subtype == FC_NS_SUBTYPE && |
1148 | ntohs(ct->ct_cmd) == FC_FS_ACC) |
1149 | switch (lport->state) { |
1150 | case LPORT_ST_RNN_ID: |
1151 | fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN); |
1152 | break; |
1153 | case LPORT_ST_RSNN_NN: |
1154 | fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID); |
1155 | break; |
1156 | case LPORT_ST_RSPN_ID: |
1157 | fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); |
1158 | break; |
1159 | case LPORT_ST_RFT_ID: |
1160 | fc_lport_enter_ns(lport, LPORT_ST_RFF_ID); |
1161 | break; |
1162 | case LPORT_ST_RFF_ID: |
1163 | if (lport->fdmi_enabled) |
1164 | fc_lport_enter_fdmi(lport); |
1165 | else |
1166 | fc_lport_enter_scr(lport); |
1167 | break; |
1168 | default: |
1169 | /* should have already been caught by state checks */ |
1170 | break; |
1171 | } |
1172 | else |
1173 | fc_lport_error(lport, fp); |
1174 | out: |
1175 | fc_frame_free(fp); |
1176 | err: |
1177 | mutex_unlock(lock: &lport->lp_mutex); |
1178 | } |
1179 | |
1180 | /** |
1181 | * fc_lport_ms_resp() - Handle response to a management server |
1182 | * exchange |
1183 | * @sp: current sequence in exchange |
1184 | * @fp: response frame |
1185 | * @lp_arg: Fibre Channel host port instance |
1186 | * |
1187 | * Locking Note: This function will be called without the lport lock |
1188 | * held, but it will lock, call an _enter_* function or fc_lport_error() |
1189 | * and then unlock the lport. |
1190 | */ |
1191 | static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp, |
1192 | void *lp_arg) |
1193 | { |
1194 | struct fc_lport *lport = lp_arg; |
1195 | struct fc_frame_header *fh; |
1196 | struct fc_ct_hdr *ct; |
1197 | struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host); |
1198 | FC_LPORT_DBG(lport, "Received a ms %s\n" , fc_els_resp_type(fp)); |
1199 | |
1200 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1201 | return; |
1202 | |
1203 | mutex_lock(&lport->lp_mutex); |
1204 | |
1205 | if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) { |
1206 | FC_LPORT_DBG(lport, "Received a management server response, " |
1207 | "but in state %s\n" , fc_lport_state(lport)); |
1208 | if (IS_ERR(ptr: fp)) |
1209 | goto err; |
1210 | goto out; |
1211 | } |
1212 | |
1213 | if (IS_ERR(ptr: fp)) { |
1214 | fc_lport_error(lport, fp); |
1215 | goto err; |
1216 | } |
1217 | |
1218 | fh = fc_frame_header_get(fp); |
1219 | ct = fc_frame_payload_get(fp, len: sizeof(*ct)); |
1220 | |
1221 | if (fh && ct && fh->fh_type == FC_TYPE_CT && |
1222 | ct->ct_fs_type == FC_FST_MGMT && |
1223 | ct->ct_fs_subtype == FC_FDMI_SUBTYPE) { |
1224 | FC_LPORT_DBG(lport, "Received a management server response, " |
1225 | "reason=%d explain=%d\n" , |
1226 | ct->ct_reason, |
1227 | ct->ct_explan); |
1228 | |
1229 | switch (lport->state) { |
1230 | case LPORT_ST_RHBA: |
1231 | if ((ntohs(ct->ct_cmd) == FC_FS_RJT) && fc_host->fdmi_version == FDMI_V2) { |
1232 | FC_LPORT_DBG(lport, "Error for FDMI-V2, fall back to FDMI-V1\n" ); |
1233 | fc_host->fdmi_version = FDMI_V1; |
1234 | |
1235 | fc_lport_enter_ms(lport, LPORT_ST_RHBA); |
1236 | |
1237 | } else if (ntohs(ct->ct_cmd) == FC_FS_ACC) |
1238 | fc_lport_enter_ms(lport, LPORT_ST_RPA); |
1239 | else /* Error Skip RPA */ |
1240 | fc_lport_enter_scr(lport); |
1241 | break; |
1242 | case LPORT_ST_RPA: |
1243 | fc_lport_enter_scr(lport); |
1244 | break; |
1245 | case LPORT_ST_DPRT: |
1246 | fc_lport_enter_ms(lport, LPORT_ST_RHBA); |
1247 | break; |
1248 | case LPORT_ST_DHBA: |
1249 | fc_lport_enter_ms(lport, LPORT_ST_DPRT); |
1250 | break; |
1251 | default: |
1252 | /* should have already been caught by state checks */ |
1253 | break; |
1254 | } |
1255 | } else { |
1256 | /* Invalid Frame? */ |
1257 | fc_lport_error(lport, fp); |
1258 | } |
1259 | out: |
1260 | fc_frame_free(fp); |
1261 | err: |
1262 | mutex_unlock(lock: &lport->lp_mutex); |
1263 | } |
1264 | |
1265 | /** |
1266 | * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request |
1267 | * @sp: current sequence in SCR exchange |
1268 | * @fp: response frame |
1269 | * @lp_arg: Fibre Channel lport port instance that sent the registration request |
1270 | * |
1271 | * Locking Note: This function will be called without the lport lock |
1272 | * held, but it will lock, call an _enter_* function or fc_lport_error |
1273 | * and then unlock the lport. |
1274 | */ |
1275 | static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp, |
1276 | void *lp_arg) |
1277 | { |
1278 | struct fc_lport *lport = lp_arg; |
1279 | u8 op; |
1280 | |
1281 | FC_LPORT_DBG(lport, "Received a SCR %s\n" , fc_els_resp_type(fp)); |
1282 | |
1283 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1284 | return; |
1285 | |
1286 | mutex_lock(&lport->lp_mutex); |
1287 | |
1288 | if (lport->state != LPORT_ST_SCR) { |
1289 | FC_LPORT_DBG(lport, "Received a SCR response, but in state " |
1290 | "%s\n" , fc_lport_state(lport)); |
1291 | if (IS_ERR(ptr: fp)) |
1292 | goto err; |
1293 | goto out; |
1294 | } |
1295 | |
1296 | if (IS_ERR(ptr: fp)) { |
1297 | fc_lport_error(lport, fp); |
1298 | goto err; |
1299 | } |
1300 | |
1301 | op = fc_frame_payload_op(fp); |
1302 | if (op == ELS_LS_ACC) |
1303 | fc_lport_enter_ready(lport); |
1304 | else |
1305 | fc_lport_error(lport, fp); |
1306 | |
1307 | out: |
1308 | fc_frame_free(fp); |
1309 | err: |
1310 | mutex_unlock(lock: &lport->lp_mutex); |
1311 | } |
1312 | |
1313 | /** |
1314 | * fc_lport_enter_scr() - Send a SCR (State Change Register) request |
1315 | * @lport: The local port to register for state changes |
1316 | */ |
1317 | static void fc_lport_enter_scr(struct fc_lport *lport) |
1318 | { |
1319 | struct fc_frame *fp; |
1320 | |
1321 | lockdep_assert_held(&lport->lp_mutex); |
1322 | |
1323 | FC_LPORT_DBG(lport, "Entered SCR state from %s state\n" , |
1324 | fc_lport_state(lport)); |
1325 | |
1326 | fc_lport_state_enter(lport, state: LPORT_ST_SCR); |
1327 | |
1328 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_scr)); |
1329 | if (!fp) { |
1330 | fc_lport_error(lport, fp); |
1331 | return; |
1332 | } |
1333 | |
1334 | if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR, |
1335 | fc_lport_scr_resp, lport, |
1336 | 2 * lport->r_a_tov)) |
1337 | fc_lport_error(lport, NULL); |
1338 | } |
1339 | |
1340 | /** |
1341 | * fc_lport_enter_ns() - register some object with the name server |
1342 | * @lport: Fibre Channel local port to register |
1343 | * @state: Local port state |
1344 | */ |
1345 | static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state) |
1346 | { |
1347 | struct fc_frame *fp; |
1348 | enum fc_ns_req cmd; |
1349 | int size = sizeof(struct fc_ct_hdr); |
1350 | size_t len; |
1351 | |
1352 | lockdep_assert_held(&lport->lp_mutex); |
1353 | |
1354 | FC_LPORT_DBG(lport, "Entered %s state from %s state\n" , |
1355 | fc_lport_state_names[state], |
1356 | fc_lport_state(lport)); |
1357 | |
1358 | fc_lport_state_enter(lport, state); |
1359 | |
1360 | switch (state) { |
1361 | case LPORT_ST_RNN_ID: |
1362 | cmd = FC_NS_RNN_ID; |
1363 | size += sizeof(struct fc_ns_rn_id); |
1364 | break; |
1365 | case LPORT_ST_RSNN_NN: |
1366 | len = strnlen(fc_host_symbolic_name(lport->host), maxlen: 255); |
1367 | /* if there is no symbolic name, skip to RFT_ID */ |
1368 | if (!len) |
1369 | return fc_lport_enter_ns(lport, state: LPORT_ST_RFT_ID); |
1370 | cmd = FC_NS_RSNN_NN; |
1371 | size += sizeof(struct fc_ns_rsnn) + len; |
1372 | break; |
1373 | case LPORT_ST_RSPN_ID: |
1374 | len = strnlen(fc_host_symbolic_name(lport->host), maxlen: 255); |
1375 | /* if there is no symbolic name, skip to RFT_ID */ |
1376 | if (!len) |
1377 | return fc_lport_enter_ns(lport, state: LPORT_ST_RFT_ID); |
1378 | cmd = FC_NS_RSPN_ID; |
1379 | size += sizeof(struct fc_ns_rspn) + len; |
1380 | break; |
1381 | case LPORT_ST_RFT_ID: |
1382 | cmd = FC_NS_RFT_ID; |
1383 | size += sizeof(struct fc_ns_rft); |
1384 | break; |
1385 | case LPORT_ST_RFF_ID: |
1386 | cmd = FC_NS_RFF_ID; |
1387 | size += sizeof(struct fc_ns_rff_id); |
1388 | break; |
1389 | default: |
1390 | fc_lport_error(lport, NULL); |
1391 | return; |
1392 | } |
1393 | |
1394 | fp = fc_frame_alloc(dev: lport, len: size); |
1395 | if (!fp) { |
1396 | fc_lport_error(lport, fp); |
1397 | return; |
1398 | } |
1399 | |
1400 | if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd, |
1401 | fc_lport_ns_resp, |
1402 | lport, 3 * lport->r_a_tov)) |
1403 | fc_lport_error(lport, fp); |
1404 | } |
1405 | |
1406 | static struct fc_rport_operations fc_lport_rport_ops = { |
1407 | .event_callback = fc_lport_rport_callback, |
1408 | }; |
1409 | |
1410 | /** |
1411 | * fc_lport_enter_dns() - Create a fc_rport for the name server |
1412 | * @lport: The local port requesting a remote port for the name server |
1413 | */ |
1414 | static void fc_lport_enter_dns(struct fc_lport *lport) |
1415 | { |
1416 | struct fc_rport_priv *rdata; |
1417 | |
1418 | lockdep_assert_held(&lport->lp_mutex); |
1419 | |
1420 | FC_LPORT_DBG(lport, "Entered DNS state from %s state\n" , |
1421 | fc_lport_state(lport)); |
1422 | |
1423 | fc_lport_state_enter(lport, state: LPORT_ST_DNS); |
1424 | |
1425 | mutex_lock(&lport->disc.disc_mutex); |
1426 | rdata = fc_rport_create(lport, FC_FID_DIR_SERV); |
1427 | mutex_unlock(lock: &lport->disc.disc_mutex); |
1428 | if (!rdata) |
1429 | goto err; |
1430 | |
1431 | rdata->ops = &fc_lport_rport_ops; |
1432 | fc_rport_login(rdata); |
1433 | return; |
1434 | |
1435 | err: |
1436 | fc_lport_error(lport, NULL); |
1437 | } |
1438 | |
1439 | /** |
1440 | * fc_lport_enter_ms() - management server commands |
1441 | * @lport: Fibre Channel local port to register |
1442 | * @state: Local port state |
1443 | */ |
1444 | static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state) |
1445 | { |
1446 | struct fc_frame *fp; |
1447 | enum fc_fdmi_req cmd; |
1448 | int size = sizeof(struct fc_ct_hdr); |
1449 | size_t len; |
1450 | int numattrs; |
1451 | struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host); |
1452 | lockdep_assert_held(&lport->lp_mutex); |
1453 | |
1454 | FC_LPORT_DBG(lport, "Entered %s state from %s state\n" , |
1455 | fc_lport_state_names[state], |
1456 | fc_lport_state(lport)); |
1457 | |
1458 | fc_lport_state_enter(lport, state); |
1459 | |
1460 | switch (state) { |
1461 | case LPORT_ST_RHBA: |
1462 | cmd = FC_FDMI_RHBA; |
1463 | /* Number of HBA Attributes */ |
1464 | numattrs = 11; |
1465 | len = sizeof(struct fc_fdmi_rhba); |
1466 | len -= sizeof(struct fc_fdmi_attr_entry); |
1467 | |
1468 | len += FC_FDMI_HBA_ATTR_NODENAME_LEN; |
1469 | len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; |
1470 | len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; |
1471 | len += FC_FDMI_HBA_ATTR_MODEL_LEN; |
1472 | len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; |
1473 | len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; |
1474 | len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; |
1475 | len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; |
1476 | len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; |
1477 | len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; |
1478 | len += FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN; |
1479 | |
1480 | |
1481 | if (fc_host->fdmi_version == FDMI_V2) { |
1482 | numattrs += 7; |
1483 | len += FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN; |
1484 | len += FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN; |
1485 | len += FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN; |
1486 | len += FC_FDMI_HBA_ATTR_FABRICNAME_LEN; |
1487 | len += FC_FDMI_HBA_ATTR_BIOSVERSION_LEN; |
1488 | len += FC_FDMI_HBA_ATTR_BIOSSTATE_LEN; |
1489 | len += FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN; |
1490 | } |
1491 | |
1492 | len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); |
1493 | |
1494 | size += len; |
1495 | break; |
1496 | case LPORT_ST_RPA: |
1497 | cmd = FC_FDMI_RPA; |
1498 | /* Number of Port Attributes */ |
1499 | numattrs = 6; |
1500 | len = sizeof(struct fc_fdmi_rpa); |
1501 | len -= sizeof(struct fc_fdmi_attr_entry); |
1502 | len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; |
1503 | len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; |
1504 | len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; |
1505 | len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; |
1506 | len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; |
1507 | len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; |
1508 | |
1509 | if (fc_host->fdmi_version == FDMI_V2) { |
1510 | numattrs += 10; |
1511 | len += FC_FDMI_PORT_ATTR_NODENAME_LEN; |
1512 | len += FC_FDMI_PORT_ATTR_PORTNAME_LEN; |
1513 | len += FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN; |
1514 | len += FC_FDMI_PORT_ATTR_PORTTYPE_LEN; |
1515 | len += FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN; |
1516 | len += FC_FDMI_PORT_ATTR_FABRICNAME_LEN; |
1517 | len += FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN; |
1518 | len += FC_FDMI_PORT_ATTR_PORTSTATE_LEN; |
1519 | len += FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN; |
1520 | len += FC_FDMI_PORT_ATTR_PORTID_LEN; |
1521 | } |
1522 | |
1523 | len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); |
1524 | |
1525 | size += len; |
1526 | break; |
1527 | case LPORT_ST_DPRT: |
1528 | cmd = FC_FDMI_DPRT; |
1529 | len = sizeof(struct fc_fdmi_dprt); |
1530 | size += len; |
1531 | break; |
1532 | case LPORT_ST_DHBA: |
1533 | cmd = FC_FDMI_DHBA; |
1534 | len = sizeof(struct fc_fdmi_dhba); |
1535 | size += len; |
1536 | break; |
1537 | default: |
1538 | fc_lport_error(lport, NULL); |
1539 | return; |
1540 | } |
1541 | |
1542 | FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n" , |
1543 | cmd, (int)len, size); |
1544 | fp = fc_frame_alloc(dev: lport, len: size); |
1545 | if (!fp) { |
1546 | fc_lport_error(lport, fp); |
1547 | return; |
1548 | } |
1549 | |
1550 | if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd, |
1551 | fc_lport_ms_resp, |
1552 | lport, 3 * lport->r_a_tov)) |
1553 | fc_lport_error(lport, fp); |
1554 | } |
1555 | |
1556 | /** |
1557 | * fc_lport_enter_fdmi() - Create a fc_rport for the management server |
1558 | * @lport: The local port requesting a remote port for the management server |
1559 | */ |
1560 | static void fc_lport_enter_fdmi(struct fc_lport *lport) |
1561 | { |
1562 | struct fc_rport_priv *rdata; |
1563 | |
1564 | lockdep_assert_held(&lport->lp_mutex); |
1565 | |
1566 | FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n" , |
1567 | fc_lport_state(lport)); |
1568 | |
1569 | fc_lport_state_enter(lport, state: LPORT_ST_FDMI); |
1570 | |
1571 | mutex_lock(&lport->disc.disc_mutex); |
1572 | rdata = fc_rport_create(lport, FC_FID_MGMT_SERV); |
1573 | mutex_unlock(lock: &lport->disc.disc_mutex); |
1574 | if (!rdata) |
1575 | goto err; |
1576 | |
1577 | rdata->ops = &fc_lport_rport_ops; |
1578 | fc_rport_login(rdata); |
1579 | return; |
1580 | |
1581 | err: |
1582 | fc_lport_error(lport, NULL); |
1583 | } |
1584 | |
1585 | /** |
1586 | * fc_lport_timeout() - Handler for the retry_work timer |
1587 | * @work: The work struct of the local port |
1588 | */ |
1589 | static void fc_lport_timeout(struct work_struct *work) |
1590 | { |
1591 | struct fc_lport *lport = |
1592 | container_of(work, struct fc_lport, |
1593 | retry_work.work); |
1594 | struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host); |
1595 | |
1596 | mutex_lock(&lport->lp_mutex); |
1597 | |
1598 | switch (lport->state) { |
1599 | case LPORT_ST_DISABLED: |
1600 | break; |
1601 | case LPORT_ST_READY: |
1602 | break; |
1603 | case LPORT_ST_RESET: |
1604 | break; |
1605 | case LPORT_ST_FLOGI: |
1606 | fc_lport_enter_flogi(lport); |
1607 | break; |
1608 | case LPORT_ST_DNS: |
1609 | fc_lport_enter_dns(lport); |
1610 | break; |
1611 | case LPORT_ST_RNN_ID: |
1612 | case LPORT_ST_RSNN_NN: |
1613 | case LPORT_ST_RSPN_ID: |
1614 | case LPORT_ST_RFT_ID: |
1615 | case LPORT_ST_RFF_ID: |
1616 | fc_lport_enter_ns(lport, state: lport->state); |
1617 | break; |
1618 | case LPORT_ST_FDMI: |
1619 | fc_lport_enter_fdmi(lport); |
1620 | break; |
1621 | case LPORT_ST_RHBA: |
1622 | if (fc_host->fdmi_version == FDMI_V2) { |
1623 | FC_LPORT_DBG(lport, "timeout for FDMI-V2 RHBA,fall back to FDMI-V1\n" ); |
1624 | fc_host->fdmi_version = FDMI_V1; |
1625 | fc_lport_enter_ms(lport, state: LPORT_ST_RHBA); |
1626 | break; |
1627 | } |
1628 | fallthrough; |
1629 | case LPORT_ST_RPA: |
1630 | case LPORT_ST_DHBA: |
1631 | case LPORT_ST_DPRT: |
1632 | FC_LPORT_DBG(lport, "Skipping lport state %s to SCR\n" , |
1633 | fc_lport_state(lport)); |
1634 | fallthrough; |
1635 | case LPORT_ST_SCR: |
1636 | fc_lport_enter_scr(lport); |
1637 | break; |
1638 | case LPORT_ST_LOGO: |
1639 | fc_lport_enter_logo(lport); |
1640 | break; |
1641 | } |
1642 | |
1643 | mutex_unlock(lock: &lport->lp_mutex); |
1644 | } |
1645 | |
1646 | /** |
1647 | * fc_lport_logo_resp() - Handle response to LOGO request |
1648 | * @sp: The sequence that the LOGO was on |
1649 | * @fp: The LOGO frame |
1650 | * @lp_arg: The lport port that received the LOGO request |
1651 | * |
1652 | * Locking Note: This function will be called without the lport lock |
1653 | * held, but it will lock, call an _enter_* function or fc_lport_error() |
1654 | * and then unlock the lport. |
1655 | */ |
1656 | void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, |
1657 | void *lp_arg) |
1658 | { |
1659 | struct fc_lport *lport = lp_arg; |
1660 | u8 op; |
1661 | |
1662 | FC_LPORT_DBG(lport, "Received a LOGO %s\n" , fc_els_resp_type(fp)); |
1663 | |
1664 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1665 | return; |
1666 | |
1667 | mutex_lock(&lport->lp_mutex); |
1668 | |
1669 | if (lport->state != LPORT_ST_LOGO) { |
1670 | FC_LPORT_DBG(lport, "Received a LOGO response, but in state " |
1671 | "%s\n" , fc_lport_state(lport)); |
1672 | if (IS_ERR(ptr: fp)) |
1673 | goto err; |
1674 | goto out; |
1675 | } |
1676 | |
1677 | if (IS_ERR(ptr: fp)) { |
1678 | fc_lport_error(lport, fp); |
1679 | goto err; |
1680 | } |
1681 | |
1682 | op = fc_frame_payload_op(fp); |
1683 | if (op == ELS_LS_ACC) |
1684 | fc_lport_enter_disabled(lport); |
1685 | else |
1686 | fc_lport_error(lport, fp); |
1687 | |
1688 | out: |
1689 | fc_frame_free(fp); |
1690 | err: |
1691 | mutex_unlock(lock: &lport->lp_mutex); |
1692 | } |
1693 | EXPORT_SYMBOL(fc_lport_logo_resp); |
1694 | |
1695 | /** |
1696 | * fc_lport_enter_logo() - Logout of the fabric |
1697 | * @lport: The local port to be logged out |
1698 | */ |
1699 | static void fc_lport_enter_logo(struct fc_lport *lport) |
1700 | { |
1701 | struct fc_frame *fp; |
1702 | struct fc_els_logo *logo; |
1703 | |
1704 | lockdep_assert_held(&lport->lp_mutex); |
1705 | |
1706 | FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n" , |
1707 | fc_lport_state(lport)); |
1708 | |
1709 | fc_lport_state_enter(lport, state: LPORT_ST_LOGO); |
1710 | fc_vports_linkchange(lport); |
1711 | |
1712 | fp = fc_frame_alloc(dev: lport, len: sizeof(*logo)); |
1713 | if (!fp) { |
1714 | fc_lport_error(lport, fp); |
1715 | return; |
1716 | } |
1717 | |
1718 | if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO, |
1719 | fc_lport_logo_resp, lport, |
1720 | 2 * lport->r_a_tov)) |
1721 | fc_lport_error(lport, NULL); |
1722 | } |
1723 | |
1724 | /** |
1725 | * fc_lport_flogi_resp() - Handle response to FLOGI request |
1726 | * @sp: The sequence that the FLOGI was on |
1727 | * @fp: The FLOGI response frame |
1728 | * @lp_arg: The lport port that received the FLOGI response |
1729 | * |
1730 | * Locking Note: This function will be called without the lport lock |
1731 | * held, but it will lock, call an _enter_* function or fc_lport_error() |
1732 | * and then unlock the lport. |
1733 | */ |
1734 | void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, |
1735 | void *lp_arg) |
1736 | { |
1737 | struct fc_lport *lport = lp_arg; |
1738 | struct fc_frame_header *fh; |
1739 | struct fc_els_flogi *flp; |
1740 | u32 did; |
1741 | u16 csp_flags; |
1742 | unsigned int r_a_tov; |
1743 | unsigned int e_d_tov; |
1744 | u16 mfs; |
1745 | |
1746 | FC_LPORT_DBG(lport, "Received a FLOGI %s\n" , fc_els_resp_type(fp)); |
1747 | |
1748 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1749 | return; |
1750 | |
1751 | mutex_lock(&lport->lp_mutex); |
1752 | |
1753 | if (lport->state != LPORT_ST_FLOGI) { |
1754 | FC_LPORT_DBG(lport, "Received a FLOGI response, but in state " |
1755 | "%s\n" , fc_lport_state(lport)); |
1756 | if (IS_ERR(ptr: fp)) |
1757 | goto err; |
1758 | goto out; |
1759 | } |
1760 | |
1761 | if (IS_ERR(ptr: fp)) { |
1762 | fc_lport_error(lport, fp); |
1763 | goto err; |
1764 | } |
1765 | |
1766 | fh = fc_frame_header_get(fp); |
1767 | did = fc_frame_did(fp); |
1768 | if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 || |
1769 | fc_frame_payload_op(fp) != ELS_LS_ACC) { |
1770 | FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n" ); |
1771 | fc_lport_error(lport, fp); |
1772 | goto out; |
1773 | } |
1774 | |
1775 | flp = fc_frame_payload_get(fp, len: sizeof(*flp)); |
1776 | if (!flp) { |
1777 | FC_LPORT_DBG(lport, "FLOGI bad response\n" ); |
1778 | fc_lport_error(lport, fp); |
1779 | goto out; |
1780 | } |
1781 | |
1782 | mfs = ntohs(flp->fl_csp.sp_bb_data) & |
1783 | FC_SP_BB_DATA_MASK; |
1784 | |
1785 | if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) { |
1786 | FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, " |
1787 | "lport->mfs:%u\n" , mfs, lport->mfs); |
1788 | fc_lport_error(lport, fp); |
1789 | goto out; |
1790 | } |
1791 | |
1792 | if (mfs <= lport->mfs) { |
1793 | lport->mfs = mfs; |
1794 | fc_host_maxframe_size(lport->host) = mfs; |
1795 | } |
1796 | |
1797 | csp_flags = ntohs(flp->fl_csp.sp_features); |
1798 | r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); |
1799 | e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); |
1800 | if (csp_flags & FC_SP_FT_EDTR) |
1801 | e_d_tov /= 1000000; |
1802 | |
1803 | lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); |
1804 | |
1805 | if ((csp_flags & FC_SP_FT_FPORT) == 0) { |
1806 | if (e_d_tov > lport->e_d_tov) |
1807 | lport->e_d_tov = e_d_tov; |
1808 | lport->r_a_tov = 2 * lport->e_d_tov; |
1809 | fc_lport_set_port_id(lport, port_id: did, fp); |
1810 | printk(KERN_INFO "host%d: libfc: " |
1811 | "Port (%6.6x) entered " |
1812 | "point-to-point mode\n" , |
1813 | lport->host->host_no, did); |
1814 | fc_lport_ptp_setup(lport, remote_fid: fc_frame_sid(fp), |
1815 | remote_wwpn: get_unaligned_be64( |
1816 | p: &flp->fl_wwpn), |
1817 | remote_wwnn: get_unaligned_be64( |
1818 | p: &flp->fl_wwnn)); |
1819 | } else { |
1820 | if (e_d_tov > lport->e_d_tov) |
1821 | lport->e_d_tov = e_d_tov; |
1822 | if (r_a_tov > lport->r_a_tov) |
1823 | lport->r_a_tov = r_a_tov; |
1824 | fc_host_fabric_name(lport->host) = |
1825 | get_unaligned_be64(p: &flp->fl_wwnn); |
1826 | fc_lport_set_port_id(lport, port_id: did, fp); |
1827 | fc_lport_enter_dns(lport); |
1828 | } |
1829 | |
1830 | out: |
1831 | fc_frame_free(fp); |
1832 | err: |
1833 | mutex_unlock(lock: &lport->lp_mutex); |
1834 | } |
1835 | EXPORT_SYMBOL(fc_lport_flogi_resp); |
1836 | |
1837 | /** |
1838 | * fc_lport_enter_flogi() - Send a FLOGI request to the fabric manager |
1839 | * @lport: Fibre Channel local port to be logged in to the fabric |
1840 | */ |
1841 | static void fc_lport_enter_flogi(struct fc_lport *lport) |
1842 | { |
1843 | struct fc_frame *fp; |
1844 | |
1845 | lockdep_assert_held(&lport->lp_mutex); |
1846 | |
1847 | FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n" , |
1848 | fc_lport_state(lport)); |
1849 | |
1850 | fc_lport_state_enter(lport, state: LPORT_ST_FLOGI); |
1851 | |
1852 | if (lport->point_to_multipoint) { |
1853 | if (lport->port_id) |
1854 | fc_lport_enter_ready(lport); |
1855 | return; |
1856 | } |
1857 | |
1858 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_flogi)); |
1859 | if (!fp) |
1860 | return fc_lport_error(lport, fp); |
1861 | |
1862 | if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, |
1863 | lport->vport ? ELS_FDISC : ELS_FLOGI, |
1864 | fc_lport_flogi_resp, lport, |
1865 | lport->vport ? 2 * lport->r_a_tov : |
1866 | lport->e_d_tov)) |
1867 | fc_lport_error(lport, NULL); |
1868 | } |
1869 | |
1870 | /** |
1871 | * fc_lport_config() - Configure a fc_lport |
1872 | * @lport: The local port to be configured |
1873 | */ |
1874 | int fc_lport_config(struct fc_lport *lport) |
1875 | { |
1876 | INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout); |
1877 | mutex_init(&lport->lp_mutex); |
1878 | |
1879 | fc_lport_state_enter(lport, state: LPORT_ST_DISABLED); |
1880 | |
1881 | fc_lport_add_fc4_type(lport, type: FC_TYPE_FCP); |
1882 | fc_lport_add_fc4_type(lport, type: FC_TYPE_CT); |
1883 | fc_fc4_conf_lport_params(lport, FC_TYPE_FCP); |
1884 | |
1885 | return 0; |
1886 | } |
1887 | EXPORT_SYMBOL(fc_lport_config); |
1888 | |
1889 | /** |
1890 | * fc_lport_init() - Initialize the lport layer for a local port |
1891 | * @lport: The local port to initialize the exchange layer for |
1892 | */ |
1893 | int fc_lport_init(struct fc_lport *lport) |
1894 | { |
1895 | struct fc_host_attrs *fc_host; |
1896 | |
1897 | fc_host = shost_to_fc_host(lport->host); |
1898 | |
1899 | /* Set FDMI version to FDMI-2 specification*/ |
1900 | fc_host->fdmi_version = FDMI_V2; |
1901 | |
1902 | fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; |
1903 | fc_host_node_name(lport->host) = lport->wwnn; |
1904 | fc_host_port_name(lport->host) = lport->wwpn; |
1905 | fc_host_supported_classes(lport->host) = FC_COS_CLASS3; |
1906 | memset(fc_host_supported_fc4s(lport->host), 0, |
1907 | sizeof(fc_host_supported_fc4s(lport->host))); |
1908 | fc_host_supported_fc4s(lport->host)[2] = 1; |
1909 | fc_host_supported_fc4s(lport->host)[7] = 1; |
1910 | fc_host_num_discovered_ports(lport->host) = 4; |
1911 | |
1912 | /* This value is also unchanging */ |
1913 | memset(fc_host_active_fc4s(lport->host), 0, |
1914 | sizeof(fc_host_active_fc4s(lport->host))); |
1915 | fc_host_active_fc4s(lport->host)[2] = 1; |
1916 | fc_host_active_fc4s(lport->host)[7] = 1; |
1917 | fc_host_maxframe_size(lport->host) = lport->mfs; |
1918 | fc_host_supported_speeds(lport->host) = 0; |
1919 | if (lport->link_supported_speeds & FC_PORTSPEED_1GBIT) |
1920 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT; |
1921 | if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT) |
1922 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; |
1923 | if (lport->link_supported_speeds & FC_PORTSPEED_40GBIT) |
1924 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_40GBIT; |
1925 | if (lport->link_supported_speeds & FC_PORTSPEED_100GBIT) |
1926 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_100GBIT; |
1927 | if (lport->link_supported_speeds & FC_PORTSPEED_25GBIT) |
1928 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_25GBIT; |
1929 | if (lport->link_supported_speeds & FC_PORTSPEED_50GBIT) |
1930 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_50GBIT; |
1931 | if (lport->link_supported_speeds & FC_PORTSPEED_100GBIT) |
1932 | fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_100GBIT; |
1933 | |
1934 | fc_fc4_add_lport(lport); |
1935 | |
1936 | fc_host_num_discovered_ports(lport->host) = DISCOVERED_PORTS; |
1937 | fc_host_port_state(lport->host) = FC_PORTSTATE_ONLINE; |
1938 | fc_host_max_ct_payload(lport->host) = MAX_CT_PAYLOAD; |
1939 | fc_host_num_ports(lport->host) = NUMBER_OF_PORTS; |
1940 | fc_host_bootbios_state(lport->host) = 0X00000000; |
1941 | snprintf(fc_host_bootbios_version(lport->host), |
1942 | FC_SYMBOLIC_NAME_SIZE, fmt: "%s" , "Unknown" ); |
1943 | |
1944 | return 0; |
1945 | } |
1946 | EXPORT_SYMBOL(fc_lport_init); |
1947 | |
1948 | /** |
1949 | * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests |
1950 | * @sp: The sequence for the FC Passthrough response |
1951 | * @fp: The response frame |
1952 | * @info_arg: The BSG info that the response is for |
1953 | */ |
1954 | static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, |
1955 | void *info_arg) |
1956 | { |
1957 | struct fc_bsg_info *info = info_arg; |
1958 | struct bsg_job *job = info->job; |
1959 | struct fc_bsg_reply *bsg_reply = job->reply; |
1960 | struct fc_lport *lport = info->lport; |
1961 | struct fc_frame_header *fh; |
1962 | size_t len; |
1963 | void *buf; |
1964 | |
1965 | if (IS_ERR(ptr: fp)) { |
1966 | bsg_reply->result = (PTR_ERR(ptr: fp) == -FC_EX_CLOSED) ? |
1967 | -ECONNABORTED : -ETIMEDOUT; |
1968 | job->reply_len = sizeof(uint32_t); |
1969 | bsg_job_done(job, result: bsg_reply->result, |
1970 | reply_payload_rcv_len: bsg_reply->reply_payload_rcv_len); |
1971 | kfree(objp: info); |
1972 | return; |
1973 | } |
1974 | |
1975 | mutex_lock(&lport->lp_mutex); |
1976 | fh = fc_frame_header_get(fp); |
1977 | len = fr_len(fp) - sizeof(*fh); |
1978 | buf = fc_frame_payload_get(fp, len: 0); |
1979 | |
1980 | if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) { |
1981 | /* Get the response code from the first frame payload */ |
1982 | unsigned short cmd = (info->rsp_code == FC_FS_ACC) ? |
1983 | ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) : |
1984 | (unsigned short)fc_frame_payload_op(fp); |
1985 | |
1986 | /* Save the reply status of the job */ |
1987 | bsg_reply->reply_data.ctels_reply.status = |
1988 | (cmd == info->rsp_code) ? |
1989 | FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT; |
1990 | } |
1991 | |
1992 | bsg_reply->reply_payload_rcv_len += |
1993 | fc_copy_buffer_to_sglist(buf, len, sg: info->sg, nents: &info->nents, |
1994 | offset: &info->offset, NULL); |
1995 | |
1996 | if (fr_eof(fp) == FC_EOF_T && |
1997 | (ntoh24(p: fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == |
1998 | (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { |
1999 | if (bsg_reply->reply_payload_rcv_len > |
2000 | job->reply_payload.payload_len) |
2001 | bsg_reply->reply_payload_rcv_len = |
2002 | job->reply_payload.payload_len; |
2003 | bsg_reply->result = 0; |
2004 | bsg_job_done(job, result: bsg_reply->result, |
2005 | reply_payload_rcv_len: bsg_reply->reply_payload_rcv_len); |
2006 | kfree(objp: info); |
2007 | } |
2008 | fc_frame_free(fp); |
2009 | mutex_unlock(lock: &lport->lp_mutex); |
2010 | } |
2011 | |
2012 | /** |
2013 | * fc_lport_els_request() - Send ELS passthrough request |
2014 | * @job: The BSG Passthrough job |
2015 | * @lport: The local port sending the request |
2016 | * @did: The destination port id |
2017 | * @tov: The timeout period (in ms) |
2018 | */ |
2019 | static int fc_lport_els_request(struct bsg_job *job, |
2020 | struct fc_lport *lport, |
2021 | u32 did, u32 tov) |
2022 | { |
2023 | struct fc_bsg_info *info; |
2024 | struct fc_frame *fp; |
2025 | struct fc_frame_header *fh; |
2026 | char *pp; |
2027 | int len; |
2028 | |
2029 | lockdep_assert_held(&lport->lp_mutex); |
2030 | |
2031 | fp = fc_frame_alloc(dev: lport, len: job->request_payload.payload_len); |
2032 | if (!fp) |
2033 | return -ENOMEM; |
2034 | |
2035 | len = job->request_payload.payload_len; |
2036 | pp = fc_frame_payload_get(fp, len); |
2037 | |
2038 | sg_copy_to_buffer(sgl: job->request_payload.sg_list, |
2039 | nents: job->request_payload.sg_cnt, |
2040 | buf: pp, buflen: len); |
2041 | |
2042 | fh = fc_frame_header_get(fp); |
2043 | fh->fh_r_ctl = FC_RCTL_ELS_REQ; |
2044 | hton24(p: fh->fh_d_id, v: did); |
2045 | hton24(p: fh->fh_s_id, v: lport->port_id); |
2046 | fh->fh_type = FC_TYPE_ELS; |
2047 | hton24(p: fh->fh_f_ctl, FC_FCTL_REQ); |
2048 | fh->fh_cs_ctl = 0; |
2049 | fh->fh_df_ctl = 0; |
2050 | fh->fh_parm_offset = 0; |
2051 | |
2052 | info = kzalloc(size: sizeof(struct fc_bsg_info), GFP_KERNEL); |
2053 | if (!info) { |
2054 | fc_frame_free(fp); |
2055 | return -ENOMEM; |
2056 | } |
2057 | |
2058 | info->job = job; |
2059 | info->lport = lport; |
2060 | info->rsp_code = ELS_LS_ACC; |
2061 | info->nents = job->reply_payload.sg_cnt; |
2062 | info->sg = job->reply_payload.sg_list; |
2063 | |
2064 | if (!fc_exch_seq_send(lport, fp, resp: fc_lport_bsg_resp, |
2065 | NULL, arg: info, timer_msec: tov)) { |
2066 | kfree(objp: info); |
2067 | return -ECOMM; |
2068 | } |
2069 | return 0; |
2070 | } |
2071 | |
2072 | /** |
2073 | * fc_lport_ct_request() - Send CT Passthrough request |
2074 | * @job: The BSG Passthrough job |
2075 | * @lport: The local port sending the request |
2076 | * @did: The destination FC-ID |
2077 | * @tov: The timeout period to wait for the response |
2078 | */ |
2079 | static int fc_lport_ct_request(struct bsg_job *job, |
2080 | struct fc_lport *lport, u32 did, u32 tov) |
2081 | { |
2082 | struct fc_bsg_info *info; |
2083 | struct fc_frame *fp; |
2084 | struct fc_frame_header *fh; |
2085 | struct fc_ct_req *ct; |
2086 | size_t len; |
2087 | |
2088 | lockdep_assert_held(&lport->lp_mutex); |
2089 | |
2090 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_ct_hdr) + |
2091 | job->request_payload.payload_len); |
2092 | if (!fp) |
2093 | return -ENOMEM; |
2094 | |
2095 | len = job->request_payload.payload_len; |
2096 | ct = fc_frame_payload_get(fp, len); |
2097 | |
2098 | sg_copy_to_buffer(sgl: job->request_payload.sg_list, |
2099 | nents: job->request_payload.sg_cnt, |
2100 | buf: ct, buflen: len); |
2101 | |
2102 | fh = fc_frame_header_get(fp); |
2103 | fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; |
2104 | hton24(p: fh->fh_d_id, v: did); |
2105 | hton24(p: fh->fh_s_id, v: lport->port_id); |
2106 | fh->fh_type = FC_TYPE_CT; |
2107 | hton24(p: fh->fh_f_ctl, FC_FCTL_REQ); |
2108 | fh->fh_cs_ctl = 0; |
2109 | fh->fh_df_ctl = 0; |
2110 | fh->fh_parm_offset = 0; |
2111 | |
2112 | info = kzalloc(size: sizeof(struct fc_bsg_info), GFP_KERNEL); |
2113 | if (!info) { |
2114 | fc_frame_free(fp); |
2115 | return -ENOMEM; |
2116 | } |
2117 | |
2118 | info->job = job; |
2119 | info->lport = lport; |
2120 | info->rsp_code = FC_FS_ACC; |
2121 | info->nents = job->reply_payload.sg_cnt; |
2122 | info->sg = job->reply_payload.sg_list; |
2123 | |
2124 | if (!fc_exch_seq_send(lport, fp, resp: fc_lport_bsg_resp, |
2125 | NULL, arg: info, timer_msec: tov)) { |
2126 | kfree(objp: info); |
2127 | return -ECOMM; |
2128 | } |
2129 | return 0; |
2130 | } |
2131 | |
2132 | /** |
2133 | * fc_lport_bsg_request() - The common entry point for sending |
2134 | * FC Passthrough requests |
2135 | * @job: The BSG passthrough job |
2136 | */ |
2137 | int fc_lport_bsg_request(struct bsg_job *job) |
2138 | { |
2139 | struct fc_bsg_request *bsg_request = job->request; |
2140 | struct fc_bsg_reply *bsg_reply = job->reply; |
2141 | struct Scsi_Host *shost = fc_bsg_to_shost(job); |
2142 | struct fc_lport *lport = shost_priv(shost); |
2143 | struct fc_rport *rport; |
2144 | struct fc_rport_priv *rdata; |
2145 | int rc = -EINVAL; |
2146 | u32 did, tov; |
2147 | |
2148 | bsg_reply->reply_payload_rcv_len = 0; |
2149 | |
2150 | mutex_lock(&lport->lp_mutex); |
2151 | |
2152 | switch (bsg_request->msgcode) { |
2153 | case FC_BSG_RPT_ELS: |
2154 | rport = fc_bsg_to_rport(job); |
2155 | if (!rport) |
2156 | break; |
2157 | |
2158 | rdata = rport->dd_data; |
2159 | rc = fc_lport_els_request(job, lport, did: rport->port_id, |
2160 | tov: rdata->e_d_tov); |
2161 | break; |
2162 | |
2163 | case FC_BSG_RPT_CT: |
2164 | rport = fc_bsg_to_rport(job); |
2165 | if (!rport) |
2166 | break; |
2167 | |
2168 | rdata = rport->dd_data; |
2169 | rc = fc_lport_ct_request(job, lport, did: rport->port_id, |
2170 | tov: rdata->e_d_tov); |
2171 | break; |
2172 | |
2173 | case FC_BSG_HST_CT: |
2174 | did = ntoh24(p: bsg_request->rqst_data.h_ct.port_id); |
2175 | if (did == FC_FID_DIR_SERV) { |
2176 | rdata = lport->dns_rdata; |
2177 | if (!rdata) |
2178 | break; |
2179 | tov = rdata->e_d_tov; |
2180 | } else { |
2181 | rdata = fc_rport_lookup(lport, port_id: did); |
2182 | if (!rdata) |
2183 | break; |
2184 | tov = rdata->e_d_tov; |
2185 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
2186 | } |
2187 | |
2188 | rc = fc_lport_ct_request(job, lport, did, tov); |
2189 | break; |
2190 | |
2191 | case FC_BSG_HST_ELS_NOLOGIN: |
2192 | did = ntoh24(p: bsg_request->rqst_data.h_els.port_id); |
2193 | rc = fc_lport_els_request(job, lport, did, tov: lport->e_d_tov); |
2194 | break; |
2195 | } |
2196 | |
2197 | mutex_unlock(lock: &lport->lp_mutex); |
2198 | return rc; |
2199 | } |
2200 | EXPORT_SYMBOL(fc_lport_bsg_request); |
2201 | |