Commit | Line | Data |
---|---|---|
853e2bd2 BG |
1 | /* |
2 | * bnx2fc_els.c: Broadcom NetXtreme II Linux FCoE offload driver. | |
3 | * This file contains helper routines that handle ELS requests | |
4 | * and responses. | |
5 | * | |
6 | * Copyright (c) 2008 - 2010 Broadcom Corporation | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation. | |
11 | * | |
12 | * Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com) | |
13 | */ | |
14 | ||
15 | #include "bnx2fc.h" | |
16 | ||
17 | static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, | |
18 | void *arg); | |
19 | static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, | |
20 | void *arg); | |
21 | static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, | |
22 | void *data, u32 data_len, | |
23 | void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), | |
24 | struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec); | |
25 | ||
26 | static void bnx2fc_rrq_compl(struct bnx2fc_els_cb_arg *cb_arg) | |
27 | { | |
28 | struct bnx2fc_cmd *orig_io_req; | |
29 | struct bnx2fc_cmd *rrq_req; | |
30 | int rc = 0; | |
31 | ||
32 | BUG_ON(!cb_arg); | |
33 | rrq_req = cb_arg->io_req; | |
34 | orig_io_req = cb_arg->aborted_io_req; | |
35 | BUG_ON(!orig_io_req); | |
36 | BNX2FC_ELS_DBG("rrq_compl: orig xid = 0x%x, rrq_xid = 0x%x\n", | |
37 | orig_io_req->xid, rrq_req->xid); | |
38 | ||
39 | kref_put(&orig_io_req->refcount, bnx2fc_cmd_release); | |
40 | ||
41 | if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &rrq_req->req_flags)) { | |
42 | /* | |
43 | * els req is timed out. cleanup the IO with FW and | |
44 | * drop the completion. Remove from active_cmd_queue. | |
45 | */ | |
46 | BNX2FC_ELS_DBG("rrq xid - 0x%x timed out, clean it up\n", | |
47 | rrq_req->xid); | |
48 | ||
49 | if (rrq_req->on_active_queue) { | |
50 | list_del_init(&rrq_req->link); | |
51 | rrq_req->on_active_queue = 0; | |
52 | rc = bnx2fc_initiate_cleanup(rrq_req); | |
53 | BUG_ON(rc); | |
54 | } | |
55 | } | |
56 | kfree(cb_arg); | |
57 | } | |
58 | int bnx2fc_send_rrq(struct bnx2fc_cmd *aborted_io_req) | |
59 | { | |
60 | ||
61 | struct fc_els_rrq rrq; | |
62 | struct bnx2fc_rport *tgt = aborted_io_req->tgt; | |
63 | struct fc_lport *lport = tgt->rdata->local_port; | |
64 | struct bnx2fc_els_cb_arg *cb_arg = NULL; | |
65 | u32 sid = tgt->sid; | |
66 | u32 r_a_tov = lport->r_a_tov; | |
67 | unsigned long start = jiffies; | |
68 | int rc; | |
69 | ||
70 | BNX2FC_ELS_DBG("Sending RRQ orig_xid = 0x%x\n", | |
71 | aborted_io_req->xid); | |
72 | memset(&rrq, 0, sizeof(rrq)); | |
73 | ||
74 | cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_NOIO); | |
75 | if (!cb_arg) { | |
76 | printk(KERN_ERR PFX "Unable to allocate cb_arg for RRQ\n"); | |
77 | rc = -ENOMEM; | |
78 | goto rrq_err; | |
79 | } | |
80 | ||
81 | cb_arg->aborted_io_req = aborted_io_req; | |
82 | ||
83 | rrq.rrq_cmd = ELS_RRQ; | |
84 | hton24(rrq.rrq_s_id, sid); | |
85 | rrq.rrq_ox_id = htons(aborted_io_req->xid); | |
86 | rrq.rrq_rx_id = htons(aborted_io_req->task->rx_wr_tx_rd.rx_id); | |
87 | ||
88 | retry_rrq: | |
89 | rc = bnx2fc_initiate_els(tgt, ELS_RRQ, &rrq, sizeof(rrq), | |
90 | bnx2fc_rrq_compl, cb_arg, | |
91 | r_a_tov); | |
92 | if (rc == -ENOMEM) { | |
93 | if (time_after(jiffies, start + (10 * HZ))) { | |
94 | BNX2FC_ELS_DBG("rrq Failed\n"); | |
95 | rc = FAILED; | |
96 | goto rrq_err; | |
97 | } | |
98 | msleep(20); | |
99 | goto retry_rrq; | |
100 | } | |
101 | rrq_err: | |
102 | if (rc) { | |
103 | BNX2FC_ELS_DBG("RRQ failed - release orig io req 0x%x\n", | |
104 | aborted_io_req->xid); | |
105 | kfree(cb_arg); | |
106 | spin_lock_bh(&tgt->tgt_lock); | |
107 | kref_put(&aborted_io_req->refcount, bnx2fc_cmd_release); | |
108 | spin_unlock_bh(&tgt->tgt_lock); | |
109 | } | |
110 | return rc; | |
111 | } | |
112 | ||
113 | static void bnx2fc_l2_els_compl(struct bnx2fc_els_cb_arg *cb_arg) | |
114 | { | |
115 | struct bnx2fc_cmd *els_req; | |
116 | struct bnx2fc_rport *tgt; | |
117 | struct bnx2fc_mp_req *mp_req; | |
118 | struct fc_frame_header *fc_hdr; | |
119 | unsigned char *buf; | |
120 | void *resp_buf; | |
121 | u32 resp_len, hdr_len; | |
122 | u16 l2_oxid; | |
123 | int frame_len; | |
124 | int rc = 0; | |
125 | ||
126 | l2_oxid = cb_arg->l2_oxid; | |
127 | BNX2FC_ELS_DBG("ELS COMPL - l2_oxid = 0x%x\n", l2_oxid); | |
128 | ||
129 | els_req = cb_arg->io_req; | |
130 | if (test_and_clear_bit(BNX2FC_FLAG_ELS_TIMEOUT, &els_req->req_flags)) { | |
131 | /* | |
132 | * els req is timed out. cleanup the IO with FW and | |
133 | * drop the completion. libfc will handle the els timeout | |
134 | */ | |
135 | if (els_req->on_active_queue) { | |
136 | list_del_init(&els_req->link); | |
137 | els_req->on_active_queue = 0; | |
138 | rc = bnx2fc_initiate_cleanup(els_req); | |
139 | BUG_ON(rc); | |
140 | } | |
141 | goto free_arg; | |
142 | } | |
143 | ||
144 | tgt = els_req->tgt; | |
145 | mp_req = &(els_req->mp_req); | |
146 | fc_hdr = &(mp_req->resp_fc_hdr); | |
147 | resp_len = mp_req->resp_len; | |
148 | resp_buf = mp_req->resp_buf; | |
149 | ||
150 | buf = kzalloc(PAGE_SIZE, GFP_ATOMIC); | |
151 | if (!buf) { | |
152 | printk(KERN_ERR PFX "Unable to alloc mp buf\n"); | |
153 | goto free_arg; | |
154 | } | |
155 | hdr_len = sizeof(*fc_hdr); | |
156 | if (hdr_len + resp_len > PAGE_SIZE) { | |
157 | printk(KERN_ERR PFX "l2_els_compl: resp len is " | |
158 | "beyond page size\n"); | |
159 | goto free_buf; | |
160 | } | |
161 | memcpy(buf, fc_hdr, hdr_len); | |
162 | memcpy(buf + hdr_len, resp_buf, resp_len); | |
163 | frame_len = hdr_len + resp_len; | |
164 | ||
165 | bnx2fc_process_l2_frame_compl(tgt, buf, frame_len, l2_oxid); | |
166 | ||
167 | free_buf: | |
168 | kfree(buf); | |
169 | free_arg: | |
170 | kfree(cb_arg); | |
171 | } | |
172 | ||
173 | int bnx2fc_send_adisc(struct bnx2fc_rport *tgt, struct fc_frame *fp) | |
174 | { | |
175 | struct fc_els_adisc *adisc; | |
176 | struct fc_frame_header *fh; | |
177 | struct bnx2fc_els_cb_arg *cb_arg; | |
178 | struct fc_lport *lport = tgt->rdata->local_port; | |
179 | u32 r_a_tov = lport->r_a_tov; | |
180 | int rc; | |
181 | ||
182 | fh = fc_frame_header_get(fp); | |
183 | cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); | |
184 | if (!cb_arg) { | |
185 | printk(KERN_ERR PFX "Unable to allocate cb_arg for ADISC\n"); | |
186 | return -ENOMEM; | |
187 | } | |
188 | ||
189 | cb_arg->l2_oxid = ntohs(fh->fh_ox_id); | |
190 | ||
191 | BNX2FC_ELS_DBG("send ADISC: l2_oxid = 0x%x\n", cb_arg->l2_oxid); | |
192 | adisc = fc_frame_payload_get(fp, sizeof(*adisc)); | |
193 | /* adisc is initialized by libfc */ | |
194 | rc = bnx2fc_initiate_els(tgt, ELS_ADISC, adisc, sizeof(*adisc), | |
195 | bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); | |
196 | if (rc) | |
197 | kfree(cb_arg); | |
198 | return rc; | |
199 | } | |
200 | ||
201 | int bnx2fc_send_logo(struct bnx2fc_rport *tgt, struct fc_frame *fp) | |
202 | { | |
203 | struct fc_els_logo *logo; | |
204 | struct fc_frame_header *fh; | |
205 | struct bnx2fc_els_cb_arg *cb_arg; | |
206 | struct fc_lport *lport = tgt->rdata->local_port; | |
207 | u32 r_a_tov = lport->r_a_tov; | |
208 | int rc; | |
209 | ||
210 | fh = fc_frame_header_get(fp); | |
211 | cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); | |
212 | if (!cb_arg) { | |
213 | printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n"); | |
214 | return -ENOMEM; | |
215 | } | |
216 | ||
217 | cb_arg->l2_oxid = ntohs(fh->fh_ox_id); | |
218 | ||
219 | BNX2FC_ELS_DBG("Send LOGO: l2_oxid = 0x%x\n", cb_arg->l2_oxid); | |
220 | logo = fc_frame_payload_get(fp, sizeof(*logo)); | |
221 | /* logo is initialized by libfc */ | |
222 | rc = bnx2fc_initiate_els(tgt, ELS_LOGO, logo, sizeof(*logo), | |
223 | bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); | |
224 | if (rc) | |
225 | kfree(cb_arg); | |
226 | return rc; | |
227 | } | |
228 | ||
229 | int bnx2fc_send_rls(struct bnx2fc_rport *tgt, struct fc_frame *fp) | |
230 | { | |
231 | struct fc_els_rls *rls; | |
232 | struct fc_frame_header *fh; | |
233 | struct bnx2fc_els_cb_arg *cb_arg; | |
234 | struct fc_lport *lport = tgt->rdata->local_port; | |
235 | u32 r_a_tov = lport->r_a_tov; | |
236 | int rc; | |
237 | ||
238 | fh = fc_frame_header_get(fp); | |
239 | cb_arg = kzalloc(sizeof(struct bnx2fc_els_cb_arg), GFP_ATOMIC); | |
240 | if (!cb_arg) { | |
241 | printk(KERN_ERR PFX "Unable to allocate cb_arg for LOGO\n"); | |
242 | return -ENOMEM; | |
243 | } | |
244 | ||
245 | cb_arg->l2_oxid = ntohs(fh->fh_ox_id); | |
246 | ||
247 | rls = fc_frame_payload_get(fp, sizeof(*rls)); | |
248 | /* rls is initialized by libfc */ | |
249 | rc = bnx2fc_initiate_els(tgt, ELS_RLS, rls, sizeof(*rls), | |
250 | bnx2fc_l2_els_compl, cb_arg, 2 * r_a_tov); | |
251 | if (rc) | |
252 | kfree(cb_arg); | |
253 | return rc; | |
254 | } | |
255 | ||
256 | static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, | |
257 | void *data, u32 data_len, | |
258 | void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), | |
259 | struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec) | |
260 | { | |
261 | struct fcoe_port *port = tgt->port; | |
262 | struct bnx2fc_hba *hba = port->priv; | |
263 | struct fc_rport *rport = tgt->rport; | |
264 | struct fc_lport *lport = port->lport; | |
265 | struct bnx2fc_cmd *els_req; | |
266 | struct bnx2fc_mp_req *mp_req; | |
267 | struct fc_frame_header *fc_hdr; | |
268 | struct fcoe_task_ctx_entry *task; | |
269 | struct fcoe_task_ctx_entry *task_page; | |
270 | int rc = 0; | |
271 | int task_idx, index; | |
272 | u32 did, sid; | |
273 | u16 xid; | |
274 | ||
275 | rc = fc_remote_port_chkready(rport); | |
276 | if (rc) { | |
277 | printk(KERN_ALERT PFX "els 0x%x: rport not ready\n", op); | |
278 | rc = -EINVAL; | |
279 | goto els_err; | |
280 | } | |
281 | if (lport->state != LPORT_ST_READY || !(lport->link_up)) { | |
282 | printk(KERN_ALERT PFX "els 0x%x: link is not ready\n", op); | |
283 | rc = -EINVAL; | |
284 | goto els_err; | |
285 | } | |
286 | if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) || | |
287 | (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) { | |
288 | printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op); | |
289 | rc = -EINVAL; | |
290 | goto els_err; | |
291 | } | |
292 | els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS); | |
293 | if (!els_req) { | |
294 | rc = -ENOMEM; | |
295 | goto els_err; | |
296 | } | |
297 | ||
298 | els_req->sc_cmd = NULL; | |
299 | els_req->port = port; | |
300 | els_req->tgt = tgt; | |
301 | els_req->cb_func = cb_func; | |
302 | cb_arg->io_req = els_req; | |
303 | els_req->cb_arg = cb_arg; | |
304 | ||
305 | mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req); | |
306 | rc = bnx2fc_init_mp_req(els_req); | |
307 | if (rc == FAILED) { | |
308 | printk(KERN_ALERT PFX "ELS MP request init failed\n"); | |
309 | spin_lock_bh(&tgt->tgt_lock); | |
310 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
311 | spin_unlock_bh(&tgt->tgt_lock); | |
312 | rc = -ENOMEM; | |
313 | goto els_err; | |
314 | } else { | |
315 | /* rc SUCCESS */ | |
316 | rc = 0; | |
317 | } | |
318 | ||
319 | /* Set the data_xfer_len to the size of ELS payload */ | |
320 | mp_req->req_len = data_len; | |
321 | els_req->data_xfer_len = mp_req->req_len; | |
322 | ||
323 | /* Fill ELS Payload */ | |
324 | if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { | |
325 | memcpy(mp_req->req_buf, data, data_len); | |
326 | } else { | |
327 | printk(KERN_ALERT PFX "Invalid ELS op 0x%x\n", op); | |
328 | els_req->cb_func = NULL; | |
329 | els_req->cb_arg = NULL; | |
330 | spin_lock_bh(&tgt->tgt_lock); | |
331 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
332 | spin_unlock_bh(&tgt->tgt_lock); | |
333 | rc = -EINVAL; | |
334 | } | |
335 | ||
336 | if (rc) | |
337 | goto els_err; | |
338 | ||
339 | /* Fill FC header */ | |
340 | fc_hdr = &(mp_req->req_fc_hdr); | |
341 | ||
342 | did = tgt->rport->port_id; | |
343 | sid = tgt->sid; | |
344 | ||
345 | __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid, | |
346 | FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | | |
347 | FC_FC_SEQ_INIT, 0); | |
348 | ||
349 | /* Obtain exchange id */ | |
350 | xid = els_req->xid; | |
351 | task_idx = xid/BNX2FC_TASKS_PER_PAGE; | |
352 | index = xid % BNX2FC_TASKS_PER_PAGE; | |
353 | ||
354 | /* Initialize task context for this IO request */ | |
355 | task_page = (struct fcoe_task_ctx_entry *) hba->task_ctx[task_idx]; | |
356 | task = &(task_page[index]); | |
357 | bnx2fc_init_mp_task(els_req, task); | |
358 | ||
359 | spin_lock_bh(&tgt->tgt_lock); | |
360 | ||
361 | if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) { | |
362 | printk(KERN_ERR PFX "initiate_els.. session not ready\n"); | |
363 | els_req->cb_func = NULL; | |
364 | els_req->cb_arg = NULL; | |
365 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
366 | spin_unlock_bh(&tgt->tgt_lock); | |
367 | return -EINVAL; | |
368 | } | |
369 | ||
370 | if (timer_msec) | |
371 | bnx2fc_cmd_timer_set(els_req, timer_msec); | |
372 | bnx2fc_add_2_sq(tgt, xid); | |
373 | ||
374 | els_req->on_active_queue = 1; | |
375 | list_add_tail(&els_req->link, &tgt->els_queue); | |
376 | ||
377 | /* Ring doorbell */ | |
378 | bnx2fc_ring_doorbell(tgt); | |
379 | spin_unlock_bh(&tgt->tgt_lock); | |
380 | ||
381 | els_err: | |
382 | return rc; | |
383 | } | |
384 | ||
385 | void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req, | |
386 | struct fcoe_task_ctx_entry *task, u8 num_rq) | |
387 | { | |
388 | struct bnx2fc_mp_req *mp_req; | |
389 | struct fc_frame_header *fc_hdr; | |
390 | u64 *hdr; | |
391 | u64 *temp_hdr; | |
392 | ||
393 | BNX2FC_ELS_DBG("Entered process_els_compl xid = 0x%x" | |
394 | "cmd_type = %d\n", els_req->xid, els_req->cmd_type); | |
395 | ||
396 | if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE, | |
397 | &els_req->req_flags)) { | |
398 | BNX2FC_ELS_DBG("Timer context finished processing this " | |
399 | "els - 0x%x\n", els_req->xid); | |
25985edc | 400 | /* This IO doesn't receive cleanup completion */ |
853e2bd2 BG |
401 | kref_put(&els_req->refcount, bnx2fc_cmd_release); |
402 | return; | |
403 | } | |
404 | ||
405 | /* Cancel the timeout_work, as we received the response */ | |
406 | if (cancel_delayed_work(&els_req->timeout_work)) | |
407 | kref_put(&els_req->refcount, | |
408 | bnx2fc_cmd_release); /* drop timer hold */ | |
409 | ||
410 | if (els_req->on_active_queue) { | |
411 | list_del_init(&els_req->link); | |
412 | els_req->on_active_queue = 0; | |
413 | } | |
414 | ||
415 | mp_req = &(els_req->mp_req); | |
416 | fc_hdr = &(mp_req->resp_fc_hdr); | |
417 | ||
418 | hdr = (u64 *)fc_hdr; | |
419 | temp_hdr = (u64 *) | |
420 | &task->cmn.general.cmd_info.mp_fc_frame.fc_hdr; | |
421 | hdr[0] = cpu_to_be64(temp_hdr[0]); | |
422 | hdr[1] = cpu_to_be64(temp_hdr[1]); | |
423 | hdr[2] = cpu_to_be64(temp_hdr[2]); | |
424 | ||
425 | mp_req->resp_len = task->rx_wr_only.sgl_ctx.mul_sges.cur_sge_off; | |
426 | ||
427 | /* Parse ELS response */ | |
428 | if ((els_req->cb_func) && (els_req->cb_arg)) { | |
429 | els_req->cb_func(els_req->cb_arg); | |
430 | els_req->cb_arg = NULL; | |
431 | } | |
432 | ||
433 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
434 | } | |
435 | ||
436 | static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, | |
437 | void *arg) | |
438 | { | |
439 | struct fcoe_ctlr *fip = arg; | |
440 | struct fc_exch *exch = fc_seq_exch(seq); | |
441 | struct fc_lport *lport = exch->lp; | |
442 | u8 *mac; | |
443 | struct fc_frame_header *fh; | |
444 | u8 op; | |
445 | ||
446 | if (IS_ERR(fp)) | |
447 | goto done; | |
448 | ||
449 | mac = fr_cb(fp)->granted_mac; | |
450 | if (is_zero_ether_addr(mac)) { | |
451 | fh = fc_frame_header_get(fp); | |
452 | if (fh->fh_type != FC_TYPE_ELS) { | |
453 | printk(KERN_ERR PFX "bnx2fc_flogi_resp:" | |
454 | "fh_type != FC_TYPE_ELS\n"); | |
455 | fc_frame_free(fp); | |
456 | return; | |
457 | } | |
458 | op = fc_frame_payload_op(fp); | |
459 | if (lport->vport) { | |
460 | if (op == ELS_LS_RJT) { | |
461 | printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n"); | |
462 | fc_vport_terminate(lport->vport); | |
463 | fc_frame_free(fp); | |
464 | return; | |
465 | } | |
466 | } | |
467 | if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { | |
468 | fc_frame_free(fp); | |
469 | return; | |
470 | } | |
471 | } | |
472 | fip->update_mac(lport, mac); | |
473 | done: | |
474 | fc_lport_flogi_resp(seq, fp, lport); | |
475 | } | |
476 | ||
477 | static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, | |
478 | void *arg) | |
479 | { | |
480 | struct fcoe_ctlr *fip = arg; | |
481 | struct fc_exch *exch = fc_seq_exch(seq); | |
482 | struct fc_lport *lport = exch->lp; | |
483 | static u8 zero_mac[ETH_ALEN] = { 0 }; | |
484 | ||
485 | if (!IS_ERR(fp)) | |
486 | fip->update_mac(lport, zero_mac); | |
487 | fc_lport_logo_resp(seq, fp, lport); | |
488 | } | |
489 | ||
490 | struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did, | |
491 | struct fc_frame *fp, unsigned int op, | |
492 | void (*resp)(struct fc_seq *, | |
493 | struct fc_frame *, | |
494 | void *), | |
495 | void *arg, u32 timeout) | |
496 | { | |
497 | struct fcoe_port *port = lport_priv(lport); | |
498 | struct bnx2fc_hba *hba = port->priv; | |
499 | struct fcoe_ctlr *fip = &hba->ctlr; | |
500 | struct fc_frame_header *fh = fc_frame_header_get(fp); | |
501 | ||
502 | switch (op) { | |
503 | case ELS_FLOGI: | |
504 | case ELS_FDISC: | |
505 | return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp, | |
506 | fip, timeout); | |
507 | case ELS_LOGO: | |
508 | /* only hook onto fabric logouts, not port logouts */ | |
509 | if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) | |
510 | break; | |
511 | return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp, | |
512 | fip, timeout); | |
513 | } | |
514 | return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); | |
515 | } |