1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. |
4 | * |
5 | * Maintained at www.Open-FCoE.org |
6 | */ |
7 | |
8 | /* |
9 | * RPORT GENERAL INFO |
10 | * |
11 | * This file contains all processing regarding fc_rports. It contains the |
12 | * rport state machine and does all rport interaction with the transport class. |
13 | * There should be no other places in libfc that interact directly with the |
14 | * transport class in regards to adding and deleting rports. |
15 | * |
16 | * fc_rport's represent N_Port's within the fabric. |
17 | */ |
18 | |
19 | /* |
20 | * RPORT LOCKING |
21 | * |
22 | * The rport should never hold the rport mutex and then attempt to acquire |
23 | * either the lport or disc mutexes. The rport's mutex is considered lesser |
24 | * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for |
25 | * more comments on the hierarchy. |
26 | * |
27 | * The locking strategy is similar to the lport's strategy. The lock protects |
28 | * the rport's states and is held and released by the entry points to the rport |
29 | * block. All _enter_* functions correspond to rport states and expect the rport |
30 | * mutex to be locked before calling them. This means that rports only handle |
31 | * one request or response at a time, since they're not critical for the I/O |
32 | * path this potential over-use of the mutex is acceptable. |
33 | */ |
34 | |
35 | /* |
36 | * RPORT REFERENCE COUNTING |
37 | * |
38 | * A rport reference should be taken when: |
39 | * - an rport is allocated |
40 | * - a workqueue item is scheduled |
41 | * - an ELS request is send |
42 | * The reference should be dropped when: |
43 | * - the workqueue function has finished |
44 | * - the ELS response is handled |
45 | * - an rport is removed |
46 | */ |
47 | |
48 | #include <linux/kernel.h> |
49 | #include <linux/spinlock.h> |
50 | #include <linux/interrupt.h> |
51 | #include <linux/slab.h> |
52 | #include <linux/rcupdate.h> |
53 | #include <linux/timer.h> |
54 | #include <linux/workqueue.h> |
55 | #include <linux/export.h> |
56 | #include <linux/rculist.h> |
57 | |
58 | #include <asm/unaligned.h> |
59 | |
60 | #include <scsi/libfc.h> |
61 | |
62 | #include "fc_encode.h" |
63 | #include "fc_libfc.h" |
64 | |
65 | static struct workqueue_struct *rport_event_queue; |
66 | |
67 | static void fc_rport_enter_flogi(struct fc_rport_priv *); |
68 | static void fc_rport_enter_plogi(struct fc_rport_priv *); |
69 | static void fc_rport_enter_prli(struct fc_rport_priv *); |
70 | static void fc_rport_enter_rtv(struct fc_rport_priv *); |
71 | static void fc_rport_enter_ready(struct fc_rport_priv *); |
72 | static void fc_rport_enter_logo(struct fc_rport_priv *); |
73 | static void fc_rport_enter_adisc(struct fc_rport_priv *); |
74 | |
75 | static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *); |
76 | static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *); |
77 | static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *); |
78 | static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *); |
79 | static void fc_rport_timeout(struct work_struct *); |
80 | static void fc_rport_error(struct fc_rport_priv *, int); |
81 | static void fc_rport_error_retry(struct fc_rport_priv *, int); |
82 | static void fc_rport_work(struct work_struct *); |
83 | |
84 | static const char *fc_rport_state_names[] = { |
85 | [RPORT_ST_INIT] = "Init" , |
86 | [RPORT_ST_FLOGI] = "FLOGI" , |
87 | [RPORT_ST_PLOGI_WAIT] = "PLOGI_WAIT" , |
88 | [RPORT_ST_PLOGI] = "PLOGI" , |
89 | [RPORT_ST_PRLI] = "PRLI" , |
90 | [RPORT_ST_RTV] = "RTV" , |
91 | [RPORT_ST_READY] = "Ready" , |
92 | [RPORT_ST_ADISC] = "ADISC" , |
93 | [RPORT_ST_DELETE] = "Delete" , |
94 | }; |
95 | |
96 | /** |
97 | * fc_rport_lookup() - Lookup a remote port by port_id |
98 | * @lport: The local port to lookup the remote port on |
99 | * @port_id: The remote port ID to look up |
100 | * |
101 | * The reference count of the fc_rport_priv structure is |
102 | * increased by one. |
103 | */ |
104 | struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport, |
105 | u32 port_id) |
106 | { |
107 | struct fc_rport_priv *rdata = NULL, *tmp_rdata; |
108 | |
109 | rcu_read_lock(); |
110 | list_for_each_entry_rcu(tmp_rdata, &lport->disc.rports, peers) |
111 | if (tmp_rdata->ids.port_id == port_id && |
112 | kref_get_unless_zero(kref: &tmp_rdata->kref)) { |
113 | rdata = tmp_rdata; |
114 | break; |
115 | } |
116 | rcu_read_unlock(); |
117 | return rdata; |
118 | } |
119 | EXPORT_SYMBOL(fc_rport_lookup); |
120 | |
121 | /** |
122 | * fc_rport_create() - Create a new remote port |
123 | * @lport: The local port this remote port will be associated with |
124 | * @port_id: The identifiers for the new remote port |
125 | * |
126 | * The remote port will start in the INIT state. |
127 | */ |
128 | struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, u32 port_id) |
129 | { |
130 | struct fc_rport_priv *rdata; |
131 | size_t rport_priv_size = sizeof(*rdata); |
132 | |
133 | lockdep_assert_held(&lport->disc.disc_mutex); |
134 | |
135 | rdata = fc_rport_lookup(lport, port_id); |
136 | if (rdata) { |
137 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
138 | return rdata; |
139 | } |
140 | |
141 | if (lport->rport_priv_size > 0) |
142 | rport_priv_size = lport->rport_priv_size; |
143 | rdata = kzalloc(size: rport_priv_size, GFP_KERNEL); |
144 | if (!rdata) |
145 | return NULL; |
146 | |
147 | rdata->ids.node_name = -1; |
148 | rdata->ids.port_name = -1; |
149 | rdata->ids.port_id = port_id; |
150 | rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; |
151 | |
152 | kref_init(kref: &rdata->kref); |
153 | mutex_init(&rdata->rp_mutex); |
154 | rdata->local_port = lport; |
155 | rdata->rp_state = RPORT_ST_INIT; |
156 | rdata->event = RPORT_EV_NONE; |
157 | rdata->flags = FC_RP_FLAGS_REC_SUPPORTED; |
158 | rdata->e_d_tov = lport->e_d_tov; |
159 | rdata->r_a_tov = lport->r_a_tov; |
160 | rdata->maxframe_size = FC_MIN_MAX_PAYLOAD; |
161 | INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout); |
162 | INIT_WORK(&rdata->event_work, fc_rport_work); |
163 | if (port_id != FC_FID_DIR_SERV) { |
164 | rdata->lld_event_callback = lport->tt.rport_event_callback; |
165 | list_add_rcu(new: &rdata->peers, head: &lport->disc.rports); |
166 | } |
167 | return rdata; |
168 | } |
169 | EXPORT_SYMBOL(fc_rport_create); |
170 | |
171 | /** |
172 | * fc_rport_destroy() - Free a remote port after last reference is released |
173 | * @kref: The remote port's kref |
174 | */ |
175 | void fc_rport_destroy(struct kref *kref) |
176 | { |
177 | struct fc_rport_priv *rdata; |
178 | |
179 | rdata = container_of(kref, struct fc_rport_priv, kref); |
180 | kfree_rcu(rdata, rcu); |
181 | } |
182 | EXPORT_SYMBOL(fc_rport_destroy); |
183 | |
184 | /** |
185 | * fc_rport_state() - Return a string identifying the remote port's state |
186 | * @rdata: The remote port |
187 | */ |
188 | static const char *fc_rport_state(struct fc_rport_priv *rdata) |
189 | { |
190 | const char *cp; |
191 | |
192 | cp = fc_rport_state_names[rdata->rp_state]; |
193 | if (!cp) |
194 | cp = "Unknown" ; |
195 | return cp; |
196 | } |
197 | |
198 | /** |
199 | * fc_set_rport_loss_tmo() - Set the remote port loss timeout |
200 | * @rport: The remote port that gets a new timeout value |
201 | * @timeout: The new timeout value (in seconds) |
202 | */ |
203 | void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) |
204 | { |
205 | if (timeout) |
206 | rport->dev_loss_tmo = timeout; |
207 | else |
208 | rport->dev_loss_tmo = 1; |
209 | } |
210 | EXPORT_SYMBOL(fc_set_rport_loss_tmo); |
211 | |
212 | /** |
213 | * fc_plogi_get_maxframe() - Get the maximum payload from the common service |
214 | * parameters in a FLOGI frame |
215 | * @flp: The FLOGI or PLOGI payload |
216 | * @maxval: The maximum frame size upper limit; this may be less than what |
217 | * is in the service parameters |
218 | */ |
219 | static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp, |
220 | unsigned int maxval) |
221 | { |
222 | unsigned int mfs; |
223 | |
224 | /* |
225 | * Get max payload from the common service parameters and the |
226 | * class 3 receive data field size. |
227 | */ |
228 | mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; |
229 | if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval) |
230 | maxval = mfs; |
231 | mfs = ntohs(flp->fl_cssp[3 - 1].cp_rdfs); |
232 | if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval) |
233 | maxval = mfs; |
234 | return maxval; |
235 | } |
236 | |
237 | /** |
238 | * fc_rport_state_enter() - Change the state of a remote port |
239 | * @rdata: The remote port whose state should change |
240 | * @new: The new state |
241 | */ |
242 | static void fc_rport_state_enter(struct fc_rport_priv *rdata, |
243 | enum fc_rport_state new) |
244 | { |
245 | lockdep_assert_held(&rdata->rp_mutex); |
246 | |
247 | if (rdata->rp_state != new) |
248 | rdata->retries = 0; |
249 | rdata->rp_state = new; |
250 | } |
251 | |
252 | /** |
253 | * fc_rport_work() - Handler for remote port events in the rport_event_queue |
254 | * @work: Handle to the remote port being dequeued |
255 | * |
256 | * Reference counting: drops kref on return |
257 | */ |
258 | static void fc_rport_work(struct work_struct *work) |
259 | { |
260 | u32 port_id; |
261 | struct fc_rport_priv *rdata = |
262 | container_of(work, struct fc_rport_priv, event_work); |
263 | struct fc_rport_libfc_priv *rpriv; |
264 | enum fc_rport_event event; |
265 | struct fc_lport *lport = rdata->local_port; |
266 | struct fc_rport_operations *rport_ops; |
267 | struct fc_rport_identifiers ids; |
268 | struct fc_rport *rport; |
269 | struct fc4_prov *prov; |
270 | u8 type; |
271 | |
272 | mutex_lock(&rdata->rp_mutex); |
273 | event = rdata->event; |
274 | rport_ops = rdata->ops; |
275 | rport = rdata->rport; |
276 | |
277 | FC_RPORT_DBG(rdata, "work event %u\n" , event); |
278 | |
279 | switch (event) { |
280 | case RPORT_EV_READY: |
281 | ids = rdata->ids; |
282 | rdata->event = RPORT_EV_NONE; |
283 | rdata->major_retries = 0; |
284 | kref_get(kref: &rdata->kref); |
285 | mutex_unlock(lock: &rdata->rp_mutex); |
286 | |
287 | if (!rport) { |
288 | FC_RPORT_DBG(rdata, "No rport!\n" ); |
289 | rport = fc_remote_port_add(shost: lport->host, channel: 0, ids: &ids); |
290 | } |
291 | if (!rport) { |
292 | FC_RPORT_DBG(rdata, "Failed to add the rport\n" ); |
293 | fc_rport_logoff(rdata); |
294 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
295 | return; |
296 | } |
297 | mutex_lock(&rdata->rp_mutex); |
298 | if (rdata->rport) |
299 | FC_RPORT_DBG(rdata, "rport already allocated\n" ); |
300 | rdata->rport = rport; |
301 | rport->maxframe_size = rdata->maxframe_size; |
302 | rport->supported_classes = rdata->supported_classes; |
303 | |
304 | rpriv = rport->dd_data; |
305 | rpriv->local_port = lport; |
306 | rpriv->rp_state = rdata->rp_state; |
307 | rpriv->flags = rdata->flags; |
308 | rpriv->e_d_tov = rdata->e_d_tov; |
309 | rpriv->r_a_tov = rdata->r_a_tov; |
310 | mutex_unlock(lock: &rdata->rp_mutex); |
311 | |
312 | if (rport_ops && rport_ops->event_callback) { |
313 | FC_RPORT_DBG(rdata, "callback ev %d\n" , event); |
314 | rport_ops->event_callback(lport, rdata, event); |
315 | } |
316 | if (rdata->lld_event_callback) { |
317 | FC_RPORT_DBG(rdata, "lld callback ev %d\n" , event); |
318 | rdata->lld_event_callback(lport, rdata, event); |
319 | } |
320 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
321 | break; |
322 | |
323 | case RPORT_EV_FAILED: |
324 | case RPORT_EV_LOGO: |
325 | case RPORT_EV_STOP: |
326 | if (rdata->prli_count) { |
327 | mutex_lock(&fc_prov_mutex); |
328 | for (type = 1; type < FC_FC4_PROV_SIZE; type++) { |
329 | prov = fc_passive_prov[type]; |
330 | if (prov && prov->prlo) |
331 | prov->prlo(rdata); |
332 | } |
333 | mutex_unlock(lock: &fc_prov_mutex); |
334 | } |
335 | port_id = rdata->ids.port_id; |
336 | mutex_unlock(lock: &rdata->rp_mutex); |
337 | |
338 | if (rport_ops && rport_ops->event_callback) { |
339 | FC_RPORT_DBG(rdata, "callback ev %d\n" , event); |
340 | rport_ops->event_callback(lport, rdata, event); |
341 | } |
342 | if (rdata->lld_event_callback) { |
343 | FC_RPORT_DBG(rdata, "lld callback ev %d\n" , event); |
344 | rdata->lld_event_callback(lport, rdata, event); |
345 | } |
346 | if (cancel_delayed_work_sync(dwork: &rdata->retry_work)) |
347 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
348 | |
349 | /* |
350 | * Reset any outstanding exchanges before freeing rport. |
351 | */ |
352 | lport->tt.exch_mgr_reset(lport, 0, port_id); |
353 | lport->tt.exch_mgr_reset(lport, port_id, 0); |
354 | |
355 | if (rport) { |
356 | rpriv = rport->dd_data; |
357 | rpriv->rp_state = RPORT_ST_DELETE; |
358 | mutex_lock(&rdata->rp_mutex); |
359 | rdata->rport = NULL; |
360 | mutex_unlock(lock: &rdata->rp_mutex); |
361 | fc_remote_port_delete(rport); |
362 | } |
363 | |
364 | mutex_lock(&rdata->rp_mutex); |
365 | if (rdata->rp_state == RPORT_ST_DELETE) { |
366 | if (port_id == FC_FID_DIR_SERV) { |
367 | rdata->event = RPORT_EV_NONE; |
368 | mutex_unlock(lock: &rdata->rp_mutex); |
369 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
370 | } else if ((rdata->flags & FC_RP_STARTED) && |
371 | rdata->major_retries < |
372 | lport->max_rport_retry_count) { |
373 | rdata->major_retries++; |
374 | rdata->event = RPORT_EV_NONE; |
375 | FC_RPORT_DBG(rdata, "work restart\n" ); |
376 | fc_rport_enter_flogi(rdata); |
377 | mutex_unlock(lock: &rdata->rp_mutex); |
378 | } else { |
379 | mutex_unlock(lock: &rdata->rp_mutex); |
380 | FC_RPORT_DBG(rdata, "work delete\n" ); |
381 | mutex_lock(&lport->disc.disc_mutex); |
382 | list_del_rcu(entry: &rdata->peers); |
383 | mutex_unlock(lock: &lport->disc.disc_mutex); |
384 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
385 | } |
386 | } else { |
387 | /* |
388 | * Re-open for events. Reissue READY event if ready. |
389 | */ |
390 | rdata->event = RPORT_EV_NONE; |
391 | if (rdata->rp_state == RPORT_ST_READY) { |
392 | FC_RPORT_DBG(rdata, "work reopen\n" ); |
393 | fc_rport_enter_ready(rdata); |
394 | } |
395 | mutex_unlock(lock: &rdata->rp_mutex); |
396 | } |
397 | break; |
398 | |
399 | default: |
400 | mutex_unlock(lock: &rdata->rp_mutex); |
401 | break; |
402 | } |
403 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
404 | } |
405 | |
406 | /** |
407 | * fc_rport_login() - Start the remote port login state machine |
408 | * @rdata: The remote port to be logged in to |
409 | * |
410 | * Initiates the RP state machine. It is called from the LP module. |
411 | * This function will issue the following commands to the N_Port |
412 | * identified by the FC ID provided. |
413 | * |
414 | * - PLOGI |
415 | * - PRLI |
416 | * - RTV |
417 | * |
418 | * Locking Note: Called without the rport lock held. This |
419 | * function will hold the rport lock, call an _enter_* |
420 | * function and then unlock the rport. |
421 | * |
422 | * This indicates the intent to be logged into the remote port. |
423 | * If it appears we are already logged in, ADISC is used to verify |
424 | * the setup. |
425 | */ |
426 | int fc_rport_login(struct fc_rport_priv *rdata) |
427 | { |
428 | mutex_lock(&rdata->rp_mutex); |
429 | |
430 | if (rdata->flags & FC_RP_STARTED) { |
431 | FC_RPORT_DBG(rdata, "port already started\n" ); |
432 | mutex_unlock(lock: &rdata->rp_mutex); |
433 | return 0; |
434 | } |
435 | |
436 | rdata->flags |= FC_RP_STARTED; |
437 | switch (rdata->rp_state) { |
438 | case RPORT_ST_READY: |
439 | FC_RPORT_DBG(rdata, "ADISC port\n" ); |
440 | fc_rport_enter_adisc(rdata); |
441 | break; |
442 | case RPORT_ST_DELETE: |
443 | FC_RPORT_DBG(rdata, "Restart deleted port\n" ); |
444 | break; |
445 | case RPORT_ST_INIT: |
446 | FC_RPORT_DBG(rdata, "Login to port\n" ); |
447 | fc_rport_enter_flogi(rdata); |
448 | break; |
449 | default: |
450 | FC_RPORT_DBG(rdata, "Login in progress, state %s\n" , |
451 | fc_rport_state(rdata)); |
452 | break; |
453 | } |
454 | mutex_unlock(lock: &rdata->rp_mutex); |
455 | |
456 | return 0; |
457 | } |
458 | EXPORT_SYMBOL(fc_rport_login); |
459 | |
460 | /** |
461 | * fc_rport_enter_delete() - Schedule a remote port to be deleted |
462 | * @rdata: The remote port to be deleted |
463 | * @event: The event to report as the reason for deletion |
464 | * |
465 | * Allow state change into DELETE only once. |
466 | * |
467 | * Call queue_work only if there's no event already pending. |
468 | * Set the new event so that the old pending event will not occur. |
469 | * Since we have the mutex, even if fc_rport_work() is already started, |
470 | * it'll see the new event. |
471 | * |
472 | * Reference counting: does not modify kref |
473 | */ |
474 | static void fc_rport_enter_delete(struct fc_rport_priv *rdata, |
475 | enum fc_rport_event event) |
476 | { |
477 | lockdep_assert_held(&rdata->rp_mutex); |
478 | |
479 | if (rdata->rp_state == RPORT_ST_DELETE) |
480 | return; |
481 | |
482 | FC_RPORT_DBG(rdata, "Delete port\n" ); |
483 | |
484 | fc_rport_state_enter(rdata, new: RPORT_ST_DELETE); |
485 | |
486 | if (rdata->event == RPORT_EV_NONE) { |
487 | kref_get(kref: &rdata->kref); |
488 | if (!queue_work(wq: rport_event_queue, work: &rdata->event_work)) |
489 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
490 | } |
491 | |
492 | rdata->event = event; |
493 | } |
494 | |
495 | /** |
496 | * fc_rport_logoff() - Logoff and remove a remote port |
497 | * @rdata: The remote port to be logged off of |
498 | * |
499 | * Locking Note: Called without the rport lock held. This |
500 | * function will hold the rport lock, call an _enter_* |
501 | * function and then unlock the rport. |
502 | */ |
503 | int fc_rport_logoff(struct fc_rport_priv *rdata) |
504 | { |
505 | struct fc_lport *lport = rdata->local_port; |
506 | u32 port_id = rdata->ids.port_id; |
507 | |
508 | mutex_lock(&rdata->rp_mutex); |
509 | |
510 | FC_RPORT_DBG(rdata, "Remove port\n" ); |
511 | |
512 | rdata->flags &= ~FC_RP_STARTED; |
513 | if (rdata->rp_state == RPORT_ST_DELETE) { |
514 | FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n" ); |
515 | goto out; |
516 | } |
517 | /* |
518 | * FC-LS states: |
519 | * To explicitly Logout, the initiating Nx_Port shall terminate |
520 | * other open Sequences that it initiated with the destination |
521 | * Nx_Port prior to performing Logout. |
522 | */ |
523 | lport->tt.exch_mgr_reset(lport, 0, port_id); |
524 | lport->tt.exch_mgr_reset(lport, port_id, 0); |
525 | |
526 | fc_rport_enter_logo(rdata); |
527 | |
528 | /* |
529 | * Change the state to Delete so that we discard |
530 | * the response. |
531 | */ |
532 | fc_rport_enter_delete(rdata, event: RPORT_EV_STOP); |
533 | out: |
534 | mutex_unlock(lock: &rdata->rp_mutex); |
535 | return 0; |
536 | } |
537 | EXPORT_SYMBOL(fc_rport_logoff); |
538 | |
539 | /** |
540 | * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state |
541 | * @rdata: The remote port that is ready |
542 | * |
543 | * Reference counting: schedules workqueue, does not modify kref |
544 | */ |
545 | static void fc_rport_enter_ready(struct fc_rport_priv *rdata) |
546 | { |
547 | lockdep_assert_held(&rdata->rp_mutex); |
548 | |
549 | fc_rport_state_enter(rdata, new: RPORT_ST_READY); |
550 | |
551 | FC_RPORT_DBG(rdata, "Port is Ready\n" ); |
552 | |
553 | kref_get(kref: &rdata->kref); |
554 | if (rdata->event == RPORT_EV_NONE && |
555 | !queue_work(wq: rport_event_queue, work: &rdata->event_work)) |
556 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
557 | |
558 | rdata->event = RPORT_EV_READY; |
559 | } |
560 | |
561 | /** |
562 | * fc_rport_timeout() - Handler for the retry_work timer |
563 | * @work: Handle to the remote port that has timed out |
564 | * |
565 | * Locking Note: Called without the rport lock held. This |
566 | * function will hold the rport lock, call an _enter_* |
567 | * function and then unlock the rport. |
568 | * |
569 | * Reference counting: Drops kref on return. |
570 | */ |
571 | static void fc_rport_timeout(struct work_struct *work) |
572 | { |
573 | struct fc_rport_priv *rdata = |
574 | container_of(work, struct fc_rport_priv, retry_work.work); |
575 | |
576 | mutex_lock(&rdata->rp_mutex); |
577 | FC_RPORT_DBG(rdata, "Port timeout, state %s\n" , fc_rport_state(rdata)); |
578 | |
579 | switch (rdata->rp_state) { |
580 | case RPORT_ST_FLOGI: |
581 | fc_rport_enter_flogi(rdata); |
582 | break; |
583 | case RPORT_ST_PLOGI: |
584 | fc_rport_enter_plogi(rdata); |
585 | break; |
586 | case RPORT_ST_PRLI: |
587 | fc_rport_enter_prli(rdata); |
588 | break; |
589 | case RPORT_ST_RTV: |
590 | fc_rport_enter_rtv(rdata); |
591 | break; |
592 | case RPORT_ST_ADISC: |
593 | fc_rport_enter_adisc(rdata); |
594 | break; |
595 | case RPORT_ST_PLOGI_WAIT: |
596 | case RPORT_ST_READY: |
597 | case RPORT_ST_INIT: |
598 | case RPORT_ST_DELETE: |
599 | break; |
600 | } |
601 | |
602 | mutex_unlock(lock: &rdata->rp_mutex); |
603 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
604 | } |
605 | |
606 | /** |
607 | * fc_rport_error() - Error handler, called once retries have been exhausted |
608 | * @rdata: The remote port the error is happened on |
609 | * @err: The error code |
610 | * |
611 | * Reference counting: does not modify kref |
612 | */ |
613 | static void fc_rport_error(struct fc_rport_priv *rdata, int err) |
614 | { |
615 | struct fc_lport *lport = rdata->local_port; |
616 | |
617 | lockdep_assert_held(&rdata->rp_mutex); |
618 | |
619 | FC_RPORT_DBG(rdata, "Error %d in state %s, retries %d\n" , |
620 | -err, fc_rport_state(rdata), rdata->retries); |
621 | |
622 | switch (rdata->rp_state) { |
623 | case RPORT_ST_FLOGI: |
624 | rdata->flags &= ~FC_RP_STARTED; |
625 | fc_rport_enter_delete(rdata, event: RPORT_EV_FAILED); |
626 | break; |
627 | case RPORT_ST_PLOGI: |
628 | if (lport->point_to_multipoint) { |
629 | rdata->flags &= ~FC_RP_STARTED; |
630 | fc_rport_enter_delete(rdata, event: RPORT_EV_FAILED); |
631 | } else |
632 | fc_rport_enter_logo(rdata); |
633 | break; |
634 | case RPORT_ST_RTV: |
635 | fc_rport_enter_ready(rdata); |
636 | break; |
637 | case RPORT_ST_PRLI: |
638 | fc_rport_enter_plogi(rdata); |
639 | break; |
640 | case RPORT_ST_ADISC: |
641 | fc_rport_enter_logo(rdata); |
642 | break; |
643 | case RPORT_ST_PLOGI_WAIT: |
644 | case RPORT_ST_DELETE: |
645 | case RPORT_ST_READY: |
646 | case RPORT_ST_INIT: |
647 | break; |
648 | } |
649 | } |
650 | |
651 | /** |
652 | * fc_rport_error_retry() - Handler for remote port state retries |
653 | * @rdata: The remote port whose state is to be retried |
654 | * @err: The error code |
655 | * |
656 | * If the error was an exchange timeout retry immediately, |
657 | * otherwise wait for E_D_TOV. |
658 | * |
659 | * Reference counting: increments kref when scheduling retry_work |
660 | */ |
661 | static void fc_rport_error_retry(struct fc_rport_priv *rdata, int err) |
662 | { |
663 | unsigned long delay = msecs_to_jiffies(m: rdata->e_d_tov); |
664 | |
665 | lockdep_assert_held(&rdata->rp_mutex); |
666 | |
667 | /* make sure this isn't an FC_EX_CLOSED error, never retry those */ |
668 | if (err == -FC_EX_CLOSED) |
669 | goto out; |
670 | |
671 | if (rdata->retries < rdata->local_port->max_rport_retry_count) { |
672 | FC_RPORT_DBG(rdata, "Error %d in state %s, retrying\n" , |
673 | err, fc_rport_state(rdata)); |
674 | rdata->retries++; |
675 | /* no additional delay on exchange timeouts */ |
676 | if (err == -FC_EX_TIMEOUT) |
677 | delay = 0; |
678 | kref_get(kref: &rdata->kref); |
679 | if (!schedule_delayed_work(dwork: &rdata->retry_work, delay)) |
680 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
681 | return; |
682 | } |
683 | |
684 | out: |
685 | fc_rport_error(rdata, err); |
686 | } |
687 | |
688 | /** |
689 | * fc_rport_login_complete() - Handle parameters and completion of p-mp login. |
690 | * @rdata: The remote port which we logged into or which logged into us. |
691 | * @fp: The FLOGI or PLOGI request or response frame |
692 | * |
693 | * Returns non-zero error if a problem is detected with the frame. |
694 | * Does not free the frame. |
695 | * |
696 | * This is only used in point-to-multipoint mode for FIP currently. |
697 | */ |
698 | static int fc_rport_login_complete(struct fc_rport_priv *rdata, |
699 | struct fc_frame *fp) |
700 | { |
701 | struct fc_lport *lport = rdata->local_port; |
702 | struct fc_els_flogi *flogi; |
703 | unsigned int e_d_tov; |
704 | u16 csp_flags; |
705 | |
706 | flogi = fc_frame_payload_get(fp, len: sizeof(*flogi)); |
707 | if (!flogi) |
708 | return -EINVAL; |
709 | |
710 | csp_flags = ntohs(flogi->fl_csp.sp_features); |
711 | |
712 | if (fc_frame_payload_op(fp) == ELS_FLOGI) { |
713 | if (csp_flags & FC_SP_FT_FPORT) { |
714 | FC_RPORT_DBG(rdata, "Fabric bit set in FLOGI\n" ); |
715 | return -EINVAL; |
716 | } |
717 | } else { |
718 | |
719 | /* |
720 | * E_D_TOV is not valid on an incoming FLOGI request. |
721 | */ |
722 | e_d_tov = ntohl(flogi->fl_csp.sp_e_d_tov); |
723 | if (csp_flags & FC_SP_FT_EDTR) |
724 | e_d_tov /= 1000000; |
725 | if (e_d_tov > rdata->e_d_tov) |
726 | rdata->e_d_tov = e_d_tov; |
727 | } |
728 | rdata->maxframe_size = fc_plogi_get_maxframe(flp: flogi, maxval: lport->mfs); |
729 | return 0; |
730 | } |
731 | |
732 | /** |
733 | * fc_rport_flogi_resp() - Handle response to FLOGI request for p-mp mode |
734 | * @sp: The sequence that the FLOGI was on |
735 | * @fp: The FLOGI response frame |
736 | * @rp_arg: The remote port that received the FLOGI response |
737 | */ |
738 | static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, |
739 | void *rp_arg) |
740 | { |
741 | struct fc_rport_priv *rdata = rp_arg; |
742 | struct fc_lport *lport = rdata->local_port; |
743 | struct fc_els_flogi *flogi; |
744 | unsigned int r_a_tov; |
745 | u8 opcode; |
746 | int err = 0; |
747 | |
748 | FC_RPORT_DBG(rdata, "Received a FLOGI %s\n" , |
749 | IS_ERR(fp) ? "error" : fc_els_resp_type(fp)); |
750 | |
751 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
752 | goto put; |
753 | |
754 | mutex_lock(&rdata->rp_mutex); |
755 | |
756 | if (rdata->rp_state != RPORT_ST_FLOGI) { |
757 | FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state " |
758 | "%s\n" , fc_rport_state(rdata)); |
759 | if (IS_ERR(ptr: fp)) |
760 | goto err; |
761 | goto out; |
762 | } |
763 | |
764 | if (IS_ERR(ptr: fp)) { |
765 | fc_rport_error(rdata, err: PTR_ERR(ptr: fp)); |
766 | goto err; |
767 | } |
768 | opcode = fc_frame_payload_op(fp); |
769 | if (opcode == ELS_LS_RJT) { |
770 | struct fc_els_ls_rjt *rjt; |
771 | |
772 | rjt = fc_frame_payload_get(fp, len: sizeof(*rjt)); |
773 | FC_RPORT_DBG(rdata, "FLOGI ELS rejected, reason %x expl %x\n" , |
774 | rjt->er_reason, rjt->er_explan); |
775 | err = -FC_EX_ELS_RJT; |
776 | goto bad; |
777 | } else if (opcode != ELS_LS_ACC) { |
778 | FC_RPORT_DBG(rdata, "FLOGI ELS invalid opcode %x\n" , opcode); |
779 | err = -FC_EX_ELS_RJT; |
780 | goto bad; |
781 | } |
782 | if (fc_rport_login_complete(rdata, fp)) { |
783 | FC_RPORT_DBG(rdata, "FLOGI failed, no login\n" ); |
784 | err = -FC_EX_INV_LOGIN; |
785 | goto bad; |
786 | } |
787 | |
788 | flogi = fc_frame_payload_get(fp, len: sizeof(*flogi)); |
789 | if (!flogi) { |
790 | err = -FC_EX_ALLOC_ERR; |
791 | goto bad; |
792 | } |
793 | r_a_tov = ntohl(flogi->fl_csp.sp_r_a_tov); |
794 | if (r_a_tov > rdata->r_a_tov) |
795 | rdata->r_a_tov = r_a_tov; |
796 | |
797 | if (rdata->ids.port_name < lport->wwpn) |
798 | fc_rport_enter_plogi(rdata); |
799 | else |
800 | fc_rport_state_enter(rdata, new: RPORT_ST_PLOGI_WAIT); |
801 | out: |
802 | fc_frame_free(fp); |
803 | err: |
804 | mutex_unlock(lock: &rdata->rp_mutex); |
805 | put: |
806 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
807 | return; |
808 | bad: |
809 | FC_RPORT_DBG(rdata, "Bad FLOGI response\n" ); |
810 | fc_rport_error_retry(rdata, err); |
811 | goto out; |
812 | } |
813 | |
814 | /** |
815 | * fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp |
816 | * @rdata: The remote port to send a FLOGI to |
817 | * |
818 | * Reference counting: increments kref when sending ELS |
819 | */ |
820 | static void fc_rport_enter_flogi(struct fc_rport_priv *rdata) |
821 | { |
822 | struct fc_lport *lport = rdata->local_port; |
823 | struct fc_frame *fp; |
824 | |
825 | lockdep_assert_held(&rdata->rp_mutex); |
826 | |
827 | if (!lport->point_to_multipoint) |
828 | return fc_rport_enter_plogi(rdata); |
829 | |
830 | FC_RPORT_DBG(rdata, "Entered FLOGI state from %s state\n" , |
831 | fc_rport_state(rdata)); |
832 | |
833 | fc_rport_state_enter(rdata, new: RPORT_ST_FLOGI); |
834 | |
835 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_flogi)); |
836 | if (!fp) |
837 | return fc_rport_error_retry(rdata, err: -FC_EX_ALLOC_ERR); |
838 | |
839 | kref_get(kref: &rdata->kref); |
840 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_FLOGI, |
841 | fc_rport_flogi_resp, rdata, |
842 | 2 * lport->r_a_tov)) { |
843 | fc_rport_error_retry(rdata, err: -FC_EX_XMIT_ERR); |
844 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
845 | } |
846 | } |
847 | |
848 | /** |
849 | * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode |
850 | * @lport: The local port that received the PLOGI request |
851 | * @rx_fp: The PLOGI request frame |
852 | * |
853 | * Reference counting: drops kref on return |
854 | */ |
855 | static void fc_rport_recv_flogi_req(struct fc_lport *lport, |
856 | struct fc_frame *rx_fp) |
857 | { |
858 | struct fc_els_flogi *flp; |
859 | struct fc_rport_priv *rdata; |
860 | struct fc_frame *fp = rx_fp; |
861 | struct fc_seq_els_data rjt_data; |
862 | u32 sid; |
863 | |
864 | sid = fc_frame_sid(fp); |
865 | |
866 | FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n" ); |
867 | |
868 | if (!lport->point_to_multipoint) { |
869 | rjt_data.reason = ELS_RJT_UNSUP; |
870 | rjt_data.explan = ELS_EXPL_NONE; |
871 | goto reject; |
872 | } |
873 | |
874 | flp = fc_frame_payload_get(fp, len: sizeof(*flp)); |
875 | if (!flp) { |
876 | rjt_data.reason = ELS_RJT_LOGIC; |
877 | rjt_data.explan = ELS_EXPL_INV_LEN; |
878 | goto reject; |
879 | } |
880 | |
881 | rdata = fc_rport_lookup(lport, sid); |
882 | if (!rdata) { |
883 | rjt_data.reason = ELS_RJT_FIP; |
884 | rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR; |
885 | goto reject; |
886 | } |
887 | mutex_lock(&rdata->rp_mutex); |
888 | |
889 | FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n" , |
890 | fc_rport_state(rdata)); |
891 | |
892 | switch (rdata->rp_state) { |
893 | case RPORT_ST_INIT: |
894 | /* |
895 | * If received the FLOGI request on RPORT which is INIT state |
896 | * (means not transition to FLOGI either fc_rport timeout |
897 | * function didn;t trigger or this end hasn;t received |
898 | * beacon yet from other end. In that case only, allow RPORT |
899 | * state machine to continue, otherwise fall through which |
900 | * causes the code to send reject response. |
901 | * NOTE; Not checking for FIP->state such as VNMP_UP or |
902 | * VNMP_CLAIM because if FIP state is not one of those, |
903 | * RPORT wouldn;t have created and 'rport_lookup' would have |
904 | * failed anyway in that case. |
905 | */ |
906 | break; |
907 | case RPORT_ST_DELETE: |
908 | mutex_unlock(lock: &rdata->rp_mutex); |
909 | rjt_data.reason = ELS_RJT_FIP; |
910 | rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR; |
911 | goto reject_put; |
912 | case RPORT_ST_FLOGI: |
913 | case RPORT_ST_PLOGI_WAIT: |
914 | case RPORT_ST_PLOGI: |
915 | break; |
916 | case RPORT_ST_PRLI: |
917 | case RPORT_ST_RTV: |
918 | case RPORT_ST_READY: |
919 | case RPORT_ST_ADISC: |
920 | /* |
921 | * Set the remote port to be deleted and to then restart. |
922 | * This queues work to be sure exchanges are reset. |
923 | */ |
924 | fc_rport_enter_delete(rdata, event: RPORT_EV_LOGO); |
925 | mutex_unlock(lock: &rdata->rp_mutex); |
926 | rjt_data.reason = ELS_RJT_BUSY; |
927 | rjt_data.explan = ELS_EXPL_NONE; |
928 | goto reject_put; |
929 | } |
930 | if (fc_rport_login_complete(rdata, fp)) { |
931 | mutex_unlock(lock: &rdata->rp_mutex); |
932 | rjt_data.reason = ELS_RJT_LOGIC; |
933 | rjt_data.explan = ELS_EXPL_NONE; |
934 | goto reject_put; |
935 | } |
936 | |
937 | fp = fc_frame_alloc(dev: lport, len: sizeof(*flp)); |
938 | if (!fp) |
939 | goto out; |
940 | |
941 | fc_flogi_fill(lport, fp); |
942 | flp = fc_frame_payload_get(fp, len: sizeof(*flp)); |
943 | flp->fl_cmd = ELS_LS_ACC; |
944 | |
945 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
946 | lport->tt.frame_send(lport, fp); |
947 | |
948 | /* |
949 | * Do not proceed with the state machine if our |
950 | * FLOGI has crossed with an FLOGI from the |
951 | * remote port; wait for the FLOGI response instead. |
952 | */ |
953 | if (rdata->rp_state != RPORT_ST_FLOGI) { |
954 | if (rdata->ids.port_name < lport->wwpn) |
955 | fc_rport_enter_plogi(rdata); |
956 | else |
957 | fc_rport_state_enter(rdata, new: RPORT_ST_PLOGI_WAIT); |
958 | } |
959 | out: |
960 | mutex_unlock(lock: &rdata->rp_mutex); |
961 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
962 | fc_frame_free(fp: rx_fp); |
963 | return; |
964 | |
965 | reject_put: |
966 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
967 | reject: |
968 | fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); |
969 | fc_frame_free(fp: rx_fp); |
970 | } |
971 | |
972 | /** |
973 | * fc_rport_plogi_resp() - Handler for ELS PLOGI responses |
974 | * @sp: The sequence the PLOGI is on |
975 | * @fp: The PLOGI response frame |
976 | * @rdata_arg: The remote port that sent the PLOGI response |
977 | * |
978 | * Locking Note: This function will be called without the rport lock |
979 | * held, but it will lock, call an _enter_* function or fc_rport_error |
980 | * and then unlock the rport. |
981 | */ |
982 | static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, |
983 | void *rdata_arg) |
984 | { |
985 | struct fc_rport_priv *rdata = rdata_arg; |
986 | struct fc_lport *lport = rdata->local_port; |
987 | struct fc_els_flogi *plp = NULL; |
988 | u16 csp_seq; |
989 | u16 cssp_seq; |
990 | u8 op; |
991 | |
992 | FC_RPORT_DBG(rdata, "Received a PLOGI %s\n" , fc_els_resp_type(fp)); |
993 | |
994 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
995 | goto put; |
996 | |
997 | mutex_lock(&rdata->rp_mutex); |
998 | |
999 | if (rdata->rp_state != RPORT_ST_PLOGI) { |
1000 | FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state " |
1001 | "%s\n" , fc_rport_state(rdata)); |
1002 | if (IS_ERR(ptr: fp)) |
1003 | goto err; |
1004 | goto out; |
1005 | } |
1006 | |
1007 | if (IS_ERR(ptr: fp)) { |
1008 | fc_rport_error_retry(rdata, err: PTR_ERR(ptr: fp)); |
1009 | goto err; |
1010 | } |
1011 | |
1012 | op = fc_frame_payload_op(fp); |
1013 | if (op == ELS_LS_ACC && |
1014 | (plp = fc_frame_payload_get(fp, len: sizeof(*plp))) != NULL) { |
1015 | rdata->ids.port_name = get_unaligned_be64(p: &plp->fl_wwpn); |
1016 | rdata->ids.node_name = get_unaligned_be64(p: &plp->fl_wwnn); |
1017 | |
1018 | /* save plogi response sp_features for further reference */ |
1019 | rdata->sp_features = ntohs(plp->fl_csp.sp_features); |
1020 | |
1021 | if (lport->point_to_multipoint) |
1022 | fc_rport_login_complete(rdata, fp); |
1023 | csp_seq = ntohs(plp->fl_csp.sp_tot_seq); |
1024 | cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq); |
1025 | if (cssp_seq < csp_seq) |
1026 | csp_seq = cssp_seq; |
1027 | rdata->max_seq = csp_seq; |
1028 | rdata->maxframe_size = fc_plogi_get_maxframe(flp: plp, maxval: lport->mfs); |
1029 | fc_rport_enter_prli(rdata); |
1030 | } else { |
1031 | struct fc_els_ls_rjt *rjt; |
1032 | |
1033 | rjt = fc_frame_payload_get(fp, len: sizeof(*rjt)); |
1034 | if (!rjt) |
1035 | FC_RPORT_DBG(rdata, "PLOGI bad response\n" ); |
1036 | else |
1037 | FC_RPORT_DBG(rdata, "PLOGI ELS rejected, reason %x expl %x\n" , |
1038 | rjt->er_reason, rjt->er_explan); |
1039 | fc_rport_error_retry(rdata, err: -FC_EX_ELS_RJT); |
1040 | } |
1041 | out: |
1042 | fc_frame_free(fp); |
1043 | err: |
1044 | mutex_unlock(lock: &rdata->rp_mutex); |
1045 | put: |
1046 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1047 | } |
1048 | |
1049 | static bool |
1050 | fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata) |
1051 | { |
1052 | if (rdata->ids.roles == FC_PORT_ROLE_UNKNOWN) |
1053 | return true; |
1054 | if ((rdata->ids.roles & FC_PORT_ROLE_FCP_TARGET) && |
1055 | (lport->service_params & FCP_SPPF_INIT_FCN)) |
1056 | return true; |
1057 | if ((rdata->ids.roles & FC_PORT_ROLE_FCP_INITIATOR) && |
1058 | (lport->service_params & FCP_SPPF_TARG_FCN)) |
1059 | return true; |
1060 | return false; |
1061 | } |
1062 | |
1063 | /** |
1064 | * fc_rport_enter_plogi() - Send Port Login (PLOGI) request |
1065 | * @rdata: The remote port to send a PLOGI to |
1066 | * |
1067 | * Reference counting: increments kref when sending ELS |
1068 | */ |
1069 | static void fc_rport_enter_plogi(struct fc_rport_priv *rdata) |
1070 | { |
1071 | struct fc_lport *lport = rdata->local_port; |
1072 | struct fc_frame *fp; |
1073 | |
1074 | lockdep_assert_held(&rdata->rp_mutex); |
1075 | |
1076 | if (!fc_rport_compatible_roles(lport, rdata)) { |
1077 | FC_RPORT_DBG(rdata, "PLOGI suppressed for incompatible role\n" ); |
1078 | fc_rport_state_enter(rdata, new: RPORT_ST_PLOGI_WAIT); |
1079 | return; |
1080 | } |
1081 | |
1082 | FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n" , |
1083 | fc_rport_state(rdata)); |
1084 | |
1085 | fc_rport_state_enter(rdata, new: RPORT_ST_PLOGI); |
1086 | |
1087 | rdata->maxframe_size = FC_MIN_MAX_PAYLOAD; |
1088 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_flogi)); |
1089 | if (!fp) { |
1090 | FC_RPORT_DBG(rdata, "%s frame alloc failed\n" , __func__); |
1091 | fc_rport_error_retry(rdata, err: -FC_EX_ALLOC_ERR); |
1092 | return; |
1093 | } |
1094 | rdata->e_d_tov = lport->e_d_tov; |
1095 | |
1096 | kref_get(kref: &rdata->kref); |
1097 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI, |
1098 | fc_rport_plogi_resp, rdata, |
1099 | 2 * lport->r_a_tov)) { |
1100 | fc_rport_error_retry(rdata, err: -FC_EX_XMIT_ERR); |
1101 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1102 | } |
1103 | } |
1104 | |
1105 | /** |
1106 | * fc_rport_prli_resp() - Process Login (PRLI) response handler |
1107 | * @sp: The sequence the PRLI response was on |
1108 | * @fp: The PRLI response frame |
1109 | * @rdata_arg: The remote port that sent the PRLI response |
1110 | * |
1111 | * Locking Note: This function will be called without the rport lock |
1112 | * held, but it will lock, call an _enter_* function or fc_rport_error |
1113 | * and then unlock the rport. |
1114 | */ |
1115 | static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, |
1116 | void *rdata_arg) |
1117 | { |
1118 | struct fc_rport_priv *rdata = rdata_arg; |
1119 | struct { |
1120 | struct fc_els_prli prli; |
1121 | struct fc_els_spp spp; |
1122 | } *pp; |
1123 | struct fc_els_spp temp_spp; |
1124 | struct fc_els_ls_rjt *rjt; |
1125 | struct fc4_prov *prov; |
1126 | u32 roles = FC_RPORT_ROLE_UNKNOWN; |
1127 | u32 fcp_parm = 0; |
1128 | u8 op; |
1129 | enum fc_els_spp_resp resp_code; |
1130 | |
1131 | FC_RPORT_DBG(rdata, "Received a PRLI %s\n" , fc_els_resp_type(fp)); |
1132 | |
1133 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1134 | goto put; |
1135 | |
1136 | mutex_lock(&rdata->rp_mutex); |
1137 | |
1138 | if (rdata->rp_state != RPORT_ST_PRLI) { |
1139 | FC_RPORT_DBG(rdata, "Received a PRLI response, but in state " |
1140 | "%s\n" , fc_rport_state(rdata)); |
1141 | if (IS_ERR(ptr: fp)) |
1142 | goto err; |
1143 | goto out; |
1144 | } |
1145 | |
1146 | if (IS_ERR(ptr: fp)) { |
1147 | fc_rport_error_retry(rdata, err: PTR_ERR(ptr: fp)); |
1148 | goto err; |
1149 | } |
1150 | |
1151 | /* reinitialize remote port roles */ |
1152 | rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; |
1153 | |
1154 | op = fc_frame_payload_op(fp); |
1155 | if (op == ELS_LS_ACC) { |
1156 | pp = fc_frame_payload_get(fp, len: sizeof(*pp)); |
1157 | if (!pp) { |
1158 | fc_rport_error_retry(rdata, err: -FC_EX_SEQ_ERR); |
1159 | goto out; |
1160 | } |
1161 | |
1162 | resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK); |
1163 | FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x spp_type 0x%x\n" , |
1164 | pp->spp.spp_flags, pp->spp.spp_type); |
1165 | |
1166 | rdata->spp_type = pp->spp.spp_type; |
1167 | if (resp_code != FC_SPP_RESP_ACK) { |
1168 | if (resp_code == FC_SPP_RESP_CONF) |
1169 | fc_rport_error(rdata, err: -FC_EX_SEQ_ERR); |
1170 | else |
1171 | fc_rport_error_retry(rdata, err: -FC_EX_SEQ_ERR); |
1172 | goto out; |
1173 | } |
1174 | if (pp->prli.prli_spp_len < sizeof(pp->spp)) { |
1175 | fc_rport_error_retry(rdata, err: -FC_EX_SEQ_ERR); |
1176 | goto out; |
1177 | } |
1178 | |
1179 | fcp_parm = ntohl(pp->spp.spp_params); |
1180 | if (fcp_parm & FCP_SPPF_RETRY) |
1181 | rdata->flags |= FC_RP_FLAGS_RETRY; |
1182 | if (fcp_parm & FCP_SPPF_CONF_COMPL) |
1183 | rdata->flags |= FC_RP_FLAGS_CONF_REQ; |
1184 | |
1185 | /* |
1186 | * Call prli provider if we should act as a target |
1187 | */ |
1188 | if (rdata->spp_type < FC_FC4_PROV_SIZE) { |
1189 | prov = fc_passive_prov[rdata->spp_type]; |
1190 | if (prov) { |
1191 | memset(&temp_spp, 0, sizeof(temp_spp)); |
1192 | prov->prli(rdata, pp->prli.prli_spp_len, |
1193 | &pp->spp, &temp_spp); |
1194 | } |
1195 | } |
1196 | /* |
1197 | * Check if the image pair could be established |
1198 | */ |
1199 | if (rdata->spp_type != FC_TYPE_FCP || |
1200 | !(pp->spp.spp_flags & FC_SPP_EST_IMG_PAIR)) { |
1201 | /* |
1202 | * Nope; we can't use this port as a target. |
1203 | */ |
1204 | fcp_parm &= ~FCP_SPPF_TARG_FCN; |
1205 | } |
1206 | rdata->supported_classes = FC_COS_CLASS3; |
1207 | if (fcp_parm & FCP_SPPF_INIT_FCN) |
1208 | roles |= FC_RPORT_ROLE_FCP_INITIATOR; |
1209 | if (fcp_parm & FCP_SPPF_TARG_FCN) |
1210 | roles |= FC_RPORT_ROLE_FCP_TARGET; |
1211 | |
1212 | rdata->ids.roles = roles; |
1213 | fc_rport_enter_rtv(rdata); |
1214 | |
1215 | } else { |
1216 | rjt = fc_frame_payload_get(fp, len: sizeof(*rjt)); |
1217 | if (!rjt) |
1218 | FC_RPORT_DBG(rdata, "PRLI bad response\n" ); |
1219 | else { |
1220 | FC_RPORT_DBG(rdata, "PRLI ELS rejected, reason %x expl %x\n" , |
1221 | rjt->er_reason, rjt->er_explan); |
1222 | if (rjt->er_reason == ELS_RJT_UNAB && |
1223 | rjt->er_explan == ELS_EXPL_PLOGI_REQD) { |
1224 | fc_rport_enter_plogi(rdata); |
1225 | goto out; |
1226 | } |
1227 | } |
1228 | fc_rport_error_retry(rdata, FC_EX_ELS_RJT); |
1229 | } |
1230 | |
1231 | out: |
1232 | fc_frame_free(fp); |
1233 | err: |
1234 | mutex_unlock(lock: &rdata->rp_mutex); |
1235 | put: |
1236 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1237 | } |
1238 | |
1239 | /** |
1240 | * fc_rport_enter_prli() - Send Process Login (PRLI) request |
1241 | * @rdata: The remote port to send the PRLI request to |
1242 | * |
1243 | * Reference counting: increments kref when sending ELS |
1244 | */ |
1245 | static void fc_rport_enter_prli(struct fc_rport_priv *rdata) |
1246 | { |
1247 | struct fc_lport *lport = rdata->local_port; |
1248 | struct { |
1249 | struct fc_els_prli prli; |
1250 | struct fc_els_spp spp; |
1251 | } *pp; |
1252 | struct fc_frame *fp; |
1253 | struct fc4_prov *prov; |
1254 | |
1255 | lockdep_assert_held(&rdata->rp_mutex); |
1256 | |
1257 | /* |
1258 | * If the rport is one of the well known addresses |
1259 | * we skip PRLI and RTV and go straight to READY. |
1260 | */ |
1261 | if (rdata->ids.port_id >= FC_FID_DOM_MGR) { |
1262 | fc_rport_enter_ready(rdata); |
1263 | return; |
1264 | } |
1265 | |
1266 | /* |
1267 | * And if the local port does not support the initiator function |
1268 | * there's no need to send a PRLI, either. |
1269 | */ |
1270 | if (!(lport->service_params & FCP_SPPF_INIT_FCN)) { |
1271 | fc_rport_enter_ready(rdata); |
1272 | return; |
1273 | } |
1274 | |
1275 | FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n" , |
1276 | fc_rport_state(rdata)); |
1277 | |
1278 | fc_rport_state_enter(rdata, new: RPORT_ST_PRLI); |
1279 | |
1280 | fp = fc_frame_alloc(dev: lport, len: sizeof(*pp)); |
1281 | if (!fp) { |
1282 | fc_rport_error_retry(rdata, err: -FC_EX_ALLOC_ERR); |
1283 | return; |
1284 | } |
1285 | |
1286 | fc_prli_fill(lport, fp); |
1287 | |
1288 | prov = fc_passive_prov[FC_TYPE_FCP]; |
1289 | if (prov) { |
1290 | pp = fc_frame_payload_get(fp, len: sizeof(*pp)); |
1291 | prov->prli(rdata, sizeof(pp->spp), NULL, &pp->spp); |
1292 | } |
1293 | |
1294 | fc_fill_fc_hdr(fp, r_ctl: FC_RCTL_ELS_REQ, did: rdata->ids.port_id, |
1295 | fc_host_port_id(lport->host), type: FC_TYPE_ELS, |
1296 | FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, parm_offset: 0); |
1297 | |
1298 | kref_get(kref: &rdata->kref); |
1299 | if (!fc_exch_seq_send(lport, fp, resp: fc_rport_prli_resp, |
1300 | NULL, arg: rdata, timer_msec: 2 * lport->r_a_tov)) { |
1301 | fc_rport_error_retry(rdata, err: -FC_EX_XMIT_ERR); |
1302 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1303 | } |
1304 | } |
1305 | |
1306 | /** |
1307 | * fc_rport_rtv_resp() - Handler for Request Timeout Value (RTV) responses |
1308 | * @sp: The sequence the RTV was on |
1309 | * @fp: The RTV response frame |
1310 | * @rdata_arg: The remote port that sent the RTV response |
1311 | * |
1312 | * Many targets don't seem to support this. |
1313 | * |
1314 | * Locking Note: This function will be called without the rport lock |
1315 | * held, but it will lock, call an _enter_* function or fc_rport_error |
1316 | * and then unlock the rport. |
1317 | */ |
1318 | static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp, |
1319 | void *rdata_arg) |
1320 | { |
1321 | struct fc_rport_priv *rdata = rdata_arg; |
1322 | u8 op; |
1323 | |
1324 | FC_RPORT_DBG(rdata, "Received a RTV %s\n" , fc_els_resp_type(fp)); |
1325 | |
1326 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1327 | goto put; |
1328 | |
1329 | mutex_lock(&rdata->rp_mutex); |
1330 | |
1331 | if (rdata->rp_state != RPORT_ST_RTV) { |
1332 | FC_RPORT_DBG(rdata, "Received a RTV response, but in state " |
1333 | "%s\n" , fc_rport_state(rdata)); |
1334 | if (IS_ERR(ptr: fp)) |
1335 | goto err; |
1336 | goto out; |
1337 | } |
1338 | |
1339 | if (IS_ERR(ptr: fp)) { |
1340 | fc_rport_error(rdata, err: PTR_ERR(ptr: fp)); |
1341 | goto err; |
1342 | } |
1343 | |
1344 | op = fc_frame_payload_op(fp); |
1345 | if (op == ELS_LS_ACC) { |
1346 | struct fc_els_rtv_acc *rtv; |
1347 | u32 toq; |
1348 | u32 tov; |
1349 | |
1350 | rtv = fc_frame_payload_get(fp, len: sizeof(*rtv)); |
1351 | if (rtv) { |
1352 | toq = ntohl(rtv->rtv_toq); |
1353 | tov = ntohl(rtv->rtv_r_a_tov); |
1354 | if (tov == 0) |
1355 | tov = 1; |
1356 | if (tov > rdata->r_a_tov) |
1357 | rdata->r_a_tov = tov; |
1358 | tov = ntohl(rtv->rtv_e_d_tov); |
1359 | if (toq & FC_ELS_RTV_EDRES) |
1360 | tov /= 1000000; |
1361 | if (tov == 0) |
1362 | tov = 1; |
1363 | if (tov > rdata->e_d_tov) |
1364 | rdata->e_d_tov = tov; |
1365 | } |
1366 | } |
1367 | |
1368 | fc_rport_enter_ready(rdata); |
1369 | |
1370 | out: |
1371 | fc_frame_free(fp); |
1372 | err: |
1373 | mutex_unlock(lock: &rdata->rp_mutex); |
1374 | put: |
1375 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1376 | } |
1377 | |
1378 | /** |
1379 | * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request |
1380 | * @rdata: The remote port to send the RTV request to |
1381 | * |
1382 | * Reference counting: increments kref when sending ELS |
1383 | */ |
1384 | static void fc_rport_enter_rtv(struct fc_rport_priv *rdata) |
1385 | { |
1386 | struct fc_frame *fp; |
1387 | struct fc_lport *lport = rdata->local_port; |
1388 | |
1389 | lockdep_assert_held(&rdata->rp_mutex); |
1390 | |
1391 | FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n" , |
1392 | fc_rport_state(rdata)); |
1393 | |
1394 | fc_rport_state_enter(rdata, new: RPORT_ST_RTV); |
1395 | |
1396 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_rtv)); |
1397 | if (!fp) { |
1398 | fc_rport_error_retry(rdata, err: -FC_EX_ALLOC_ERR); |
1399 | return; |
1400 | } |
1401 | |
1402 | kref_get(kref: &rdata->kref); |
1403 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV, |
1404 | fc_rport_rtv_resp, rdata, |
1405 | 2 * lport->r_a_tov)) { |
1406 | fc_rport_error_retry(rdata, err: -FC_EX_XMIT_ERR); |
1407 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1408 | } |
1409 | } |
1410 | |
1411 | /** |
1412 | * fc_rport_recv_rtv_req() - Handler for Read Timeout Value (RTV) requests |
1413 | * @rdata: The remote port that sent the RTV request |
1414 | * @in_fp: The RTV request frame |
1415 | */ |
1416 | static void fc_rport_recv_rtv_req(struct fc_rport_priv *rdata, |
1417 | struct fc_frame *in_fp) |
1418 | { |
1419 | struct fc_lport *lport = rdata->local_port; |
1420 | struct fc_frame *fp; |
1421 | struct fc_els_rtv_acc *rtv; |
1422 | struct fc_seq_els_data rjt_data; |
1423 | |
1424 | lockdep_assert_held(&rdata->rp_mutex); |
1425 | lockdep_assert_held(&lport->lp_mutex); |
1426 | |
1427 | FC_RPORT_DBG(rdata, "Received RTV request\n" ); |
1428 | |
1429 | fp = fc_frame_alloc(dev: lport, len: sizeof(*rtv)); |
1430 | if (!fp) { |
1431 | rjt_data.reason = ELS_RJT_UNAB; |
1432 | rjt_data.explan = ELS_EXPL_INSUF_RES; |
1433 | fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); |
1434 | goto drop; |
1435 | } |
1436 | rtv = fc_frame_payload_get(fp, len: sizeof(*rtv)); |
1437 | rtv->rtv_cmd = ELS_LS_ACC; |
1438 | rtv->rtv_r_a_tov = htonl(lport->r_a_tov); |
1439 | rtv->rtv_e_d_tov = htonl(lport->e_d_tov); |
1440 | rtv->rtv_toq = 0; |
1441 | fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
1442 | lport->tt.frame_send(lport, fp); |
1443 | drop: |
1444 | fc_frame_free(fp: in_fp); |
1445 | } |
1446 | |
1447 | /** |
1448 | * fc_rport_logo_resp() - Handler for logout (LOGO) responses |
1449 | * @sp: The sequence the LOGO was on |
1450 | * @fp: The LOGO response frame |
1451 | * @rdata_arg: The remote port |
1452 | */ |
1453 | static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, |
1454 | void *rdata_arg) |
1455 | { |
1456 | struct fc_rport_priv *rdata = rdata_arg; |
1457 | struct fc_lport *lport = rdata->local_port; |
1458 | |
1459 | FC_RPORT_ID_DBG(lport, fc_seq_exch(sp)->did, |
1460 | "Received a LOGO %s\n" , fc_els_resp_type(fp)); |
1461 | if (!IS_ERR(ptr: fp)) |
1462 | fc_frame_free(fp); |
1463 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1464 | } |
1465 | |
1466 | /** |
1467 | * fc_rport_enter_logo() - Send a logout (LOGO) request |
1468 | * @rdata: The remote port to send the LOGO request to |
1469 | * |
1470 | * Reference counting: increments kref when sending ELS |
1471 | */ |
1472 | static void fc_rport_enter_logo(struct fc_rport_priv *rdata) |
1473 | { |
1474 | struct fc_lport *lport = rdata->local_port; |
1475 | struct fc_frame *fp; |
1476 | |
1477 | lockdep_assert_held(&rdata->rp_mutex); |
1478 | |
1479 | FC_RPORT_DBG(rdata, "Port sending LOGO from %s state\n" , |
1480 | fc_rport_state(rdata)); |
1481 | |
1482 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_logo)); |
1483 | if (!fp) |
1484 | return; |
1485 | kref_get(kref: &rdata->kref); |
1486 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO, |
1487 | fc_rport_logo_resp, rdata, 0)) |
1488 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1489 | } |
1490 | |
1491 | /** |
1492 | * fc_rport_adisc_resp() - Handler for Address Discovery (ADISC) responses |
1493 | * @sp: The sequence the ADISC response was on |
1494 | * @fp: The ADISC response frame |
1495 | * @rdata_arg: The remote port that sent the ADISC response |
1496 | * |
1497 | * Locking Note: This function will be called without the rport lock |
1498 | * held, but it will lock, call an _enter_* function or fc_rport_error |
1499 | * and then unlock the rport. |
1500 | */ |
1501 | static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp, |
1502 | void *rdata_arg) |
1503 | { |
1504 | struct fc_rport_priv *rdata = rdata_arg; |
1505 | struct fc_els_adisc *adisc; |
1506 | u8 op; |
1507 | |
1508 | FC_RPORT_DBG(rdata, "Received a ADISC response\n" ); |
1509 | |
1510 | if (fp == ERR_PTR(error: -FC_EX_CLOSED)) |
1511 | goto put; |
1512 | |
1513 | mutex_lock(&rdata->rp_mutex); |
1514 | |
1515 | if (rdata->rp_state != RPORT_ST_ADISC) { |
1516 | FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n" , |
1517 | fc_rport_state(rdata)); |
1518 | if (IS_ERR(ptr: fp)) |
1519 | goto err; |
1520 | goto out; |
1521 | } |
1522 | |
1523 | if (IS_ERR(ptr: fp)) { |
1524 | fc_rport_error(rdata, err: PTR_ERR(ptr: fp)); |
1525 | goto err; |
1526 | } |
1527 | |
1528 | /* |
1529 | * If address verification failed. Consider us logged out of the rport. |
1530 | * Since the rport is still in discovery, we want to be |
1531 | * logged in, so go to PLOGI state. Otherwise, go back to READY. |
1532 | */ |
1533 | op = fc_frame_payload_op(fp); |
1534 | adisc = fc_frame_payload_get(fp, len: sizeof(*adisc)); |
1535 | if (op != ELS_LS_ACC || !adisc || |
1536 | ntoh24(p: adisc->adisc_port_id) != rdata->ids.port_id || |
1537 | get_unaligned_be64(p: &adisc->adisc_wwpn) != rdata->ids.port_name || |
1538 | get_unaligned_be64(p: &adisc->adisc_wwnn) != rdata->ids.node_name) { |
1539 | FC_RPORT_DBG(rdata, "ADISC error or mismatch\n" ); |
1540 | fc_rport_enter_flogi(rdata); |
1541 | } else { |
1542 | FC_RPORT_DBG(rdata, "ADISC OK\n" ); |
1543 | fc_rport_enter_ready(rdata); |
1544 | } |
1545 | out: |
1546 | fc_frame_free(fp); |
1547 | err: |
1548 | mutex_unlock(lock: &rdata->rp_mutex); |
1549 | put: |
1550 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1551 | } |
1552 | |
1553 | /** |
1554 | * fc_rport_enter_adisc() - Send Address Discover (ADISC) request |
1555 | * @rdata: The remote port to send the ADISC request to |
1556 | * |
1557 | * Reference counting: increments kref when sending ELS |
1558 | */ |
1559 | static void fc_rport_enter_adisc(struct fc_rport_priv *rdata) |
1560 | { |
1561 | struct fc_lport *lport = rdata->local_port; |
1562 | struct fc_frame *fp; |
1563 | |
1564 | lockdep_assert_held(&rdata->rp_mutex); |
1565 | |
1566 | FC_RPORT_DBG(rdata, "sending ADISC from %s state\n" , |
1567 | fc_rport_state(rdata)); |
1568 | |
1569 | fc_rport_state_enter(rdata, new: RPORT_ST_ADISC); |
1570 | |
1571 | fp = fc_frame_alloc(dev: lport, len: sizeof(struct fc_els_adisc)); |
1572 | if (!fp) { |
1573 | fc_rport_error_retry(rdata, err: -FC_EX_ALLOC_ERR); |
1574 | return; |
1575 | } |
1576 | kref_get(kref: &rdata->kref); |
1577 | if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC, |
1578 | fc_rport_adisc_resp, rdata, |
1579 | 2 * lport->r_a_tov)) { |
1580 | fc_rport_error_retry(rdata, err: -FC_EX_XMIT_ERR); |
1581 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1582 | } |
1583 | } |
1584 | |
1585 | /** |
1586 | * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests |
1587 | * @rdata: The remote port that sent the ADISC request |
1588 | * @in_fp: The ADISC request frame |
1589 | */ |
1590 | static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata, |
1591 | struct fc_frame *in_fp) |
1592 | { |
1593 | struct fc_lport *lport = rdata->local_port; |
1594 | struct fc_frame *fp; |
1595 | struct fc_els_adisc *adisc; |
1596 | struct fc_seq_els_data rjt_data; |
1597 | |
1598 | lockdep_assert_held(&rdata->rp_mutex); |
1599 | lockdep_assert_held(&lport->lp_mutex); |
1600 | |
1601 | FC_RPORT_DBG(rdata, "Received ADISC request\n" ); |
1602 | |
1603 | adisc = fc_frame_payload_get(fp: in_fp, len: sizeof(*adisc)); |
1604 | if (!adisc) { |
1605 | rjt_data.reason = ELS_RJT_PROT; |
1606 | rjt_data.explan = ELS_EXPL_INV_LEN; |
1607 | fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data); |
1608 | goto drop; |
1609 | } |
1610 | |
1611 | fp = fc_frame_alloc(dev: lport, len: sizeof(*adisc)); |
1612 | if (!fp) |
1613 | goto drop; |
1614 | fc_adisc_fill(lport, fp); |
1615 | adisc = fc_frame_payload_get(fp, len: sizeof(*adisc)); |
1616 | adisc->adisc_cmd = ELS_LS_ACC; |
1617 | fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
1618 | lport->tt.frame_send(lport, fp); |
1619 | drop: |
1620 | fc_frame_free(fp: in_fp); |
1621 | } |
1622 | |
1623 | /** |
1624 | * fc_rport_recv_rls_req() - Handle received Read Link Status request |
1625 | * @rdata: The remote port that sent the RLS request |
1626 | * @rx_fp: The PRLI request frame |
1627 | */ |
1628 | static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata, |
1629 | struct fc_frame *rx_fp) |
1630 | |
1631 | { |
1632 | struct fc_lport *lport = rdata->local_port; |
1633 | struct fc_frame *fp; |
1634 | struct fc_els_rls *rls; |
1635 | struct fc_els_rls_resp *rsp; |
1636 | struct fc_els_lesb *lesb; |
1637 | struct fc_seq_els_data rjt_data; |
1638 | struct fc_host_statistics *hst; |
1639 | |
1640 | lockdep_assert_held(&rdata->rp_mutex); |
1641 | |
1642 | FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n" , |
1643 | fc_rport_state(rdata)); |
1644 | |
1645 | rls = fc_frame_payload_get(fp: rx_fp, len: sizeof(*rls)); |
1646 | if (!rls) { |
1647 | rjt_data.reason = ELS_RJT_PROT; |
1648 | rjt_data.explan = ELS_EXPL_INV_LEN; |
1649 | goto out_rjt; |
1650 | } |
1651 | |
1652 | fp = fc_frame_alloc(dev: lport, len: sizeof(*rsp)); |
1653 | if (!fp) { |
1654 | rjt_data.reason = ELS_RJT_UNAB; |
1655 | rjt_data.explan = ELS_EXPL_INSUF_RES; |
1656 | goto out_rjt; |
1657 | } |
1658 | |
1659 | rsp = fc_frame_payload_get(fp, len: sizeof(*rsp)); |
1660 | memset(rsp, 0, sizeof(*rsp)); |
1661 | rsp->rls_cmd = ELS_LS_ACC; |
1662 | lesb = &rsp->rls_lesb; |
1663 | if (lport->tt.get_lesb) { |
1664 | /* get LESB from LLD if it supports it */ |
1665 | lport->tt.get_lesb(lport, lesb); |
1666 | } else { |
1667 | fc_get_host_stats(lport->host); |
1668 | hst = &lport->host_stats; |
1669 | lesb->lesb_link_fail = htonl(hst->link_failure_count); |
1670 | lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count); |
1671 | lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count); |
1672 | lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count); |
1673 | lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count); |
1674 | lesb->lesb_inv_crc = htonl(hst->invalid_crc_count); |
1675 | } |
1676 | |
1677 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
1678 | lport->tt.frame_send(lport, fp); |
1679 | goto out; |
1680 | |
1681 | out_rjt: |
1682 | fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); |
1683 | out: |
1684 | fc_frame_free(fp: rx_fp); |
1685 | } |
1686 | |
1687 | /** |
1688 | * fc_rport_recv_els_req() - Handler for validated ELS requests |
1689 | * @lport: The local port that received the ELS request |
1690 | * @fp: The ELS request frame |
1691 | * |
1692 | * Handle incoming ELS requests that require port login. |
1693 | * The ELS opcode has already been validated by the caller. |
1694 | * |
1695 | * Reference counting: does not modify kref |
1696 | */ |
1697 | static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp) |
1698 | { |
1699 | struct fc_rport_priv *rdata; |
1700 | struct fc_seq_els_data els_data; |
1701 | |
1702 | lockdep_assert_held(&lport->lp_mutex); |
1703 | |
1704 | rdata = fc_rport_lookup(lport, fc_frame_sid(fp)); |
1705 | if (!rdata) { |
1706 | FC_RPORT_ID_DBG(lport, fc_frame_sid(fp), |
1707 | "Received ELS 0x%02x from non-logged-in port\n" , |
1708 | fc_frame_payload_op(fp)); |
1709 | goto reject; |
1710 | } |
1711 | |
1712 | mutex_lock(&rdata->rp_mutex); |
1713 | |
1714 | switch (rdata->rp_state) { |
1715 | case RPORT_ST_PRLI: |
1716 | case RPORT_ST_RTV: |
1717 | case RPORT_ST_READY: |
1718 | case RPORT_ST_ADISC: |
1719 | break; |
1720 | case RPORT_ST_PLOGI: |
1721 | if (fc_frame_payload_op(fp) == ELS_PRLI) { |
1722 | FC_RPORT_DBG(rdata, "Reject ELS PRLI " |
1723 | "while in state %s\n" , |
1724 | fc_rport_state(rdata)); |
1725 | mutex_unlock(lock: &rdata->rp_mutex); |
1726 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1727 | goto busy; |
1728 | } |
1729 | fallthrough; |
1730 | default: |
1731 | FC_RPORT_DBG(rdata, |
1732 | "Reject ELS 0x%02x while in state %s\n" , |
1733 | fc_frame_payload_op(fp), fc_rport_state(rdata)); |
1734 | mutex_unlock(lock: &rdata->rp_mutex); |
1735 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1736 | goto reject; |
1737 | } |
1738 | |
1739 | switch (fc_frame_payload_op(fp)) { |
1740 | case ELS_PRLI: |
1741 | fc_rport_recv_prli_req(rdata, fp); |
1742 | break; |
1743 | case ELS_PRLO: |
1744 | fc_rport_recv_prlo_req(rdata, fp); |
1745 | break; |
1746 | case ELS_ADISC: |
1747 | fc_rport_recv_adisc_req(rdata, in_fp: fp); |
1748 | break; |
1749 | case ELS_RRQ: |
1750 | fc_seq_els_rsp_send(fp, ELS_RRQ, NULL); |
1751 | fc_frame_free(fp); |
1752 | break; |
1753 | case ELS_REC: |
1754 | fc_seq_els_rsp_send(fp, ELS_REC, NULL); |
1755 | fc_frame_free(fp); |
1756 | break; |
1757 | case ELS_RLS: |
1758 | fc_rport_recv_rls_req(rdata, rx_fp: fp); |
1759 | break; |
1760 | case ELS_RTV: |
1761 | fc_rport_recv_rtv_req(rdata, in_fp: fp); |
1762 | break; |
1763 | default: |
1764 | fc_frame_free(fp); /* can't happen */ |
1765 | break; |
1766 | } |
1767 | |
1768 | mutex_unlock(lock: &rdata->rp_mutex); |
1769 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
1770 | return; |
1771 | |
1772 | reject: |
1773 | els_data.reason = ELS_RJT_UNAB; |
1774 | els_data.explan = ELS_EXPL_PLOGI_REQD; |
1775 | fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data); |
1776 | fc_frame_free(fp); |
1777 | return; |
1778 | |
1779 | busy: |
1780 | els_data.reason = ELS_RJT_BUSY; |
1781 | els_data.explan = ELS_EXPL_NONE; |
1782 | fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data); |
1783 | fc_frame_free(fp); |
1784 | return; |
1785 | } |
1786 | |
1787 | /** |
1788 | * fc_rport_recv_req() - Handler for requests |
1789 | * @lport: The local port that received the request |
1790 | * @fp: The request frame |
1791 | * |
1792 | * Reference counting: does not modify kref |
1793 | */ |
1794 | void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) |
1795 | { |
1796 | struct fc_seq_els_data els_data; |
1797 | |
1798 | lockdep_assert_held(&lport->lp_mutex); |
1799 | |
1800 | /* |
1801 | * Handle FLOGI, PLOGI and LOGO requests separately, since they |
1802 | * don't require prior login. |
1803 | * Check for unsupported opcodes first and reject them. |
1804 | * For some ops, it would be incorrect to reject with "PLOGI required". |
1805 | */ |
1806 | switch (fc_frame_payload_op(fp)) { |
1807 | case ELS_FLOGI: |
1808 | fc_rport_recv_flogi_req(lport, rx_fp: fp); |
1809 | break; |
1810 | case ELS_PLOGI: |
1811 | fc_rport_recv_plogi_req(lport, fp); |
1812 | break; |
1813 | case ELS_LOGO: |
1814 | fc_rport_recv_logo_req(lport, fp); |
1815 | break; |
1816 | case ELS_PRLI: |
1817 | case ELS_PRLO: |
1818 | case ELS_ADISC: |
1819 | case ELS_RRQ: |
1820 | case ELS_REC: |
1821 | case ELS_RLS: |
1822 | case ELS_RTV: |
1823 | fc_rport_recv_els_req(lport, fp); |
1824 | break; |
1825 | default: |
1826 | els_data.reason = ELS_RJT_UNSUP; |
1827 | els_data.explan = ELS_EXPL_NONE; |
1828 | fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data); |
1829 | fc_frame_free(fp); |
1830 | break; |
1831 | } |
1832 | } |
1833 | EXPORT_SYMBOL(fc_rport_recv_req); |
1834 | |
1835 | /** |
1836 | * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests |
1837 | * @lport: The local port that received the PLOGI request |
1838 | * @rx_fp: The PLOGI request frame |
1839 | * |
1840 | * Reference counting: increments kref on return |
1841 | */ |
1842 | static void fc_rport_recv_plogi_req(struct fc_lport *lport, |
1843 | struct fc_frame *rx_fp) |
1844 | { |
1845 | struct fc_disc *disc; |
1846 | struct fc_rport_priv *rdata; |
1847 | struct fc_frame *fp = rx_fp; |
1848 | struct fc_els_flogi *pl; |
1849 | struct fc_seq_els_data rjt_data; |
1850 | u32 sid; |
1851 | |
1852 | lockdep_assert_held(&lport->lp_mutex); |
1853 | |
1854 | sid = fc_frame_sid(fp); |
1855 | |
1856 | FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n" ); |
1857 | |
1858 | pl = fc_frame_payload_get(fp, len: sizeof(*pl)); |
1859 | if (!pl) { |
1860 | FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n" ); |
1861 | rjt_data.reason = ELS_RJT_PROT; |
1862 | rjt_data.explan = ELS_EXPL_INV_LEN; |
1863 | goto reject; |
1864 | } |
1865 | |
1866 | disc = &lport->disc; |
1867 | mutex_lock(&disc->disc_mutex); |
1868 | rdata = fc_rport_create(lport, sid); |
1869 | if (!rdata) { |
1870 | mutex_unlock(lock: &disc->disc_mutex); |
1871 | rjt_data.reason = ELS_RJT_UNAB; |
1872 | rjt_data.explan = ELS_EXPL_INSUF_RES; |
1873 | goto reject; |
1874 | } |
1875 | |
1876 | mutex_lock(&rdata->rp_mutex); |
1877 | mutex_unlock(lock: &disc->disc_mutex); |
1878 | |
1879 | rdata->ids.port_name = get_unaligned_be64(p: &pl->fl_wwpn); |
1880 | rdata->ids.node_name = get_unaligned_be64(p: &pl->fl_wwnn); |
1881 | |
1882 | /* |
1883 | * If the rport was just created, possibly due to the incoming PLOGI, |
1884 | * set the state appropriately and accept the PLOGI. |
1885 | * |
1886 | * If we had also sent a PLOGI, and if the received PLOGI is from a |
1887 | * higher WWPN, we accept it, otherwise an LS_RJT is sent with reason |
1888 | * "command already in progress". |
1889 | * |
1890 | * XXX TBD: If the session was ready before, the PLOGI should result in |
1891 | * all outstanding exchanges being reset. |
1892 | */ |
1893 | switch (rdata->rp_state) { |
1894 | case RPORT_ST_INIT: |
1895 | FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n" ); |
1896 | break; |
1897 | case RPORT_ST_PLOGI_WAIT: |
1898 | FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI_WAIT state\n" ); |
1899 | break; |
1900 | case RPORT_ST_PLOGI: |
1901 | FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n" ); |
1902 | if (rdata->ids.port_name < lport->wwpn) { |
1903 | mutex_unlock(lock: &rdata->rp_mutex); |
1904 | rjt_data.reason = ELS_RJT_INPROG; |
1905 | rjt_data.explan = ELS_EXPL_NONE; |
1906 | goto reject; |
1907 | } |
1908 | break; |
1909 | case RPORT_ST_PRLI: |
1910 | case RPORT_ST_RTV: |
1911 | case RPORT_ST_READY: |
1912 | case RPORT_ST_ADISC: |
1913 | FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d " |
1914 | "- ignored for now\n" , rdata->rp_state); |
1915 | /* XXX TBD - should reset */ |
1916 | break; |
1917 | case RPORT_ST_FLOGI: |
1918 | case RPORT_ST_DELETE: |
1919 | FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n" , |
1920 | fc_rport_state(rdata)); |
1921 | mutex_unlock(lock: &rdata->rp_mutex); |
1922 | rjt_data.reason = ELS_RJT_BUSY; |
1923 | rjt_data.explan = ELS_EXPL_NONE; |
1924 | goto reject; |
1925 | } |
1926 | if (!fc_rport_compatible_roles(lport, rdata)) { |
1927 | FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n" ); |
1928 | mutex_unlock(lock: &rdata->rp_mutex); |
1929 | rjt_data.reason = ELS_RJT_LOGIC; |
1930 | rjt_data.explan = ELS_EXPL_NONE; |
1931 | goto reject; |
1932 | } |
1933 | |
1934 | /* |
1935 | * Get session payload size from incoming PLOGI. |
1936 | */ |
1937 | rdata->maxframe_size = fc_plogi_get_maxframe(flp: pl, maxval: lport->mfs); |
1938 | |
1939 | /* |
1940 | * Send LS_ACC. If this fails, the originator should retry. |
1941 | */ |
1942 | fp = fc_frame_alloc(dev: lport, len: sizeof(*pl)); |
1943 | if (!fp) |
1944 | goto out; |
1945 | |
1946 | fc_plogi_fill(lport, fp, op: ELS_LS_ACC); |
1947 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
1948 | lport->tt.frame_send(lport, fp); |
1949 | fc_rport_enter_prli(rdata); |
1950 | out: |
1951 | mutex_unlock(lock: &rdata->rp_mutex); |
1952 | fc_frame_free(fp: rx_fp); |
1953 | return; |
1954 | |
1955 | reject: |
1956 | fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data); |
1957 | fc_frame_free(fp); |
1958 | } |
1959 | |
1960 | /** |
1961 | * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests |
1962 | * @rdata: The remote port that sent the PRLI request |
1963 | * @rx_fp: The PRLI request frame |
1964 | */ |
1965 | static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, |
1966 | struct fc_frame *rx_fp) |
1967 | { |
1968 | struct fc_lport *lport = rdata->local_port; |
1969 | struct fc_frame *fp; |
1970 | struct { |
1971 | struct fc_els_prli prli; |
1972 | struct fc_els_spp spp; |
1973 | } *pp; |
1974 | struct fc_els_spp *rspp; /* request service param page */ |
1975 | struct fc_els_spp *spp; /* response spp */ |
1976 | unsigned int len; |
1977 | unsigned int plen; |
1978 | enum fc_els_spp_resp resp; |
1979 | struct fc_seq_els_data rjt_data; |
1980 | struct fc4_prov *prov; |
1981 | |
1982 | lockdep_assert_held(&rdata->rp_mutex); |
1983 | |
1984 | FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n" , |
1985 | fc_rport_state(rdata)); |
1986 | |
1987 | len = fr_len(rx_fp) - sizeof(struct fc_frame_header); |
1988 | pp = fc_frame_payload_get(fp: rx_fp, len: sizeof(*pp)); |
1989 | if (!pp) |
1990 | goto reject_len; |
1991 | plen = ntohs(pp->prli.prli_len); |
1992 | if ((plen % 4) != 0 || plen > len || plen < 16) |
1993 | goto reject_len; |
1994 | if (plen < len) |
1995 | len = plen; |
1996 | plen = pp->prli.prli_spp_len; |
1997 | if ((plen % 4) != 0 || plen < sizeof(*spp) || |
1998 | plen > len || len < sizeof(*pp) || plen < 12) |
1999 | goto reject_len; |
2000 | rspp = &pp->spp; |
2001 | |
2002 | fp = fc_frame_alloc(dev: lport, len); |
2003 | if (!fp) { |
2004 | rjt_data.reason = ELS_RJT_UNAB; |
2005 | rjt_data.explan = ELS_EXPL_INSUF_RES; |
2006 | goto reject; |
2007 | } |
2008 | pp = fc_frame_payload_get(fp, len); |
2009 | WARN_ON(!pp); |
2010 | memset(pp, 0, len); |
2011 | pp->prli.prli_cmd = ELS_LS_ACC; |
2012 | pp->prli.prli_spp_len = plen; |
2013 | pp->prli.prli_len = htons(len); |
2014 | len -= sizeof(struct fc_els_prli); |
2015 | |
2016 | /* |
2017 | * Go through all the service parameter pages and build |
2018 | * response. If plen indicates longer SPP than standard, |
2019 | * use that. The entire response has been pre-cleared above. |
2020 | */ |
2021 | spp = &pp->spp; |
2022 | mutex_lock(&fc_prov_mutex); |
2023 | while (len >= plen) { |
2024 | rdata->spp_type = rspp->spp_type; |
2025 | spp->spp_type = rspp->spp_type; |
2026 | spp->spp_type_ext = rspp->spp_type_ext; |
2027 | resp = 0; |
2028 | |
2029 | if (rspp->spp_type < FC_FC4_PROV_SIZE) { |
2030 | enum fc_els_spp_resp active = 0, passive = 0; |
2031 | |
2032 | prov = fc_active_prov[rspp->spp_type]; |
2033 | if (prov) |
2034 | active = prov->prli(rdata, plen, rspp, spp); |
2035 | prov = fc_passive_prov[rspp->spp_type]; |
2036 | if (prov) |
2037 | passive = prov->prli(rdata, plen, rspp, spp); |
2038 | if (!active || passive == FC_SPP_RESP_ACK) |
2039 | resp = passive; |
2040 | else |
2041 | resp = active; |
2042 | FC_RPORT_DBG(rdata, "PRLI rspp type %x " |
2043 | "active %x passive %x\n" , |
2044 | rspp->spp_type, active, passive); |
2045 | } |
2046 | if (!resp) { |
2047 | if (spp->spp_flags & FC_SPP_EST_IMG_PAIR) |
2048 | resp |= FC_SPP_RESP_CONF; |
2049 | else |
2050 | resp |= FC_SPP_RESP_INVL; |
2051 | } |
2052 | spp->spp_flags |= resp; |
2053 | len -= plen; |
2054 | rspp = (struct fc_els_spp *)((char *)rspp + plen); |
2055 | spp = (struct fc_els_spp *)((char *)spp + plen); |
2056 | } |
2057 | mutex_unlock(lock: &fc_prov_mutex); |
2058 | |
2059 | /* |
2060 | * Send LS_ACC. If this fails, the originator should retry. |
2061 | */ |
2062 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
2063 | lport->tt.frame_send(lport, fp); |
2064 | |
2065 | goto drop; |
2066 | |
2067 | reject_len: |
2068 | rjt_data.reason = ELS_RJT_PROT; |
2069 | rjt_data.explan = ELS_EXPL_INV_LEN; |
2070 | reject: |
2071 | fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); |
2072 | drop: |
2073 | fc_frame_free(fp: rx_fp); |
2074 | } |
2075 | |
2076 | /** |
2077 | * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests |
2078 | * @rdata: The remote port that sent the PRLO request |
2079 | * @rx_fp: The PRLO request frame |
2080 | */ |
2081 | static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, |
2082 | struct fc_frame *rx_fp) |
2083 | { |
2084 | struct fc_lport *lport = rdata->local_port; |
2085 | struct fc_frame *fp; |
2086 | struct { |
2087 | struct fc_els_prlo prlo; |
2088 | struct fc_els_spp spp; |
2089 | } *pp; |
2090 | struct fc_els_spp *rspp; /* request service param page */ |
2091 | struct fc_els_spp *spp; /* response spp */ |
2092 | unsigned int len; |
2093 | unsigned int plen; |
2094 | struct fc_seq_els_data rjt_data; |
2095 | |
2096 | lockdep_assert_held(&rdata->rp_mutex); |
2097 | |
2098 | FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n" , |
2099 | fc_rport_state(rdata)); |
2100 | |
2101 | len = fr_len(rx_fp) - sizeof(struct fc_frame_header); |
2102 | pp = fc_frame_payload_get(fp: rx_fp, len: sizeof(*pp)); |
2103 | if (!pp) |
2104 | goto reject_len; |
2105 | plen = ntohs(pp->prlo.prlo_len); |
2106 | if (plen != 20) |
2107 | goto reject_len; |
2108 | if (plen < len) |
2109 | len = plen; |
2110 | |
2111 | rspp = &pp->spp; |
2112 | |
2113 | fp = fc_frame_alloc(dev: lport, len); |
2114 | if (!fp) { |
2115 | rjt_data.reason = ELS_RJT_UNAB; |
2116 | rjt_data.explan = ELS_EXPL_INSUF_RES; |
2117 | goto reject; |
2118 | } |
2119 | |
2120 | pp = fc_frame_payload_get(fp, len); |
2121 | WARN_ON(!pp); |
2122 | memset(pp, 0, len); |
2123 | pp->prlo.prlo_cmd = ELS_LS_ACC; |
2124 | pp->prlo.prlo_obs = 0x10; |
2125 | pp->prlo.prlo_len = htons(len); |
2126 | spp = &pp->spp; |
2127 | spp->spp_type = rspp->spp_type; |
2128 | spp->spp_type_ext = rspp->spp_type_ext; |
2129 | spp->spp_flags = FC_SPP_RESP_ACK; |
2130 | |
2131 | fc_rport_enter_prli(rdata); |
2132 | |
2133 | fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, parm_offset: 0); |
2134 | lport->tt.frame_send(lport, fp); |
2135 | goto drop; |
2136 | |
2137 | reject_len: |
2138 | rjt_data.reason = ELS_RJT_PROT; |
2139 | rjt_data.explan = ELS_EXPL_INV_LEN; |
2140 | reject: |
2141 | fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data); |
2142 | drop: |
2143 | fc_frame_free(fp: rx_fp); |
2144 | } |
2145 | |
2146 | /** |
2147 | * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests |
2148 | * @lport: The local port that received the LOGO request |
2149 | * @fp: The LOGO request frame |
2150 | * |
2151 | * Reference counting: drops kref on return |
2152 | */ |
2153 | static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) |
2154 | { |
2155 | struct fc_rport_priv *rdata; |
2156 | u32 sid; |
2157 | |
2158 | lockdep_assert_held(&lport->lp_mutex); |
2159 | |
2160 | fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL); |
2161 | |
2162 | sid = fc_frame_sid(fp); |
2163 | |
2164 | rdata = fc_rport_lookup(lport, sid); |
2165 | if (rdata) { |
2166 | mutex_lock(&rdata->rp_mutex); |
2167 | FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n" , |
2168 | fc_rport_state(rdata)); |
2169 | |
2170 | fc_rport_enter_delete(rdata, event: RPORT_EV_STOP); |
2171 | mutex_unlock(lock: &rdata->rp_mutex); |
2172 | kref_put(kref: &rdata->kref, release: fc_rport_destroy); |
2173 | } else |
2174 | FC_RPORT_ID_DBG(lport, sid, |
2175 | "Received LOGO from non-logged-in port\n" ); |
2176 | fc_frame_free(fp); |
2177 | } |
2178 | |
2179 | /** |
2180 | * fc_rport_flush_queue() - Flush the rport_event_queue |
2181 | */ |
2182 | void fc_rport_flush_queue(void) |
2183 | { |
2184 | flush_workqueue(rport_event_queue); |
2185 | } |
2186 | EXPORT_SYMBOL(fc_rport_flush_queue); |
2187 | |
2188 | /** |
2189 | * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator. |
2190 | * @rdata: remote port private |
2191 | * @spp_len: service parameter page length |
2192 | * @rspp: received service parameter page |
2193 | * @spp: response service parameter page |
2194 | * |
2195 | * Returns the value for the response code to be placed in spp_flags; |
2196 | * Returns 0 if not an initiator. |
2197 | */ |
2198 | static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len, |
2199 | const struct fc_els_spp *rspp, |
2200 | struct fc_els_spp *spp) |
2201 | { |
2202 | struct fc_lport *lport = rdata->local_port; |
2203 | u32 fcp_parm; |
2204 | |
2205 | fcp_parm = ntohl(rspp->spp_params); |
2206 | rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; |
2207 | if (fcp_parm & FCP_SPPF_INIT_FCN) |
2208 | rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; |
2209 | if (fcp_parm & FCP_SPPF_TARG_FCN) |
2210 | rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET; |
2211 | if (fcp_parm & FCP_SPPF_RETRY) |
2212 | rdata->flags |= FC_RP_FLAGS_RETRY; |
2213 | rdata->supported_classes = FC_COS_CLASS3; |
2214 | |
2215 | if (!(lport->service_params & FCP_SPPF_INIT_FCN)) |
2216 | return 0; |
2217 | |
2218 | spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR; |
2219 | |
2220 | /* |
2221 | * OR in our service parameters with other providers (target), if any. |
2222 | */ |
2223 | fcp_parm = ntohl(spp->spp_params); |
2224 | spp->spp_params = htonl(fcp_parm | lport->service_params); |
2225 | return FC_SPP_RESP_ACK; |
2226 | } |
2227 | |
2228 | /* |
2229 | * FC-4 provider ops for FCP initiator. |
2230 | */ |
2231 | struct fc4_prov fc_rport_fcp_init = { |
2232 | .prli = fc_rport_fcp_prli, |
2233 | }; |
2234 | |
2235 | /** |
2236 | * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0 |
2237 | * @rdata: remote port private |
2238 | * @spp_len: service parameter page length |
2239 | * @rspp: received service parameter page |
2240 | * @spp: response service parameter page |
2241 | */ |
2242 | static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len, |
2243 | const struct fc_els_spp *rspp, |
2244 | struct fc_els_spp *spp) |
2245 | { |
2246 | if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) |
2247 | return FC_SPP_RESP_INVL; |
2248 | return FC_SPP_RESP_ACK; |
2249 | } |
2250 | |
2251 | /* |
2252 | * FC-4 provider ops for type 0 service parameters. |
2253 | * |
2254 | * This handles the special case of type 0 which is always successful |
2255 | * but doesn't do anything otherwise. |
2256 | */ |
2257 | struct fc4_prov fc_rport_t0_prov = { |
2258 | .prli = fc_rport_t0_prli, |
2259 | }; |
2260 | |
2261 | /** |
2262 | * fc_setup_rport() - Initialize the rport_event_queue |
2263 | */ |
2264 | int fc_setup_rport(void) |
2265 | { |
2266 | rport_event_queue = create_singlethread_workqueue("fc_rport_eq" ); |
2267 | if (!rport_event_queue) |
2268 | return -ENOMEM; |
2269 | return 0; |
2270 | } |
2271 | |
2272 | /** |
2273 | * fc_destroy_rport() - Destroy the rport_event_queue |
2274 | */ |
2275 | void fc_destroy_rport(void) |
2276 | { |
2277 | destroy_workqueue(wq: rport_event_queue); |
2278 | } |
2279 | |
2280 | /** |
2281 | * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port |
2282 | * @rport: The remote port whose I/O should be terminated |
2283 | */ |
2284 | void fc_rport_terminate_io(struct fc_rport *rport) |
2285 | { |
2286 | struct fc_rport_libfc_priv *rpriv = rport->dd_data; |
2287 | struct fc_lport *lport = rpriv->local_port; |
2288 | |
2289 | lport->tt.exch_mgr_reset(lport, 0, rport->port_id); |
2290 | lport->tt.exch_mgr_reset(lport, rport->port_id, 0); |
2291 | } |
2292 | EXPORT_SYMBOL(fc_rport_terminate_io); |
2293 | |