diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_mid.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mid.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c new file mode 100644 index 000000000000..54dc415d8b53 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_mid.c | |||
@@ -0,0 +1,497 @@ | |||
1 | /* | ||
2 | * QLOGIC LINUX SOFTWARE | ||
3 | * | ||
4 | * QLogic ISP2x00 device driver for Linux 2.6.x | ||
5 | * Copyright (C) 2003-2005 QLogic Corporation | ||
6 | * (www.qlogic.com) | ||
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 as published by the | ||
10 | * Free Software Foundation; either version 2, or (at your option) any | ||
11 | * later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but | ||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
16 | * General Public License for more details. | ||
17 | * | ||
18 | */ | ||
19 | #include "qla_def.h" | ||
20 | |||
21 | #include <linux/version.h> | ||
22 | #include <linux/moduleparam.h> | ||
23 | #include <linux/vmalloc.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | #include <linux/list.h> | ||
26 | |||
27 | #include <scsi/scsi_tcq.h> | ||
28 | #include <scsi/scsicam.h> | ||
29 | #include <linux/delay.h> | ||
30 | |||
31 | void qla2x00_vp_stop_timer(scsi_qla_host_t *); | ||
32 | |||
33 | void | ||
34 | qla2x00_vp_stop_timer(scsi_qla_host_t *vha) | ||
35 | { | ||
36 | if (vha->parent && vha->timer_active) { | ||
37 | del_timer_sync(&vha->timer); | ||
38 | vha->timer_active = 0; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | uint32_t | ||
43 | qla24xx_allocate_vp_id(scsi_qla_host_t *vha) | ||
44 | { | ||
45 | uint32_t vp_id; | ||
46 | scsi_qla_host_t *ha = vha->parent; | ||
47 | |||
48 | /* Find an empty slot and assign an vp_id */ | ||
49 | down(&ha->vport_sem); | ||
50 | vp_id = find_first_zero_bit((unsigned long *)ha->vp_idx_map, | ||
51 | MAX_MULTI_ID_FABRIC); | ||
52 | if (vp_id > MAX_MULTI_ID_FABRIC) { | ||
53 | DEBUG15(printk ("vp_id %d is bigger than MAX_MULTI_ID_FABRID\n", | ||
54 | vp_id)); | ||
55 | up(&ha->vport_sem); | ||
56 | return vp_id; | ||
57 | } | ||
58 | |||
59 | set_bit(vp_id, (unsigned long *)ha->vp_idx_map); | ||
60 | ha->num_vhosts++; | ||
61 | vha->vp_idx = vp_id; | ||
62 | list_add_tail(&vha->vp_list, &ha->vp_list); | ||
63 | up(&ha->vport_sem); | ||
64 | return vp_id; | ||
65 | } | ||
66 | |||
67 | void | ||
68 | qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) | ||
69 | { | ||
70 | uint16_t vp_id; | ||
71 | scsi_qla_host_t *ha = vha->parent; | ||
72 | |||
73 | down(&ha->vport_sem); | ||
74 | vp_id = vha->vp_idx; | ||
75 | ha->num_vhosts--; | ||
76 | clear_bit(vp_id, (unsigned long *)ha->vp_idx_map); | ||
77 | list_del(&vha->vp_list); | ||
78 | up(&ha->vport_sem); | ||
79 | } | ||
80 | |||
81 | scsi_qla_host_t * | ||
82 | qla24xx_find_vhost_by_name(scsi_qla_host_t *ha, uint8_t *port_name) | ||
83 | { | ||
84 | scsi_qla_host_t *vha; | ||
85 | |||
86 | /* Locate matching device in database. */ | ||
87 | list_for_each_entry(vha, &ha->vp_list, vp_list) { | ||
88 | if (!memcmp(port_name, vha->port_name, WWN_SIZE)) | ||
89 | return vha; | ||
90 | } | ||
91 | return NULL; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * qla2x00_mark_vp_devices_dead | ||
96 | * Updates fcport state when device goes offline. | ||
97 | * | ||
98 | * Input: | ||
99 | * ha = adapter block pointer. | ||
100 | * fcport = port structure pointer. | ||
101 | * | ||
102 | * Return: | ||
103 | * None. | ||
104 | * | ||
105 | * Context: | ||
106 | */ | ||
107 | void | ||
108 | qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) | ||
109 | { | ||
110 | fc_port_t *fcport; | ||
111 | scsi_qla_host_t *pha = to_qla_parent(vha); | ||
112 | |||
113 | list_for_each_entry(fcport, &pha->fcports, list) { | ||
114 | if (fcport->vp_idx != vha->vp_idx) | ||
115 | continue; | ||
116 | |||
117 | DEBUG15(printk("scsi(%ld): Marking port dead, " | ||
118 | "loop_id=0x%04x :%x\n", | ||
119 | vha->host_no, fcport->loop_id, fcport->vp_idx)); | ||
120 | |||
121 | atomic_set(&fcport->state, FCS_DEVICE_DEAD); | ||
122 | qla2x00_mark_device_lost(vha, fcport, 0, 0); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | int | ||
127 | qla24xx_disable_vp(scsi_qla_host_t *vha) | ||
128 | { | ||
129 | int ret; | ||
130 | |||
131 | ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); | ||
132 | atomic_set(&vha->loop_state, LOOP_DOWN); | ||
133 | atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); | ||
134 | |||
135 | /* Delete all vp's fcports from parent's list */ | ||
136 | qla2x00_mark_vp_devices_dead(vha); | ||
137 | atomic_set(&vha->vp_state, VP_FAILED); | ||
138 | vha->flags.management_server_logged_in = 0; | ||
139 | if (ret == QLA_SUCCESS) { | ||
140 | fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED); | ||
141 | } else { | ||
142 | fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); | ||
143 | return -1; | ||
144 | } | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | int | ||
149 | qla24xx_enable_vp(scsi_qla_host_t *vha) | ||
150 | { | ||
151 | int ret; | ||
152 | scsi_qla_host_t *ha = vha->parent; | ||
153 | |||
154 | /* Check if physical ha port is Up */ | ||
155 | if (atomic_read(&ha->loop_state) == LOOP_DOWN || | ||
156 | atomic_read(&ha->loop_state) == LOOP_DEAD ) { | ||
157 | vha->vp_err_state = VP_ERR_PORTDWN; | ||
158 | fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN); | ||
159 | goto enable_failed; | ||
160 | } | ||
161 | |||
162 | /* Initialize the new vport unless it is a persistent port */ | ||
163 | down(&ha->vport_sem); | ||
164 | ret = qla24xx_modify_vp_config(vha); | ||
165 | up(&ha->vport_sem); | ||
166 | |||
167 | if (ret != QLA_SUCCESS) { | ||
168 | fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED); | ||
169 | goto enable_failed; | ||
170 | } | ||
171 | |||
172 | DEBUG15(qla_printk(KERN_INFO, ha, | ||
173 | "Virtual port with id: %d - Enabled\n", vha->vp_idx)); | ||
174 | return 0; | ||
175 | |||
176 | enable_failed: | ||
177 | DEBUG15(qla_printk(KERN_INFO, ha, | ||
178 | "Virtual port with id: %d - Disabled\n", vha->vp_idx)); | ||
179 | return 1; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * qla24xx_modify_vport() - Modifies the virtual fabric port's configuration | ||
184 | * @ha: HA context | ||
185 | * @vp: pointer to buffer of virtual port parameters. | ||
186 | * @ret_code: return error code: | ||
187 | * | ||
188 | * Returns the virtual port id, or MAX_VSAN_ID, if couldn't create. | ||
189 | */ | ||
190 | uint32_t | ||
191 | qla24xx_modify_vhba(scsi_qla_host_t *ha, vport_params_t *vp, uint32_t *vp_id) | ||
192 | { | ||
193 | scsi_qla_host_t *vha; | ||
194 | |||
195 | vha = qla24xx_find_vhost_by_name(ha, vp->port_name); | ||
196 | if (!vha) { | ||
197 | *vp_id = MAX_NUM_VPORT_LOOP; | ||
198 | return VP_RET_CODE_WWPN; | ||
199 | } | ||
200 | |||
201 | if (qla24xx_enable_vp(vha)) { | ||
202 | scsi_host_put(vha->host); | ||
203 | qla2x00_mem_free(vha); | ||
204 | *vp_id = MAX_NUM_VPORT_LOOP; | ||
205 | return VP_RET_CODE_RESOURCES; | ||
206 | } | ||
207 | |||
208 | *vp_id = vha->vp_idx; | ||
209 | return VP_RET_CODE_OK; | ||
210 | } | ||
211 | |||
212 | void | ||
213 | qla24xx_configure_vp(scsi_qla_host_t *vha) | ||
214 | { | ||
215 | struct fc_vport *fc_vport; | ||
216 | int ret; | ||
217 | |||
218 | fc_vport = vha->fc_vport; | ||
219 | |||
220 | DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n", | ||
221 | vha->host_no, __func__)); | ||
222 | ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx); | ||
223 | if (ret != QLA_SUCCESS) { | ||
224 | DEBUG15(qla_printk(KERN_ERR, vha, "Failed to enable receiving" | ||
225 | " of RSCN requests: 0x%x\n", ret)); | ||
226 | return; | ||
227 | } else { | ||
228 | /* Corresponds to SCR enabled */ | ||
229 | clear_bit(VP_SCR_NEEDED, &vha->vp_flags); | ||
230 | } | ||
231 | |||
232 | vha->flags.online = 1; | ||
233 | if (qla24xx_configure_vhba(vha)) | ||
234 | return; | ||
235 | |||
236 | atomic_set(&vha->vp_state, VP_ACTIVE); | ||
237 | fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); | ||
238 | } | ||
239 | |||
240 | void | ||
241 | qla2x00_alert_all_vps(scsi_qla_host_t *ha, uint16_t *mb) | ||
242 | { | ||
243 | int i, vp_idx_matched; | ||
244 | scsi_qla_host_t *vha; | ||
245 | |||
246 | if (ha->parent) | ||
247 | return; | ||
248 | |||
249 | i = find_next_bit((unsigned long *)ha->vp_idx_map, | ||
250 | MAX_MULTI_ID_FABRIC + 1, 1); | ||
251 | for (;i <= MAX_MULTI_ID_FABRIC; | ||
252 | i = find_next_bit((unsigned long *)ha->vp_idx_map, | ||
253 | MAX_MULTI_ID_FABRIC + 1, i + 1)) { | ||
254 | vp_idx_matched = 0; | ||
255 | |||
256 | list_for_each_entry(vha, &ha->vp_list, vp_list) { | ||
257 | if (i == vha->vp_idx) { | ||
258 | vp_idx_matched = 1; | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (vp_idx_matched) { | ||
264 | switch (mb[0]) { | ||
265 | case MBA_LIP_OCCURRED: | ||
266 | case MBA_LOOP_UP: | ||
267 | case MBA_LOOP_DOWN: | ||
268 | case MBA_LIP_RESET: | ||
269 | case MBA_POINT_TO_POINT: | ||
270 | case MBA_CHG_IN_CONNECTION: | ||
271 | case MBA_PORT_UPDATE: | ||
272 | case MBA_RSCN_UPDATE: | ||
273 | DEBUG15(printk("scsi(%ld)%s: Async_event for" | ||
274 | " VP[%d], mb = 0x%x, vha=%p\n", | ||
275 | vha->host_no, __func__,i, *mb, vha)); | ||
276 | qla2x00_async_event(vha, mb); | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | void | ||
284 | qla2x00_vp_abort_isp(scsi_qla_host_t *vha) | ||
285 | { | ||
286 | /* | ||
287 | * Physical port will do most of the abort and recovery work. We can | ||
288 | * just treat it as a loop down | ||
289 | */ | ||
290 | if (atomic_read(&vha->loop_state) != LOOP_DOWN) { | ||
291 | atomic_set(&vha->loop_state, LOOP_DOWN); | ||
292 | qla2x00_mark_all_devices_lost(vha, 0); | ||
293 | } else { | ||
294 | if (!atomic_read(&vha->loop_down_timer)) | ||
295 | atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); | ||
296 | } | ||
297 | |||
298 | DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n", | ||
299 | vha->host_no, vha->vp_idx)); | ||
300 | qla24xx_enable_vp(vha); | ||
301 | } | ||
302 | |||
303 | int | ||
304 | qla2x00_do_dpc_vp(scsi_qla_host_t *vha) | ||
305 | { | ||
306 | if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) { | ||
307 | /* VP acquired. complete port configuration */ | ||
308 | qla24xx_configure_vp(vha); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | if (test_and_clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) | ||
313 | qla2x00_vp_abort_isp(vha); | ||
314 | |||
315 | if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) && | ||
316 | (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) { | ||
317 | clear_bit(RESET_ACTIVE, &vha->dpc_flags); | ||
318 | } | ||
319 | |||
320 | if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { | ||
321 | if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) { | ||
322 | qla2x00_loop_resync(vha); | ||
323 | clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | void | ||
331 | qla2x00_do_dpc_all_vps(scsi_qla_host_t *ha) | ||
332 | { | ||
333 | int ret; | ||
334 | int i, vp_idx_matched; | ||
335 | scsi_qla_host_t *vha; | ||
336 | |||
337 | if (ha->parent) | ||
338 | return; | ||
339 | if (list_empty(&ha->vp_list)) | ||
340 | return; | ||
341 | |||
342 | clear_bit(VP_DPC_NEEDED, &ha->dpc_flags); | ||
343 | |||
344 | i = find_next_bit((unsigned long *)ha->vp_idx_map, | ||
345 | MAX_MULTI_ID_FABRIC + 1, 1); | ||
346 | for (;i <= MAX_MULTI_ID_FABRIC; | ||
347 | i = find_next_bit((unsigned long *)ha->vp_idx_map, | ||
348 | MAX_MULTI_ID_FABRIC + 1, i + 1)) { | ||
349 | vp_idx_matched = 0; | ||
350 | |||
351 | list_for_each_entry(vha, &ha->vp_list, vp_list) { | ||
352 | if (i == vha->vp_idx) { | ||
353 | vp_idx_matched = 1; | ||
354 | break; | ||
355 | } | ||
356 | } | ||
357 | |||
358 | if (vp_idx_matched) | ||
359 | ret = qla2x00_do_dpc_vp(vha); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | int | ||
364 | qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) | ||
365 | { | ||
366 | scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; | ||
367 | scsi_qla_host_t *vha; | ||
368 | uint8_t port_name[WWN_SIZE]; | ||
369 | |||
370 | if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR) | ||
371 | return VPCERR_UNSUPPORTED; | ||
372 | |||
373 | /* Check up the F/W and H/W support NPIV */ | ||
374 | if (!ha->flags.npiv_supported) | ||
375 | return VPCERR_UNSUPPORTED; | ||
376 | |||
377 | /* Check up whether npiv supported switch presented */ | ||
378 | if (!(ha->switch_cap & FLOGI_MID_SUPPORT)) | ||
379 | return VPCERR_NO_FABRIC_SUPP; | ||
380 | |||
381 | /* Check up unique WWPN */ | ||
382 | u64_to_wwn(fc_vport->port_name, port_name); | ||
383 | vha = qla24xx_find_vhost_by_name(ha, port_name); | ||
384 | if (vha) | ||
385 | return VPCERR_BAD_WWN; | ||
386 | |||
387 | /* Check up max-npiv-supports */ | ||
388 | if (ha->num_vhosts > ha->max_npiv_vports) { | ||
389 | DEBUG15(printk("scsi(%ld): num_vhosts %d is bigger than " | ||
390 | "max_npv_vports %d.\n", ha->host_no, | ||
391 | (uint16_t) ha->num_vhosts, (int) ha->max_npiv_vports)); | ||
392 | return VPCERR_UNSUPPORTED; | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | scsi_qla_host_t * | ||
398 | qla24xx_create_vhost(struct fc_vport *fc_vport) | ||
399 | { | ||
400 | scsi_qla_host_t *ha = (scsi_qla_host_t *) fc_vport->shost->hostdata; | ||
401 | scsi_qla_host_t *vha; | ||
402 | struct Scsi_Host *host; | ||
403 | |||
404 | host = scsi_host_alloc(&qla24xx_driver_template, | ||
405 | sizeof(scsi_qla_host_t)); | ||
406 | if (!host) { | ||
407 | printk(KERN_WARNING | ||
408 | "qla2xxx: scsi_host_alloc() failed for vport\n"); | ||
409 | return(NULL); | ||
410 | } | ||
411 | |||
412 | vha = (scsi_qla_host_t *)host->hostdata; | ||
413 | |||
414 | /* clone the parent hba */ | ||
415 | memcpy(vha, ha, sizeof (scsi_qla_host_t)); | ||
416 | |||
417 | fc_vport->dd_data = vha; | ||
418 | |||
419 | vha->node_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); | ||
420 | if (!vha->node_name) | ||
421 | goto create_vhost_failed_1; | ||
422 | |||
423 | vha->port_name = kmalloc(WWN_SIZE * sizeof(char), GFP_KERNEL); | ||
424 | if (!vha->port_name) | ||
425 | goto create_vhost_failed_2; | ||
426 | |||
427 | /* New host info */ | ||
428 | u64_to_wwn(fc_vport->node_name, vha->node_name); | ||
429 | u64_to_wwn(fc_vport->port_name, vha->port_name); | ||
430 | |||
431 | vha->host = host; | ||
432 | vha->host_no = host->host_no; | ||
433 | vha->parent = ha; | ||
434 | vha->fc_vport = fc_vport; | ||
435 | vha->device_flags = 0; | ||
436 | vha->instance = num_hosts; | ||
437 | vha->vp_idx = qla24xx_allocate_vp_id(vha); | ||
438 | if (vha->vp_idx > ha->max_npiv_vports) { | ||
439 | DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", | ||
440 | vha->host_no)); | ||
441 | goto create_vhost_failed_3; | ||
442 | } | ||
443 | vha->mgmt_svr_loop_id = 10 + vha->vp_idx; | ||
444 | |||
445 | init_MUTEX(&vha->mbx_cmd_sem); | ||
446 | init_MUTEX_LOCKED(&vha->mbx_intr_sem); | ||
447 | |||
448 | INIT_LIST_HEAD(&vha->list); | ||
449 | INIT_LIST_HEAD(&vha->fcports); | ||
450 | INIT_LIST_HEAD(&vha->vp_fcports); | ||
451 | |||
452 | vha->dpc_flags = 0L; | ||
453 | set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); | ||
454 | set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); | ||
455 | |||
456 | /* | ||
457 | * To fix the issue of processing a parent's RSCN for the vport before | ||
458 | * its SCR is complete. | ||
459 | */ | ||
460 | set_bit(VP_SCR_NEEDED, &vha->vp_flags); | ||
461 | atomic_set(&vha->loop_state, LOOP_DOWN); | ||
462 | atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); | ||
463 | |||
464 | qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL); | ||
465 | |||
466 | host->can_queue = vha->request_q_length + 128; | ||
467 | host->this_id = 255; | ||
468 | host->cmd_per_lun = 3; | ||
469 | host->max_cmd_len = MAX_CMDSZ; | ||
470 | host->max_channel = MAX_BUSES - 1; | ||
471 | host->max_lun = MAX_LUNS; | ||
472 | host->unique_id = vha->instance; | ||
473 | host->max_id = MAX_TARGETS_2200; | ||
474 | host->transportt = qla2xxx_transport_vport_template; | ||
475 | |||
476 | DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n", | ||
477 | vha->host_no, vha)); | ||
478 | |||
479 | vha->flags.init_done = 1; | ||
480 | num_hosts++; | ||
481 | |||
482 | down(&ha->vport_sem); | ||
483 | set_bit(vha->vp_idx, (unsigned long *)ha->vp_idx_map); | ||
484 | ha->cur_vport_count++; | ||
485 | up(&ha->vport_sem); | ||
486 | |||
487 | return vha; | ||
488 | |||
489 | create_vhost_failed_3: | ||
490 | kfree(vha->port_name); | ||
491 | |||
492 | create_vhost_failed_2: | ||
493 | kfree(vha->node_name); | ||
494 | |||
495 | create_vhost_failed_1: | ||
496 | return NULL; | ||
497 | } | ||