diff options
Diffstat (limited to 'drivers/scsi/bnx2fc/bnx2fc_els.c')
-rw-r--r-- | drivers/scsi/bnx2fc/bnx2fc_els.c | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/drivers/scsi/bnx2fc/bnx2fc_els.c b/drivers/scsi/bnx2fc/bnx2fc_els.c new file mode 100644 index 000000000000..7a11a255157f --- /dev/null +++ b/drivers/scsi/bnx2fc/bnx2fc_els.c | |||
@@ -0,0 +1,515 @@ | |||
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); | ||
400 | /* This IO doesnt receive cleanup completion */ | ||
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 | } | ||