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); | |
619c5cb6 | 86 | rrq.rrq_rx_id = htons(aborted_io_req->task->rxwr_txrd.var_ctx.rx_id); |
853e2bd2 BG |
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 | ||
6c5a7ce4 BPG |
256 | int bnx2fc_send_rec(struct bnx2fc_cmd *orig_io_req) |
257 | { | |
258 | /* | |
259 | * Dummy function to enable compiling individual patches. Real function | |
260 | * is in the next patch. | |
261 | */ | |
262 | return 0; | |
263 | } | |
264 | ||
265 | int bnx2fc_send_srr(struct bnx2fc_cmd *orig_io_req, u32 offset, u8 r_ctl) | |
266 | { | |
267 | /* | |
268 | * Dummy function to enable compiling individual patches. Real function | |
269 | * is in the next patch. | |
270 | */ | |
271 | return 0; | |
272 | } | |
273 | ||
274 | ||
275 | ||
853e2bd2 BG |
276 | static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, |
277 | void *data, u32 data_len, | |
278 | void (*cb_func)(struct bnx2fc_els_cb_arg *cb_arg), | |
279 | struct bnx2fc_els_cb_arg *cb_arg, u32 timer_msec) | |
280 | { | |
281 | struct fcoe_port *port = tgt->port; | |
aea71a02 | 282 | struct bnx2fc_interface *interface = port->priv; |
853e2bd2 BG |
283 | struct fc_rport *rport = tgt->rport; |
284 | struct fc_lport *lport = port->lport; | |
285 | struct bnx2fc_cmd *els_req; | |
286 | struct bnx2fc_mp_req *mp_req; | |
287 | struct fc_frame_header *fc_hdr; | |
288 | struct fcoe_task_ctx_entry *task; | |
289 | struct fcoe_task_ctx_entry *task_page; | |
290 | int rc = 0; | |
291 | int task_idx, index; | |
292 | u32 did, sid; | |
293 | u16 xid; | |
294 | ||
295 | rc = fc_remote_port_chkready(rport); | |
296 | if (rc) { | |
b2a554ff | 297 | printk(KERN_ERR PFX "els 0x%x: rport not ready\n", op); |
853e2bd2 BG |
298 | rc = -EINVAL; |
299 | goto els_err; | |
300 | } | |
301 | if (lport->state != LPORT_ST_READY || !(lport->link_up)) { | |
b2a554ff | 302 | printk(KERN_ERR PFX "els 0x%x: link is not ready\n", op); |
853e2bd2 BG |
303 | rc = -EINVAL; |
304 | goto els_err; | |
305 | } | |
306 | if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) || | |
307 | (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) { | |
308 | printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op); | |
309 | rc = -EINVAL; | |
310 | goto els_err; | |
311 | } | |
312 | els_req = bnx2fc_elstm_alloc(tgt, BNX2FC_ELS); | |
313 | if (!els_req) { | |
314 | rc = -ENOMEM; | |
315 | goto els_err; | |
316 | } | |
317 | ||
318 | els_req->sc_cmd = NULL; | |
319 | els_req->port = port; | |
320 | els_req->tgt = tgt; | |
321 | els_req->cb_func = cb_func; | |
322 | cb_arg->io_req = els_req; | |
323 | els_req->cb_arg = cb_arg; | |
324 | ||
325 | mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req); | |
326 | rc = bnx2fc_init_mp_req(els_req); | |
327 | if (rc == FAILED) { | |
b2a554ff | 328 | printk(KERN_ERR PFX "ELS MP request init failed\n"); |
853e2bd2 BG |
329 | spin_lock_bh(&tgt->tgt_lock); |
330 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
331 | spin_unlock_bh(&tgt->tgt_lock); | |
332 | rc = -ENOMEM; | |
333 | goto els_err; | |
334 | } else { | |
335 | /* rc SUCCESS */ | |
336 | rc = 0; | |
337 | } | |
338 | ||
339 | /* Set the data_xfer_len to the size of ELS payload */ | |
340 | mp_req->req_len = data_len; | |
341 | els_req->data_xfer_len = mp_req->req_len; | |
342 | ||
343 | /* Fill ELS Payload */ | |
344 | if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS)) { | |
345 | memcpy(mp_req->req_buf, data, data_len); | |
346 | } else { | |
b2a554ff | 347 | printk(KERN_ERR PFX "Invalid ELS op 0x%x\n", op); |
853e2bd2 BG |
348 | els_req->cb_func = NULL; |
349 | els_req->cb_arg = NULL; | |
350 | spin_lock_bh(&tgt->tgt_lock); | |
351 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
352 | spin_unlock_bh(&tgt->tgt_lock); | |
353 | rc = -EINVAL; | |
354 | } | |
355 | ||
356 | if (rc) | |
357 | goto els_err; | |
358 | ||
359 | /* Fill FC header */ | |
360 | fc_hdr = &(mp_req->req_fc_hdr); | |
361 | ||
362 | did = tgt->rport->port_id; | |
363 | sid = tgt->sid; | |
364 | ||
365 | __fc_fill_fc_hdr(fc_hdr, FC_RCTL_ELS_REQ, did, sid, | |
366 | FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | | |
367 | FC_FC_SEQ_INIT, 0); | |
368 | ||
369 | /* Obtain exchange id */ | |
370 | xid = els_req->xid; | |
371 | task_idx = xid/BNX2FC_TASKS_PER_PAGE; | |
372 | index = xid % BNX2FC_TASKS_PER_PAGE; | |
373 | ||
374 | /* Initialize task context for this IO request */ | |
aea71a02 BPG |
375 | task_page = (struct fcoe_task_ctx_entry *) |
376 | interface->hba->task_ctx[task_idx]; | |
853e2bd2 BG |
377 | task = &(task_page[index]); |
378 | bnx2fc_init_mp_task(els_req, task); | |
379 | ||
380 | spin_lock_bh(&tgt->tgt_lock); | |
381 | ||
382 | if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) { | |
383 | printk(KERN_ERR PFX "initiate_els.. session not ready\n"); | |
384 | els_req->cb_func = NULL; | |
385 | els_req->cb_arg = NULL; | |
386 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
387 | spin_unlock_bh(&tgt->tgt_lock); | |
388 | return -EINVAL; | |
389 | } | |
390 | ||
391 | if (timer_msec) | |
392 | bnx2fc_cmd_timer_set(els_req, timer_msec); | |
393 | bnx2fc_add_2_sq(tgt, xid); | |
394 | ||
395 | els_req->on_active_queue = 1; | |
396 | list_add_tail(&els_req->link, &tgt->els_queue); | |
397 | ||
398 | /* Ring doorbell */ | |
399 | bnx2fc_ring_doorbell(tgt); | |
400 | spin_unlock_bh(&tgt->tgt_lock); | |
401 | ||
402 | els_err: | |
403 | return rc; | |
404 | } | |
405 | ||
406 | void bnx2fc_process_els_compl(struct bnx2fc_cmd *els_req, | |
407 | struct fcoe_task_ctx_entry *task, u8 num_rq) | |
408 | { | |
409 | struct bnx2fc_mp_req *mp_req; | |
410 | struct fc_frame_header *fc_hdr; | |
411 | u64 *hdr; | |
412 | u64 *temp_hdr; | |
413 | ||
414 | BNX2FC_ELS_DBG("Entered process_els_compl xid = 0x%x" | |
415 | "cmd_type = %d\n", els_req->xid, els_req->cmd_type); | |
416 | ||
417 | if (test_and_set_bit(BNX2FC_FLAG_ELS_DONE, | |
418 | &els_req->req_flags)) { | |
419 | BNX2FC_ELS_DBG("Timer context finished processing this " | |
420 | "els - 0x%x\n", els_req->xid); | |
25985edc | 421 | /* This IO doesn't receive cleanup completion */ |
853e2bd2 BG |
422 | kref_put(&els_req->refcount, bnx2fc_cmd_release); |
423 | return; | |
424 | } | |
425 | ||
426 | /* Cancel the timeout_work, as we received the response */ | |
427 | if (cancel_delayed_work(&els_req->timeout_work)) | |
428 | kref_put(&els_req->refcount, | |
429 | bnx2fc_cmd_release); /* drop timer hold */ | |
430 | ||
431 | if (els_req->on_active_queue) { | |
432 | list_del_init(&els_req->link); | |
433 | els_req->on_active_queue = 0; | |
434 | } | |
435 | ||
436 | mp_req = &(els_req->mp_req); | |
437 | fc_hdr = &(mp_req->resp_fc_hdr); | |
438 | ||
439 | hdr = (u64 *)fc_hdr; | |
440 | temp_hdr = (u64 *) | |
619c5cb6 | 441 | &task->rxwr_only.union_ctx.comp_info.mp_rsp.fc_hdr; |
853e2bd2 BG |
442 | hdr[0] = cpu_to_be64(temp_hdr[0]); |
443 | hdr[1] = cpu_to_be64(temp_hdr[1]); | |
444 | hdr[2] = cpu_to_be64(temp_hdr[2]); | |
445 | ||
619c5cb6 VZ |
446 | mp_req->resp_len = |
447 | task->rxwr_only.union_ctx.comp_info.mp_rsp.mp_payload_len; | |
853e2bd2 BG |
448 | |
449 | /* Parse ELS response */ | |
450 | if ((els_req->cb_func) && (els_req->cb_arg)) { | |
451 | els_req->cb_func(els_req->cb_arg); | |
452 | els_req->cb_arg = NULL; | |
453 | } | |
454 | ||
455 | kref_put(&els_req->refcount, bnx2fc_cmd_release); | |
456 | } | |
457 | ||
458 | static void bnx2fc_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, | |
459 | void *arg) | |
460 | { | |
461 | struct fcoe_ctlr *fip = arg; | |
462 | struct fc_exch *exch = fc_seq_exch(seq); | |
463 | struct fc_lport *lport = exch->lp; | |
464 | u8 *mac; | |
465 | struct fc_frame_header *fh; | |
466 | u8 op; | |
467 | ||
468 | if (IS_ERR(fp)) | |
469 | goto done; | |
470 | ||
471 | mac = fr_cb(fp)->granted_mac; | |
472 | if (is_zero_ether_addr(mac)) { | |
473 | fh = fc_frame_header_get(fp); | |
474 | if (fh->fh_type != FC_TYPE_ELS) { | |
475 | printk(KERN_ERR PFX "bnx2fc_flogi_resp:" | |
476 | "fh_type != FC_TYPE_ELS\n"); | |
477 | fc_frame_free(fp); | |
478 | return; | |
479 | } | |
480 | op = fc_frame_payload_op(fp); | |
481 | if (lport->vport) { | |
482 | if (op == ELS_LS_RJT) { | |
483 | printk(KERN_ERR PFX "bnx2fc_flogi_resp is LS_RJT\n"); | |
484 | fc_vport_terminate(lport->vport); | |
485 | fc_frame_free(fp); | |
486 | return; | |
487 | } | |
488 | } | |
489 | if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { | |
490 | fc_frame_free(fp); | |
491 | return; | |
492 | } | |
493 | } | |
494 | fip->update_mac(lport, mac); | |
495 | done: | |
496 | fc_lport_flogi_resp(seq, fp, lport); | |
497 | } | |
498 | ||
499 | static void bnx2fc_logo_resp(struct fc_seq *seq, struct fc_frame *fp, | |
500 | void *arg) | |
501 | { | |
502 | struct fcoe_ctlr *fip = arg; | |
503 | struct fc_exch *exch = fc_seq_exch(seq); | |
504 | struct fc_lport *lport = exch->lp; | |
505 | static u8 zero_mac[ETH_ALEN] = { 0 }; | |
506 | ||
507 | if (!IS_ERR(fp)) | |
508 | fip->update_mac(lport, zero_mac); | |
509 | fc_lport_logo_resp(seq, fp, lport); | |
510 | } | |
511 | ||
512 | struct fc_seq *bnx2fc_elsct_send(struct fc_lport *lport, u32 did, | |
513 | struct fc_frame *fp, unsigned int op, | |
514 | void (*resp)(struct fc_seq *, | |
515 | struct fc_frame *, | |
516 | void *), | |
517 | void *arg, u32 timeout) | |
518 | { | |
519 | struct fcoe_port *port = lport_priv(lport); | |
aea71a02 BPG |
520 | struct bnx2fc_interface *interface = port->priv; |
521 | struct fcoe_ctlr *fip = &interface->ctlr; | |
853e2bd2 BG |
522 | struct fc_frame_header *fh = fc_frame_header_get(fp); |
523 | ||
524 | switch (op) { | |
525 | case ELS_FLOGI: | |
526 | case ELS_FDISC: | |
527 | return fc_elsct_send(lport, did, fp, op, bnx2fc_flogi_resp, | |
528 | fip, timeout); | |
529 | case ELS_LOGO: | |
530 | /* only hook onto fabric logouts, not port logouts */ | |
531 | if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) | |
532 | break; | |
533 | return fc_elsct_send(lport, did, fp, op, bnx2fc_logo_resp, | |
534 | fip, timeout); | |
535 | } | |
536 | return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); | |
537 | } |