Commit | Line | Data |
---|---|---|
2496af39 MED |
1 | /* |
2 | * linux/drivers/message/fusion/mptfc.c | |
3 | * For use with LSI Logic PCI chip/adapter(s) | |
4 | * running LSI Logic Fusion MPT (Message Passing Technology) firmware. | |
5 | * | |
6 | * Copyright (c) 1999-2005 LSI Logic Corporation | |
7 | * (mailto:mpt_linux_developer@lsil.com) | |
8 | * | |
9 | */ | |
10 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
11 | /* | |
12 | This program is free software; you can redistribute it and/or modify | |
13 | it under the terms of the GNU General Public License as published by | |
14 | the Free Software Foundation; version 2 of the License. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | GNU General Public License for more details. | |
20 | ||
21 | NO WARRANTY | |
22 | THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR | |
23 | CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT | |
24 | LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, | |
25 | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is | |
26 | solely responsible for determining the appropriateness of using and | |
27 | distributing the Program and assumes all risks associated with its | |
28 | exercise of rights under this Agreement, including but not limited to | |
29 | the risks and costs of program errors, damage to or loss of data, | |
30 | programs or equipment, and unavailability or interruption of operations. | |
31 | ||
32 | DISCLAIMER OF LIABILITY | |
33 | NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY | |
34 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
35 | DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND | |
36 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
37 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
38 | USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED | |
39 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES | |
40 | ||
41 | You should have received a copy of the GNU General Public License | |
42 | along with this program; if not, write to the Free Software | |
43 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
44 | */ | |
45 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
46 | #include "linux_compat.h" /* linux-2.6 tweaks */ | |
47 | #include <linux/module.h> | |
48 | #include <linux/kernel.h> | |
49 | #include <linux/init.h> | |
50 | #include <linux/errno.h> | |
51 | #include <linux/kdev_t.h> | |
52 | #include <linux/blkdev.h> | |
53 | #include <linux/delay.h> /* for mdelay */ | |
54 | #include <linux/interrupt.h> /* needed for in_interrupt() proto */ | |
55 | #include <linux/reboot.h> /* notifier code */ | |
56 | #include <linux/sched.h> | |
57 | #include <linux/workqueue.h> | |
05e8ec17 | 58 | #include <linux/sort.h> |
2496af39 MED |
59 | |
60 | #include <scsi/scsi.h> | |
61 | #include <scsi/scsi_cmnd.h> | |
62 | #include <scsi/scsi_device.h> | |
63 | #include <scsi/scsi_host.h> | |
64 | #include <scsi/scsi_tcq.h> | |
05e8ec17 | 65 | #include <scsi/scsi_transport_fc.h> |
2496af39 MED |
66 | |
67 | #include "mptbase.h" | |
68 | #include "mptscsih.h" | |
69 | ||
70 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
71 | #define my_NAME "Fusion MPT FC Host driver" | |
72 | #define my_VERSION MPT_LINUX_VERSION_COMMON | |
73 | #define MYNAM "mptfc" | |
74 | ||
75 | MODULE_AUTHOR(MODULEAUTHOR); | |
76 | MODULE_DESCRIPTION(my_NAME); | |
77 | MODULE_LICENSE("GPL"); | |
78 | ||
79 | /* Command line args */ | |
80 | static int mpt_pq_filter = 0; | |
81 | module_param(mpt_pq_filter, int, 0); | |
82 | MODULE_PARM_DESC(mpt_pq_filter, " Enable peripheral qualifier filter: enable=1 (default=0)"); | |
83 | ||
05e8ec17 MR |
84 | #define MPTFC_DEV_LOSS_TMO (60) |
85 | static int mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */ | |
86 | module_param(mptfc_dev_loss_tmo, int, 0); | |
87 | MODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the " | |
88 | " transport to wait for an rport to " | |
89 | " return following a device loss event." | |
90 | " Default=60."); | |
91 | ||
2496af39 MED |
92 | static int mptfcDoneCtx = -1; |
93 | static int mptfcTaskCtx = -1; | |
94 | static int mptfcInternalCtx = -1; /* Used only for internal commands */ | |
95 | ||
05e8ec17 MR |
96 | int mptfc_slave_alloc(struct scsi_device *device); |
97 | static int mptfc_qcmd(struct scsi_cmnd *SCpnt, | |
98 | void (*done)(struct scsi_cmnd *)); | |
99 | ||
100 | static void mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout); | |
101 | static void __devexit mptfc_remove(struct pci_dev *pdev); | |
102 | ||
2496af39 | 103 | static struct scsi_host_template mptfc_driver_template = { |
f78496da | 104 | .module = THIS_MODULE, |
2496af39 MED |
105 | .proc_name = "mptfc", |
106 | .proc_info = mptscsih_proc_info, | |
107 | .name = "MPT FC Host", | |
108 | .info = mptscsih_info, | |
05e8ec17 | 109 | .queuecommand = mptfc_qcmd, |
c7c82987 | 110 | .target_alloc = mptscsih_target_alloc, |
05e8ec17 | 111 | .slave_alloc = mptfc_slave_alloc, |
2496af39 | 112 | .slave_configure = mptscsih_slave_configure, |
c7c82987 | 113 | .target_destroy = mptscsih_target_destroy, |
2496af39 | 114 | .slave_destroy = mptscsih_slave_destroy, |
6e3815ba | 115 | .change_queue_depth = mptscsih_change_queue_depth, |
2496af39 MED |
116 | .eh_abort_handler = mptscsih_abort, |
117 | .eh_device_reset_handler = mptscsih_dev_reset, | |
118 | .eh_bus_reset_handler = mptscsih_bus_reset, | |
119 | .eh_host_reset_handler = mptscsih_host_reset, | |
120 | .bios_param = mptscsih_bios_param, | |
121 | .can_queue = MPT_FC_CAN_QUEUE, | |
122 | .this_id = -1, | |
123 | .sg_tablesize = MPT_SCSI_SG_DEPTH, | |
124 | .max_sectors = 8192, | |
125 | .cmd_per_lun = 7, | |
126 | .use_clustering = ENABLE_CLUSTERING, | |
2496af39 MED |
127 | }; |
128 | ||
129 | /**************************************************************************** | |
130 | * Supported hardware | |
131 | */ | |
132 | ||
133 | static struct pci_device_id mptfc_pci_table[] = { | |
134 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909, | |
135 | PCI_ANY_ID, PCI_ANY_ID }, | |
136 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919, | |
137 | PCI_ANY_ID, PCI_ANY_ID }, | |
138 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929, | |
139 | PCI_ANY_ID, PCI_ANY_ID }, | |
140 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X, | |
141 | PCI_ANY_ID, PCI_ANY_ID }, | |
142 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X, | |
143 | PCI_ANY_ID, PCI_ANY_ID }, | |
3fadc59d MED |
144 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC939X, |
145 | PCI_ANY_ID, PCI_ANY_ID }, | |
146 | { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC949X, | |
147 | PCI_ANY_ID, PCI_ANY_ID }, | |
2496af39 MED |
148 | {0} /* Terminating entry */ |
149 | }; | |
150 | MODULE_DEVICE_TABLE(pci, mptfc_pci_table); | |
151 | ||
05e8ec17 MR |
152 | static struct scsi_transport_template *mptfc_transport_template = NULL; |
153 | ||
154 | struct fc_function_template mptfc_transport_functions = { | |
155 | .dd_fcrport_size = 8, | |
156 | .show_host_node_name = 1, | |
157 | .show_host_port_name = 1, | |
158 | .show_host_supported_classes = 1, | |
159 | .show_host_port_id = 1, | |
160 | .show_rport_supported_classes = 1, | |
161 | .show_starget_node_name = 1, | |
162 | .show_starget_port_name = 1, | |
163 | .show_starget_port_id = 1, | |
164 | .set_rport_dev_loss_tmo = mptfc_set_rport_loss_tmo, | |
165 | .show_rport_dev_loss_tmo = 1, | |
166 | ||
167 | }; | |
168 | ||
169 | /* FIXME! values controlling firmware RESCAN event | |
170 | * need to be set low to allow dev_loss_tmo to | |
171 | * work as expected. Currently, firmware doesn't | |
172 | * notify driver of RESCAN event until some number | |
173 | * of seconds elapse. This value can be set via | |
174 | * lsiutil. | |
175 | */ | |
176 | static void | |
177 | mptfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) | |
178 | { | |
179 | if (timeout > 0) | |
180 | rport->dev_loss_tmo = timeout; | |
181 | else | |
182 | rport->dev_loss_tmo = mptfc_dev_loss_tmo; | |
183 | } | |
184 | ||
185 | static int | |
186 | mptfc_FcDevPage0_cmp_func(const void *a, const void *b) | |
187 | { | |
188 | FCDevicePage0_t **aa = (FCDevicePage0_t **)a; | |
189 | FCDevicePage0_t **bb = (FCDevicePage0_t **)b; | |
190 | ||
191 | if ((*aa)->CurrentBus == (*bb)->CurrentBus) { | |
192 | if ((*aa)->CurrentTargetID == (*bb)->CurrentTargetID) | |
193 | return 0; | |
194 | if ((*aa)->CurrentTargetID < (*bb)->CurrentTargetID) | |
195 | return -1; | |
196 | return 1; | |
197 | } | |
198 | if ((*aa)->CurrentBus < (*bb)->CurrentBus) | |
199 | return -1; | |
200 | return 1; | |
201 | } | |
202 | ||
203 | static int | |
204 | mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, int ioc_port, | |
205 | void(*func)(MPT_ADAPTER *ioc,int channel, FCDevicePage0_t *arg)) | |
206 | { | |
207 | ConfigPageHeader_t hdr; | |
208 | CONFIGPARMS cfg; | |
209 | FCDevicePage0_t *ppage0_alloc, *fc; | |
210 | dma_addr_t page0_dma; | |
211 | int data_sz; | |
212 | int ii; | |
213 | ||
214 | FCDevicePage0_t *p0_array=NULL, *p_p0; | |
215 | FCDevicePage0_t **pp0_array=NULL, **p_pp0; | |
216 | ||
217 | int rc = -ENOMEM; | |
218 | U32 port_id = 0xffffff; | |
219 | int num_targ = 0; | |
220 | int max_bus = ioc->facts.MaxBuses; | |
221 | int max_targ = ioc->facts.MaxDevices; | |
222 | ||
223 | if (max_bus == 0 || max_targ == 0) | |
224 | goto out; | |
225 | ||
226 | data_sz = sizeof(FCDevicePage0_t) * max_bus * max_targ; | |
227 | p_p0 = p0_array = kzalloc(data_sz, GFP_KERNEL); | |
228 | if (!p0_array) | |
229 | goto out; | |
230 | ||
231 | data_sz = sizeof(FCDevicePage0_t *) * max_bus * max_targ; | |
232 | p_pp0 = pp0_array = kzalloc(data_sz, GFP_KERNEL); | |
233 | if (!pp0_array) | |
234 | goto out; | |
235 | ||
236 | do { | |
237 | /* Get FC Device Page 0 header */ | |
238 | hdr.PageVersion = 0; | |
239 | hdr.PageLength = 0; | |
240 | hdr.PageNumber = 0; | |
241 | hdr.PageType = MPI_CONFIG_PAGETYPE_FC_DEVICE; | |
242 | cfg.cfghdr.hdr = &hdr; | |
243 | cfg.physAddr = -1; | |
244 | cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; | |
245 | cfg.dir = 0; | |
246 | cfg.pageAddr = port_id; | |
247 | cfg.timeout = 0; | |
248 | ||
249 | if ((rc = mpt_config(ioc, &cfg)) != 0) | |
250 | break; | |
251 | ||
252 | if (hdr.PageLength <= 0) | |
253 | break; | |
254 | ||
255 | data_sz = hdr.PageLength * 4; | |
256 | ppage0_alloc = pci_alloc_consistent(ioc->pcidev, data_sz, | |
257 | &page0_dma); | |
258 | rc = -ENOMEM; | |
259 | if (!ppage0_alloc) | |
260 | break; | |
261 | ||
262 | cfg.physAddr = page0_dma; | |
263 | cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; | |
264 | ||
265 | if ((rc = mpt_config(ioc, &cfg)) == 0) { | |
266 | ppage0_alloc->PortIdentifier = | |
267 | le32_to_cpu(ppage0_alloc->PortIdentifier); | |
268 | ||
269 | ppage0_alloc->WWNN.Low = | |
270 | le32_to_cpu(ppage0_alloc->WWNN.Low); | |
271 | ||
272 | ppage0_alloc->WWNN.High = | |
273 | le32_to_cpu(ppage0_alloc->WWNN.High); | |
274 | ||
275 | ppage0_alloc->WWPN.Low = | |
276 | le32_to_cpu(ppage0_alloc->WWPN.Low); | |
277 | ||
278 | ppage0_alloc->WWPN.High = | |
279 | le32_to_cpu(ppage0_alloc->WWPN.High); | |
280 | ||
281 | ppage0_alloc->BBCredit = | |
282 | le16_to_cpu(ppage0_alloc->BBCredit); | |
283 | ||
284 | ppage0_alloc->MaxRxFrameSize = | |
285 | le16_to_cpu(ppage0_alloc->MaxRxFrameSize); | |
286 | ||
287 | port_id = ppage0_alloc->PortIdentifier; | |
288 | num_targ++; | |
289 | *p_p0 = *ppage0_alloc; /* save data */ | |
290 | *p_pp0++ = p_p0++; /* save addr */ | |
291 | } | |
292 | pci_free_consistent(ioc->pcidev, data_sz, | |
293 | (u8 *) ppage0_alloc, page0_dma); | |
294 | if (rc != 0) | |
295 | break; | |
296 | ||
297 | } while (port_id <= 0xff0000); | |
298 | ||
299 | if (num_targ) { | |
300 | /* sort array */ | |
301 | if (num_targ > 1) | |
302 | sort (pp0_array, num_targ, sizeof(FCDevicePage0_t *), | |
303 | mptfc_FcDevPage0_cmp_func, NULL); | |
304 | /* call caller's func for each targ */ | |
305 | for (ii = 0; ii < num_targ; ii++) { | |
306 | fc = *(pp0_array+ii); | |
307 | func(ioc, ioc_port, fc); | |
308 | } | |
309 | } | |
310 | ||
311 | out: | |
312 | if (pp0_array) | |
313 | kfree(pp0_array); | |
314 | if (p0_array) | |
315 | kfree(p0_array); | |
316 | return rc; | |
317 | } | |
318 | ||
319 | static int | |
320 | mptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid) | |
321 | { | |
322 | /* not currently usable */ | |
323 | if (pg0->Flags & (MPI_FC_DEVICE_PAGE0_FLAGS_PLOGI_INVALID | | |
324 | MPI_FC_DEVICE_PAGE0_FLAGS_PRLI_INVALID)) | |
325 | return -1; | |
326 | ||
327 | if (!(pg0->Flags & MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID)) | |
328 | return -1; | |
329 | ||
330 | if (!(pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET)) | |
331 | return -1; | |
332 | ||
333 | /* | |
334 | * board data structure already normalized to platform endianness | |
335 | * shifted to avoid unaligned access on 64 bit architecture | |
336 | */ | |
337 | rid->node_name = ((u64)pg0->WWNN.High) << 32 | (u64)pg0->WWNN.Low; | |
338 | rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low; | |
339 | rid->port_id = pg0->PortIdentifier; | |
340 | rid->roles = FC_RPORT_ROLE_UNKNOWN; | |
341 | rid->roles |= FC_RPORT_ROLE_FCP_TARGET; | |
342 | if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) | |
343 | rid->roles |= FC_RPORT_ROLE_FCP_INITIATOR; | |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | static void | |
349 | mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) | |
350 | { | |
351 | struct fc_rport_identifiers rport_ids; | |
352 | struct fc_rport *rport; | |
353 | struct mptfc_rport_info *ri; | |
354 | int match = 0; | |
355 | u64 port_name; | |
356 | unsigned long flags; | |
357 | ||
358 | if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0) | |
359 | return; | |
360 | ||
361 | /* scan list looking for a match */ | |
362 | spin_lock_irqsave(&ioc->fc_rport_lock, flags); | |
363 | list_for_each_entry(ri, &ioc->fc_rports, list) { | |
364 | port_name = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; | |
365 | if (port_name == rport_ids.port_name) { /* match */ | |
366 | list_move_tail(&ri->list, &ioc->fc_rports); | |
367 | match = 1; | |
368 | break; | |
369 | } | |
370 | } | |
371 | if (!match) { /* allocate one */ | |
372 | spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); | |
373 | ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL); | |
374 | if (!ri) | |
375 | return; | |
376 | spin_lock_irqsave(&ioc->fc_rport_lock, flags); | |
377 | list_add_tail(&ri->list, &ioc->fc_rports); | |
378 | } | |
379 | ||
380 | ri->pg0 = *pg0; /* add/update pg0 data */ | |
381 | ri->flags &= ~MPT_RPORT_INFO_FLAGS_MISSING; | |
382 | ||
383 | if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) { | |
384 | ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED; | |
385 | spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); | |
386 | rport = fc_remote_port_add(ioc->sh,channel, &rport_ids); | |
387 | spin_lock_irqsave(&ioc->fc_rport_lock, flags); | |
388 | if (rport) { | |
389 | if (*((struct mptfc_rport_info **)rport->dd_data) != ri) { | |
390 | ri->flags &= ~MPT_RPORT_INFO_FLAGS_MAPPED_VDEV; | |
391 | ri->vdev = NULL; | |
392 | ri->rport = rport; | |
393 | *((struct mptfc_rport_info **)rport->dd_data) = ri; | |
394 | } | |
395 | rport->dev_loss_tmo = mptfc_dev_loss_tmo; | |
396 | /* | |
397 | * if already mapped, remap here. If not mapped, | |
398 | * slave_alloc will allocate vdev and map | |
399 | */ | |
400 | if (ri->flags & MPT_RPORT_INFO_FLAGS_MAPPED_VDEV) { | |
401 | ri->vdev->target_id = ri->pg0.CurrentTargetID; | |
402 | ri->vdev->bus_id = ri->pg0.CurrentBus; | |
403 | ri->vdev->vtarget->target_id = ri->vdev->target_id; | |
404 | ri->vdev->vtarget->bus_id = ri->vdev->bus_id; | |
405 | } | |
406 | #ifdef MPT_DEBUG | |
407 | printk ("mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, " | |
408 | "rport tid %d, tmo %d\n", | |
409 | ioc->sh->host_no, | |
410 | pg0->PortIdentifier, | |
411 | pg0->WWNN, | |
412 | pg0->WWPN, | |
413 | pg0->CurrentTargetID, | |
414 | ri->rport->scsi_target_id, | |
415 | ri->rport->dev_loss_tmo); | |
416 | #endif | |
417 | } else { | |
418 | list_del(&ri->list); | |
419 | kfree(ri); | |
420 | ri = NULL; | |
421 | } | |
422 | } | |
423 | spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); | |
424 | ||
425 | } | |
426 | ||
2496af39 | 427 | /* |
05e8ec17 MR |
428 | * OS entry point to allow host driver to alloc memory |
429 | * for each scsi device. Called once per device the bus scan. | |
430 | * Return non-zero if allocation fails. | |
431 | * Init memory once per LUN. | |
2496af39 | 432 | */ |
05e8ec17 MR |
433 | int |
434 | mptfc_slave_alloc(struct scsi_device *sdev) | |
435 | { | |
436 | MPT_SCSI_HOST *hd; | |
437 | VirtTarget *vtarget; | |
438 | VirtDevice *vdev; | |
439 | struct scsi_target *starget; | |
440 | struct fc_rport *rport; | |
441 | struct mptfc_rport_info *ri; | |
442 | unsigned long flags; | |
443 | ||
444 | ||
445 | rport = starget_to_rport(scsi_target(sdev)); | |
446 | ||
447 | if (!rport || fc_remote_port_chkready(rport)) | |
448 | return -ENXIO; | |
449 | ||
450 | hd = (MPT_SCSI_HOST *)sdev->host->hostdata; | |
451 | ||
452 | vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL); | |
453 | if (!vdev) { | |
454 | printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n", | |
455 | hd->ioc->name, sizeof(VirtDevice)); | |
456 | return -ENOMEM; | |
457 | } | |
458 | memset(vdev, 0, sizeof(VirtDevice)); | |
459 | ||
460 | spin_lock_irqsave(&hd->ioc->fc_rport_lock,flags); | |
461 | ||
462 | if (!(ri = *((struct mptfc_rport_info **)rport->dd_data))) { | |
463 | spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags); | |
464 | kfree(vdev); | |
465 | return -ENODEV; | |
466 | } | |
467 | ||
468 | sdev->hostdata = vdev; | |
469 | starget = scsi_target(sdev); | |
470 | vtarget = starget->hostdata; | |
471 | if (vtarget->num_luns == 0) { | |
472 | vtarget->tflags = MPT_TARGET_FLAGS_Q_YES | | |
473 | MPT_TARGET_FLAGS_VALID_INQUIRY; | |
474 | hd->Targets[sdev->id] = vtarget; | |
475 | } | |
476 | ||
477 | vtarget->target_id = vdev->target_id; | |
478 | vtarget->bus_id = vdev->bus_id; | |
479 | ||
480 | vdev->vtarget = vtarget; | |
481 | vdev->ioc_id = hd->ioc->id; | |
482 | vdev->lun = sdev->lun; | |
483 | vdev->target_id = ri->pg0.CurrentTargetID; | |
484 | vdev->bus_id = ri->pg0.CurrentBus; | |
485 | ||
486 | ri->flags |= MPT_RPORT_INFO_FLAGS_MAPPED_VDEV; | |
487 | ri->vdev = vdev; | |
488 | ||
489 | spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags); | |
490 | ||
491 | vtarget->num_luns++; | |
492 | ||
493 | #ifdef MPT_DEBUG | |
494 | printk ("mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, " | |
495 | "CurrentTargetID %d, %x %llx %llx\n", | |
496 | sdev->host->host_no, | |
497 | vtarget->num_luns, | |
498 | sdev->id, ri->pg0.CurrentTargetID, | |
499 | ri->pg0.PortIdentifier, ri->pg0.WWPN, ri->pg0.WWNN); | |
500 | #endif | |
501 | ||
502 | return 0; | |
503 | } | |
504 | ||
505 | static int | |
506 | mptfc_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) | |
507 | { | |
508 | struct fc_rport *rport = starget_to_rport(scsi_target(SCpnt->device)); | |
509 | int err; | |
510 | ||
511 | err = fc_remote_port_chkready(rport); | |
512 | if (unlikely(err)) { | |
513 | SCpnt->result = err; | |
514 | done(SCpnt); | |
515 | return 0; | |
516 | } | |
517 | return mptscsih_qcmd(SCpnt,done); | |
518 | } | |
519 | ||
520 | static void | |
521 | mptfc_init_host_attr(MPT_ADAPTER *ioc,int portnum) | |
522 | { | |
523 | unsigned class = 0, cos = 0; | |
524 | ||
525 | /* don't know what to do as only one scsi (fc) host was allocated */ | |
526 | if (portnum != 0) | |
527 | return; | |
528 | ||
529 | class = ioc->fc_port_page0[portnum].SupportedServiceClass; | |
530 | if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1) | |
531 | cos |= FC_COS_CLASS1; | |
532 | if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2) | |
533 | cos |= FC_COS_CLASS2; | |
534 | if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3) | |
535 | cos |= FC_COS_CLASS3; | |
536 | ||
537 | fc_host_node_name(ioc->sh) = | |
538 | (u64)ioc->fc_port_page0[portnum].WWNN.High << 32 | |
539 | | (u64)ioc->fc_port_page0[portnum].WWNN.Low; | |
540 | ||
541 | fc_host_port_name(ioc->sh) = | |
542 | (u64)ioc->fc_port_page0[portnum].WWPN.High << 32 | |
543 | | (u64)ioc->fc_port_page0[portnum].WWPN.Low; | |
544 | ||
545 | fc_host_port_id(ioc->sh) = ioc->fc_port_page0[portnum].PortIdentifier; | |
546 | ||
547 | fc_host_supported_classes(ioc->sh) = cos; | |
548 | ||
549 | fc_host_tgtid_bind_type(ioc->sh) = FC_TGTID_BIND_BY_WWPN; | |
550 | } | |
551 | ||
552 | static void | |
553 | mptfc_rescan_devices(void *arg) | |
554 | { | |
555 | MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; | |
556 | int ii; | |
557 | int work_to_do; | |
558 | unsigned long flags; | |
559 | struct mptfc_rport_info *ri; | |
560 | ||
561 | do { | |
562 | /* start by tagging all ports as missing */ | |
563 | spin_lock_irqsave(&ioc->fc_rport_lock,flags); | |
564 | list_for_each_entry(ri, &ioc->fc_rports, list) { | |
565 | if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) { | |
566 | ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING; | |
567 | } | |
568 | } | |
569 | spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); | |
570 | ||
571 | /* | |
572 | * now rescan devices known to adapter, | |
573 | * will reregister existing rports | |
574 | */ | |
575 | for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { | |
576 | (void) mptbase_GetFcPortPage0(ioc, ii); | |
577 | mptfc_init_host_attr(ioc,ii); /* refresh */ | |
578 | mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev); | |
579 | } | |
580 | ||
581 | /* delete devices still missing */ | |
582 | spin_lock_irqsave(&ioc->fc_rport_lock, flags); | |
583 | list_for_each_entry(ri, &ioc->fc_rports, list) { | |
584 | /* if newly missing, delete it */ | |
585 | if ((ri->flags & (MPT_RPORT_INFO_FLAGS_REGISTERED | | |
586 | MPT_RPORT_INFO_FLAGS_MISSING)) | |
587 | == (MPT_RPORT_INFO_FLAGS_REGISTERED | | |
588 | MPT_RPORT_INFO_FLAGS_MISSING)) { | |
589 | ||
590 | ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED| | |
591 | MPT_RPORT_INFO_FLAGS_MISSING); | |
592 | fc_remote_port_delete(ri->rport); | |
593 | /* | |
594 | * remote port not really deleted 'cause | |
595 | * binding is by WWPN and driver only | |
596 | * registers FCP_TARGETs | |
597 | */ | |
598 | #ifdef MPT_DEBUG | |
599 | printk ("mptfc_rescan.%d: %llx deleted\n", | |
600 | ioc->sh->host_no, ri->pg0.WWPN); | |
601 | #endif | |
602 | } | |
603 | } | |
604 | spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); | |
605 | ||
606 | /* | |
607 | * allow multiple passes as target state | |
608 | * might have changed during scan | |
609 | */ | |
610 | spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); | |
611 | if (ioc->fc_rescan_work_count > 2) /* only need one more */ | |
612 | ioc->fc_rescan_work_count = 2; | |
613 | work_to_do = --ioc->fc_rescan_work_count; | |
614 | spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); | |
615 | } while (work_to_do); | |
616 | } | |
617 | ||
2496af39 MED |
618 | static int |
619 | mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |
620 | { | |
621 | struct Scsi_Host *sh; | |
622 | MPT_SCSI_HOST *hd; | |
623 | MPT_ADAPTER *ioc; | |
624 | unsigned long flags; | |
1ca00bb7 | 625 | int ii; |
2496af39 MED |
626 | int numSGE = 0; |
627 | int scale; | |
628 | int ioc_cap; | |
2496af39 MED |
629 | int error=0; |
630 | int r; | |
05e8ec17 | 631 | |
2496af39 MED |
632 | if ((r = mpt_attach(pdev,id)) != 0) |
633 | return r; | |
05e8ec17 | 634 | |
2496af39 | 635 | ioc = pci_get_drvdata(pdev); |
d335cc38 MED |
636 | ioc->DoneCtx = mptfcDoneCtx; |
637 | ioc->TaskCtx = mptfcTaskCtx; | |
638 | ioc->InternalCtx = mptfcInternalCtx; | |
2496af39 MED |
639 | |
640 | /* Added sanity check on readiness of the MPT adapter. | |
641 | */ | |
642 | if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) { | |
643 | printk(MYIOC_s_WARN_FMT | |
644 | "Skipping because it's not operational!\n", | |
645 | ioc->name); | |
7acec1e7 MED |
646 | error = -ENODEV; |
647 | goto out_mptfc_probe; | |
2496af39 MED |
648 | } |
649 | ||
650 | if (!ioc->active) { | |
651 | printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", | |
652 | ioc->name); | |
7acec1e7 MED |
653 | error = -ENODEV; |
654 | goto out_mptfc_probe; | |
2496af39 MED |
655 | } |
656 | ||
657 | /* Sanity check - ensure at least 1 port is INITIATOR capable | |
658 | */ | |
659 | ioc_cap = 0; | |
660 | for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { | |
661 | if (ioc->pfacts[ii].ProtocolFlags & | |
662 | MPI_PORTFACTS_PROTOCOL_INITIATOR) | |
663 | ioc_cap ++; | |
664 | } | |
665 | ||
666 | if (!ioc_cap) { | |
667 | printk(MYIOC_s_WARN_FMT | |
668 | "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n", | |
669 | ioc->name, ioc); | |
05e8ec17 | 670 | return -ENODEV; |
2496af39 MED |
671 | } |
672 | ||
673 | sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST)); | |
674 | ||
675 | if (!sh) { | |
676 | printk(MYIOC_s_WARN_FMT | |
677 | "Unable to register controller with SCSI subsystem\n", | |
678 | ioc->name); | |
7acec1e7 MED |
679 | error = -1; |
680 | goto out_mptfc_probe; | |
2496af39 MED |
681 | } |
682 | ||
05e8ec17 MR |
683 | INIT_WORK(&ioc->fc_rescan_work, mptfc_rescan_devices,(void *)ioc); |
684 | ||
2496af39 MED |
685 | spin_lock_irqsave(&ioc->FreeQlock, flags); |
686 | ||
687 | /* Attach the SCSI Host to the IOC structure | |
688 | */ | |
689 | ioc->sh = sh; | |
690 | ||
691 | sh->io_port = 0; | |
692 | sh->n_io_port = 0; | |
693 | sh->irq = 0; | |
694 | ||
695 | /* set 16 byte cdb's */ | |
696 | sh->max_cmd_len = 16; | |
697 | ||
698 | sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; | |
699 | ||
700 | sh->max_lun = MPT_LAST_LUN + 1; | |
701 | sh->max_channel = 0; | |
702 | sh->this_id = ioc->pfacts[0].PortSCSIID; | |
703 | ||
704 | /* Required entry. | |
705 | */ | |
706 | sh->unique_id = ioc->id; | |
707 | ||
708 | /* Verify that we won't exceed the maximum | |
709 | * number of chain buffers | |
710 | * We can optimize: ZZ = req_sz/sizeof(SGE) | |
711 | * For 32bit SGE's: | |
712 | * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ | |
713 | * + (req_sz - 64)/sizeof(SGE) | |
714 | * A slightly different algorithm is required for | |
715 | * 64bit SGEs. | |
716 | */ | |
717 | scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); | |
718 | if (sizeof(dma_addr_t) == sizeof(u64)) { | |
719 | numSGE = (scale - 1) * | |
720 | (ioc->facts.MaxChainDepth-1) + scale + | |
721 | (ioc->req_sz - 60) / (sizeof(dma_addr_t) + | |
722 | sizeof(u32)); | |
723 | } else { | |
724 | numSGE = 1 + (scale - 1) * | |
725 | (ioc->facts.MaxChainDepth-1) + scale + | |
726 | (ioc->req_sz - 64) / (sizeof(dma_addr_t) + | |
727 | sizeof(u32)); | |
728 | } | |
729 | ||
730 | if (numSGE < sh->sg_tablesize) { | |
731 | /* Reset this value */ | |
732 | dprintk((MYIOC_s_INFO_FMT | |
733 | "Resetting sg_tablesize to %d from %d\n", | |
734 | ioc->name, numSGE, sh->sg_tablesize)); | |
735 | sh->sg_tablesize = numSGE; | |
736 | } | |
737 | ||
2496af39 MED |
738 | spin_unlock_irqrestore(&ioc->FreeQlock, flags); |
739 | ||
740 | hd = (MPT_SCSI_HOST *) sh->hostdata; | |
741 | hd->ioc = ioc; | |
742 | ||
743 | /* SCSI needs scsi_cmnd lookup table! | |
744 | * (with size equal to req_depth*PtrSz!) | |
745 | */ | |
1ca00bb7 CH |
746 | hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC); |
747 | if (!hd->ScsiLookup) { | |
2496af39 | 748 | error = -ENOMEM; |
7acec1e7 | 749 | goto out_mptfc_probe; |
2496af39 MED |
750 | } |
751 | ||
1ca00bb7 CH |
752 | dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p\n", |
753 | ioc->name, hd->ScsiLookup)); | |
2496af39 MED |
754 | |
755 | /* Allocate memory for the device structures. | |
756 | * A non-Null pointer at an offset | |
757 | * indicates a device exists. | |
758 | * max_id = 1 + maximum id (hosts.h) | |
759 | */ | |
1ca00bb7 CH |
760 | hd->Targets = kcalloc(sh->max_id, sizeof(void *), GFP_ATOMIC); |
761 | if (!hd->Targets) { | |
2496af39 | 762 | error = -ENOMEM; |
7acec1e7 | 763 | goto out_mptfc_probe; |
2496af39 MED |
764 | } |
765 | ||
1ca00bb7 | 766 | dprintk((KERN_INFO " vdev @ %p\n", hd->Targets)); |
2496af39 MED |
767 | |
768 | /* Clear the TM flags | |
769 | */ | |
770 | hd->tmPending = 0; | |
771 | hd->tmState = TM_STATE_NONE; | |
772 | hd->resetPending = 0; | |
773 | hd->abortSCpnt = NULL; | |
774 | ||
775 | /* Clear the pointer used to store | |
776 | * single-threaded commands, i.e., those | |
777 | * issued during a bus scan, dv and | |
778 | * configuration pages. | |
779 | */ | |
780 | hd->cmdPtr = NULL; | |
781 | ||
782 | /* Initialize this SCSI Hosts' timers | |
783 | * To use, set the timer expires field | |
784 | * and add_timer | |
785 | */ | |
786 | init_timer(&hd->timer); | |
787 | hd->timer.data = (unsigned long) hd; | |
788 | hd->timer.function = mptscsih_timer_expired; | |
789 | ||
2496af39 MED |
790 | hd->mpt_pq_filter = mpt_pq_filter; |
791 | ||
792 | ddvprintk((MYIOC_s_INFO_FMT | |
793 | "mpt_pq_filter %x\n", | |
794 | ioc->name, | |
795 | mpt_pq_filter)); | |
796 | ||
797 | init_waitqueue_head(&hd->scandv_waitq); | |
798 | hd->scandv_wait_done = 0; | |
799 | hd->last_queue_full = 0; | |
800 | ||
05e8ec17 | 801 | sh->transportt = mptfc_transport_template; |
2496af39 MED |
802 | error = scsi_add_host (sh, &ioc->pcidev->dev); |
803 | if(error) { | |
804 | dprintk((KERN_ERR MYNAM | |
805 | "scsi_add_host failed\n")); | |
7acec1e7 | 806 | goto out_mptfc_probe; |
2496af39 MED |
807 | } |
808 | ||
05e8ec17 MR |
809 | for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { |
810 | mptfc_init_host_attr(ioc,ii); | |
811 | mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev); | |
812 | } | |
813 | ||
2496af39 MED |
814 | return 0; |
815 | ||
7acec1e7 | 816 | out_mptfc_probe: |
2496af39 MED |
817 | |
818 | mptscsih_remove(pdev); | |
819 | return error; | |
820 | } | |
821 | ||
822 | static struct pci_driver mptfc_driver = { | |
823 | .name = "mptfc", | |
824 | .id_table = mptfc_pci_table, | |
825 | .probe = mptfc_probe, | |
05e8ec17 | 826 | .remove = __devexit_p(mptfc_remove), |
d18c3db5 | 827 | .shutdown = mptscsih_shutdown, |
2496af39 MED |
828 | #ifdef CONFIG_PM |
829 | .suspend = mptscsih_suspend, | |
830 | .resume = mptscsih_resume, | |
831 | #endif | |
832 | }; | |
833 | ||
834 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
835 | /** | |
836 | * mptfc_init - Register MPT adapter(s) as SCSI host(s) with | |
837 | * linux scsi mid-layer. | |
838 | * | |
839 | * Returns 0 for success, non-zero for failure. | |
840 | */ | |
841 | static int __init | |
842 | mptfc_init(void) | |
843 | { | |
05e8ec17 | 844 | int error; |
2496af39 MED |
845 | |
846 | show_mptmod_ver(my_NAME, my_VERSION); | |
847 | ||
05e8ec17 MR |
848 | /* sanity check module parameter */ |
849 | if (mptfc_dev_loss_tmo == 0) | |
850 | mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; | |
851 | ||
852 | mptfc_transport_template = | |
853 | fc_attach_transport(&mptfc_transport_functions); | |
854 | ||
855 | if (!mptfc_transport_template) | |
856 | return -ENODEV; | |
857 | ||
2496af39 MED |
858 | mptfcDoneCtx = mpt_register(mptscsih_io_done, MPTFC_DRIVER); |
859 | mptfcTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTFC_DRIVER); | |
860 | mptfcInternalCtx = mpt_register(mptscsih_scandv_complete, MPTFC_DRIVER); | |
861 | ||
862 | if (mpt_event_register(mptfcDoneCtx, mptscsih_event_process) == 0) { | |
863 | devtprintk((KERN_INFO MYNAM | |
864 | ": Registered for IOC event notifications\n")); | |
865 | } | |
866 | ||
867 | if (mpt_reset_register(mptfcDoneCtx, mptscsih_ioc_reset) == 0) { | |
868 | dprintk((KERN_INFO MYNAM | |
869 | ": Registered for IOC reset notifications\n")); | |
870 | } | |
871 | ||
05e8ec17 MR |
872 | error = pci_register_driver(&mptfc_driver); |
873 | if (error) { | |
874 | fc_release_transport(mptfc_transport_template); | |
875 | } | |
876 | ||
877 | return error; | |
878 | } | |
879 | ||
880 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
881 | /** | |
882 | * mptfc_remove - Removed fc infrastructure for devices | |
883 | * @pdev: Pointer to pci_dev structure | |
884 | * | |
885 | */ | |
886 | static void __devexit mptfc_remove(struct pci_dev *pdev) | |
887 | { | |
888 | MPT_ADAPTER *ioc = pci_get_drvdata(pdev); | |
889 | struct mptfc_rport_info *p, *n; | |
890 | ||
891 | fc_remove_host(ioc->sh); | |
892 | ||
893 | list_for_each_entry_safe(p, n, &ioc->fc_rports, list) { | |
894 | list_del(&p->list); | |
895 | kfree(p); | |
896 | } | |
897 | ||
898 | mptscsih_remove(pdev); | |
2496af39 MED |
899 | } |
900 | ||
901 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
902 | /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ | |
903 | /** | |
904 | * mptfc_exit - Unregisters MPT adapter(s) | |
905 | * | |
906 | */ | |
907 | static void __exit | |
908 | mptfc_exit(void) | |
909 | { | |
910 | pci_unregister_driver(&mptfc_driver); | |
05e8ec17 MR |
911 | fc_release_transport(mptfc_transport_template); |
912 | ||
2496af39 MED |
913 | mpt_reset_deregister(mptfcDoneCtx); |
914 | dprintk((KERN_INFO MYNAM | |
915 | ": Deregistered for IOC reset notifications\n")); | |
916 | ||
917 | mpt_event_deregister(mptfcDoneCtx); | |
918 | dprintk((KERN_INFO MYNAM | |
919 | ": Deregistered for IOC event notifications\n")); | |
920 | ||
921 | mpt_deregister(mptfcInternalCtx); | |
922 | mpt_deregister(mptfcTaskCtx); | |
923 | mpt_deregister(mptfcDoneCtx); | |
924 | } | |
925 | ||
926 | module_init(mptfc_init); | |
927 | module_exit(mptfc_exit); |