diff options
Diffstat (limited to 'drivers/scsi/bfa/bfad_im.c')
-rw-r--r-- | drivers/scsi/bfa/bfad_im.c | 1230 |
1 files changed, 1230 insertions, 0 deletions
diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c new file mode 100644 index 000000000000..158c99243c08 --- /dev/null +++ b/drivers/scsi/bfa/bfad_im.c | |||
@@ -0,0 +1,1230 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005-2009 Brocade Communications Systems, Inc. | ||
3 | * All rights reserved | ||
4 | * www.brocade.com | ||
5 | * | ||
6 | * Linux driver for Brocade Fibre Channel Host Bus Adapter. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License (GPL) Version 2 as | ||
10 | * published by the Free Software Foundation | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | /** | ||
19 | * bfad_im.c Linux driver IM module. | ||
20 | */ | ||
21 | |||
22 | #include "bfad_drv.h" | ||
23 | #include "bfad_im.h" | ||
24 | #include "bfad_trcmod.h" | ||
25 | #include "bfa_cb_ioim_macros.h" | ||
26 | #include <fcb/bfa_fcb_fcpim.h> | ||
27 | |||
28 | BFA_TRC_FILE(LDRV, IM); | ||
29 | |||
30 | DEFINE_IDR(bfad_im_port_index); | ||
31 | struct scsi_transport_template *bfad_im_scsi_transport_template; | ||
32 | static void bfad_im_itnim_work_handler(struct work_struct *work); | ||
33 | static int bfad_im_queuecommand(struct scsi_cmnd *cmnd, | ||
34 | void (*done)(struct scsi_cmnd *)); | ||
35 | static int bfad_im_slave_alloc(struct scsi_device *sdev); | ||
36 | |||
37 | void | ||
38 | bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio, | ||
39 | enum bfi_ioim_status io_status, u8 scsi_status, | ||
40 | int sns_len, u8 *sns_info, s32 residue) | ||
41 | { | ||
42 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; | ||
43 | struct bfad_s *bfad = drv; | ||
44 | struct bfad_itnim_data_s *itnim_data; | ||
45 | struct bfad_itnim_s *itnim; | ||
46 | |||
47 | switch (io_status) { | ||
48 | case BFI_IOIM_STS_OK: | ||
49 | bfa_trc(bfad, scsi_status); | ||
50 | cmnd->result = ScsiResult(DID_OK, scsi_status); | ||
51 | scsi_set_resid(cmnd, 0); | ||
52 | |||
53 | if (sns_len > 0) { | ||
54 | bfa_trc(bfad, sns_len); | ||
55 | if (sns_len > SCSI_SENSE_BUFFERSIZE) | ||
56 | sns_len = SCSI_SENSE_BUFFERSIZE; | ||
57 | memcpy(cmnd->sense_buffer, sns_info, sns_len); | ||
58 | } | ||
59 | if (residue > 0) | ||
60 | scsi_set_resid(cmnd, residue); | ||
61 | break; | ||
62 | |||
63 | case BFI_IOIM_STS_ABORTED: | ||
64 | case BFI_IOIM_STS_TIMEDOUT: | ||
65 | case BFI_IOIM_STS_PATHTOV: | ||
66 | default: | ||
67 | cmnd->result = ScsiResult(DID_ERROR, 0); | ||
68 | } | ||
69 | |||
70 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ | ||
71 | if (cmnd->device->host != NULL) | ||
72 | scsi_dma_unmap(cmnd); | ||
73 | |||
74 | cmnd->host_scribble = NULL; | ||
75 | bfa_trc(bfad, cmnd->result); | ||
76 | |||
77 | itnim_data = cmnd->device->hostdata; | ||
78 | if (itnim_data) { | ||
79 | itnim = itnim_data->itnim; | ||
80 | if (!cmnd->result && itnim && | ||
81 | (bfa_lun_queue_depth > cmnd->device->queue_depth)) { | ||
82 | /* Queue depth adjustment for good status completion */ | ||
83 | bfad_os_ramp_up_qdepth(itnim, cmnd->device); | ||
84 | } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) { | ||
85 | /* qfull handling */ | ||
86 | bfad_os_handle_qfull(itnim, cmnd->device); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | cmnd->scsi_done(cmnd); | ||
91 | } | ||
92 | |||
93 | void | ||
94 | bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio) | ||
95 | { | ||
96 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; | ||
97 | struct bfad_itnim_data_s *itnim_data; | ||
98 | struct bfad_itnim_s *itnim; | ||
99 | |||
100 | cmnd->result = ScsiResult(DID_OK, SCSI_STATUS_GOOD); | ||
101 | |||
102 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ | ||
103 | if (cmnd->device->host != NULL) | ||
104 | scsi_dma_unmap(cmnd); | ||
105 | |||
106 | cmnd->host_scribble = NULL; | ||
107 | |||
108 | /* Queue depth adjustment */ | ||
109 | if (bfa_lun_queue_depth > cmnd->device->queue_depth) { | ||
110 | itnim_data = cmnd->device->hostdata; | ||
111 | if (itnim_data) { | ||
112 | itnim = itnim_data->itnim; | ||
113 | if (itnim) | ||
114 | bfad_os_ramp_up_qdepth(itnim, cmnd->device); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | cmnd->scsi_done(cmnd); | ||
119 | } | ||
120 | |||
121 | void | ||
122 | bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio) | ||
123 | { | ||
124 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; | ||
125 | struct bfad_s *bfad = drv; | ||
126 | |||
127 | cmnd->result = ScsiResult(DID_ERROR, 0); | ||
128 | |||
129 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ | ||
130 | if (cmnd->device->host != NULL) | ||
131 | scsi_dma_unmap(cmnd); | ||
132 | |||
133 | bfa_trc(bfad, cmnd->result); | ||
134 | cmnd->host_scribble = NULL; | ||
135 | } | ||
136 | |||
137 | void | ||
138 | bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, | ||
139 | enum bfi_tskim_status tsk_status) | ||
140 | { | ||
141 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk; | ||
142 | wait_queue_head_t *wq; | ||
143 | |||
144 | cmnd->SCp.Status |= tsk_status << 1; | ||
145 | set_bit(IO_DONE_BIT, (unsigned long *)&cmnd->SCp.Status); | ||
146 | wq = (wait_queue_head_t *) cmnd->SCp.ptr; | ||
147 | cmnd->SCp.ptr = NULL; | ||
148 | |||
149 | if (wq) | ||
150 | wake_up(wq); | ||
151 | } | ||
152 | |||
153 | void | ||
154 | bfa_cb_ioim_resfree(void *drv) | ||
155 | { | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Scsi_Host_template SCSI host template | ||
160 | */ | ||
161 | /** | ||
162 | * Scsi_Host template entry, returns BFAD PCI info. | ||
163 | */ | ||
164 | static const char * | ||
165 | bfad_im_info(struct Scsi_Host *shost) | ||
166 | { | ||
167 | static char bfa_buf[256]; | ||
168 | struct bfad_im_port_s *im_port = | ||
169 | (struct bfad_im_port_s *) shost->hostdata[0]; | ||
170 | struct bfa_ioc_attr_s ioc_attr; | ||
171 | struct bfad_s *bfad = im_port->bfad; | ||
172 | |||
173 | memset(&ioc_attr, 0, sizeof(ioc_attr)); | ||
174 | bfa_get_attr(&bfad->bfa, &ioc_attr); | ||
175 | |||
176 | memset(bfa_buf, 0, sizeof(bfa_buf)); | ||
177 | snprintf(bfa_buf, sizeof(bfa_buf), | ||
178 | "Brocade FC/FCOE Adapter, " "model: %s hwpath: %s driver: %s", | ||
179 | ioc_attr.adapter_attr.model, bfad->pci_name, | ||
180 | BFAD_DRIVER_VERSION); | ||
181 | return bfa_buf; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * Scsi_Host template entry, aborts the specified SCSI command. | ||
186 | * | ||
187 | * Returns: SUCCESS or FAILED. | ||
188 | */ | ||
189 | static int | ||
190 | bfad_im_abort_handler(struct scsi_cmnd *cmnd) | ||
191 | { | ||
192 | struct Scsi_Host *shost = cmnd->device->host; | ||
193 | struct bfad_im_port_s *im_port = | ||
194 | (struct bfad_im_port_s *) shost->hostdata[0]; | ||
195 | struct bfad_s *bfad = im_port->bfad; | ||
196 | struct bfa_ioim_s *hal_io; | ||
197 | unsigned long flags; | ||
198 | u32 timeout; | ||
199 | int rc = FAILED; | ||
200 | |||
201 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
202 | hal_io = (struct bfa_ioim_s *) cmnd->host_scribble; | ||
203 | if (!hal_io) { | ||
204 | /* IO has been completed, retrun success */ | ||
205 | rc = SUCCESS; | ||
206 | goto out; | ||
207 | } | ||
208 | if (hal_io->dio != (struct bfad_ioim_s *) cmnd) { | ||
209 | rc = FAILED; | ||
210 | goto out; | ||
211 | } | ||
212 | |||
213 | bfa_trc(bfad, hal_io->iotag); | ||
214 | bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT, | ||
215 | im_port->shost->host_no, cmnd, hal_io->iotag); | ||
216 | bfa_ioim_abort(hal_io); | ||
217 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
218 | |||
219 | /* Need to wait until the command get aborted */ | ||
220 | timeout = 10; | ||
221 | while ((struct bfa_ioim_s *) cmnd->host_scribble == hal_io) { | ||
222 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
223 | schedule_timeout(timeout); | ||
224 | if (timeout < 4 * HZ) | ||
225 | timeout *= 2; | ||
226 | } | ||
227 | |||
228 | cmnd->scsi_done(cmnd); | ||
229 | bfa_trc(bfad, hal_io->iotag); | ||
230 | bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_ABORT_COMP, | ||
231 | im_port->shost->host_no, cmnd, hal_io->iotag); | ||
232 | return SUCCESS; | ||
233 | out: | ||
234 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
235 | return rc; | ||
236 | } | ||
237 | |||
238 | static bfa_status_t | ||
239 | bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd, | ||
240 | struct bfad_itnim_s *itnim) | ||
241 | { | ||
242 | struct bfa_tskim_s *tskim; | ||
243 | struct bfa_itnim_s *bfa_itnim; | ||
244 | bfa_status_t rc = BFA_STATUS_OK; | ||
245 | |||
246 | bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); | ||
247 | tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); | ||
248 | if (!tskim) { | ||
249 | BFA_DEV_PRINTF(bfad, BFA_ERR, | ||
250 | "target reset, fail to allocate tskim\n"); | ||
251 | rc = BFA_STATUS_FAILED; | ||
252 | goto out; | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * Set host_scribble to NULL to avoid aborting a task command if | ||
257 | * happens. | ||
258 | */ | ||
259 | cmnd->host_scribble = NULL; | ||
260 | cmnd->SCp.Status = 0; | ||
261 | bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); | ||
262 | bfa_tskim_start(tskim, bfa_itnim, (lun_t)0, | ||
263 | FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO); | ||
264 | out: | ||
265 | return rc; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * Scsi_Host template entry, resets a LUN and abort its all commands. | ||
270 | * | ||
271 | * Returns: SUCCESS or FAILED. | ||
272 | * | ||
273 | */ | ||
274 | static int | ||
275 | bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd) | ||
276 | { | ||
277 | struct Scsi_Host *shost = cmnd->device->host; | ||
278 | struct bfad_im_port_s *im_port = | ||
279 | (struct bfad_im_port_s *) shost->hostdata[0]; | ||
280 | struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; | ||
281 | struct bfad_s *bfad = im_port->bfad; | ||
282 | struct bfa_tskim_s *tskim; | ||
283 | struct bfad_itnim_s *itnim; | ||
284 | struct bfa_itnim_s *bfa_itnim; | ||
285 | DECLARE_WAIT_QUEUE_HEAD(wq); | ||
286 | int rc = SUCCESS; | ||
287 | unsigned long flags; | ||
288 | enum bfi_tskim_status task_status; | ||
289 | |||
290 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
291 | itnim = itnim_data->itnim; | ||
292 | if (!itnim) { | ||
293 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
294 | rc = FAILED; | ||
295 | goto out; | ||
296 | } | ||
297 | |||
298 | tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); | ||
299 | if (!tskim) { | ||
300 | BFA_DEV_PRINTF(bfad, BFA_ERR, | ||
301 | "LUN reset, fail to allocate tskim"); | ||
302 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
303 | rc = FAILED; | ||
304 | goto out; | ||
305 | } | ||
306 | |||
307 | /** | ||
308 | * Set host_scribble to NULL to avoid aborting a task command | ||
309 | * if happens. | ||
310 | */ | ||
311 | cmnd->host_scribble = NULL; | ||
312 | cmnd->SCp.ptr = (char *)&wq; | ||
313 | cmnd->SCp.Status = 0; | ||
314 | bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); | ||
315 | bfa_tskim_start(tskim, bfa_itnim, | ||
316 | bfad_int_to_lun(cmnd->device->lun), | ||
317 | FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO); | ||
318 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
319 | |||
320 | wait_event(wq, test_bit(IO_DONE_BIT, | ||
321 | (unsigned long *)&cmnd->SCp.Status)); | ||
322 | |||
323 | task_status = cmnd->SCp.Status >> 1; | ||
324 | if (task_status != BFI_TSKIM_STS_OK) { | ||
325 | BFA_DEV_PRINTF(bfad, BFA_ERR, "LUN reset failure, status: %d\n", | ||
326 | task_status); | ||
327 | rc = FAILED; | ||
328 | } | ||
329 | |||
330 | out: | ||
331 | return rc; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * Scsi_Host template entry, resets the bus and abort all commands. | ||
336 | */ | ||
337 | static int | ||
338 | bfad_im_reset_bus_handler(struct scsi_cmnd *cmnd) | ||
339 | { | ||
340 | struct Scsi_Host *shost = cmnd->device->host; | ||
341 | struct bfad_im_port_s *im_port = | ||
342 | (struct bfad_im_port_s *) shost->hostdata[0]; | ||
343 | struct bfad_s *bfad = im_port->bfad; | ||
344 | struct bfad_itnim_s *itnim; | ||
345 | unsigned long flags; | ||
346 | u32 i, rc, err_cnt = 0; | ||
347 | DECLARE_WAIT_QUEUE_HEAD(wq); | ||
348 | enum bfi_tskim_status task_status; | ||
349 | |||
350 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
351 | for (i = 0; i < MAX_FCP_TARGET; i++) { | ||
352 | itnim = bfad_os_get_itnim(im_port, i); | ||
353 | if (itnim) { | ||
354 | cmnd->SCp.ptr = (char *)&wq; | ||
355 | rc = bfad_im_target_reset_send(bfad, cmnd, itnim); | ||
356 | if (rc != BFA_STATUS_OK) { | ||
357 | err_cnt++; | ||
358 | continue; | ||
359 | } | ||
360 | |||
361 | /* wait target reset to complete */ | ||
362 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
363 | wait_event(wq, test_bit(IO_DONE_BIT, | ||
364 | (unsigned long *)&cmnd->SCp.Status)); | ||
365 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
366 | |||
367 | task_status = cmnd->SCp.Status >> 1; | ||
368 | if (task_status != BFI_TSKIM_STS_OK) { | ||
369 | BFA_DEV_PRINTF(bfad, BFA_ERR, | ||
370 | "target reset failure," | ||
371 | " status: %d\n", task_status); | ||
372 | err_cnt++; | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
377 | |||
378 | if (err_cnt) | ||
379 | return FAILED; | ||
380 | |||
381 | return SUCCESS; | ||
382 | } | ||
383 | |||
384 | /** | ||
385 | * Scsi_Host template entry slave_destroy. | ||
386 | */ | ||
387 | static void | ||
388 | bfad_im_slave_destroy(struct scsi_device *sdev) | ||
389 | { | ||
390 | sdev->hostdata = NULL; | ||
391 | return; | ||
392 | } | ||
393 | |||
394 | /** | ||
395 | * BFA FCS itnim callbacks | ||
396 | */ | ||
397 | |||
398 | /** | ||
399 | * BFA FCS itnim alloc callback, after successful PRLI | ||
400 | * Context: Interrupt | ||
401 | */ | ||
402 | void | ||
403 | bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, | ||
404 | struct bfad_itnim_s **itnim_drv) | ||
405 | { | ||
406 | *itnim_drv = kzalloc(sizeof(struct bfad_itnim_s), GFP_ATOMIC); | ||
407 | if (*itnim_drv == NULL) | ||
408 | return; | ||
409 | |||
410 | (*itnim_drv)->im = bfad->im; | ||
411 | *itnim = &(*itnim_drv)->fcs_itnim; | ||
412 | (*itnim_drv)->state = ITNIM_STATE_NONE; | ||
413 | |||
414 | /* | ||
415 | * Initiaze the itnim_work | ||
416 | */ | ||
417 | INIT_WORK(&(*itnim_drv)->itnim_work, bfad_im_itnim_work_handler); | ||
418 | bfad->bfad_flags |= BFAD_RPORT_ONLINE; | ||
419 | } | ||
420 | |||
421 | /** | ||
422 | * BFA FCS itnim free callback. | ||
423 | * Context: Interrupt. bfad_lock is held | ||
424 | */ | ||
425 | void | ||
426 | bfa_fcb_itnim_free(struct bfad_s *bfad, struct bfad_itnim_s *itnim_drv) | ||
427 | { | ||
428 | struct bfad_port_s *port; | ||
429 | wwn_t wwpn; | ||
430 | u32 fcid; | ||
431 | char wwpn_str[32], fcid_str[16]; | ||
432 | |||
433 | /* online to free state transtion should not happen */ | ||
434 | bfa_assert(itnim_drv->state != ITNIM_STATE_ONLINE); | ||
435 | |||
436 | itnim_drv->queue_work = 1; | ||
437 | /* offline request is not yet done, use the same request to free */ | ||
438 | if (itnim_drv->state == ITNIM_STATE_OFFLINE_PENDING) | ||
439 | itnim_drv->queue_work = 0; | ||
440 | |||
441 | itnim_drv->state = ITNIM_STATE_FREE; | ||
442 | port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); | ||
443 | itnim_drv->im_port = port->im_port; | ||
444 | wwpn = bfa_fcs_itnim_get_pwwn(&itnim_drv->fcs_itnim); | ||
445 | fcid = bfa_fcs_itnim_get_fcid(&itnim_drv->fcs_itnim); | ||
446 | wwn2str(wwpn_str, wwpn); | ||
447 | fcid2str(fcid_str, fcid); | ||
448 | bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_FREE, | ||
449 | port->im_port->shost->host_no, | ||
450 | fcid_str, wwpn_str); | ||
451 | bfad_os_itnim_process(itnim_drv); | ||
452 | } | ||
453 | |||
454 | /** | ||
455 | * BFA FCS itnim online callback. | ||
456 | * Context: Interrupt. bfad_lock is held | ||
457 | */ | ||
458 | void | ||
459 | bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv) | ||
460 | { | ||
461 | struct bfad_port_s *port; | ||
462 | |||
463 | itnim_drv->bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim_drv->fcs_itnim); | ||
464 | port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); | ||
465 | itnim_drv->state = ITNIM_STATE_ONLINE; | ||
466 | itnim_drv->queue_work = 1; | ||
467 | itnim_drv->im_port = port->im_port; | ||
468 | bfad_os_itnim_process(itnim_drv); | ||
469 | } | ||
470 | |||
471 | /** | ||
472 | * BFA FCS itnim offline callback. | ||
473 | * Context: Interrupt. bfad_lock is held | ||
474 | */ | ||
475 | void | ||
476 | bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv) | ||
477 | { | ||
478 | struct bfad_port_s *port; | ||
479 | struct bfad_s *bfad; | ||
480 | |||
481 | port = bfa_fcs_itnim_get_drvport(&itnim_drv->fcs_itnim); | ||
482 | bfad = port->bfad; | ||
483 | if ((bfad->pport.flags & BFAD_PORT_DELETE) || | ||
484 | (port->flags & BFAD_PORT_DELETE)) { | ||
485 | itnim_drv->state = ITNIM_STATE_OFFLINE; | ||
486 | return; | ||
487 | } | ||
488 | itnim_drv->im_port = port->im_port; | ||
489 | itnim_drv->state = ITNIM_STATE_OFFLINE_PENDING; | ||
490 | itnim_drv->queue_work = 1; | ||
491 | bfad_os_itnim_process(itnim_drv); | ||
492 | } | ||
493 | |||
494 | /** | ||
495 | * BFA FCS itnim timeout callback. | ||
496 | * Context: Interrupt. bfad_lock is held | ||
497 | */ | ||
498 | void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim) | ||
499 | { | ||
500 | itnim->state = ITNIM_STATE_TIMEOUT; | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * Path TOV processing begin notification -- dummy for linux | ||
505 | */ | ||
506 | void | ||
507 | bfa_fcb_itnim_tov_begin(struct bfad_itnim_s *itnim) | ||
508 | { | ||
509 | } | ||
510 | |||
511 | |||
512 | |||
513 | /** | ||
514 | * Allocate a Scsi_Host for a port. | ||
515 | */ | ||
516 | int | ||
517 | bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) | ||
518 | { | ||
519 | int error = 1; | ||
520 | |||
521 | if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) { | ||
522 | printk(KERN_WARNING "idr_pre_get failure\n"); | ||
523 | goto out; | ||
524 | } | ||
525 | |||
526 | error = idr_get_new(&bfad_im_port_index, im_port, | ||
527 | &im_port->idr_id); | ||
528 | if (error) { | ||
529 | printk(KERN_WARNING "idr_get_new failure\n"); | ||
530 | goto out; | ||
531 | } | ||
532 | |||
533 | im_port->shost = bfad_os_scsi_host_alloc(im_port, bfad); | ||
534 | if (!im_port->shost) { | ||
535 | error = 1; | ||
536 | goto out_free_idr; | ||
537 | } | ||
538 | |||
539 | im_port->shost->hostdata[0] = (unsigned long)im_port; | ||
540 | im_port->shost->unique_id = im_port->idr_id; | ||
541 | im_port->shost->this_id = -1; | ||
542 | im_port->shost->max_id = MAX_FCP_TARGET; | ||
543 | im_port->shost->max_lun = MAX_FCP_LUN; | ||
544 | im_port->shost->max_cmd_len = 16; | ||
545 | im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth; | ||
546 | im_port->shost->transportt = bfad_im_scsi_transport_template; | ||
547 | |||
548 | error = bfad_os_scsi_add_host(im_port->shost, im_port, bfad); | ||
549 | if (error) { | ||
550 | printk(KERN_WARNING "bfad_os_scsi_add_host failure %d\n", | ||
551 | error); | ||
552 | goto out_fc_rel; | ||
553 | } | ||
554 | |||
555 | /* setup host fixed attribute if the lk supports */ | ||
556 | bfad_os_fc_host_init(im_port); | ||
557 | |||
558 | return 0; | ||
559 | |||
560 | out_fc_rel: | ||
561 | scsi_host_put(im_port->shost); | ||
562 | out_free_idr: | ||
563 | idr_remove(&bfad_im_port_index, im_port->idr_id); | ||
564 | out: | ||
565 | return error; | ||
566 | } | ||
567 | |||
568 | void | ||
569 | bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) | ||
570 | { | ||
571 | unsigned long flags; | ||
572 | |||
573 | bfa_trc(bfad, bfad->inst_no); | ||
574 | bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_HOST_FREE, | ||
575 | im_port->shost->host_no); | ||
576 | |||
577 | fc_remove_host(im_port->shost); | ||
578 | |||
579 | scsi_remove_host(im_port->shost); | ||
580 | scsi_host_put(im_port->shost); | ||
581 | |||
582 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
583 | idr_remove(&bfad_im_port_index, im_port->idr_id); | ||
584 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
585 | } | ||
586 | |||
587 | static void | ||
588 | bfad_im_port_delete_handler(struct work_struct *work) | ||
589 | { | ||
590 | struct bfad_im_port_s *im_port = | ||
591 | container_of(work, struct bfad_im_port_s, port_delete_work); | ||
592 | |||
593 | bfad_im_scsi_host_free(im_port->bfad, im_port); | ||
594 | bfad_im_port_clean(im_port); | ||
595 | kfree(im_port); | ||
596 | } | ||
597 | |||
598 | bfa_status_t | ||
599 | bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port) | ||
600 | { | ||
601 | int rc = BFA_STATUS_OK; | ||
602 | struct bfad_im_port_s *im_port; | ||
603 | |||
604 | im_port = kzalloc(sizeof(struct bfad_im_port_s), GFP_ATOMIC); | ||
605 | if (im_port == NULL) { | ||
606 | rc = BFA_STATUS_ENOMEM; | ||
607 | goto ext; | ||
608 | } | ||
609 | port->im_port = im_port; | ||
610 | im_port->port = port; | ||
611 | im_port->bfad = bfad; | ||
612 | |||
613 | INIT_WORK(&im_port->port_delete_work, bfad_im_port_delete_handler); | ||
614 | INIT_LIST_HEAD(&im_port->itnim_mapped_list); | ||
615 | INIT_LIST_HEAD(&im_port->binding_list); | ||
616 | |||
617 | ext: | ||
618 | return rc; | ||
619 | } | ||
620 | |||
621 | void | ||
622 | bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port) | ||
623 | { | ||
624 | struct bfad_im_port_s *im_port = port->im_port; | ||
625 | |||
626 | queue_work(bfad->im->drv_workq, | ||
627 | &im_port->port_delete_work); | ||
628 | } | ||
629 | |||
630 | void | ||
631 | bfad_im_port_clean(struct bfad_im_port_s *im_port) | ||
632 | { | ||
633 | struct bfad_fcp_binding *bp, *bp_new; | ||
634 | unsigned long flags; | ||
635 | struct bfad_s *bfad = im_port->bfad; | ||
636 | |||
637 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
638 | list_for_each_entry_safe(bp, bp_new, &im_port->binding_list, | ||
639 | list_entry) { | ||
640 | list_del(&bp->list_entry); | ||
641 | kfree(bp); | ||
642 | } | ||
643 | |||
644 | /* the itnim_mapped_list must be empty at this time */ | ||
645 | bfa_assert(list_empty(&im_port->itnim_mapped_list)); | ||
646 | |||
647 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
648 | } | ||
649 | |||
650 | void | ||
651 | bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port) | ||
652 | { | ||
653 | } | ||
654 | |||
655 | void | ||
656 | bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port) | ||
657 | { | ||
658 | } | ||
659 | |||
660 | bfa_status_t | ||
661 | bfad_im_probe(struct bfad_s *bfad) | ||
662 | { | ||
663 | struct bfad_im_s *im; | ||
664 | bfa_status_t rc = BFA_STATUS_OK; | ||
665 | |||
666 | im = kzalloc(sizeof(struct bfad_im_s), GFP_KERNEL); | ||
667 | if (im == NULL) { | ||
668 | rc = BFA_STATUS_ENOMEM; | ||
669 | goto ext; | ||
670 | } | ||
671 | |||
672 | bfad->im = im; | ||
673 | im->bfad = bfad; | ||
674 | |||
675 | if (bfad_os_thread_workq(bfad) != BFA_STATUS_OK) { | ||
676 | kfree(im); | ||
677 | rc = BFA_STATUS_FAILED; | ||
678 | } | ||
679 | |||
680 | ext: | ||
681 | return rc; | ||
682 | } | ||
683 | |||
684 | void | ||
685 | bfad_im_probe_undo(struct bfad_s *bfad) | ||
686 | { | ||
687 | if (bfad->im) { | ||
688 | bfad_os_destroy_workq(bfad->im); | ||
689 | kfree(bfad->im); | ||
690 | bfad->im = NULL; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | |||
695 | |||
696 | |||
697 | int | ||
698 | bfad_os_scsi_add_host(struct Scsi_Host *shost, struct bfad_im_port_s *im_port, | ||
699 | struct bfad_s *bfad) | ||
700 | { | ||
701 | struct device *dev; | ||
702 | |||
703 | if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) | ||
704 | dev = &bfad->pcidev->dev; | ||
705 | else | ||
706 | dev = &bfad->pport.im_port->shost->shost_gendev; | ||
707 | |||
708 | return scsi_add_host(shost, dev); | ||
709 | } | ||
710 | |||
711 | struct Scsi_Host * | ||
712 | bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) | ||
713 | { | ||
714 | struct scsi_host_template *sht; | ||
715 | |||
716 | if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) | ||
717 | sht = &bfad_im_scsi_host_template; | ||
718 | else | ||
719 | sht = &bfad_im_vport_template; | ||
720 | |||
721 | sht->sg_tablesize = bfad->cfg_data.io_max_sge; | ||
722 | |||
723 | return scsi_host_alloc(sht, sizeof(unsigned long)); | ||
724 | } | ||
725 | |||
726 | void | ||
727 | bfad_os_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) | ||
728 | { | ||
729 | flush_workqueue(bfad->im->drv_workq); | ||
730 | bfad_im_scsi_host_free(im_port->bfad, im_port); | ||
731 | bfad_im_port_clean(im_port); | ||
732 | kfree(im_port); | ||
733 | } | ||
734 | |||
735 | void | ||
736 | bfad_os_destroy_workq(struct bfad_im_s *im) | ||
737 | { | ||
738 | if (im && im->drv_workq) { | ||
739 | destroy_workqueue(im->drv_workq); | ||
740 | im->drv_workq = NULL; | ||
741 | } | ||
742 | } | ||
743 | |||
744 | bfa_status_t | ||
745 | bfad_os_thread_workq(struct bfad_s *bfad) | ||
746 | { | ||
747 | struct bfad_im_s *im = bfad->im; | ||
748 | |||
749 | bfa_trc(bfad, 0); | ||
750 | snprintf(im->drv_workq_name, BFAD_KOBJ_NAME_LEN, "bfad_wq_%d", | ||
751 | bfad->inst_no); | ||
752 | im->drv_workq = create_singlethread_workqueue(im->drv_workq_name); | ||
753 | if (!im->drv_workq) | ||
754 | return BFA_STATUS_FAILED; | ||
755 | |||
756 | return BFA_STATUS_OK; | ||
757 | } | ||
758 | |||
759 | /** | ||
760 | * Scsi_Host template entry. | ||
761 | * | ||
762 | * Description: | ||
763 | * OS entry point to adjust the queue_depths on a per-device basis. | ||
764 | * Called once per device during the bus scan. | ||
765 | * Return non-zero if fails. | ||
766 | */ | ||
767 | static int | ||
768 | bfad_im_slave_configure(struct scsi_device *sdev) | ||
769 | { | ||
770 | if (sdev->tagged_supported) | ||
771 | scsi_activate_tcq(sdev, bfa_lun_queue_depth); | ||
772 | else | ||
773 | scsi_deactivate_tcq(sdev, bfa_lun_queue_depth); | ||
774 | |||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | struct scsi_host_template bfad_im_scsi_host_template = { | ||
779 | .module = THIS_MODULE, | ||
780 | .name = BFAD_DRIVER_NAME, | ||
781 | .info = bfad_im_info, | ||
782 | .queuecommand = bfad_im_queuecommand, | ||
783 | .eh_abort_handler = bfad_im_abort_handler, | ||
784 | .eh_device_reset_handler = bfad_im_reset_lun_handler, | ||
785 | .eh_bus_reset_handler = bfad_im_reset_bus_handler, | ||
786 | |||
787 | .slave_alloc = bfad_im_slave_alloc, | ||
788 | .slave_configure = bfad_im_slave_configure, | ||
789 | .slave_destroy = bfad_im_slave_destroy, | ||
790 | |||
791 | .this_id = -1, | ||
792 | .sg_tablesize = BFAD_IO_MAX_SGE, | ||
793 | .cmd_per_lun = 3, | ||
794 | .use_clustering = ENABLE_CLUSTERING, | ||
795 | .shost_attrs = bfad_im_host_attrs, | ||
796 | .max_sectors = 0xFFFF, | ||
797 | }; | ||
798 | |||
799 | struct scsi_host_template bfad_im_vport_template = { | ||
800 | .module = THIS_MODULE, | ||
801 | .name = BFAD_DRIVER_NAME, | ||
802 | .info = bfad_im_info, | ||
803 | .queuecommand = bfad_im_queuecommand, | ||
804 | .eh_abort_handler = bfad_im_abort_handler, | ||
805 | .eh_device_reset_handler = bfad_im_reset_lun_handler, | ||
806 | .eh_bus_reset_handler = bfad_im_reset_bus_handler, | ||
807 | |||
808 | .slave_alloc = bfad_im_slave_alloc, | ||
809 | .slave_configure = bfad_im_slave_configure, | ||
810 | .slave_destroy = bfad_im_slave_destroy, | ||
811 | |||
812 | .this_id = -1, | ||
813 | .sg_tablesize = BFAD_IO_MAX_SGE, | ||
814 | .cmd_per_lun = 3, | ||
815 | .use_clustering = ENABLE_CLUSTERING, | ||
816 | .shost_attrs = bfad_im_vport_attrs, | ||
817 | .max_sectors = 0xFFFF, | ||
818 | }; | ||
819 | |||
820 | void | ||
821 | bfad_im_probe_post(struct bfad_im_s *im) | ||
822 | { | ||
823 | flush_workqueue(im->drv_workq); | ||
824 | } | ||
825 | |||
826 | bfa_status_t | ||
827 | bfad_im_module_init(void) | ||
828 | { | ||
829 | bfad_im_scsi_transport_template = | ||
830 | fc_attach_transport(&bfad_im_fc_function_template); | ||
831 | if (!bfad_im_scsi_transport_template) | ||
832 | return BFA_STATUS_ENOMEM; | ||
833 | |||
834 | return BFA_STATUS_OK; | ||
835 | } | ||
836 | |||
837 | void | ||
838 | bfad_im_module_exit(void) | ||
839 | { | ||
840 | if (bfad_im_scsi_transport_template) | ||
841 | fc_release_transport(bfad_im_scsi_transport_template); | ||
842 | } | ||
843 | |||
844 | void | ||
845 | bfad_os_itnim_process(struct bfad_itnim_s *itnim_drv) | ||
846 | { | ||
847 | struct bfad_im_s *im = itnim_drv->im; | ||
848 | |||
849 | if (itnim_drv->queue_work) | ||
850 | queue_work(im->drv_workq, &itnim_drv->itnim_work); | ||
851 | } | ||
852 | |||
853 | void | ||
854 | bfad_os_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev) | ||
855 | { | ||
856 | struct scsi_device *tmp_sdev; | ||
857 | |||
858 | if (((jiffies - itnim->last_ramp_up_time) > | ||
859 | BFA_QUEUE_FULL_RAMP_UP_TIME * HZ) && | ||
860 | ((jiffies - itnim->last_queue_full_time) > | ||
861 | BFA_QUEUE_FULL_RAMP_UP_TIME * HZ)) { | ||
862 | shost_for_each_device(tmp_sdev, sdev->host) { | ||
863 | if (bfa_lun_queue_depth > tmp_sdev->queue_depth) { | ||
864 | if (tmp_sdev->id != sdev->id) | ||
865 | continue; | ||
866 | if (tmp_sdev->ordered_tags) | ||
867 | scsi_adjust_queue_depth(tmp_sdev, | ||
868 | MSG_ORDERED_TAG, | ||
869 | tmp_sdev->queue_depth + 1); | ||
870 | else | ||
871 | scsi_adjust_queue_depth(tmp_sdev, | ||
872 | MSG_SIMPLE_TAG, | ||
873 | tmp_sdev->queue_depth + 1); | ||
874 | |||
875 | itnim->last_ramp_up_time = jiffies; | ||
876 | } | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | |||
881 | void | ||
882 | bfad_os_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev) | ||
883 | { | ||
884 | struct scsi_device *tmp_sdev; | ||
885 | |||
886 | itnim->last_queue_full_time = jiffies; | ||
887 | |||
888 | shost_for_each_device(tmp_sdev, sdev->host) { | ||
889 | if (tmp_sdev->id != sdev->id) | ||
890 | continue; | ||
891 | scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1); | ||
892 | } | ||
893 | } | ||
894 | |||
895 | |||
896 | |||
897 | |||
898 | struct bfad_itnim_s * | ||
899 | bfad_os_get_itnim(struct bfad_im_port_s *im_port, int id) | ||
900 | { | ||
901 | struct bfad_itnim_s *itnim = NULL; | ||
902 | |||
903 | /* Search the mapped list for this target ID */ | ||
904 | list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) { | ||
905 | if (id == itnim->scsi_tgt_id) | ||
906 | return itnim; | ||
907 | } | ||
908 | |||
909 | return NULL; | ||
910 | } | ||
911 | |||
912 | /** | ||
913 | * Scsi_Host template entry slave_alloc | ||
914 | */ | ||
915 | static int | ||
916 | bfad_im_slave_alloc(struct scsi_device *sdev) | ||
917 | { | ||
918 | struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); | ||
919 | |||
920 | if (!rport || fc_remote_port_chkready(rport)) | ||
921 | return -ENXIO; | ||
922 | |||
923 | sdev->hostdata = rport->dd_data; | ||
924 | |||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | void | ||
929 | bfad_os_fc_host_init(struct bfad_im_port_s *im_port) | ||
930 | { | ||
931 | struct Scsi_Host *host = im_port->shost; | ||
932 | struct bfad_s *bfad = im_port->bfad; | ||
933 | struct bfad_port_s *port = im_port->port; | ||
934 | union attr { | ||
935 | struct bfa_pport_attr_s pattr; | ||
936 | struct bfa_ioc_attr_s ioc_attr; | ||
937 | } attr; | ||
938 | |||
939 | fc_host_node_name(host) = | ||
940 | bfa_os_htonll((bfa_fcs_port_get_nwwn(port->fcs_port))); | ||
941 | fc_host_port_name(host) = | ||
942 | bfa_os_htonll((bfa_fcs_port_get_pwwn(port->fcs_port))); | ||
943 | |||
944 | fc_host_supported_classes(host) = FC_COS_CLASS3; | ||
945 | |||
946 | memset(fc_host_supported_fc4s(host), 0, | ||
947 | sizeof(fc_host_supported_fc4s(host))); | ||
948 | if (bfad_supported_fc4s & (BFA_PORT_ROLE_FCP_IM | BFA_PORT_ROLE_FCP_TM)) | ||
949 | /* For FCP type 0x08 */ | ||
950 | fc_host_supported_fc4s(host)[2] = 1; | ||
951 | if (bfad_supported_fc4s | BFA_PORT_ROLE_FCP_IPFC) | ||
952 | /* For LLC/SNAP type 0x05 */ | ||
953 | fc_host_supported_fc4s(host)[3] = 0x20; | ||
954 | /* For fibre channel services type 0x20 */ | ||
955 | fc_host_supported_fc4s(host)[7] = 1; | ||
956 | |||
957 | memset(&attr.ioc_attr, 0, sizeof(attr.ioc_attr)); | ||
958 | bfa_get_attr(&bfad->bfa, &attr.ioc_attr); | ||
959 | sprintf(fc_host_symbolic_name(host), "Brocade %s FV%s DV%s", | ||
960 | attr.ioc_attr.adapter_attr.model, | ||
961 | attr.ioc_attr.adapter_attr.fw_ver, BFAD_DRIVER_VERSION); | ||
962 | |||
963 | fc_host_supported_speeds(host) = 0; | ||
964 | fc_host_supported_speeds(host) |= | ||
965 | FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | | ||
966 | FC_PORTSPEED_1GBIT; | ||
967 | |||
968 | memset(&attr.pattr, 0, sizeof(attr.pattr)); | ||
969 | bfa_pport_get_attr(&bfad->bfa, &attr.pattr); | ||
970 | fc_host_maxframe_size(host) = attr.pattr.pport_cfg.maxfrsize; | ||
971 | } | ||
972 | |||
973 | static void | ||
974 | bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim) | ||
975 | { | ||
976 | struct fc_rport_identifiers rport_ids; | ||
977 | struct fc_rport *fc_rport; | ||
978 | struct bfad_itnim_data_s *itnim_data; | ||
979 | |||
980 | rport_ids.node_name = | ||
981 | bfa_os_htonll(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim)); | ||
982 | rport_ids.port_name = | ||
983 | bfa_os_htonll(bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); | ||
984 | rport_ids.port_id = | ||
985 | bfa_os_hton3b(bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim)); | ||
986 | rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; | ||
987 | |||
988 | itnim->fc_rport = fc_rport = | ||
989 | fc_remote_port_add(im_port->shost, 0, &rport_ids); | ||
990 | |||
991 | if (!fc_rport) | ||
992 | return; | ||
993 | |||
994 | fc_rport->maxframe_size = | ||
995 | bfa_fcs_itnim_get_maxfrsize(&itnim->fcs_itnim); | ||
996 | fc_rport->supported_classes = bfa_fcs_itnim_get_cos(&itnim->fcs_itnim); | ||
997 | |||
998 | itnim_data = fc_rport->dd_data; | ||
999 | itnim_data->itnim = itnim; | ||
1000 | |||
1001 | rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; | ||
1002 | |||
1003 | if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) | ||
1004 | fc_remote_port_rolechg(fc_rport, rport_ids.roles); | ||
1005 | |||
1006 | if ((fc_rport->scsi_target_id != -1) | ||
1007 | && (fc_rport->scsi_target_id < MAX_FCP_TARGET)) | ||
1008 | itnim->scsi_tgt_id = fc_rport->scsi_target_id; | ||
1009 | |||
1010 | return; | ||
1011 | } | ||
1012 | |||
1013 | /** | ||
1014 | * Work queue handler using FC transport service | ||
1015 | * Context: kernel | ||
1016 | */ | ||
1017 | static void | ||
1018 | bfad_im_itnim_work_handler(struct work_struct *work) | ||
1019 | { | ||
1020 | struct bfad_itnim_s *itnim = container_of(work, struct bfad_itnim_s, | ||
1021 | itnim_work); | ||
1022 | struct bfad_im_s *im = itnim->im; | ||
1023 | struct bfad_s *bfad = im->bfad; | ||
1024 | struct bfad_im_port_s *im_port; | ||
1025 | unsigned long flags; | ||
1026 | struct fc_rport *fc_rport; | ||
1027 | wwn_t wwpn; | ||
1028 | u32 fcid; | ||
1029 | char wwpn_str[32], fcid_str[16]; | ||
1030 | |||
1031 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
1032 | im_port = itnim->im_port; | ||
1033 | bfa_trc(bfad, itnim->state); | ||
1034 | switch (itnim->state) { | ||
1035 | case ITNIM_STATE_ONLINE: | ||
1036 | if (!itnim->fc_rport) { | ||
1037 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1038 | bfad_im_fc_rport_add(im_port, itnim); | ||
1039 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
1040 | wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); | ||
1041 | fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); | ||
1042 | wwn2str(wwpn_str, wwpn); | ||
1043 | fcid2str(fcid_str, fcid); | ||
1044 | list_add_tail(&itnim->list_entry, | ||
1045 | &im_port->itnim_mapped_list); | ||
1046 | bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_ONLINE, | ||
1047 | im_port->shost->host_no, | ||
1048 | itnim->scsi_tgt_id, | ||
1049 | fcid_str, wwpn_str); | ||
1050 | } else { | ||
1051 | printk(KERN_WARNING | ||
1052 | "%s: itnim %llx is already in online state\n", | ||
1053 | __FUNCTION__, | ||
1054 | bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); | ||
1055 | } | ||
1056 | |||
1057 | break; | ||
1058 | case ITNIM_STATE_OFFLINE_PENDING: | ||
1059 | itnim->state = ITNIM_STATE_OFFLINE; | ||
1060 | if (itnim->fc_rport) { | ||
1061 | fc_rport = itnim->fc_rport; | ||
1062 | ((struct bfad_itnim_data_s *) | ||
1063 | fc_rport->dd_data)->itnim = NULL; | ||
1064 | itnim->fc_rport = NULL; | ||
1065 | if (!(im_port->port->flags & BFAD_PORT_DELETE)) { | ||
1066 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1067 | fc_rport->dev_loss_tmo = | ||
1068 | bfa_fcpim_path_tov_get(&bfad->bfa) + 1; | ||
1069 | fc_remote_port_delete(fc_rport); | ||
1070 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
1071 | } | ||
1072 | wwpn = bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim); | ||
1073 | fcid = bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim); | ||
1074 | wwn2str(wwpn_str, wwpn); | ||
1075 | fcid2str(fcid_str, fcid); | ||
1076 | list_del(&itnim->list_entry); | ||
1077 | bfa_log(bfad->logmod, BFA_LOG_LINUX_ITNIM_OFFLINE, | ||
1078 | im_port->shost->host_no, | ||
1079 | itnim->scsi_tgt_id, | ||
1080 | fcid_str, wwpn_str); | ||
1081 | } | ||
1082 | break; | ||
1083 | case ITNIM_STATE_FREE: | ||
1084 | if (itnim->fc_rport) { | ||
1085 | fc_rport = itnim->fc_rport; | ||
1086 | ((struct bfad_itnim_data_s *) | ||
1087 | fc_rport->dd_data)->itnim = NULL; | ||
1088 | itnim->fc_rport = NULL; | ||
1089 | if (!(im_port->port->flags & BFAD_PORT_DELETE)) { | ||
1090 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1091 | fc_rport->dev_loss_tmo = | ||
1092 | bfa_fcpim_path_tov_get(&bfad->bfa) + 1; | ||
1093 | fc_remote_port_delete(fc_rport); | ||
1094 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
1095 | } | ||
1096 | list_del(&itnim->list_entry); | ||
1097 | } | ||
1098 | |||
1099 | kfree(itnim); | ||
1100 | break; | ||
1101 | default: | ||
1102 | bfa_assert(0); | ||
1103 | break; | ||
1104 | } | ||
1105 | |||
1106 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1107 | } | ||
1108 | |||
1109 | /** | ||
1110 | * Scsi_Host template entry, queue a SCSI command to the BFAD. | ||
1111 | */ | ||
1112 | static int | ||
1113 | bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | ||
1114 | { | ||
1115 | struct bfad_im_port_s *im_port = | ||
1116 | (struct bfad_im_port_s *) cmnd->device->host->hostdata[0]; | ||
1117 | struct bfad_s *bfad = im_port->bfad; | ||
1118 | struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; | ||
1119 | struct bfad_itnim_s *itnim; | ||
1120 | struct bfa_ioim_s *hal_io; | ||
1121 | unsigned long flags; | ||
1122 | int rc; | ||
1123 | s16 sg_cnt = 0; | ||
1124 | struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); | ||
1125 | |||
1126 | rc = fc_remote_port_chkready(rport); | ||
1127 | if (rc) { | ||
1128 | cmnd->result = rc; | ||
1129 | done(cmnd); | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | sg_cnt = scsi_dma_map(cmnd); | ||
1134 | |||
1135 | if (sg_cnt < 0) | ||
1136 | return SCSI_MLQUEUE_HOST_BUSY; | ||
1137 | |||
1138 | cmnd->scsi_done = done; | ||
1139 | |||
1140 | spin_lock_irqsave(&bfad->bfad_lock, flags); | ||
1141 | if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) { | ||
1142 | printk(KERN_WARNING | ||
1143 | "bfad%d, queuecommand %p %x failed, BFA stopped\n", | ||
1144 | bfad->inst_no, cmnd, cmnd->cmnd[0]); | ||
1145 | cmnd->result = ScsiResult(DID_NO_CONNECT, 0); | ||
1146 | goto out_fail_cmd; | ||
1147 | } | ||
1148 | |||
1149 | itnim = itnim_data->itnim; | ||
1150 | if (!itnim) { | ||
1151 | cmnd->result = ScsiResult(DID_IMM_RETRY, 0); | ||
1152 | goto out_fail_cmd; | ||
1153 | } | ||
1154 | |||
1155 | hal_io = bfa_ioim_alloc(&bfad->bfa, (struct bfad_ioim_s *) cmnd, | ||
1156 | itnim->bfa_itnim, sg_cnt); | ||
1157 | if (!hal_io) { | ||
1158 | printk(KERN_WARNING "hal_io failure\n"); | ||
1159 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1160 | scsi_dma_unmap(cmnd); | ||
1161 | return SCSI_MLQUEUE_HOST_BUSY; | ||
1162 | } | ||
1163 | |||
1164 | cmnd->host_scribble = (char *)hal_io; | ||
1165 | bfa_trc_fp(bfad, hal_io->iotag); | ||
1166 | bfa_ioim_start(hal_io); | ||
1167 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1168 | |||
1169 | return 0; | ||
1170 | |||
1171 | out_fail_cmd: | ||
1172 | spin_unlock_irqrestore(&bfad->bfad_lock, flags); | ||
1173 | scsi_dma_unmap(cmnd); | ||
1174 | if (done) | ||
1175 | done(cmnd); | ||
1176 | |||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | void | ||
1181 | bfad_os_rport_online_wait(struct bfad_s *bfad) | ||
1182 | { | ||
1183 | int i; | ||
1184 | int rport_delay = 10; | ||
1185 | |||
1186 | for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE) | ||
1187 | && i < bfa_linkup_delay; i++) | ||
1188 | schedule_timeout_uninterruptible(HZ); | ||
1189 | |||
1190 | if (bfad->bfad_flags & BFAD_PORT_ONLINE) { | ||
1191 | rport_delay = rport_delay < bfa_linkup_delay ? | ||
1192 | rport_delay : bfa_linkup_delay; | ||
1193 | for (i = 0; !(bfad->bfad_flags & BFAD_RPORT_ONLINE) | ||
1194 | && i < rport_delay; i++) | ||
1195 | schedule_timeout_uninterruptible(HZ); | ||
1196 | |||
1197 | if (rport_delay > 0 && (bfad->bfad_flags & BFAD_RPORT_ONLINE)) | ||
1198 | schedule_timeout_uninterruptible(rport_delay * HZ); | ||
1199 | } | ||
1200 | } | ||
1201 | |||
1202 | int | ||
1203 | bfad_os_get_linkup_delay(struct bfad_s *bfad) | ||
1204 | { | ||
1205 | |||
1206 | u8 nwwns = 0; | ||
1207 | wwn_t *wwns; | ||
1208 | int ldelay; | ||
1209 | |||
1210 | /* | ||
1211 | * Querying for the boot target port wwns | ||
1212 | * -- read from boot information in flash. | ||
1213 | * If nwwns > 0 => boot over SAN and set bfa_linkup_delay = 30 | ||
1214 | * else => local boot machine set bfa_linkup_delay = 10 | ||
1215 | */ | ||
1216 | |||
1217 | bfa_iocfc_get_bootwwns(&bfad->bfa, &nwwns, &wwns); | ||
1218 | |||
1219 | if (nwwns > 0) { | ||
1220 | /* If boot over SAN; linkup_delay = 30sec */ | ||
1221 | ldelay = 30; | ||
1222 | } else { | ||
1223 | /* If local boot; linkup_delay = 10sec */ | ||
1224 | ldelay = 0; | ||
1225 | } | ||
1226 | |||
1227 | return ldelay; | ||
1228 | } | ||
1229 | |||
1230 | |||