/* * linux/drivers/message/fusion/mptfc.c * For use with LSI PCI chip/adapter(s) * running LSI Fusion MPT (Message Passing Technology) firmware. * * Copyright (c) 1999-2008 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) *
*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
NO WARRANTY THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
DISCLAIMER OF LIABILITY NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/kdev_t.h> #include <linux/blkdev.h> #include <linux/delay.h> /* for mdelay */ #include <linux/interrupt.h> #include <linux/reboot.h> /* notifier code */ #include <linux/workqueue.h> #include <linux/sort.h> #include <linux/slab.h>
/* Command line args */ #define MPTFC_DEV_LOSS_TMO (60) staticint mptfc_dev_loss_tmo = MPTFC_DEV_LOSS_TMO; /* reasonable default */
module_param(mptfc_dev_loss_tmo, int, 0);
MODULE_PARM_DESC(mptfc_dev_loss_tmo, " Initial time the driver programs the " " transport to wait for an rport to " " return following a device loss event." " Default=60.");
/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ #define MPTFC_MAX_LUN (16895) staticint max_lun = MPTFC_MAX_LUN;
module_param(max_lun, int, 0);
MODULE_PARM_DESC(max_lun, " max lun, default=16895 ");
/* MPT_RPORT_INFO_FLAGS_REGISTERED - rport not previously deleted */ if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) {
ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED;
rport = fc_remote_port_add(ioc->sh, channel, &rport_ids); if (rport) {
ri->rport = rport; if (new_ri) /* may have been reset by user */
rport->dev_loss_tmo = mptfc_dev_loss_tmo; /* * if already mapped, remap here. If not mapped, * target_alloc will allocate vtarget and map, * sdev_init will fill in vdevice from vtarget.
*/ if (ri->starget) {
vtarget = ri->starget->hostdata; if (vtarget) {
vtarget->id = pg0->CurrentTargetID;
vtarget->channel = pg0->CurrentBus;
vtarget->deleted = 0;
}
}
*((struct mptfc_rport_info **)rport->dd_data) = ri; /* scan will be scheduled once rport becomes a target */
fc_remote_port_rolechg(rport,roles);
/* * OS entry point to allow for host driver to free allocated memory * Called if no device present or device being unloaded
*/ staticvoid
mptfc_target_destroy(struct scsi_target *starget)
{ struct fc_rport *rport; struct mptfc_rport_info *ri;
rport = starget_to_rport(starget); if (rport) {
ri = *((struct mptfc_rport_info **)rport->dd_data); if (ri) /* better be! */
ri->starget = NULL;
}
kfree(starget->hostdata);
starget->hostdata = NULL;
}
/* * OS entry point to allow host driver to alloc memory * for each scsi target. Called once per device the bus scan. * Return non-zero if allocation fails.
*/ staticint
mptfc_target_alloc(struct scsi_target *starget)
{
VirtTarget *vtarget; struct fc_rport *rport; struct mptfc_rport_info *ri; int rc;
/* * OS entry point to allow host driver to alloc memory * for each scsi device. Called once per device the bus scan. * Return non-zero if allocation fails. * Init memory once per LUN.
*/ staticint
mptfc_sdev_init(struct scsi_device *sdev)
{
MPT_SCSI_HOST *hd;
VirtTarget *vtarget;
VirtDevice *vdevice; struct scsi_target *starget; struct fc_rport *rport;
MPT_ADAPTER *ioc;
/* dd_data is null until finished adding target */
ri = *((struct mptfc_rport_info **)rport->dd_data); if (unlikely(!ri)) {
SCpnt->result = DID_IMM_RETRY << 16;
scsi_done(SCpnt); return 0;
}
return mptscsih_qcmd(SCpnt);
}
/* * mptfc_display_port_link_speed - displaying link speed * @ioc: Pointer to MPT_ADAPTER structure * @portnum: IOC Port number * @pp0dest: port page0 data payload *
*/ staticvoid
mptfc_display_port_link_speed(MPT_ADAPTER *ioc, int portnum, FCPortPage0_t *pp0dest)
{
u8 old_speed, new_speed, state; char *old, *new;
if (portnum >= 2) return;
old_speed = ioc->fc_link_speed[portnum];
new_speed = pp0dest->CurrentSpeed;
state = pp0dest->PortState;
if (state != MPI_FCPORTPAGE0_PORTSTATE_OFFLINE &&
new_speed != MPI_FCPORTPAGE0_CURRENT_SPEED_UNKNOWN) {
old = old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT ? "1 Gbps" :
old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT ? "2 Gbps" :
old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT ? "4 Gbps" : "Unknown"; new = new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT ? "1 Gbps" :
new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT ? "2 Gbps" :
new_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT ? "4 Gbps" : "Unknown"; if (old_speed == 0)
printk(MYIOC_s_NOTE_FMT "FC Link Established, Speed = %s\n",
ioc->name, new); elseif (old_speed != new_speed)
printk(MYIOC_s_WARN_FMT "FC Link Speed Change, Old Speed = %s, New Speed = %s\n",
ioc->name, old, new);
ioc->fc_link_speed[portnum] = new_speed;
}
}
/* * mptfc_GetFcPortPage0 - Fetch FCPort config Page0. * @ioc: Pointer to MPT_ADAPTER structure * @portnum: IOC Port number * * Return: 0 for success * -ENOMEM if no memory available * -EPERM if not allowed due to ISR context * -EAGAIN if no msg frames currently available * -EFAULT for non-successful reply or no reply (timeout) * -EINVAL portnum arg out of range (hardwired to two elements)
*/ staticint
mptfc_GetFcPortPage0(MPT_ADAPTER *ioc, int portnum)
{
ConfigPageHeader_t hdr;
CONFIGPARMS cfg;
FCPortPage0_t *ppage0_alloc;
FCPortPage0_t *pp0dest;
dma_addr_t page0_dma; int data_sz; int copy_sz; int rc; int count = 400;
class = pp0->SupportedServiceClass; if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1)
cos |= FC_COS_CLASS1; if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2)
cos |= FC_COS_CLASS2; if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3)
cos |= FC_COS_CLASS3;
fc_host_supported_classes(sh) = cos;
/* start by tagging all ports as missing */
list_for_each_entry(ri, &ioc->fc_rports, list) { if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) {
ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING;
}
}
/* * now rescan devices known to adapter, * will reregister existing rports
*/ for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
(void) mptfc_GetFcPortPage0(ioc, ii);
mptfc_init_host_attr(ioc, ii); /* refresh */
mptfc_GetFcDevPage0(ioc, ii, mptfc_register_dev);
}
/* delete devices still missing */
list_for_each_entry(ri, &ioc->fc_rports, list) { /* if newly missing, delete it */ if (ri->flags & MPT_RPORT_INFO_FLAGS_MISSING) {
/* Added sanity check on readiness of the MPT adapter.
*/ if (ioc->last_state != MPI_IOC_STATE_OPERATIONAL) {
printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!\n",
ioc->name);
error = -ENODEV; goto out_mptfc_probe;
}
if (!ioc->active) {
printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
ioc->name);
error = -ENODEV; goto out_mptfc_probe;
}
/* Sanity check - ensure at least 1 port is INITIATOR capable
*/
ioc_cap = 0; for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { if (ioc->pfacts[ii].ProtocolFlags &
MPI_PORTFACTS_PROTOCOL_INITIATOR)
ioc_cap ++;
}
if (!ioc_cap) {
printk(MYIOC_s_WARN_FMT "Skipping ioc=%p because SCSI Initiator mode is NOT enabled!\n",
ioc->name, ioc); return 0;
}
sh = scsi_host_alloc(&mptfc_driver_template, sizeof(MPT_SCSI_HOST));
if (!sh) {
printk(MYIOC_s_WARN_FMT "Unable to register controller with SCSI subsystem\n",
ioc->name);
error = -1; goto out_mptfc_probe;
}
/* Verify that we won't exceed the maximum * number of chain buffers * We can optimize: ZZ = req_sz/sizeof(SGE) * For 32bit SGE's: * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ * + (req_sz - 64)/sizeof(SGE) * A slightly different algorithm is required for * 64bit SGEs.
*/
scale = ioc->req_sz/ioc->SGE_size; if (ioc->sg_addr_size == sizeof(u64)) {
numSGE = (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
(ioc->req_sz - 60) / ioc->SGE_size;
} else {
numSGE = 1 + (scale - 1) *
(ioc->facts.MaxChainDepth-1) + scale +
(ioc->req_sz - 64) / ioc->SGE_size;
}
if (numSGE < sh->sg_tablesize) { /* Reset this value */
dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Resetting sg_tablesize to %d from %d\n",
ioc->name, numSGE, sh->sg_tablesize));
sh->sg_tablesize = numSGE;
}
/* * Pre-fetch FC port WWN and stuff... * (FCPortPage0_t stuff)
*/ for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) {
(void) mptfc_GetFcPortPage0(ioc, ii);
}
mptfc_SetFcPortPage1_defaults(ioc);
/* * scan for rports - * by doing it via the workqueue, some locking is eliminated
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.