1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2005-2014 Brocade Communications Systems, Inc. |
4 | * Copyright (c) 2014- QLogic Corporation. |
5 | * All rights reserved |
6 | * www.qlogic.com |
7 | * |
8 | * Linux driver for QLogic BR-series Fibre Channel Host Bus Adapter. |
9 | */ |
10 | |
11 | /* |
12 | * bfad_im.c Linux driver IM module. |
13 | */ |
14 | |
15 | #include <linux/export.h> |
16 | |
17 | #include "bfad_drv.h" |
18 | #include "bfad_im.h" |
19 | #include "bfa_fcs.h" |
20 | |
21 | BFA_TRC_FILE(LDRV, IM); |
22 | |
23 | DEFINE_IDR(bfad_im_port_index); |
24 | struct scsi_transport_template *bfad_im_scsi_transport_template; |
25 | struct scsi_transport_template *bfad_im_scsi_vport_transport_template; |
26 | static void bfad_im_itnim_work_handler(struct work_struct *work); |
27 | static int bfad_im_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmnd); |
28 | static int bfad_im_slave_alloc(struct scsi_device *sdev); |
29 | static void bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, |
30 | struct bfad_itnim_s *itnim); |
31 | |
32 | void |
33 | bfa_cb_ioim_done(void *drv, struct bfad_ioim_s *dio, |
34 | enum bfi_ioim_status io_status, u8 scsi_status, |
35 | int sns_len, u8 *sns_info, s32 residue) |
36 | { |
37 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; |
38 | struct bfad_s *bfad = drv; |
39 | struct bfad_itnim_data_s *itnim_data; |
40 | struct bfad_itnim_s *itnim; |
41 | u8 host_status = DID_OK; |
42 | |
43 | switch (io_status) { |
44 | case BFI_IOIM_STS_OK: |
45 | bfa_trc(bfad, scsi_status); |
46 | scsi_set_resid(cmd: cmnd, resid: 0); |
47 | |
48 | if (sns_len > 0) { |
49 | bfa_trc(bfad, sns_len); |
50 | if (sns_len > SCSI_SENSE_BUFFERSIZE) |
51 | sns_len = SCSI_SENSE_BUFFERSIZE; |
52 | memcpy(cmnd->sense_buffer, sns_info, sns_len); |
53 | } |
54 | |
55 | if (residue > 0) { |
56 | bfa_trc(bfad, residue); |
57 | scsi_set_resid(cmd: cmnd, resid: residue); |
58 | if (!sns_len && (scsi_status == SAM_STAT_GOOD) && |
59 | (scsi_bufflen(cmd: cmnd) - residue) < |
60 | cmnd->underflow) { |
61 | bfa_trc(bfad, 0); |
62 | host_status = DID_ERROR; |
63 | } |
64 | } |
65 | cmnd->result = host_status << 16 | scsi_status; |
66 | |
67 | break; |
68 | |
69 | case BFI_IOIM_STS_TIMEDOUT: |
70 | cmnd->result = DID_TIME_OUT << 16; |
71 | break; |
72 | case BFI_IOIM_STS_PATHTOV: |
73 | cmnd->result = DID_TRANSPORT_DISRUPTED << 16; |
74 | break; |
75 | default: |
76 | cmnd->result = DID_ERROR << 16; |
77 | } |
78 | |
79 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ |
80 | if (cmnd->device->host != NULL) |
81 | scsi_dma_unmap(cmd: cmnd); |
82 | |
83 | cmnd->host_scribble = NULL; |
84 | bfa_trc(bfad, cmnd->result); |
85 | |
86 | itnim_data = cmnd->device->hostdata; |
87 | if (itnim_data) { |
88 | itnim = itnim_data->itnim; |
89 | if (!cmnd->result && itnim && |
90 | (bfa_lun_queue_depth > cmnd->device->queue_depth)) { |
91 | /* Queue depth adjustment for good status completion */ |
92 | bfad_ramp_up_qdepth(itnim, sdev: cmnd->device); |
93 | } else if (cmnd->result == SAM_STAT_TASK_SET_FULL && itnim) { |
94 | /* qfull handling */ |
95 | bfad_handle_qfull(itnim, sdev: cmnd->device); |
96 | } |
97 | } |
98 | |
99 | scsi_done(cmd: cmnd); |
100 | } |
101 | |
102 | void |
103 | bfa_cb_ioim_good_comp(void *drv, struct bfad_ioim_s *dio) |
104 | { |
105 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; |
106 | struct bfad_itnim_data_s *itnim_data; |
107 | struct bfad_itnim_s *itnim; |
108 | |
109 | cmnd->result = DID_OK << 16 | SAM_STAT_GOOD; |
110 | |
111 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ |
112 | if (cmnd->device->host != NULL) |
113 | scsi_dma_unmap(cmd: cmnd); |
114 | |
115 | cmnd->host_scribble = NULL; |
116 | |
117 | /* Queue depth adjustment */ |
118 | if (bfa_lun_queue_depth > cmnd->device->queue_depth) { |
119 | itnim_data = cmnd->device->hostdata; |
120 | if (itnim_data) { |
121 | itnim = itnim_data->itnim; |
122 | if (itnim) |
123 | bfad_ramp_up_qdepth(itnim, sdev: cmnd->device); |
124 | } |
125 | } |
126 | |
127 | scsi_done(cmd: cmnd); |
128 | } |
129 | |
130 | void |
131 | bfa_cb_ioim_abort(void *drv, struct bfad_ioim_s *dio) |
132 | { |
133 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; |
134 | struct bfad_s *bfad = drv; |
135 | |
136 | cmnd->result = DID_ERROR << 16; |
137 | |
138 | /* Unmap DMA, if host is NULL, it means a scsi passthru cmd */ |
139 | if (cmnd->device->host != NULL) |
140 | scsi_dma_unmap(cmd: cmnd); |
141 | |
142 | bfa_trc(bfad, cmnd->result); |
143 | cmnd->host_scribble = NULL; |
144 | } |
145 | |
146 | void |
147 | bfa_cb_tskim_done(void *bfad, struct bfad_tskim_s *dtsk, |
148 | enum bfi_tskim_status tsk_status) |
149 | { |
150 | struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dtsk; |
151 | wait_queue_head_t *wq; |
152 | |
153 | bfad_priv(cmd: cmnd)->status |= tsk_status << 1; |
154 | set_bit(IO_DONE_BIT, addr: &bfad_priv(cmd: cmnd)->status); |
155 | wq = bfad_priv(cmd: cmnd)->wq; |
156 | bfad_priv(cmd: cmnd)->wq = NULL; |
157 | |
158 | if (wq) |
159 | wake_up(wq); |
160 | } |
161 | |
162 | /* |
163 | * Scsi_Host_template SCSI host template |
164 | */ |
165 | /* |
166 | * Scsi_Host template entry, returns BFAD PCI info. |
167 | */ |
168 | static const char * |
169 | bfad_im_info(struct Scsi_Host *shost) |
170 | { |
171 | static char bfa_buf[256]; |
172 | struct bfad_im_port_s *im_port = |
173 | (struct bfad_im_port_s *) shost->hostdata[0]; |
174 | struct bfad_s *bfad = im_port->bfad; |
175 | |
176 | memset(bfa_buf, 0, sizeof(bfa_buf)); |
177 | snprintf(buf: bfa_buf, size: sizeof(bfa_buf), |
178 | fmt: "QLogic BR-series FC/FCOE Adapter, hwpath: %s driver: %s" , |
179 | bfad->pci_name, BFAD_DRIVER_VERSION); |
180 | |
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, return 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(KERN_INFO, bfad, bfa_log_level, |
215 | "scsi%d: abort cmnd %p iotag %x\n" , |
216 | im_port->shost->host_no, cmnd, hal_io->iotag); |
217 | (void) bfa_ioim_abort(ioim: hal_io); |
218 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
219 | |
220 | /* Need to wait until the command get aborted */ |
221 | timeout = 10; |
222 | while ((struct bfa_ioim_s *) cmnd->host_scribble == hal_io) { |
223 | set_current_state(TASK_UNINTERRUPTIBLE); |
224 | schedule_timeout(timeout); |
225 | if (timeout < 4 * HZ) |
226 | timeout *= 2; |
227 | } |
228 | |
229 | scsi_done(cmd: cmnd); |
230 | bfa_trc(bfad, hal_io->iotag); |
231 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
232 | "scsi%d: complete abort 0x%p iotag 0x%x\n" , |
233 | im_port->shost->host_no, cmnd, hal_io->iotag); |
234 | return SUCCESS; |
235 | out: |
236 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
237 | return rc; |
238 | } |
239 | |
240 | static bfa_status_t |
241 | bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd, |
242 | struct bfad_itnim_s *itnim) |
243 | { |
244 | struct bfa_tskim_s *tskim; |
245 | struct bfa_itnim_s *bfa_itnim; |
246 | bfa_status_t rc = BFA_STATUS_OK; |
247 | struct scsi_lun scsilun; |
248 | |
249 | tskim = bfa_tskim_alloc(bfa: &bfad->bfa, dtsk: (struct bfad_tskim_s *) cmnd); |
250 | if (!tskim) { |
251 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
252 | "target reset, fail to allocate tskim\n" ); |
253 | rc = BFA_STATUS_FAILED; |
254 | goto out; |
255 | } |
256 | |
257 | /* |
258 | * Set host_scribble to NULL to avoid aborting a task command if |
259 | * happens. |
260 | */ |
261 | cmnd->host_scribble = NULL; |
262 | bfad_priv(cmd: cmnd)->status = 0; |
263 | bfa_itnim = bfa_fcs_itnim_get_halitn(itnim: &itnim->fcs_itnim); |
264 | /* |
265 | * bfa_itnim can be NULL if the port gets disconnected and the bfa |
266 | * and fcs layers have cleaned up their nexus with the targets and |
267 | * the same has not been cleaned up by the shim |
268 | */ |
269 | if (bfa_itnim == NULL) { |
270 | bfa_tskim_free(tskim); |
271 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
272 | "target reset, bfa_itnim is NULL\n" ); |
273 | rc = BFA_STATUS_FAILED; |
274 | goto out; |
275 | } |
276 | |
277 | memset(&scsilun, 0, sizeof(scsilun)); |
278 | bfa_tskim_start(tskim, itnim: bfa_itnim, lun: scsilun, |
279 | tm: FCP_TM_TARGET_RESET, BFAD_TARGET_RESET_TMO); |
280 | out: |
281 | return rc; |
282 | } |
283 | |
284 | /* |
285 | * Scsi_Host template entry, resets a LUN and abort its all commands. |
286 | * |
287 | * Returns: SUCCESS or FAILED. |
288 | * |
289 | */ |
290 | static int |
291 | bfad_im_reset_lun_handler(struct scsi_cmnd *cmnd) |
292 | { |
293 | struct Scsi_Host *shost = cmnd->device->host; |
294 | struct bfad_im_port_s *im_port = |
295 | (struct bfad_im_port_s *) shost->hostdata[0]; |
296 | struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; |
297 | struct bfad_s *bfad = im_port->bfad; |
298 | struct bfa_tskim_s *tskim; |
299 | struct bfad_itnim_s *itnim; |
300 | struct bfa_itnim_s *bfa_itnim; |
301 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); |
302 | int rc = SUCCESS; |
303 | unsigned long flags; |
304 | enum bfi_tskim_status task_status; |
305 | struct scsi_lun scsilun; |
306 | |
307 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
308 | itnim = itnim_data->itnim; |
309 | if (!itnim) { |
310 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
311 | rc = FAILED; |
312 | goto out; |
313 | } |
314 | |
315 | tskim = bfa_tskim_alloc(bfa: &bfad->bfa, dtsk: (struct bfad_tskim_s *) cmnd); |
316 | if (!tskim) { |
317 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
318 | "LUN reset, fail to allocate tskim" ); |
319 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
320 | rc = FAILED; |
321 | goto out; |
322 | } |
323 | |
324 | /* |
325 | * Set host_scribble to NULL to avoid aborting a task command |
326 | * if happens. |
327 | */ |
328 | cmnd->host_scribble = NULL; |
329 | bfad_priv(cmd: cmnd)->wq = &wq; |
330 | bfad_priv(cmd: cmnd)->status = 0; |
331 | bfa_itnim = bfa_fcs_itnim_get_halitn(itnim: &itnim->fcs_itnim); |
332 | /* |
333 | * bfa_itnim can be NULL if the port gets disconnected and the bfa |
334 | * and fcs layers have cleaned up their nexus with the targets and |
335 | * the same has not been cleaned up by the shim |
336 | */ |
337 | if (bfa_itnim == NULL) { |
338 | bfa_tskim_free(tskim); |
339 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
340 | "lun reset, bfa_itnim is NULL\n" ); |
341 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
342 | rc = FAILED; |
343 | goto out; |
344 | } |
345 | int_to_scsilun(cmnd->device->lun, &scsilun); |
346 | bfa_tskim_start(tskim, itnim: bfa_itnim, lun: scsilun, |
347 | tm: FCP_TM_LUN_RESET, BFAD_LUN_RESET_TMO); |
348 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
349 | |
350 | wait_event(wq, test_bit(IO_DONE_BIT, &bfad_priv(cmnd)->status)); |
351 | |
352 | task_status = bfad_priv(cmd: cmnd)->status >> 1; |
353 | if (task_status != BFI_TSKIM_STS_OK) { |
354 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
355 | "LUN reset failure, status: %d\n" , task_status); |
356 | rc = FAILED; |
357 | } |
358 | |
359 | out: |
360 | return rc; |
361 | } |
362 | |
363 | /* |
364 | * Scsi_Host template entry, resets the target and abort all commands. |
365 | */ |
366 | static int |
367 | bfad_im_reset_target_handler(struct scsi_cmnd *cmnd) |
368 | { |
369 | struct Scsi_Host *shost = cmnd->device->host; |
370 | struct scsi_target *starget = scsi_target(sdev: cmnd->device); |
371 | struct bfad_im_port_s *im_port = |
372 | (struct bfad_im_port_s *) shost->hostdata[0]; |
373 | struct bfad_s *bfad = im_port->bfad; |
374 | struct bfad_itnim_s *itnim; |
375 | unsigned long flags; |
376 | u32 rc, rtn = FAILED; |
377 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); |
378 | enum bfi_tskim_status task_status; |
379 | |
380 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
381 | itnim = bfad_get_itnim(im_port, id: starget->id); |
382 | if (itnim) { |
383 | bfad_priv(cmd: cmnd)->wq = &wq; |
384 | rc = bfad_im_target_reset_send(bfad, cmnd, itnim); |
385 | if (rc == BFA_STATUS_OK) { |
386 | /* wait target reset to complete */ |
387 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
388 | wait_event(wq, test_bit(IO_DONE_BIT, |
389 | &bfad_priv(cmnd)->status)); |
390 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
391 | |
392 | task_status = bfad_priv(cmd: cmnd)->status >> 1; |
393 | if (task_status != BFI_TSKIM_STS_OK) |
394 | BFA_LOG(KERN_ERR, bfad, bfa_log_level, |
395 | "target reset failure," |
396 | " status: %d\n" , task_status); |
397 | else |
398 | rtn = SUCCESS; |
399 | } |
400 | } |
401 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
402 | |
403 | return rtn; |
404 | } |
405 | |
406 | /* |
407 | * Scsi_Host template entry slave_destroy. |
408 | */ |
409 | static void |
410 | bfad_im_slave_destroy(struct scsi_device *sdev) |
411 | { |
412 | sdev->hostdata = NULL; |
413 | return; |
414 | } |
415 | |
416 | /* |
417 | * BFA FCS itnim callbacks |
418 | */ |
419 | |
420 | /* |
421 | * BFA FCS itnim alloc callback, after successful PRLI |
422 | * Context: Interrupt |
423 | */ |
424 | int |
425 | bfa_fcb_itnim_alloc(struct bfad_s *bfad, struct bfa_fcs_itnim_s **itnim, |
426 | struct bfad_itnim_s **itnim_drv) |
427 | { |
428 | *itnim_drv = kzalloc(size: sizeof(struct bfad_itnim_s), GFP_ATOMIC); |
429 | if (*itnim_drv == NULL) |
430 | return -ENOMEM; |
431 | |
432 | (*itnim_drv)->im = bfad->im; |
433 | *itnim = &(*itnim_drv)->fcs_itnim; |
434 | (*itnim_drv)->state = ITNIM_STATE_NONE; |
435 | |
436 | /* |
437 | * Initiaze the itnim_work |
438 | */ |
439 | INIT_WORK(&(*itnim_drv)->itnim_work, bfad_im_itnim_work_handler); |
440 | bfad->bfad_flags |= BFAD_RPORT_ONLINE; |
441 | return 0; |
442 | } |
443 | |
444 | /* |
445 | * BFA FCS itnim free callback. |
446 | * Context: Interrupt. bfad_lock is held |
447 | */ |
448 | void |
449 | bfa_fcb_itnim_free(struct bfad_s *bfad, struct bfad_itnim_s *itnim_drv) |
450 | { |
451 | struct bfad_port_s *port; |
452 | wwn_t wwpn; |
453 | u32 fcid; |
454 | char wwpn_str[32], fcid_str[16]; |
455 | struct bfad_im_s *im = itnim_drv->im; |
456 | |
457 | /* online to free state transtion should not happen */ |
458 | WARN_ON(itnim_drv->state == ITNIM_STATE_ONLINE); |
459 | |
460 | itnim_drv->queue_work = 1; |
461 | /* offline request is not yet done, use the same request to free */ |
462 | if (itnim_drv->state == ITNIM_STATE_OFFLINE_PENDING) |
463 | itnim_drv->queue_work = 0; |
464 | |
465 | itnim_drv->state = ITNIM_STATE_FREE; |
466 | port = bfa_fcs_itnim_get_drvport(itnim: &itnim_drv->fcs_itnim); |
467 | itnim_drv->im_port = port->im_port; |
468 | wwpn = bfa_fcs_itnim_get_pwwn(itnim: &itnim_drv->fcs_itnim); |
469 | fcid = bfa_fcs_itnim_get_fcid(itnim: &itnim_drv->fcs_itnim); |
470 | wwn2str(wwn_str: wwpn_str, wwn: wwpn); |
471 | fcid2str(fcid_str, fcid); |
472 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
473 | "ITNIM FREE scsi%d: FCID: %s WWPN: %s\n" , |
474 | port->im_port->shost->host_no, |
475 | fcid_str, wwpn_str); |
476 | |
477 | /* ITNIM processing */ |
478 | if (itnim_drv->queue_work) |
479 | queue_work(wq: im->drv_workq, work: &itnim_drv->itnim_work); |
480 | } |
481 | |
482 | /* |
483 | * BFA FCS itnim online callback. |
484 | * Context: Interrupt. bfad_lock is held |
485 | */ |
486 | void |
487 | bfa_fcb_itnim_online(struct bfad_itnim_s *itnim_drv) |
488 | { |
489 | struct bfad_port_s *port; |
490 | struct bfad_im_s *im = itnim_drv->im; |
491 | |
492 | itnim_drv->bfa_itnim = bfa_fcs_itnim_get_halitn(itnim: &itnim_drv->fcs_itnim); |
493 | port = bfa_fcs_itnim_get_drvport(itnim: &itnim_drv->fcs_itnim); |
494 | itnim_drv->state = ITNIM_STATE_ONLINE; |
495 | itnim_drv->queue_work = 1; |
496 | itnim_drv->im_port = port->im_port; |
497 | |
498 | /* ITNIM processing */ |
499 | if (itnim_drv->queue_work) |
500 | queue_work(wq: im->drv_workq, work: &itnim_drv->itnim_work); |
501 | } |
502 | |
503 | /* |
504 | * BFA FCS itnim offline callback. |
505 | * Context: Interrupt. bfad_lock is held |
506 | */ |
507 | void |
508 | bfa_fcb_itnim_offline(struct bfad_itnim_s *itnim_drv) |
509 | { |
510 | struct bfad_port_s *port; |
511 | struct bfad_s *bfad; |
512 | struct bfad_im_s *im = itnim_drv->im; |
513 | |
514 | port = bfa_fcs_itnim_get_drvport(itnim: &itnim_drv->fcs_itnim); |
515 | bfad = port->bfad; |
516 | if ((bfad->pport.flags & BFAD_PORT_DELETE) || |
517 | (port->flags & BFAD_PORT_DELETE)) { |
518 | itnim_drv->state = ITNIM_STATE_OFFLINE; |
519 | return; |
520 | } |
521 | itnim_drv->im_port = port->im_port; |
522 | itnim_drv->state = ITNIM_STATE_OFFLINE_PENDING; |
523 | itnim_drv->queue_work = 1; |
524 | |
525 | /* ITNIM processing */ |
526 | if (itnim_drv->queue_work) |
527 | queue_work(wq: im->drv_workq, work: &itnim_drv->itnim_work); |
528 | } |
529 | |
530 | /* |
531 | * Allocate a Scsi_Host for a port. |
532 | */ |
533 | int |
534 | bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port, |
535 | struct device *dev) |
536 | { |
537 | struct bfad_im_port_pointer *im_portp; |
538 | int error; |
539 | |
540 | mutex_lock(&bfad_mutex); |
541 | error = idr_alloc(&bfad_im_port_index, ptr: im_port, start: 0, end: 0, GFP_KERNEL); |
542 | if (error < 0) { |
543 | mutex_unlock(lock: &bfad_mutex); |
544 | printk(KERN_WARNING "idr_alloc failure\n" ); |
545 | goto out; |
546 | } |
547 | im_port->idr_id = error; |
548 | mutex_unlock(lock: &bfad_mutex); |
549 | |
550 | im_port->shost = bfad_scsi_host_alloc(im_port, bfad); |
551 | if (!im_port->shost) { |
552 | error = 1; |
553 | goto out_free_idr; |
554 | } |
555 | |
556 | im_portp = shost_priv(shost: im_port->shost); |
557 | im_portp->p = im_port; |
558 | im_port->shost->unique_id = im_port->idr_id; |
559 | im_port->shost->this_id = -1; |
560 | im_port->shost->max_id = MAX_FCP_TARGET; |
561 | im_port->shost->max_lun = MAX_FCP_LUN; |
562 | im_port->shost->max_cmd_len = 16; |
563 | im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth; |
564 | if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) |
565 | im_port->shost->transportt = bfad_im_scsi_transport_template; |
566 | else |
567 | im_port->shost->transportt = |
568 | bfad_im_scsi_vport_transport_template; |
569 | |
570 | error = scsi_add_host_with_dma(im_port->shost, dev, &bfad->pcidev->dev); |
571 | if (error) { |
572 | printk(KERN_WARNING "scsi_add_host failure %d\n" , error); |
573 | goto out_fc_rel; |
574 | } |
575 | |
576 | return 0; |
577 | |
578 | out_fc_rel: |
579 | scsi_host_put(t: im_port->shost); |
580 | im_port->shost = NULL; |
581 | out_free_idr: |
582 | mutex_lock(&bfad_mutex); |
583 | idr_remove(&bfad_im_port_index, id: im_port->idr_id); |
584 | mutex_unlock(lock: &bfad_mutex); |
585 | out: |
586 | return error; |
587 | } |
588 | |
589 | void |
590 | bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) |
591 | { |
592 | bfa_trc(bfad, bfad->inst_no); |
593 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, "Free scsi%d\n" , |
594 | im_port->shost->host_no); |
595 | |
596 | fc_remove_host(im_port->shost); |
597 | |
598 | scsi_remove_host(im_port->shost); |
599 | scsi_host_put(t: im_port->shost); |
600 | |
601 | mutex_lock(&bfad_mutex); |
602 | idr_remove(&bfad_im_port_index, id: im_port->idr_id); |
603 | mutex_unlock(lock: &bfad_mutex); |
604 | } |
605 | |
606 | static void |
607 | bfad_im_port_delete_handler(struct work_struct *work) |
608 | { |
609 | struct bfad_im_port_s *im_port = |
610 | container_of(work, struct bfad_im_port_s, port_delete_work); |
611 | |
612 | if (im_port->port->pvb_type != BFAD_PORT_PHYS_BASE) { |
613 | im_port->flags |= BFAD_PORT_DELETE; |
614 | fc_vport_terminate(vport: im_port->fc_vport); |
615 | } |
616 | } |
617 | |
618 | bfa_status_t |
619 | bfad_im_port_new(struct bfad_s *bfad, struct bfad_port_s *port) |
620 | { |
621 | int rc = BFA_STATUS_OK; |
622 | struct bfad_im_port_s *im_port; |
623 | |
624 | im_port = kzalloc(size: sizeof(struct bfad_im_port_s), GFP_ATOMIC); |
625 | if (im_port == NULL) { |
626 | rc = BFA_STATUS_ENOMEM; |
627 | goto ext; |
628 | } |
629 | port->im_port = im_port; |
630 | im_port->port = port; |
631 | im_port->bfad = bfad; |
632 | |
633 | INIT_WORK(&im_port->port_delete_work, bfad_im_port_delete_handler); |
634 | INIT_LIST_HEAD(list: &im_port->itnim_mapped_list); |
635 | INIT_LIST_HEAD(list: &im_port->binding_list); |
636 | |
637 | ext: |
638 | return rc; |
639 | } |
640 | |
641 | void |
642 | bfad_im_port_delete(struct bfad_s *bfad, struct bfad_port_s *port) |
643 | { |
644 | struct bfad_im_port_s *im_port = port->im_port; |
645 | |
646 | queue_work(wq: bfad->im->drv_workq, |
647 | work: &im_port->port_delete_work); |
648 | } |
649 | |
650 | void |
651 | bfad_im_port_clean(struct bfad_im_port_s *im_port) |
652 | { |
653 | struct bfad_fcp_binding *bp, *bp_new; |
654 | unsigned long flags; |
655 | struct bfad_s *bfad = im_port->bfad; |
656 | |
657 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
658 | list_for_each_entry_safe(bp, bp_new, &im_port->binding_list, |
659 | list_entry) { |
660 | list_del(entry: &bp->list_entry); |
661 | kfree(objp: bp); |
662 | } |
663 | |
664 | /* the itnim_mapped_list must be empty at this time */ |
665 | WARN_ON(!list_empty(&im_port->itnim_mapped_list)); |
666 | |
667 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
668 | } |
669 | |
670 | static void bfad_aen_im_notify_handler(struct work_struct *work) |
671 | { |
672 | struct bfad_im_s *im = |
673 | container_of(work, struct bfad_im_s, aen_im_notify_work); |
674 | struct bfa_aen_entry_s *aen_entry; |
675 | struct bfad_s *bfad = im->bfad; |
676 | struct Scsi_Host *shost = bfad->pport.im_port->shost; |
677 | void *event_data; |
678 | unsigned long flags; |
679 | |
680 | while (!list_empty(head: &bfad->active_aen_q)) { |
681 | spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags); |
682 | bfa_q_deq(&bfad->active_aen_q, &aen_entry); |
683 | spin_unlock_irqrestore(lock: &bfad->bfad_aen_spinlock, flags); |
684 | event_data = (char *)aen_entry + sizeof(struct list_head); |
685 | fc_host_post_vendor_event(shost, event_number: fc_get_event_number(), |
686 | data_len: sizeof(struct bfa_aen_entry_s) - |
687 | sizeof(struct list_head), |
688 | data_buf: (char *)event_data, BFAD_NL_VENDOR_ID); |
689 | spin_lock_irqsave(&bfad->bfad_aen_spinlock, flags); |
690 | list_add_tail(new: &aen_entry->qe, head: &bfad->free_aen_q); |
691 | spin_unlock_irqrestore(lock: &bfad->bfad_aen_spinlock, flags); |
692 | } |
693 | } |
694 | |
695 | bfa_status_t |
696 | bfad_im_probe(struct bfad_s *bfad) |
697 | { |
698 | struct bfad_im_s *im; |
699 | |
700 | im = kzalloc(size: sizeof(struct bfad_im_s), GFP_KERNEL); |
701 | if (im == NULL) |
702 | return BFA_STATUS_ENOMEM; |
703 | |
704 | bfad->im = im; |
705 | im->bfad = bfad; |
706 | |
707 | if (bfad_thread_workq(bfad) != BFA_STATUS_OK) { |
708 | kfree(objp: im); |
709 | return BFA_STATUS_FAILED; |
710 | } |
711 | |
712 | INIT_WORK(&im->aen_im_notify_work, bfad_aen_im_notify_handler); |
713 | return BFA_STATUS_OK; |
714 | } |
715 | |
716 | void |
717 | bfad_im_probe_undo(struct bfad_s *bfad) |
718 | { |
719 | if (bfad->im) { |
720 | bfad_destroy_workq(im: bfad->im); |
721 | kfree(objp: bfad->im); |
722 | bfad->im = NULL; |
723 | } |
724 | } |
725 | |
726 | struct Scsi_Host * |
727 | bfad_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) |
728 | { |
729 | struct scsi_host_template *sht; |
730 | |
731 | if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) |
732 | sht = &bfad_im_scsi_host_template; |
733 | else |
734 | sht = &bfad_im_vport_template; |
735 | |
736 | if (max_xfer_size != BFAD_MAX_SECTORS >> 1) |
737 | sht->max_sectors = max_xfer_size << 1; |
738 | |
739 | sht->sg_tablesize = bfad->cfg_data.io_max_sge; |
740 | |
741 | return scsi_host_alloc(sht, sizeof(struct bfad_im_port_pointer)); |
742 | } |
743 | |
744 | void |
745 | bfad_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) |
746 | { |
747 | if (!(im_port->flags & BFAD_PORT_DELETE)) |
748 | flush_workqueue(bfad->im->drv_workq); |
749 | bfad_im_scsi_host_free(bfad: im_port->bfad, im_port); |
750 | bfad_im_port_clean(im_port); |
751 | kfree(objp: im_port); |
752 | } |
753 | |
754 | void |
755 | bfad_destroy_workq(struct bfad_im_s *im) |
756 | { |
757 | if (im && im->drv_workq) { |
758 | destroy_workqueue(wq: im->drv_workq); |
759 | im->drv_workq = NULL; |
760 | } |
761 | } |
762 | |
763 | bfa_status_t |
764 | bfad_thread_workq(struct bfad_s *bfad) |
765 | { |
766 | struct bfad_im_s *im = bfad->im; |
767 | |
768 | bfa_trc(bfad, 0); |
769 | snprintf(buf: im->drv_workq_name, KOBJ_NAME_LEN, fmt: "bfad_wq_%d" , |
770 | bfad->inst_no); |
771 | im->drv_workq = create_singlethread_workqueue(im->drv_workq_name); |
772 | if (!im->drv_workq) |
773 | return BFA_STATUS_FAILED; |
774 | |
775 | return BFA_STATUS_OK; |
776 | } |
777 | |
778 | /* |
779 | * Scsi_Host template entry. |
780 | * |
781 | * Description: |
782 | * OS entry point to adjust the queue_depths on a per-device basis. |
783 | * Called once per device during the bus scan. |
784 | * Return non-zero if fails. |
785 | */ |
786 | static int |
787 | bfad_im_slave_configure(struct scsi_device *sdev) |
788 | { |
789 | scsi_change_queue_depth(sdev, bfa_lun_queue_depth); |
790 | return 0; |
791 | } |
792 | |
793 | struct scsi_host_template bfad_im_scsi_host_template = { |
794 | .module = THIS_MODULE, |
795 | .name = BFAD_DRIVER_NAME, |
796 | .info = bfad_im_info, |
797 | .queuecommand = bfad_im_queuecommand, |
798 | .cmd_size = sizeof(struct bfad_cmd_priv), |
799 | .eh_timed_out = fc_eh_timed_out, |
800 | .eh_abort_handler = bfad_im_abort_handler, |
801 | .eh_device_reset_handler = bfad_im_reset_lun_handler, |
802 | .eh_target_reset_handler = bfad_im_reset_target_handler, |
803 | |
804 | .slave_alloc = bfad_im_slave_alloc, |
805 | .slave_configure = bfad_im_slave_configure, |
806 | .slave_destroy = bfad_im_slave_destroy, |
807 | |
808 | .this_id = -1, |
809 | .sg_tablesize = BFAD_IO_MAX_SGE, |
810 | .cmd_per_lun = 3, |
811 | .shost_groups = bfad_im_host_groups, |
812 | .max_sectors = BFAD_MAX_SECTORS, |
813 | .vendor_id = BFA_PCI_VENDOR_ID_BROCADE, |
814 | }; |
815 | |
816 | struct scsi_host_template bfad_im_vport_template = { |
817 | .module = THIS_MODULE, |
818 | .name = BFAD_DRIVER_NAME, |
819 | .info = bfad_im_info, |
820 | .queuecommand = bfad_im_queuecommand, |
821 | .cmd_size = sizeof(struct bfad_cmd_priv), |
822 | .eh_timed_out = fc_eh_timed_out, |
823 | .eh_abort_handler = bfad_im_abort_handler, |
824 | .eh_device_reset_handler = bfad_im_reset_lun_handler, |
825 | .eh_target_reset_handler = bfad_im_reset_target_handler, |
826 | |
827 | .slave_alloc = bfad_im_slave_alloc, |
828 | .slave_configure = bfad_im_slave_configure, |
829 | .slave_destroy = bfad_im_slave_destroy, |
830 | |
831 | .this_id = -1, |
832 | .sg_tablesize = BFAD_IO_MAX_SGE, |
833 | .cmd_per_lun = 3, |
834 | .shost_groups = bfad_im_vport_groups, |
835 | .max_sectors = BFAD_MAX_SECTORS, |
836 | }; |
837 | |
838 | bfa_status_t |
839 | bfad_im_module_init(void) |
840 | { |
841 | bfad_im_scsi_transport_template = |
842 | fc_attach_transport(&bfad_im_fc_function_template); |
843 | if (!bfad_im_scsi_transport_template) |
844 | return BFA_STATUS_ENOMEM; |
845 | |
846 | bfad_im_scsi_vport_transport_template = |
847 | fc_attach_transport(&bfad_im_vport_fc_function_template); |
848 | if (!bfad_im_scsi_vport_transport_template) { |
849 | fc_release_transport(bfad_im_scsi_transport_template); |
850 | return BFA_STATUS_ENOMEM; |
851 | } |
852 | |
853 | return BFA_STATUS_OK; |
854 | } |
855 | |
856 | void |
857 | bfad_im_module_exit(void) |
858 | { |
859 | if (bfad_im_scsi_transport_template) |
860 | fc_release_transport(bfad_im_scsi_transport_template); |
861 | |
862 | if (bfad_im_scsi_vport_transport_template) |
863 | fc_release_transport(bfad_im_scsi_vport_transport_template); |
864 | |
865 | idr_destroy(&bfad_im_port_index); |
866 | } |
867 | |
868 | void |
869 | bfad_ramp_up_qdepth(struct bfad_itnim_s *itnim, struct scsi_device *sdev) |
870 | { |
871 | struct scsi_device *tmp_sdev; |
872 | |
873 | if (((jiffies - itnim->last_ramp_up_time) > |
874 | BFA_QUEUE_FULL_RAMP_UP_TIME * HZ) && |
875 | ((jiffies - itnim->last_queue_full_time) > |
876 | BFA_QUEUE_FULL_RAMP_UP_TIME * HZ)) { |
877 | shost_for_each_device(tmp_sdev, sdev->host) { |
878 | if (bfa_lun_queue_depth > tmp_sdev->queue_depth) { |
879 | if (tmp_sdev->id != sdev->id) |
880 | continue; |
881 | scsi_change_queue_depth(tmp_sdev, |
882 | tmp_sdev->queue_depth + 1); |
883 | |
884 | itnim->last_ramp_up_time = jiffies; |
885 | } |
886 | } |
887 | } |
888 | } |
889 | |
890 | void |
891 | bfad_handle_qfull(struct bfad_itnim_s *itnim, struct scsi_device *sdev) |
892 | { |
893 | struct scsi_device *tmp_sdev; |
894 | |
895 | itnim->last_queue_full_time = jiffies; |
896 | |
897 | shost_for_each_device(tmp_sdev, sdev->host) { |
898 | if (tmp_sdev->id != sdev->id) |
899 | continue; |
900 | scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1); |
901 | } |
902 | } |
903 | |
904 | struct bfad_itnim_s * |
905 | bfad_get_itnim(struct bfad_im_port_s *im_port, int id) |
906 | { |
907 | struct bfad_itnim_s *itnim = NULL; |
908 | |
909 | /* Search the mapped list for this target ID */ |
910 | list_for_each_entry(itnim, &im_port->itnim_mapped_list, list_entry) { |
911 | if (id == itnim->scsi_tgt_id) |
912 | return itnim; |
913 | } |
914 | |
915 | return NULL; |
916 | } |
917 | |
918 | /* |
919 | * Function is invoked from the SCSI Host Template slave_alloc() entry point. |
920 | * Has the logic to query the LUN Mask database to check if this LUN needs to |
921 | * be made visible to the SCSI mid-layer or not. |
922 | * |
923 | * Returns BFA_STATUS_OK if this LUN needs to be added to the OS stack. |
924 | * Returns -ENXIO to notify SCSI mid-layer to not add this LUN to the OS stack. |
925 | */ |
926 | static int |
927 | bfad_im_check_if_make_lun_visible(struct scsi_device *sdev, |
928 | struct fc_rport *rport) |
929 | { |
930 | struct bfad_itnim_data_s *itnim_data = |
931 | (struct bfad_itnim_data_s *) rport->dd_data; |
932 | struct bfa_s *bfa = itnim_data->itnim->bfa_itnim->bfa; |
933 | struct bfa_rport_s *bfa_rport = itnim_data->itnim->bfa_itnim->rport; |
934 | struct bfa_lun_mask_s *lun_list = bfa_get_lun_mask_list(bfa); |
935 | int i = 0, ret = -ENXIO; |
936 | |
937 | for (i = 0; i < MAX_LUN_MASK_CFG; i++) { |
938 | if (lun_list[i].state == BFA_IOIM_LUN_MASK_ACTIVE && |
939 | scsilun_to_int(&lun_list[i].lun) == sdev->lun && |
940 | lun_list[i].rp_tag == bfa_rport->rport_tag && |
941 | lun_list[i].lp_tag == (u8)bfa_rport->rport_info.lp_tag) { |
942 | ret = BFA_STATUS_OK; |
943 | break; |
944 | } |
945 | } |
946 | return ret; |
947 | } |
948 | |
949 | /* |
950 | * Scsi_Host template entry slave_alloc |
951 | */ |
952 | static int |
953 | bfad_im_slave_alloc(struct scsi_device *sdev) |
954 | { |
955 | struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); |
956 | struct bfad_itnim_data_s *itnim_data; |
957 | struct bfa_s *bfa; |
958 | |
959 | if (!rport || fc_remote_port_chkready(rport)) |
960 | return -ENXIO; |
961 | |
962 | itnim_data = (struct bfad_itnim_data_s *) rport->dd_data; |
963 | bfa = itnim_data->itnim->bfa_itnim->bfa; |
964 | |
965 | if (bfa_get_lun_mask_status(bfa) == BFA_LUNMASK_ENABLED) { |
966 | /* |
967 | * We should not mask LUN 0 - since this will translate |
968 | * to no LUN / TARGET for SCSI ml resulting no scan. |
969 | */ |
970 | if (sdev->lun == 0) { |
971 | sdev->sdev_bflags |= BLIST_NOREPORTLUN | |
972 | BLIST_SPARSELUN; |
973 | goto done; |
974 | } |
975 | |
976 | /* |
977 | * Query LUN Mask configuration - to expose this LUN |
978 | * to the SCSI mid-layer or to mask it. |
979 | */ |
980 | if (bfad_im_check_if_make_lun_visible(sdev, rport) != |
981 | BFA_STATUS_OK) |
982 | return -ENXIO; |
983 | } |
984 | done: |
985 | sdev->hostdata = rport->dd_data; |
986 | |
987 | return 0; |
988 | } |
989 | |
990 | u32 |
991 | bfad_im_supported_speeds(struct bfa_s *bfa) |
992 | { |
993 | struct bfa_ioc_attr_s *ioc_attr; |
994 | u32 supported_speed = 0; |
995 | |
996 | ioc_attr = kzalloc(size: sizeof(struct bfa_ioc_attr_s), GFP_KERNEL); |
997 | if (!ioc_attr) |
998 | return 0; |
999 | |
1000 | bfa_ioc_get_attr(ioc: &bfa->ioc, ioc_attr); |
1001 | if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_16GBPS) |
1002 | supported_speed |= FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT | |
1003 | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT; |
1004 | else if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_8GBPS) { |
1005 | if (ioc_attr->adapter_attr.is_mezz) { |
1006 | supported_speed |= FC_PORTSPEED_8GBIT | |
1007 | FC_PORTSPEED_4GBIT | |
1008 | FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; |
1009 | } else { |
1010 | supported_speed |= FC_PORTSPEED_8GBIT | |
1011 | FC_PORTSPEED_4GBIT | |
1012 | FC_PORTSPEED_2GBIT; |
1013 | } |
1014 | } else if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_4GBPS) { |
1015 | supported_speed |= FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | |
1016 | FC_PORTSPEED_1GBIT; |
1017 | } else if (ioc_attr->adapter_attr.max_speed == BFA_PORT_SPEED_10GBPS) { |
1018 | supported_speed |= FC_PORTSPEED_10GBIT; |
1019 | } |
1020 | kfree(objp: ioc_attr); |
1021 | return supported_speed; |
1022 | } |
1023 | |
1024 | void |
1025 | bfad_fc_host_init(struct bfad_im_port_s *im_port) |
1026 | { |
1027 | struct Scsi_Host *host = im_port->shost; |
1028 | struct bfad_s *bfad = im_port->bfad; |
1029 | struct bfad_port_s *port = im_port->port; |
1030 | char symname[BFA_SYMNAME_MAXLEN]; |
1031 | struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa); |
1032 | |
1033 | fc_host_node_name(host) = |
1034 | cpu_to_be64((bfa_fcs_lport_get_nwwn(port->fcs_port))); |
1035 | fc_host_port_name(host) = |
1036 | cpu_to_be64((bfa_fcs_lport_get_pwwn(port->fcs_port))); |
1037 | fc_host_max_npiv_vports(host) = bfa_lps_get_max_vport(bfa: &bfad->bfa); |
1038 | |
1039 | fc_host_supported_classes(host) = FC_COS_CLASS3; |
1040 | |
1041 | memset(fc_host_supported_fc4s(host), 0, |
1042 | sizeof(fc_host_supported_fc4s(host))); |
1043 | if (supported_fc4s & BFA_LPORT_ROLE_FCP_IM) |
1044 | /* For FCP type 0x08 */ |
1045 | fc_host_supported_fc4s(host)[2] = 1; |
1046 | /* For fibre channel services type 0x20 */ |
1047 | fc_host_supported_fc4s(host)[7] = 1; |
1048 | |
1049 | strscpy(p: symname, q: bfad->bfa_fcs.fabric.bport.port_cfg.sym_name.symname, |
1050 | BFA_SYMNAME_MAXLEN); |
1051 | sprintf(fc_host_symbolic_name(host), fmt: "%s" , symname); |
1052 | |
1053 | fc_host_supported_speeds(host) = bfad_im_supported_speeds(bfa: &bfad->bfa); |
1054 | fc_host_maxframe_size(host) = fcport->cfg.maxfrsize; |
1055 | } |
1056 | |
1057 | static void |
1058 | bfad_im_fc_rport_add(struct bfad_im_port_s *im_port, struct bfad_itnim_s *itnim) |
1059 | { |
1060 | struct fc_rport_identifiers rport_ids; |
1061 | struct fc_rport *fc_rport; |
1062 | struct bfad_itnim_data_s *itnim_data; |
1063 | |
1064 | rport_ids.node_name = |
1065 | cpu_to_be64(bfa_fcs_itnim_get_nwwn(&itnim->fcs_itnim)); |
1066 | rport_ids.port_name = |
1067 | cpu_to_be64(bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); |
1068 | rport_ids.port_id = |
1069 | bfa_hton3b(bfa_fcs_itnim_get_fcid(&itnim->fcs_itnim)); |
1070 | rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; |
1071 | |
1072 | itnim->fc_rport = fc_rport = |
1073 | fc_remote_port_add(shost: im_port->shost, channel: 0, ids: &rport_ids); |
1074 | |
1075 | if (!fc_rport) |
1076 | return; |
1077 | |
1078 | fc_rport->maxframe_size = |
1079 | bfa_fcs_itnim_get_maxfrsize(itnim: &itnim->fcs_itnim); |
1080 | fc_rport->supported_classes = bfa_fcs_itnim_get_cos(itnim: &itnim->fcs_itnim); |
1081 | |
1082 | itnim_data = fc_rport->dd_data; |
1083 | itnim_data->itnim = itnim; |
1084 | |
1085 | rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET; |
1086 | |
1087 | if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) |
1088 | fc_remote_port_rolechg(rport: fc_rport, roles: rport_ids.roles); |
1089 | |
1090 | if ((fc_rport->scsi_target_id != -1) |
1091 | && (fc_rport->scsi_target_id < MAX_FCP_TARGET)) |
1092 | itnim->scsi_tgt_id = fc_rport->scsi_target_id; |
1093 | |
1094 | itnim->channel = fc_rport->channel; |
1095 | |
1096 | return; |
1097 | } |
1098 | |
1099 | /* |
1100 | * Work queue handler using FC transport service |
1101 | * Context: kernel |
1102 | */ |
1103 | static void |
1104 | bfad_im_itnim_work_handler(struct work_struct *work) |
1105 | { |
1106 | struct bfad_itnim_s *itnim = container_of(work, struct bfad_itnim_s, |
1107 | itnim_work); |
1108 | struct bfad_im_s *im = itnim->im; |
1109 | struct bfad_s *bfad = im->bfad; |
1110 | struct bfad_im_port_s *im_port; |
1111 | unsigned long flags; |
1112 | struct fc_rport *fc_rport; |
1113 | wwn_t wwpn; |
1114 | u32 fcid; |
1115 | char wwpn_str[32], fcid_str[16]; |
1116 | |
1117 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
1118 | im_port = itnim->im_port; |
1119 | bfa_trc(bfad, itnim->state); |
1120 | switch (itnim->state) { |
1121 | case ITNIM_STATE_ONLINE: |
1122 | if (!itnim->fc_rport) { |
1123 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1124 | bfad_im_fc_rport_add(im_port, itnim); |
1125 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
1126 | wwpn = bfa_fcs_itnim_get_pwwn(itnim: &itnim->fcs_itnim); |
1127 | fcid = bfa_fcs_itnim_get_fcid(itnim: &itnim->fcs_itnim); |
1128 | wwn2str(wwn_str: wwpn_str, wwn: wwpn); |
1129 | fcid2str(fcid_str, fcid); |
1130 | list_add_tail(new: &itnim->list_entry, |
1131 | head: &im_port->itnim_mapped_list); |
1132 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
1133 | "ITNIM ONLINE Target: %d:0:%d " |
1134 | "FCID: %s WWPN: %s\n" , |
1135 | im_port->shost->host_no, |
1136 | itnim->scsi_tgt_id, |
1137 | fcid_str, wwpn_str); |
1138 | } else { |
1139 | printk(KERN_WARNING |
1140 | "%s: itnim %llx is already in online state\n" , |
1141 | __func__, |
1142 | bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim)); |
1143 | } |
1144 | |
1145 | break; |
1146 | case ITNIM_STATE_OFFLINE_PENDING: |
1147 | itnim->state = ITNIM_STATE_OFFLINE; |
1148 | if (itnim->fc_rport) { |
1149 | fc_rport = itnim->fc_rport; |
1150 | ((struct bfad_itnim_data_s *) |
1151 | fc_rport->dd_data)->itnim = NULL; |
1152 | itnim->fc_rport = NULL; |
1153 | if (!(im_port->port->flags & BFAD_PORT_DELETE)) { |
1154 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1155 | fc_rport->dev_loss_tmo = |
1156 | bfa_fcpim_path_tov_get(bfa: &bfad->bfa) + 1; |
1157 | fc_remote_port_delete(rport: fc_rport); |
1158 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
1159 | } |
1160 | wwpn = bfa_fcs_itnim_get_pwwn(itnim: &itnim->fcs_itnim); |
1161 | fcid = bfa_fcs_itnim_get_fcid(itnim: &itnim->fcs_itnim); |
1162 | wwn2str(wwn_str: wwpn_str, wwn: wwpn); |
1163 | fcid2str(fcid_str, fcid); |
1164 | list_del(entry: &itnim->list_entry); |
1165 | BFA_LOG(KERN_INFO, bfad, bfa_log_level, |
1166 | "ITNIM OFFLINE Target: %d:0:%d " |
1167 | "FCID: %s WWPN: %s\n" , |
1168 | im_port->shost->host_no, |
1169 | itnim->scsi_tgt_id, |
1170 | fcid_str, wwpn_str); |
1171 | } |
1172 | break; |
1173 | case ITNIM_STATE_FREE: |
1174 | if (itnim->fc_rport) { |
1175 | fc_rport = itnim->fc_rport; |
1176 | ((struct bfad_itnim_data_s *) |
1177 | fc_rport->dd_data)->itnim = NULL; |
1178 | itnim->fc_rport = NULL; |
1179 | if (!(im_port->port->flags & BFAD_PORT_DELETE)) { |
1180 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1181 | fc_rport->dev_loss_tmo = |
1182 | bfa_fcpim_path_tov_get(bfa: &bfad->bfa) + 1; |
1183 | fc_remote_port_delete(rport: fc_rport); |
1184 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
1185 | } |
1186 | list_del(entry: &itnim->list_entry); |
1187 | } |
1188 | |
1189 | kfree(objp: itnim); |
1190 | break; |
1191 | default: |
1192 | WARN_ON(1); |
1193 | break; |
1194 | } |
1195 | |
1196 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1197 | } |
1198 | |
1199 | /* |
1200 | * Scsi_Host template entry, queue a SCSI command to the BFAD. |
1201 | */ |
1202 | static int bfad_im_queuecommand_lck(struct scsi_cmnd *cmnd) |
1203 | { |
1204 | void (*done)(struct scsi_cmnd *) = scsi_done; |
1205 | struct bfad_im_port_s *im_port = |
1206 | (struct bfad_im_port_s *) cmnd->device->host->hostdata[0]; |
1207 | struct bfad_s *bfad = im_port->bfad; |
1208 | struct bfad_itnim_data_s *itnim_data = cmnd->device->hostdata; |
1209 | struct bfad_itnim_s *itnim; |
1210 | struct bfa_ioim_s *hal_io; |
1211 | unsigned long flags; |
1212 | int rc; |
1213 | int sg_cnt = 0; |
1214 | struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); |
1215 | |
1216 | rc = fc_remote_port_chkready(rport); |
1217 | if (rc) { |
1218 | cmnd->result = rc; |
1219 | done(cmnd); |
1220 | return 0; |
1221 | } |
1222 | |
1223 | if (bfad->bfad_flags & BFAD_EEH_BUSY) { |
1224 | if (bfad->bfad_flags & BFAD_EEH_PCI_CHANNEL_IO_PERM_FAILURE) |
1225 | cmnd->result = DID_NO_CONNECT << 16; |
1226 | else |
1227 | cmnd->result = DID_REQUEUE << 16; |
1228 | done(cmnd); |
1229 | return 0; |
1230 | } |
1231 | |
1232 | sg_cnt = scsi_dma_map(cmd: cmnd); |
1233 | if (sg_cnt < 0) |
1234 | return SCSI_MLQUEUE_HOST_BUSY; |
1235 | |
1236 | spin_lock_irqsave(&bfad->bfad_lock, flags); |
1237 | if (!(bfad->bfad_flags & BFAD_HAL_START_DONE)) { |
1238 | printk(KERN_WARNING |
1239 | "bfad%d, queuecommand %p %x failed, BFA stopped\n" , |
1240 | bfad->inst_no, cmnd, cmnd->cmnd[0]); |
1241 | cmnd->result = DID_NO_CONNECT << 16; |
1242 | goto out_fail_cmd; |
1243 | } |
1244 | |
1245 | |
1246 | itnim = itnim_data->itnim; |
1247 | if (!itnim) { |
1248 | cmnd->result = DID_IMM_RETRY << 16; |
1249 | goto out_fail_cmd; |
1250 | } |
1251 | |
1252 | hal_io = bfa_ioim_alloc(bfa: &bfad->bfa, dio: (struct bfad_ioim_s *) cmnd, |
1253 | itnim: itnim->bfa_itnim, nsgles: sg_cnt); |
1254 | if (!hal_io) { |
1255 | printk(KERN_WARNING "hal_io failure\n" ); |
1256 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1257 | scsi_dma_unmap(cmd: cmnd); |
1258 | return SCSI_MLQUEUE_HOST_BUSY; |
1259 | } |
1260 | |
1261 | cmnd->host_scribble = (char *)hal_io; |
1262 | bfa_ioim_start(ioim: hal_io); |
1263 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1264 | |
1265 | return 0; |
1266 | |
1267 | out_fail_cmd: |
1268 | spin_unlock_irqrestore(lock: &bfad->bfad_lock, flags); |
1269 | scsi_dma_unmap(cmd: cmnd); |
1270 | if (done) |
1271 | done(cmnd); |
1272 | |
1273 | return 0; |
1274 | } |
1275 | |
1276 | static DEF_SCSI_QCMD(bfad_im_queuecommand) |
1277 | |
1278 | void |
1279 | bfad_rport_online_wait(struct bfad_s *bfad) |
1280 | { |
1281 | int i; |
1282 | int rport_delay = 10; |
1283 | |
1284 | for (i = 0; !(bfad->bfad_flags & BFAD_PORT_ONLINE) |
1285 | && i < bfa_linkup_delay; i++) { |
1286 | set_current_state(TASK_UNINTERRUPTIBLE); |
1287 | schedule_timeout(HZ); |
1288 | } |
1289 | |
1290 | if (bfad->bfad_flags & BFAD_PORT_ONLINE) { |
1291 | rport_delay = rport_delay < bfa_linkup_delay ? |
1292 | rport_delay : bfa_linkup_delay; |
1293 | for (i = 0; !(bfad->bfad_flags & BFAD_RPORT_ONLINE) |
1294 | && i < rport_delay; i++) { |
1295 | set_current_state(TASK_UNINTERRUPTIBLE); |
1296 | schedule_timeout(HZ); |
1297 | } |
1298 | |
1299 | if (rport_delay > 0 && (bfad->bfad_flags & BFAD_RPORT_ONLINE)) { |
1300 | set_current_state(TASK_UNINTERRUPTIBLE); |
1301 | schedule_timeout(timeout: rport_delay * HZ); |
1302 | } |
1303 | } |
1304 | } |
1305 | |
1306 | int |
1307 | bfad_get_linkup_delay(struct bfad_s *bfad) |
1308 | { |
1309 | u8 nwwns = 0; |
1310 | wwn_t wwns[BFA_PREBOOT_BOOTLUN_MAX]; |
1311 | int linkup_delay; |
1312 | |
1313 | /* |
1314 | * Querying for the boot target port wwns |
1315 | * -- read from boot information in flash. |
1316 | * If nwwns > 0 => boot over SAN and set linkup_delay = 30 |
1317 | * else => local boot machine set linkup_delay = 0 |
1318 | */ |
1319 | |
1320 | bfa_iocfc_get_bootwwns(bfa: &bfad->bfa, nwwns: &nwwns, wwns); |
1321 | |
1322 | if (nwwns > 0) |
1323 | /* If Boot over SAN set linkup_delay = 30sec */ |
1324 | linkup_delay = 30; |
1325 | else |
1326 | /* If local boot; no linkup_delay */ |
1327 | linkup_delay = 0; |
1328 | |
1329 | return linkup_delay; |
1330 | } |
1331 | |