diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_bsg.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_bsg.c | 1212 |
1 files changed, 1212 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c new file mode 100644 index 00000000000..b905dfe5ea6 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_bsg.c | |||
@@ -0,0 +1,1212 @@ | |||
1 | /* | ||
2 | * QLogic Fibre Channel HBA Driver | ||
3 | * Copyright (c) 2003-2008 QLogic Corporation | ||
4 | * | ||
5 | * See LICENSE.qla2xxx for copyright and licensing details. | ||
6 | */ | ||
7 | #include "qla_def.h" | ||
8 | |||
9 | #include <linux/kthread.h> | ||
10 | #include <linux/vmalloc.h> | ||
11 | #include <linux/delay.h> | ||
12 | |||
13 | /* BSG support for ELS/CT pass through */ | ||
14 | inline srb_t * | ||
15 | qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size) | ||
16 | { | ||
17 | srb_t *sp; | ||
18 | struct qla_hw_data *ha = vha->hw; | ||
19 | struct srb_ctx *ctx; | ||
20 | |||
21 | sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); | ||
22 | if (!sp) | ||
23 | goto done; | ||
24 | ctx = kzalloc(size, GFP_KERNEL); | ||
25 | if (!ctx) { | ||
26 | mempool_free(sp, ha->srb_mempool); | ||
27 | sp = NULL; | ||
28 | goto done; | ||
29 | } | ||
30 | |||
31 | memset(sp, 0, sizeof(*sp)); | ||
32 | sp->fcport = fcport; | ||
33 | sp->ctx = ctx; | ||
34 | done: | ||
35 | return sp; | ||
36 | } | ||
37 | |||
38 | int | ||
39 | qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) | ||
40 | { | ||
41 | int i, ret, num_valid; | ||
42 | uint8_t *bcode; | ||
43 | struct qla_fcp_prio_entry *pri_entry; | ||
44 | |||
45 | ret = 1; | ||
46 | num_valid = 0; | ||
47 | bcode = (uint8_t *)pri_cfg; | ||
48 | |||
49 | if (bcode[0x0] != 'H' || bcode[0x1] != 'Q' || bcode[0x2] != 'O' || | ||
50 | bcode[0x3] != 'S') { | ||
51 | return 0; | ||
52 | } | ||
53 | if (flag != 1) | ||
54 | return ret; | ||
55 | |||
56 | pri_entry = &pri_cfg->entry[0]; | ||
57 | for (i = 0; i < pri_cfg->num_entries; i++) { | ||
58 | if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) | ||
59 | num_valid++; | ||
60 | pri_entry++; | ||
61 | } | ||
62 | |||
63 | if (num_valid == 0) | ||
64 | ret = 0; | ||
65 | |||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) | ||
71 | { | ||
72 | struct Scsi_Host *host = bsg_job->shost; | ||
73 | scsi_qla_host_t *vha = shost_priv(host); | ||
74 | struct qla_hw_data *ha = vha->hw; | ||
75 | int ret = 0; | ||
76 | uint32_t len; | ||
77 | uint32_t oper; | ||
78 | |||
79 | bsg_job->reply->reply_payload_rcv_len = 0; | ||
80 | |||
81 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
82 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
83 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { | ||
84 | ret = -EBUSY; | ||
85 | goto exit_fcp_prio_cfg; | ||
86 | } | ||
87 | |||
88 | /* Get the sub command */ | ||
89 | oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; | ||
90 | |||
91 | /* Only set config is allowed if config memory is not allocated */ | ||
92 | if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) { | ||
93 | ret = -EINVAL; | ||
94 | goto exit_fcp_prio_cfg; | ||
95 | } | ||
96 | switch (oper) { | ||
97 | case QLFC_FCP_PRIO_DISABLE: | ||
98 | if (ha->flags.fcp_prio_enabled) { | ||
99 | ha->flags.fcp_prio_enabled = 0; | ||
100 | ha->fcp_prio_cfg->attributes &= | ||
101 | ~FCP_PRIO_ATTR_ENABLE; | ||
102 | qla24xx_update_all_fcp_prio(vha); | ||
103 | bsg_job->reply->result = DID_OK; | ||
104 | } else { | ||
105 | ret = -EINVAL; | ||
106 | bsg_job->reply->result = (DID_ERROR << 16); | ||
107 | goto exit_fcp_prio_cfg; | ||
108 | } | ||
109 | break; | ||
110 | |||
111 | case QLFC_FCP_PRIO_ENABLE: | ||
112 | if (!ha->flags.fcp_prio_enabled) { | ||
113 | if (ha->fcp_prio_cfg) { | ||
114 | ha->flags.fcp_prio_enabled = 1; | ||
115 | ha->fcp_prio_cfg->attributes |= | ||
116 | FCP_PRIO_ATTR_ENABLE; | ||
117 | qla24xx_update_all_fcp_prio(vha); | ||
118 | bsg_job->reply->result = DID_OK; | ||
119 | } else { | ||
120 | ret = -EINVAL; | ||
121 | bsg_job->reply->result = (DID_ERROR << 16); | ||
122 | goto exit_fcp_prio_cfg; | ||
123 | } | ||
124 | } | ||
125 | break; | ||
126 | |||
127 | case QLFC_FCP_PRIO_GET_CONFIG: | ||
128 | len = bsg_job->reply_payload.payload_len; | ||
129 | if (!len || len > FCP_PRIO_CFG_SIZE) { | ||
130 | ret = -EINVAL; | ||
131 | bsg_job->reply->result = (DID_ERROR << 16); | ||
132 | goto exit_fcp_prio_cfg; | ||
133 | } | ||
134 | |||
135 | bsg_job->reply->result = DID_OK; | ||
136 | bsg_job->reply->reply_payload_rcv_len = | ||
137 | sg_copy_from_buffer( | ||
138 | bsg_job->reply_payload.sg_list, | ||
139 | bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg, | ||
140 | len); | ||
141 | |||
142 | break; | ||
143 | |||
144 | case QLFC_FCP_PRIO_SET_CONFIG: | ||
145 | len = bsg_job->request_payload.payload_len; | ||
146 | if (!len || len > FCP_PRIO_CFG_SIZE) { | ||
147 | bsg_job->reply->result = (DID_ERROR << 16); | ||
148 | ret = -EINVAL; | ||
149 | goto exit_fcp_prio_cfg; | ||
150 | } | ||
151 | |||
152 | if (!ha->fcp_prio_cfg) { | ||
153 | ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); | ||
154 | if (!ha->fcp_prio_cfg) { | ||
155 | qla_printk(KERN_WARNING, ha, | ||
156 | "Unable to allocate memory " | ||
157 | "for fcp prio config data (%x).\n", | ||
158 | FCP_PRIO_CFG_SIZE); | ||
159 | bsg_job->reply->result = (DID_ERROR << 16); | ||
160 | ret = -ENOMEM; | ||
161 | goto exit_fcp_prio_cfg; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); | ||
166 | sg_copy_to_buffer(bsg_job->request_payload.sg_list, | ||
167 | bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg, | ||
168 | FCP_PRIO_CFG_SIZE); | ||
169 | |||
170 | /* validate fcp priority data */ | ||
171 | if (!qla24xx_fcp_prio_cfg_valid( | ||
172 | (struct qla_fcp_prio_cfg *) | ||
173 | ha->fcp_prio_cfg, 1)) { | ||
174 | bsg_job->reply->result = (DID_ERROR << 16); | ||
175 | ret = -EINVAL; | ||
176 | /* If buffer was invalidatic int | ||
177 | * fcp_prio_cfg is of no use | ||
178 | */ | ||
179 | vfree(ha->fcp_prio_cfg); | ||
180 | ha->fcp_prio_cfg = NULL; | ||
181 | goto exit_fcp_prio_cfg; | ||
182 | } | ||
183 | |||
184 | ha->flags.fcp_prio_enabled = 0; | ||
185 | if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE) | ||
186 | ha->flags.fcp_prio_enabled = 1; | ||
187 | qla24xx_update_all_fcp_prio(vha); | ||
188 | bsg_job->reply->result = DID_OK; | ||
189 | break; | ||
190 | default: | ||
191 | ret = -EINVAL; | ||
192 | break; | ||
193 | } | ||
194 | exit_fcp_prio_cfg: | ||
195 | bsg_job->job_done(bsg_job); | ||
196 | return ret; | ||
197 | } | ||
198 | static int | ||
199 | qla2x00_process_els(struct fc_bsg_job *bsg_job) | ||
200 | { | ||
201 | struct fc_rport *rport; | ||
202 | fc_port_t *fcport; | ||
203 | struct Scsi_Host *host; | ||
204 | scsi_qla_host_t *vha; | ||
205 | struct qla_hw_data *ha; | ||
206 | srb_t *sp; | ||
207 | const char *type; | ||
208 | int req_sg_cnt, rsp_sg_cnt; | ||
209 | int rval = (DRIVER_ERROR << 16); | ||
210 | uint16_t nextlid = 0; | ||
211 | struct srb_ctx *els; | ||
212 | |||
213 | /* Multiple SG's are not supported for ELS requests */ | ||
214 | if (bsg_job->request_payload.sg_cnt > 1 || | ||
215 | bsg_job->reply_payload.sg_cnt > 1) { | ||
216 | DEBUG2(printk(KERN_INFO | ||
217 | "multiple SG's are not supported for ELS requests" | ||
218 | " [request_sg_cnt: %x reply_sg_cnt: %x]\n", | ||
219 | bsg_job->request_payload.sg_cnt, | ||
220 | bsg_job->reply_payload.sg_cnt)); | ||
221 | rval = -EPERM; | ||
222 | goto done; | ||
223 | } | ||
224 | |||
225 | /* ELS request for rport */ | ||
226 | if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { | ||
227 | rport = bsg_job->rport; | ||
228 | fcport = *(fc_port_t **) rport->dd_data; | ||
229 | host = rport_to_shost(rport); | ||
230 | vha = shost_priv(host); | ||
231 | ha = vha->hw; | ||
232 | type = "FC_BSG_RPT_ELS"; | ||
233 | |||
234 | /* make sure the rport is logged in, | ||
235 | * if not perform fabric login | ||
236 | */ | ||
237 | if (qla2x00_fabric_login(vha, fcport, &nextlid)) { | ||
238 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
239 | "failed to login port %06X for ELS passthru\n", | ||
240 | fcport->d_id.b24)); | ||
241 | rval = -EIO; | ||
242 | goto done; | ||
243 | } | ||
244 | } else { | ||
245 | host = bsg_job->shost; | ||
246 | vha = shost_priv(host); | ||
247 | ha = vha->hw; | ||
248 | type = "FC_BSG_HST_ELS_NOLOGIN"; | ||
249 | |||
250 | /* Allocate a dummy fcport structure, since functions | ||
251 | * preparing the IOCB and mailbox command retrieves port | ||
252 | * specific information from fcport structure. For Host based | ||
253 | * ELS commands there will be no fcport structure allocated | ||
254 | */ | ||
255 | fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); | ||
256 | if (!fcport) { | ||
257 | rval = -ENOMEM; | ||
258 | goto done; | ||
259 | } | ||
260 | |||
261 | /* Initialize all required fields of fcport */ | ||
262 | fcport->vha = vha; | ||
263 | fcport->vp_idx = vha->vp_idx; | ||
264 | fcport->d_id.b.al_pa = | ||
265 | bsg_job->request->rqst_data.h_els.port_id[0]; | ||
266 | fcport->d_id.b.area = | ||
267 | bsg_job->request->rqst_data.h_els.port_id[1]; | ||
268 | fcport->d_id.b.domain = | ||
269 | bsg_job->request->rqst_data.h_els.port_id[2]; | ||
270 | fcport->loop_id = | ||
271 | (fcport->d_id.b.al_pa == 0xFD) ? | ||
272 | NPH_FABRIC_CONTROLLER : NPH_F_PORT; | ||
273 | } | ||
274 | |||
275 | if (!vha->flags.online) { | ||
276 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
277 | "host not online\n")); | ||
278 | rval = -EIO; | ||
279 | goto done; | ||
280 | } | ||
281 | |||
282 | req_sg_cnt = | ||
283 | dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
284 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
285 | if (!req_sg_cnt) { | ||
286 | rval = -ENOMEM; | ||
287 | goto done_free_fcport; | ||
288 | } | ||
289 | |||
290 | rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
291 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
292 | if (!rsp_sg_cnt) { | ||
293 | rval = -ENOMEM; | ||
294 | goto done_free_fcport; | ||
295 | } | ||
296 | |||
297 | if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || | ||
298 | (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { | ||
299 | DEBUG2(printk(KERN_INFO | ||
300 | "dma mapping resulted in different sg counts \ | ||
301 | [request_sg_cnt: %x dma_request_sg_cnt: %x\ | ||
302 | reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", | ||
303 | bsg_job->request_payload.sg_cnt, req_sg_cnt, | ||
304 | bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); | ||
305 | rval = -EAGAIN; | ||
306 | goto done_unmap_sg; | ||
307 | } | ||
308 | |||
309 | /* Alloc SRB structure */ | ||
310 | sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); | ||
311 | if (!sp) { | ||
312 | rval = -ENOMEM; | ||
313 | goto done_unmap_sg; | ||
314 | } | ||
315 | |||
316 | els = sp->ctx; | ||
317 | els->type = | ||
318 | (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? | ||
319 | SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); | ||
320 | els->name = | ||
321 | (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? | ||
322 | "bsg_els_rpt" : "bsg_els_hst"); | ||
323 | els->u.bsg_job = bsg_job; | ||
324 | |||
325 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
326 | "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " | ||
327 | "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, | ||
328 | bsg_job->request->rqst_data.h_els.command_code, | ||
329 | fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, | ||
330 | fcport->d_id.b.al_pa)); | ||
331 | |||
332 | rval = qla2x00_start_sp(sp); | ||
333 | if (rval != QLA_SUCCESS) { | ||
334 | kfree(sp->ctx); | ||
335 | mempool_free(sp, ha->srb_mempool); | ||
336 | rval = -EIO; | ||
337 | goto done_unmap_sg; | ||
338 | } | ||
339 | return rval; | ||
340 | |||
341 | done_unmap_sg: | ||
342 | dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
343 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
344 | dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
345 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
346 | goto done_free_fcport; | ||
347 | |||
348 | done_free_fcport: | ||
349 | if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) | ||
350 | kfree(fcport); | ||
351 | done: | ||
352 | return rval; | ||
353 | } | ||
354 | |||
355 | static int | ||
356 | qla2x00_process_ct(struct fc_bsg_job *bsg_job) | ||
357 | { | ||
358 | srb_t *sp; | ||
359 | struct Scsi_Host *host = bsg_job->shost; | ||
360 | scsi_qla_host_t *vha = shost_priv(host); | ||
361 | struct qla_hw_data *ha = vha->hw; | ||
362 | int rval = (DRIVER_ERROR << 16); | ||
363 | int req_sg_cnt, rsp_sg_cnt; | ||
364 | uint16_t loop_id; | ||
365 | struct fc_port *fcport; | ||
366 | char *type = "FC_BSG_HST_CT"; | ||
367 | struct srb_ctx *ct; | ||
368 | |||
369 | /* pass through is supported only for ISP 4Gb or higher */ | ||
370 | if (!IS_FWI2_CAPABLE(ha)) { | ||
371 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
372 | "scsi(%ld):Firmware is not capable to support FC " | ||
373 | "CT pass thru\n", vha->host_no)); | ||
374 | rval = -EPERM; | ||
375 | goto done; | ||
376 | } | ||
377 | |||
378 | req_sg_cnt = | ||
379 | dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
380 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
381 | if (!req_sg_cnt) { | ||
382 | rval = -ENOMEM; | ||
383 | goto done; | ||
384 | } | ||
385 | |||
386 | rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
387 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
388 | if (!rsp_sg_cnt) { | ||
389 | rval = -ENOMEM; | ||
390 | goto done; | ||
391 | } | ||
392 | |||
393 | if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || | ||
394 | (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { | ||
395 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
396 | "[request_sg_cnt: %x dma_request_sg_cnt: %x\ | ||
397 | reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", | ||
398 | bsg_job->request_payload.sg_cnt, req_sg_cnt, | ||
399 | bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); | ||
400 | rval = -EAGAIN; | ||
401 | goto done_unmap_sg; | ||
402 | } | ||
403 | |||
404 | if (!vha->flags.online) { | ||
405 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
406 | "host not online\n")); | ||
407 | rval = -EIO; | ||
408 | goto done_unmap_sg; | ||
409 | } | ||
410 | |||
411 | loop_id = | ||
412 | (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000) | ||
413 | >> 24; | ||
414 | switch (loop_id) { | ||
415 | case 0xFC: | ||
416 | loop_id = cpu_to_le16(NPH_SNS); | ||
417 | break; | ||
418 | case 0xFA: | ||
419 | loop_id = vha->mgmt_svr_loop_id; | ||
420 | break; | ||
421 | default: | ||
422 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
423 | "Unknown loop id: %x\n", loop_id)); | ||
424 | rval = -EINVAL; | ||
425 | goto done_unmap_sg; | ||
426 | } | ||
427 | |||
428 | /* Allocate a dummy fcport structure, since functions preparing the | ||
429 | * IOCB and mailbox command retrieves port specific information | ||
430 | * from fcport structure. For Host based ELS commands there will be | ||
431 | * no fcport structure allocated | ||
432 | */ | ||
433 | fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); | ||
434 | if (!fcport) { | ||
435 | rval = -ENOMEM; | ||
436 | goto done_unmap_sg; | ||
437 | } | ||
438 | |||
439 | /* Initialize all required fields of fcport */ | ||
440 | fcport->vha = vha; | ||
441 | fcport->vp_idx = vha->vp_idx; | ||
442 | fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0]; | ||
443 | fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1]; | ||
444 | fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2]; | ||
445 | fcport->loop_id = loop_id; | ||
446 | |||
447 | /* Alloc SRB structure */ | ||
448 | sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); | ||
449 | if (!sp) { | ||
450 | rval = -ENOMEM; | ||
451 | goto done_free_fcport; | ||
452 | } | ||
453 | |||
454 | ct = sp->ctx; | ||
455 | ct->type = SRB_CT_CMD; | ||
456 | ct->name = "bsg_ct"; | ||
457 | ct->u.bsg_job = bsg_job; | ||
458 | |||
459 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
460 | "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " | ||
461 | "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, | ||
462 | (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16), | ||
463 | fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, | ||
464 | fcport->d_id.b.al_pa)); | ||
465 | |||
466 | rval = qla2x00_start_sp(sp); | ||
467 | if (rval != QLA_SUCCESS) { | ||
468 | kfree(sp->ctx); | ||
469 | mempool_free(sp, ha->srb_mempool); | ||
470 | rval = -EIO; | ||
471 | goto done_free_fcport; | ||
472 | } | ||
473 | return rval; | ||
474 | |||
475 | done_free_fcport: | ||
476 | kfree(fcport); | ||
477 | done_unmap_sg: | ||
478 | dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
479 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
480 | dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
481 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
482 | done: | ||
483 | return rval; | ||
484 | } | ||
485 | |||
486 | static int | ||
487 | qla2x00_process_loopback(struct fc_bsg_job *bsg_job) | ||
488 | { | ||
489 | struct Scsi_Host *host = bsg_job->shost; | ||
490 | scsi_qla_host_t *vha = shost_priv(host); | ||
491 | struct qla_hw_data *ha = vha->hw; | ||
492 | int rval; | ||
493 | uint8_t command_sent; | ||
494 | char *type; | ||
495 | struct msg_echo_lb elreq; | ||
496 | uint16_t response[MAILBOX_REGISTER_COUNT]; | ||
497 | uint8_t *fw_sts_ptr; | ||
498 | uint8_t *req_data = NULL; | ||
499 | dma_addr_t req_data_dma; | ||
500 | uint32_t req_data_len; | ||
501 | uint8_t *rsp_data = NULL; | ||
502 | dma_addr_t rsp_data_dma; | ||
503 | uint32_t rsp_data_len; | ||
504 | |||
505 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
506 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
507 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) | ||
508 | return -EBUSY; | ||
509 | |||
510 | if (!vha->flags.online) { | ||
511 | DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); | ||
512 | return -EIO; | ||
513 | } | ||
514 | |||
515 | elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev, | ||
516 | bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, | ||
517 | DMA_TO_DEVICE); | ||
518 | |||
519 | if (!elreq.req_sg_cnt) | ||
520 | return -ENOMEM; | ||
521 | |||
522 | elreq.rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, | ||
523 | bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, | ||
524 | DMA_FROM_DEVICE); | ||
525 | |||
526 | if (!elreq.rsp_sg_cnt) { | ||
527 | rval = -ENOMEM; | ||
528 | goto done_unmap_req_sg; | ||
529 | } | ||
530 | |||
531 | if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) || | ||
532 | (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { | ||
533 | DEBUG2(printk(KERN_INFO | ||
534 | "dma mapping resulted in different sg counts " | ||
535 | "[request_sg_cnt: %x dma_request_sg_cnt: %x " | ||
536 | "reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", | ||
537 | bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt, | ||
538 | bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt)); | ||
539 | rval = -EAGAIN; | ||
540 | goto done_unmap_sg; | ||
541 | } | ||
542 | req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; | ||
543 | req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, | ||
544 | &req_data_dma, GFP_KERNEL); | ||
545 | if (!req_data) { | ||
546 | DEBUG2(printk(KERN_ERR "%s: dma alloc for req_data " | ||
547 | "failed for host=%lu\n", __func__, vha->host_no)); | ||
548 | rval = -ENOMEM; | ||
549 | goto done_unmap_sg; | ||
550 | } | ||
551 | |||
552 | rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, | ||
553 | &rsp_data_dma, GFP_KERNEL); | ||
554 | if (!rsp_data) { | ||
555 | DEBUG2(printk(KERN_ERR "%s: dma alloc for rsp_data " | ||
556 | "failed for host=%lu\n", __func__, vha->host_no)); | ||
557 | rval = -ENOMEM; | ||
558 | goto done_free_dma_req; | ||
559 | } | ||
560 | |||
561 | /* Copy the request buffer in req_data now */ | ||
562 | sg_copy_to_buffer(bsg_job->request_payload.sg_list, | ||
563 | bsg_job->request_payload.sg_cnt, req_data, req_data_len); | ||
564 | |||
565 | elreq.send_dma = req_data_dma; | ||
566 | elreq.rcv_dma = rsp_data_dma; | ||
567 | elreq.transfer_size = req_data_len; | ||
568 | |||
569 | elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; | ||
570 | |||
571 | if (ha->current_topology != ISP_CFG_F) { | ||
572 | type = "FC_BSG_HST_VENDOR_LOOPBACK"; | ||
573 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
574 | "scsi(%ld) bsg rqst type: %s\n", | ||
575 | vha->host_no, type)); | ||
576 | |||
577 | command_sent = INT_DEF_LB_LOOPBACK_CMD; | ||
578 | rval = qla2x00_loopback_test(vha, &elreq, response); | ||
579 | if (IS_QLA81XX(ha)) { | ||
580 | if (response[0] == MBS_COMMAND_ERROR && | ||
581 | response[1] == MBS_LB_RESET) { | ||
582 | DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing " | ||
583 | "ISP\n", __func__, vha->host_no)); | ||
584 | set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); | ||
585 | qla2xxx_wake_dpc(vha); | ||
586 | } | ||
587 | } | ||
588 | } else { | ||
589 | type = "FC_BSG_HST_VENDOR_ECHO_DIAG"; | ||
590 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
591 | "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); | ||
592 | command_sent = INT_DEF_LB_ECHO_CMD; | ||
593 | rval = qla2x00_echo_test(vha, &elreq, response); | ||
594 | } | ||
595 | |||
596 | if (rval) { | ||
597 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
598 | "request %s failed\n", vha->host_no, type)); | ||
599 | |||
600 | fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + | ||
601 | sizeof(struct fc_bsg_reply); | ||
602 | |||
603 | memcpy(fw_sts_ptr, response, sizeof(response)); | ||
604 | fw_sts_ptr += sizeof(response); | ||
605 | *fw_sts_ptr = command_sent; | ||
606 | rval = 0; | ||
607 | bsg_job->reply->reply_payload_rcv_len = 0; | ||
608 | bsg_job->reply->result = (DID_ERROR << 16); | ||
609 | } else { | ||
610 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
611 | "request %s completed\n", vha->host_no, type)); | ||
612 | |||
613 | bsg_job->reply_len = sizeof(struct fc_bsg_reply) + | ||
614 | sizeof(response) + sizeof(uint8_t); | ||
615 | bsg_job->reply->reply_payload_rcv_len = | ||
616 | bsg_job->reply_payload.payload_len; | ||
617 | fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + | ||
618 | sizeof(struct fc_bsg_reply); | ||
619 | memcpy(fw_sts_ptr, response, sizeof(response)); | ||
620 | fw_sts_ptr += sizeof(response); | ||
621 | *fw_sts_ptr = command_sent; | ||
622 | bsg_job->reply->result = DID_OK; | ||
623 | sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | ||
624 | bsg_job->reply_payload.sg_cnt, rsp_data, | ||
625 | rsp_data_len); | ||
626 | } | ||
627 | bsg_job->job_done(bsg_job); | ||
628 | |||
629 | dma_free_coherent(&ha->pdev->dev, rsp_data_len, | ||
630 | rsp_data, rsp_data_dma); | ||
631 | done_free_dma_req: | ||
632 | dma_free_coherent(&ha->pdev->dev, req_data_len, | ||
633 | req_data, req_data_dma); | ||
634 | done_unmap_sg: | ||
635 | dma_unmap_sg(&ha->pdev->dev, | ||
636 | bsg_job->reply_payload.sg_list, | ||
637 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
638 | done_unmap_req_sg: | ||
639 | dma_unmap_sg(&ha->pdev->dev, | ||
640 | bsg_job->request_payload.sg_list, | ||
641 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
642 | return rval; | ||
643 | } | ||
644 | |||
645 | static int | ||
646 | qla84xx_reset(struct fc_bsg_job *bsg_job) | ||
647 | { | ||
648 | struct Scsi_Host *host = bsg_job->shost; | ||
649 | scsi_qla_host_t *vha = shost_priv(host); | ||
650 | struct qla_hw_data *ha = vha->hw; | ||
651 | int rval = 0; | ||
652 | uint32_t flag; | ||
653 | |||
654 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
655 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
656 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) | ||
657 | return -EBUSY; | ||
658 | |||
659 | if (!IS_QLA84XX(ha)) { | ||
660 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " | ||
661 | "exiting.\n", vha->host_no)); | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | |||
665 | flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; | ||
666 | |||
667 | rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); | ||
668 | |||
669 | if (rval) { | ||
670 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
671 | "request 84xx reset failed\n", vha->host_no)); | ||
672 | rval = bsg_job->reply->reply_payload_rcv_len = 0; | ||
673 | bsg_job->reply->result = (DID_ERROR << 16); | ||
674 | |||
675 | } else { | ||
676 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
677 | "request 84xx reset completed\n", vha->host_no)); | ||
678 | bsg_job->reply->result = DID_OK; | ||
679 | } | ||
680 | |||
681 | bsg_job->job_done(bsg_job); | ||
682 | return rval; | ||
683 | } | ||
684 | |||
685 | static int | ||
686 | qla84xx_updatefw(struct fc_bsg_job *bsg_job) | ||
687 | { | ||
688 | struct Scsi_Host *host = bsg_job->shost; | ||
689 | scsi_qla_host_t *vha = shost_priv(host); | ||
690 | struct qla_hw_data *ha = vha->hw; | ||
691 | struct verify_chip_entry_84xx *mn = NULL; | ||
692 | dma_addr_t mn_dma, fw_dma; | ||
693 | void *fw_buf = NULL; | ||
694 | int rval = 0; | ||
695 | uint32_t sg_cnt; | ||
696 | uint32_t data_len; | ||
697 | uint16_t options; | ||
698 | uint32_t flag; | ||
699 | uint32_t fw_ver; | ||
700 | |||
701 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
702 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
703 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) | ||
704 | return -EBUSY; | ||
705 | |||
706 | if (!IS_QLA84XX(ha)) { | ||
707 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " | ||
708 | "exiting.\n", vha->host_no)); | ||
709 | return -EINVAL; | ||
710 | } | ||
711 | |||
712 | sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
713 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
714 | if (!sg_cnt) | ||
715 | return -ENOMEM; | ||
716 | |||
717 | if (sg_cnt != bsg_job->request_payload.sg_cnt) { | ||
718 | DEBUG2(printk(KERN_INFO | ||
719 | "dma mapping resulted in different sg counts " | ||
720 | "request_sg_cnt: %x dma_request_sg_cnt: %x ", | ||
721 | bsg_job->request_payload.sg_cnt, sg_cnt)); | ||
722 | rval = -EAGAIN; | ||
723 | goto done_unmap_sg; | ||
724 | } | ||
725 | |||
726 | data_len = bsg_job->request_payload.payload_len; | ||
727 | fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len, | ||
728 | &fw_dma, GFP_KERNEL); | ||
729 | if (!fw_buf) { | ||
730 | DEBUG2(printk(KERN_ERR "%s: dma alloc for fw_buf " | ||
731 | "failed for host=%lu\n", __func__, vha->host_no)); | ||
732 | rval = -ENOMEM; | ||
733 | goto done_unmap_sg; | ||
734 | } | ||
735 | |||
736 | sg_copy_to_buffer(bsg_job->request_payload.sg_list, | ||
737 | bsg_job->request_payload.sg_cnt, fw_buf, data_len); | ||
738 | |||
739 | mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); | ||
740 | if (!mn) { | ||
741 | DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " | ||
742 | "failed for host=%lu\n", __func__, vha->host_no)); | ||
743 | rval = -ENOMEM; | ||
744 | goto done_free_fw_buf; | ||
745 | } | ||
746 | |||
747 | flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; | ||
748 | fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2))); | ||
749 | |||
750 | memset(mn, 0, sizeof(struct access_chip_84xx)); | ||
751 | mn->entry_type = VERIFY_CHIP_IOCB_TYPE; | ||
752 | mn->entry_count = 1; | ||
753 | |||
754 | options = VCO_FORCE_UPDATE | VCO_END_OF_DATA; | ||
755 | if (flag == A84_ISSUE_UPDATE_DIAGFW_CMD) | ||
756 | options |= VCO_DIAG_FW; | ||
757 | |||
758 | mn->options = cpu_to_le16(options); | ||
759 | mn->fw_ver = cpu_to_le32(fw_ver); | ||
760 | mn->fw_size = cpu_to_le32(data_len); | ||
761 | mn->fw_seq_size = cpu_to_le32(data_len); | ||
762 | mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma)); | ||
763 | mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma)); | ||
764 | mn->dseg_length = cpu_to_le32(data_len); | ||
765 | mn->data_seg_cnt = cpu_to_le16(1); | ||
766 | |||
767 | rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); | ||
768 | |||
769 | if (rval) { | ||
770 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
771 | "request 84xx updatefw failed\n", vha->host_no)); | ||
772 | |||
773 | rval = bsg_job->reply->reply_payload_rcv_len = 0; | ||
774 | bsg_job->reply->result = (DID_ERROR << 16); | ||
775 | |||
776 | } else { | ||
777 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
778 | "request 84xx updatefw completed\n", vha->host_no)); | ||
779 | |||
780 | bsg_job->reply_len = sizeof(struct fc_bsg_reply); | ||
781 | bsg_job->reply->result = DID_OK; | ||
782 | } | ||
783 | |||
784 | bsg_job->job_done(bsg_job); | ||
785 | dma_pool_free(ha->s_dma_pool, mn, mn_dma); | ||
786 | |||
787 | done_free_fw_buf: | ||
788 | dma_free_coherent(&ha->pdev->dev, data_len, fw_buf, fw_dma); | ||
789 | |||
790 | done_unmap_sg: | ||
791 | dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
792 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
793 | |||
794 | return rval; | ||
795 | } | ||
796 | |||
797 | static int | ||
798 | qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) | ||
799 | { | ||
800 | struct Scsi_Host *host = bsg_job->shost; | ||
801 | scsi_qla_host_t *vha = shost_priv(host); | ||
802 | struct qla_hw_data *ha = vha->hw; | ||
803 | struct access_chip_84xx *mn = NULL; | ||
804 | dma_addr_t mn_dma, mgmt_dma; | ||
805 | void *mgmt_b = NULL; | ||
806 | int rval = 0; | ||
807 | struct qla_bsg_a84_mgmt *ql84_mgmt; | ||
808 | uint32_t sg_cnt; | ||
809 | uint32_t data_len = 0; | ||
810 | uint32_t dma_direction = DMA_NONE; | ||
811 | |||
812 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
813 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
814 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) | ||
815 | return -EBUSY; | ||
816 | |||
817 | if (!IS_QLA84XX(ha)) { | ||
818 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " | ||
819 | "exiting.\n", vha->host_no)); | ||
820 | return -EINVAL; | ||
821 | } | ||
822 | |||
823 | ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request + | ||
824 | sizeof(struct fc_bsg_request)); | ||
825 | if (!ql84_mgmt) { | ||
826 | DEBUG2(printk("%s(%ld): mgmt header not provided, exiting.\n", | ||
827 | __func__, vha->host_no)); | ||
828 | return -EINVAL; | ||
829 | } | ||
830 | |||
831 | mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); | ||
832 | if (!mn) { | ||
833 | DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " | ||
834 | "failed for host=%lu\n", __func__, vha->host_no)); | ||
835 | return -ENOMEM; | ||
836 | } | ||
837 | |||
838 | memset(mn, 0, sizeof(struct access_chip_84xx)); | ||
839 | mn->entry_type = ACCESS_CHIP_IOCB_TYPE; | ||
840 | mn->entry_count = 1; | ||
841 | |||
842 | switch (ql84_mgmt->mgmt.cmd) { | ||
843 | case QLA84_MGMT_READ_MEM: | ||
844 | case QLA84_MGMT_GET_INFO: | ||
845 | sg_cnt = dma_map_sg(&ha->pdev->dev, | ||
846 | bsg_job->reply_payload.sg_list, | ||
847 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
848 | if (!sg_cnt) { | ||
849 | rval = -ENOMEM; | ||
850 | goto exit_mgmt; | ||
851 | } | ||
852 | |||
853 | dma_direction = DMA_FROM_DEVICE; | ||
854 | |||
855 | if (sg_cnt != bsg_job->reply_payload.sg_cnt) { | ||
856 | DEBUG2(printk(KERN_INFO | ||
857 | "dma mapping resulted in different sg counts " | ||
858 | "reply_sg_cnt: %x dma_reply_sg_cnt: %x\n", | ||
859 | bsg_job->reply_payload.sg_cnt, sg_cnt)); | ||
860 | rval = -EAGAIN; | ||
861 | goto done_unmap_sg; | ||
862 | } | ||
863 | |||
864 | data_len = bsg_job->reply_payload.payload_len; | ||
865 | |||
866 | mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, | ||
867 | &mgmt_dma, GFP_KERNEL); | ||
868 | if (!mgmt_b) { | ||
869 | DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " | ||
870 | "failed for host=%lu\n", | ||
871 | __func__, vha->host_no)); | ||
872 | rval = -ENOMEM; | ||
873 | goto done_unmap_sg; | ||
874 | } | ||
875 | |||
876 | if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) { | ||
877 | mn->options = cpu_to_le16(ACO_DUMP_MEMORY); | ||
878 | mn->parameter1 = | ||
879 | cpu_to_le32( | ||
880 | ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); | ||
881 | |||
882 | } else if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO) { | ||
883 | mn->options = cpu_to_le16(ACO_REQUEST_INFO); | ||
884 | mn->parameter1 = | ||
885 | cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.info.type); | ||
886 | |||
887 | mn->parameter2 = | ||
888 | cpu_to_le32( | ||
889 | ql84_mgmt->mgmt.mgmtp.u.info.context); | ||
890 | } | ||
891 | break; | ||
892 | |||
893 | case QLA84_MGMT_WRITE_MEM: | ||
894 | sg_cnt = dma_map_sg(&ha->pdev->dev, | ||
895 | bsg_job->request_payload.sg_list, | ||
896 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
897 | |||
898 | if (!sg_cnt) { | ||
899 | rval = -ENOMEM; | ||
900 | goto exit_mgmt; | ||
901 | } | ||
902 | |||
903 | dma_direction = DMA_TO_DEVICE; | ||
904 | |||
905 | if (sg_cnt != bsg_job->request_payload.sg_cnt) { | ||
906 | DEBUG2(printk(KERN_INFO | ||
907 | "dma mapping resulted in different sg counts " | ||
908 | "request_sg_cnt: %x dma_request_sg_cnt: %x ", | ||
909 | bsg_job->request_payload.sg_cnt, sg_cnt)); | ||
910 | rval = -EAGAIN; | ||
911 | goto done_unmap_sg; | ||
912 | } | ||
913 | |||
914 | data_len = bsg_job->request_payload.payload_len; | ||
915 | mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, | ||
916 | &mgmt_dma, GFP_KERNEL); | ||
917 | if (!mgmt_b) { | ||
918 | DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " | ||
919 | "failed for host=%lu\n", | ||
920 | __func__, vha->host_no)); | ||
921 | rval = -ENOMEM; | ||
922 | goto done_unmap_sg; | ||
923 | } | ||
924 | |||
925 | sg_copy_to_buffer(bsg_job->request_payload.sg_list, | ||
926 | bsg_job->request_payload.sg_cnt, mgmt_b, data_len); | ||
927 | |||
928 | mn->options = cpu_to_le16(ACO_LOAD_MEMORY); | ||
929 | mn->parameter1 = | ||
930 | cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); | ||
931 | break; | ||
932 | |||
933 | case QLA84_MGMT_CHNG_CONFIG: | ||
934 | mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM); | ||
935 | mn->parameter1 = | ||
936 | cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.id); | ||
937 | |||
938 | mn->parameter2 = | ||
939 | cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param0); | ||
940 | |||
941 | mn->parameter3 = | ||
942 | cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param1); | ||
943 | break; | ||
944 | |||
945 | default: | ||
946 | rval = -EIO; | ||
947 | goto exit_mgmt; | ||
948 | } | ||
949 | |||
950 | if (ql84_mgmt->mgmt.cmd != QLA84_MGMT_CHNG_CONFIG) { | ||
951 | mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->mgmt.len); | ||
952 | mn->dseg_count = cpu_to_le16(1); | ||
953 | mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma)); | ||
954 | mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma)); | ||
955 | mn->dseg_length = cpu_to_le32(ql84_mgmt->mgmt.len); | ||
956 | } | ||
957 | |||
958 | rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0); | ||
959 | |||
960 | if (rval) { | ||
961 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
962 | "request 84xx mgmt failed\n", vha->host_no)); | ||
963 | |||
964 | rval = bsg_job->reply->reply_payload_rcv_len = 0; | ||
965 | bsg_job->reply->result = (DID_ERROR << 16); | ||
966 | |||
967 | } else { | ||
968 | DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " | ||
969 | "request 84xx mgmt completed\n", vha->host_no)); | ||
970 | |||
971 | bsg_job->reply_len = sizeof(struct fc_bsg_reply); | ||
972 | bsg_job->reply->result = DID_OK; | ||
973 | |||
974 | if ((ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) || | ||
975 | (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO)) { | ||
976 | bsg_job->reply->reply_payload_rcv_len = | ||
977 | bsg_job->reply_payload.payload_len; | ||
978 | |||
979 | sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | ||
980 | bsg_job->reply_payload.sg_cnt, mgmt_b, | ||
981 | data_len); | ||
982 | } | ||
983 | } | ||
984 | |||
985 | bsg_job->job_done(bsg_job); | ||
986 | |||
987 | done_unmap_sg: | ||
988 | if (mgmt_b) | ||
989 | dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma); | ||
990 | |||
991 | if (dma_direction == DMA_TO_DEVICE) | ||
992 | dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, | ||
993 | bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
994 | else if (dma_direction == DMA_FROM_DEVICE) | ||
995 | dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, | ||
996 | bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
997 | |||
998 | exit_mgmt: | ||
999 | dma_pool_free(ha->s_dma_pool, mn, mn_dma); | ||
1000 | |||
1001 | return rval; | ||
1002 | } | ||
1003 | |||
1004 | static int | ||
1005 | qla24xx_iidma(struct fc_bsg_job *bsg_job) | ||
1006 | { | ||
1007 | struct Scsi_Host *host = bsg_job->shost; | ||
1008 | scsi_qla_host_t *vha = shost_priv(host); | ||
1009 | struct qla_hw_data *ha = vha->hw; | ||
1010 | int rval = 0; | ||
1011 | struct qla_port_param *port_param = NULL; | ||
1012 | fc_port_t *fcport = NULL; | ||
1013 | uint16_t mb[MAILBOX_REGISTER_COUNT]; | ||
1014 | uint8_t *rsp_ptr = NULL; | ||
1015 | |||
1016 | bsg_job->reply->reply_payload_rcv_len = 0; | ||
1017 | |||
1018 | if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || | ||
1019 | test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || | ||
1020 | test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) | ||
1021 | return -EBUSY; | ||
1022 | |||
1023 | if (!IS_IIDMA_CAPABLE(vha->hw)) { | ||
1024 | DEBUG2(qla_printk(KERN_WARNING, ha, "%s(%lu): iiDMA not " | ||
1025 | "supported\n", __func__, vha->host_no)); | ||
1026 | return -EINVAL; | ||
1027 | } | ||
1028 | |||
1029 | port_param = (struct qla_port_param *)((char *)bsg_job->request + | ||
1030 | sizeof(struct fc_bsg_request)); | ||
1031 | if (!port_param) { | ||
1032 | DEBUG2(printk("%s(%ld): port_param header not provided, " | ||
1033 | "exiting.\n", __func__, vha->host_no)); | ||
1034 | return -EINVAL; | ||
1035 | } | ||
1036 | |||
1037 | if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) { | ||
1038 | DEBUG2(printk(KERN_ERR "%s(%ld): Invalid destination type\n", | ||
1039 | __func__, vha->host_no)); | ||
1040 | return -EINVAL; | ||
1041 | } | ||
1042 | |||
1043 | list_for_each_entry(fcport, &vha->vp_fcports, list) { | ||
1044 | if (fcport->port_type != FCT_TARGET) | ||
1045 | continue; | ||
1046 | |||
1047 | if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn, | ||
1048 | fcport->port_name, sizeof(fcport->port_name))) | ||
1049 | continue; | ||
1050 | break; | ||
1051 | } | ||
1052 | |||
1053 | if (!fcport) { | ||
1054 | DEBUG2(printk(KERN_ERR "%s(%ld): Failed to find port\n", | ||
1055 | __func__, vha->host_no)); | ||
1056 | return -EINVAL; | ||
1057 | } | ||
1058 | |||
1059 | if (port_param->mode) | ||
1060 | rval = qla2x00_set_idma_speed(vha, fcport->loop_id, | ||
1061 | port_param->speed, mb); | ||
1062 | else | ||
1063 | rval = qla2x00_get_idma_speed(vha, fcport->loop_id, | ||
1064 | &port_param->speed, mb); | ||
1065 | |||
1066 | if (rval) { | ||
1067 | DEBUG16(printk(KERN_ERR "scsi(%ld): iIDMA cmd failed for " | ||
1068 | "%02x%02x%02x%02x%02x%02x%02x%02x -- " | ||
1069 | "%04x %x %04x %04x.\n", | ||
1070 | vha->host_no, fcport->port_name[0], | ||
1071 | fcport->port_name[1], | ||
1072 | fcport->port_name[2], fcport->port_name[3], | ||
1073 | fcport->port_name[4], fcport->port_name[5], | ||
1074 | fcport->port_name[6], fcport->port_name[7], rval, | ||
1075 | fcport->fp_speed, mb[0], mb[1])); | ||
1076 | rval = 0; | ||
1077 | bsg_job->reply->result = (DID_ERROR << 16); | ||
1078 | |||
1079 | } else { | ||
1080 | if (!port_param->mode) { | ||
1081 | bsg_job->reply_len = sizeof(struct fc_bsg_reply) + | ||
1082 | sizeof(struct qla_port_param); | ||
1083 | |||
1084 | rsp_ptr = ((uint8_t *)bsg_job->reply) + | ||
1085 | sizeof(struct fc_bsg_reply); | ||
1086 | |||
1087 | memcpy(rsp_ptr, port_param, | ||
1088 | sizeof(struct qla_port_param)); | ||
1089 | } | ||
1090 | |||
1091 | bsg_job->reply->result = DID_OK; | ||
1092 | } | ||
1093 | |||
1094 | bsg_job->job_done(bsg_job); | ||
1095 | return rval; | ||
1096 | } | ||
1097 | |||
1098 | static int | ||
1099 | qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) | ||
1100 | { | ||
1101 | switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { | ||
1102 | case QL_VND_LOOPBACK: | ||
1103 | return qla2x00_process_loopback(bsg_job); | ||
1104 | |||
1105 | case QL_VND_A84_RESET: | ||
1106 | return qla84xx_reset(bsg_job); | ||
1107 | |||
1108 | case QL_VND_A84_UPDATE_FW: | ||
1109 | return qla84xx_updatefw(bsg_job); | ||
1110 | |||
1111 | case QL_VND_A84_MGMT_CMD: | ||
1112 | return qla84xx_mgmt_cmd(bsg_job); | ||
1113 | |||
1114 | case QL_VND_IIDMA: | ||
1115 | return qla24xx_iidma(bsg_job); | ||
1116 | |||
1117 | case QL_VND_FCP_PRIO_CFG_CMD: | ||
1118 | return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job); | ||
1119 | |||
1120 | default: | ||
1121 | bsg_job->reply->result = (DID_ERROR << 16); | ||
1122 | bsg_job->job_done(bsg_job); | ||
1123 | return -ENOSYS; | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 | int | ||
1128 | qla24xx_bsg_request(struct fc_bsg_job *bsg_job) | ||
1129 | { | ||
1130 | int ret = -EINVAL; | ||
1131 | |||
1132 | switch (bsg_job->request->msgcode) { | ||
1133 | case FC_BSG_RPT_ELS: | ||
1134 | case FC_BSG_HST_ELS_NOLOGIN: | ||
1135 | ret = qla2x00_process_els(bsg_job); | ||
1136 | break; | ||
1137 | case FC_BSG_HST_CT: | ||
1138 | ret = qla2x00_process_ct(bsg_job); | ||
1139 | break; | ||
1140 | case FC_BSG_HST_VENDOR: | ||
1141 | ret = qla2x00_process_vendor_specific(bsg_job); | ||
1142 | break; | ||
1143 | case FC_BSG_HST_ADD_RPORT: | ||
1144 | case FC_BSG_HST_DEL_RPORT: | ||
1145 | case FC_BSG_RPT_CT: | ||
1146 | default: | ||
1147 | DEBUG2(printk("qla2xxx: unsupported BSG request\n")); | ||
1148 | break; | ||
1149 | } | ||
1150 | return ret; | ||
1151 | } | ||
1152 | |||
1153 | int | ||
1154 | qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) | ||
1155 | { | ||
1156 | scsi_qla_host_t *vha = shost_priv(bsg_job->shost); | ||
1157 | struct qla_hw_data *ha = vha->hw; | ||
1158 | srb_t *sp; | ||
1159 | int cnt, que; | ||
1160 | unsigned long flags; | ||
1161 | struct req_que *req; | ||
1162 | struct srb_ctx *sp_bsg; | ||
1163 | |||
1164 | /* find the bsg job from the active list of commands */ | ||
1165 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
1166 | for (que = 0; que < ha->max_req_queues; que++) { | ||
1167 | req = ha->req_q_map[que]; | ||
1168 | if (!req) | ||
1169 | continue; | ||
1170 | |||
1171 | for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { | ||
1172 | sp = req->outstanding_cmds[cnt]; | ||
1173 | if (sp) { | ||
1174 | sp_bsg = sp->ctx; | ||
1175 | |||
1176 | if (((sp_bsg->type == SRB_CT_CMD) || | ||
1177 | (sp_bsg->type == SRB_ELS_CMD_HST)) | ||
1178 | && (sp_bsg->u.bsg_job == bsg_job)) { | ||
1179 | if (ha->isp_ops->abort_command(sp)) { | ||
1180 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
1181 | "scsi(%ld): mbx " | ||
1182 | "abort_command failed\n", | ||
1183 | vha->host_no)); | ||
1184 | bsg_job->req->errors = | ||
1185 | bsg_job->reply->result = -EIO; | ||
1186 | } else { | ||
1187 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
1188 | "scsi(%ld): mbx " | ||
1189 | "abort_command success\n", | ||
1190 | vha->host_no)); | ||
1191 | bsg_job->req->errors = | ||
1192 | bsg_job->reply->result = 0; | ||
1193 | } | ||
1194 | goto done; | ||
1195 | } | ||
1196 | } | ||
1197 | } | ||
1198 | } | ||
1199 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
1200 | DEBUG2(qla_printk(KERN_INFO, ha, | ||
1201 | "scsi(%ld) SRB not found to abort\n", vha->host_no)); | ||
1202 | bsg_job->req->errors = bsg_job->reply->result = -ENXIO; | ||
1203 | return 0; | ||
1204 | |||
1205 | done: | ||
1206 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
1207 | if (bsg_job->request->msgcode == FC_BSG_HST_CT) | ||
1208 | kfree(sp->fcport); | ||
1209 | kfree(sp->ctx); | ||
1210 | mempool_free(sp, ha->srb_mempool); | ||
1211 | return 0; | ||
1212 | } | ||