pmcs_attach.c revision d78a6b7e13de1e122f6c7e0d539522698d83ec22
1N/A * The contents of this file are subject to the terms of the 1N/A * Common Development and Distribution License (the "License"). 1N/A * You may not use this file except in compliance with the License. 1N/A * See the License for the specific language governing permissions 1N/A * and limitations under the License. 1N/A * When distributing Covered Code, include this CDDL HEADER in each 1N/A * If applicable, add the following below this CDDL HEADER, with the 1N/A * fields enclosed by brackets "[]" replaced with your own identifying 1N/A * information: Portions Copyright [yyyy] [name of copyright owner] 1N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 1N/A * Use is subject to license terms. 1N/A * Non-DDI Compliant stuff 1N/A * Global driver data * Tracing and Logging info * If pmcs_force_syslog value is non-zero, all messages put in the trace log * will also be sent to system log. * Local configuration data NULL,
/* driver operations */ NULL,
/* bus operations */ 0x0000000000000000ull,
/* dma_attr_addr_lo */ 0xFFFFFFFFFFFFFFFFull,
/* dma_attr_addr_hi */ 0x00000000FFFFFFFFull,
/* dma_attr_count_max */ 0x0000000000000001ull,
/* dma_attr_align */ 0x00000078,
/* dma_attr_burstsizes */ 0x00000001,
/* dma_attr_minxfer */ 0x00000000FFFFFFFFull,
/* dma_attr_maxxfer */ 0x00000000FFFFFFFFull,
/* dma_attr_seg */ 512,
/* dma_attr_granular */ * Allocate soft state for iports /* Initialize the global trace lock */ /* Free pmcs log buffer and destroy the global lock */ "%s: invoked with NULL unit address, inst (%d)",
"Failed to alloc soft state for iport %d",
inst);
"cannot get iport soft state");
/* Set some data on the iport handle */ /* Dup the UA into the iport handle */ * If our unit address is active in the phymap, configure our /* Non-NULL private data indicates the unit address is active */ "configure phys on iport handle (0x%p), " /* Allocate string-based soft state pool for targets */ "cannot get iport tgt soft state");
/* Create this iport's target map */ "Failed to create tgtmap on iport %d",
inst);
/* Set up the 'initiator-port' DDI property on this iport */ "%s: separate ports not supported",
__func__);
/* Set initiator-port value to the HBA's base WWN */ /* Set up a 'num-phys' DDI property for the iport node */ /* Create kstats for each of the phys in this port */ * Insert this iport handle into our list and set * iports_attached on the HBA node. "iport%d attached",
inst);
/* No DDI_?_RESUME on iport nodes */ * If this is an iport node, invoke iport attach. * From here on is attach for the HBA node * Check to see if this unit is to be disabled. We can't disable * on a per-iport node. It's either the entire HBA or nothing. * Create the list for iports /* Allocate trace buffer */ "%s: firmware event log files: %s, %s",
__func__,
"%s: No firmware event log will be written " "%s: No firmware event log will be written " "(no filename configured - too long?)",
__func__);
"pmcs-fw-disable-update", 0);
"pci config setup failed");
* Get the size of register set 3. "failed to map Message Unit registers");
"failed to map TOP registers");
"failed to map GSM registers");
"failed to map MPI registers");
* Make sure we can support this card. "Rev A/B Card no longer supported");
* Allocate DMA addressable area for Inbound and Outbound Queue indices * that the chip needs to access plus a space for scratch usage * Allocate DMA S/G list chunks * Allocate a DMA addressable area for the firmware log (if needed) * Align to event log header and entry size "Failed to setup DMA for fwlog area");
"Failed to setup DMA for register dump area");
* More bits of local initialization... "unable to create worker taskq");
* Cache of structures for dealing with I/O completion callbacks. * Cache of PHY structures * Allocate space for the I/O completion threads * Set the quantum value in clock ticks for the I/O interrupt * We have a delicate dance here. We need to set up * interrupts so we know how to set up some OQC * tables. However, while we're setting up table * access, we may need to flash new firmware and * reset the card, which will take some finessing. * Set up interrupts here. * Set these up now becuase they are used to initialize the OQC tables. * If we have MSI or MSI-X interrupts set up and we have enough * vectors for each OQ, the Outbound Queue vectors can all be the * same as the appropriate interrupt routine will have been called * and the doorbell register automatically cleared. * This keeps us from having to check the Outbound Doorbell register * when the routines for these interrupts are called. * If we have Legacy INT-X interrupts set up or we didn't have enough * MSI/MSI-X vectors to uniquely identify each OQ, we point these * vectors to the bits we would like to have set in the Outbound * Doorbell register because pmcs_all_intr will read the doorbell * register to find out why we have an interrupt and write the * corresponding 'clear' bit for that interrupt. * Only one vector, so we must check all OQs for MSI. For * INT-X, there's only one vector anyway, so we can just * use the outbound queue bits to keep from having to * check each queue for each interrupt. /* With 2, we can at least isolate IODONE */ /* With 4 vectors, everybody gets one */ * Do the first part of setup * Now do some additonal allocations based upon information * gathered during MPI setup. * Start MPI communication. * Do some initial acceptance tests. * This tests interrupts and queues. /* Read VPD - if it exists */ "%s: Unable to read VPD: " * When we release, this must goto failure and the call * to pmcs_fabricate_wwid is removed. * We're now officially running * Check firmware versions and load new firmware "%s: Firmware update failed",
__func__);
* Create completion threads. * Create one thread to deal with the updating of the interrupt * Do the SCSI attachment code (before starting phys) * Initialize the rwlock for the iport elements. /* Check all acc & dma handles allocated in attach */ * Create the phymap for this HBA instance * Create the iportmap for this HBA instance * From this point on, we can't fail. "iport%d detached",
inst);
/* No DDI_SUSPEND on iport nodes */ * First, check if there are still any configured targets on this * iport. If so, we fail detach. "iport%d detach failure: iport has targets (luns)",
* Remove this iport from our list if it is inactive in the phymap. "iport%d detach failure: " "iport unit address active in phymap",
/* If it's our only iport, clear iports_attached */ * We have removed the iport handle from the HBA's iports list, * there will be no new references to it. Two things must be * guarded against here. First, we could have PHY up events, * adding themselves to the iport->phys list and grabbing ref's * on our iport handle. Second, we could have existing references * to this iport handle from a point in time prior to the list * So first, destroy the phys list. Remove any phys that have snuck * in after the phymap deactivate, dropping the refcnt accordingly. * If these PHYs are still up if and when the phymap reactivates * (i.e. when this iport reattaches), we'll populate the list with * them and bump the refcnt back up. * Second, wait for any other references to this iport to be * dropped, then continue teardown. /* Destroy the iport target map */ /* Free the tgt soft state */ /* Free our unit address string */ /* Finish teardown and free the softstate */ * Tear down the interrupt infrastructure. * Grab a lock, if initted, to set state. * Stop the I/O completion threads. * Stop the interrupt coalescing timer thread /* Destroy the iports lock */ /* Destroy the iports list */ /* Destroy the iportmap */ * Make sure that any pending watchdog won't * be called from this point on out. * After the above action, the watchdog * timer that starts up the worker task * may trigger but will exit immediately * Now that this is done, we can destroy * the task queue, which will wait if we're * running something on it. * If the chip hasn't been marked dead, shut it down now * to bring it back to a known state without attempting * De-register all registered devices * Shut Down Message Passing * Turn off interrupts on the chip * Free DMA handles and associated consistent memory "Condition check failed " "Condition check failed " "Condition check failed " "Condition check failed at %s():%d",
"Condition check failed at %s():%d",
* Unmap registers and destroy access handles * Do memory allocation cleanup. * Free all PHYs (at level > 0), then free the cache /* Free the targets list */ * Do last property and SCSA cleanup /* Free register dump area if allocated */ * quiesce (9E) entry point * This function is called when the system is single-threaded at high PIL * with preemption disabled. Therefore, the function must not block/wait/sleep. * Returns DDI_SUCCESS or DDI_FAILURE. /* No quiesce necessary on a per-iport basis */ /* Stop MPI & Reset chip (no need to re-initialize) */ * Called with xp->statlock and PHY lock and scratch acquired. * Safe defaults - use only if this target is brand new (i.e. doesn't * already have these settings configured) * We only try and issue an IDENTIFY for first level * (direct attached) devices. We don't try and * set other quirks here (this will happen later, * if the device is fully configured) for (i = 0; i <
4; i++) {
* Check the returned data for being a valid (NAA=5) WWN. * If so, use that and override the SAS address we were if ((u.
nsa[0] >>
4) ==
5) {
* Called with PHY lock and target statlock held and scratch acquired target,
"%s: add_sata_device failed for tgt 0x%p",
* Set the PHY's config stop time to 0. This is one of the final * stops along the config path, so we're indicating that we * successfully configured the PHY. "Not enough memory for DMA chunks");
"Failed to setup DMA for chunks");
* Ensure that inbound work is getting picked up. First, check to * see if new work has been posted. If it has, ensure that the * work is moving forward by checking the consumer index and the * last_htag for the work being processed against what we saw last * time. Note: we use the work structure's 'last_htag' because at * any given moment it could be freed back, thus clearing 'htag' * and setting 'last_htag' (see pmcs_pwork). "Inbound Queue stall detected, issuing reset");
* Check heartbeat on both the MSGU and IOP. It is unlikely that * we'd ever fail here, as the inbound queue monitoring code above * would detect a stall due to either of these elements being * stalled, but we might as well keep an eye on them. "Stall detected on MSGU, issuing reset");
"Stall detected on IOP, issuing reset");
* We've detected a stall. Attempt to recover service via hot * reset. In case of failure, pmcs_hot_reset() will handle the * failure and issue any required FM notifications. * If the command isn't active, we can't be timing it still. * Active means the tag is not free and the state is "on chip". * No timer active for this command. * Knock off bits for the time interval. * The command has now officially timed out. * Get the path for it. If it doesn't have * a phy pointer any more, it's really dead * and can just be put back on the free list. * There should *not* be any commands associated "dead command with gone phy being recycled");
* If this is a non-SCSA command, stop here. Eventually * we might do something with non-SCSA commands here- * but so far their timeout mechanisms are handled in "%s: non-SCSA cmd tag 0x%x timed out",
"%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d",
"%s: SCSA cmd tag 0x%x timed out (state %x)",
* Mark the work structure as timed out. * No point attempting recovery if the device is gone "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE " /* Complete this command here */ "%s: Completing cmd (htag 0x%08x) " "%s: Bad status (%d) on abort of HTAG 0x%08x",
/* Complete this command here */ "%s: Completing cmd (htag 0x%08x) " * No need to reschedule ABORT if we get any other * Run any completions that may have been queued up. * Check forward progress on the chip * Check to see if we need to kick discovery off again "%s: Timer expired for re-enumeration: Start discovery",
"Could not dispatch to worker thread");
for (i = 0; i <
icnt; i++) {
"%s: unable to remove interrupt handler %d",
__func__, i);
"unable to disable interrupt block");
for (i = 0; i <
icnt; i++) {
"unable to disable interrupt %d", i);
for (i = 0; i <
icnt; i++) {
"unable to free interrupt %d", i);
* Try to set up interrupts of type "type" with a minimum number of interrupts "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d",
"%s: get_navail failed; type: %d rc: %d avail: %d min: %d",
"%s: ddi_intr_alloc failed; type: %d rc: %d",
* We return one of three values: * EAGAIN - failure to set up interrupts * EIO - "" + we're now stuck partly enabled * If EIO is returned, we can't unload the driver. "cannot get interrupt types");
* We won't know what firmware we're running until we call pmcs_setup, * and we can't call pmcs_setup until we establish interrupts. * We want PMCS_MAX_MSIX vectors for MSI-X. Anything less would be "No interrupts available");
* Get iblock cookie and add handlers. "unable to get int capabilities");
"intr blk enable failed");
"unable to enable interrupt %d", i);
"unable to get interrupt priority");
* It's possible that if we just turned interrupt coalescing off * (and thus, re-enabled auto clear for interrupts on the I/O outbound * queue) that there was an interrupt already pending. We use * io_intr_coal.int_cleared to ensure that we still drop in here and * clear the appropriate interrupt bit one last time. * Check for Fatal Interrupts * Check for Fatal Interrupts * Check for Outbound Queue service needed "interrupt bits not handled (0x%x)",
obdb);
* Attempt a hot reset. In case of failure, pmcs_hot_reset() will * handle the failure and issue any required FM notifications. * Called with PHY lock and target statlock held and scratch acquired. "%s: Target %p has PHY %p with invalid dtype",
* Called with softstate lock held "cancel config of vtgt %u",
vtgt);
"Removed tgt 0x%p vtgt %u",
* Store the pertinent PHY and target information if there is any /* Indicate truncation */ * When pmcs_force_syslog in non-zero, everything goes also * to syslog, at CE_CONT level. * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also * If "wait" is true, the caller will wait until it can acquire the scratch. * This implies the caller needs to be in a context where spinning for an * indeterminate amount of time is acceptable. * Caller will wait for scratch. /* We've already created this kstat instance */ "%s: Failed to create %s kstats",
__func__,
* We just want to lock against other invocations of kstat; * we don't need to pmcs_lock_phy() for this. /* Get Stats from Chip */ * as the driver can always deal with an error in any dma or * access handle, we can just return the fme_status value. /* Only register with IO Fault Services if we have some capability */ /* Adjust access and dma attributes for FMA */ * Register capabilities with IO Fault Services. * Initialize pci ereport capabilities if ereport * capable (should always be.) * Register error callback if error callback capable. /* Only unregister FMA capabilities if registered */ * Un-register error callback if error callback capable. * Release any resources allocated by pci_ereport_setup() /* Unregister from IO Fault Services */ /* Adjust access and dma attributes for FMA */ "%s: No serial number available to fabricate WWN",