From e09b41010ba33a20a87472ee821fa407a5b8da36 Mon Sep 17 00:00:00 2001 From: José Pekkarinen Date: Mon, 11 Apr 2016 10:41:07 +0300 Subject: These changes are the raw update to linux-4.4.6-rt14. Kernel sources are taken from kernel.org, and rt patch from the rt wiki download page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen --- kernel/drivers/scsi/3w-9xxx.c | 28 +- kernel/drivers/scsi/53c700.c | 13 +- kernel/drivers/scsi/FlashPoint.c | 2 +- kernel/drivers/scsi/Kconfig | 33 +- kernel/drivers/scsi/Makefile | 5 +- kernel/drivers/scsi/NCR53c406a.c | 1 - kernel/drivers/scsi/a100u2w.c | 3 +- kernel/drivers/scsi/aacraid/aachba.c | 265 +- kernel/drivers/scsi/aacraid/aacraid.h | 20 +- kernel/drivers/scsi/aacraid/comminit.c | 147 +- kernel/drivers/scsi/aacraid/commsup.c | 113 +- kernel/drivers/scsi/aacraid/linit.c | 152 + kernel/drivers/scsi/aacraid/rx.c | 1 + kernel/drivers/scsi/aacraid/sa.c | 1 + kernel/drivers/scsi/aacraid/src.c | 66 +- kernel/drivers/scsi/advansys.c | 1466 ++-- kernel/drivers/scsi/aha152x.c | 1 - kernel/drivers/scsi/aha1542.c | 1 - kernel/drivers/scsi/aha1740.c | 1 - kernel/drivers/scsi/aha1740.h | 1 - kernel/drivers/scsi/aic7xxx/aic79xx_osm.c | 1 - kernel/drivers/scsi/aic7xxx/aic7xxx_core.c | 2 +- kernel/drivers/scsi/aic7xxx/aic7xxx_osm.c | 1 - kernel/drivers/scsi/aic94xx/aic94xx_init.c | 12 +- kernel/drivers/scsi/aic94xx/aic94xx_sds.c | 5 +- kernel/drivers/scsi/arcmsr/arcmsr_hba.c | 7 +- kernel/drivers/scsi/arm/arxescsi.c | 1 - kernel/drivers/scsi/arm/cumana_2.c | 1 - kernel/drivers/scsi/arm/eesox.c | 1 - kernel/drivers/scsi/atp870u.c | 1 - kernel/drivers/scsi/atp870u.h | 1 - kernel/drivers/scsi/be2iscsi/Kconfig | 4 +- kernel/drivers/scsi/be2iscsi/Makefile | 2 +- kernel/drivers/scsi/be2iscsi/be.h | 4 +- kernel/drivers/scsi/be2iscsi/be_cmds.c | 8 +- kernel/drivers/scsi/be2iscsi/be_cmds.h | 16 +- kernel/drivers/scsi/be2iscsi/be_iscsi.c | 4 +- kernel/drivers/scsi/be2iscsi/be_main.c | 148 +- kernel/drivers/scsi/be2iscsi/be_main.h | 17 +- kernel/drivers/scsi/be2iscsi/be_mgmt.c | 97 +- kernel/drivers/scsi/be2iscsi/be_mgmt.h | 14 +- kernel/drivers/scsi/bfa/bfa_ioc.c | 24 +- kernel/drivers/scsi/bfa/bfad_im.c | 4 +- kernel/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h | 6 +- kernel/drivers/scsi/bnx2fc/Kconfig | 5 +- kernel/drivers/scsi/bnx2fc/bnx2fc.h | 9 +- kernel/drivers/scsi/bnx2fc/bnx2fc_constants.h | 6 +- kernel/drivers/scsi/bnx2fc/bnx2fc_debug.c | 6 +- kernel/drivers/scsi/bnx2fc/bnx2fc_debug.h | 6 +- kernel/drivers/scsi/bnx2fc/bnx2fc_els.c | 10 +- kernel/drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 85 +- kernel/drivers/scsi/bnx2fc/bnx2fc_hwi.c | 6 +- kernel/drivers/scsi/bnx2fc/bnx2fc_io.c | 106 +- kernel/drivers/scsi/bnx2fc/bnx2fc_tgt.c | 12 +- kernel/drivers/scsi/bnx2i/bnx2i_iscsi.c | 5 +- kernel/drivers/scsi/csiostor/csio_hw.c | 1 + kernel/drivers/scsi/csiostor/csio_scsi.c | 2 - kernel/drivers/scsi/cxgbi/cxgb3i/Kbuild | 2 +- kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 20 +- kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h | 2 +- kernel/drivers/scsi/cxgbi/cxgb4i/Kbuild | 2 +- kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 54 +- kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h | 4 +- kernel/drivers/scsi/cxgbi/libcxgbi.c | 22 +- kernel/drivers/scsi/cxgbi/libcxgbi.h | 11 +- kernel/drivers/scsi/cxlflash/Kconfig | 11 + kernel/drivers/scsi/cxlflash/Makefile | 2 + kernel/drivers/scsi/cxlflash/common.h | 204 + kernel/drivers/scsi/cxlflash/lunmgt.c | 269 + kernel/drivers/scsi/cxlflash/main.c | 2608 ++++++ kernel/drivers/scsi/cxlflash/main.h | 109 + kernel/drivers/scsi/cxlflash/sislite.h | 472 ++ kernel/drivers/scsi/cxlflash/superpipe.c | 2143 +++++ kernel/drivers/scsi/cxlflash/superpipe.h | 151 + kernel/drivers/scsi/cxlflash/vlun.c | 1273 +++ kernel/drivers/scsi/cxlflash/vlun.h | 86 + kernel/drivers/scsi/device_handler/Kconfig | 2 +- kernel/drivers/scsi/device_handler/Makefile | 1 - kernel/drivers/scsi/device_handler/scsi_dh.c | 621 -- kernel/drivers/scsi/device_handler/scsi_dh_alua.c | 31 +- kernel/drivers/scsi/device_handler/scsi_dh_emc.c | 58 +- kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.c | 55 +- kernel/drivers/scsi/device_handler/scsi_dh_rdac.c | 84 +- kernel/drivers/scsi/dpt_i2o.c | 4 +- kernel/drivers/scsi/esas2r/esas2r_main.c | 1 - kernel/drivers/scsi/esp_scsi.c | 1 - kernel/drivers/scsi/fcoe/fcoe.c | 6 +- kernel/drivers/scsi/fcoe/fcoe_transport.c | 8 +- kernel/drivers/scsi/fdomain.c | 1 - kernel/drivers/scsi/fnic/fnic_debugfs.c | 1 + kernel/drivers/scsi/fnic/fnic_fcs.c | 46 +- kernel/drivers/scsi/fnic/fnic_main.c | 8 - kernel/drivers/scsi/fnic/fnic_scsi.c | 16 + kernel/drivers/scsi/fnic/fnic_trace.c | 1 + kernel/drivers/scsi/hosts.c | 18 + kernel/drivers/scsi/hpsa.c | 4197 +++++++--- kernel/drivers/scsi/hpsa.h | 72 +- kernel/drivers/scsi/hpsa_cmd.h | 70 +- kernel/drivers/scsi/hptiop.c | 97 +- kernel/drivers/scsi/hptiop.h | 6 +- kernel/drivers/scsi/ibmvscsi/ibmvfc.c | 1 - kernel/drivers/scsi/ibmvscsi/ibmvscsi.c | 16 +- kernel/drivers/scsi/ibmvscsi/ibmvscsi.h | 1 + kernel/drivers/scsi/imm.c | 1 - kernel/drivers/scsi/initio.c | 1 - kernel/drivers/scsi/ipr.c | 177 +- kernel/drivers/scsi/ipr.h | 35 +- kernel/drivers/scsi/ips.c | 9 +- kernel/drivers/scsi/isci/init.c | 6 +- kernel/drivers/scsi/libfc/fc_fcp.c | 2 +- kernel/drivers/scsi/libfc/fc_npiv.c | 2 +- kernel/drivers/scsi/libiscsi.c | 26 +- kernel/drivers/scsi/lpfc/lpfc.h | 10 +- kernel/drivers/scsi/lpfc/lpfc_attr.c | 23 +- kernel/drivers/scsi/lpfc/lpfc_bsg.c | 20 - kernel/drivers/scsi/lpfc/lpfc_crtn.h | 2 + kernel/drivers/scsi/lpfc/lpfc_ct.c | 11 +- kernel/drivers/scsi/lpfc/lpfc_debugfs.c | 12 +- kernel/drivers/scsi/lpfc/lpfc_disc.h | 4 +- kernel/drivers/scsi/lpfc/lpfc_els.c | 835 +- kernel/drivers/scsi/lpfc/lpfc_hbadisc.c | 210 +- kernel/drivers/scsi/lpfc/lpfc_hw.h | 207 +- kernel/drivers/scsi/lpfc/lpfc_hw4.h | 236 +- kernel/drivers/scsi/lpfc/lpfc_init.c | 62 +- kernel/drivers/scsi/lpfc/lpfc_mbox.c | 172 +- kernel/drivers/scsi/lpfc/lpfc_nportdisc.c | 12 +- kernel/drivers/scsi/lpfc/lpfc_scsi.c | 82 +- kernel/drivers/scsi/lpfc/lpfc_scsi.h | 3 + kernel/drivers/scsi/lpfc/lpfc_sli.c | 92 +- kernel/drivers/scsi/lpfc/lpfc_sli4.h | 22 +- kernel/drivers/scsi/lpfc/lpfc_version.h | 2 +- kernel/drivers/scsi/lpfc/lpfc_vport.c | 9 +- kernel/drivers/scsi/mac53c94.c | 1 - kernel/drivers/scsi/megaraid.c | 140 +- kernel/drivers/scsi/megaraid/megaraid_sas.h | 400 +- kernel/drivers/scsi/megaraid/megaraid_sas_base.c | 1707 ++-- kernel/drivers/scsi/megaraid/megaraid_sas_fp.c | 53 +- kernel/drivers/scsi/megaraid/megaraid_sas_fusion.c | 1013 +-- kernel/drivers/scsi/megaraid/megaraid_sas_fusion.h | 317 +- kernel/drivers/scsi/mpt2sas/Kconfig | 67 - kernel/drivers/scsi/mpt2sas/Makefile | 7 - kernel/drivers/scsi/mpt2sas/mpi/mpi2.h | 1170 --- kernel/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 3068 ------- kernel/drivers/scsi/mpt2sas/mpi/mpi2_init.h | 461 -- kernel/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 1708 ---- kernel/drivers/scsi/mpt2sas/mpi/mpi2_raid.h | 366 - kernel/drivers/scsi/mpt2sas/mpi/mpi2_sas.h | 288 - kernel/drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 481 -- kernel/drivers/scsi/mpt2sas/mpi/mpi2_type.h | 61 - kernel/drivers/scsi/mpt2sas/mpt2sas_base.c | 4891 ----------- kernel/drivers/scsi/mpt2sas/mpt2sas_base.h | 1198 --- kernel/drivers/scsi/mpt2sas/mpt2sas_config.c | 1527 ---- kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.c | 3077 ------- kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.h | 419 - kernel/drivers/scsi/mpt2sas/mpt2sas_debug.h | 182 - kernel/drivers/scsi/mpt2sas/mpt2sas_scsih.c | 8592 -------------------- kernel/drivers/scsi/mpt2sas/mpt2sas_transport.c | 2169 ----- kernel/drivers/scsi/mpt3sas/Kconfig | 27 +- kernel/drivers/scsi/mpt3sas/Makefile | 3 +- kernel/drivers/scsi/mpt3sas/mpi/mpi2.h | 8 +- kernel/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h | 52 +- kernel/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h | 4 +- kernel/drivers/scsi/mpt3sas/mpi/mpi2_tool.h | 4 +- kernel/drivers/scsi/mpt3sas/mpt3sas_base.c | 806 +- kernel/drivers/scsi/mpt3sas/mpt3sas_base.h | 278 +- kernel/drivers/scsi/mpt3sas/mpt3sas_config.c | 42 +- kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.c | 259 +- kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.h | 6 +- kernel/drivers/scsi/mpt3sas/mpt3sas_debug.h | 16 +- kernel/drivers/scsi/mpt3sas/mpt3sas_scsih.c | 1927 +++-- kernel/drivers/scsi/mpt3sas/mpt3sas_transport.c | 36 +- kernel/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c | 344 + kernel/drivers/scsi/mvsas/mv_init.c | 28 +- kernel/drivers/scsi/mvsas/mv_sas.c | 2 + kernel/drivers/scsi/mvumi.c | 10 +- kernel/drivers/scsi/nsp32.c | 1 - kernel/drivers/scsi/pcmcia/nsp_cs.c | 1 - kernel/drivers/scsi/pcmcia/qlogic_stub.c | 1 - kernel/drivers/scsi/pcmcia/sym53c500_cs.c | 1 - kernel/drivers/scsi/pm8001/pm8001_defs.h | 6 +- kernel/drivers/scsi/pm8001/pm8001_hwi.c | 5 + kernel/drivers/scsi/pm8001/pm8001_init.c | 220 +- kernel/drivers/scsi/pm8001/pm8001_sas.c | 19 +- kernel/drivers/scsi/pm8001/pm8001_sas.h | 18 +- kernel/drivers/scsi/pm8001/pm80xx_hwi.c | 146 +- kernel/drivers/scsi/pm8001/pm80xx_hwi.h | 5 +- kernel/drivers/scsi/pmcraid.c | 6 +- kernel/drivers/scsi/ppa.c | 1 - kernel/drivers/scsi/ps3rom.c | 1 - kernel/drivers/scsi/qla1280.c | 1 - kernel/drivers/scsi/qla2xxx/Kconfig | 4 +- kernel/drivers/scsi/qla2xxx/qla_attr.c | 26 +- kernel/drivers/scsi/qla2xxx/qla_bsg.c | 7 +- kernel/drivers/scsi/qla2xxx/qla_dbg.c | 108 +- kernel/drivers/scsi/qla2xxx/qla_def.h | 35 +- kernel/drivers/scsi/qla2xxx/qla_gs.c | 52 +- kernel/drivers/scsi/qla2xxx/qla_init.c | 231 +- kernel/drivers/scsi/qla2xxx/qla_iocb.c | 143 +- kernel/drivers/scsi/qla2xxx/qla_isr.c | 78 +- kernel/drivers/scsi/qla2xxx/qla_mbx.c | 87 +- kernel/drivers/scsi/qla2xxx/qla_mid.c | 7 +- kernel/drivers/scsi/qla2xxx/qla_mr.c | 22 +- kernel/drivers/scsi/qla2xxx/qla_nx.c | 168 +- kernel/drivers/scsi/qla2xxx/qla_nx2.c | 33 +- kernel/drivers/scsi/qla2xxx/qla_nx2.h | 6 +- kernel/drivers/scsi/qla2xxx/qla_os.c | 67 +- kernel/drivers/scsi/qla2xxx/qla_sup.c | 16 +- kernel/drivers/scsi/qla2xxx/qla_target.c | 918 ++- kernel/drivers/scsi/qla2xxx/qla_target.h | 73 +- kernel/drivers/scsi/qla2xxx/qla_tmpl.c | 43 +- kernel/drivers/scsi/qla2xxx/qla_version.h | 2 +- kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.c | 428 +- kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.h | 6 +- kernel/drivers/scsi/qla4xxx/ql4_83xx.c | 2 +- kernel/drivers/scsi/qla4xxx/ql4_bsg.c | 2 +- kernel/drivers/scsi/qla4xxx/ql4_def.h | 1 + kernel/drivers/scsi/qla4xxx/ql4_nx.c | 2 +- kernel/drivers/scsi/qla4xxx/ql4_os.c | 8 - kernel/drivers/scsi/qlogicfas.c | 1 - kernel/drivers/scsi/qlogicpti.c | 1 - kernel/drivers/scsi/scsi.c | 73 +- kernel/drivers/scsi/scsi_common.c | 287 + kernel/drivers/scsi/scsi_debug.c | 179 +- kernel/drivers/scsi/scsi_devinfo.c | 183 +- kernel/drivers/scsi/scsi_dh.c | 441 + kernel/drivers/scsi/scsi_error.c | 160 +- kernel/drivers/scsi/scsi_lib.c | 23 +- kernel/drivers/scsi/scsi_pm.c | 20 +- kernel/drivers/scsi/scsi_priv.h | 11 + kernel/drivers/scsi/scsi_scan.c | 83 +- kernel/drivers/scsi/scsi_sysfs.c | 66 +- kernel/drivers/scsi/scsi_transport_iscsi.c | 15 +- kernel/drivers/scsi/scsi_transport_sas.c | 10 - kernel/drivers/scsi/scsi_transport_spi.c | 4 +- kernel/drivers/scsi/scsi_transport_srp.c | 12 +- kernel/drivers/scsi/sd.c | 184 +- kernel/drivers/scsi/sd.h | 1 + kernel/drivers/scsi/sd_dif.c | 31 +- kernel/drivers/scsi/ses.c | 30 +- kernel/drivers/scsi/sg.c | 10 +- kernel/drivers/scsi/snic/Makefile | 17 + kernel/drivers/scsi/snic/cq_desc.h | 77 + kernel/drivers/scsi/snic/cq_enet_desc.h | 38 + kernel/drivers/scsi/snic/snic.h | 414 + kernel/drivers/scsi/snic/snic_attrs.c | 77 + kernel/drivers/scsi/snic/snic_ctl.c | 279 + kernel/drivers/scsi/snic/snic_debugfs.c | 560 ++ kernel/drivers/scsi/snic/snic_disc.c | 551 ++ kernel/drivers/scsi/snic/snic_disc.h | 124 + kernel/drivers/scsi/snic/snic_fwint.h | 525 ++ kernel/drivers/scsi/snic/snic_io.c | 518 ++ kernel/drivers/scsi/snic/snic_io.h | 118 + kernel/drivers/scsi/snic/snic_isr.c | 204 + kernel/drivers/scsi/snic/snic_main.c | 1034 +++ kernel/drivers/scsi/snic/snic_res.c | 295 + kernel/drivers/scsi/snic/snic_res.h | 97 + kernel/drivers/scsi/snic/snic_scsi.c | 2632 ++++++ kernel/drivers/scsi/snic/snic_stats.h | 123 + kernel/drivers/scsi/snic/snic_trc.c | 181 + kernel/drivers/scsi/snic/snic_trc.h | 121 + kernel/drivers/scsi/snic/vnic_cq.c | 86 + kernel/drivers/scsi/snic/vnic_cq.h | 110 + kernel/drivers/scsi/snic/vnic_cq_fw.h | 62 + kernel/drivers/scsi/snic/vnic_dev.c | 748 ++ kernel/drivers/scsi/snic/vnic_dev.h | 110 + kernel/drivers/scsi/snic/vnic_devcmd.h | 270 + kernel/drivers/scsi/snic/vnic_intr.c | 59 + kernel/drivers/scsi/snic/vnic_intr.h | 105 + kernel/drivers/scsi/snic/vnic_resource.h | 68 + kernel/drivers/scsi/snic/vnic_snic.h | 54 + kernel/drivers/scsi/snic/vnic_stats.h | 68 + kernel/drivers/scsi/snic/vnic_wq.c | 237 + kernel/drivers/scsi/snic/vnic_wq.h | 170 + kernel/drivers/scsi/snic/wq_enet_desc.h | 96 + kernel/drivers/scsi/sr.c | 4 + kernel/drivers/scsi/st.c | 380 +- kernel/drivers/scsi/st.h | 22 + kernel/drivers/scsi/stex.c | 21 +- kernel/drivers/scsi/storvsc_drv.c | 518 +- kernel/drivers/scsi/sun3x_esp.c | 2 +- kernel/drivers/scsi/sym53c416.c | 1 - kernel/drivers/scsi/ufs/Kconfig | 4 +- kernel/drivers/scsi/ufs/ufs-qcom.c | 938 ++- kernel/drivers/scsi/ufs/ufs-qcom.h | 68 +- kernel/drivers/scsi/ufs/ufshcd-pltfrm.c | 98 +- kernel/drivers/scsi/ufs/ufshcd-pltfrm.h | 41 + kernel/drivers/scsi/ufs/ufshcd.c | 226 +- kernel/drivers/scsi/ufs/ufshcd.h | 202 +- kernel/drivers/scsi/ufs/ufshci.h | 8 +- kernel/drivers/scsi/ufs/unipro.h | 8 + kernel/drivers/scsi/virtio_scsi.c | 15 +- kernel/drivers/scsi/wd719x.c | 3 +- kernel/drivers/scsi/wd719x.h | 2 - kernel/drivers/scsi/xen-scsifront.c | 10 +- 294 files changed, 34331 insertions(+), 38628 deletions(-) create mode 100644 kernel/drivers/scsi/cxlflash/Kconfig create mode 100644 kernel/drivers/scsi/cxlflash/Makefile create mode 100644 kernel/drivers/scsi/cxlflash/common.h create mode 100644 kernel/drivers/scsi/cxlflash/lunmgt.c create mode 100644 kernel/drivers/scsi/cxlflash/main.c create mode 100644 kernel/drivers/scsi/cxlflash/main.h create mode 100644 kernel/drivers/scsi/cxlflash/sislite.h create mode 100644 kernel/drivers/scsi/cxlflash/superpipe.c create mode 100644 kernel/drivers/scsi/cxlflash/superpipe.h create mode 100644 kernel/drivers/scsi/cxlflash/vlun.c create mode 100644 kernel/drivers/scsi/cxlflash/vlun.h delete mode 100644 kernel/drivers/scsi/device_handler/scsi_dh.c delete mode 100644 kernel/drivers/scsi/mpt2sas/Kconfig delete mode 100644 kernel/drivers/scsi/mpt2sas/Makefile delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_init.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_raid.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_sas.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_tool.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpi/mpi2_type.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_base.c delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_base.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_config.c delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.c delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_debug.h delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_scsih.c delete mode 100644 kernel/drivers/scsi/mpt2sas/mpt2sas_transport.c create mode 100644 kernel/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c create mode 100644 kernel/drivers/scsi/scsi_common.c create mode 100644 kernel/drivers/scsi/scsi_dh.c create mode 100644 kernel/drivers/scsi/snic/Makefile create mode 100644 kernel/drivers/scsi/snic/cq_desc.h create mode 100644 kernel/drivers/scsi/snic/cq_enet_desc.h create mode 100644 kernel/drivers/scsi/snic/snic.h create mode 100644 kernel/drivers/scsi/snic/snic_attrs.c create mode 100644 kernel/drivers/scsi/snic/snic_ctl.c create mode 100644 kernel/drivers/scsi/snic/snic_debugfs.c create mode 100644 kernel/drivers/scsi/snic/snic_disc.c create mode 100644 kernel/drivers/scsi/snic/snic_disc.h create mode 100644 kernel/drivers/scsi/snic/snic_fwint.h create mode 100644 kernel/drivers/scsi/snic/snic_io.c create mode 100644 kernel/drivers/scsi/snic/snic_io.h create mode 100644 kernel/drivers/scsi/snic/snic_isr.c create mode 100644 kernel/drivers/scsi/snic/snic_main.c create mode 100644 kernel/drivers/scsi/snic/snic_res.c create mode 100644 kernel/drivers/scsi/snic/snic_res.h create mode 100644 kernel/drivers/scsi/snic/snic_scsi.c create mode 100644 kernel/drivers/scsi/snic/snic_stats.h create mode 100644 kernel/drivers/scsi/snic/snic_trc.c create mode 100644 kernel/drivers/scsi/snic/snic_trc.h create mode 100644 kernel/drivers/scsi/snic/vnic_cq.c create mode 100644 kernel/drivers/scsi/snic/vnic_cq.h create mode 100644 kernel/drivers/scsi/snic/vnic_cq_fw.h create mode 100644 kernel/drivers/scsi/snic/vnic_dev.c create mode 100644 kernel/drivers/scsi/snic/vnic_dev.h create mode 100644 kernel/drivers/scsi/snic/vnic_devcmd.h create mode 100644 kernel/drivers/scsi/snic/vnic_intr.c create mode 100644 kernel/drivers/scsi/snic/vnic_intr.h create mode 100644 kernel/drivers/scsi/snic/vnic_resource.h create mode 100644 kernel/drivers/scsi/snic/vnic_snic.h create mode 100644 kernel/drivers/scsi/snic/vnic_stats.h create mode 100644 kernel/drivers/scsi/snic/vnic_wq.c create mode 100644 kernel/drivers/scsi/snic/vnic_wq.h create mode 100644 kernel/drivers/scsi/snic/wq_enet_desc.h create mode 100644 kernel/drivers/scsi/ufs/ufshcd-pltfrm.h (limited to 'kernel/drivers/scsi') diff --git a/kernel/drivers/scsi/3w-9xxx.c b/kernel/drivers/scsi/3w-9xxx.c index add419d6f..a56a7b243 100644 --- a/kernel/drivers/scsi/3w-9xxx.c +++ b/kernel/drivers/scsi/3w-9xxx.c @@ -212,6 +212,17 @@ static const struct file_operations twa_fops = { .llseek = noop_llseek, }; +/* + * The controllers use an inline buffer instead of a mapped SGL for small, + * single entry buffers. Note that we treat a zero-length transfer like + * a mapped SGL. + */ +static bool twa_command_mapped(struct scsi_cmnd *cmd) +{ + return scsi_sg_count(cmd) != 1 || + scsi_bufflen(cmd) >= TW_MIN_SGL_LENGTH; +} + /* This function will complete an aen request from the isr */ static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id) { @@ -1339,7 +1350,8 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) } /* Now complete the io */ - scsi_dma_unmap(cmd); + if (twa_command_mapped(cmd)) + scsi_dma_unmap(cmd); cmd->scsi_done(cmd); tw_dev->state[request_id] = TW_S_COMPLETED; twa_free_request_id(tw_dev, request_id); @@ -1582,7 +1594,8 @@ static int twa_reset_device_extension(TW_Device_Extension *tw_dev) struct scsi_cmnd *cmd = tw_dev->srb[i]; cmd->result = (DID_RESET << 16); - scsi_dma_unmap(cmd); + if (twa_command_mapped(cmd)) + scsi_dma_unmap(cmd); cmd->scsi_done(cmd); } } @@ -1765,12 +1778,14 @@ static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_ retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL); switch (retval) { case SCSI_MLQUEUE_HOST_BUSY: - scsi_dma_unmap(SCpnt); + if (twa_command_mapped(SCpnt)) + scsi_dma_unmap(SCpnt); twa_free_request_id(tw_dev, request_id); break; case 1: SCpnt->result = (DID_ERROR << 16); - scsi_dma_unmap(SCpnt); + if (twa_command_mapped(SCpnt)) + scsi_dma_unmap(SCpnt); done(SCpnt); tw_dev->state[request_id] = TW_S_COMPLETED; twa_free_request_id(tw_dev, request_id); @@ -1831,8 +1846,7 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, /* Map sglist from scsi layer to cmd packet */ if (scsi_sg_count(srb)) { - if ((scsi_sg_count(srb) == 1) && - (scsi_bufflen(srb) < TW_MIN_SGL_LENGTH)) { + if (!twa_command_mapped(srb)) { if (srb->sc_data_direction == DMA_TO_DEVICE || srb->sc_data_direction == DMA_BIDIRECTIONAL) scsi_sg_copy_to_buffer(srb, @@ -1905,7 +1919,7 @@ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int re { struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - if (scsi_bufflen(cmd) < TW_MIN_SGL_LENGTH && + if (!twa_command_mapped(cmd) && (cmd->sc_data_direction == DMA_FROM_DEVICE || cmd->sc_data_direction == DMA_BIDIRECTIONAL)) { if (scsi_sg_count(cmd) == 1) { diff --git a/kernel/drivers/scsi/53c700.c b/kernel/drivers/scsi/53c700.c index 82abfce1c..d4c285688 100644 --- a/kernel/drivers/scsi/53c700.c +++ b/kernel/drivers/scsi/53c700.c @@ -299,7 +299,7 @@ NCR_700_detect(struct scsi_host_template *tpnt, memory = dma_alloc_noncoherent(hostdata->dev, TOTAL_MEM_SIZE, &pScript, GFP_KERNEL); if(memory == NULL) { - printk(KERN_ERR "53c700: Failed to allocate memory for driver, detatching\n"); + printk(KERN_ERR "53c700: Failed to allocate memory for driver, detaching\n"); return NULL; } @@ -325,7 +325,6 @@ NCR_700_detect(struct scsi_host_template *tpnt, tpnt->slave_destroy = NCR_700_slave_destroy; tpnt->slave_alloc = NCR_700_slave_alloc; tpnt->change_queue_depth = NCR_700_change_queue_depth; - tpnt->use_blk_tags = 1; if(tpnt->name == NULL) tpnt->name = "53c700"; @@ -1107,7 +1106,9 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp, BUG(); } if(hostdata->msgin[1] == A_SIMPLE_TAG_MSG) { - struct scsi_cmnd *SCp = scsi_find_tag(SDp, hostdata->msgin[2]); + struct scsi_cmnd *SCp; + + SCp = scsi_host_find_tag(SDp->host, hostdata->msgin[2]); if(unlikely(SCp == NULL)) { printk(KERN_ERR "scsi%d: (%d:%d) no saved request for tag %d\n", host->host_no, reselection_id, lun, hostdata->msgin[2]); @@ -1119,7 +1120,9 @@ process_script_interrupt(__u32 dsps, __u32 dsp, struct scsi_cmnd *SCp, "reselection is tag %d, slot %p(%d)\n", hostdata->msgin[2], slot, slot->tag); } else { - struct scsi_cmnd *SCp = scsi_find_tag(SDp, SCSI_NO_TAG); + struct scsi_cmnd *SCp; + + SCp = scsi_host_find_tag(SDp->host, SCSI_NO_TAG); if(unlikely(SCp == NULL)) { sdev_printk(KERN_ERR, SDp, "no saved request for untagged cmd\n"); @@ -1823,7 +1826,7 @@ NCR_700_queuecommand_lck(struct scsi_cmnd *SCp, void (*done)(struct scsi_cmnd *) slot->tag, slot); } else { slot->tag = SCSI_NO_TAG; - /* must populate current_cmnd for scsi_find_tag to work */ + /* must populate current_cmnd for scsi_host_find_tag to work */ SCp->device->current_cmnd = SCp; } /* sanity check: some of the commands generated by the mid-layer diff --git a/kernel/drivers/scsi/FlashPoint.c b/kernel/drivers/scsi/FlashPoint.c index 5c74e4c52..867b864f5 100644 --- a/kernel/drivers/scsi/FlashPoint.c +++ b/kernel/drivers/scsi/FlashPoint.c @@ -2136,7 +2136,7 @@ static unsigned char FPT_SccbMgr_bad_isr(u32 p_port, unsigned char p_card, * *---------------------------------------------------------------------*/ -static void FPT_SccbMgrTableInitAll() +static void FPT_SccbMgrTableInitAll(void) { unsigned char thisCard; diff --git a/kernel/drivers/scsi/Kconfig b/kernel/drivers/scsi/Kconfig index b021bcb88..64eed87d3 100644 --- a/kernel/drivers/scsi/Kconfig +++ b/kernel/drivers/scsi/Kconfig @@ -52,7 +52,7 @@ config SCSI_MQ_DEFAULT This option enables the new blk-mq based I/O path for SCSI devices by default. With the option the scsi_mod.use_blk_mq module/boot option defaults to Y, without it to N, but it can - still be overriden either way. + still be overridden either way. If unsure say N. @@ -242,13 +242,6 @@ config SCSI_SCAN_ASYNC system continues booting, and even probe devices on different busses in parallel, leading to a significant speed-up. - If you have built SCSI as modules, enabling this option can - be a problem as the devices may not have been found by the - time your system expects them to have been. You can load the - scsi_wait_scan module to ensure that all scans have completed. - If you build your SCSI drivers into the kernel, then everything - will work fine if you say Y here. - You can override this choice by specifying "scsi_mod.scan=sync" or async on the kernel's command line. @@ -345,6 +338,7 @@ source "drivers/scsi/cxgbi/Kconfig" source "drivers/scsi/bnx2i/Kconfig" source "drivers/scsi/bnx2fc/Kconfig" source "drivers/scsi/be2iscsi/Kconfig" +source "drivers/scsi/cxlflash/Kconfig" config SGIWD93_SCSI tristate "SGI WD93C93 SCSI Driver" @@ -370,6 +364,7 @@ config SCSI_HPSA tristate "HP Smart Array SCSI driver" depends on PCI && SCSI select CHECK_SIGNATURE + select SCSI_SAS_ATTRS help This driver supports HP Smart Array Controllers (circa 2009). It is a SCSI alternative to the cciss driver, which is a block @@ -503,8 +498,9 @@ config SCSI_DPT_I2O config SCSI_ADVANSYS tristate "AdvanSys SCSI support" - depends on SCSI && VIRT_TO_BUS && !ARM + depends on SCSI depends on ISA || EISA || PCI + depends on ISA_DMA_API || !ISA help This is a driver for all SCSI host adapters manufactured by AdvanSys. It is documented in the kernel source in @@ -540,7 +536,6 @@ config SCSI_ARCMSR source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" -source "drivers/scsi/mpt2sas/Kconfig" source "drivers/scsi/mpt3sas/Kconfig" source "drivers/scsi/ufs/Kconfig" @@ -634,6 +629,23 @@ config FCOE_FNIC . The module will be called fnic. +config SCSI_SNIC + tristate "Cisco SNIC Driver" + depends on PCI && SCSI + help + This is support for the Cisco PCI-Express SCSI HBA. + + To compile this driver as a module, choose M here and read + . + The module will be called snic. + +config SCSI_SNIC_DEBUG_FS + bool "Cisco SNIC Driver Debugfs Support" + depends on SCSI_SNIC && DEBUG_FS + help + This enables to list debugging information from SNIC Driver + available via debugfs file system + config SCSI_DMX3191D tristate "DMX3191D SCSI support" depends on PCI && SCSI @@ -1743,7 +1755,6 @@ config SCSI_BFA_FC config SCSI_VIRTIO tristate "virtio-scsi support" depends on VIRTIO - select BLK_DEV_INTEGRITY help This is the virtual HBA driver for virtio. If the kernel will be used in a virtual machine, say Y or M. diff --git a/kernel/drivers/scsi/Makefile b/kernel/drivers/scsi/Makefile index dee160a4f..c14bca4a9 100644 --- a/kernel/drivers/scsi/Makefile +++ b/kernel/drivers/scsi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_LIBFC) += libfc/ obj-$(CONFIG_LIBFCOE) += fcoe/ obj-$(CONFIG_FCOE) += fcoe/ obj-$(CONFIG_FCOE_FNIC) += fnic/ +obj-$(CONFIG_SCSI_SNIC) += snic/ obj-$(CONFIG_SCSI_BNX2X_FCOE) += libfc/ fcoe/ bnx2fc/ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o libiscsi_tcp.o iscsi_tcp.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o @@ -101,10 +102,10 @@ obj-$(CONFIG_SCSI_7000FASST) += wd7000.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o +obj-$(CONFIG_CXLFLASH) += cxlflash/ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ -obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/ obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/ obj-$(CONFIG_SCSI_UFSHCD) += ufs/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o @@ -161,6 +162,7 @@ obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/ obj-$(CONFIG_SCSI_DEBUG) += scsi_debug.o scsi_mod-y += scsi.o hosts.o scsi_ioctl.o \ scsicam.o scsi_error.o scsi_lib.o +scsi_mod-y += scsi_common.o scsi_mod-$(CONFIG_SCSI_CONSTANTS) += constants.o scsi_mod-$(CONFIG_SCSI_DMA) += scsi_lib_dma.o scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o @@ -169,6 +171,7 @@ scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_mod-y += scsi_trace.o scsi_logging.o scsi_mod-$(CONFIG_PM) += scsi_pm.o +scsi_mod-$(CONFIG_SCSI_DH) += scsi_dh.o hv_storvsc-y := storvsc_drv.o diff --git a/kernel/drivers/scsi/NCR53c406a.c b/kernel/drivers/scsi/NCR53c406a.c index 42c716147..6e110c630 100644 --- a/kernel/drivers/scsi/NCR53c406a.c +++ b/kernel/drivers/scsi/NCR53c406a.c @@ -1064,7 +1064,6 @@ static struct scsi_host_template driver_template = .can_queue = 1 /* can_queue */, .this_id = 7 /* SCSI ID of the chip */, .sg_tablesize = 32 /*SG_ALL*/ /*SG_NONE*/, - .cmd_per_lun = 1 /* commands per lun */, .unchecked_isa_dma = 1 /* unchecked_isa_dma */, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/a100u2w.c b/kernel/drivers/scsi/a100u2w.c index 7e33a61c1..8086bd0ac 100644 --- a/kernel/drivers/scsi/a100u2w.c +++ b/kernel/drivers/scsi/a100u2w.c @@ -888,7 +888,7 @@ static int inia100_build_scb(struct orc_host * host, struct orc_scb * scb, struc scb->sense_len = SENSE_SIZE; scb->cdb_len = cmd->cmd_len; if (scb->cdb_len >= IMAX_CDB) { - printk("max cdb length= %x\b", cmd->cmd_len); + printk("max cdb length= %x\n", cmd->cmd_len); scb->cdb_len = IMAX_CDB; } scb->ident = (u8)(cmd->device->lun & 0xff) | DISC_ALLOW; @@ -1078,7 +1078,6 @@ static struct scsi_host_template inia100_template = { .can_queue = 1, .this_id = 1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/aacraid/aachba.c b/kernel/drivers/scsi/aacraid/aachba.c index 9b3dd6ef6..e4c243748 100644 --- a/kernel/drivers/scsi/aacraid/aachba.c +++ b/kernel/drivers/scsi/aacraid/aachba.c @@ -259,7 +259,7 @@ MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the" " 0=off, 1=on"); module_param_named(msi, aac_msi, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(msi, "IRQ handling." - " 0=PIC(default), 1=MSI, 2=MSI-X(unsupported, uses MSI)"); + " 0=PIC(default), 1=MSI, 2=MSI-X)"); module_param(startup_timeout, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(startup_timeout, "The duration of time in seconds to wait for" " adapter to have it's kernel up and\n" @@ -570,7 +570,7 @@ static int aac_get_container_name(struct scsi_cmnd * scsicmd) status = aac_fib_send(ContainerCommand, cmd_fibcontext, - sizeof (struct aac_get_name), + sizeof(struct aac_get_name_resp), FsaNormal, 0, 1, (fib_callback)get_container_name_callback, @@ -1052,7 +1052,7 @@ static int aac_get_container_serial(struct scsi_cmnd * scsicmd) status = aac_fib_send(ContainerCommand, cmd_fibcontext, - sizeof (struct aac_get_serial), + sizeof(struct aac_get_serial_resp), FsaNormal, 0, 1, (fib_callback) get_container_serial_callback, @@ -2977,11 +2977,16 @@ static void aac_srb_callback(void *context, struct fib * fibptr) return; BUG_ON(fibptr == NULL); - dev = fibptr->dev; - srbreply = (struct aac_srb_reply *) fib_data(fibptr); + scsi_dma_unmap(scsicmd); + /* expose physical device if expose_physicald flag is on */ + if (scsicmd->cmnd[0] == INQUIRY && !(scsicmd->cmnd[1] & 0x01) + && expose_physicals > 0) + aac_expose_phy_device(scsicmd); + + srbreply = (struct aac_srb_reply *) fib_data(fibptr); scsicmd->sense_buffer[0] = '\0'; /* Initialize sense valid flag to false */ if (fibptr->flags & FIB_CONTEXT_FLAG_FASTRESP) { @@ -2994,147 +2999,157 @@ static void aac_srb_callback(void *context, struct fib * fibptr) */ scsi_set_resid(scsicmd, scsi_bufflen(scsicmd) - le32_to_cpu(srbreply->data_xfer_length)); - } - - scsi_dma_unmap(scsicmd); - - /* expose physical device if expose_physicald flag is on */ - if (scsicmd->cmnd[0] == INQUIRY && !(scsicmd->cmnd[1] & 0x01) - && expose_physicals > 0) - aac_expose_phy_device(scsicmd); + /* + * First check the fib status + */ - /* - * First check the fib status - */ + if (le32_to_cpu(srbreply->status) != ST_OK) { + int len; - if (le32_to_cpu(srbreply->status) != ST_OK){ - int len; - printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status)); - len = min_t(u32, le32_to_cpu(srbreply->sense_data_size), - SCSI_SENSE_BUFFERSIZE); - scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION; - memcpy(scsicmd->sense_buffer, srbreply->sense_data, len); - } + printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status)); + len = min_t(u32, le32_to_cpu(srbreply->sense_data_size), + SCSI_SENSE_BUFFERSIZE); + scsicmd->result = DID_ERROR << 16 + | COMMAND_COMPLETE << 8 + | SAM_STAT_CHECK_CONDITION; + memcpy(scsicmd->sense_buffer, + srbreply->sense_data, len); + } - /* - * Next check the srb status - */ - switch( (le32_to_cpu(srbreply->srb_status))&0x3f){ - case SRB_STATUS_ERROR_RECOVERY: - case SRB_STATUS_PENDING: - case SRB_STATUS_SUCCESS: - scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; - break; - case SRB_STATUS_DATA_OVERRUN: - switch(scsicmd->cmnd[0]){ - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - case READ_12: - case WRITE_12: - case READ_16: - case WRITE_16: - if (le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow) { - printk(KERN_WARNING"aacraid: SCSI CMD underflow\n"); - } else { - printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n"); + /* + * Next check the srb status + */ + switch ((le32_to_cpu(srbreply->srb_status))&0x3f) { + case SRB_STATUS_ERROR_RECOVERY: + case SRB_STATUS_PENDING: + case SRB_STATUS_SUCCESS: + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + break; + case SRB_STATUS_DATA_OVERRUN: + switch (scsicmd->cmnd[0]) { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + case READ_16: + case WRITE_16: + if (le32_to_cpu(srbreply->data_xfer_length) + < scsicmd->underflow) + printk(KERN_WARNING"aacraid: SCSI CMD underflow\n"); + else + printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n"); + scsicmd->result = DID_ERROR << 16 + | COMMAND_COMPLETE << 8; + break; + case INQUIRY: { + scsicmd->result = DID_OK << 16 + | COMMAND_COMPLETE << 8; + break; + } + default: + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + break; } - scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8; break; - case INQUIRY: { - scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + case SRB_STATUS_ABORTED: + scsicmd->result = DID_ABORT << 16 | ABORT << 8; break; - } - default: - scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8; + case SRB_STATUS_ABORT_FAILED: + /* + * Not sure about this one - but assuming the + * hba was trying to abort for some reason + */ + scsicmd->result = DID_ERROR << 16 | ABORT << 8; + break; + case SRB_STATUS_PARITY_ERROR: + scsicmd->result = DID_PARITY << 16 + | MSG_PARITY_ERROR << 8; + break; + case SRB_STATUS_NO_DEVICE: + case SRB_STATUS_INVALID_PATH_ID: + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_SELECTION_TIMEOUT: + scsicmd->result = DID_NO_CONNECT << 16 + | COMMAND_COMPLETE << 8; break; - } - break; - case SRB_STATUS_ABORTED: - scsicmd->result = DID_ABORT << 16 | ABORT << 8; - break; - case SRB_STATUS_ABORT_FAILED: - // Not sure about this one - but assuming the hba was trying to abort for some reason - scsicmd->result = DID_ERROR << 16 | ABORT << 8; - break; - case SRB_STATUS_PARITY_ERROR: - scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8; - break; - case SRB_STATUS_NO_DEVICE: - case SRB_STATUS_INVALID_PATH_ID: - case SRB_STATUS_INVALID_TARGET_ID: - case SRB_STATUS_INVALID_LUN: - case SRB_STATUS_SELECTION_TIMEOUT: - scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8; - break; - case SRB_STATUS_COMMAND_TIMEOUT: - case SRB_STATUS_TIMEOUT: - scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8; - break; + case SRB_STATUS_COMMAND_TIMEOUT: + case SRB_STATUS_TIMEOUT: + scsicmd->result = DID_TIME_OUT << 16 + | COMMAND_COMPLETE << 8; + break; - case SRB_STATUS_BUSY: - scsicmd->result = DID_BUS_BUSY << 16 | COMMAND_COMPLETE << 8; - break; + case SRB_STATUS_BUSY: + scsicmd->result = DID_BUS_BUSY << 16 + | COMMAND_COMPLETE << 8; + break; - case SRB_STATUS_BUS_RESET: - scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8; - break; + case SRB_STATUS_BUS_RESET: + scsicmd->result = DID_RESET << 16 + | COMMAND_COMPLETE << 8; + break; - case SRB_STATUS_MESSAGE_REJECTED: - scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8; - break; - case SRB_STATUS_REQUEST_FLUSHED: - case SRB_STATUS_ERROR: - case SRB_STATUS_INVALID_REQUEST: - case SRB_STATUS_REQUEST_SENSE_FAILED: - case SRB_STATUS_NO_HBA: - case SRB_STATUS_UNEXPECTED_BUS_FREE: - case SRB_STATUS_PHASE_SEQUENCE_FAILURE: - case SRB_STATUS_BAD_SRB_BLOCK_LENGTH: - case SRB_STATUS_DELAYED_RETRY: - case SRB_STATUS_BAD_FUNCTION: - case SRB_STATUS_NOT_STARTED: - case SRB_STATUS_NOT_IN_USE: - case SRB_STATUS_FORCE_ABORT: - case SRB_STATUS_DOMAIN_VALIDATION_FAIL: - default: + case SRB_STATUS_MESSAGE_REJECTED: + scsicmd->result = DID_ERROR << 16 + | MESSAGE_REJECT << 8; + break; + case SRB_STATUS_REQUEST_FLUSHED: + case SRB_STATUS_ERROR: + case SRB_STATUS_INVALID_REQUEST: + case SRB_STATUS_REQUEST_SENSE_FAILED: + case SRB_STATUS_NO_HBA: + case SRB_STATUS_UNEXPECTED_BUS_FREE: + case SRB_STATUS_PHASE_SEQUENCE_FAILURE: + case SRB_STATUS_BAD_SRB_BLOCK_LENGTH: + case SRB_STATUS_DELAYED_RETRY: + case SRB_STATUS_BAD_FUNCTION: + case SRB_STATUS_NOT_STARTED: + case SRB_STATUS_NOT_IN_USE: + case SRB_STATUS_FORCE_ABORT: + case SRB_STATUS_DOMAIN_VALIDATION_FAIL: + default: #ifdef AAC_DETAILED_STATUS_INFO - printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n", - le32_to_cpu(srbreply->srb_status) & 0x3F, - aac_get_status_string( - le32_to_cpu(srbreply->srb_status) & 0x3F), - scsicmd->cmnd[0], - le32_to_cpu(srbreply->scsi_status)); + printk(KERN_INFO "aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n", + le32_to_cpu(srbreply->srb_status) & 0x3F, + aac_get_status_string( + le32_to_cpu(srbreply->srb_status) & 0x3F), + scsicmd->cmnd[0], + le32_to_cpu(srbreply->scsi_status)); #endif - if ((scsicmd->cmnd[0] == ATA_12) - || (scsicmd->cmnd[0] == ATA_16)) { - if (scsicmd->cmnd[2] & (0x01 << 5)) { - scsicmd->result = DID_OK << 16 - | COMMAND_COMPLETE << 8; + if ((scsicmd->cmnd[0] == ATA_12) + || (scsicmd->cmnd[0] == ATA_16)) { + if (scsicmd->cmnd[2] & (0x01 << 5)) { + scsicmd->result = DID_OK << 16 + | COMMAND_COMPLETE << 8; break; + } else { + scsicmd->result = DID_ERROR << 16 + | COMMAND_COMPLETE << 8; + break; + } } else { scsicmd->result = DID_ERROR << 16 - | COMMAND_COMPLETE << 8; + | COMMAND_COMPLETE << 8; break; } - } else { - scsicmd->result = DID_ERROR << 16 - | COMMAND_COMPLETE << 8; - break; } - } - if (le32_to_cpu(srbreply->scsi_status) == SAM_STAT_CHECK_CONDITION) { - int len; - scsicmd->result |= SAM_STAT_CHECK_CONDITION; - len = min_t(u32, le32_to_cpu(srbreply->sense_data_size), - SCSI_SENSE_BUFFERSIZE); + if (le32_to_cpu(srbreply->scsi_status) + == SAM_STAT_CHECK_CONDITION) { + int len; + + scsicmd->result |= SAM_STAT_CHECK_CONDITION; + len = min_t(u32, le32_to_cpu(srbreply->sense_data_size), + SCSI_SENSE_BUFFERSIZE); #ifdef AAC_DETAILED_STATUS_INFO - printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", - le32_to_cpu(srbreply->status), len); + printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", + le32_to_cpu(srbreply->status), len); #endif - memcpy(scsicmd->sense_buffer, srbreply->sense_data, len); + memcpy(scsicmd->sense_buffer, + srbreply->sense_data, len); + } } /* * OR in the scsi status (already shifted up a bit) diff --git a/kernel/drivers/scsi/aacraid/aacraid.h b/kernel/drivers/scsi/aacraid/aacraid.h index 40fe65c91..074878b55 100644 --- a/kernel/drivers/scsi/aacraid/aacraid.h +++ b/kernel/drivers/scsi/aacraid/aacraid.h @@ -12,7 +12,7 @@ * D E F I N E S *----------------------------------------------------------------------------*/ -#define AAC_MAX_MSIX 8 /* vectors */ +#define AAC_MAX_MSIX 32 /* vectors */ #define AAC_PCI_MSI_ENABLE 0x8000 enum { @@ -62,7 +62,7 @@ enum { #define PMC_GLOBAL_INT_BIT0 0x00000001 #ifndef AAC_DRIVER_BUILD -# define AAC_DRIVER_BUILD 40709 +# define AAC_DRIVER_BUILD 41010 # define AAC_DRIVER_BRANCH "-ms" #endif #define MAXIMUM_NUM_CONTAINERS 32 @@ -547,6 +547,7 @@ struct adapter_ops int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, u32 *status, u32 *r1, u32 *r2, u32 *r3, u32 *r4); int (*adapter_check_health)(struct aac_dev *dev); int (*adapter_restart)(struct aac_dev *dev, int bled); + void (*adapter_start)(struct aac_dev *dev); /* Transport operations */ int (*adapter_ioremap)(struct aac_dev * dev, u32 size); irq_handler_t adapter_intr; @@ -843,6 +844,10 @@ struct src_registers { &((AEP)->regs.src.bar0->CSR)) #define src_writel(AEP, CSR, value) writel(value, \ &((AEP)->regs.src.bar0->CSR)) +#if defined(writeq) +#define src_writeq(AEP, CSR, value) writeq(value, \ + &((AEP)->regs.src.bar0->CSR)) +#endif #define SRC_ODR_SHIFT 12 #define SRC_IDR_SHIFT 9 @@ -1162,6 +1167,11 @@ struct aac_dev struct fsa_dev_info *fsa_dev; struct task_struct *thread; int cardtype; + /* + *This lock will protect the two 32-bit + *writes to the Inbound Queue + */ + spinlock_t iq_lock; /* * The following is the device specific extension. @@ -1247,6 +1257,9 @@ struct aac_dev #define aac_adapter_restart(dev,bled) \ (dev)->a_ops.adapter_restart(dev,bled) +#define aac_adapter_start(dev) \ + ((dev)->a_ops.adapter_start(dev)) + #define aac_adapter_ioremap(dev, size) \ (dev)->a_ops.adapter_ioremap(dev, size) @@ -2097,6 +2110,8 @@ static inline unsigned int cap_to_cyls(sector_t capacity, unsigned divisor) #define AAC_OWNER_ERROR_HANDLER 0x103 #define AAC_OWNER_FIRMWARE 0x106 +int aac_acquire_irq(struct aac_dev *dev); +void aac_free_irq(struct aac_dev *dev); const char *aac_driverinfo(struct Scsi_Host *); struct fib *aac_fib_alloc(struct aac_dev *dev); int aac_fib_setup(struct aac_dev *dev); @@ -2127,6 +2142,7 @@ int aac_sa_init(struct aac_dev *dev); int aac_src_init(struct aac_dev *dev); int aac_srcv_init(struct aac_dev *dev); int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw_fib, int wait, struct fib * fibptr, unsigned long *nonotify); +void aac_define_int_mode(struct aac_dev *dev); unsigned int aac_response_normal(struct aac_queue * q); unsigned int aac_command_normal(struct aac_queue * q); unsigned int aac_intr_normal(struct aac_dev *dev, u32 Index, diff --git a/kernel/drivers/scsi/aacraid/comminit.c b/kernel/drivers/scsi/aacraid/comminit.c index 45db84ad3..0e954e37f 100644 --- a/kernel/drivers/scsi/aacraid/comminit.c +++ b/kernel/drivers/scsi/aacraid/comminit.c @@ -43,8 +43,6 @@ #include "aacraid.h" -static void aac_define_int_mode(struct aac_dev *dev); - struct aac_common aac_config = { .irq_mod = 1 }; @@ -338,6 +336,74 @@ static int aac_comm_init(struct aac_dev * dev) return 0; } +void aac_define_int_mode(struct aac_dev *dev) +{ + int i, msi_count, min_msix; + + msi_count = i = 0; + /* max. vectors from GET_COMM_PREFERRED_SETTINGS */ + if (dev->max_msix == 0 || + dev->pdev->device == PMC_DEVICE_S6 || + dev->sync_mode) { + dev->max_msix = 1; + dev->vector_cap = + dev->scsi_host_ptr->can_queue + + AAC_NUM_MGT_FIB; + return; + } + + /* Don't bother allocating more MSI-X vectors than cpus */ + msi_count = min(dev->max_msix, + (unsigned int)num_online_cpus()); + + dev->max_msix = msi_count; + + if (msi_count > AAC_MAX_MSIX) + msi_count = AAC_MAX_MSIX; + + for (i = 0; i < msi_count; i++) + dev->msixentry[i].entry = i; + + if (msi_count > 1 && + pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX)) { + min_msix = 2; + i = pci_enable_msix_range(dev->pdev, + dev->msixentry, + min_msix, + msi_count); + if (i > 0) { + dev->msi_enabled = 1; + msi_count = i; + } else { + dev->msi_enabled = 0; + printk(KERN_ERR "%s%d: MSIX not supported!! Will try MSI 0x%x.\n", + dev->name, dev->id, i); + } + } + + if (!dev->msi_enabled) { + msi_count = 1; + i = pci_enable_msi(dev->pdev); + + if (!i) { + dev->msi_enabled = 1; + dev->msi = 1; + } else { + printk(KERN_ERR "%s%d: MSI not supported!! Will try INTx 0x%x.\n", + dev->name, dev->id, i); + } + } + + if (!dev->msi_enabled) + dev->max_msix = msi_count = 1; + else { + if (dev->max_msix > msi_count) + dev->max_msix = msi_count; + } + dev->vector_cap = + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) / + msi_count; +} struct aac_dev *aac_init_adapter(struct aac_dev *dev) { u32 status[5]; @@ -350,6 +416,7 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) dev->management_fib_count = 0; spin_lock_init(&dev->manage_lock); spin_lock_init(&dev->sync_lock); + spin_lock_init(&dev->iq_lock); dev->max_fib_size = sizeof(struct hw_fib); dev->sg_tablesize = host->sg_tablesize = (dev->max_fib_size - sizeof(struct aac_fibhdr) @@ -508,79 +575,3 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) return dev; } -static void aac_define_int_mode(struct aac_dev *dev) -{ - - int i, msi_count; - - msi_count = i = 0; - /* max. vectors from GET_COMM_PREFERRED_SETTINGS */ - if (dev->max_msix == 0 || - dev->pdev->device == PMC_DEVICE_S6 || - dev->sync_mode) { - dev->max_msix = 1; - dev->vector_cap = - dev->scsi_host_ptr->can_queue + - AAC_NUM_MGT_FIB; - return; - } - - msi_count = min(dev->max_msix, - (unsigned int)num_online_cpus()); - - dev->max_msix = msi_count; - - if (msi_count > AAC_MAX_MSIX) - msi_count = AAC_MAX_MSIX; - - for (i = 0; i < msi_count; i++) - dev->msixentry[i].entry = i; - - if (msi_count > 1 && - pci_find_capability(dev->pdev, PCI_CAP_ID_MSIX)) { - i = pci_enable_msix(dev->pdev, - dev->msixentry, - msi_count); - /* Check how many MSIX vectors are allocated */ - if (i >= 0) { - dev->msi_enabled = 1; - if (i) { - msi_count = i; - if (pci_enable_msix(dev->pdev, - dev->msixentry, - msi_count)) { - dev->msi_enabled = 0; - printk(KERN_ERR "%s%d: MSIX not supported!! Will try MSI 0x%x.\n", - dev->name, dev->id, i); - } - } - } else { - dev->msi_enabled = 0; - printk(KERN_ERR "%s%d: MSIX not supported!! Will try MSI 0x%x.\n", - dev->name, dev->id, i); - } - } - - if (!dev->msi_enabled) { - msi_count = 1; - i = pci_enable_msi(dev->pdev); - - if (!i) { - dev->msi_enabled = 1; - dev->msi = 1; - } else { - printk(KERN_ERR "%s%d: MSI not supported!! Will try INTx 0x%x.\n", - dev->name, dev->id, i); - } - } - - if (!dev->msi_enabled) - dev->max_msix = msi_count = 1; - else { - if (dev->max_msix > msi_count) - dev->max_msix = msi_count; - } - dev->vector_cap = - (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) / - msi_count; -} diff --git a/kernel/drivers/scsi/aacraid/commsup.c b/kernel/drivers/scsi/aacraid/commsup.c index 4da574925..a1f90fe84 100644 --- a/kernel/drivers/scsi/aacraid/commsup.c +++ b/kernel/drivers/scsi/aacraid/commsup.c @@ -1270,13 +1270,12 @@ retry_next: static int _aac_reset_adapter(struct aac_dev *aac, int forced) { int index, quirks; - int retval, i; + int retval; struct Scsi_Host *host; struct scsi_device *dev; struct scsi_cmnd *command; struct scsi_cmnd *command_list; int jafo = 0; - int cpu; /* * Assumptions: @@ -1339,35 +1338,7 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) aac->comm_phys = 0; kfree(aac->queues); aac->queues = NULL; - cpu = cpumask_first(cpu_online_mask); - if (aac->pdev->device == PMC_DEVICE_S6 || - aac->pdev->device == PMC_DEVICE_S7 || - aac->pdev->device == PMC_DEVICE_S8 || - aac->pdev->device == PMC_DEVICE_S9) { - if (aac->max_msix > 1) { - for (i = 0; i < aac->max_msix; i++) { - if (irq_set_affinity_hint( - aac->msixentry[i].vector, - NULL)) { - printk(KERN_ERR "%s%d: Failed to reset IRQ affinity for cpu %d\n", - aac->name, - aac->id, - cpu); - } - cpu = cpumask_next(cpu, - cpu_online_mask); - free_irq(aac->msixentry[i].vector, - &(aac->aac_msix[i])); - } - pci_disable_msix(aac->pdev); - } else { - free_irq(aac->pdev->irq, &(aac->aac_msix[0])); - } - } else { - free_irq(aac->pdev->irq, aac); - } - if (aac->msi) - pci_disable_msi(aac->pdev); + aac_free_irq(aac); kfree(aac->fsa_dev); aac->fsa_dev = NULL; quirks = aac_get_driver_ident(index)->quirks; @@ -1978,3 +1949,83 @@ int aac_command_thread(void *data) dev->aif_thread = 0; return 0; } + +int aac_acquire_irq(struct aac_dev *dev) +{ + int i; + int j; + int ret = 0; + int cpu; + + cpu = cpumask_first(cpu_online_mask); + if (!dev->sync_mode && dev->msi_enabled && dev->max_msix > 1) { + for (i = 0; i < dev->max_msix; i++) { + dev->aac_msix[i].vector_no = i; + dev->aac_msix[i].dev = dev; + if (request_irq(dev->msixentry[i].vector, + dev->a_ops.adapter_intr, + 0, "aacraid", &(dev->aac_msix[i]))) { + printk(KERN_ERR "%s%d: Failed to register IRQ for vector %d.\n", + dev->name, dev->id, i); + for (j = 0 ; j < i ; j++) + free_irq(dev->msixentry[j].vector, + &(dev->aac_msix[j])); + pci_disable_msix(dev->pdev); + ret = -1; + } + if (irq_set_affinity_hint(dev->msixentry[i].vector, + get_cpu_mask(cpu))) { + printk(KERN_ERR "%s%d: Failed to set IRQ affinity for cpu %d\n", + dev->name, dev->id, cpu); + } + cpu = cpumask_next(cpu, cpu_online_mask); + } + } else { + dev->aac_msix[0].vector_no = 0; + dev->aac_msix[0].dev = dev; + + if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, + IRQF_SHARED, "aacraid", + &(dev->aac_msix[0])) < 0) { + if (dev->msi) + pci_disable_msi(dev->pdev); + printk(KERN_ERR "%s%d: Interrupt unavailable.\n", + dev->name, dev->id); + ret = -1; + } + } + return ret; +} + +void aac_free_irq(struct aac_dev *dev) +{ + int i; + int cpu; + + cpu = cpumask_first(cpu_online_mask); + if (dev->pdev->device == PMC_DEVICE_S6 || + dev->pdev->device == PMC_DEVICE_S7 || + dev->pdev->device == PMC_DEVICE_S8 || + dev->pdev->device == PMC_DEVICE_S9) { + if (dev->max_msix > 1) { + for (i = 0; i < dev->max_msix; i++) { + if (irq_set_affinity_hint( + dev->msixentry[i].vector, NULL)) { + printk(KERN_ERR "%s%d: Failed to reset IRQ affinity for cpu %d\n", + dev->name, dev->id, cpu); + } + cpu = cpumask_next(cpu, cpu_online_mask); + free_irq(dev->msixentry[i].vector, + &(dev->aac_msix[i])); + } + } else { + free_irq(dev->pdev->irq, &(dev->aac_msix[0])); + } + } else { + free_irq(dev->pdev->irq, dev); + } + if (dev->msi) + pci_disable_msi(dev->pdev); + else if (dev->max_msix > 1) + pci_disable_msix(dev->pdev); +} diff --git a/kernel/drivers/scsi/aacraid/linit.c b/kernel/drivers/scsi/aacraid/linit.c index 9eec02733..3b6e5c67e 100644 --- a/kernel/drivers/scsi/aacraid/linit.c +++ b/kernel/drivers/scsi/aacraid/linit.c @@ -1317,6 +1317,154 @@ static int aac_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) return error; } +#if (defined(CONFIG_PM)) +void aac_release_resources(struct aac_dev *aac) +{ + int i; + + aac_adapter_disable_int(aac); + if (aac->pdev->device == PMC_DEVICE_S6 || + aac->pdev->device == PMC_DEVICE_S7 || + aac->pdev->device == PMC_DEVICE_S8 || + aac->pdev->device == PMC_DEVICE_S9) { + if (aac->max_msix > 1) { + for (i = 0; i < aac->max_msix; i++) + free_irq(aac->msixentry[i].vector, + &(aac->aac_msix[i])); + } else { + free_irq(aac->pdev->irq, &(aac->aac_msix[0])); + } + } else { + free_irq(aac->pdev->irq, aac); + } + if (aac->msi) + pci_disable_msi(aac->pdev); + else if (aac->max_msix > 1) + pci_disable_msix(aac->pdev); + +} + +static int aac_acquire_resources(struct aac_dev *dev) +{ + int i, j; + int instance = dev->id; + const char *name = dev->name; + unsigned long status; + /* + * First clear out all interrupts. Then enable the one's that we + * can handle. + */ + while (!((status = src_readl(dev, MUnit.OMR)) & KERNEL_UP_AND_RUNNING) + || status == 0xffffffff) + msleep(20); + + aac_adapter_disable_int(dev); + aac_adapter_enable_int(dev); + + + if ((dev->pdev->device == PMC_DEVICE_S7 || + dev->pdev->device == PMC_DEVICE_S8 || + dev->pdev->device == PMC_DEVICE_S9)) + aac_define_int_mode(dev); + + if (dev->msi_enabled) + aac_src_access_devreg(dev, AAC_ENABLE_MSIX); + + if (!dev->sync_mode && dev->msi_enabled && dev->max_msix > 1) { + for (i = 0; i < dev->max_msix; i++) { + dev->aac_msix[i].vector_no = i; + dev->aac_msix[i].dev = dev; + + if (request_irq(dev->msixentry[i].vector, + dev->a_ops.adapter_intr, + 0, "aacraid", &(dev->aac_msix[i]))) { + printk(KERN_ERR "%s%d: Failed to register IRQ for vector %d.\n", + name, instance, i); + for (j = 0 ; j < i ; j++) + free_irq(dev->msixentry[j].vector, + &(dev->aac_msix[j])); + pci_disable_msix(dev->pdev); + goto error_iounmap; + } + } + } else { + dev->aac_msix[0].vector_no = 0; + dev->aac_msix[0].dev = dev; + + if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, + IRQF_SHARED, "aacraid", + &(dev->aac_msix[0])) < 0) { + if (dev->msi) + pci_disable_msi(dev->pdev); + printk(KERN_ERR "%s%d: Interrupt unavailable.\n", + name, instance); + goto error_iounmap; + } + } + + aac_adapter_enable_int(dev); + + if (!dev->sync_mode) + aac_adapter_start(dev); + return 0; + +error_iounmap: + return -1; + +} +static int aac_suspend(struct pci_dev *pdev, pm_message_t state) +{ + + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct aac_dev *aac = (struct aac_dev *)shost->hostdata; + + scsi_block_requests(shost); + aac_send_shutdown(aac); + + aac_release_resources(aac); + + pci_set_drvdata(pdev, shost); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int aac_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct aac_dev *aac = (struct aac_dev *)shost->hostdata; + int r; + + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + r = pci_enable_device(pdev); + + if (r) + goto fail_device; + + pci_set_master(pdev); + if (aac_acquire_resources(aac)) + goto fail_device; + /* + * reset this flag to unblock ioctl() as it was set at + * aac_send_shutdown() to block ioctls from upperlayer + */ + aac->adapter_shutdown = 0; + scsi_unblock_requests(shost); + + return 0; + +fail_device: + printk(KERN_INFO "%s%d: resume failed.\n", aac->name, aac->id); + scsi_host_put(shost); + pci_disable_device(pdev); + return -ENODEV; +} +#endif + static void aac_shutdown(struct pci_dev *dev) { struct Scsi_Host *shost = pci_get_drvdata(dev); @@ -1356,6 +1504,10 @@ static struct pci_driver aac_pci_driver = { .id_table = aac_pci_tbl, .probe = aac_probe_one, .remove = aac_remove_one, +#if (defined(CONFIG_PM)) + .suspend = aac_suspend, + .resume = aac_resume, +#endif .shutdown = aac_shutdown, }; diff --git a/kernel/drivers/scsi/aacraid/rx.c b/kernel/drivers/scsi/aacraid/rx.c index 9570612b8..ac1638069 100644 --- a/kernel/drivers/scsi/aacraid/rx.c +++ b/kernel/drivers/scsi/aacraid/rx.c @@ -623,6 +623,7 @@ int _aac_rx_init(struct aac_dev *dev) dev->a_ops.adapter_sync_cmd = rx_sync_cmd; dev->a_ops.adapter_check_health = aac_rx_check_health; dev->a_ops.adapter_restart = aac_rx_restart_adapter; + dev->a_ops.adapter_start = aac_rx_start_adapter; /* * First clear out all interrupts. Then enable the one's that we diff --git a/kernel/drivers/scsi/aacraid/sa.c b/kernel/drivers/scsi/aacraid/sa.c index e66477c98..869aea23c 100644 --- a/kernel/drivers/scsi/aacraid/sa.c +++ b/kernel/drivers/scsi/aacraid/sa.c @@ -372,6 +372,7 @@ int aac_sa_init(struct aac_dev *dev) dev->a_ops.adapter_sync_cmd = sa_sync_cmd; dev->a_ops.adapter_check_health = aac_sa_check_health; dev->a_ops.adapter_restart = aac_sa_restart_adapter; + dev->a_ops.adapter_start = aac_sa_start_adapter; dev->a_ops.adapter_intr = aac_sa_intr; dev->a_ops.adapter_deliver = aac_rx_deliver_producer; dev->a_ops.adapter_ioremap = aac_sa_ioremap; diff --git a/kernel/drivers/scsi/aacraid/src.c b/kernel/drivers/scsi/aacraid/src.c index 4596e9dd7..2aa34ea8c 100644 --- a/kernel/drivers/scsi/aacraid/src.c +++ b/kernel/drivers/scsi/aacraid/src.c @@ -46,7 +46,7 @@ static int aac_src_get_sync_status(struct aac_dev *dev); -irqreturn_t aac_src_intr_message(int irq, void *dev_id) +static irqreturn_t aac_src_intr_message(int irq, void *dev_id) { struct aac_msix_ctx *ctx; struct aac_dev *dev; @@ -447,6 +447,10 @@ static int aac_src_deliver_message(struct fib *fib) u32 fibsize; dma_addr_t address; struct aac_fib_xporthdr *pFibX; +#if !defined(writeq) + unsigned long flags; +#endif + u16 hdr_size = le16_to_cpu(fib->hw_fib_va->header.Size); atomic_inc(&q->numpending); @@ -511,10 +515,14 @@ static int aac_src_deliver_message(struct fib *fib) return -EINVAL; address |= fibsize; } - +#if defined(writeq) + src_writeq(dev, MUnit.IQ_L, (u64)address); +#else + spin_lock_irqsave(&fib->dev->iq_lock, flags); src_writel(dev, MUnit.IQ_H, upper_32_bits(address) & 0xffffffff); src_writel(dev, MUnit.IQ_L, address & 0xffffffff); - + spin_unlock_irqrestore(&fib->dev->iq_lock, flags); +#endif return 0; } @@ -726,6 +734,7 @@ int aac_src_init(struct aac_dev *dev) dev->a_ops.adapter_sync_cmd = src_sync_cmd; dev->a_ops.adapter_check_health = aac_src_check_health; dev->a_ops.adapter_restart = aac_src_restart_adapter; + dev->a_ops.adapter_start = aac_src_start_adapter; /* * First clear out all interrupts. Then enable the one's that we @@ -741,7 +750,7 @@ int aac_src_init(struct aac_dev *dev) if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE1) goto error_iounmap; - dev->msi = aac_msi && !pci_enable_msi(dev->pdev); + dev->msi = !pci_enable_msi(dev->pdev); dev->aac_msix[0].vector_no = 0; dev->aac_msix[0].dev = dev; @@ -789,9 +798,7 @@ int aac_srcv_init(struct aac_dev *dev) unsigned long status; int restart = 0; int instance = dev->id; - int i, j; const char *name = dev->name; - int cpu; dev->a_ops.adapter_ioremap = aac_srcv_ioremap; dev->a_ops.adapter_comm = aac_src_select_comm; @@ -892,6 +899,7 @@ int aac_srcv_init(struct aac_dev *dev) dev->a_ops.adapter_sync_cmd = src_sync_cmd; dev->a_ops.adapter_check_health = aac_src_check_health; dev->a_ops.adapter_restart = aac_src_restart_adapter; + dev->a_ops.adapter_start = aac_src_start_adapter; /* * First clear out all interrupts. Then enable the one's that we @@ -908,48 +916,10 @@ int aac_srcv_init(struct aac_dev *dev) goto error_iounmap; if (dev->msi_enabled) aac_src_access_devreg(dev, AAC_ENABLE_MSIX); - if (!dev->sync_mode && dev->msi_enabled && dev->max_msix > 1) { - cpu = cpumask_first(cpu_online_mask); - for (i = 0; i < dev->max_msix; i++) { - dev->aac_msix[i].vector_no = i; - dev->aac_msix[i].dev = dev; - - if (request_irq(dev->msixentry[i].vector, - dev->a_ops.adapter_intr, - 0, - "aacraid", - &(dev->aac_msix[i]))) { - printk(KERN_ERR "%s%d: Failed to register IRQ for vector %d.\n", - name, instance, i); - for (j = 0 ; j < i ; j++) - free_irq(dev->msixentry[j].vector, - &(dev->aac_msix[j])); - pci_disable_msix(dev->pdev); - goto error_iounmap; - } - if (irq_set_affinity_hint( - dev->msixentry[i].vector, - get_cpu_mask(cpu))) { - printk(KERN_ERR "%s%d: Failed to set IRQ affinity for cpu %d\n", - name, instance, cpu); - } - cpu = cpumask_next(cpu, cpu_online_mask); - } - } else { - dev->aac_msix[0].vector_no = 0; - dev->aac_msix[0].dev = dev; - - if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, - IRQF_SHARED, - "aacraid", - &(dev->aac_msix[0])) < 0) { - if (dev->msi) - pci_disable_msi(dev->pdev); - printk(KERN_ERR "%s%d: Interrupt unavailable.\n", - name, instance); - goto error_iounmap; - } - } + + if (aac_acquire_irq(dev)) + goto error_iounmap; + dev->dbg_base = dev->base_start; dev->dbg_base_mapped = dev->base; dev->dbg_size = dev->base_size; diff --git a/kernel/drivers/scsi/advansys.c b/kernel/drivers/scsi/advansys.c index ae95e347f..febbd83e2 100644 --- a/kernel/drivers/scsi/advansys.c +++ b/kernel/drivers/scsi/advansys.c @@ -1,12 +1,10 @@ -#define DRV_NAME "advansys" -#define ASC_VERSION "3.4" /* AdvanSys Driver Version */ - /* * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * * Copyright (c) 1995-2000 Advanced System Products, Inc. * Copyright (c) 2000-2001 ConnectCom Solutions, Inc. * Copyright (c) 2007 Matthew Wilcox + * Copyright (c) 2014 Hannes Reinecke * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -39,6 +37,7 @@ #include #include #include +#include #include #include @@ -49,26 +48,15 @@ #include #include +#define DRV_NAME "advansys" +#define ASC_VERSION "3.5" /* AdvanSys Driver Version */ + /* FIXME: * - * 1. Although all of the necessary command mapping places have the - * appropriate dma_map.. APIs, the driver still processes its internal - * queue using bus_to_virt() and virt_to_bus() which are illegal under - * the API. The entire queue processing structure will need to be - * altered to fix this. - * 2. Need to add memory mapping workaround. Test the memory mapping. - * If it doesn't work revert to I/O port access. Can a test be done - * safely? - * 3. Handle an interrupt not working. Keep an interrupt counter in - * the interrupt handler. In the timeout function if the interrupt - * has not occurred then print a message and run in polled mode. - * 4. Need to add support for target mode commands, cf. CAM XPT. - * 5. check DMA mapping functions for failure - * 6. Use scsi_transport_spi - * 7. advansys_info is not safe against multiple simultaneous callers - * 8. Add module_param to override ISA/VLB ioport array + * 1. Use scsi_transport_spi + * 2. advansys_info is not safe against multiple simultaneous callers + * 3. Add module_param to override ISA/VLB ioport array */ -#warning this driver is still not properly converted to the DMA API /* Enable driver /proc statistics. */ #define ADVANSYS_STATS @@ -76,31 +64,8 @@ /* Enable driver tracing. */ #undef ADVANSYS_DEBUG -/* - * Portable Data Types - * - * Any instance where a 32-bit long or pointer type is assumed - * for precision or HW defined structures, the following define - * types must be used. In Linux the char, short, and int types - * are all consistent at 8, 16, and 32 bits respectively. Pointers - * and long types are 64 bits on Alpha and UltraSPARC. - */ -#define ASC_PADDR __u32 /* Physical/Bus address data type. */ -#define ASC_VADDR __u32 /* Virtual address data type. */ -#define ASC_DCNT __u32 /* Unsigned Data count type. */ -#define ASC_SDCNT __s32 /* Signed Data count type. */ - typedef unsigned char uchar; -#ifndef TRUE -#define TRUE (1) -#endif -#ifndef FALSE -#define FALSE (0) -#endif - -#define ERR (-1) -#define UW_ERR (uint)(0xFFFF) #define isodd_word(val) ((((uint)val) & (uint)0x0001) != 0) #define PCI_VENDOR_ID_ASP 0x10cd @@ -111,15 +76,6 @@ typedef unsigned char uchar; #define PCI_DEVICE_ID_38C0800_REV1 0x2500 #define PCI_DEVICE_ID_38C1600_REV1 0x2700 -/* - * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists. - * The SRB structure will have to be changed and the ASC_SRB2SCSIQ() - * macro re-defined to be able to obtain a ASC_SCSI_Q pointer from the - * SRB structure. - */ -#define CC_VERY_LONG_SG_LIST 0 -#define ASC_SRB2SCSIQ(srb_ptr) (srb_ptr) - #define PortAddr unsigned int /* port address size */ #define inp(port) inb(port) #define outp(port, byte) outb((byte), (port)) @@ -307,15 +263,15 @@ typedef struct asc_scsiq_1 { uchar sg_queue_cnt; uchar target_id; uchar target_lun; - ASC_PADDR data_addr; - ASC_DCNT data_cnt; - ASC_PADDR sense_addr; + __le32 data_addr; + __le32 data_cnt; + __le32 sense_addr; uchar sense_len; uchar extra_bytes; } ASC_SCSIQ_1; typedef struct asc_scsiq_2 { - ASC_VADDR srb_ptr; + u32 srb_tag; uchar target_ix; uchar flag; uchar cdb_len; @@ -338,8 +294,8 @@ typedef struct asc_scsiq_4 { uchar y_res; ushort x_req_count; ushort x_reconnect_rtn; - ASC_PADDR x_saved_data_addr; - ASC_DCNT x_saved_data_cnt; + __le32 x_saved_data_addr; + __le32 x_saved_data_cnt; } ASC_SCSIQ_4; typedef struct asc_q_done_info { @@ -351,12 +307,12 @@ typedef struct asc_q_done_info { uchar sense_len; uchar extra_bytes; uchar res; - ASC_DCNT remain_bytes; + u32 remain_bytes; } ASC_QDONE_INFO; typedef struct asc_sg_list { - ASC_PADDR addr; - ASC_DCNT bytes; + __le32 addr; + __le32 bytes; } ASC_SG_LIST; typedef struct asc_sg_head { @@ -376,17 +332,6 @@ typedef struct asc_scsi_q { ushort next_sg_index; } ASC_SCSI_Q; -typedef struct asc_scsi_req_q { - ASC_SCSIQ_1 r1; - ASC_SCSIQ_2 r2; - uchar *cdbptr; - ASC_SG_HEAD *sg_head; - uchar *sense_ptr; - ASC_SCSIQ_3 r3; - uchar cdb[ASC_MAX_CDB_LEN]; - uchar sense[ASC_MIN_SENSE_LEN]; -} ASC_SCSI_REQ_Q; - typedef struct asc_scsi_bios_req_q { ASC_SCSIQ_1 r1; ASC_SCSIQ_2 r2; @@ -570,7 +515,7 @@ typedef struct asc_dvc_var { dma_addr_t overrun_dma; uchar scsi_reset_wait; uchar chip_no; - char is_in_int; + bool is_in_int; uchar max_total_qng; uchar cur_total_qng; uchar in_critical_cnt; @@ -586,15 +531,13 @@ typedef struct asc_dvc_var { char redo_scam; ushort res2; uchar dos_int13_table[ASC_MAX_TID + 1]; - ASC_DCNT max_dma_count; + unsigned int max_dma_count; ASC_SCSI_BIT_ID_TYPE no_scam; ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer; uchar min_sdtr_index; uchar max_sdtr_index; struct asc_board *drv_ptr; - int ptr_map_count; - void **ptr_map; - ASC_DCNT uc_break; + unsigned int uc_break; } ASC_DVC_VAR; typedef struct asc_dvc_inq_info { @@ -602,8 +545,8 @@ typedef struct asc_dvc_inq_info { } ASC_DVC_INQ_INFO; typedef struct asc_cap_info { - ASC_DCNT lba; - ASC_DCNT blk_size; + u32 lba; + u32 blk_size; } ASC_CAP_INFO; typedef struct asc_cap_info_array { @@ -929,31 +872,6 @@ typedef struct asc_mc_saved { #define AscReadChipDvcID(port) (uchar)inp((port)+IOP_REG_ID) #define AscWriteChipDvcID(port, data) outp((port)+IOP_REG_ID, data) -/* - * Portable Data Types - * - * Any instance where a 32-bit long or pointer type is assumed - * for precision or HW defined structures, the following define - * types must be used. In Linux the char, short, and int types - * are all consistent at 8, 16, and 32 bits respectively. Pointers - * and long types are 64 bits on Alpha and UltraSPARC. - */ -#define ADV_PADDR __u32 /* Physical address data type. */ -#define ADV_VADDR __u32 /* Virtual address data type. */ -#define ADV_DCNT __u32 /* Unsigned Data count type. */ -#define ADV_SDCNT __s32 /* Signed Data count type. */ - -/* - * These macros are used to convert a virtual address to a - * 32-bit value. This currently can be used on Linux Alpha - * which uses 64-bit virtual address but a 32-bit bus address. - * This is likely to break in the future, but doing this now - * will give us time to change the HW and FW to handle 64-bit - * addresses. - */ -#define ADV_VADDR_TO_U32 virt_to_bus -#define ADV_U32_TO_VADDR bus_to_virt - #define AdvPortAddr void __iomem * /* Virtual memory address size */ /* @@ -965,8 +883,6 @@ typedef struct asc_mc_saved { #define ADV_MEM_WRITEW(addr, word) writew(word, addr) #define ADV_MEM_WRITEDW(addr, dword) writel(dword, addr) -#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 15) - /* * Define total number of simultaneous maximum element scatter-gather * request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the @@ -1747,44 +1663,37 @@ typedef struct adveep_38C1600_config { * little-endian. */ typedef struct adv_carr_t { - ADV_VADDR carr_va; /* Carrier Virtual Address */ - ADV_PADDR carr_pa; /* Carrier Physical Address */ - ADV_VADDR areq_vpa; /* ASC_SCSI_REQ_Q Virtual or Physical Address */ + __le32 carr_va; /* Carrier Virtual Address */ + __le32 carr_pa; /* Carrier Physical Address */ + __le32 areq_vpa; /* ADV_SCSI_REQ_Q Virtual or Physical Address */ /* * next_vpa [31:4] Carrier Virtual or Physical Next Pointer * * next_vpa [3:1] Reserved Bits * next_vpa [0] Done Flag set in Response Queue. */ - ADV_VADDR next_vpa; + __le32 next_vpa; } ADV_CARR_T; /* * Mask used to eliminate low 4 bits of carrier 'next_vpa' field. */ -#define ASC_NEXT_VPA_MASK 0xFFFFFFF0 - -#define ASC_RQ_DONE 0x00000001 -#define ASC_RQ_GOOD 0x00000002 -#define ASC_CQ_STOPPER 0x00000000 - -#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK) +#define ADV_NEXT_VPA_MASK 0xFFFFFFF0 -#define ADV_CARRIER_NUM_PAGE_CROSSING \ - (((ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) + (PAGE_SIZE - 1))/PAGE_SIZE) +#define ADV_RQ_DONE 0x00000001 +#define ADV_RQ_GOOD 0x00000002 +#define ADV_CQ_STOPPER 0x00000000 -#define ADV_CARRIER_BUFSIZE \ - ((ADV_CARRIER_COUNT + ADV_CARRIER_NUM_PAGE_CROSSING) * sizeof(ADV_CARR_T)) +#define ADV_GET_CARRP(carrp) ((carrp) & ADV_NEXT_VPA_MASK) /* - * ASC_SCSI_REQ_Q 'a_flag' definitions - * - * The Adv Library should limit use to the lower nibble (4 bits) of - * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag. + * Each carrier is 64 bytes, and we need three additional + * carrier for icq, irq, and the termination carrier. */ -#define ADV_POLL_REQUEST 0x01 /* poll for request completion */ -#define ADV_SCSIQ_DONE 0x02 /* request done */ -#define ADV_DONT_RETRY 0x08 /* don't do retry */ +#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 3) + +#define ADV_CARRIER_BUFSIZE \ + (ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) #define ADV_CHIP_ASC3550 0x01 /* Ultra-Wide IC */ #define ADV_CHIP_ASC38C0800 0x02 /* Ultra2-Wide/LVD IC */ @@ -1816,15 +1725,15 @@ typedef struct adv_dvc_cfg { struct adv_dvc_var; struct adv_scsi_req_q; -typedef struct asc_sg_block { +typedef struct adv_sg_block { uchar reserved1; uchar reserved2; uchar reserved3; uchar sg_cnt; /* Valid entries in block. */ - ADV_PADDR sg_ptr; /* Pointer to next sg block. */ + __le32 sg_ptr; /* Pointer to next sg block. */ struct { - ADV_PADDR sg_addr; /* SG element address. */ - ADV_DCNT sg_count; /* SG element count. */ + __le32 sg_addr; /* SG element address. */ + __le32 sg_count; /* SG element count. */ } sg_list[NO_OF_SG_PER_BLOCK]; } ADV_SG_BLOCK; @@ -1844,10 +1753,10 @@ typedef struct adv_scsi_req_q { uchar target_cmd; uchar target_id; /* Device target identifier. */ uchar target_lun; /* Device target logical unit number. */ - ADV_PADDR data_addr; /* Data buffer physical address. */ - ADV_DCNT data_cnt; /* Data count. Ucode sets to residual. */ - ADV_PADDR sense_addr; - ADV_PADDR carr_pa; + __le32 data_addr; /* Data buffer physical address. */ + __le32 data_cnt; /* Data count. Ucode sets to residual. */ + __le32 sense_addr; + __le32 carr_pa; uchar mflag; uchar sense_len; uchar cdb_len; /* SCSI CDB length. Must <= 16 bytes. */ @@ -1857,29 +1766,26 @@ typedef struct adv_scsi_req_q { uchar host_status; /* Ucode host status. */ uchar sg_working_ix; uchar cdb[12]; /* SCSI CDB bytes 0-11. */ - ADV_PADDR sg_real_addr; /* SG list physical address. */ - ADV_PADDR scsiq_rptr; + __le32 sg_real_addr; /* SG list physical address. */ + __le32 scsiq_rptr; uchar cdb16[4]; /* SCSI CDB bytes 12-15. */ - ADV_VADDR scsiq_ptr; - ADV_VADDR carr_va; + __le32 scsiq_ptr; + __le32 carr_va; /* * End of microcode structure - 60 bytes. The rest of the structure * is used by the Adv Library and ignored by the microcode. */ - ADV_VADDR srb_ptr; + u32 srb_tag; ADV_SG_BLOCK *sg_list_ptr; /* SG list virtual address. */ - char *vdata_addr; /* Data buffer virtual address. */ - uchar a_flag; - uchar pad[2]; /* Pad out to a word boundary. */ } ADV_SCSI_REQ_Q; /* * The following two structures are used to process Wide Board requests. * * The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library - * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the - * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the - * Mid-Level SCSI request structure. + * and microcode with the ADV_SCSI_REQ_Q field 'srb_tag' set to the + * SCSI request tag. The adv_req_t structure 'cmndp' field in turn points + * to the Mid-Level SCSI request structure. * * Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each * ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux @@ -1890,17 +1796,17 @@ typedef struct adv_scsi_req_q { */ typedef struct adv_sgblk { ADV_SG_BLOCK sg_block; /* Sgblock structure. */ - uchar align[32]; /* Sgblock structure padding. */ + dma_addr_t sg_addr; /* Physical address */ struct adv_sgblk *next_sgblkp; /* Next scatter-gather structure. */ } adv_sgblk_t; typedef struct adv_req { ADV_SCSI_REQ_Q scsi_req_q; /* Adv Library request structure. */ - uchar align[32]; /* Request structure padding. */ + uchar align[24]; /* Request structure padding. */ struct scsi_cmnd *cmndp; /* Mid-Level SCSI command pointer. */ + dma_addr_t req_addr; adv_sgblk_t *sgblkp; /* Adv Library scatter-gather pointer. */ - struct adv_req *next_reqp; /* Next Request Structure. */ -} adv_req_t; +} adv_req_t __aligned(32); /* * Adapter operation variable structure. @@ -1937,12 +1843,12 @@ typedef struct adv_dvc_var { uchar chip_scsi_id; /* chip SCSI target ID */ uchar chip_type; uchar bist_err_code; - ADV_CARR_T *carrier_buf; + ADV_CARR_T *carrier; ADV_CARR_T *carr_freelist; /* Carrier free list. */ + dma_addr_t carrier_addr; ADV_CARR_T *icq_sp; /* Initiator command queue stopper pointer. */ ADV_CARR_T *irq_sp; /* Initiator response queue stopper pointer. */ ushort carr_pending_cnt; /* Count of pending carriers. */ - struct adv_req *orig_reqp; /* adv_req_t memory block. */ /* * Note: The following fields will not be used after initialization. The * driver may discard the buffer after initialization is done. @@ -2068,8 +1974,8 @@ do { \ AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV) /* - * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must - * match the ASC_SCSI_REQ_Q 'srb_ptr' field. + * Abort an SRB in the chip's RISC Memory. The 'srb_tag' argument must + * match the ADV_SCSI_REQ_Q 'srb_tag' field. * * If the request has not yet been sent to the device it will simply be * aborted from RISC memory. If the request is disconnected it will be @@ -2079,9 +1985,9 @@ do { \ * ADV_TRUE(1) - Queue was successfully aborted. * ADV_FALSE(0) - Queue was not found on the active queue list. */ -#define AdvAbortQueue(asc_dvc, scsiq) \ - AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \ - (ADV_DCNT) (scsiq)) +#define AdvAbortQueue(asc_dvc, srb_tag) \ + AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \ + (ADV_DCNT) (srb_tag)) /* * Send a Bus Device Reset Message to the specified target ID. @@ -2095,8 +2001,8 @@ do { \ * are not purged. */ #define AdvResetDevice(asc_dvc, target_id) \ - AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \ - (ADV_DCNT) (target_id)) + AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \ + (ADV_DCNT) (target_id)) /* * SCSI Wide Type definition. @@ -2115,7 +2021,7 @@ do { \ #define ADV_TID_TO_TIDMASK(tid) (0x01 << ((tid) & ADV_MAX_TID)) /* - * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values. + * ADV_SCSI_REQ_Q 'done_status' and 'host_status' return values. */ #define QD_NO_STATUS 0x00 /* Request not completed yet. */ @@ -2153,8 +2059,6 @@ do { \ #define QHSTA_M_SGBACKUP_ERROR 0x47 /* Scatter-Gather backup error */ /* Return the address that is aligned at the next doubleword >= to 'addr'. */ -#define ADV_8BALIGN(addr) (((ulong) (addr) + 0x7) & ~0x7) -#define ADV_16BALIGN(addr) (((ulong) (addr) + 0xF) & ~0xF) #define ADV_32BALIGN(addr) (((ulong) (addr) + 0x1F) & ~0x1F) /* @@ -2315,24 +2219,24 @@ do { \ /* Per board statistics structure */ struct asc_stats { /* Driver Entrypoint Statistics */ - ADV_DCNT queuecommand; /* # calls to advansys_queuecommand() */ - ADV_DCNT reset; /* # calls to advansys_eh_bus_reset() */ - ADV_DCNT biosparam; /* # calls to advansys_biosparam() */ - ADV_DCNT interrupt; /* # advansys_interrupt() calls */ - ADV_DCNT callback; /* # calls to asc/adv_isr_callback() */ - ADV_DCNT done; /* # calls to request's scsi_done function */ - ADV_DCNT build_error; /* # asc/adv_build_req() ASC_ERROR returns. */ - ADV_DCNT adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */ - ADV_DCNT adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */ + unsigned int queuecommand; /* # calls to advansys_queuecommand() */ + unsigned int reset; /* # calls to advansys_eh_bus_reset() */ + unsigned int biosparam; /* # calls to advansys_biosparam() */ + unsigned int interrupt; /* # advansys_interrupt() calls */ + unsigned int callback; /* # calls to asc/adv_isr_callback() */ + unsigned int done; /* # calls to request's scsi_done function */ + unsigned int build_error; /* # asc/adv_build_req() ASC_ERROR returns. */ + unsigned int adv_build_noreq; /* # adv_build_req() adv_req_t alloc. fail. */ + unsigned int adv_build_nosg; /* # adv_build_req() adv_sgblk_t alloc. fail. */ /* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */ - ADV_DCNT exe_noerror; /* # ASC_NOERROR returns. */ - ADV_DCNT exe_busy; /* # ASC_BUSY returns. */ - ADV_DCNT exe_error; /* # ASC_ERROR returns. */ - ADV_DCNT exe_unknown; /* # unknown returns. */ + unsigned int exe_noerror; /* # ASC_NOERROR returns. */ + unsigned int exe_busy; /* # ASC_BUSY returns. */ + unsigned int exe_error; /* # ASC_ERROR returns. */ + unsigned int exe_unknown; /* # unknown returns. */ /* Data Transfer Statistics */ - ADV_DCNT xfer_cnt; /* # I/O requests received */ - ADV_DCNT xfer_elem; /* # scatter-gather elements */ - ADV_DCNT xfer_sect; /* # 512-byte blocks */ + unsigned int xfer_cnt; /* # I/O requests received */ + unsigned int xfer_elem; /* # scatter-gather elements */ + unsigned int xfer_sect; /* # 512-byte blocks */ }; #endif /* ADVANSYS_STATS */ @@ -2345,6 +2249,7 @@ struct asc_stats { */ struct asc_board { struct device *dev; + struct Scsi_Host *shost; uint flags; /* Board flags */ unsigned int irq; union { @@ -2366,7 +2271,6 @@ struct asc_board { ADVEEP_38C0800_CONFIG adv_38C0800_eep; /* 38C0800 EEPROM config. */ ADVEEP_38C1600_CONFIG adv_38C1600_eep; /* 38C1600 EEPROM config. */ } eep_config; - ulong last_reset; /* Saved last reset time */ /* /proc/scsi/advansys/[0...] */ #ifdef ADVANSYS_STATS struct asc_stats asc_stats; /* Board statistics */ @@ -2381,7 +2285,9 @@ struct asc_board { void __iomem *ioremap_addr; /* I/O Memory remap address. */ ushort ioport; /* I/O Port address. */ adv_req_t *adv_reqp; /* Request structures. */ - adv_sgblk_t *adv_sgblkp; /* Scatter-gather structures. */ + dma_addr_t adv_reqp_addr; + size_t adv_reqp_size; + struct dma_pool *adv_sgblk_pool; /* Scatter-gather structures. */ ushort bios_signature; /* BIOS Signature. */ ushort bios_version; /* BIOS Version. */ ushort bios_codeseg; /* BIOS Code Segment. */ @@ -2470,12 +2376,11 @@ static void asc_prt_adv_dvc_var(ADV_DVC_VAR *h) printk(" start_motor 0x%x, scsi_reset_wait 0x%x\n", (unsigned)h->start_motor, (unsigned)h->scsi_reset_wait); - printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%lxn\n", + printk(" max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%p\n", (unsigned)h->max_host_qng, (unsigned)h->max_dvc_qng, - (ulong)h->carr_freelist); + h->carr_freelist); - printk(" icq_sp 0x%lx, irq_sp 0x%lx\n", - (ulong)h->icq_sp, (ulong)h->irq_sp); + printk(" icq_sp 0x%p, irq_sp 0x%p\n", h->icq_sp, h->irq_sp); printk(" no_scam 0x%x, tagqng_able 0x%x\n", (unsigned)h->no_scam, (unsigned)h->tagqng_able); @@ -2600,8 +2505,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q) printk("ASC_SCSI_Q at addr 0x%lx\n", (ulong)q); printk - (" target_ix 0x%x, target_lun %u, srb_ptr 0x%lx, tag_code 0x%x,\n", - q->q2.target_ix, q->q1.target_lun, (ulong)q->q2.srb_ptr, + (" target_ix 0x%x, target_lun %u, srb_tag 0x%x, tag_code 0x%x,\n", + q->q2.target_ix, q->q1.target_lun, q->q2.srb_tag, q->q2.tag_code); printk @@ -2634,8 +2539,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q) static void asc_prt_asc_qdone_info(ASC_QDONE_INFO *q) { printk("ASC_QDONE_INFO at addr 0x%lx\n", (ulong)q); - printk(" srb_ptr 0x%lx, target_ix %u, cdb_len %u, tag_code %u,\n", - (ulong)q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len, + printk(" srb_tag 0x%x, target_ix %u, cdb_len %u, tag_code %u,\n", + q->d2.srb_tag, q->d2.target_ix, q->d2.cdb_len, q->d2.tag_code); printk (" done_stat 0x%x, host_stat 0x%x, scsi_stat 0x%x, scsi_msg 0x%x\n", @@ -2651,17 +2556,17 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b) { int i; - printk(" ASC_SG_BLOCK at addr 0x%lx (sgblockno %d)\n", + printk(" ADV_SG_BLOCK at addr 0x%lx (sgblockno %d)\n", (ulong)b, sgblockno); - printk(" sg_cnt %u, sg_ptr 0x%lx\n", - b->sg_cnt, (ulong)le32_to_cpu(b->sg_ptr)); + printk(" sg_cnt %u, sg_ptr 0x%x\n", + b->sg_cnt, (u32)le32_to_cpu(b->sg_ptr)); BUG_ON(b->sg_cnt > NO_OF_SG_PER_BLOCK); if (b->sg_ptr != 0) BUG_ON(b->sg_cnt != NO_OF_SG_PER_BLOCK); for (i = 0; i < b->sg_cnt; i++) { - printk(" [%u]: sg_addr 0x%lx, sg_count 0x%lx\n", - i, (ulong)b->sg_list[i].sg_addr, - (ulong)b->sg_list[i].sg_count); + printk(" [%u]: sg_addr 0x%x, sg_count 0x%x\n", + i, (u32)le32_to_cpu(b->sg_list[i].sg_addr), + (u32)le32_to_cpu(b->sg_list[i].sg_count)); } } @@ -2673,15 +2578,16 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b) static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q) { int sg_blk_cnt; - struct asc_sg_block *sg_ptr; + struct adv_sg_block *sg_ptr; + adv_sgblk_t *sgblkp; printk("ADV_SCSI_REQ_Q at addr 0x%lx\n", (ulong)q); - printk(" target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n", - q->target_id, q->target_lun, (ulong)q->srb_ptr, q->a_flag); + printk(" target_id %u, target_lun %u, srb_tag 0x%x\n", + q->target_id, q->target_lun, q->srb_tag); - printk(" cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n", - q->cntl, (ulong)le32_to_cpu(q->data_addr), (ulong)q->vdata_addr); + printk(" cntl 0x%x, data_addr 0x%lx\n", + q->cntl, (ulong)le32_to_cpu(q->data_addr)); printk(" data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n", (ulong)le32_to_cpu(q->data_cnt), @@ -2700,80 +2606,21 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q) /* Display the request's ADV_SG_BLOCK structures. */ if (q->sg_list_ptr != NULL) { + sgblkp = container_of(q->sg_list_ptr, adv_sgblk_t, sg_block); sg_blk_cnt = 0; - while (1) { - /* - * 'sg_ptr' is a physical address. Convert it to a virtual - * address by indexing 'sg_blk_cnt' into the virtual address - * array 'sg_list_ptr'. - * - * XXX - Assumes all SG physical blocks are virtually contiguous. - */ - sg_ptr = - &(((ADV_SG_BLOCK *)(q->sg_list_ptr))[sg_blk_cnt]); + while (sgblkp) { + sg_ptr = &sgblkp->sg_block; asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr); if (sg_ptr->sg_ptr == 0) { break; } + sgblkp = sgblkp->next_sgblkp; sg_blk_cnt++; } } } #endif /* ADVANSYS_DEBUG */ -/* - * The advansys chip/microcode contains a 32-bit identifier for each command - * known as the 'srb'. I don't know what it stands for. The driver used - * to encode the scsi_cmnd pointer by calling virt_to_bus and retrieve it - * with bus_to_virt. Now the driver keeps a per-host map of integers to - * pointers. It auto-expands when full, unless it can't allocate memory. - * Note that an srb of 0 is treated specially by the chip/firmware, hence - * the return of i+1 in this routine, and the corresponding subtraction in - * the inverse routine. - */ -#define BAD_SRB 0 -static u32 advansys_ptr_to_srb(struct asc_dvc_var *asc_dvc, void *ptr) -{ - int i; - void **new_ptr; - - for (i = 0; i < asc_dvc->ptr_map_count; i++) { - if (!asc_dvc->ptr_map[i]) - goto out; - } - - if (asc_dvc->ptr_map_count == 0) - asc_dvc->ptr_map_count = 1; - else - asc_dvc->ptr_map_count *= 2; - - new_ptr = krealloc(asc_dvc->ptr_map, - asc_dvc->ptr_map_count * sizeof(void *), GFP_ATOMIC); - if (!new_ptr) - return BAD_SRB; - asc_dvc->ptr_map = new_ptr; - out: - ASC_DBG(3, "Putting ptr %p into array offset %d\n", ptr, i); - asc_dvc->ptr_map[i] = ptr; - return i + 1; -} - -static void * advansys_srb_to_ptr(struct asc_dvc_var *asc_dvc, u32 srb) -{ - void *ptr; - - srb--; - if (srb >= asc_dvc->ptr_map_count) { - printk("advansys: bad SRB %u, max %u\n", srb, - asc_dvc->ptr_map_count); - return NULL; - } - ptr = asc_dvc->ptr_map[srb]; - asc_dvc->ptr_map[srb] = NULL; - ASC_DBG(3, "Returning ptr %p from array offset %d\n", ptr, srb); - return ptr; -} - /* * advansys_info() * @@ -3350,7 +3197,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost) seq_printf(m, " flags 0x%x, last_reset 0x%lx, jiffies 0x%lx, asc_n_io_port 0x%x\n", - boardp->flags, boardp->last_reset, jiffies, + boardp->flags, shost->last_reset, jiffies, boardp->asc_n_io_port); seq_printf(m, " io_port 0x%lx\n", shost->io_port); @@ -3844,7 +3691,7 @@ static int AscStartChip(PortAddr iop_base) return (1); } -static int AscStopChip(PortAddr iop_base) +static bool AscStopChip(PortAddr iop_base) { uchar cc_val; @@ -3855,22 +3702,22 @@ static int AscStopChip(PortAddr iop_base) AscSetChipIH(iop_base, INS_HALT); AscSetChipIH(iop_base, INS_RFLAG_WTM); if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) { - return (0); + return false; } - return (1); + return true; } -static int AscIsChipHalted(PortAddr iop_base) +static bool AscIsChipHalted(PortAddr iop_base) { if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) { if ((AscGetChipControl(iop_base) & CC_HALT) != 0) { - return (1); + return true; } } - return (0); + return false; } -static int AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc) +static bool AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc) { PortAddr iop_base; int i = 10; @@ -3953,20 +3800,6 @@ static ushort AscReadLramWord(PortAddr iop_base, ushort addr) return (word_data); } -#if CC_VERY_LONG_SG_LIST -static ASC_DCNT AscReadLramDWord(PortAddr iop_base, ushort addr) -{ - ushort val_low, val_high; - ASC_DCNT dword_data; - - AscSetChipLramAddr(iop_base, addr); - val_low = AscGetChipLramData(iop_base); - val_high = AscGetChipLramData(iop_base); - dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low; - return (dword_data); -} -#endif /* CC_VERY_LONG_SG_LIST */ - static void AscMemWordSetLram(PortAddr iop_base, ushort s_addr, ushort set_wval, int words) { @@ -4068,27 +3901,24 @@ AscMemWordCopyPtrFromLram(PortAddr iop_base, } } -static ASC_DCNT AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words) +static u32 AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words) { - ASC_DCNT sum; + u32 sum = 0; int i; - sum = 0L; for (i = 0; i < words; i++, s_addr += 2) { sum += AscReadLramWord(iop_base, s_addr); } return (sum); } -static ushort AscInitLram(ASC_DVC_VAR *asc_dvc) +static void AscInitLram(ASC_DVC_VAR *asc_dvc) { uchar i; ushort s_addr; PortAddr iop_base; - ushort warn_code; iop_base = asc_dvc->iop_base; - warn_code = 0; AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0, (ushort)(((int)(asc_dvc->max_total_qng + 2 + 1) * 64) >> 1)); @@ -4127,14 +3957,13 @@ static ushort AscInitLram(ASC_DVC_VAR *asc_dvc) AscWriteLramByte(iop_base, (ushort)(s_addr + (ushort)ASC_SCSIQ_B_QNO), i); } - return warn_code; } -static ASC_DCNT +static u32 AscLoadMicroCode(PortAddr iop_base, ushort s_addr, const uchar *mcode_buf, ushort mcode_size) { - ASC_DCNT chksum; + u32 chksum; ushort mcode_word_size; ushort mcode_chksum; @@ -4186,13 +4015,13 @@ static void AscInitQLinkVar(ASC_DVC_VAR *asc_dvc) } } -static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc) +static int AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc) { int i; - ushort warn_code; + int warn_code; PortAddr iop_base; - ASC_PADDR phy_addr; - ASC_DCNT phy_size; + __le32 phy_addr; + __le32 phy_size; struct asc_board *board = asc_dvc_to_board(asc_dvc); iop_base = asc_dvc->iop_base; @@ -4231,12 +4060,12 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc) AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR); if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) { asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR; - warn_code = UW_ERR; + warn_code = -EINVAL; goto err_mcode_start; } if (AscStartChip(iop_base) != 1) { asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP; - warn_code = UW_ERR; + warn_code = -EIO; goto err_mcode_start; } @@ -4250,13 +4079,13 @@ err_dma_map: return warn_code; } -static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc) +static int AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc) { const struct firmware *fw; const char fwname[] = "advansys/mcode.bin"; int err; unsigned long chksum; - ushort warn_code; + int warn_code; PortAddr iop_base; iop_base = asc_dvc->iop_base; @@ -4268,15 +4097,13 @@ static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc) } asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC; if (asc_dvc->err_code != 0) - return UW_ERR; + return ASC_ERROR; if (!AscFindSignature(asc_dvc->iop_base)) { asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE; return warn_code; } AscDisableInterrupt(iop_base); - warn_code |= AscInitLram(asc_dvc); - if (asc_dvc->err_code != 0) - return UW_ERR; + AscInitLram(asc_dvc); err = request_firmware(&fw, fwname, asc_dvc->drv_ptr->dev); if (err) { @@ -4336,7 +4163,7 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf, int size, int memsize, int chksum) { int i, j, end, len = 0; - ADV_DCNT sum; + u32 sum; AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0); @@ -4382,38 +4209,72 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf, return 0; } -static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc) +static void AdvBuildCarrierFreelist(struct adv_dvc_var *adv_dvc) { - ADV_CARR_T *carrp; - ADV_SDCNT buf_size; - ADV_PADDR carr_paddr; + off_t carr_offset = 0, next_offset; + dma_addr_t carr_paddr; + int carr_num = ADV_CARRIER_BUFSIZE / sizeof(ADV_CARR_T), i; - carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf); - asc_dvc->carr_freelist = NULL; - if (carrp == asc_dvc->carrier_buf) { - buf_size = ADV_CARRIER_BUFSIZE; - } else { - buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T); + for (i = 0; i < carr_num; i++) { + carr_offset = i * sizeof(ADV_CARR_T); + /* Get physical address of the carrier 'carrp'. */ + carr_paddr = adv_dvc->carrier_addr + carr_offset; + + adv_dvc->carrier[i].carr_pa = cpu_to_le32(carr_paddr); + adv_dvc->carrier[i].carr_va = cpu_to_le32(carr_offset); + adv_dvc->carrier[i].areq_vpa = 0; + next_offset = carr_offset + sizeof(ADV_CARR_T); + if (i == carr_num) + next_offset = ~0; + adv_dvc->carrier[i].next_vpa = cpu_to_le32(next_offset); } + /* + * We cannot have a carrier with 'carr_va' of '0', as + * a reference to this carrier would be interpreted as + * list termination. + * So start at carrier 1 with the freelist. + */ + adv_dvc->carr_freelist = &adv_dvc->carrier[1]; +} - do { - /* Get physical address of the carrier 'carrp'. */ - carr_paddr = cpu_to_le32(virt_to_bus(carrp)); +static ADV_CARR_T *adv_get_carrier(struct adv_dvc_var *adv_dvc, u32 offset) +{ + int index; - buf_size -= sizeof(ADV_CARR_T); + BUG_ON(offset > ADV_CARRIER_BUFSIZE); - carrp->carr_pa = carr_paddr; - carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp)); + index = offset / sizeof(ADV_CARR_T); + return &adv_dvc->carrier[index]; +} - /* - * Insert the carrier at the beginning of the freelist. - */ - carrp->next_vpa = - cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist)); - asc_dvc->carr_freelist = carrp; +static ADV_CARR_T *adv_get_next_carrier(struct adv_dvc_var *adv_dvc) +{ + ADV_CARR_T *carrp = adv_dvc->carr_freelist; + u32 next_vpa = le32_to_cpu(carrp->next_vpa); - carrp++; - } while (buf_size > 0); + if (next_vpa == 0 || next_vpa == ~0) { + ASC_DBG(1, "invalid vpa offset 0x%x\n", next_vpa); + return NULL; + } + + adv_dvc->carr_freelist = adv_get_carrier(adv_dvc, next_vpa); + /* + * insert stopper carrier to terminate list + */ + carrp->next_vpa = cpu_to_le32(ADV_CQ_STOPPER); + + return carrp; +} + +/* + * 'offset' is the index in the request pointer array + */ +static adv_req_t * adv_get_reqp(struct adv_dvc_var *adv_dvc, u32 offset) +{ + struct asc_board *boardp = adv_dvc->drv_ptr; + + BUG_ON(offset > adv_dvc->max_host_qng); + return &boardp->adv_reqp[offset]; } /* @@ -4432,10 +4293,9 @@ static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc) */ static int AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc, - ushort idle_cmd, ADV_DCNT idle_cmd_parameter) + ushort idle_cmd, u32 idle_cmd_parameter) { - int result; - ADV_DCNT i, j; + int result, i, j; AdvPortAddr iop_base; iop_base = asc_dvc->iop_base; @@ -4902,17 +4762,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc) * Set-up the Host->RISC Initiator Command Queue (ICQ). */ - if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->icq_sp) { asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa)); - - /* - * The first command issued will be placed in the stopper carrier. - */ - asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC ICQ physical address start value. @@ -4922,21 +4776,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc) /* * Set-up the RISC->Host Initiator Response Queue (IRQ). */ - if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->irq_sp) { asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa)); - - /* - * The first command completed by the RISC will be placed in - * the stopper. - * - * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is - * completed the RISC will set the ASC_RQ_STOPPER bit. - */ - asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC IRQ physical address start value. @@ -5399,17 +5243,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc) * Set-up the Host->RISC Initiator Command Queue (ICQ). */ - if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->icq_sp) { + ASC_DBG(0, "Failed to get ICQ carrier\n"); asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa)); - - /* - * The first command issued will be placed in the stopper carrier. - */ - asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC ICQ physical address start value. @@ -5420,21 +5259,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc) /* * Set-up the RISC->Host Initiator Response Queue (IRQ). */ - if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->irq_sp) { + ASC_DBG(0, "Failed to get IRQ carrier\n"); asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa)); - - /* - * The first command completed by the RISC will be placed in - * the stopper. - * - * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is - * completed the RISC will set the ASC_RQ_STOPPER bit. - */ - asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC IRQ physical address start value. @@ -5909,17 +5739,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc) /* * Set-up the Host->RISC Initiator Command Queue (ICQ). */ - if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->icq_sp) { asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa)); - - /* - * The first command issued will be placed in the stopper carrier. - */ - asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC ICQ physical address start value. Initialize the @@ -5933,21 +5757,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc) /* * Set-up the RISC->Host Initiator Response Queue (IRQ). */ - if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) { + asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc); + if (!asc_dvc->irq_sp) { asc_dvc->err_code |= ASC_IERR_NO_CARRIER; return ADV_ERROR; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa)); - - /* - * The first command completed by the RISC will be placed in - * the stopper. - * - * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is - * completed the RISC will set the ASC_RQ_STOPPER bit. - */ - asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); /* * Set RISC IRQ physical address start value. @@ -6134,15 +5948,16 @@ static void adv_async_callback(ADV_DVC_VAR *adv_dvc_varp, uchar code) */ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) { - struct asc_board *boardp; + struct asc_board *boardp = adv_dvc_varp->drv_ptr; + u32 srb_tag; adv_req_t *reqp; adv_sgblk_t *sgblkp; struct scsi_cmnd *scp; - struct Scsi_Host *shost; - ADV_DCNT resid_cnt; + u32 resid_cnt; + dma_addr_t sense_addr; - ASC_DBG(1, "adv_dvc_varp 0x%lx, scsiqp 0x%lx\n", - (ulong)adv_dvc_varp, (ulong)scsiqp); + ASC_DBG(1, "adv_dvc_varp 0x%p, scsiqp 0x%p\n", + adv_dvc_varp, scsiqp); ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp); /* @@ -6150,22 +5965,9 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) * completed. The adv_req_t structure actually contains the * completed ADV_SCSI_REQ_Q structure. */ - reqp = (adv_req_t *)ADV_U32_TO_VADDR(scsiqp->srb_ptr); - ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp); - if (reqp == NULL) { - ASC_PRINT("adv_isr_callback: reqp is NULL\n"); - return; - } + srb_tag = le32_to_cpu(scsiqp->srb_tag); + scp = scsi_host_find_tag(boardp->shost, scsiqp->srb_tag); - /* - * Get the struct scsi_cmnd structure and Scsi_Host structure for the - * command that has been completed. - * - * Note: The adv_req_t request structure and adv_sgblk_t structure, - * if any, are dropped, because a board structure pointer can not be - * determined. - */ - scp = reqp->cmndp; ASC_DBG(1, "scp 0x%p\n", scp); if (scp == NULL) { ASC_PRINT @@ -6174,12 +5976,25 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) } ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len); - shost = scp->device->host; - ASC_STATS(shost, callback); - ASC_DBG(1, "shost 0x%p\n", shost); + reqp = (adv_req_t *)scp->host_scribble; + ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp); + if (reqp == NULL) { + ASC_PRINT("adv_isr_callback: reqp is NULL\n"); + return; + } + /* + * Remove backreferences to avoid duplicate + * command completions. + */ + scp->host_scribble = NULL; + reqp->cmndp = NULL; + + ASC_STATS(boardp->shost, callback); + ASC_DBG(1, "shost 0x%p\n", boardp->shost); - boardp = shost_priv(shost); - BUG_ON(adv_dvc_varp != &boardp->dvc_var.adv_dvc_var); + sense_addr = le32_to_cpu(scsiqp->sense_addr); + dma_unmap_single(boardp->dev, sense_addr, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); /* * 'done_status' contains the command's ending status. @@ -6272,18 +6087,10 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp) /* Remove 'sgblkp' from the request list. */ reqp->sgblkp = sgblkp->next_sgblkp; - /* Add 'sgblkp' to the board free list. */ - sgblkp->next_sgblkp = boardp->adv_sgblkp; - boardp->adv_sgblkp = sgblkp; + dma_pool_free(boardp->adv_sgblk_pool, sgblkp, + sgblkp->sg_addr); } - /* - * Free the adv_req_t structure used with the command by adding - * it back to the board free list. - */ - reqp->next_reqp = boardp->adv_reqp; - boardp->adv_reqp = reqp; - ASC_DBG(1, "done\n"); } @@ -6312,8 +6119,9 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc) uchar int_stat; ushort target_bit; ADV_CARR_T *free_carrp; - ADV_VADDR irq_next_vpa; + __le32 irq_next_vpa; ADV_SCSI_REQ_Q *scsiq; + adv_req_t *reqp; iop_base = asc_dvc->iop_base; @@ -6356,25 +6164,28 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc) * Check if the IRQ stopper carrier contains a completed request. */ while (((irq_next_vpa = - le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ASC_RQ_DONE) != 0) { + le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ADV_RQ_DONE) != 0) { /* * Get a pointer to the newly completed ADV_SCSI_REQ_Q structure. * The RISC will have set 'areq_vpa' to a virtual address. * - * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr + * The firmware will have copied the ADV_SCSI_REQ_Q.scsiq_ptr * field to the carrier ADV_CARR_T.areq_vpa field. The conversion - * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr' + * below complements the conversion of ADV_SCSI_REQ_Q.scsiq_ptr' * in AdvExeScsiQueue(). */ - scsiq = (ADV_SCSI_REQ_Q *) - ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->areq_vpa)); + u32 pa_offset = le32_to_cpu(asc_dvc->irq_sp->areq_vpa); + ASC_DBG(1, "irq_sp %p areq_vpa %u\n", + asc_dvc->irq_sp, pa_offset); + reqp = adv_get_reqp(asc_dvc, pa_offset); + scsiq = &reqp->scsi_req_q; /* * Request finished with good status and the queue was not * DMAed to host memory by the firmware. Set all status fields * to indicate good status. */ - if ((irq_next_vpa & ASC_RQ_GOOD) != 0) { + if ((irq_next_vpa & ADV_RQ_GOOD) != 0) { scsiq->done_status = QD_NO_ERROR; scsiq->host_status = scsiq->scsi_status = 0; scsiq->data_cnt = 0L; @@ -6386,11 +6197,10 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc) * stopper carrier. */ free_carrp = asc_dvc->irq_sp; - asc_dvc->irq_sp = (ADV_CARR_T *) - ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa)); + asc_dvc->irq_sp = adv_get_carrier(asc_dvc, + ADV_GET_CARRP(irq_next_vpa)); - free_carrp->next_vpa = - cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist)); + free_carrp->next_vpa = asc_dvc->carr_freelist->carr_va; asc_dvc->carr_freelist = free_carrp; asc_dvc->carr_pending_cnt--; @@ -6405,7 +6215,6 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc) * Notify the driver of the completed request by passing * the ADV_SCSI_REQ_Q pointer to its callback function. */ - scsiq->a_flag |= ADV_SCSIQ_DONE; adv_isr_callback(asc_dvc, scsiq); /* * Note: After the driver callback function is called, 'scsiq' @@ -6521,11 +6330,11 @@ AscCalSDTRData(ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar syn_offset) return byte; } -static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data) +static bool AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data) { ASC_SCSI_BIT_ID_TYPE org_id; int i; - int sta = TRUE; + bool sta = true; AscSetBank(iop_base, 1); org_id = AscReadChipDvcID(iop_base); @@ -6539,10 +6348,10 @@ static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data) AscSetBank(iop_base, 0); AscSetChipSyn(iop_base, sdtr_data); if (AscGetChipSyn(iop_base) != sdtr_data) { - sta = FALSE; + sta = false; } } else { - sta = FALSE; + sta = false; } AscSetBank(iop_base, 1); AscWriteChipDvcID(iop_base, org_id); @@ -6556,12 +6365,12 @@ static void AscSetChipSDTR(PortAddr iop_base, uchar sdtr_data, uchar tid_no) AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data); } -static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) +static void AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) { EXT_MSG ext_msg; EXT_MSG out_msg; ushort halt_q_addr; - int sdtr_accept; + bool sdtr_accept; ushort int_halt_code; ASC_SCSI_BIT_ID_TYPE scsi_busy; ASC_SCSI_BIT_ID_TYPE target_id; @@ -6603,14 +6412,14 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) boardp->sdtr_data[tid_no] = 0; } AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) { if (asc_dvc->pci_fix_asyn_xfer & target_id) { AscSetChipSDTR(iop_base, asyn_sdtr, tid_no); boardp->sdtr_data[tid_no] = asyn_sdtr; } AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else if (int_halt_code == ASC_HALT_EXTMSG_IN) { AscMemWordCopyPtrFromLram(iop_base, ASCV_MSGIN_BEG, @@ -6620,10 +6429,10 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) if (ext_msg.msg_type == EXTENDED_MESSAGE && ext_msg.msg_req == EXTENDED_SDTR && ext_msg.msg_len == MS_SDTR_LEN) { - sdtr_accept = TRUE; + sdtr_accept = true; if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) { - sdtr_accept = FALSE; + sdtr_accept = false; ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET; } if ((ext_msg.xfer_period < @@ -6631,7 +6440,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) || (ext_msg.xfer_period > asc_dvc->sdtr_period_tbl[asc_dvc-> max_sdtr_index])) { - sdtr_accept = FALSE; + sdtr_accept = false; ext_msg.xfer_period = asc_dvc->sdtr_period_tbl[asc_dvc-> min_sdtr_index]; @@ -6696,7 +6505,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) (ushort)ASC_SCSIQ_B_CNTL), q_cntl); AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else if (ext_msg.msg_type == EXTENDED_MESSAGE && ext_msg.msg_req == EXTENDED_WDTR && ext_msg.msg_len == MS_WDTR_LEN) { @@ -6712,7 +6521,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) (ushort)ASC_SCSIQ_B_CNTL), q_cntl); AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else { ext_msg.msg_type = MESSAGE_REJECT; @@ -6726,7 +6535,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) (ushort)ASC_SCSIQ_B_CNTL), q_cntl); AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } } else if (int_halt_code == ASC_HALT_CHK_CONDITION) { @@ -6783,7 +6592,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) AscWriteLramByte(iop_base, (ushort)ASCV_SCSIBUSY_B, scsi_busy); AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else if (int_halt_code == ASC_HALT_SDTR_REJECTED) { AscMemWordCopyPtrFromLram(iop_base, @@ -6805,7 +6614,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) (ushort)(halt_q_addr + (ushort)ASC_SCSIQ_B_CNTL), q_cntl); AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) { scsi_status = AscReadLramByte(iop_base, @@ -6850,166 +6659,9 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc) } } AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); - } -#if CC_VERY_LONG_SG_LIST - else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC) { - uchar q_no; - ushort q_addr; - uchar sg_wk_q_no; - uchar first_sg_wk_q_no; - ASC_SCSI_Q *scsiq; /* Ptr to driver request. */ - ASC_SG_HEAD *sg_head; /* Ptr to driver SG request. */ - ASC_SG_LIST_Q scsi_sg_q; /* Structure written to queue. */ - ushort sg_list_dwords; - ushort sg_entry_cnt; - uchar next_qp; - int i; - - q_no = AscReadLramByte(iop_base, (ushort)ASCV_REQ_SG_LIST_QP); - if (q_no == ASC_QLINK_END) - return 0; - - q_addr = ASC_QNO_TO_QADDR(q_no); - - /* - * Convert the request's SRB pointer to a host ASC_SCSI_REQ - * structure pointer using a macro provided by the driver. - * The ASC_SCSI_REQ pointer provides a pointer to the - * host ASC_SG_HEAD structure. - */ - /* Read request's SRB pointer. */ - scsiq = (ASC_SCSI_Q *) - ASC_SRB2SCSIQ(ASC_U32_TO_VADDR(AscReadLramDWord(iop_base, - (ushort) - (q_addr + - ASC_SCSIQ_D_SRBPTR)))); - - /* - * Get request's first and working SG queue. - */ - sg_wk_q_no = AscReadLramByte(iop_base, - (ushort)(q_addr + - ASC_SCSIQ_B_SG_WK_QP)); - - first_sg_wk_q_no = AscReadLramByte(iop_base, - (ushort)(q_addr + - ASC_SCSIQ_B_FIRST_SG_WK_QP)); - - /* - * Reset request's working SG queue back to the - * first SG queue. - */ - AscWriteLramByte(iop_base, - (ushort)(q_addr + - (ushort)ASC_SCSIQ_B_SG_WK_QP), - first_sg_wk_q_no); - - sg_head = scsiq->sg_head; - - /* - * Set sg_entry_cnt to the number of SG elements - * that will be completed on this interrupt. - * - * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1 - * SG elements. The data_cnt and data_addr fields which - * add 1 to the SG element capacity are not used when - * restarting SG handling after a halt. - */ - if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) { - sg_entry_cnt = ASC_MAX_SG_LIST - 1; - - /* - * Keep track of remaining number of SG elements that - * will need to be handled on the next interrupt. - */ - scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1); - } else { - sg_entry_cnt = scsiq->remain_sg_entry_cnt; - scsiq->remain_sg_entry_cnt = 0; - } - - /* - * Copy SG elements into the list of allocated SG queues. - * - * Last index completed is saved in scsiq->next_sg_index. - */ - next_qp = first_sg_wk_q_no; - q_addr = ASC_QNO_TO_QADDR(next_qp); - scsi_sg_q.sg_head_qp = q_no; - scsi_sg_q.cntl = QCSG_SG_XFER_LIST; - for (i = 0; i < sg_head->queue_cnt; i++) { - scsi_sg_q.seq_no = i + 1; - if (sg_entry_cnt > ASC_SG_LIST_PER_Q) { - sg_list_dwords = (uchar)(ASC_SG_LIST_PER_Q * 2); - sg_entry_cnt -= ASC_SG_LIST_PER_Q; - /* - * After very first SG queue RISC FW uses next - * SG queue first element then checks sg_list_cnt - * against zero and then decrements, so set - * sg_list_cnt 1 less than number of SG elements - * in each SG queue. - */ - scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1; - scsi_sg_q.sg_cur_list_cnt = - ASC_SG_LIST_PER_Q - 1; - } else { - /* - * This is the last SG queue in the list of - * allocated SG queues. If there are more - * SG elements than will fit in the allocated - * queues, then set the QCSG_SG_XFER_MORE flag. - */ - if (scsiq->remain_sg_entry_cnt != 0) { - scsi_sg_q.cntl |= QCSG_SG_XFER_MORE; - } else { - scsi_sg_q.cntl |= QCSG_SG_XFER_END; - } - /* equals sg_entry_cnt * 2 */ - sg_list_dwords = sg_entry_cnt << 1; - scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1; - scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1; - sg_entry_cnt = 0; - } - - scsi_sg_q.q_no = next_qp; - AscMemWordCopyPtrToLram(iop_base, - q_addr + ASC_SCSIQ_SGHD_CPY_BEG, - (uchar *)&scsi_sg_q, - sizeof(ASC_SG_LIST_Q) >> 1); - - AscMemDWordCopyPtrToLram(iop_base, - q_addr + ASC_SGQ_LIST_BEG, - (uchar *)&sg_head-> - sg_list[scsiq->next_sg_index], - sg_list_dwords); - - scsiq->next_sg_index += ASC_SG_LIST_PER_Q; - - /* - * If the just completed SG queue contained the - * last SG element, then no more SG queues need - * to be written. - */ - if (scsi_sg_q.cntl & QCSG_SG_XFER_END) { - break; - } - - next_qp = AscReadLramByte(iop_base, - (ushort)(q_addr + - ASC_SCSIQ_B_FWD)); - q_addr = ASC_QNO_TO_QADDR(next_qp); - } - - /* - * Clear the halt condition so the RISC will be restarted - * after the return. - */ - AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0); - return (0); + return; } -#endif /* CC_VERY_LONG_SG_LIST */ - return (0); + return; } /* @@ -7043,7 +6695,7 @@ DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words) static uchar _AscCopyLramScsiDoneQ(PortAddr iop_base, ushort q_addr, - ASC_QDONE_INFO *scsiq, ASC_DCNT max_dma_count) + ASC_QDONE_INFO *scsiq, unsigned int max_dma_count) { ushort _val; uchar sg_queue_cnt; @@ -7070,10 +6722,10 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base, /* * Read high word of remain bytes from alternate location. */ - scsiq->remain_bytes = (((ADV_DCNT)AscReadLramWord(iop_base, - (ushort)(q_addr + - (ushort) - ASC_SCSIQ_W_ALT_DC1))) + scsiq->remain_bytes = (((u32)AscReadLramWord(iop_base, + (ushort)(q_addr + + (ushort) + ASC_SCSIQ_W_ALT_DC1))) << 16); /* * Read low word of remain bytes from original location. @@ -7093,25 +6745,24 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base, */ static void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep) { - struct asc_board *boardp; + struct asc_board *boardp = asc_dvc_varp->drv_ptr; + u32 srb_tag; struct scsi_cmnd *scp; - struct Scsi_Host *shost; ASC_DBG(1, "asc_dvc_varp 0x%p, qdonep 0x%p\n", asc_dvc_varp, qdonep); ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep); - scp = advansys_srb_to_ptr(asc_dvc_varp, qdonep->d2.srb_ptr); + /* + * Decrease the srb_tag by 1 to find the SCSI command + */ + srb_tag = qdonep->d2.srb_tag - 1; + scp = scsi_host_find_tag(boardp->shost, srb_tag); if (!scp) return; ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len); - shost = scp->device->host; - ASC_STATS(shost, callback); - ASC_DBG(1, "shost 0x%p\n", shost); - - boardp = shost_priv(shost); - BUG_ON(asc_dvc_varp != &boardp->dvc_var.asc_dvc_var); + ASC_STATS(boardp->shost, callback); dma_unmap_single(boardp->dev, scp->SCp.dma_handle, SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); @@ -7220,7 +6871,7 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc) uchar cur_target_qng; ASC_QDONE_INFO scsiq_buf; ASC_QDONE_INFO *scsiq; - int false_overrun; + bool false_overrun; iop_base = asc_dvc->iop_base; n_q_used = 1; @@ -7294,14 +6945,17 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc) scsiq->d3.done_stat = QD_WITH_ERROR; goto FATAL_ERR_QDONE; } - if ((scsiq->d2.srb_ptr == 0UL) || + if ((scsiq->d2.srb_tag == 0UL) || ((scsiq->q_status & QS_ABORTED) != 0)) { return (0x11); } else if (scsiq->q_status == QS_DONE) { - false_overrun = FALSE; + /* + * This is also curious. + * false_overrun will _always_ be set to 'false' + */ + false_overrun = false; if (scsiq->extra_bytes != 0) { - scsiq->remain_bytes += - (ADV_DCNT)scsiq->extra_bytes; + scsiq->remain_bytes += scsiq->extra_bytes; } if (scsiq->d3.done_stat == QD_WITH_ERROR) { if (scsiq->d3.host_stat == @@ -7372,23 +7026,23 @@ static int AscISR(ASC_DVC_VAR *asc_dvc) uchar host_flag; iop_base = asc_dvc->iop_base; - int_pending = FALSE; + int_pending = ASC_FALSE; if (AscIsIntPending(iop_base) == 0) return int_pending; if ((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0) { - return ERR; + return ASC_ERROR; } if (asc_dvc->in_critical_cnt != 0) { AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL); - return ERR; + return ASC_ERROR; } if (asc_dvc->is_in_int) { AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY); - return ERR; + return ASC_ERROR; } - asc_dvc->is_in_int = TRUE; + asc_dvc->is_in_int = true; ctrl_reg = AscGetChipControl(iop_base); saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET | CC_SINGLE_STEP | CC_DIAG | CC_TEST)); @@ -7396,7 +7050,7 @@ static int AscISR(ASC_DVC_VAR *asc_dvc) if (chipstat & CSW_SCSI_RESET_LATCH) { if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) { int i = 10; - int_pending = TRUE; + int_pending = ASC_TRUE; asc_dvc->sdtr_done = 0; saved_ctrl_reg &= (uchar)(~CC_HALT); while ((AscGetChipStatus(iop_base) & @@ -7418,15 +7072,11 @@ static int AscISR(ASC_DVC_VAR *asc_dvc) (uchar)(host_flag | (uchar)ASC_HOST_FLAG_IN_ISR)); if ((chipstat & CSW_INT_PENDING) || (int_pending)) { AscAckInterrupt(iop_base); - int_pending = TRUE; + int_pending = ASC_TRUE; if ((chipstat & CSW_HALTED) && (ctrl_reg & CC_SINGLE_STEP)) { - if (AscIsrChipHalted(asc_dvc) == ERR) { - goto ISR_REPORT_QDONE_FATAL_ERROR; - } else { - saved_ctrl_reg &= (uchar)(~CC_HALT); - } + AscIsrChipHalted(asc_dvc); + saved_ctrl_reg &= (uchar)(~CC_HALT); } else { - ISR_REPORT_QDONE_FATAL_ERROR: if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) { while (((status = AscIsrQDone(asc_dvc)) & 0x01) != 0) { @@ -7440,20 +7090,20 @@ static int AscISR(ASC_DVC_VAR *asc_dvc) } while (status == 0x11); } if ((status & 0x80) != 0) - int_pending = ERR; + int_pending = ASC_ERROR; } } AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag); AscSetChipLramAddr(iop_base, saved_ram_addr); AscSetChipControl(iop_base, saved_ctrl_reg); - asc_dvc->is_in_int = FALSE; + asc_dvc->is_in_int = false; return int_pending; } /* * advansys_reset() * - * Reset the bus associated with the command 'scp'. + * Reset the host associated with the command 'scp'. * * This function runs its own thread. Interrupts must be blocked but * sleeping is allowed and no locking other than for host structures is @@ -7471,7 +7121,7 @@ static int advansys_reset(struct scsi_cmnd *scp) ASC_STATS(shost, reset); - scmd_printk(KERN_INFO, scp, "SCSI bus reset started...\n"); + scmd_printk(KERN_INFO, scp, "SCSI host reset started...\n"); if (ASC_NARROW_BOARD(boardp)) { ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var; @@ -7482,20 +7132,19 @@ static int advansys_reset(struct scsi_cmnd *scp) /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */ if (asc_dvc->err_code || !asc_dvc->overrun_dma) { - scmd_printk(KERN_INFO, scp, "SCSI bus reset error: " + scmd_printk(KERN_INFO, scp, "SCSI host reset error: " "0x%x, status: 0x%x\n", asc_dvc->err_code, status); ret = FAILED; } else if (status) { - scmd_printk(KERN_INFO, scp, "SCSI bus reset warning: " + scmd_printk(KERN_INFO, scp, "SCSI host reset warning: " "0x%x\n", status); } else { - scmd_printk(KERN_INFO, scp, "SCSI bus reset " + scmd_printk(KERN_INFO, scp, "SCSI host reset " "successful\n"); } ASC_DBG(1, "after AscInitAsc1000Driver()\n"); - spin_lock_irqsave(shost->host_lock, flags); } else { /* * If the suggest reset bus flags are set, then reset the bus. @@ -7504,28 +7153,25 @@ static int advansys_reset(struct scsi_cmnd *scp) ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var; /* - * Reset the target's SCSI bus. + * Reset the chip and SCSI bus. */ ASC_DBG(1, "before AdvResetChipAndSB()\n"); switch (AdvResetChipAndSB(adv_dvc)) { case ASC_TRUE: - scmd_printk(KERN_INFO, scp, "SCSI bus reset " + scmd_printk(KERN_INFO, scp, "SCSI host reset " "successful\n"); break; case ASC_FALSE: default: - scmd_printk(KERN_INFO, scp, "SCSI bus reset error\n"); + scmd_printk(KERN_INFO, scp, "SCSI host reset error\n"); ret = FAILED; break; } spin_lock_irqsave(shost->host_lock, flags); AdvISR(adv_dvc); + spin_unlock_irqrestore(shost->host_lock, flags); } - /* Save the time of the most recently completed reset. */ - boardp->last_reset = jiffies; - spin_unlock_irqrestore(shost->host_lock, flags); - ASC_DBG(1, "ret %d\n", ret); return ret; @@ -7584,9 +7230,10 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id) struct Scsi_Host *shost = dev_id; struct asc_board *boardp = shost_priv(shost); irqreturn_t result = IRQ_NONE; + unsigned long flags; ASC_DBG(2, "boardp 0x%p\n", boardp); - spin_lock(shost->host_lock); + spin_lock_irqsave(shost->host_lock, flags); if (ASC_NARROW_BOARD(boardp)) { if (AscIsIntPending(shost->io_port)) { result = IRQ_HANDLED; @@ -7601,38 +7248,38 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id) ASC_STATS(shost, interrupt); } } - spin_unlock(shost->host_lock); + spin_unlock_irqrestore(shost->host_lock, flags); ASC_DBG(1, "end\n"); return result; } -static int AscHostReqRiscHalt(PortAddr iop_base) +static bool AscHostReqRiscHalt(PortAddr iop_base) { int count = 0; - int sta = 0; + bool sta = false; uchar saved_stop_code; if (AscIsChipHalted(iop_base)) - return (1); + return true; saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B); AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP); do { if (AscIsChipHalted(iop_base)) { - sta = 1; + sta = true; break; } mdelay(100); } while (count++ < 20); AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code); - return (sta); + return sta; } -static int +static bool AscSetRunChipSynRegAtID(PortAddr iop_base, uchar tid_no, uchar sdtr_data) { - int sta = FALSE; + bool sta = false; if (AscHostReqRiscHalt(iop_base)) { sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data); @@ -7851,13 +7498,17 @@ static int advansys_slave_configure(struct scsi_device *sdev) return 0; } -static __le32 advansys_get_sense_buffer_dma(struct scsi_cmnd *scp) +static __le32 asc_get_sense_buffer_dma(struct scsi_cmnd *scp) { struct asc_board *board = shost_priv(scp->device->host); + scp->SCp.dma_handle = dma_map_single(board->dev, scp->sense_buffer, - SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); - dma_cache_sync(board->dev, scp->sense_buffer, - SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + SCSI_SENSE_BUFFERSIZE, + DMA_FROM_DEVICE); + if (dma_mapping_error(board->dev, scp->SCp.dma_handle)) { + ASC_DBG(1, "failed to map sense buffer\n"); + return 0; + } return cpu_to_le32(scp->SCp.dma_handle); } @@ -7866,17 +7517,16 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, { struct asc_dvc_var *asc_dvc = &boardp->dvc_var.asc_dvc_var; int use_sg; + u32 srb_tag; memset(asc_scsi_q, 0, sizeof(*asc_scsi_q)); /* - * Point the ASC_SCSI_Q to the 'struct scsi_cmnd'. + * Set the srb_tag to the command tag + 1, as + * srb_tag '0' is used internally by the chip. */ - asc_scsi_q->q2.srb_ptr = advansys_ptr_to_srb(asc_dvc, scp); - if (asc_scsi_q->q2.srb_ptr == BAD_SRB) { - scp->result = HOST_BYTE(DID_SOFT_ERROR); - return ASC_ERROR; - } + srb_tag = scp->request->tag + 1; + asc_scsi_q->q2.srb_tag = srb_tag; /* * Build the ASC_SCSI_Q request. @@ -7887,8 +7537,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, asc_scsi_q->q1.target_lun = scp->device->lun; asc_scsi_q->q2.target_ix = ASC_TIDLUN_TO_IX(scp->device->id, scp->device->lun); - asc_scsi_q->q1.sense_addr = advansys_get_sense_buffer_dma(scp); + asc_scsi_q->q1.sense_addr = asc_get_sense_buffer_dma(scp); asc_scsi_q->q1.sense_len = SCSI_SENSE_BUFFERSIZE; + if (!asc_scsi_q->q1.sense_addr) + return ASC_BUSY; /* * If there are any outstanding requests for the current target, @@ -7910,7 +7562,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, /* Build ASC_SCSI_Q */ use_sg = scsi_dma_map(scp); - if (use_sg != 0) { + if (use_sg < 0) { + ASC_DBG(1, "failed to map sglist\n"); + return ASC_BUSY; + } else if (use_sg > 0) { int sgcnt; struct scatterlist *slp; struct asc_sg_head *asc_sg_head; @@ -7975,20 +7630,19 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, * ADV_ERROR(-1) - SG List creation failed */ static int -adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, - int use_sg) +adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, + ADV_SCSI_REQ_Q *scsiqp, struct scsi_cmnd *scp, int use_sg) { - adv_sgblk_t *sgblkp; - ADV_SCSI_REQ_Q *scsiqp; + adv_sgblk_t *sgblkp, *prev_sgblkp; struct scatterlist *slp; int sg_elem_cnt; ADV_SG_BLOCK *sg_block, *prev_sg_block; - ADV_PADDR sg_block_paddr; + dma_addr_t sgblk_paddr; int i; - scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q); slp = scsi_sglist(scp); sg_elem_cnt = use_sg; + prev_sgblkp = NULL; prev_sg_block = NULL; reqp->sgblkp = NULL; @@ -7998,7 +7652,9 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, * list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK * (15) scatter-gather elements. */ - if ((sgblkp = boardp->adv_sgblkp) == NULL) { + sgblkp = dma_pool_alloc(boardp->adv_sgblk_pool, GFP_ATOMIC, + &sgblk_paddr); + if (!sgblkp) { ASC_DBG(1, "no free adv_sgblk_t\n"); ASC_STATS(scp->device->host, adv_build_nosg); @@ -8009,24 +7665,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, while ((sgblkp = reqp->sgblkp) != NULL) { /* Remove 'sgblkp' from the request list. */ reqp->sgblkp = sgblkp->next_sgblkp; - - /* Add 'sgblkp' to the board free list. */ - sgblkp->next_sgblkp = boardp->adv_sgblkp; - boardp->adv_sgblkp = sgblkp; + sgblkp->next_sgblkp = NULL; + dma_pool_free(boardp->adv_sgblk_pool, sgblkp, + sgblkp->sg_addr); } return ASC_BUSY; } - /* Complete 'adv_sgblk_t' board allocation. */ - boardp->adv_sgblkp = sgblkp->next_sgblkp; + sgblkp->sg_addr = sgblk_paddr; sgblkp->next_sgblkp = NULL; - - /* - * Get 8 byte aligned virtual and physical addresses - * for the allocated ADV_SG_BLOCK structure. - */ - sg_block = (ADV_SG_BLOCK *)ADV_8BALIGN(&sgblkp->sg_block); - sg_block_paddr = virt_to_bus(sg_block); + sg_block = &sgblkp->sg_block; /* * Check if this is the first 'adv_sgblk_t' for the @@ -8041,17 +7689,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, * address pointers. */ scsiqp->sg_list_ptr = sg_block; - scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr); + scsiqp->sg_real_addr = cpu_to_le32(sgblk_paddr); } else { /* Request's second or later scatter-gather block. */ - sgblkp->next_sgblkp = reqp->sgblkp; - reqp->sgblkp = sgblkp; + prev_sgblkp->next_sgblkp = sgblkp; /* * Point the previous ADV_SG_BLOCK structure to * the newly allocated ADV_SG_BLOCK structure. */ - prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr); + prev_sg_block->sg_ptr = cpu_to_le32(sgblk_paddr); } for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) { @@ -8062,15 +7709,19 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, ASC_STATS_ADD(scp->device->host, xfer_sect, DIV_ROUND_UP(sg_dma_len(slp), 512)); - if (--sg_elem_cnt == 0) { /* Last ADV_SG_BLOCK and scatter-gather entry. */ + if (--sg_elem_cnt == 0) { + /* + * Last ADV_SG_BLOCK and scatter-gather entry. + */ sg_block->sg_cnt = i + 1; - sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */ + sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */ return ADV_SUCCESS; } slp++; } sg_block->sg_cnt = NO_OF_SG_PER_BLOCK; prev_sg_block = sg_block; + prev_sgblkp = sgblkp; } } @@ -8080,38 +7731,35 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp, * If an adv_req_t can not be allocated to issue the request, * then return ASC_BUSY. If an error occurs, then return ASC_ERROR. * - * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the + * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the * microcode for DMA addresses or math operations are byte swapped * to little-endian order. */ static int adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, - ADV_SCSI_REQ_Q **adv_scsiqpp) + adv_req_t **adv_reqpp) { + u32 srb_tag = scp->request->tag; adv_req_t *reqp; ADV_SCSI_REQ_Q *scsiqp; - int i; int ret; int use_sg; + dma_addr_t sense_addr; /* * Allocate an adv_req_t structure from the board to execute * the command. */ - if (boardp->adv_reqp == NULL) { + reqp = &boardp->adv_reqp[srb_tag]; + if (reqp->cmndp && reqp->cmndp != scp ) { ASC_DBG(1, "no free adv_req_t\n"); ASC_STATS(scp->device->host, adv_build_noreq); return ASC_BUSY; - } else { - reqp = boardp->adv_reqp; - boardp->adv_reqp = reqp->next_reqp; - reqp->next_reqp = NULL; } - /* - * Get 32-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers. - */ - scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q); + reqp->req_addr = boardp->adv_reqp_addr + (srb_tag * sizeof(adv_req_t)); + + scsiqp = &reqp->scsi_req_q; /* * Initialize the structure. @@ -8119,14 +7767,15 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0; /* - * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure. + * Set the srb_tag to the command tag. */ - scsiqp->srb_ptr = ADV_VADDR_TO_U32(reqp); + scsiqp->srb_tag = srb_tag; /* - * Set the adv_req_t 'cmndp' to point to the struct scsi_cmnd structure. + * Set 'host_scribble' to point to the adv_req_t structure. */ reqp->cmndp = scp; + scp->host_scribble = (void *)reqp; /* * Build the ADV_SCSI_REQ_Q request. @@ -8135,28 +7784,38 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, /* Set CDB length and copy it to the request structure. */ scsiqp->cdb_len = scp->cmd_len; /* Copy first 12 CDB bytes to cdb[]. */ - for (i = 0; i < scp->cmd_len && i < 12; i++) { - scsiqp->cdb[i] = scp->cmnd[i]; - } + memcpy(scsiqp->cdb, scp->cmnd, scp->cmd_len < 12 ? scp->cmd_len : 12); /* Copy last 4 CDB bytes, if present, to cdb16[]. */ - for (; i < scp->cmd_len; i++) { - scsiqp->cdb16[i - 12] = scp->cmnd[i]; + if (scp->cmd_len > 12) { + int cdb16_len = scp->cmd_len - 12; + + memcpy(scsiqp->cdb16, &scp->cmnd[12], cdb16_len); } scsiqp->target_id = scp->device->id; scsiqp->target_lun = scp->device->lun; - scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0])); + sense_addr = dma_map_single(boardp->dev, scp->sense_buffer, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(boardp->dev, sense_addr)) { + ASC_DBG(1, "failed to map sense buffer\n"); + ASC_STATS(scp->device->host, adv_build_noreq); + return ASC_BUSY; + } + scsiqp->sense_addr = cpu_to_le32(sense_addr); scsiqp->sense_len = SCSI_SENSE_BUFFERSIZE; /* Build ADV_SCSI_REQ_Q */ use_sg = scsi_dma_map(scp); - if (use_sg == 0) { + if (use_sg < 0) { + ASC_DBG(1, "failed to map SG list\n"); + ASC_STATS(scp->device->host, adv_build_noreq); + return ASC_BUSY; + } else if (use_sg == 0) { /* Zero-length transfer */ reqp->sgblkp = NULL; scsiqp->data_cnt = 0; - scsiqp->vdata_addr = NULL; scsiqp->data_addr = 0; scsiqp->sg_list_ptr = NULL; @@ -8168,27 +7827,20 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, scp->device->host->sg_tablesize); scsi_dma_unmap(scp); scp->result = HOST_BYTE(DID_ERROR); - - /* - * Free the 'adv_req_t' structure by adding it back - * to the board free list. - */ - reqp->next_reqp = boardp->adv_reqp; - boardp->adv_reqp = reqp; + reqp->cmndp = NULL; + scp->host_scribble = NULL; return ASC_ERROR; } scsiqp->data_cnt = cpu_to_le32(scsi_bufflen(scp)); - ret = adv_get_sglist(boardp, reqp, scp, use_sg); + ret = adv_get_sglist(boardp, reqp, scsiqp, scp, use_sg); if (ret != ADV_SUCCESS) { - /* - * Free the adv_req_t structure by adding it back to - * the board free list. - */ - reqp->next_reqp = boardp->adv_reqp; - boardp->adv_reqp = reqp; + scsi_dma_unmap(scp); + scp->result = HOST_BYTE(DID_ERROR); + reqp->cmndp = NULL; + scp->host_scribble = NULL; return ret; } @@ -8201,7 +7853,7 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp, ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp); ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len); - *adv_scsiqpp = scsiqp; + *adv_reqpp = reqp; return ASC_NOERROR; } @@ -8358,8 +8010,8 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no) int i; ASC_SG_HEAD *sg_head; ASC_SG_LIST_Q scsi_sg_q; - ASC_DCNT saved_data_addr; - ASC_DCNT saved_data_cnt; + __le32 saved_data_addr; + __le32 saved_data_cnt; PortAddr iop_base; ushort sg_list_dwords; ushort sg_index; @@ -8371,42 +8023,15 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no) sg_head = scsiq->sg_head; saved_data_addr = scsiq->q1.data_addr; saved_data_cnt = scsiq->q1.data_cnt; - scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr; - scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes; -#if CC_VERY_LONG_SG_LIST + scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr); + scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes); /* - * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST - * then not all SG elements will fit in the allocated queues. - * The rest of the SG elements will be copied when the RISC - * completes the SG elements that fit and halts. + * Set sg_entry_cnt to be the number of SG elements that + * will fit in the allocated SG queues. It is minus 1, because + * the first SG element is handled above. */ - if (sg_head->entry_cnt > ASC_MAX_SG_LIST) { - /* - * Set sg_entry_cnt to be the number of SG elements that - * will fit in the allocated SG queues. It is minus 1, because - * the first SG element is handled above. ASC_MAX_SG_LIST is - * already inflated by 1 to account for this. For example it - * may be 50 which is 1 + 7 queues * 7 SG elements. - */ - sg_entry_cnt = ASC_MAX_SG_LIST - 1; + sg_entry_cnt = sg_head->entry_cnt - 1; - /* - * Keep track of remaining number of SG elements that will - * need to be handled from a_isr.c. - */ - scsiq->remain_sg_entry_cnt = - sg_head->entry_cnt - ASC_MAX_SG_LIST; - } else { -#endif /* CC_VERY_LONG_SG_LIST */ - /* - * Set sg_entry_cnt to be the number of SG elements that - * will fit in the allocated SG queues. It is minus 1, because - * the first SG element is handled above. - */ - sg_entry_cnt = sg_head->entry_cnt - 1; -#if CC_VERY_LONG_SG_LIST - } -#endif /* CC_VERY_LONG_SG_LIST */ if (sg_entry_cnt != 0) { scsiq->q1.cntl |= QC_SG_HEAD; q_addr = ASC_QNO_TO_QADDR(q_no); @@ -8431,21 +8056,7 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no) ASC_SG_LIST_PER_Q - 1; } } else { -#if CC_VERY_LONG_SG_LIST - /* - * This is the last SG queue in the list of - * allocated SG queues. If there are more - * SG elements than will fit in the allocated - * queues, then set the QCSG_SG_XFER_MORE flag. - */ - if (sg_head->entry_cnt > ASC_MAX_SG_LIST) { - scsi_sg_q.cntl |= QCSG_SG_XFER_MORE; - } else { -#endif /* CC_VERY_LONG_SG_LIST */ - scsi_sg_q.cntl |= QCSG_SG_XFER_END; -#if CC_VERY_LONG_SG_LIST - } -#endif /* CC_VERY_LONG_SG_LIST */ + scsi_sg_q.cntl |= QCSG_SG_XFER_END; sg_list_dwords = sg_entry_cnt << 1; if (i == 0) { scsi_sg_q.sg_list_cnt = sg_entry_cnt; @@ -8550,9 +8161,9 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) PortAddr iop_base; int sta; int n_q_required; - int disable_syn_offset_one_fix; + bool disable_syn_offset_one_fix; int i; - ASC_PADDR addr; + u32 addr; ushort sg_entry_cnt = 0; ushort sg_entry_cnt_minus_one = 0; uchar target_ix; @@ -8562,12 +8173,12 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) uchar scsi_cmd; uchar disable_cmd; ASC_SG_HEAD *sg_head; - ASC_DCNT data_cnt; + unsigned long data_cnt; iop_base = asc_dvc->iop_base; sg_head = scsiq->sg_head; if (asc_dvc->err_code != 0) - return (ERR); + return ASC_ERROR; scsiq->q1.q_no = 0; if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) { scsiq->q1.extra_bytes = 0; @@ -8593,46 +8204,41 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) } if (asc_dvc->in_critical_cnt != 0) { AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY); - return (ERR); + return ASC_ERROR; } asc_dvc->in_critical_cnt++; if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) { if ((sg_entry_cnt = sg_head->entry_cnt) == 0) { asc_dvc->in_critical_cnt--; - return (ERR); + return ASC_ERROR; } -#if !CC_VERY_LONG_SG_LIST if (sg_entry_cnt > ASC_MAX_SG_LIST) { asc_dvc->in_critical_cnt--; - return (ERR); + return ASC_ERROR; } -#endif /* !CC_VERY_LONG_SG_LIST */ if (sg_entry_cnt == 1) { - scsiq->q1.data_addr = - (ADV_PADDR)sg_head->sg_list[0].addr; - scsiq->q1.data_cnt = - (ADV_DCNT)sg_head->sg_list[0].bytes; + scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr); + scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes); scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE); } sg_entry_cnt_minus_one = sg_entry_cnt - 1; } scsi_cmd = scsiq->cdbptr[0]; - disable_syn_offset_one_fix = FALSE; + disable_syn_offset_one_fix = false; if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) && !(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) { if (scsiq->q1.cntl & QC_SG_HEAD) { data_cnt = 0; for (i = 0; i < sg_entry_cnt; i++) { - data_cnt += - (ADV_DCNT)le32_to_cpu(sg_head->sg_list[i]. - bytes); + data_cnt += le32_to_cpu(sg_head->sg_list[i]. + bytes); } } else { data_cnt = le32_to_cpu(scsiq->q1.data_cnt); } if (data_cnt != 0UL) { if (data_cnt < 512UL) { - disable_syn_offset_one_fix = TRUE; + disable_syn_offset_one_fix = true; } else { for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST; i++) { @@ -8643,7 +8249,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) } if (scsi_cmd == disable_cmd) { disable_syn_offset_one_fix = - TRUE; + true; break; } } @@ -8662,12 +8268,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) { if ((scsi_cmd == READ_6) || (scsi_cmd == READ_10)) { - addr = - (ADV_PADDR)le32_to_cpu(sg_head-> + addr = le32_to_cpu(sg_head-> sg_list [sg_entry_cnt_minus_one]. addr) + - (ADV_DCNT)le32_to_cpu(sg_head-> + le32_to_cpu(sg_head-> sg_list [sg_entry_cnt_minus_one]. bytes); @@ -8688,8 +8293,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) sg_list [sg_entry_cnt_minus_one]. bytes); - data_cnt -= - (ASC_DCNT) extra_bytes; + data_cnt -= extra_bytes; sg_head-> sg_list [sg_entry_cnt_minus_one]. @@ -8700,16 +8304,6 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) } } sg_head->entry_to_copy = sg_head->entry_cnt; -#if CC_VERY_LONG_SG_LIST - /* - * Set the sg_entry_cnt to the maximum possible. The rest of - * the SG elements will be copied when the RISC completes the - * SG elements that fit and halts. - */ - if (sg_entry_cnt > ASC_MAX_SG_LIST) { - sg_entry_cnt = ASC_MAX_SG_LIST; - } -#endif /* CC_VERY_LONG_SG_LIST */ n_q_required = AscSgListToQueue(sg_entry_cnt); if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >= (uint) n_q_required) @@ -8744,8 +8338,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) == 0) { scsiq->q2.tag_code |= ASC_TAG_FLAG_EXTRA_BYTES; - data_cnt -= (ASC_DCNT) - extra_bytes; + data_cnt -= extra_bytes; scsiq->q1.data_cnt = cpu_to_le32 (data_cnt); @@ -8780,7 +8373,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) * If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be * set to SCSI_MAX_RETRY. * - * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode + * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the microcode * for DMA addresses or math operations are byte swapped to little-endian * order. * @@ -8791,11 +8384,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq) * ADV_ERROR(-1) - Invalid ADV_SCSI_REQ_Q request structure * host IC error. */ -static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq) +static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, adv_req_t *reqp) { AdvPortAddr iop_base; - ADV_PADDR req_paddr; ADV_CARR_T *new_carrp; + ADV_SCSI_REQ_Q *scsiq = &reqp->scsi_req_q; /* * The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID. @@ -8812,39 +8405,19 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq) * Allocate a carrier ensuring at least one carrier always * remains on the freelist and initialize fields. */ - if ((new_carrp = asc_dvc->carr_freelist) == NULL) { + new_carrp = adv_get_next_carrier(asc_dvc); + if (!new_carrp) { + ASC_DBG(1, "No free carriers\n"); return ADV_BUSY; } - asc_dvc->carr_freelist = (ADV_CARR_T *) - ADV_U32_TO_VADDR(le32_to_cpu(new_carrp->next_vpa)); - asc_dvc->carr_pending_cnt++; - - /* - * Set the carrier to be a stopper by setting 'next_vpa' - * to the stopper value. The current stopper will be changed - * below to point to the new stopper. - */ - new_carrp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER); - - /* - * Clear the ADV_SCSI_REQ_Q done flag. - */ - scsiq->a_flag &= ~ADV_SCSIQ_DONE; - req_paddr = virt_to_bus(scsiq); - BUG_ON(req_paddr & 31); - /* Wait for assertion before making little-endian */ - req_paddr = cpu_to_le32(req_paddr); + asc_dvc->carr_pending_cnt++; /* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */ - scsiq->scsiq_ptr = cpu_to_le32(ADV_VADDR_TO_U32(scsiq)); - scsiq->scsiq_rptr = req_paddr; + scsiq->scsiq_ptr = cpu_to_le32(scsiq->srb_tag); + scsiq->scsiq_rptr = cpu_to_le32(reqp->req_addr); - scsiq->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->icq_sp)); - /* - * Every ADV_CARR_T.carr_pa is byte swapped to little-endian - * order during initialization. - */ + scsiq->carr_va = asc_dvc->icq_sp->carr_va; scsiq->carr_pa = asc_dvc->icq_sp->carr_pa; /* @@ -8852,7 +8425,7 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq) * the microcode. The newly allocated stopper will become the new * stopper. */ - asc_dvc->icq_sp->areq_vpa = req_paddr; + asc_dvc->icq_sp->areq_vpa = scsiq->scsiq_rptr; /* * Set the 'next_vpa' pointer for the old stopper to be the @@ -8907,11 +8480,10 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp) ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var; struct asc_scsi_q asc_scsi_q; - /* asc_build_req() can not return ASC_BUSY. */ ret = asc_build_req(boardp, scp, &asc_scsi_q); - if (ret == ASC_ERROR) { + if (ret != ASC_NOERROR) { ASC_STATS(scp->device->host, build_error); - return ASC_ERROR; + return ret; } ret = AscExeScsiQueue(asc_dvc, &asc_scsi_q); @@ -8919,9 +8491,9 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp) err_code = asc_dvc->err_code; } else { ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var; - ADV_SCSI_REQ_Q *adv_scsiqp; + adv_req_t *adv_reqp; - switch (adv_build_req(boardp, scp, &adv_scsiqp)) { + switch (adv_build_req(boardp, scp, &adv_reqp)) { case ASC_NOERROR: ASC_DBG(3, "adv_build_req ASC_NOERROR\n"); break; @@ -8941,7 +8513,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp) return ASC_ERROR; } - ret = AdvExeScsiQueue(adv_dvc, adv_scsiqp); + ret = AdvExeScsiQueue(adv_dvc, adv_reqp); err_code = adv_dvc->err_code; } @@ -8956,6 +8528,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp) ASC_DBG(1, "ExeScsiQueue() ASC_NOERROR\n"); break; case ASC_BUSY: + ASC_DBG(1, "ExeScsiQueue() ASC_BUSY\n"); ASC_STATS(scp->device->host, exe_busy); break; case ASC_ERROR: @@ -9122,7 +8695,7 @@ static int AscStopQueueExe(PortAddr iop_base) return (0); } -static ASC_DCNT AscGetMaxDmaCount(ushort bus_type) +static unsigned int AscGetMaxDmaCount(ushort bus_type) { if (bus_type & ASC_IS_ISA) return ASC_MAX_ISA_DMA_COUNT; @@ -9183,15 +8756,13 @@ static uchar AscSetIsaDmaSpeed(PortAddr iop_base, uchar speed_value) } #endif /* CONFIG_ISA */ -static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc) +static void AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc) { int i; PortAddr iop_base; - ushort warn_code; uchar chip_version; iop_base = asc_dvc->iop_base; - warn_code = 0; asc_dvc->err_code = 0; if ((asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) { @@ -9205,7 +8776,7 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc) /* asc_dvc->init_state initialized in AscInitGetConfig(). */ asc_dvc->sdtr_done = 0; asc_dvc->cur_total_qng = 0; - asc_dvc->is_in_int = 0; + asc_dvc->is_in_int = false; asc_dvc->in_critical_cnt = 0; asc_dvc->last_q_shortage = 0; asc_dvc->use_tagged_qng = 0; @@ -9267,7 +8838,6 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc) asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *)0L; asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG; } - return warn_code; } static int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg) @@ -9385,7 +8955,7 @@ static int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg) int retry; retry = 0; - while (TRUE) { + while (true) { AscSetChipEEPData(iop_base, data_reg); mdelay(1); read_back = AscGetChipEEPData(iop_base); @@ -9521,7 +9091,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, int n_error; retry = 0; - while (TRUE) { + while (true) { if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf, bus_type)) == 0) { break; @@ -9533,7 +9103,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf, return n_error; } -static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc) +static int AscInitFromEEP(ASC_DVC_VAR *asc_dvc) { ASCEEP_CONFIG eep_config_buf; ASCEEP_CONFIG *eep_config; @@ -9548,13 +9118,13 @@ static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc) warn_code = 0; AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE); AscStopQueueExe(iop_base); - if ((AscStopChip(iop_base) == FALSE) || + if ((AscStopChip(iop_base)) || (AscGetChipScsiCtrl(iop_base) != 0)) { asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE; AscResetChipAndScsiBus(asc_dvc); mdelay(asc_dvc->scsi_reset_wait * 1000); /* XXX: msleep? */ } - if (AscIsChipHalted(iop_base) == FALSE) { + if (!AscIsChipHalted(iop_base)) { asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP; return (warn_code); } @@ -9709,8 +9279,8 @@ static int AscInitGetConfig(struct Scsi_Host *shost) return asc_dvc->err_code; if (AscFindSignature(asc_dvc->iop_base)) { - warn_code |= AscInitAscDvcVar(asc_dvc); - warn_code |= AscInitFromEEP(asc_dvc); + AscInitAscDvcVar(asc_dvc); + warn_code = AscInitFromEEP(asc_dvc); asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG; if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT) asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT; @@ -9866,6 +9436,7 @@ static int AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *shost) * on big-endian platforms so char fields read as words are actually being * unswapped on big-endian platforms. */ +#ifdef CONFIG_PCI static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config = { ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */ 0x0000, /* cfg_msw */ @@ -10202,7 +9773,6 @@ static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar = { 0 /* 63 reserved */ }; -#ifdef CONFIG_PCI /* * Wait for EEPROM command to complete */ @@ -11232,7 +10802,7 @@ static struct scsi_host_template advansys_template = { .name = DRV_NAME, .info = advansys_info, .queuecommand = advansys_queuecommand, - .eh_bus_reset_handler = advansys_reset, + .eh_host_reset_handler = advansys_reset, .bios_param = advansys_biosparam, .slave_configure = advansys_slave_configure, /* @@ -11240,7 +10810,7 @@ static struct scsi_host_template advansys_template = { * must be set. The flag will be cleared in advansys_board_found * for non-ISA adapters. */ - .unchecked_isa_dma = 1, + .unchecked_isa_dma = true, /* * All adapters controlled by this driver are capable of large * scatter-gather lists. According to the mid-level SCSI documentation @@ -11255,20 +10825,18 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost) { struct asc_board *board = shost_priv(shost); struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var; - int req_cnt = 0; - adv_req_t *reqp = NULL; - int sg_cnt = 0; - adv_sgblk_t *sgp; + size_t sgblk_pool_size; int warn_code, err_code; /* * Allocate buffer carrier structures. The total size - * is about 4 KB, so allocate all at once. + * is about 8 KB, so allocate all at once. */ - adv_dvc->carrier_buf = kmalloc(ADV_CARRIER_BUFSIZE, GFP_KERNEL); - ASC_DBG(1, "carrier_buf 0x%p\n", adv_dvc->carrier_buf); + adv_dvc->carrier = dma_alloc_coherent(board->dev, + ADV_CARRIER_BUFSIZE, &adv_dvc->carrier_addr, GFP_KERNEL); + ASC_DBG(1, "carrier 0x%p\n", adv_dvc->carrier); - if (!adv_dvc->carrier_buf) + if (!adv_dvc->carrier) goto kmalloc_failed; /* @@ -11276,54 +10844,34 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost) * board. The total size is about 16 KB, so allocate all at once. * If the allocation fails decrement and try again. */ - for (req_cnt = adv_dvc->max_host_qng; req_cnt > 0; req_cnt--) { - reqp = kmalloc(sizeof(adv_req_t) * req_cnt, GFP_KERNEL); - - ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", reqp, req_cnt, - (ulong)sizeof(adv_req_t) * req_cnt); - - if (reqp) - break; + board->adv_reqp_size = adv_dvc->max_host_qng * sizeof(adv_req_t); + if (board->adv_reqp_size & 0x1f) { + ASC_DBG(1, "unaligned reqp %lu bytes\n", sizeof(adv_req_t)); + board->adv_reqp_size = ADV_32BALIGN(board->adv_reqp_size); } + board->adv_reqp = dma_alloc_coherent(board->dev, board->adv_reqp_size, + &board->adv_reqp_addr, GFP_KERNEL); - if (!reqp) + if (!board->adv_reqp) goto kmalloc_failed; - adv_dvc->orig_reqp = reqp; + ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", board->adv_reqp, + adv_dvc->max_host_qng, board->adv_reqp_size); /* * Allocate up to ADV_TOT_SG_BLOCK request structures for * the Wide board. Each structure is about 136 bytes. */ - board->adv_sgblkp = NULL; - for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) { - sgp = kmalloc(sizeof(adv_sgblk_t), GFP_KERNEL); + sgblk_pool_size = sizeof(adv_sgblk_t) * ADV_TOT_SG_BLOCK; + board->adv_sgblk_pool = dma_pool_create("adv_sgblk", board->dev, + sgblk_pool_size, 32, 0); - if (!sgp) - break; - - sgp->next_sgblkp = board->adv_sgblkp; - board->adv_sgblkp = sgp; + ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", ADV_TOT_SG_BLOCK, + sizeof(adv_sgblk_t), sgblk_pool_size); - } - - ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", sg_cnt, sizeof(adv_sgblk_t), - sizeof(adv_sgblk_t) * sg_cnt); - - if (!board->adv_sgblkp) + if (!board->adv_sgblk_pool) goto kmalloc_failed; - /* - * Point 'adv_reqp' to the request structures and - * link them together. - */ - req_cnt--; - reqp[req_cnt].next_reqp = NULL; - for (; req_cnt > 0; req_cnt--) { - reqp[req_cnt - 1].next_reqp = &reqp[req_cnt]; - } - board->adv_reqp = &reqp[0]; - if (adv_dvc->chip_type == ADV_CHIP_ASC3550) { ASC_DBG(2, "AdvInitAsc3550Driver()\n"); warn_code = AdvInitAsc3550Driver(adv_dvc); @@ -11353,14 +10901,20 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost) static void advansys_wide_free_mem(struct asc_board *board) { struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var; - kfree(adv_dvc->carrier_buf); - adv_dvc->carrier_buf = NULL; - kfree(adv_dvc->orig_reqp); - adv_dvc->orig_reqp = board->adv_reqp = NULL; - while (board->adv_sgblkp) { - adv_sgblk_t *sgp = board->adv_sgblkp; - board->adv_sgblkp = sgp->next_sgblkp; - kfree(sgp); + + if (adv_dvc->carrier) { + dma_free_coherent(board->dev, ADV_CARRIER_BUFSIZE, + adv_dvc->carrier, adv_dvc->carrier_addr); + adv_dvc->carrier = NULL; + } + if (board->adv_reqp) { + dma_free_coherent(board->dev, board->adv_reqp_size, + board->adv_reqp, board->adv_reqp_addr); + board->adv_reqp = NULL; + } + if (board->adv_sgblk_pool) { + dma_pool_destroy(board->adv_sgblk_pool); + board->adv_sgblk_pool = NULL; } } @@ -11431,28 +10985,28 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop, switch (asc_dvc_varp->bus_type) { #ifdef CONFIG_ISA case ASC_IS_ISA: - shost->unchecked_isa_dma = TRUE; + shost->unchecked_isa_dma = true; share_irq = 0; break; case ASC_IS_VL: - shost->unchecked_isa_dma = FALSE; + shost->unchecked_isa_dma = false; share_irq = 0; break; case ASC_IS_EISA: - shost->unchecked_isa_dma = FALSE; + shost->unchecked_isa_dma = false; share_irq = IRQF_SHARED; break; #endif /* CONFIG_ISA */ #ifdef CONFIG_PCI case ASC_IS_PCI: - shost->unchecked_isa_dma = FALSE; + shost->unchecked_isa_dma = false; share_irq = IRQF_SHARED; break; #endif /* CONFIG_PCI */ default: shost_printk(KERN_ERR, shost, "unknown adapter type: " "%d\n", asc_dvc_varp->bus_type); - shost->unchecked_isa_dma = TRUE; + shost->unchecked_isa_dma = false; share_irq = 0; break; } @@ -11471,7 +11025,7 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop, * For Wide boards set PCI information before calling * AdvInitGetConfig(). */ - shost->unchecked_isa_dma = FALSE; + shost->unchecked_isa_dma = false; share_irq = IRQF_SHARED; ASC_DBG(2, "AdvInitGetConfig()\n"); @@ -11657,24 +11211,6 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop, shost->can_queue = adv_dvc_varp->max_host_qng; } - /* - * Following v1.3.89, 'cmd_per_lun' is no longer needed - * and should be set to zero. - * - * But because of a bug introduced in v1.3.89 if the driver is - * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level - * SCSI function 'allocate_device' will panic. To allow the driver - * to work as a module in these kernels set 'cmd_per_lun' to 1. - * - * Note: This is wrong. cmd_per_lun should be set to the depth - * you want on untagged devices always. - #ifdef MODULE - */ - shost->cmd_per_lun = 1; -/* #else - shost->cmd_per_lun = 0; -#endif */ - /* * Set the maximum number of scatter-gather elements the * adapter can handle. @@ -11844,7 +11380,9 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop, err_unmap: if (boardp->ioremap_addr) iounmap(boardp->ioremap_addr); +#ifdef CONFIG_PCI err_shost: +#endif return ret; } @@ -11927,6 +11465,7 @@ static int advansys_isa_probe(struct device *dev, unsigned int id) board = shost_priv(shost); board->irq = advansys_isa_irq_no(iop_base); board->dev = dev; + board->shost = shost; err = advansys_board_found(shost, iop_base, ASC_IS_ISA); if (err) @@ -12009,6 +11548,7 @@ static int advansys_vlb_probe(struct device *dev, unsigned int id) board = shost_priv(shost); board->irq = advansys_vlb_irq_no(iop_base); board->dev = dev; + board->shost = shost; err = advansys_board_found(shost, iop_base, ASC_IS_VL); if (err) @@ -12116,6 +11656,7 @@ static int advansys_eisa_probe(struct device *dev) board = shost_priv(shost); board->irq = irq; board->dev = dev; + board->shost = shost; err = advansys_board_found(shost, ioport, ASC_IS_EISA); if (!err) { @@ -12232,6 +11773,7 @@ static int advansys_pci_probe(struct pci_dev *pdev, board = shost_priv(shost); board->irq = pdev->irq; board->dev = &pdev->dev; + board->shost = shost; if (pdev->device == PCI_DEVICE_ID_ASP_ABP940UW || pdev->device == PCI_DEVICE_ID_38C0800_REV1 || diff --git a/kernel/drivers/scsi/aha152x.c b/kernel/drivers/scsi/aha152x.c index e31c460a1..f44d04872 100644 --- a/kernel/drivers/scsi/aha152x.c +++ b/kernel/drivers/scsi/aha152x.c @@ -2922,7 +2922,6 @@ static struct scsi_host_template aha152x_driver_template = { .can_queue = 1, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, .slave_alloc = aha152x_adjust_queue, }; diff --git a/kernel/drivers/scsi/aha1542.c b/kernel/drivers/scsi/aha1542.c index b95d2779f..5b8b2937a 100644 --- a/kernel/drivers/scsi/aha1542.c +++ b/kernel/drivers/scsi/aha1542.c @@ -950,7 +950,6 @@ static struct scsi_host_template driver_template = { .can_queue = AHA1542_MAILBOXES, .this_id = 7, .sg_tablesize = 16, - .cmd_per_lun = 1, .unchecked_isa_dma = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/aha1740.c b/kernel/drivers/scsi/aha1740.c index 31ace4bef..bad35ffc0 100644 --- a/kernel/drivers/scsi/aha1740.c +++ b/kernel/drivers/scsi/aha1740.c @@ -544,7 +544,6 @@ static struct scsi_host_template aha1740_template = { .can_queue = AHA1740_ECBS, .this_id = 7, .sg_tablesize = AHA1740_SCATTER, - .cmd_per_lun = AHA1740_CMDLUN, .use_clustering = ENABLE_CLUSTERING, .eh_abort_handler = aha1740_eh_abort_handler, }; diff --git a/kernel/drivers/scsi/aha1740.h b/kernel/drivers/scsi/aha1740.h index af23fd6bd..b0c560346 100644 --- a/kernel/drivers/scsi/aha1740.h +++ b/kernel/drivers/scsi/aha1740.h @@ -149,6 +149,5 @@ struct ecb { /* Enhanced Control Block 6.1 */ #define AHA1740_ECBS 32 #define AHA1740_SCATTER 16 -#define AHA1740_CMDLUN 1 #endif diff --git a/kernel/drivers/scsi/aic7xxx/aic79xx_osm.c b/kernel/drivers/scsi/aic7xxx/aic79xx_osm.c index ce96a0be3..2588b8f84 100644 --- a/kernel/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/kernel/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -925,7 +925,6 @@ struct scsi_host_template aic79xx_driver_template = { .slave_configure = ahd_linux_slave_configure, .target_alloc = ahd_linux_target_alloc, .target_destroy = ahd_linux_target_destroy, - .use_blk_tags = 1, }; /******************************** Bus DMA *************************************/ diff --git a/kernel/drivers/scsi/aic7xxx/aic7xxx_core.c b/kernel/drivers/scsi/aic7xxx/aic7xxx_core.c index c4829d84b..64ab9eaec 100644 --- a/kernel/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/kernel/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -79,7 +79,7 @@ struct ahc_hard_error_entry { static const struct ahc_hard_error_entry ahc_hard_errors[] = { { ILLHADDR, "Illegal Host Access" }, - { ILLSADDR, "Illegal Sequencer Address referrenced" }, + { ILLSADDR, "Illegal Sequencer Address referenced" }, { ILLOPCODE, "Illegal Opcode in sequencer program" }, { SQPARERR, "Sequencer Parity Error" }, { DPARERR, "Data-path Parity Error" }, diff --git a/kernel/drivers/scsi/aic7xxx/aic7xxx_osm.c b/kernel/drivers/scsi/aic7xxx/aic7xxx_osm.c index a2f2c774c..b846a4683 100644 --- a/kernel/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/kernel/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -812,7 +812,6 @@ struct scsi_host_template aic7xxx_driver_template = { .slave_configure = ahc_linux_slave_configure, .target_alloc = ahc_linux_target_alloc, .target_destroy = ahc_linux_target_destroy, - .use_blk_tags = 1, }; /**************************** Tasklet Handler *********************************/ diff --git a/kernel/drivers/scsi/aic94xx/aic94xx_init.c b/kernel/drivers/scsi/aic94xx/aic94xx_init.c index 02a2512b7..662b2321d 100644 --- a/kernel/drivers/scsi/aic94xx/aic94xx_init.c +++ b/kernel/drivers/scsi/aic94xx/aic94xx_init.c @@ -65,7 +65,6 @@ static struct scsi_host_template aic94xx_sht = { .change_queue_depth = sas_change_queue_depth, .bios_param = sas_bios_param, .can_queue = 1, - .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, @@ -74,7 +73,6 @@ static struct scsi_host_template aic94xx_sht = { .eh_bus_reset_handler = sas_eh_bus_reset_handler, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -101,15 +99,11 @@ static int asd_map_memio(struct asd_ha_struct *asd_ha) pci_name(asd_ha->pcidev)); goto Err; } - if (io_handle->flags & IORESOURCE_CACHEABLE) - io_handle->addr = ioremap(io_handle->start, - io_handle->len); - else - io_handle->addr = ioremap_nocache(io_handle->start, - io_handle->len); + io_handle->addr = ioremap(io_handle->start, io_handle->len); if (!io_handle->addr) { asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1, pci_name(asd_ha->pcidev)); + err = -ENOMEM; goto Err_unreq; } } @@ -709,10 +703,10 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha) { int err; + scsi_remove_host(asd_ha->sas_ha.core.shost); err = sas_unregister_ha(&asd_ha->sas_ha); sas_remove_host(asd_ha->sas_ha.core.shost); - scsi_remove_host(asd_ha->sas_ha.core.shost); scsi_host_put(asd_ha->sas_ha.core.shost); kfree(asd_ha->sas_ha.sas_phy); diff --git a/kernel/drivers/scsi/aic94xx/aic94xx_sds.c b/kernel/drivers/scsi/aic94xx/aic94xx_sds.c index edb43fda9..c831e3041 100644 --- a/kernel/drivers/scsi/aic94xx/aic94xx_sds.c +++ b/kernel/drivers/scsi/aic94xx/aic94xx_sds.c @@ -983,7 +983,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, { int err, i; u32 offs, size; - struct asd_ll_el *el; + struct asd_ll_el *el = NULL; struct asd_ctrla_phy_settings *ps; struct asd_ctrla_phy_settings dflt_ps; @@ -1004,6 +1004,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, size = sizeof(struct asd_ctrla_phy_settings); ps = &dflt_ps; + goto out_process; } if (size == 0) @@ -1028,7 +1029,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha, ASD_DPRINTK("couldn't find ctrla phy settings struct\n"); goto out2; } - +out_process: err = asd_process_ctrla_phy_settings(asd_ha, ps); if (err) { ASD_DPRINTK("couldn't process ctrla phy settings\n"); diff --git a/kernel/drivers/scsi/arcmsr/arcmsr_hba.c b/kernel/drivers/scsi/arcmsr/arcmsr_hba.c index 914c39f9f..333db5953 100644 --- a/kernel/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/kernel/drivers/scsi/arcmsr/arcmsr_hba.c @@ -259,10 +259,7 @@ static bool arcmsr_remap_pciregion(struct AdapterControlBlock *acb) addr = (unsigned long)pci_resource_start(pdev, 0); range = pci_resource_len(pdev, 0); flags = pci_resource_flags(pdev, 0); - if (flags & IORESOURCE_CACHEABLE) - mem_base0 = ioremap(addr, range); - else - mem_base0 = ioremap_nocache(addr, range); + mem_base0 = ioremap(addr, range); if (!mem_base0) { pr_notice("arcmsr%d: memory mapping region fail\n", acb->host->host_no); @@ -3264,7 +3261,7 @@ static int arcmsr_iop_confirm(struct AdapterControlBlock *acb) reg->doneq_index = 0; writel(ARCMSR_MESSAGE_SET_POST_WINDOW, reg->drv2iop_doorbell); if (!arcmsr_hbaB_wait_msgint_ready(acb)) { - printk(KERN_NOTICE "arcmsr%d:can not set diver mode\n", \ + printk(KERN_NOTICE "arcmsr%d: cannot set driver mode\n", \ acb->host->host_no); return 1; } diff --git a/kernel/drivers/scsi/arm/arxescsi.c b/kernel/drivers/scsi/arm/arxescsi.c index 32d23212d..3110736fd 100644 --- a/kernel/drivers/scsi/arm/arxescsi.c +++ b/kernel/drivers/scsi/arm/arxescsi.c @@ -245,7 +245,6 @@ static struct scsi_host_template arxescsi_template = { .can_queue = 0, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, .proc_name = "arxescsi", }; diff --git a/kernel/drivers/scsi/arm/cumana_2.c b/kernel/drivers/scsi/arm/cumana_2.c index abc66f526..faa1bee07 100644 --- a/kernel/drivers/scsi/arm/cumana_2.c +++ b/kernel/drivers/scsi/arm/cumana_2.c @@ -367,7 +367,6 @@ static struct scsi_host_template cumanascsi2_template = { .this_id = 7, .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, .dma_boundary = IOMD_DMA_BOUNDARY, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, .proc_name = "cumanascsi2", }; diff --git a/kernel/drivers/scsi/arm/eesox.c b/kernel/drivers/scsi/arm/eesox.c index 5bf3c0d13..a8ad6880d 100644 --- a/kernel/drivers/scsi/arm/eesox.c +++ b/kernel/drivers/scsi/arm/eesox.c @@ -486,7 +486,6 @@ static struct scsi_host_template eesox_template = { .this_id = 7, .sg_tablesize = SCSI_MAX_SG_CHAIN_SEGMENTS, .dma_boundary = IOMD_DMA_BOUNDARY, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, .proc_name = "eesox", }; diff --git a/kernel/drivers/scsi/atp870u.c b/kernel/drivers/scsi/atp870u.c index 0836433e3..05301bc75 100644 --- a/kernel/drivers/scsi/atp870u.c +++ b/kernel/drivers/scsi/atp870u.c @@ -3158,7 +3158,6 @@ static struct scsi_host_template atp870u_template = { .can_queue = qcnt /* can_queue */, .this_id = 7 /* SCSI ID */, .sg_tablesize = ATP870U_SCATTER /*SG_ALL*/ /*SG_NONE*/, - .cmd_per_lun = ATP870U_CMDLUN /* commands per lun */, .use_clustering = ENABLE_CLUSTERING, .max_sectors = ATP870U_MAX_SECTORS, }; diff --git a/kernel/drivers/scsi/atp870u.h b/kernel/drivers/scsi/atp870u.h index 62bae64a0..5cf62566a 100644 --- a/kernel/drivers/scsi/atp870u.h +++ b/kernel/drivers/scsi/atp870u.h @@ -10,7 +10,6 @@ #define MAX_SENSE 14 #define qcnt 32 #define ATP870U_SCATTER 128 -#define ATP870U_CMDLUN 1 #define MAX_ADAPTER 8 #define MAX_SCSI_ID 16 diff --git a/kernel/drivers/scsi/be2iscsi/Kconfig b/kernel/drivers/scsi/be2iscsi/Kconfig index ceaca32e7..4e7cad272 100644 --- a/kernel/drivers/scsi/be2iscsi/Kconfig +++ b/kernel/drivers/scsi/be2iscsi/Kconfig @@ -1,9 +1,9 @@ config BE2ISCSI - tristate "ServerEngines' 10Gbps iSCSI - BladeEngine 2" + tristate "Emulex 10Gbps iSCSI - BladeEngine 2" depends on PCI && SCSI && NET select SCSI_ISCSI_ATTRS select ISCSI_BOOT_SYSFS help - This driver implements the iSCSI functionality for ServerEngines' + This driver implements the iSCSI functionality for Emulex 10Gbps Storage adapter - BladeEngine 2. diff --git a/kernel/drivers/scsi/be2iscsi/Makefile b/kernel/drivers/scsi/be2iscsi/Makefile index c11f443e3..d0488eaaf 100644 --- a/kernel/drivers/scsi/be2iscsi/Makefile +++ b/kernel/drivers/scsi/be2iscsi/Makefile @@ -1,5 +1,5 @@ # -# Makefile to build the iSCSI driver for ServerEngine's BladeEngine. +# Makefile to build the iSCSI driver for Emulex OneConnect. # # diff --git a/kernel/drivers/scsi/be2iscsi/be.h b/kernel/drivers/scsi/be2iscsi/be.h index 32070099c..77f992e74 100644 --- a/kernel/drivers/scsi/be2iscsi/be.h +++ b/kernel/drivers/scsi/be2iscsi/be.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -10,7 +10,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ diff --git a/kernel/drivers/scsi/be2iscsi/be_cmds.c b/kernel/drivers/scsi/be2iscsi/be_cmds.c index 447cf7ce6..2778089b0 100644 --- a/kernel/drivers/scsi/be2iscsi/be_cmds.c +++ b/kernel/drivers/scsi/be2iscsi/be_cmds.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -10,7 +10,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -452,6 +452,7 @@ void beiscsi_async_link_state_process(struct beiscsi_hba *phba, ((evt->port_link_status & ASYNC_EVENT_LOGICAL) && (evt->port_fault == BEISCSI_PHY_LINK_FAULT_NONE))) { phba->state = BE_ADAPTER_LINK_UP | BE_ADAPTER_CHECK_BOOT; + phba->get_boot = BE_GET_BOOT_RETRIES; beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT, @@ -480,6 +481,7 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba) case ASYNC_EVENT_NEW_ISCSI_CONN: case ASYNC_EVENT_NEW_TCP_CONN: phba->state |= BE_ADAPTER_CHECK_BOOT; + phba->get_boot = BE_GET_BOOT_RETRIES; beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, @@ -488,6 +490,8 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba) compl->flags); break; default: + phba->state |= BE_ADAPTER_CHECK_BOOT; + phba->get_boot = BE_GET_BOOT_RETRIES; beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, diff --git a/kernel/drivers/scsi/be2iscsi/be_cmds.h b/kernel/drivers/scsi/be2iscsi/be_cmds.h index f11d325fe..4bfca355f 100644 --- a/kernel/drivers/scsi/be2iscsi/be_cmds.h +++ b/kernel/drivers/scsi/be2iscsi/be_cmds.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -10,7 +10,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -304,6 +304,17 @@ struct mgmt_auth_method_format { struct mgmt_chap_format chap; } __packed; +struct be_cmd_req_logout_fw_sess { + struct be_cmd_req_hdr hdr; /* dw[4] */ + uint32_t session_handle; +} __packed; + +struct be_cmd_resp_logout_fw_sess { + struct be_cmd_resp_hdr hdr; /* dw[4] */ +#define BEISCSI_MGMT_SESSION_CLOSE 0x20 + uint32_t session_status; +} __packed; + struct mgmt_conn_login_options { u8 flags; u8 header_digest; @@ -1136,6 +1147,7 @@ struct be_cmd_get_all_if_id_req { #define OPCODE_ISCSI_INI_CFG_GET_HBA_NAME 6 #define OPCODE_ISCSI_INI_CFG_SET_HBA_NAME 7 #define OPCODE_ISCSI_INI_SESSION_GET_A_SESSION 14 +#define OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET 24 #define OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS 36 #define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41 #define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42 diff --git a/kernel/drivers/scsi/be2iscsi/be_iscsi.c b/kernel/drivers/scsi/be2iscsi/be_iscsi.c index 2f0700796..b7087ba69 100644 --- a/kernel/drivers/scsi/be2iscsi/be_iscsi.c +++ b/kernel/drivers/scsi/be2iscsi/be_iscsi.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +12,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ diff --git a/kernel/drivers/scsi/be2iscsi/be_main.c b/kernel/drivers/scsi/be2iscsi/be_main.c index 1f74760ce..fe0c5143f 100644 --- a/kernel/drivers/scsi/be2iscsi/be_main.c +++ b/kernel/drivers/scsi/be2iscsi/be_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +12,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -50,7 +50,7 @@ static unsigned int enable_msix = 1; MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR); MODULE_VERSION(BUILD_STR); -MODULE_AUTHOR("Avago Technologies"); +MODULE_AUTHOR("Emulex Corporation"); MODULE_LICENSE("GPL"); module_param(be_iopoll_budget, int, 0); module_param(enable_msix, int, 0); @@ -552,7 +552,7 @@ MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table); static struct scsi_host_template beiscsi_sht = { .module = THIS_MODULE, - .name = "Avago Technologies 10Gbe open-iscsi Initiator Driver", + .name = "Emulex 10Gbe open-iscsi Initiator Driver", .proc_name = DRV_NAME, .queuecommand = iscsi_queuecommand, .change_queue_depth = scsi_change_queue_depth, @@ -668,14 +668,20 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev) return ret; } + ret = pci_request_regions(pcidev, DRV_NAME); + if (ret) { + dev_err(&pcidev->dev, + "beiscsi_enable_pci - request region failed\n"); + goto pci_dev_disable; + } + pci_set_master(pcidev); ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(64)); if (ret) { ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); if (ret) { dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n"); - pci_disable_device(pcidev); - return ret; + goto pci_region_release; } else { ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)); @@ -684,11 +690,17 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev) ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64)); if (ret) { dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n"); - pci_disable_device(pcidev); - return ret; + goto pci_region_release; } } return 0; + +pci_region_release: + pci_release_regions(pcidev); +pci_dev_disable: + pci_disable_device(pcidev); + + return ret; } static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev) @@ -1186,14 +1198,16 @@ free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle) * alloc_wrb_handle - To allocate a wrb handle * @phba: The hba pointer * @cid: The cid to use for allocation + * @pwrb_context: ptr to ptr to wrb context * * This happens under session_lock until submission to chip */ -struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid) +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + struct hwi_wrb_context **pcontext) { struct hwi_wrb_context *pwrb_context; struct hwi_controller *phwi_ctrlr; - struct wrb_handle *pwrb_handle, *pwrb_handle_tmp; + struct wrb_handle *pwrb_handle; uint16_t cri_index = BE_GET_CRI_FROM_CID(cid); phwi_ctrlr = phba->phwi_ctrlr; @@ -1207,9 +1221,9 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid) pwrb_context->alloc_index = 0; else pwrb_context->alloc_index++; - pwrb_handle_tmp = pwrb_context->pwrb_handle_base[ - pwrb_context->alloc_index]; - pwrb_handle->nxt_wrb_index = pwrb_handle_tmp->wrb_index; + + /* Return the context address */ + *pcontext = pwrb_context; } else pwrb_handle = NULL; return pwrb_handle; @@ -1356,8 +1370,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn, if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ) conn->rxdata_octets += resid; unmap: - scsi_dma_unmap(io_task->scsi_cmnd); - io_task->scsi_cmnd = NULL; + if (io_task->scsi_cmnd) { + scsi_dma_unmap(io_task->scsi_cmnd); + io_task->scsi_cmnd = NULL; + } iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn); } @@ -2037,11 +2053,16 @@ static void beiscsi_process_mcc_isr(struct beiscsi_hba *phba) /* Interpret compl as a async link evt */ beiscsi_async_link_state_process(phba, (struct be_async_event_link_state *) mcc_compl); - else + else { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_MBOX, "BM_%d : Unsupported Async Event, flags" " = 0x%08x\n", mcc_compl->flags); + if (phba->state & BE_ADAPTER_LINK_UP) { + phba->state |= BE_ADAPTER_CHECK_BOOT; + phba->get_boot = BE_GET_BOOT_RETRIES; + } + } } else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) { be_mcc_compl_process_isr(&phba->ctrl, mcc_compl); atomic_dec(&phba->ctrl.mcc_obj.q.used); @@ -3165,7 +3186,7 @@ be_sgl_create_contiguous(void *virtual_address, { WARN_ON(!virtual_address); WARN_ON(!physical_address); - WARN_ON(!length > 0); + WARN_ON(!length); WARN_ON(!sgl); sgl->va = virtual_address; @@ -3678,14 +3699,16 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba) struct be_ctrl_info *ctrl = &phba->ctrl; q = &phba->ctrl.mcc_obj.q; - if (q->created) + if (q->created) { beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); - be_queue_free(phba, q); + be_queue_free(phba, q); + } q = &phba->ctrl.mcc_obj.cq; - if (q->created) + if (q->created) { beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); - be_queue_free(phba, q); + be_queue_free(phba, q); + } } static void hwi_cleanup(struct beiscsi_hba *phba) @@ -3729,8 +3752,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba) for (i = 0; i < (phba->num_cpus); i++) { q = &phwi_context->be_cq[i]; - if (q->created) + if (q->created) { + be_queue_free(phba, q); beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ); + } } be_mcc_queues_destroy(phba); @@ -3740,8 +3765,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba) eq_for_mcc = 0; for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) { q = &phwi_context->be_eq[i].q; - if (q->created) + if (q->created) { + be_queue_free(phba, q); beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ); + } } be_cmd_fw_uninit(ctrl); } @@ -4328,8 +4355,14 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba) beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, "BM_%d : No boot session\n"); + + if (ret == -ENXIO) + phba->get_boot = 0; + + return ret; } + phba->get_boot = 0; nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev, sizeof(*session_resp), &nonemb_cmd.dma); @@ -4369,6 +4402,9 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba) memcpy(&phba->boot_sess, &session_resp->session_info, sizeof(struct mgmt_session_info)); + + beiscsi_logout_fw_sess(phba, + phba->boot_sess.session_handle); ret = 0; boot_freemem: @@ -4580,11 +4616,13 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn, spin_unlock_bh(&phba->mgmt_sgl_lock); } - if (io_task->mtask_addr) + if (io_task->mtask_addr) { pci_unmap_single(phba->pcidev, io_task->mtask_addr, io_task->mtask_data_count, PCI_DMA_TODEVICE); + io_task->mtask_addr = 0; + } } /** @@ -4642,6 +4680,7 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, struct beiscsi_offload_params *params) { struct wrb_handle *pwrb_handle; + struct hwi_wrb_context *pwrb_context = NULL; struct beiscsi_hba *phba = beiscsi_conn->phba; struct iscsi_task *task = beiscsi_conn->task; struct iscsi_session *session = task->conn->session; @@ -4656,14 +4695,17 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn, beiscsi_cleanup_task(task); spin_unlock_bh(&session->back_lock); - pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid); + pwrb_handle = alloc_wrb_handle(phba, beiscsi_conn->beiscsi_conn_cid, + &pwrb_context); /* Check for the adapter family */ if (is_chip_be2_be3r(phba)) beiscsi_offload_cxn_v0(params, pwrb_handle, - phba->init_mem); + phba->init_mem, + pwrb_context); else - beiscsi_offload_cxn_v2(params, pwrb_handle); + beiscsi_offload_cxn_v2(params, pwrb_handle, + pwrb_context); be_dws_le_to_cpu(pwrb_handle->pwrb, sizeof(struct iscsi_target_context_update_wrb)); @@ -4733,7 +4775,8 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) goto free_hndls; } io_task->pwrb_handle = alloc_wrb_handle(phba, - beiscsi_conn->beiscsi_conn_cid); + beiscsi_conn->beiscsi_conn_cid, + &io_task->pwrb_context); if (!io_task->pwrb_handle) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, @@ -4767,7 +4810,8 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) io_task->psgl_handle; io_task->pwrb_handle = alloc_wrb_handle(phba, - beiscsi_conn->beiscsi_conn_cid); + beiscsi_conn->beiscsi_conn_cid, + &io_task->pwrb_context); if (!io_task->pwrb_handle) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_IO | @@ -4803,7 +4847,8 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode) } io_task->pwrb_handle = alloc_wrb_handle(phba, - beiscsi_conn->beiscsi_conn_cid); + beiscsi_conn->beiscsi_conn_cid, + &io_task->pwrb_context); if (!io_task->pwrb_handle) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG, @@ -4889,7 +4934,12 @@ int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg, hwi_write_sgl_v2(pwrb, sg, num_sg, io_task); AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb, - io_task->pwrb_handle->nxt_wrb_index); + io_task->pwrb_handle->wrb_index); + if (io_task->pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, + io_task->pwrb_context->plast_wrb, + io_task->pwrb_handle->wrb_index); + io_task->pwrb_context->plast_wrb = pwrb; be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); @@ -4946,7 +4996,13 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg, hwi_write_sgl(pwrb, sg, num_sg, io_task); AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, - io_task->pwrb_handle->nxt_wrb_index); + io_task->pwrb_handle->wrb_index); + if (io_task->pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, + io_task->pwrb_context->plast_wrb, + io_task->pwrb_handle->wrb_index); + io_task->pwrb_context->plast_wrb = pwrb; + be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb)); doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK; @@ -4984,7 +5040,13 @@ static int beiscsi_mtask(struct iscsi_task *task) AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, task->data_count); AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb, - io_task->pwrb_handle->nxt_wrb_index); + io_task->pwrb_handle->wrb_index); + if (io_task->pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, + io_task->pwrb_context->plast_wrb, + io_task->pwrb_handle->wrb_index); + io_task->pwrb_context->plast_wrb = pwrb; + pwrb_typeoffset = BE_WRB_TYPE_OFFSET; } else { AMAP_SET_BITS(struct amap_iscsi_wrb_v2, cmdsn_itt, pwrb, @@ -4996,7 +5058,13 @@ static int beiscsi_mtask(struct iscsi_task *task) AMAP_SET_BITS(struct amap_iscsi_wrb_v2, r2t_exp_dtl, pwrb, task->data_count); AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, pwrb, - io_task->pwrb_handle->nxt_wrb_index); + io_task->pwrb_handle->wrb_index); + if (io_task->pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_wrb_v2, ptr2nextwrb, + io_task->pwrb_context->plast_wrb, + io_task->pwrb_handle->wrb_index); + io_task->pwrb_context->plast_wrb = pwrb; + pwrb_typeoffset = SKH_WRB_TYPE_OFFSET; } @@ -5264,6 +5332,7 @@ static void beiscsi_remove(struct pci_dev *pcidev) iscsi_host_free(phba->shost); pci_disable_pcie_error_reporting(pcidev); pci_set_drvdata(pcidev, NULL); + pci_release_regions(pcidev); pci_disable_device(pcidev); } @@ -5374,8 +5443,14 @@ beiscsi_hw_health_check(struct work_struct *work) be_eqd_update(phba); if (phba->state & BE_ADAPTER_CHECK_BOOT) { - phba->state &= ~BE_ADAPTER_CHECK_BOOT; - be_check_boot_session(phba); + if ((phba->get_boot > 0) && (!phba->boot_kset)) { + phba->get_boot--; + if (!(phba->get_boot % BE_GET_BOOT_TO)) + be_check_boot_session(phba); + } else { + phba->state &= ~BE_ADAPTER_CHECK_BOOT; + phba->get_boot = 0; + } } beiscsi_ue_detect(phba); @@ -5738,6 +5813,7 @@ hba_free: iscsi_host_free(phba->shost); pci_set_drvdata(pcidev, NULL); disable_pci: + pci_release_regions(pcidev); pci_disable_device(pcidev); return ret; } diff --git a/kernel/drivers/scsi/be2iscsi/be_main.h b/kernel/drivers/scsi/be2iscsi/be_main.h index e70ea26bb..5c67c0732 100644 --- a/kernel/drivers/scsi/be2iscsi/be_main.h +++ b/kernel/drivers/scsi/be2iscsi/be_main.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +12,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -36,8 +36,8 @@ #include #define DRV_NAME "be2iscsi" -#define BUILD_STR "10.4.114.0" -#define BE_NAME "Avago Technologies OneConnect" \ +#define BUILD_STR "10.6.0.1" +#define BE_NAME "Emulex OneConnect" \ "Open-iSCSI Driver version" BUILD_STR #define DRV_DESC BE_NAME " " "Driver" @@ -109,6 +109,9 @@ #define BEISCSI_CLEAN_UNLOAD 0x01 #define BEISCSI_EEH_UNLOAD 0x02 + +#define BE_GET_BOOT_RETRIES 45 +#define BE_GET_BOOT_TO 20 /** * hardware needs the async PDU buffers to be posted in multiples of 8 * So have atleast 8 of them by default @@ -413,6 +416,7 @@ struct beiscsi_hba { } fw_config; unsigned int state; + int get_boot; bool fw_timeout; bool ue_detected; struct delayed_work beiscsi_hw_check_task; @@ -498,6 +502,7 @@ struct beiscsi_io_task { struct sgl_handle *psgl_handle; struct beiscsi_conn *conn; struct scsi_cmnd *scsi_cmnd; + struct hwi_wrb_context *pwrb_context; unsigned int cmd_sn; unsigned int flags; unsigned short cid; @@ -829,7 +834,8 @@ struct amap_iscsi_wrb_v2 { } __packed; -struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid); +struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid, + struct hwi_wrb_context **pcontext); void free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle); @@ -1040,7 +1046,6 @@ enum hwh_type_enum { struct wrb_handle { enum hwh_type_enum type; unsigned short wrb_index; - unsigned short nxt_wrb_index; struct iscsi_task *pio_handle; struct iscsi_wrb *pwrb; diff --git a/kernel/drivers/scsi/be2iscsi/be_mgmt.c b/kernel/drivers/scsi/be2iscsi/be_mgmt.c index c2c4d6975..aea3e6b94 100644 --- a/kernel/drivers/scsi/be2iscsi/be_mgmt.c +++ b/kernel/drivers/scsi/be2iscsi/be_mgmt.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +12,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -1573,7 +1573,8 @@ beiscsi_phys_port_disp(struct device *dev, struct device_attribute *attr, void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, struct wrb_handle *pwrb_handle, - struct be_mem_descriptor *mem_descr) + struct be_mem_descriptor *mem_descr, + struct hwi_wrb_context *pwrb_context) { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; @@ -1617,7 +1618,14 @@ void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, max_burst_length) / 32]); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb, - pwrb, pwrb_handle->nxt_wrb_index); + pwrb, pwrb_handle->wrb_index); + if (pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, + ptr2nextwrb, + pwrb_context->plast_wrb, + pwrb_handle->wrb_index); + pwrb_context->plast_wrb = pwrb; + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, session_state, pwrb, 0); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack, @@ -1637,7 +1645,8 @@ void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, } void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, - struct wrb_handle *pwrb_handle) + struct wrb_handle *pwrb_handle, + struct hwi_wrb_context *pwrb_context) { struct iscsi_wrb *pwrb = pwrb_handle->pwrb; @@ -1652,7 +1661,14 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, BE_TGT_CTX_UPDT_CMD); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, ptr2nextwrb, - pwrb, pwrb_handle->nxt_wrb_index); + pwrb, pwrb_handle->wrb_index); + if (pwrb_context->plast_wrb) + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, + ptr2nextwrb, + pwrb_context->plast_wrb, + pwrb_handle->wrb_index); + pwrb_context->plast_wrb = pwrb; + AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, wrb_idx, pwrb, pwrb_handle->wrb_index); AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb_v2, @@ -1707,3 +1723,72 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, (params->dw[offsetof(struct amap_beiscsi_offload_params, exp_statsn) / 32] + 1)); } + +/** + * beiscsi_logout_fw_sess()- Firmware Session Logout + * @phba: Device priv structure instance + * @fw_sess_handle: FW session handle + * + * Logout from the FW established sessions. + * returns + * Success: 0 + * Failure: Non-Zero Value + * + */ +int beiscsi_logout_fw_sess(struct beiscsi_hba *phba, + uint32_t fw_sess_handle) +{ + struct be_ctrl_info *ctrl = &phba->ctrl; + struct be_mcc_wrb *wrb; + struct be_cmd_req_logout_fw_sess *req; + struct be_cmd_resp_logout_fw_sess *resp; + unsigned int tag; + int rc; + + beiscsi_log(phba, KERN_INFO, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, + "BG_%d : In bescsi_logout_fwboot_sess\n"); + + spin_lock(&ctrl->mbox_lock); + tag = alloc_mcc_tag(phba); + if (!tag) { + spin_unlock(&ctrl->mbox_lock); + beiscsi_log(phba, KERN_INFO, + BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX, + "BG_%d : MBX Tag Failure\n"); + return -EINVAL; + } + + wrb = wrb_from_mccq(phba); + req = embedded_payload(wrb); + wrb->tag0 |= tag; + be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); + be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI, + OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET, + sizeof(struct be_cmd_req_logout_fw_sess)); + + /* Set the session handle */ + req->session_handle = fw_sess_handle; + be_mcc_notify(phba); + spin_unlock(&ctrl->mbox_lock); + + rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL); + if (rc) { + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, + "BG_%d : MBX CMD FW_SESSION_LOGOUT_TARGET Failed\n"); + return -EBUSY; + } + + resp = embedded_payload(wrb); + if (resp->session_status != + BEISCSI_MGMT_SESSION_CLOSE) { + beiscsi_log(phba, KERN_ERR, + BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG, + "BG_%d : FW_SESSION_LOGOUT_TARGET resp : 0x%x\n", + resp->session_status); + rc = -EINVAL; + } + + return rc; +} diff --git a/kernel/drivers/scsi/be2iscsi/be_mgmt.h b/kernel/drivers/scsi/be2iscsi/be_mgmt.h index 9356b9a86..c1dbb690e 100644 --- a/kernel/drivers/scsi/be2iscsi/be_mgmt.h +++ b/kernel/drivers/scsi/be2iscsi/be_mgmt.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2005 - 2015 Avago Technologies + * Copyright (C) 2005 - 2015 Emulex * All rights reserved. * * This program is free software; you can redistribute it and/or @@ -12,7 +12,7 @@ * Contact Information: * linux-drivers@avagotech.com * - * Avago Technologies + * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ @@ -330,12 +330,18 @@ ssize_t beiscsi_phys_port_disp(struct device *dev, void beiscsi_offload_cxn_v0(struct beiscsi_offload_params *params, struct wrb_handle *pwrb_handle, - struct be_mem_descriptor *mem_descr); + struct be_mem_descriptor *mem_descr, + struct hwi_wrb_context *pwrb_context); void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params, - struct wrb_handle *pwrb_handle); + struct wrb_handle *pwrb_handle, + struct hwi_wrb_context *pwrb_context); + void beiscsi_ue_detect(struct beiscsi_hba *phba); int be_cmd_modify_eq_delay(struct beiscsi_hba *phba, struct be_set_eqd *, int num); +int beiscsi_logout_fw_sess(struct beiscsi_hba *phba, + uint32_t fw_sess_handle); + #endif diff --git a/kernel/drivers/scsi/bfa/bfa_ioc.c b/kernel/drivers/scsi/bfa/bfa_ioc.c index 315d6d6dc..98f7e8cca 100644 --- a/kernel/drivers/scsi/bfa/bfa_ioc.c +++ b/kernel/drivers/scsi/bfa/bfa_ioc.c @@ -3665,19 +3665,19 @@ bfa_cb_sfp_state_query(struct bfa_sfp_s *sfp) if (sfp->state_query_cbfn) sfp->state_query_cbfn(sfp->state_query_cbarg, sfp->status); - sfp->media = NULL; - } + sfp->media = NULL; + } - if (sfp->portspeed) { - sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed); - if (sfp->state_query_cbfn) - sfp->state_query_cbfn(sfp->state_query_cbarg, - sfp->status); - sfp->portspeed = BFA_PORT_SPEED_UNKNOWN; - } + if (sfp->portspeed) { + sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed); + if (sfp->state_query_cbfn) + sfp->state_query_cbfn(sfp->state_query_cbarg, + sfp->status); + sfp->portspeed = BFA_PORT_SPEED_UNKNOWN; + } - sfp->state_query_lock = 0; - sfp->state_query_cbfn = NULL; + sfp->state_query_lock = 0; + sfp->state_query_cbfn = NULL; } /* @@ -3878,7 +3878,7 @@ bfa_sfp_show_comp(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg) bfa_trc(sfp, sfp->data_valid); if (sfp->data_valid) { u32 size = sizeof(struct sfp_mem_s); - u8 *des = (u8 *) &(sfp->sfpmem); + u8 *des = (u8 *)(sfp->sfpmem); memcpy(des, sfp->dbuf_kva, size); } /* diff --git a/kernel/drivers/scsi/bfa/bfad_im.c b/kernel/drivers/scsi/bfa/bfad_im.c index 7223b0006..299c6f80d 100644 --- a/kernel/drivers/scsi/bfa/bfad_im.c +++ b/kernel/drivers/scsi/bfa/bfad_im.c @@ -800,7 +800,6 @@ struct scsi_host_template bfad_im_scsi_host_template = { .shost_attrs = bfad_im_host_attrs, .max_sectors = BFAD_MAX_SECTORS, .vendor_id = BFA_PCI_VENDOR_ID_BROCADE, - .use_blk_tags = 1, }; struct scsi_host_template bfad_im_vport_template = { @@ -822,7 +821,6 @@ struct scsi_host_template bfad_im_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = bfad_im_vport_attrs, .max_sectors = BFAD_MAX_SECTORS, - .use_blk_tags = 1, }; bfa_status_t @@ -851,6 +849,8 @@ bfad_im_module_exit(void) if (bfad_im_scsi_vport_transport_template) fc_release_transport(bfad_im_scsi_vport_transport_template); + + idr_destroy(&bfad_im_port_index); } void diff --git a/kernel/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h b/kernel/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h index fe2106c91..ac1c0b631 100644 --- a/kernel/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h +++ b/kernel/drivers/scsi/bnx2fc/57xx_hsi_bnx2fc.h @@ -1,9 +1,9 @@ -/* 57xx_hsi_bnx2fc.h: QLogic NetXtreme II Linux FCoE offload driver. +/* 57xx_hsi_bnx2fc.h: QLogic Linux FCoE offload driver. * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 diff --git a/kernel/drivers/scsi/bnx2fc/Kconfig b/kernel/drivers/scsi/bnx2fc/Kconfig index 097882882..d401a096d 100644 --- a/kernel/drivers/scsi/bnx2fc/Kconfig +++ b/kernel/drivers/scsi/bnx2fc/Kconfig @@ -1,5 +1,5 @@ config SCSI_BNX2X_FCOE - tristate "QLogic NetXtreme II FCoE support" + tristate "QLogic FCoE offload support" depends on PCI depends on (IPV6 || IPV6=n) depends on LIBFC @@ -9,5 +9,4 @@ config SCSI_BNX2X_FCOE select NET_VENDOR_BROADCOM select CNIC ---help--- - This driver supports FCoE offload for the QLogic NetXtreme II - devices. + This driver supports FCoE offload for the QLogic devices. diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc.h b/kernel/drivers/scsi/bnx2fc/bnx2fc.h index 1346e052e..499e369ea 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc.h +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc.h @@ -1,7 +1,7 @@ -/* bnx2fc.h: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc.h: QLogic Linux FCoE offload driver. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 @@ -65,7 +65,7 @@ #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" -#define BNX2FC_VERSION "2.4.2" +#define BNX2FC_VERSION "2.9.6" #define PFX "bnx2fc: " @@ -303,7 +303,6 @@ struct bnx2fc_rport { #define BNX2FC_FLAG_OFLD_REQ_CMPL 0x5 #define BNX2FC_FLAG_CTX_ALLOC_FAILURE 0x6 #define BNX2FC_FLAG_UPLD_REQ_COMPL 0x7 -#define BNX2FC_FLAG_EXPL_LOGO 0x8 #define BNX2FC_FLAG_DISABLE_FAILED 0x9 #define BNX2FC_FLAG_ENABLED 0xa diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_constants.h b/kernel/drivers/scsi/bnx2fc/bnx2fc_constants.h index e147cc7ee..5b20efb66 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_constants.h +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_constants.h @@ -1,9 +1,9 @@ -/* bnx2fc_constants.h: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_constants.h: QLogic Linux FCoE offload driver. * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.c index d055df01f..c9e0bc7fa 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.c @@ -1,9 +1,9 @@ -/* bnx2fc_debug.c: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_debug.c: QLogic Linux FCoE offload driver. * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.h b/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.h index 2b9006774..34fda3e04 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.h +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_debug.h @@ -1,9 +1,9 @@ -/* bnx2fc_debug.h: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_debug.h: QLogic Linux FCoE offload driver. * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_els.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_els.c index ef355c13c..5beea776b 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_els.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_els.c @@ -1,10 +1,10 @@ /* - * bnx2fc_els.c: QLogic NetXtreme II Linux FCoE offload driver. + * bnx2fc_els.c: QLogic Linux FCoE offload driver. * This file contains helper routines that handle ELS requests * and responses. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 @@ -689,8 +689,7 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, rc = -EINVAL; goto els_err; } - if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) || - (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags))) { + if (!(test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags))) { printk(KERN_ERR PFX "els 0x%x: tgt not ready\n", op); rc = -EINVAL; goto els_err; @@ -707,6 +706,7 @@ static int bnx2fc_initiate_els(struct bnx2fc_rport *tgt, unsigned int op, els_req->cb_func = cb_func; cb_arg->io_req = els_req; els_req->cb_arg = cb_arg; + els_req->data_xfer_len = data_len; mp_req = (struct bnx2fc_mp_req *)&(els_req->mp_req); rc = bnx2fc_init_mp_req(els_req); diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 98d06d151..67405c628 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -1,10 +1,10 @@ -/* bnx2fc_fcoe.c: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_fcoe.c: QLogic Linux FCoE offload driver. * This file contains the code that interacts with libfc, libfcoe, * cnic modules to create FCoE instances, send/receive non-offloaded * FIP/FCoE packets, listen to link events etc. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 @@ -23,16 +23,16 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu); #define DRV_MODULE_NAME "bnx2fc" #define DRV_MODULE_VERSION BNX2FC_VERSION -#define DRV_MODULE_RELDATE "Dec 11, 2013" +#define DRV_MODULE_RELDATE "October 15, 2015" static char version[] = - "QLogic NetXtreme II FCoE Driver " DRV_MODULE_NAME \ + "QLogic FCoE Driver " DRV_MODULE_NAME \ " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Bhanu Prakash Gollapudi "); -MODULE_DESCRIPTION("QLogic NetXtreme II BCM57710 FCoE Driver"); +MODULE_DESCRIPTION("QLogic FCoE Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); @@ -856,7 +856,6 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, return; default: - printk(KERN_ERR PFX "Unknown netevent %ld", event); return; } @@ -2051,9 +2050,49 @@ static int bnx2fc_disable(struct net_device *netdev) return rc; } +static uint bnx2fc_npiv_create_vports(struct fc_lport *lport, + struct cnic_fc_npiv_tbl *npiv_tbl) +{ + struct fc_vport_identifiers vpid; + uint i, created = 0; + + if (npiv_tbl->count > MAX_NPIV_ENTRIES) { + BNX2FC_HBA_DBG(lport, "Exceeded count max of npiv table\n"); + goto done; + } + + /* Sanity check the first entry to make sure it's not 0 */ + if (wwn_to_u64(npiv_tbl->wwnn[0]) == 0 && + wwn_to_u64(npiv_tbl->wwpn[0]) == 0) { + BNX2FC_HBA_DBG(lport, "First NPIV table entries invalid.\n"); + goto done; + } + + vpid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vpid.vport_type = FC_PORTTYPE_NPIV; + vpid.disable = false; + + for (i = 0; i < npiv_tbl->count; i++) { + vpid.node_name = wwn_to_u64(npiv_tbl->wwnn[i]); + vpid.port_name = wwn_to_u64(npiv_tbl->wwpn[i]); + scnprintf(vpid.symbolic_name, sizeof(vpid.symbolic_name), + "NPIV[%u]:%016llx-%016llx", + created, vpid.port_name, vpid.node_name); + if (fc_vport_create(lport->host, 0, &vpid)) + created++; + else + BNX2FC_HBA_DBG(lport, "Failed to create vport\n"); + } +done: + return created; +} + static int __bnx2fc_enable(struct fcoe_ctlr *ctlr) { struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr); + struct bnx2fc_hba *hba; + struct cnic_fc_npiv_tbl *npiv_tbl; + struct fc_lport *lport; if (interface->enabled == false) { if (!ctlr->lp) { @@ -2064,6 +2103,37 @@ static int __bnx2fc_enable(struct fcoe_ctlr *ctlr) interface->enabled = true; } } + + /* Create static NPIV ports if any are contained in NVRAM */ + hba = interface->hba; + lport = ctlr->lp; + + if (!hba) + goto done; + + if (!hba->cnic) + goto done; + + if (!lport) + goto done; + + if (!lport->host) + goto done; + + if (!hba->cnic->get_fc_npiv_tbl) + goto done; + + npiv_tbl = kzalloc(sizeof(struct cnic_fc_npiv_tbl), GFP_KERNEL); + if (!npiv_tbl) + goto done; + + if (hba->cnic->get_fc_npiv_tbl(hba->cnic, npiv_tbl)) + goto done_free; + + bnx2fc_npiv_create_vports(lport, npiv_tbl); +done_free: + kfree(npiv_tbl); +done: return 0; } @@ -2797,7 +2867,6 @@ static struct scsi_host_template bnx2fc_shost_template = { .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = BNX2FC_MAX_BDS_PER_CMD, .max_sectors = 1024, - .use_blk_tags = 1, .track_queue_depth = 1, }; diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_hwi.c index c6688d72a..28c671b60 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -1,9 +1,9 @@ -/* bnx2fc_hwi.c: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_hwi.c: QLogic Linux FCoE offload driver. * This file contains the code that low level functions that interact * with 57712 FCoE firmware. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_io.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_io.c index 9ecca8504..0002caf68 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -1,8 +1,8 @@ -/* bnx2fc_io.c: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_io.c: QLogic Linux FCoE offload driver. * IO manager and SCSI IO processing. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 @@ -40,11 +40,8 @@ static void bnx2fc_cmd_timeout(struct work_struct *work) { struct bnx2fc_cmd *io_req = container_of(work, struct bnx2fc_cmd, timeout_work.work); - struct fc_lport *lport; - struct fc_rport_priv *rdata; u8 cmd_type = io_req->cmd_type; struct bnx2fc_rport *tgt = io_req->tgt; - int logo_issued; int rc; BNX2FC_IO_DBG(io_req, "cmd_timeout, cmd_type = %d," @@ -80,25 +77,14 @@ static void bnx2fc_cmd_timeout(struct work_struct *work) io_req->refcount.refcount.counter); if (!(test_and_set_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags))) { - - lport = io_req->port->lport; - rdata = io_req->tgt->rdata; - logo_issued = test_and_set_bit( - BNX2FC_FLAG_EXPL_LOGO, - &tgt->flags); + /* + * Cleanup and return original command to + * mid-layer. + */ + bnx2fc_initiate_cleanup(io_req); kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_unlock_bh(&tgt->tgt_lock); - /* Explicitly logo the target */ - if (!logo_issued) { - BNX2FC_IO_DBG(io_req, "Explicit " - "logo - tgt flags = 0x%lx\n", - tgt->flags); - - mutex_lock(&lport->disc.disc_mutex); - lport->tt.rport_logoff(rdata); - mutex_unlock(&lport->disc.disc_mutex); - } return; } } else { @@ -116,28 +102,10 @@ static void bnx2fc_cmd_timeout(struct work_struct *work) rc = bnx2fc_initiate_abts(io_req); if (rc == SUCCESS) goto done; - /* - * Explicitly logo the target if - * abts initiation fails - */ - lport = io_req->port->lport; - rdata = io_req->tgt->rdata; - logo_issued = test_and_set_bit( - BNX2FC_FLAG_EXPL_LOGO, - &tgt->flags); + kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_unlock_bh(&tgt->tgt_lock); - if (!logo_issued) { - BNX2FC_IO_DBG(io_req, "Explicit " - "logo - tgt flags = 0x%lx\n", - tgt->flags); - - - mutex_lock(&lport->disc.disc_mutex); - lport->tt.rport_logoff(rdata); - mutex_unlock(&lport->disc.disc_mutex); - } return; } else { BNX2FC_IO_DBG(io_req, "IO already in " @@ -152,22 +120,9 @@ static void bnx2fc_cmd_timeout(struct work_struct *work) if (!test_and_set_bit(BNX2FC_FLAG_ABTS_DONE, &io_req->req_flags)) { - lport = io_req->port->lport; - rdata = io_req->tgt->rdata; - logo_issued = test_and_set_bit( - BNX2FC_FLAG_EXPL_LOGO, - &tgt->flags); kref_put(&io_req->refcount, bnx2fc_cmd_release); spin_unlock_bh(&tgt->tgt_lock); - /* Explicitly logo the target */ - if (!logo_issued) { - BNX2FC_IO_DBG(io_req, "Explicitly logo" - "(els)\n"); - mutex_lock(&lport->disc.disc_mutex); - lport->tt.rport_logoff(rdata); - mutex_unlock(&lport->disc.disc_mutex); - } return; } } else { @@ -623,8 +578,12 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req = (struct bnx2fc_mp_req *)&(io_req->mp_req); memset(mp_req, 0, sizeof(struct bnx2fc_mp_req)); - mp_req->req_len = sizeof(struct fcp_cmnd); - io_req->data_xfer_len = mp_req->req_len; + if (io_req->cmd_type != BNX2FC_ELS) { + mp_req->req_len = sizeof(struct fcp_cmnd); + io_req->data_xfer_len = mp_req->req_len; + } else + mp_req->req_len = io_req->data_xfer_len; + mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &mp_req->req_buf_dma, GFP_ATOMIC); @@ -1108,18 +1067,11 @@ int bnx2fc_eh_device_reset(struct scsi_cmnd *sc_cmd) return bnx2fc_initiate_tmf(sc_cmd, FCP_TMF_LUN_RESET); } -int bnx2fc_expl_logo(struct fc_lport *lport, struct bnx2fc_cmd *io_req) +int bnx2fc_abts_cleanup(struct bnx2fc_cmd *io_req) { struct bnx2fc_rport *tgt = io_req->tgt; - struct fc_rport_priv *rdata = tgt->rdata; - int logo_issued; int rc = SUCCESS; - int wait_cnt = 0; - BNX2FC_IO_DBG(io_req, "Expl logo - tgt flags = 0x%lx\n", - tgt->flags); - logo_issued = test_and_set_bit(BNX2FC_FLAG_EXPL_LOGO, - &tgt->flags); io_req->wait_for_comp = 1; bnx2fc_initiate_cleanup(io_req); @@ -1132,21 +1084,8 @@ int bnx2fc_expl_logo(struct fc_lport *lport, struct bnx2fc_cmd *io_req) * release the reference taken in eh_abort to allow the * target to re-login after flushing IOs */ - kref_put(&io_req->refcount, bnx2fc_cmd_release); - - if (!logo_issued) { - clear_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags); - mutex_lock(&lport->disc.disc_mutex); - lport->tt.rport_logoff(rdata); - mutex_unlock(&lport->disc.disc_mutex); - do { - msleep(BNX2FC_RELOGIN_WAIT_TIME); - if (wait_cnt++ > BNX2FC_RELOGIN_WAIT_CNT) { - rc = FAILED; - break; - } - } while (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)); - } + kref_put(&io_req->refcount, bnx2fc_cmd_release); + spin_lock_bh(&tgt->tgt_lock); return rc; } @@ -1248,7 +1187,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) if (cancel_delayed_work(&io_req->timeout_work)) kref_put(&io_req->refcount, bnx2fc_cmd_release); /* drop timer hold */ - rc = bnx2fc_expl_logo(lport, io_req); + rc = bnx2fc_abts_cleanup(io_req); /* This only occurs when an task abort was requested while ABTS is in progress. Setting the IO_CLEANUP flag will skip the RRQ process in the case when the fw generated SCSI_CMD cmpl @@ -1287,7 +1226,7 @@ int bnx2fc_eh_abort(struct scsi_cmnd *sc_cmd) /* Let the scsi-ml try to recover this command */ printk(KERN_ERR PFX "abort failed, xid = 0x%x\n", io_req->xid); - rc = bnx2fc_expl_logo(lport, io_req); + rc = bnx2fc_abts_cleanup(io_req); goto out; } else { /* @@ -1755,7 +1694,10 @@ static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req, int fcp_rsp_len = 0; io_req->fcp_status = FC_GOOD; - io_req->fcp_resid = fcp_rsp->fcp_resid; + io_req->fcp_resid = 0; + if (rsp_flags & (FCOE_FCP_RSP_FLAGS_FCP_RESID_OVER | + FCOE_FCP_RSP_FLAGS_FCP_RESID_UNDER)) + io_req->fcp_resid = fcp_rsp->fcp_resid; io_req->scsi_comp_flags = rsp_flags; CMD_SCSI_STATUS(sc_cmd) = io_req->cdb_status = diff --git a/kernel/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/kernel/drivers/scsi/bnx2fc/bnx2fc_tgt.c index c66c70841..08ec318af 100644 --- a/kernel/drivers/scsi/bnx2fc/bnx2fc_tgt.c +++ b/kernel/drivers/scsi/bnx2fc/bnx2fc_tgt.c @@ -1,9 +1,9 @@ -/* bnx2fc_tgt.c: QLogic NetXtreme II Linux FCoE offload driver. +/* bnx2fc_tgt.c: QLogic Linux FCoE offload driver. * Handles operations such as session offload/upload etc, and manages * session resources such as connection id and qp resources. * - * Copyright (c) 2008 - 2013 Broadcom Corporation - * Copyright (c) 2014, QLogic Corporation + * Copyright (c) 2008-2013 Broadcom Corporation + * Copyright (c) 2014-2015 QLogic Corporation * * 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 @@ -560,12 +560,6 @@ void bnx2fc_rport_event_handler(struct fc_lport *lport, (hba->num_ofld_sess == 0)) { wake_up_interruptible(&hba->shutdown_wait); } - if (test_bit(BNX2FC_FLAG_EXPL_LOGO, &tgt->flags)) { - printk(KERN_ERR PFX "Relogin to the tgt\n"); - mutex_lock(&lport->disc.disc_mutex); - lport->tt.rport_login(rdata); - mutex_unlock(&lport->disc.disc_mutex); - } mutex_unlock(&hba->hba_mutex); break; diff --git a/kernel/drivers/scsi/bnx2i/bnx2i_iscsi.c b/kernel/drivers/scsi/bnx2i/bnx2i_iscsi.c index e53078d03..72894378f 100644 --- a/kernel/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/kernel/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -1173,8 +1173,10 @@ static void bnx2i_cleanup_task(struct iscsi_task *task) bnx2i_send_cmd_cleanup_req(hba, task->dd_data); spin_unlock_bh(&conn->session->back_lock); + spin_unlock_bh(&conn->session->frwd_lock); wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl, msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT)); + spin_lock_bh(&conn->session->frwd_lock); spin_lock_bh(&conn->session->back_lock); } bnx2i_iscsi_unmap_sg_list(task->dd_data); @@ -2093,7 +2095,8 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep) else /* wait for option-2 conn teardown */ wait_event_interruptible(bnx2i_ep->ofld_wait, - bnx2i_ep->state != EP_STATE_DISCONN_START); + ((bnx2i_ep->state != EP_STATE_DISCONN_START) + && (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD))); if (signal_pending(current)) flush_signals(current); diff --git a/kernel/drivers/scsi/csiostor/csio_hw.c b/kernel/drivers/scsi/csiostor/csio_hw.c index 2e66f34eb..622bdabc8 100644 --- a/kernel/drivers/scsi/csiostor/csio_hw.c +++ b/kernel/drivers/scsi/csiostor/csio_hw.c @@ -3928,6 +3928,7 @@ csio_hw_init(struct csio_hw *hw) evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL); if (!evt_entry) { + rv = -ENOMEM; csio_err(hw, "Failed to initialize eventq"); goto err_evtq_cleanup; } diff --git a/kernel/drivers/scsi/csiostor/csio_scsi.c b/kernel/drivers/scsi/csiostor/csio_scsi.c index 2c4562d82..c2a6f9f29 100644 --- a/kernel/drivers/scsi/csiostor/csio_scsi.c +++ b/kernel/drivers/scsi/csiostor/csio_scsi.c @@ -2283,7 +2283,6 @@ struct scsi_host_template csio_fcoe_shost_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = csio_fcoe_lport_attrs, .max_sectors = CSIO_MAX_SECTOR_SIZE, - .use_blk_tags = 1, }; struct scsi_host_template csio_fcoe_shost_vport_template = { @@ -2303,7 +2302,6 @@ struct scsi_host_template csio_fcoe_shost_vport_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = csio_fcoe_vport_attrs, .max_sectors = CSIO_MAX_SECTOR_SIZE, - .use_blk_tags = 1, }; /* diff --git a/kernel/drivers/scsi/cxgbi/cxgb3i/Kbuild b/kernel/drivers/scsi/cxgbi/cxgb3i/Kbuild index 6f095e28a..961a12f6d 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb3i/Kbuild +++ b/kernel/drivers/scsi/cxgbi/cxgb3i/Kbuild @@ -1,3 +1,3 @@ -EXTRA_CFLAGS += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb3 +ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb3 obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i.o diff --git a/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 3db4c6397..0e2bee937 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -1,7 +1,7 @@ /* * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management * - * Copyright (C) 2003-2008 Chelsio Communications. All rights reserved. + * Copyright (C) 2003-2015 Chelsio Communications. All rights reserved. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -32,8 +32,8 @@ static unsigned int dbg_level; #define DRV_MODULE_NAME "cxgb3i" #define DRV_MODULE_DESC "Chelsio T3 iSCSI Driver" -#define DRV_MODULE_VERSION "2.0.0" -#define DRV_MODULE_RELDATE "Jun. 2010" +#define DRV_MODULE_VERSION "2.0.1-ko" +#define DRV_MODULE_RELDATE "Apr. 2015" static char version[] = DRV_MODULE_DESC " " DRV_MODULE_NAME @@ -156,7 +156,7 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion); static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, const struct l2t_entry *e) { - unsigned int wscale = cxgbi_sock_compute_wscale(cxgb3i_rcv_win); + unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win); struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head; skb->priority = CPL_PRIORITY_SETUP; @@ -172,7 +172,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) | V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx)); req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) | - V_RCV_BUFSIZ(cxgb3i_rcv_win>>10)); + V_RCV_BUFSIZ(csk->rcv_win >> 10)); log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n", @@ -369,7 +369,7 @@ static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb, req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT | V_TX_CPU_IDX(csk->rss_qid)); /* sendbuffer is in units of 32KB. */ - req->param |= htonl(V_TX_SNDBUF(cxgb3i_snd_win >> 15)); + req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15)); cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT); } } @@ -503,8 +503,8 @@ static int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) csk, csk->state, csk->flags, csk->tid); csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn; - if (cxgb3i_rcv_win > (M_RCV_BUFSIZ << 10)) - csk->rcv_wup -= cxgb3i_rcv_win - (M_RCV_BUFSIZ << 10); + if (csk->rcv_win > (M_RCV_BUFSIZ << 10)) + csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10); cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt)); @@ -988,6 +988,8 @@ static int init_act_open(struct cxgbi_sock *csk) goto rel_resource; skb->sk = (struct sock *)csk; set_arp_failure_handler(skb, act_open_arp_failure); + csk->snd_win = cxgb3i_snd_win; + csk->rcv_win = cxgb3i_rcv_win; csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1; csk->wr_una_cred = 0; @@ -1320,8 +1322,6 @@ static void cxgb3i_dev_open(struct t3cdev *t3dev) cdev->nports = adapter->params.nports; cdev->mtus = adapter->params.mtus; cdev->nmtus = NMTUS; - cdev->snd_win = cxgb3i_snd_win; - cdev->rcv_win = cxgb3i_rcv_win; cdev->rx_credit_thres = cxgb3i_rx_credit_thres; cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN; cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss); diff --git a/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h b/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h index 20593fd69..b0430c935 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h +++ b/kernel/drivers/scsi/cxgbi/cxgb3i/cxgb3i.h @@ -1,7 +1,7 @@ /* * cxgb3i.h: Chelsio S3xx iSCSI driver. * - * Copyright (c) 2008 Chelsio Communications, Inc. + * Copyright (c) 2008-2015 Chelsio Communications, Inc. * * 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 diff --git a/kernel/drivers/scsi/cxgbi/cxgb4i/Kbuild b/kernel/drivers/scsi/cxgbi/cxgb4i/Kbuild index 8290cdaa4..374586437 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb4i/Kbuild +++ b/kernel/drivers/scsi/cxgbi/cxgb4i/Kbuild @@ -1,3 +1,3 @@ -EXTRA_CFLAGS += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4 +ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4 obj-$(CONFIG_SCSI_CXGB4_ISCSI) += cxgb4i.o diff --git a/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index dd00e5fe4..804806e1c 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1,7 +1,7 @@ /* * cxgb4i.c: Chelsio T4 iSCSI driver. * - * Copyright (c) 2010 Chelsio Communications, Inc. + * Copyright (c) 2010-2015 Chelsio Communications, Inc. * * 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 @@ -36,11 +36,12 @@ static unsigned int dbg_level; #define DRV_MODULE_NAME "cxgb4i" #define DRV_MODULE_DESC "Chelsio T4/T5 iSCSI Driver" -#define DRV_MODULE_VERSION "0.9.4" +#define DRV_MODULE_VERSION "0.9.5-ko" +#define DRV_MODULE_RELDATE "Apr. 2015" static char version[] = DRV_MODULE_DESC " " DRV_MODULE_NAME - " v" DRV_MODULE_VERSION "\n"; + " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Chelsio Communications, Inc."); MODULE_DESCRIPTION(DRV_MODULE_DESC); @@ -50,11 +51,13 @@ MODULE_LICENSE("GPL"); module_param(dbg_level, uint, 0644); MODULE_PARM_DESC(dbg_level, "Debug flag (default=0)"); -static int cxgb4i_rcv_win = 256 * 1024; +#define CXGB4I_DEFAULT_10G_RCV_WIN (256 * 1024) +static int cxgb4i_rcv_win = -1; module_param(cxgb4i_rcv_win, int, 0644); MODULE_PARM_DESC(cxgb4i_rcv_win, "TCP reveive window in bytes"); -static int cxgb4i_snd_win = 128 * 1024; +#define CXGB4I_DEFAULT_10G_SND_WIN (128 * 1024) +static int cxgb4i_snd_win = -1; module_param(cxgb4i_snd_win, int, 0644); MODULE_PARM_DESC(cxgb4i_snd_win, "TCP send window in bytes"); @@ -157,7 +160,7 @@ static struct scsi_transport_template *cxgb4i_stt; #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define RCV_BUFSIZ_MASK 0x3FFU -#define MAX_IMM_TX_PKT_LEN 128 +#define MAX_IMM_TX_PKT_LEN 256 static int push_tx_frames(struct cxgbi_sock *, int); @@ -196,10 +199,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, TX_CHAN_V(csk->tx_chan) | SMAC_SEL_V(csk->smac_idx) | ULP_MODE_V(ULP_MODE_ISCSI) | - RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10); + RCV_BUFSIZ_V(csk->rcv_win >> 10); + opt2 = RX_CHANNEL_V(0) | RSS_QUEUE_VALID_F | - (RX_FC_DISABLE_F) | RSS_QUEUE_V(csk->rss_qid); if (is_t4(lldi->adapter_type)) { @@ -228,6 +231,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, } else { struct cpl_t5_act_open_req *req = (struct cpl_t5_act_open_req *)skb->head; + u32 isn = (prandom_u32() & ~7UL) - 1; INIT_TP_WR(req, 0); OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, @@ -241,7 +245,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb, cxgb4_select_ntuple( csk->cdev->ports[csk->port_id], csk->l2t))); - opt2 |= 1 << 31; + req->rsvd = cpu_to_be32(isn); + opt2 |= T5_ISS_VALID; + opt2 |= T5_OPT_2_VALID_F; + req->opt2 = cpu_to_be32(opt2); log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, @@ -279,7 +286,7 @@ static void send_act_open_req6(struct cxgbi_sock *csk, struct sk_buff *skb, TX_CHAN_V(csk->tx_chan) | SMAC_SEL_V(csk->smac_idx) | ULP_MODE_V(ULP_MODE_ISCSI) | - RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10); + RCV_BUFSIZ_V(csk->rcv_win >> 10); opt2 = RX_CHANNEL_V(0) | RSS_QUEUE_VALID_F | @@ -544,7 +551,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk) flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT; flowc->mnemval[5].val = htonl(csk->rcv_nxt); flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF; - flowc->mnemval[6].val = htonl(cxgb4i_snd_win); + flowc->mnemval[6].val = htonl(csk->snd_win); flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS; flowc->mnemval[7].val = htonl(csk->advmss); flowc->mnemval[8].mnemonic = 0; @@ -557,7 +564,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk) log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p, tid 0x%x, %u,%u,%u,%u,%u,%u,%u.\n", csk, csk->tid, 0, csk->tx_chan, csk->rss_qid, - csk->snd_nxt, csk->rcv_nxt, cxgb4i_snd_win, + csk->snd_nxt, csk->rcv_nxt, csk->snd_win, csk->advmss); cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb); @@ -750,8 +757,8 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb) * Causes the first RX_DATA_ACK to supply any Rx credits we couldn't * pass through opt0. */ - if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10)) - csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10); + if (csk->rcv_win > (RCV_BUFSIZ_MASK << 10)) + csk->rcv_wup -= csk->rcv_win - (RCV_BUFSIZ_MASK << 10); csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40; if (TCPOPT_TSTAMP_G(tcp_opt)) @@ -1367,6 +1374,8 @@ static int init_act_open(struct cxgbi_sock *csk) unsigned int step; unsigned int size, size6; int t4 = is_t4(lldi->adapter_type); + unsigned int linkspeed; + unsigned int rcv_winf, snd_winf; log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n", @@ -1440,6 +1449,21 @@ static int init_act_open(struct cxgbi_sock *csk) csk->txq_idx = cxgb4_port_idx(ndev) * step; step = lldi->nrxq / lldi->nchan; csk->rss_qid = lldi->rxq_ids[cxgb4_port_idx(ndev) * step]; + linkspeed = ((struct port_info *)netdev_priv(ndev))->link_cfg.speed; + csk->snd_win = cxgb4i_snd_win; + csk->rcv_win = cxgb4i_rcv_win; + if (cxgb4i_rcv_win <= 0) { + csk->rcv_win = CXGB4I_DEFAULT_10G_RCV_WIN; + rcv_winf = linkspeed / SPEED_10000; + if (rcv_winf) + csk->rcv_win *= rcv_winf; + } + if (cxgb4i_snd_win <= 0) { + csk->snd_win = CXGB4I_DEFAULT_10G_SND_WIN; + snd_winf = linkspeed / SPEED_10000; + if (snd_winf) + csk->snd_win *= snd_winf; + } csk->wr_cred = lldi->wr_cred - DIV_ROUND_UP(sizeof(struct cpl_abort_req), 16); csk->wr_max_cred = csk->wr_cred; @@ -1758,8 +1782,6 @@ static void *t4_uld_add(const struct cxgb4_lld_info *lldi) cdev->nports = lldi->nports; cdev->mtus = lldi->mtus; cdev->nmtus = NMTUS; - cdev->snd_win = cxgb4i_snd_win; - cdev->rcv_win = cxgb4i_rcv_win; cdev->rx_credit_thres = cxgb4i_rx_credit_thres; cdev->skb_tx_rsvd = CXGB4I_TX_HEADER_LEN; cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr); diff --git a/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h b/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h index 1096026ba..22dd8d670 100644 --- a/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h +++ b/kernel/drivers/scsi/cxgbi/cxgb4i/cxgb4i.h @@ -1,7 +1,7 @@ /* * cxgb4i.h: Chelsio T4 iSCSI driver. * - * Copyright (c) 2010 Chelsio Communications, Inc. + * Copyright (c) 2010-2015 Chelsio Communications, Inc. * * 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 @@ -23,6 +23,8 @@ #define CXGB4I_TX_HEADER_LEN \ (sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr)) +#define T5_ISS_VALID (1 << 18) + struct ulptx_idata { __be32 cmd_more; __be32 len; diff --git a/kernel/drivers/scsi/cxgbi/libcxgbi.c b/kernel/drivers/scsi/cxgbi/libcxgbi.c index eb58afcfb..f3bb7af4e 100644 --- a/kernel/drivers/scsi/cxgbi/libcxgbi.c +++ b/kernel/drivers/scsi/cxgbi/libcxgbi.c @@ -1,7 +1,7 @@ /* * libcxgbi.c: Chelsio common library for T3/T4 iSCSI driver. * - * Copyright (c) 2010 Chelsio Communications, Inc. + * Copyright (c) 2010-2015 Chelsio Communications, Inc. * * 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 @@ -38,8 +38,12 @@ static unsigned int dbg_level; #define DRV_MODULE_NAME "libcxgbi" #define DRV_MODULE_DESC "Chelsio iSCSI driver library" -#define DRV_MODULE_VERSION "0.9.0" -#define DRV_MODULE_RELDATE "Jun. 2010" +#define DRV_MODULE_VERSION "0.9.1-ko" +#define DRV_MODULE_RELDATE "Apr. 2015" + +static char version[] = + DRV_MODULE_DESC " " DRV_MODULE_NAME + " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Chelsio Communications, Inc."); MODULE_DESCRIPTION(DRV_MODULE_DESC); @@ -728,7 +732,7 @@ static struct cxgbi_sock *cxgbi_check_route6(struct sockaddr *dst_addr) } ndev = n->dev; - if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) { + if (ipv6_addr_is_multicast(&daddr6->sin6_addr)) { pr_info("multi-cast route %pI6 port %u, dev %s.\n", daddr6->sin6_addr.s6_addr, ntohs(daddr6->sin6_port), ndev->name); @@ -1126,11 +1130,11 @@ static int cxgbi_sock_send_pdus(struct cxgbi_sock *csk, struct sk_buff *skb) goto out_err; } - if (csk->write_seq - csk->snd_una >= cdev->snd_win) { + if (csk->write_seq - csk->snd_una >= csk->snd_win) { log_debug(1 << CXGBI_DBG_PDU_TX, "csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n", csk, csk->state, csk->flags, csk->tid, csk->write_seq, - csk->snd_una, cdev->snd_win); + csk->snd_una, csk->snd_win); err = -ENOBUFS; goto out_err; } @@ -1885,7 +1889,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied) "csk 0x%p,%u,0x%lx,%u, seq %u, wup %u, thre %u, %u.\n", csk, csk->state, csk->flags, csk->tid, csk->copied_seq, csk->rcv_wup, cdev->rx_credit_thres, - cdev->rcv_win); + csk->rcv_win); if (csk->state != CTP_ESTABLISHED) return; @@ -1896,7 +1900,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied) if (unlikely(cdev->rx_credit_thres == 0)) return; - must_send = credits + 16384 >= cdev->rcv_win; + must_send = credits + 16384 >= csk->rcv_win; if (must_send || credits >= cdev->rx_credit_thres) csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits); } @@ -2913,6 +2917,8 @@ static int __init libcxgbi_init_module(void) sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1; sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1; + pr_info("%s", version); + pr_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n", ISCSI_ITT_MASK, sw_tag_idx_bits, ISCSI_AGE_MASK, sw_tag_age_bits); diff --git a/kernel/drivers/scsi/cxgbi/libcxgbi.h b/kernel/drivers/scsi/cxgbi/libcxgbi.h index aba1af720..9842301f7 100644 --- a/kernel/drivers/scsi/cxgbi/libcxgbi.h +++ b/kernel/drivers/scsi/cxgbi/libcxgbi.h @@ -1,7 +1,7 @@ /* * libcxgbi.h: Chelsio common library for T3/T4 iSCSI driver. * - * Copyright (c) 2010 Chelsio Communications, Inc. + * Copyright (c) 2010-2015 Chelsio Communications, Inc. * * 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 @@ -234,6 +234,8 @@ struct cxgbi_sock { u32 snd_nxt; u32 snd_una; u32 write_seq; + u32 snd_win; + u32 rcv_win; }; /* @@ -540,8 +542,6 @@ struct cxgbi_device { struct iscsi_transport *itp; unsigned int pfvf; - unsigned int snd_win; - unsigned int rcv_win; unsigned int rx_credit_thres; unsigned int skb_tx_rsvd; unsigned int skb_rx_extra; /* for msg coalesced mode */ @@ -685,10 +685,7 @@ static inline void *cxgbi_alloc_big_mem(unsigned int size, static inline void cxgbi_free_big_mem(void *addr) { - if (is_vmalloc_addr(addr)) - vfree(addr); - else - kfree(addr); + kvfree(addr); } static inline void cxgbi_set_iscsi_ipv4(struct cxgbi_hba *chba, __be32 ipaddr) diff --git a/kernel/drivers/scsi/cxlflash/Kconfig b/kernel/drivers/scsi/cxlflash/Kconfig new file mode 100644 index 000000000..c052104e5 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/Kconfig @@ -0,0 +1,11 @@ +# +# IBM CXL-attached Flash Accelerator SCSI Driver +# + +config CXLFLASH + tristate "Support for IBM CAPI Flash" + depends on PCI && SCSI && CXL && EEH + default m + help + Allows CAPI Accelerated IO to Flash + If unsure, say N. diff --git a/kernel/drivers/scsi/cxlflash/Makefile b/kernel/drivers/scsi/cxlflash/Makefile new file mode 100644 index 000000000..9e39866d4 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CXLFLASH) += cxlflash.o +cxlflash-y += main.o superpipe.o lunmgt.o vlun.o diff --git a/kernel/drivers/scsi/cxlflash/common.h b/kernel/drivers/scsi/cxlflash/common.h new file mode 100644 index 000000000..c11cd193f --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/common.h @@ -0,0 +1,204 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _CXLFLASH_COMMON_H +#define _CXLFLASH_COMMON_H + +#include +#include +#include +#include +#include + +extern const struct file_operations cxlflash_cxl_fops; + +#define MAX_CONTEXT CXLFLASH_MAX_CONTEXT /* num contexts per afu */ + +#define CXLFLASH_BLOCK_SIZE 4096 /* 4K blocks */ +#define CXLFLASH_MAX_XFER_SIZE 16777216 /* 16MB transfer */ +#define CXLFLASH_MAX_SECTORS (CXLFLASH_MAX_XFER_SIZE/512) /* SCSI wants + max_sectors + in units of + 512 byte + sectors + */ + +#define NUM_RRQ_ENTRY 16 /* for master issued cmds */ +#define MAX_RHT_PER_CONTEXT (PAGE_SIZE / sizeof(struct sisl_rht_entry)) + +/* AFU command retry limit */ +#define MC_RETRY_CNT 5 /* sufficient for SCSI check and + certain AFU errors */ + +/* Command management definitions */ +#define CXLFLASH_NUM_CMDS (2 * CXLFLASH_MAX_CMDS) /* Must be a pow2 for + alignment and more + efficient array + index derivation + */ + +#define CXLFLASH_MAX_CMDS 16 +#define CXLFLASH_MAX_CMDS_PER_LUN CXLFLASH_MAX_CMDS + + +static inline void check_sizes(void) +{ + BUILD_BUG_ON_NOT_POWER_OF_2(CXLFLASH_NUM_CMDS); +} + +/* AFU defines a fixed size of 4K for command buffers (borrow 4K page define) */ +#define CMD_BUFSIZE SIZE_4K + +/* flags in IOA status area for host use */ +#define B_DONE 0x01 +#define B_ERROR 0x02 /* set with B_DONE */ +#define B_TIMEOUT 0x04 /* set with B_DONE & B_ERROR */ + +enum cxlflash_lr_state { + LINK_RESET_INVALID, + LINK_RESET_REQUIRED, + LINK_RESET_COMPLETE +}; + +enum cxlflash_init_state { + INIT_STATE_NONE, + INIT_STATE_PCI, + INIT_STATE_AFU, + INIT_STATE_SCSI +}; + +enum cxlflash_state { + STATE_NORMAL, /* Normal running state, everything good */ + STATE_RESET, /* Reset state, trying to reset/recover */ + STATE_FAILTERM /* Failed/terminating state, error out users/threads */ +}; + +/* + * Each context has its own set of resource handles that is visible + * only from that context. + */ + +struct cxlflash_cfg { + struct afu *afu; + struct cxl_context *mcctx; + + struct pci_dev *dev; + struct pci_device_id *dev_id; + struct Scsi_Host *host; + + ulong cxlflash_regs_pci; + + struct work_struct work_q; + enum cxlflash_init_state init_state; + enum cxlflash_lr_state lr_state; + int lr_port; + atomic_t scan_host_needed; + + struct cxl_afu *cxl_afu; + struct pci_dev *parent_dev; + + atomic_t recovery_threads; + struct mutex ctx_recovery_mutex; + struct mutex ctx_tbl_list_mutex; + struct rw_semaphore ioctl_rwsem; + struct ctx_info *ctx_tbl[MAX_CONTEXT]; + struct list_head ctx_err_recovery; /* contexts w/ recovery pending */ + struct file_operations cxl_fops; + + /* Parameters that are LUN table related */ + int last_lun_index[CXLFLASH_NUM_FC_PORTS]; + int promote_lun_index; + struct list_head lluns; /* list of llun_info structs */ + + wait_queue_head_t tmf_waitq; + spinlock_t tmf_slock; + bool tmf_active; + wait_queue_head_t reset_waitq; + enum cxlflash_state state; +}; + +struct afu_cmd { + struct sisl_ioarcb rcb; /* IOARCB (cache line aligned) */ + struct sisl_ioasa sa; /* IOASA must follow IOARCB */ + spinlock_t slock; + struct completion cevent; + char *buf; /* per command buffer */ + struct afu *parent; + int slot; + atomic_t free; + + u8 cmd_tmf:1; + + /* As per the SISLITE spec the IOARCB EA has to be 16-byte aligned. + * However for performance reasons the IOARCB/IOASA should be + * cache line aligned. + */ +} __aligned(cache_line_size()); + +struct afu { + /* Stuff requiring alignment go first. */ + + u64 rrq_entry[NUM_RRQ_ENTRY]; /* 128B RRQ */ + /* + * Command & data for AFU commands. + */ + struct afu_cmd cmd[CXLFLASH_NUM_CMDS]; + + /* Beware of alignment till here. Preferably introduce new + * fields after this point + */ + + /* AFU HW */ + struct cxl_ioctl_start_work work; + struct cxlflash_afu_map __iomem *afu_map; /* entire MMIO map */ + struct sisl_host_map __iomem *host_map; /* MC host map */ + struct sisl_ctrl_map __iomem *ctrl_map; /* MC control map */ + + ctx_hndl_t ctx_hndl; /* master's context handle */ + u64 *hrrq_start; + u64 *hrrq_end; + u64 *hrrq_curr; + bool toggle; + bool read_room; + atomic64_t room; + u64 hb; + u32 cmd_couts; /* Number of command checkouts */ + u32 internal_lun; /* User-desired LUN mode for this AFU */ + + char version[16]; + u64 interface_version; + + struct cxlflash_cfg *parent; /* Pointer back to parent cxlflash_cfg */ + +}; + +static inline u64 lun_to_lunid(u64 lun) +{ + __be64 lun_id; + + int_to_scsilun(lun, (struct scsi_lun *)&lun_id); + return be64_to_cpu(lun_id); +} + +int cxlflash_afu_sync(struct afu *, ctx_hndl_t, res_hndl_t, u8); +void cxlflash_list_init(void); +void cxlflash_term_global_luns(void); +void cxlflash_free_errpage(void); +int cxlflash_ioctl(struct scsi_device *, int, void __user *); +void cxlflash_stop_term_user_contexts(struct cxlflash_cfg *); +int cxlflash_mark_contexts_error(struct cxlflash_cfg *); +void cxlflash_term_local_luns(struct cxlflash_cfg *); +void cxlflash_restore_luntable(struct cxlflash_cfg *); + +#endif /* ifndef _CXLFLASH_COMMON_H */ diff --git a/kernel/drivers/scsi/cxlflash/lunmgt.c b/kernel/drivers/scsi/cxlflash/lunmgt.c new file mode 100644 index 000000000..a0923cade --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/lunmgt.c @@ -0,0 +1,269 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include +#include + +#include "sislite.h" +#include "common.h" +#include "vlun.h" +#include "superpipe.h" + +/** + * create_local() - allocate and initialize a local LUN information structure + * @sdev: SCSI device associated with LUN. + * @wwid: World Wide Node Name for LUN. + * + * Return: Allocated local llun_info structure on success, NULL on failure + */ +static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid) +{ + struct llun_info *lli = NULL; + + lli = kzalloc(sizeof(*lli), GFP_KERNEL); + if (unlikely(!lli)) { + pr_err("%s: could not allocate lli\n", __func__); + goto out; + } + + lli->sdev = sdev; + lli->host_no = sdev->host->host_no; + lli->in_table = false; + + memcpy(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); +out: + return lli; +} + +/** + * create_global() - allocate and initialize a global LUN information structure + * @sdev: SCSI device associated with LUN. + * @wwid: World Wide Node Name for LUN. + * + * Return: Allocated global glun_info structure on success, NULL on failure + */ +static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid) +{ + struct glun_info *gli = NULL; + + gli = kzalloc(sizeof(*gli), GFP_KERNEL); + if (unlikely(!gli)) { + pr_err("%s: could not allocate gli\n", __func__); + goto out; + } + + mutex_init(&gli->mutex); + memcpy(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN); +out: + return gli; +} + +/** + * lookup_local() - find a local LUN information structure by WWID + * @cfg: Internal structure associated with the host. + * @wwid: WWID associated with LUN. + * + * Return: Found local lun_info structure on success, NULL on failure + */ +static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid) +{ + struct llun_info *lli, *temp; + + list_for_each_entry_safe(lli, temp, &cfg->lluns, list) + if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) + return lli; + + return NULL; +} + +/** + * lookup_global() - find a global LUN information structure by WWID + * @wwid: WWID associated with LUN. + * + * Return: Found global lun_info structure on success, NULL on failure + */ +static struct glun_info *lookup_global(u8 *wwid) +{ + struct glun_info *gli, *temp; + + list_for_each_entry_safe(gli, temp, &global.gluns, list) + if (!memcmp(gli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) + return gli; + + return NULL; +} + +/** + * find_and_create_lun() - find or create a local LUN information structure + * @sdev: SCSI device associated with LUN. + * @wwid: WWID associated with LUN. + * + * The LUN is kept both in a local list (per adapter) and in a global list + * (across all adapters). Certain attributes of the LUN are local to the + * adapter (such as index, port selection mask, etc.). + * + * The block allocation map is shared across all adapters (i.e. associated + * wih the global list). Since different attributes are associated with + * the per adapter and global entries, allocate two separate structures for each + * LUN (one local, one global). + * + * Keep a pointer back from the local to the global entry. + * + * This routine assumes the caller holds the global mutex. + * + * Return: Found/Allocated local lun_info structure on success, NULL on failure + */ +static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) +{ + struct llun_info *lli = NULL; + struct glun_info *gli = NULL; + struct Scsi_Host *shost = sdev->host; + struct cxlflash_cfg *cfg = shost_priv(shost); + + if (unlikely(!wwid)) + goto out; + + lli = lookup_local(cfg, wwid); + if (lli) + goto out; + + lli = create_local(sdev, wwid); + if (unlikely(!lli)) + goto out; + + gli = lookup_global(wwid); + if (gli) { + lli->parent = gli; + list_add(&lli->list, &cfg->lluns); + goto out; + } + + gli = create_global(sdev, wwid); + if (unlikely(!gli)) { + kfree(lli); + lli = NULL; + goto out; + } + + lli->parent = gli; + list_add(&lli->list, &cfg->lluns); + + list_add(&gli->list, &global.gluns); + +out: + pr_debug("%s: returning %p\n", __func__, lli); + return lli; +} + +/** + * cxlflash_term_local_luns() - Delete all entries from local LUN list, free. + * @cfg: Internal structure associated with the host. + */ +void cxlflash_term_local_luns(struct cxlflash_cfg *cfg) +{ + struct llun_info *lli, *temp; + + mutex_lock(&global.mutex); + list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { + list_del(&lli->list); + kfree(lli); + } + mutex_unlock(&global.mutex); +} + +/** + * cxlflash_list_init() - initializes the global LUN list + */ +void cxlflash_list_init(void) +{ + INIT_LIST_HEAD(&global.gluns); + mutex_init(&global.mutex); + global.err_page = NULL; +} + +/** + * cxlflash_term_global_luns() - frees resources associated with global LUN list + */ +void cxlflash_term_global_luns(void) +{ + struct glun_info *gli, *temp; + + mutex_lock(&global.mutex); + list_for_each_entry_safe(gli, temp, &global.gluns, list) { + list_del(&gli->list); + cxlflash_ba_terminate(&gli->blka.ba_lun); + kfree(gli); + } + mutex_unlock(&global.mutex); +} + +/** + * cxlflash_manage_lun() - handles LUN management activities + * @sdev: SCSI device associated with LUN. + * @manage: Manage ioctl data structure. + * + * This routine is used to notify the driver about a LUN's WWID and associate + * SCSI devices (sdev) with a global LUN instance. Additionally it serves to + * change a LUN's operating mode: legacy or superpipe. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_manage_lun(struct scsi_device *sdev, + struct dk_cxlflash_manage_lun *manage) +{ + int rc = 0; + struct llun_info *lli = NULL; + u64 flags = manage->hdr.flags; + u32 chan = sdev->channel; + + mutex_lock(&global.mutex); + lli = find_and_create_lun(sdev, manage->wwid); + pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n", + __func__, get_unaligned_be64(&manage->wwid[0]), + get_unaligned_be64(&manage->wwid[8]), + manage->hdr.flags, lli); + if (unlikely(!lli)) { + rc = -ENOMEM; + goto out; + } + + if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { + /* + * Update port selection mask based upon channel, store off LUN + * in unpacked, AFU-friendly format, and hang LUN reference in + * the sdev. + */ + lli->port_sel |= CHAN2PORT(chan); + lli->lun_id[chan] = lun_to_lunid(sdev->lun); + sdev->hostdata = lli; + } else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) { + if (lli->parent->mode != MODE_NONE) + rc = -EBUSY; + else { + sdev->hostdata = NULL; + lli->port_sel &= ~CHAN2PORT(chan); + } + } + + pr_debug("%s: port_sel = %08X chan = %u lun_id = %016llX\n", __func__, + lli->port_sel, chan, lli->lun_id[chan]); + +out: + mutex_unlock(&global.mutex); + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} diff --git a/kernel/drivers/scsi/cxlflash/main.c b/kernel/drivers/scsi/cxlflash/main.c new file mode 100644 index 000000000..1e5bf0ca8 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/main.c @@ -0,0 +1,2608 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "main.h" +#include "sislite.h" +#include "common.h" + +MODULE_DESCRIPTION(CXLFLASH_ADAPTER_NAME); +MODULE_AUTHOR("Manoj N. Kumar "); +MODULE_AUTHOR("Matthew R. Ochs "); +MODULE_LICENSE("GPL"); + +/** + * cmd_checkout() - checks out an AFU command + * @afu: AFU to checkout from. + * + * Commands are checked out in a round-robin fashion. Note that since + * the command pool is larger than the hardware queue, the majority of + * times we will only loop once or twice before getting a command. The + * buffer and CDB within the command are initialized (zeroed) prior to + * returning. + * + * Return: The checked out command or NULL when command pool is empty. + */ +static struct afu_cmd *cmd_checkout(struct afu *afu) +{ + int k, dec = CXLFLASH_NUM_CMDS; + struct afu_cmd *cmd; + + while (dec--) { + k = (afu->cmd_couts++ & (CXLFLASH_NUM_CMDS - 1)); + + cmd = &afu->cmd[k]; + + if (!atomic_dec_if_positive(&cmd->free)) { + pr_devel("%s: returning found index=%d cmd=%p\n", + __func__, cmd->slot, cmd); + memset(cmd->buf, 0, CMD_BUFSIZE); + memset(cmd->rcb.cdb, 0, sizeof(cmd->rcb.cdb)); + return cmd; + } + } + + return NULL; +} + +/** + * cmd_checkin() - checks in an AFU command + * @cmd: AFU command to checkin. + * + * Safe to pass commands that have already been checked in. Several + * internal tracking fields are reset as part of the checkin. Note + * that these are intentionally reset prior to toggling the free bit + * to avoid clobbering values in the event that the command is checked + * out right away. + */ +static void cmd_checkin(struct afu_cmd *cmd) +{ + cmd->rcb.scp = NULL; + cmd->rcb.timeout = 0; + cmd->sa.ioasc = 0; + cmd->cmd_tmf = false; + cmd->sa.host_use[0] = 0; /* clears both completion and retry bytes */ + + if (unlikely(atomic_inc_return(&cmd->free) != 1)) { + pr_err("%s: Freeing cmd (%d) that is not in use!\n", + __func__, cmd->slot); + return; + } + + pr_devel("%s: released cmd %p index=%d\n", __func__, cmd, cmd->slot); +} + +/** + * process_cmd_err() - command error handler + * @cmd: AFU command that experienced the error. + * @scp: SCSI command associated with the AFU command in error. + * + * Translates error bits from AFU command to SCSI command results. + */ +static void process_cmd_err(struct afu_cmd *cmd, struct scsi_cmnd *scp) +{ + struct sisl_ioarcb *ioarcb; + struct sisl_ioasa *ioasa; + u32 resid; + + if (unlikely(!cmd)) + return; + + ioarcb = &(cmd->rcb); + ioasa = &(cmd->sa); + + if (ioasa->rc.flags & SISL_RC_FLAGS_UNDERRUN) { + resid = ioasa->resid; + scsi_set_resid(scp, resid); + pr_debug("%s: cmd underrun cmd = %p scp = %p, resid = %d\n", + __func__, cmd, scp, resid); + } + + if (ioasa->rc.flags & SISL_RC_FLAGS_OVERRUN) { + pr_debug("%s: cmd underrun cmd = %p scp = %p\n", + __func__, cmd, scp); + scp->result = (DID_ERROR << 16); + } + + pr_debug("%s: cmd failed afu_rc=%d scsi_rc=%d fc_rc=%d " + "afu_extra=0x%X, scsi_extra=0x%X, fc_extra=0x%X\n", + __func__, ioasa->rc.afu_rc, ioasa->rc.scsi_rc, + ioasa->rc.fc_rc, ioasa->afu_extra, ioasa->scsi_extra, + ioasa->fc_extra); + + if (ioasa->rc.scsi_rc) { + /* We have a SCSI status */ + if (ioasa->rc.flags & SISL_RC_FLAGS_SENSE_VALID) { + memcpy(scp->sense_buffer, ioasa->sense_data, + SISL_SENSE_DATA_LEN); + scp->result = ioasa->rc.scsi_rc; + } else + scp->result = ioasa->rc.scsi_rc | (DID_ERROR << 16); + } + + /* + * We encountered an error. Set scp->result based on nature + * of error. + */ + if (ioasa->rc.fc_rc) { + /* We have an FC status */ + switch (ioasa->rc.fc_rc) { + case SISL_FC_RC_LINKDOWN: + scp->result = (DID_REQUEUE << 16); + break; + case SISL_FC_RC_RESID: + /* This indicates an FCP resid underrun */ + if (!(ioasa->rc.flags & SISL_RC_FLAGS_OVERRUN)) { + /* If the SISL_RC_FLAGS_OVERRUN flag was set, + * then we will handle this error else where. + * If not then we must handle it here. + * This is probably an AFU bug. + */ + scp->result = (DID_ERROR << 16); + } + break; + case SISL_FC_RC_RESIDERR: + /* Resid mismatch between adapter and device */ + case SISL_FC_RC_TGTABORT: + case SISL_FC_RC_ABORTOK: + case SISL_FC_RC_ABORTFAIL: + case SISL_FC_RC_NOLOGI: + case SISL_FC_RC_ABORTPEND: + case SISL_FC_RC_WRABORTPEND: + case SISL_FC_RC_NOEXP: + case SISL_FC_RC_INUSE: + scp->result = (DID_ERROR << 16); + break; + } + } + + if (ioasa->rc.afu_rc) { + /* We have an AFU error */ + switch (ioasa->rc.afu_rc) { + case SISL_AFU_RC_NO_CHANNELS: + scp->result = (DID_NO_CONNECT << 16); + break; + case SISL_AFU_RC_DATA_DMA_ERR: + switch (ioasa->afu_extra) { + case SISL_AFU_DMA_ERR_PAGE_IN: + /* Retry */ + scp->result = (DID_IMM_RETRY << 16); + break; + case SISL_AFU_DMA_ERR_INVALID_EA: + default: + scp->result = (DID_ERROR << 16); + } + break; + case SISL_AFU_RC_OUT_OF_DATA_BUFS: + /* Retry */ + scp->result = (DID_ALLOC_FAILURE << 16); + break; + default: + scp->result = (DID_ERROR << 16); + } + } +} + +/** + * cmd_complete() - command completion handler + * @cmd: AFU command that has completed. + * + * Prepares and submits command that has either completed or timed out to + * the SCSI stack. Checks AFU command back into command pool for non-internal + * (rcb.scp populated) commands. + */ +static void cmd_complete(struct afu_cmd *cmd) +{ + struct scsi_cmnd *scp; + ulong lock_flags; + struct afu *afu = cmd->parent; + struct cxlflash_cfg *cfg = afu->parent; + bool cmd_is_tmf; + + spin_lock_irqsave(&cmd->slock, lock_flags); + cmd->sa.host_use_b[0] |= B_DONE; + spin_unlock_irqrestore(&cmd->slock, lock_flags); + + if (cmd->rcb.scp) { + scp = cmd->rcb.scp; + if (unlikely(cmd->sa.ioasc)) + process_cmd_err(cmd, scp); + else + scp->result = (DID_OK << 16); + + cmd_is_tmf = cmd->cmd_tmf; + cmd_checkin(cmd); /* Don't use cmd after here */ + + pr_debug_ratelimited("%s: calling scsi_done scp=%p result=%X " + "ioasc=%d\n", __func__, scp, scp->result, + cmd->sa.ioasc); + + scsi_dma_unmap(scp); + scp->scsi_done(scp); + + if (cmd_is_tmf) { + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + cfg->tmf_active = false; + wake_up_all_locked(&cfg->tmf_waitq); + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + } + } else + complete(&cmd->cevent); +} + +/** + * context_reset() - timeout handler for AFU commands + * @cmd: AFU command that timed out. + * + * Sends a reset to the AFU. + */ +static void context_reset(struct afu_cmd *cmd) +{ + int nretry = 0; + u64 rrin = 0x1; + u64 room = 0; + struct afu *afu = cmd->parent; + ulong lock_flags; + + pr_debug("%s: cmd=%p\n", __func__, cmd); + + spin_lock_irqsave(&cmd->slock, lock_flags); + + /* Already completed? */ + if (cmd->sa.host_use_b[0] & B_DONE) { + spin_unlock_irqrestore(&cmd->slock, lock_flags); + return; + } + + cmd->sa.host_use_b[0] |= (B_DONE | B_ERROR | B_TIMEOUT); + spin_unlock_irqrestore(&cmd->slock, lock_flags); + + /* + * We really want to send this reset at all costs, so spread + * out wait time on successive retries for available room. + */ + do { + room = readq_be(&afu->host_map->cmd_room); + atomic64_set(&afu->room, room); + if (room) + goto write_rrin; + udelay(nretry); + } while (nretry++ < MC_ROOM_RETRY_CNT); + + pr_err("%s: no cmd_room to send reset\n", __func__); + return; + +write_rrin: + nretry = 0; + writeq_be(rrin, &afu->host_map->ioarrin); + do { + rrin = readq_be(&afu->host_map->ioarrin); + if (rrin != 0x1) + break; + /* Double delay each time */ + udelay(2 << nretry); + } while (nretry++ < MC_ROOM_RETRY_CNT); +} + +/** + * send_cmd() - sends an AFU command + * @afu: AFU associated with the host. + * @cmd: AFU command to send. + * + * Return: + * 0 on success, SCSI_MLQUEUE_HOST_BUSY on failure + */ +static int send_cmd(struct afu *afu, struct afu_cmd *cmd) +{ + struct cxlflash_cfg *cfg = afu->parent; + struct device *dev = &cfg->dev->dev; + int nretry = 0; + int rc = 0; + u64 room; + long newval; + + /* + * This routine is used by critical users such an AFU sync and to + * send a task management function (TMF). Thus we want to retry a + * bit before returning an error. To avoid the performance penalty + * of MMIO, we spread the update of 'room' over multiple commands. + */ +retry: + newval = atomic64_dec_if_positive(&afu->room); + if (!newval) { + do { + room = readq_be(&afu->host_map->cmd_room); + atomic64_set(&afu->room, room); + if (room) + goto write_ioarrin; + udelay(nretry); + } while (nretry++ < MC_ROOM_RETRY_CNT); + + dev_err(dev, "%s: no cmd_room to send 0x%X\n", + __func__, cmd->rcb.cdb[0]); + + goto no_room; + } else if (unlikely(newval < 0)) { + /* This should be rare. i.e. Only if two threads race and + * decrement before the MMIO read is done. In this case + * just benefit from the other thread having updated + * afu->room. + */ + if (nretry++ < MC_ROOM_RETRY_CNT) { + udelay(nretry); + goto retry; + } + + goto no_room; + } + +write_ioarrin: + writeq_be((u64)&cmd->rcb, &afu->host_map->ioarrin); +out: + pr_devel("%s: cmd=%p len=%d ea=%p rc=%d\n", __func__, cmd, + cmd->rcb.data_len, (void *)cmd->rcb.data_ea, rc); + return rc; + +no_room: + afu->read_room = true; + schedule_work(&cfg->work_q); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; +} + +/** + * wait_resp() - polls for a response or timeout to a sent AFU command + * @afu: AFU associated with the host. + * @cmd: AFU command that was sent. + */ +static void wait_resp(struct afu *afu, struct afu_cmd *cmd) +{ + ulong timeout = msecs_to_jiffies(cmd->rcb.timeout * 2 * 1000); + + timeout = wait_for_completion_timeout(&cmd->cevent, timeout); + if (!timeout) + context_reset(cmd); + + if (unlikely(cmd->sa.ioasc != 0)) + pr_err("%s: CMD 0x%X failed, IOASC: flags 0x%X, afu_rc 0x%X, " + "scsi_rc 0x%X, fc_rc 0x%X\n", __func__, cmd->rcb.cdb[0], + cmd->sa.rc.flags, cmd->sa.rc.afu_rc, cmd->sa.rc.scsi_rc, + cmd->sa.rc.fc_rc); +} + +/** + * send_tmf() - sends a Task Management Function (TMF) + * @afu: AFU to checkout from. + * @scp: SCSI command from stack. + * @tmfcmd: TMF command to send. + * + * Return: + * 0 on success, SCSI_MLQUEUE_HOST_BUSY on failure + */ +static int send_tmf(struct afu *afu, struct scsi_cmnd *scp, u64 tmfcmd) +{ + struct afu_cmd *cmd; + + u32 port_sel = scp->device->channel + 1; + short lflag = 0; + struct Scsi_Host *host = scp->device->host; + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)host->hostdata; + struct device *dev = &cfg->dev->dev; + ulong lock_flags; + int rc = 0; + ulong to; + + cmd = cmd_checkout(afu); + if (unlikely(!cmd)) { + dev_err(dev, "%s: could not get a free command\n", __func__); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + + /* When Task Management Function is active do not send another */ + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + if (cfg->tmf_active) + wait_event_interruptible_lock_irq(cfg->tmf_waitq, + !cfg->tmf_active, + cfg->tmf_slock); + cfg->tmf_active = true; + cmd->cmd_tmf = true; + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + + cmd->rcb.ctx_id = afu->ctx_hndl; + cmd->rcb.port_sel = port_sel; + cmd->rcb.lun_id = lun_to_lunid(scp->device->lun); + + lflag = SISL_REQ_FLAGS_TMF_CMD; + + cmd->rcb.req_flags = (SISL_REQ_FLAGS_PORT_LUN_ID | + SISL_REQ_FLAGS_SUP_UNDERRUN | lflag); + + /* Stash the scp in the reserved field, for reuse during interrupt */ + cmd->rcb.scp = scp; + + /* Copy the CDB from the cmd passed in */ + memcpy(cmd->rcb.cdb, &tmfcmd, sizeof(tmfcmd)); + + /* Send the command */ + rc = send_cmd(afu, cmd); + if (unlikely(rc)) { + cmd_checkin(cmd); + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + cfg->tmf_active = false; + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + goto out; + } + + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + to = msecs_to_jiffies(5000); + to = wait_event_interruptible_lock_irq_timeout(cfg->tmf_waitq, + !cfg->tmf_active, + cfg->tmf_slock, + to); + if (!to) { + cfg->tmf_active = false; + dev_err(dev, "%s: TMF timed out!\n", __func__); + rc = -1; + } + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); +out: + return rc; +} + +/** + * cxlflash_driver_info() - information handler for this host driver + * @host: SCSI host associated with device. + * + * Return: A string describing the device. + */ +static const char *cxlflash_driver_info(struct Scsi_Host *host) +{ + return CXLFLASH_ADAPTER_NAME; +} + +/** + * cxlflash_queuecommand() - sends a mid-layer request + * @host: SCSI host associated with device. + * @scp: SCSI command to send. + * + * Return: 0 on success, SCSI_MLQUEUE_HOST_BUSY on failure + */ +static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)host->hostdata; + struct afu *afu = cfg->afu; + struct device *dev = &cfg->dev->dev; + struct afu_cmd *cmd; + u32 port_sel = scp->device->channel + 1; + int nseg, i, ncount; + struct scatterlist *sg; + ulong lock_flags; + short lflag = 0; + int rc = 0; + + dev_dbg_ratelimited(dev, "%s: (scp=%p) %d/%d/%d/%llu " + "cdb=(%08X-%08X-%08X-%08X)\n", + __func__, scp, host->host_no, scp->device->channel, + scp->device->id, scp->device->lun, + get_unaligned_be32(&((u32 *)scp->cmnd)[0]), + get_unaligned_be32(&((u32 *)scp->cmnd)[1]), + get_unaligned_be32(&((u32 *)scp->cmnd)[2]), + get_unaligned_be32(&((u32 *)scp->cmnd)[3])); + + /* + * If a Task Management Function is active, wait for it to complete + * before continuing with regular commands. + */ + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + if (cfg->tmf_active) { + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + + switch (cfg->state) { + case STATE_RESET: + dev_dbg_ratelimited(dev, "%s: device is in reset!\n", __func__); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; + case STATE_FAILTERM: + dev_dbg_ratelimited(dev, "%s: device has failed!\n", __func__); + scp->result = (DID_NO_CONNECT << 16); + scp->scsi_done(scp); + rc = 0; + goto out; + default: + break; + } + + cmd = cmd_checkout(afu); + if (unlikely(!cmd)) { + dev_err(dev, "%s: could not get a free command\n", __func__); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + + cmd->rcb.ctx_id = afu->ctx_hndl; + cmd->rcb.port_sel = port_sel; + cmd->rcb.lun_id = lun_to_lunid(scp->device->lun); + + if (scp->sc_data_direction == DMA_TO_DEVICE) + lflag = SISL_REQ_FLAGS_HOST_WRITE; + else + lflag = SISL_REQ_FLAGS_HOST_READ; + + cmd->rcb.req_flags = (SISL_REQ_FLAGS_PORT_LUN_ID | + SISL_REQ_FLAGS_SUP_UNDERRUN | lflag); + + /* Stash the scp in the reserved field, for reuse during interrupt */ + cmd->rcb.scp = scp; + + nseg = scsi_dma_map(scp); + if (unlikely(nseg < 0)) { + dev_err(dev, "%s: Fail DMA map! nseg=%d\n", + __func__, nseg); + rc = SCSI_MLQUEUE_HOST_BUSY; + goto out; + } + + ncount = scsi_sg_count(scp); + scsi_for_each_sg(scp, sg, ncount, i) { + cmd->rcb.data_len = sg_dma_len(sg); + cmd->rcb.data_ea = sg_dma_address(sg); + } + + /* Copy the CDB from the scsi_cmnd passed in */ + memcpy(cmd->rcb.cdb, scp->cmnd, sizeof(cmd->rcb.cdb)); + + /* Send the command */ + rc = send_cmd(afu, cmd); + if (unlikely(rc)) { + cmd_checkin(cmd); + scsi_dma_unmap(scp); + } + +out: + pr_devel("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * cxlflash_wait_for_pci_err_recovery() - wait for error recovery during probe + * @cfg: Internal structure associated with the host. + */ +static void cxlflash_wait_for_pci_err_recovery(struct cxlflash_cfg *cfg) +{ + struct pci_dev *pdev = cfg->dev; + + if (pci_channel_offline(pdev)) + wait_event_timeout(cfg->reset_waitq, + !pci_channel_offline(pdev), + CXLFLASH_PCI_ERROR_RECOVERY_TIMEOUT); +} + +/** + * free_mem() - free memory associated with the AFU + * @cfg: Internal structure associated with the host. + */ +static void free_mem(struct cxlflash_cfg *cfg) +{ + int i; + char *buf = NULL; + struct afu *afu = cfg->afu; + + if (cfg->afu) { + for (i = 0; i < CXLFLASH_NUM_CMDS; i++) { + buf = afu->cmd[i].buf; + if (!((u64)buf & (PAGE_SIZE - 1))) + free_page((ulong)buf); + } + + free_pages((ulong)afu, get_order(sizeof(struct afu))); + cfg->afu = NULL; + } +} + +/** + * stop_afu() - stops the AFU command timers and unmaps the MMIO space + * @cfg: Internal structure associated with the host. + * + * Safe to call with AFU in a partially allocated/initialized state. + */ +static void stop_afu(struct cxlflash_cfg *cfg) +{ + int i; + struct afu *afu = cfg->afu; + + if (likely(afu)) { + for (i = 0; i < CXLFLASH_NUM_CMDS; i++) + complete(&afu->cmd[i].cevent); + + if (likely(afu->afu_map)) { + cxl_psa_unmap((void __iomem *)afu->afu_map); + afu->afu_map = NULL; + } + } +} + +/** + * term_mc() - terminates the master context + * @cfg: Internal structure associated with the host. + * @level: Depth of allocation, where to begin waterfall tear down. + * + * Safe to call with AFU/MC in partially allocated/initialized state. + */ +static void term_mc(struct cxlflash_cfg *cfg, enum undo_level level) +{ + int rc = 0; + struct afu *afu = cfg->afu; + struct device *dev = &cfg->dev->dev; + + if (!afu || !cfg->mcctx) { + dev_err(dev, "%s: returning from term_mc with NULL afu or MC\n", + __func__); + return; + } + + switch (level) { + case UNDO_START: + rc = cxl_stop_context(cfg->mcctx); + BUG_ON(rc); + case UNMAP_THREE: + cxl_unmap_afu_irq(cfg->mcctx, 3, afu); + case UNMAP_TWO: + cxl_unmap_afu_irq(cfg->mcctx, 2, afu); + case UNMAP_ONE: + cxl_unmap_afu_irq(cfg->mcctx, 1, afu); + case FREE_IRQ: + cxl_free_afu_irqs(cfg->mcctx); + case RELEASE_CONTEXT: + cfg->mcctx = NULL; + } +} + +/** + * term_afu() - terminates the AFU + * @cfg: Internal structure associated with the host. + * + * Safe to call with AFU/MC in partially allocated/initialized state. + */ +static void term_afu(struct cxlflash_cfg *cfg) +{ + term_mc(cfg, UNDO_START); + + if (cfg->afu) + stop_afu(cfg); + + pr_debug("%s: returning\n", __func__); +} + +/** + * cxlflash_remove() - PCI entry point to tear down host + * @pdev: PCI device associated with the host. + * + * Safe to use as a cleanup in partially allocated/initialized state. + */ +static void cxlflash_remove(struct pci_dev *pdev) +{ + struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); + ulong lock_flags; + + /* If a Task Management Function is active, wait for it to complete + * before continuing with remove. + */ + spin_lock_irqsave(&cfg->tmf_slock, lock_flags); + if (cfg->tmf_active) + wait_event_interruptible_lock_irq(cfg->tmf_waitq, + !cfg->tmf_active, + cfg->tmf_slock); + spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + + cfg->state = STATE_FAILTERM; + cxlflash_stop_term_user_contexts(cfg); + + switch (cfg->init_state) { + case INIT_STATE_SCSI: + cxlflash_term_local_luns(cfg); + scsi_remove_host(cfg->host); + /* fall through */ + case INIT_STATE_AFU: + term_afu(cfg); + cancel_work_sync(&cfg->work_q); + case INIT_STATE_PCI: + pci_release_regions(cfg->dev); + pci_disable_device(pdev); + case INIT_STATE_NONE: + free_mem(cfg); + scsi_host_put(cfg->host); + break; + } + + pr_debug("%s: returning\n", __func__); +} + +/** + * alloc_mem() - allocates the AFU and its command pool + * @cfg: Internal structure associated with the host. + * + * A partially allocated state remains on failure. + * + * Return: + * 0 on success + * -ENOMEM on failure to allocate memory + */ +static int alloc_mem(struct cxlflash_cfg *cfg) +{ + int rc = 0; + int i; + char *buf = NULL; + struct device *dev = &cfg->dev->dev; + + /* AFU is ~12k, i.e. only one 64k page or up to four 4k pages */ + cfg->afu = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(sizeof(struct afu))); + if (unlikely(!cfg->afu)) { + dev_err(dev, "%s: cannot get %d free pages\n", + __func__, get_order(sizeof(struct afu))); + rc = -ENOMEM; + goto out; + } + cfg->afu->parent = cfg; + cfg->afu->afu_map = NULL; + + for (i = 0; i < CXLFLASH_NUM_CMDS; buf += CMD_BUFSIZE, i++) { + if (!((u64)buf & (PAGE_SIZE - 1))) { + buf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (unlikely(!buf)) { + dev_err(dev, + "%s: Allocate command buffers fail!\n", + __func__); + rc = -ENOMEM; + free_mem(cfg); + goto out; + } + } + + cfg->afu->cmd[i].buf = buf; + atomic_set(&cfg->afu->cmd[i].free, 1); + cfg->afu->cmd[i].slot = i; + } + +out: + return rc; +} + +/** + * init_pci() - initializes the host as a PCI device + * @cfg: Internal structure associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int init_pci(struct cxlflash_cfg *cfg) +{ + struct pci_dev *pdev = cfg->dev; + int rc = 0; + + cfg->cxlflash_regs_pci = pci_resource_start(pdev, 0); + rc = pci_request_regions(pdev, CXLFLASH_NAME); + if (rc < 0) { + dev_err(&pdev->dev, + "%s: Couldn't register memory range of registers\n", + __func__); + goto out; + } + + rc = pci_enable_device(pdev); + if (rc || pci_channel_offline(pdev)) { + if (pci_channel_offline(pdev)) { + cxlflash_wait_for_pci_err_recovery(cfg); + rc = pci_enable_device(pdev); + } + + if (rc) { + dev_err(&pdev->dev, "%s: Cannot enable adapter\n", + __func__); + cxlflash_wait_for_pci_err_recovery(cfg); + goto out_release_regions; + } + } + + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (rc < 0) { + dev_dbg(&pdev->dev, "%s: Failed to set 64 bit PCI DMA mask\n", + __func__); + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + } + + if (rc < 0) { + dev_err(&pdev->dev, "%s: Failed to set PCI DMA mask\n", + __func__); + goto out_disable; + } + + pci_set_master(pdev); + + if (pci_channel_offline(pdev)) { + cxlflash_wait_for_pci_err_recovery(cfg); + if (pci_channel_offline(pdev)) { + rc = -EIO; + goto out_msi_disable; + } + } + + rc = pci_save_state(pdev); + + if (rc != PCIBIOS_SUCCESSFUL) { + dev_err(&pdev->dev, "%s: Failed to save PCI config space\n", + __func__); + rc = -EIO; + goto cleanup_nolog; + } + +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; + +cleanup_nolog: +out_msi_disable: + cxlflash_wait_for_pci_err_recovery(cfg); +out_disable: + pci_disable_device(pdev); +out_release_regions: + pci_release_regions(pdev); + goto out; + +} + +/** + * init_scsi() - adds the host to the SCSI stack and kicks off host scan + * @cfg: Internal structure associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int init_scsi(struct cxlflash_cfg *cfg) +{ + struct pci_dev *pdev = cfg->dev; + int rc = 0; + + rc = scsi_add_host(cfg->host, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, "%s: scsi_add_host failed (rc=%d)\n", + __func__, rc); + goto out; + } + + scsi_scan_host(cfg->host); + +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * set_port_online() - transitions the specified host FC port to online state + * @fc_regs: Top of MMIO region defined for specified port. + * + * The provided MMIO region must be mapped prior to call. Online state means + * that the FC link layer has synced, completed the handshaking process, and + * is ready for login to start. + */ +static void set_port_online(__be64 __iomem *fc_regs) +{ + u64 cmdcfg; + + cmdcfg = readq_be(&fc_regs[FC_MTIP_CMDCONFIG / 8]); + cmdcfg &= (~FC_MTIP_CMDCONFIG_OFFLINE); /* clear OFF_LINE */ + cmdcfg |= (FC_MTIP_CMDCONFIG_ONLINE); /* set ON_LINE */ + writeq_be(cmdcfg, &fc_regs[FC_MTIP_CMDCONFIG / 8]); +} + +/** + * set_port_offline() - transitions the specified host FC port to offline state + * @fc_regs: Top of MMIO region defined for specified port. + * + * The provided MMIO region must be mapped prior to call. + */ +static void set_port_offline(__be64 __iomem *fc_regs) +{ + u64 cmdcfg; + + cmdcfg = readq_be(&fc_regs[FC_MTIP_CMDCONFIG / 8]); + cmdcfg &= (~FC_MTIP_CMDCONFIG_ONLINE); /* clear ON_LINE */ + cmdcfg |= (FC_MTIP_CMDCONFIG_OFFLINE); /* set OFF_LINE */ + writeq_be(cmdcfg, &fc_regs[FC_MTIP_CMDCONFIG / 8]); +} + +/** + * wait_port_online() - waits for the specified host FC port come online + * @fc_regs: Top of MMIO region defined for specified port. + * @delay_us: Number of microseconds to delay between reading port status. + * @nretry: Number of cycles to retry reading port status. + * + * The provided MMIO region must be mapped prior to call. This will timeout + * when the cable is not plugged in. + * + * Return: + * TRUE (1) when the specified port is online + * FALSE (0) when the specified port fails to come online after timeout + * -EINVAL when @delay_us is less than 1000 + */ +static int wait_port_online(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) +{ + u64 status; + + if (delay_us < 1000) { + pr_err("%s: invalid delay specified %d\n", __func__, delay_us); + return -EINVAL; + } + + do { + msleep(delay_us / 1000); + status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_ONLINE && + nretry--); + + return ((status & FC_MTIP_STATUS_MASK) == FC_MTIP_STATUS_ONLINE); +} + +/** + * wait_port_offline() - waits for the specified host FC port go offline + * @fc_regs: Top of MMIO region defined for specified port. + * @delay_us: Number of microseconds to delay between reading port status. + * @nretry: Number of cycles to retry reading port status. + * + * The provided MMIO region must be mapped prior to call. + * + * Return: + * TRUE (1) when the specified port is offline + * FALSE (0) when the specified port fails to go offline after timeout + * -EINVAL when @delay_us is less than 1000 + */ +static int wait_port_offline(__be64 __iomem *fc_regs, u32 delay_us, u32 nretry) +{ + u64 status; + + if (delay_us < 1000) { + pr_err("%s: invalid delay specified %d\n", __func__, delay_us); + return -EINVAL; + } + + do { + msleep(delay_us / 1000); + status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + } while ((status & FC_MTIP_STATUS_MASK) != FC_MTIP_STATUS_OFFLINE && + nretry--); + + return ((status & FC_MTIP_STATUS_MASK) == FC_MTIP_STATUS_OFFLINE); +} + +/** + * afu_set_wwpn() - configures the WWPN for the specified host FC port + * @afu: AFU associated with the host that owns the specified FC port. + * @port: Port number being configured. + * @fc_regs: Top of MMIO region defined for specified port. + * @wwpn: The world-wide-port-number previously discovered for port. + * + * The provided MMIO region must be mapped prior to call. As part of the + * sequence to configure the WWPN, the port is toggled offline and then back + * online. This toggling action can cause this routine to delay up to a few + * seconds. When configured to use the internal LUN feature of the AFU, a + * failure to come online is overridden. + * + * Return: + * 0 when the WWPN is successfully written and the port comes back online + * -1 when the port fails to go offline or come back up online + */ +static int afu_set_wwpn(struct afu *afu, int port, __be64 __iomem *fc_regs, + u64 wwpn) +{ + int rc = 0; + + set_port_offline(fc_regs); + + if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, + FC_PORT_STATUS_RETRY_CNT)) { + pr_debug("%s: wait on port %d to go offline timed out\n", + __func__, port); + rc = -1; /* but continue on to leave the port back online */ + } + + if (rc == 0) + writeq_be(wwpn, &fc_regs[FC_PNAME / 8]); + + /* Always return success after programming WWPN */ + rc = 0; + + set_port_online(fc_regs); + + if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, + FC_PORT_STATUS_RETRY_CNT)) { + pr_err("%s: wait on port %d to go online timed out\n", + __func__, port); + } + + pr_debug("%s: returning rc=%d\n", __func__, rc); + + return rc; +} + +/** + * afu_link_reset() - resets the specified host FC port + * @afu: AFU associated with the host that owns the specified FC port. + * @port: Port number being configured. + * @fc_regs: Top of MMIO region defined for specified port. + * + * The provided MMIO region must be mapped prior to call. The sequence to + * reset the port involves toggling it offline and then back online. This + * action can cause this routine to delay up to a few seconds. An effort + * is made to maintain link with the device by switching to host to use + * the alternate port exclusively while the reset takes place. + * failure to come online is overridden. + */ +static void afu_link_reset(struct afu *afu, int port, __be64 __iomem *fc_regs) +{ + u64 port_sel; + + /* first switch the AFU to the other links, if any */ + port_sel = readq_be(&afu->afu_map->global.regs.afu_port_sel); + port_sel &= ~(1ULL << port); + writeq_be(port_sel, &afu->afu_map->global.regs.afu_port_sel); + cxlflash_afu_sync(afu, 0, 0, AFU_GSYNC); + + set_port_offline(fc_regs); + if (!wait_port_offline(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, + FC_PORT_STATUS_RETRY_CNT)) + pr_err("%s: wait on port %d to go offline timed out\n", + __func__, port); + + set_port_online(fc_regs); + if (!wait_port_online(fc_regs, FC_PORT_STATUS_RETRY_INTERVAL_US, + FC_PORT_STATUS_RETRY_CNT)) + pr_err("%s: wait on port %d to go online timed out\n", + __func__, port); + + /* switch back to include this port */ + port_sel |= (1ULL << port); + writeq_be(port_sel, &afu->afu_map->global.regs.afu_port_sel); + cxlflash_afu_sync(afu, 0, 0, AFU_GSYNC); + + pr_debug("%s: returning port_sel=%lld\n", __func__, port_sel); +} + +/* + * Asynchronous interrupt information table + */ +static const struct asyc_intr_info ainfo[] = { + {SISL_ASTATUS_FC0_OTHER, "other error", 0, CLR_FC_ERROR | LINK_RESET}, + {SISL_ASTATUS_FC0_LOGO, "target initiated LOGO", 0, 0}, + {SISL_ASTATUS_FC0_CRC_T, "CRC threshold exceeded", 0, LINK_RESET}, + {SISL_ASTATUS_FC0_LOGI_R, "login timed out, retrying", 0, LINK_RESET}, + {SISL_ASTATUS_FC0_LOGI_F, "login failed", 0, CLR_FC_ERROR}, + {SISL_ASTATUS_FC0_LOGI_S, "login succeeded", 0, SCAN_HOST}, + {SISL_ASTATUS_FC0_LINK_DN, "link down", 0, 0}, + {SISL_ASTATUS_FC0_LINK_UP, "link up", 0, SCAN_HOST}, + {SISL_ASTATUS_FC1_OTHER, "other error", 1, CLR_FC_ERROR | LINK_RESET}, + {SISL_ASTATUS_FC1_LOGO, "target initiated LOGO", 1, 0}, + {SISL_ASTATUS_FC1_CRC_T, "CRC threshold exceeded", 1, LINK_RESET}, + {SISL_ASTATUS_FC1_LOGI_R, "login timed out, retrying", 1, 0}, + {SISL_ASTATUS_FC1_LOGI_F, "login failed", 1, CLR_FC_ERROR}, + {SISL_ASTATUS_FC1_LOGI_S, "login succeeded", 1, SCAN_HOST}, + {SISL_ASTATUS_FC1_LINK_DN, "link down", 1, 0}, + {SISL_ASTATUS_FC1_LINK_UP, "link up", 1, SCAN_HOST}, + {0x0, "", 0, 0} /* terminator */ +}; + +/** + * find_ainfo() - locates and returns asynchronous interrupt information + * @status: Status code set by AFU on error. + * + * Return: The located information or NULL when the status code is invalid. + */ +static const struct asyc_intr_info *find_ainfo(u64 status) +{ + const struct asyc_intr_info *info; + + for (info = &ainfo[0]; info->status; info++) + if (info->status == status) + return info; + + return NULL; +} + +/** + * afu_err_intr_init() - clears and initializes the AFU for error interrupts + * @afu: AFU associated with the host. + */ +static void afu_err_intr_init(struct afu *afu) +{ + int i; + u64 reg; + + /* global async interrupts: AFU clears afu_ctrl on context exit + * if async interrupts were sent to that context. This prevents + * the AFU form sending further async interrupts when + * there is + * nobody to receive them. + */ + + /* mask all */ + writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_mask); + /* set LISN# to send and point to master context */ + reg = ((u64) (((afu->ctx_hndl << 8) | SISL_MSI_ASYNC_ERROR)) << 40); + + if (afu->internal_lun) + reg |= 1; /* Bit 63 indicates local lun */ + writeq_be(reg, &afu->afu_map->global.regs.afu_ctrl); + /* clear all */ + writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_clear); + /* unmask bits that are of interest */ + /* note: afu can send an interrupt after this step */ + writeq_be(SISL_ASTATUS_MASK, &afu->afu_map->global.regs.aintr_mask); + /* clear again in case a bit came on after previous clear but before */ + /* unmask */ + writeq_be(-1ULL, &afu->afu_map->global.regs.aintr_clear); + + /* Clear/Set internal lun bits */ + reg = readq_be(&afu->afu_map->global.fc_regs[0][FC_CONFIG2 / 8]); + reg &= SISL_FC_INTERNAL_MASK; + if (afu->internal_lun) + reg |= ((u64)(afu->internal_lun - 1) << SISL_FC_INTERNAL_SHIFT); + writeq_be(reg, &afu->afu_map->global.fc_regs[0][FC_CONFIG2 / 8]); + + /* now clear FC errors */ + for (i = 0; i < NUM_FC_PORTS; i++) { + writeq_be(0xFFFFFFFFU, + &afu->afu_map->global.fc_regs[i][FC_ERROR / 8]); + writeq_be(0, &afu->afu_map->global.fc_regs[i][FC_ERRCAP / 8]); + } + + /* sync interrupts for master's IOARRIN write */ + /* note that unlike asyncs, there can be no pending sync interrupts */ + /* at this time (this is a fresh context and master has not written */ + /* IOARRIN yet), so there is nothing to clear. */ + + /* set LISN#, it is always sent to the context that wrote IOARRIN */ + writeq_be(SISL_MSI_SYNC_ERROR, &afu->host_map->ctx_ctrl); + writeq_be(SISL_ISTATUS_MASK, &afu->host_map->intr_mask); +} + +/** + * cxlflash_sync_err_irq() - interrupt handler for synchronous errors + * @irq: Interrupt number. + * @data: Private data provided at interrupt registration, the AFU. + * + * Return: Always return IRQ_HANDLED. + */ +static irqreturn_t cxlflash_sync_err_irq(int irq, void *data) +{ + struct afu *afu = (struct afu *)data; + u64 reg; + u64 reg_unmasked; + + reg = readq_be(&afu->host_map->intr_status); + reg_unmasked = (reg & SISL_ISTATUS_UNMASK); + + if (reg_unmasked == 0UL) { + pr_err("%s: %llX: spurious interrupt, intr_status %016llX\n", + __func__, (u64)afu, reg); + goto cxlflash_sync_err_irq_exit; + } + + pr_err("%s: %llX: unexpected interrupt, intr_status %016llX\n", + __func__, (u64)afu, reg); + + writeq_be(reg_unmasked, &afu->host_map->intr_clear); + +cxlflash_sync_err_irq_exit: + pr_debug("%s: returning rc=%d\n", __func__, IRQ_HANDLED); + return IRQ_HANDLED; +} + +/** + * cxlflash_rrq_irq() - interrupt handler for read-response queue (normal path) + * @irq: Interrupt number. + * @data: Private data provided at interrupt registration, the AFU. + * + * Return: Always return IRQ_HANDLED. + */ +static irqreturn_t cxlflash_rrq_irq(int irq, void *data) +{ + struct afu *afu = (struct afu *)data; + struct afu_cmd *cmd; + bool toggle = afu->toggle; + u64 entry, + *hrrq_start = afu->hrrq_start, + *hrrq_end = afu->hrrq_end, + *hrrq_curr = afu->hrrq_curr; + + /* Process however many RRQ entries that are ready */ + while (true) { + entry = *hrrq_curr; + + if ((entry & SISL_RESP_HANDLE_T_BIT) != toggle) + break; + + cmd = (struct afu_cmd *)(entry & ~SISL_RESP_HANDLE_T_BIT); + cmd_complete(cmd); + + /* Advance to next entry or wrap and flip the toggle bit */ + if (hrrq_curr < hrrq_end) + hrrq_curr++; + else { + hrrq_curr = hrrq_start; + toggle ^= SISL_RESP_HANDLE_T_BIT; + } + } + + afu->hrrq_curr = hrrq_curr; + afu->toggle = toggle; + + return IRQ_HANDLED; +} + +/** + * cxlflash_async_err_irq() - interrupt handler for asynchronous errors + * @irq: Interrupt number. + * @data: Private data provided at interrupt registration, the AFU. + * + * Return: Always return IRQ_HANDLED. + */ +static irqreturn_t cxlflash_async_err_irq(int irq, void *data) +{ + struct afu *afu = (struct afu *)data; + struct cxlflash_cfg *cfg = afu->parent; + struct device *dev = &cfg->dev->dev; + u64 reg_unmasked; + const struct asyc_intr_info *info; + struct sisl_global_map __iomem *global = &afu->afu_map->global; + u64 reg; + u8 port; + int i; + + reg = readq_be(&global->regs.aintr_status); + reg_unmasked = (reg & SISL_ASTATUS_UNMASK); + + if (reg_unmasked == 0) { + dev_err(dev, "%s: spurious interrupt, aintr_status 0x%016llX\n", + __func__, reg); + goto out; + } + + /* FYI, it is 'okay' to clear AFU status before FC_ERROR */ + writeq_be(reg_unmasked, &global->regs.aintr_clear); + + /* Check each bit that is on */ + for (i = 0; reg_unmasked; i++, reg_unmasked = (reg_unmasked >> 1)) { + info = find_ainfo(1ULL << i); + if (((reg_unmasked & 0x1) == 0) || !info) + continue; + + port = info->port; + + dev_err(dev, "%s: FC Port %d -> %s, fc_status 0x%08llX\n", + __func__, port, info->desc, + readq_be(&global->fc_regs[port][FC_STATUS / 8])); + + /* + * Do link reset first, some OTHER errors will set FC_ERROR + * again if cleared before or w/o a reset + */ + if (info->action & LINK_RESET) { + dev_err(dev, "%s: FC Port %d: resetting link\n", + __func__, port); + cfg->lr_state = LINK_RESET_REQUIRED; + cfg->lr_port = port; + schedule_work(&cfg->work_q); + } + + if (info->action & CLR_FC_ERROR) { + reg = readq_be(&global->fc_regs[port][FC_ERROR / 8]); + + /* + * Since all errors are unmasked, FC_ERROR and FC_ERRCAP + * should be the same and tracing one is sufficient. + */ + + dev_err(dev, "%s: fc %d: clearing fc_error 0x%08llX\n", + __func__, port, reg); + + writeq_be(reg, &global->fc_regs[port][FC_ERROR / 8]); + writeq_be(0, &global->fc_regs[port][FC_ERRCAP / 8]); + } + + if (info->action & SCAN_HOST) { + atomic_inc(&cfg->scan_host_needed); + schedule_work(&cfg->work_q); + } + } + +out: + dev_dbg(dev, "%s: returning IRQ_HANDLED, afu=%p\n", __func__, afu); + return IRQ_HANDLED; +} + +/** + * start_context() - starts the master context + * @cfg: Internal structure associated with the host. + * + * Return: A success or failure value from CXL services. + */ +static int start_context(struct cxlflash_cfg *cfg) +{ + int rc = 0; + + rc = cxl_start_context(cfg->mcctx, + cfg->afu->work.work_element_descriptor, + NULL); + + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * read_vpd() - obtains the WWPNs from VPD + * @cfg: Internal structure associated with the host. + * @wwpn: Array of size NUM_FC_PORTS to pass back WWPNs + * + * Return: 0 on success, -errno on failure + */ +static int read_vpd(struct cxlflash_cfg *cfg, u64 wwpn[]) +{ + struct pci_dev *dev = cfg->parent_dev; + int rc = 0; + int ro_start, ro_size, i, j, k; + ssize_t vpd_size; + char vpd_data[CXLFLASH_VPD_LEN]; + char tmp_buf[WWPN_BUF_LEN] = { 0 }; + char *wwpn_vpd_tags[NUM_FC_PORTS] = { "V5", "V6" }; + + /* Get the VPD data from the device */ + vpd_size = pci_read_vpd(dev, 0, sizeof(vpd_data), vpd_data); + if (unlikely(vpd_size <= 0)) { + dev_err(&dev->dev, "%s: Unable to read VPD (size = %ld)\n", + __func__, vpd_size); + rc = -ENODEV; + goto out; + } + + /* Get the read only section offset */ + ro_start = pci_vpd_find_tag(vpd_data, 0, vpd_size, + PCI_VPD_LRDT_RO_DATA); + if (unlikely(ro_start < 0)) { + dev_err(&dev->dev, "%s: VPD Read-only data not found\n", + __func__); + rc = -ENODEV; + goto out; + } + + /* Get the read only section size, cap when extends beyond read VPD */ + ro_size = pci_vpd_lrdt_size(&vpd_data[ro_start]); + j = ro_size; + i = ro_start + PCI_VPD_LRDT_TAG_SIZE; + if (unlikely((i + j) > vpd_size)) { + pr_debug("%s: Might need to read more VPD (%d > %ld)\n", + __func__, (i + j), vpd_size); + ro_size = vpd_size - i; + } + + /* + * Find the offset of the WWPN tag within the read only + * VPD data and validate the found field (partials are + * no good to us). Convert the ASCII data to an integer + * value. Note that we must copy to a temporary buffer + * because the conversion service requires that the ASCII + * string be terminated. + */ + for (k = 0; k < NUM_FC_PORTS; k++) { + j = ro_size; + i = ro_start + PCI_VPD_LRDT_TAG_SIZE; + + i = pci_vpd_find_info_keyword(vpd_data, i, j, wwpn_vpd_tags[k]); + if (unlikely(i < 0)) { + dev_err(&dev->dev, "%s: Port %d WWPN not found " + "in VPD\n", __func__, k); + rc = -ENODEV; + goto out; + } + + j = pci_vpd_info_field_size(&vpd_data[i]); + i += PCI_VPD_INFO_FLD_HDR_SIZE; + if (unlikely((i + j > vpd_size) || (j != WWPN_LEN))) { + dev_err(&dev->dev, "%s: Port %d WWPN incomplete or " + "VPD corrupt\n", + __func__, k); + rc = -ENODEV; + goto out; + } + + memcpy(tmp_buf, &vpd_data[i], WWPN_LEN); + rc = kstrtoul(tmp_buf, WWPN_LEN, (ulong *)&wwpn[k]); + if (unlikely(rc)) { + dev_err(&dev->dev, "%s: Fail to convert port %d WWPN " + "to integer\n", __func__, k); + rc = -ENODEV; + goto out; + } + } + +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * init_pcr() - initialize the provisioning and control registers + * @cfg: Internal structure associated with the host. + * + * Also sets up fast access to the mapped registers and initializes AFU + * command fields that never change. + */ +static void init_pcr(struct cxlflash_cfg *cfg) +{ + struct afu *afu = cfg->afu; + struct sisl_ctrl_map __iomem *ctrl_map; + int i; + + for (i = 0; i < MAX_CONTEXT; i++) { + ctrl_map = &afu->afu_map->ctrls[i].ctrl; + /* Disrupt any clients that could be running */ + /* e.g. clients that survived a master restart */ + writeq_be(0, &ctrl_map->rht_start); + writeq_be(0, &ctrl_map->rht_cnt_id); + writeq_be(0, &ctrl_map->ctx_cap); + } + + /* Copy frequently used fields into afu */ + afu->ctx_hndl = (u16) cxl_process_element(cfg->mcctx); + afu->host_map = &afu->afu_map->hosts[afu->ctx_hndl].host; + afu->ctrl_map = &afu->afu_map->ctrls[afu->ctx_hndl].ctrl; + + /* Program the Endian Control for the master context */ + writeq_be(SISL_ENDIAN_CTRL, &afu->host_map->endian_ctrl); + + /* Initialize cmd fields that never change */ + for (i = 0; i < CXLFLASH_NUM_CMDS; i++) { + afu->cmd[i].rcb.ctx_id = afu->ctx_hndl; + afu->cmd[i].rcb.msi = SISL_MSI_RRQ_UPDATED; + afu->cmd[i].rcb.rrq = 0x0; + } +} + +/** + * init_global() - initialize AFU global registers + * @cfg: Internal structure associated with the host. + */ +static int init_global(struct cxlflash_cfg *cfg) +{ + struct afu *afu = cfg->afu; + struct device *dev = &cfg->dev->dev; + u64 wwpn[NUM_FC_PORTS]; /* wwpn of AFU ports */ + int i = 0, num_ports = 0; + int rc = 0; + u64 reg; + + rc = read_vpd(cfg, &wwpn[0]); + if (rc) { + dev_err(dev, "%s: could not read vpd rc=%d\n", __func__, rc); + goto out; + } + + pr_debug("%s: wwpn0=0x%llX wwpn1=0x%llX\n", __func__, wwpn[0], wwpn[1]); + + /* Set up RRQ in AFU for master issued cmds */ + writeq_be((u64) afu->hrrq_start, &afu->host_map->rrq_start); + writeq_be((u64) afu->hrrq_end, &afu->host_map->rrq_end); + + /* AFU configuration */ + reg = readq_be(&afu->afu_map->global.regs.afu_config); + reg |= SISL_AFUCONF_AR_ALL|SISL_AFUCONF_ENDIAN; + /* enable all auto retry options and control endianness */ + /* leave others at default: */ + /* CTX_CAP write protected, mbox_r does not clear on read and */ + /* checker on if dual afu */ + writeq_be(reg, &afu->afu_map->global.regs.afu_config); + + /* Global port select: select either port */ + if (afu->internal_lun) { + /* Only use port 0 */ + writeq_be(PORT0, &afu->afu_map->global.regs.afu_port_sel); + num_ports = NUM_FC_PORTS - 1; + } else { + writeq_be(BOTH_PORTS, &afu->afu_map->global.regs.afu_port_sel); + num_ports = NUM_FC_PORTS; + } + + for (i = 0; i < num_ports; i++) { + /* Unmask all errors (but they are still masked at AFU) */ + writeq_be(0, &afu->afu_map->global.fc_regs[i][FC_ERRMSK / 8]); + /* Clear CRC error cnt & set a threshold */ + (void)readq_be(&afu->afu_map->global. + fc_regs[i][FC_CNT_CRCERR / 8]); + writeq_be(MC_CRC_THRESH, &afu->afu_map->global.fc_regs[i] + [FC_CRC_THRESH / 8]); + + /* Set WWPNs. If already programmed, wwpn[i] is 0 */ + if (wwpn[i] != 0 && + afu_set_wwpn(afu, i, + &afu->afu_map->global.fc_regs[i][0], + wwpn[i])) { + dev_err(dev, "%s: failed to set WWPN on port %d\n", + __func__, i); + rc = -EIO; + goto out; + } + /* Programming WWPN back to back causes additional + * offline/online transitions and a PLOGI + */ + msleep(100); + } + + /* Set up master's own CTX_CAP to allow real mode, host translation */ + /* tables, afu cmds and read/write GSCSI cmds. */ + /* First, unlock ctx_cap write by reading mbox */ + (void)readq_be(&afu->ctrl_map->mbox_r); /* unlock ctx_cap */ + writeq_be((SISL_CTX_CAP_REAL_MODE | SISL_CTX_CAP_HOST_XLATE | + SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD | + SISL_CTX_CAP_AFU_CMD | SISL_CTX_CAP_GSCSI_CMD), + &afu->ctrl_map->ctx_cap); + /* Initialize heartbeat */ + afu->hb = readq_be(&afu->afu_map->global.regs.afu_hb); + +out: + return rc; +} + +/** + * start_afu() - initializes and starts the AFU + * @cfg: Internal structure associated with the host. + */ +static int start_afu(struct cxlflash_cfg *cfg) +{ + struct afu *afu = cfg->afu; + struct afu_cmd *cmd; + + int i = 0; + int rc = 0; + + for (i = 0; i < CXLFLASH_NUM_CMDS; i++) { + cmd = &afu->cmd[i]; + + init_completion(&cmd->cevent); + spin_lock_init(&cmd->slock); + cmd->parent = afu; + } + + init_pcr(cfg); + + /* After an AFU reset, RRQ entries are stale, clear them */ + memset(&afu->rrq_entry, 0, sizeof(afu->rrq_entry)); + + /* Initialize RRQ pointers */ + afu->hrrq_start = &afu->rrq_entry[0]; + afu->hrrq_end = &afu->rrq_entry[NUM_RRQ_ENTRY - 1]; + afu->hrrq_curr = afu->hrrq_start; + afu->toggle = 1; + + rc = init_global(cfg); + + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * init_mc() - create and register as the master context + * @cfg: Internal structure associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int init_mc(struct cxlflash_cfg *cfg) +{ + struct cxl_context *ctx; + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + int rc = 0; + enum undo_level level; + + ctx = cxl_get_context(cfg->dev); + if (unlikely(!ctx)) + return -ENOMEM; + cfg->mcctx = ctx; + + /* Set it up as a master with the CXL */ + cxl_set_master(ctx); + + /* During initialization reset the AFU to start from a clean slate */ + rc = cxl_afu_reset(cfg->mcctx); + if (unlikely(rc)) { + dev_err(dev, "%s: initial AFU reset failed rc=%d\n", + __func__, rc); + level = RELEASE_CONTEXT; + goto out; + } + + rc = cxl_allocate_afu_irqs(ctx, 3); + if (unlikely(rc)) { + dev_err(dev, "%s: call to allocate_afu_irqs failed rc=%d!\n", + __func__, rc); + level = RELEASE_CONTEXT; + goto out; + } + + rc = cxl_map_afu_irq(ctx, 1, cxlflash_sync_err_irq, afu, + "SISL_MSI_SYNC_ERROR"); + if (unlikely(rc <= 0)) { + dev_err(dev, "%s: IRQ 1 (SISL_MSI_SYNC_ERROR) map failed!\n", + __func__); + level = FREE_IRQ; + goto out; + } + + rc = cxl_map_afu_irq(ctx, 2, cxlflash_rrq_irq, afu, + "SISL_MSI_RRQ_UPDATED"); + if (unlikely(rc <= 0)) { + dev_err(dev, "%s: IRQ 2 (SISL_MSI_RRQ_UPDATED) map failed!\n", + __func__); + level = UNMAP_ONE; + goto out; + } + + rc = cxl_map_afu_irq(ctx, 3, cxlflash_async_err_irq, afu, + "SISL_MSI_ASYNC_ERROR"); + if (unlikely(rc <= 0)) { + dev_err(dev, "%s: IRQ 3 (SISL_MSI_ASYNC_ERROR) map failed!\n", + __func__); + level = UNMAP_TWO; + goto out; + } + + rc = 0; + + /* This performs the equivalent of the CXL_IOCTL_START_WORK. + * The CXL_IOCTL_GET_PROCESS_ELEMENT is implicit in the process + * element (pe) that is embedded in the context (ctx) + */ + rc = start_context(cfg); + if (unlikely(rc)) { + dev_err(dev, "%s: start context failed rc=%d\n", __func__, rc); + level = UNMAP_THREE; + goto out; + } +ret: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +out: + term_mc(cfg, level); + goto ret; +} + +/** + * init_afu() - setup as master context and start AFU + * @cfg: Internal structure associated with the host. + * + * This routine is a higher level of control for configuring the + * AFU on probe and reset paths. + * + * Return: 0 on success, -errno on failure + */ +static int init_afu(struct cxlflash_cfg *cfg) +{ + u64 reg; + int rc = 0; + struct afu *afu = cfg->afu; + struct device *dev = &cfg->dev->dev; + + cxl_perst_reloads_same_image(cfg->cxl_afu, true); + + rc = init_mc(cfg); + if (rc) { + dev_err(dev, "%s: call to init_mc failed, rc=%d!\n", + __func__, rc); + goto out; + } + + /* Map the entire MMIO space of the AFU */ + afu->afu_map = cxl_psa_map(cfg->mcctx); + if (!afu->afu_map) { + dev_err(dev, "%s: call to cxl_psa_map failed!\n", __func__); + rc = -ENOMEM; + goto err1; + } + + /* No byte reverse on reading afu_version or string will be backwards */ + reg = readq(&afu->afu_map->global.regs.afu_version); + memcpy(afu->version, ®, sizeof(reg)); + afu->interface_version = + readq_be(&afu->afu_map->global.regs.interface_version); + if ((afu->interface_version + 1) == 0) { + pr_err("Back level AFU, please upgrade. AFU version %s " + "interface version 0x%llx\n", afu->version, + afu->interface_version); + rc = -EINVAL; + goto err2; + } + + pr_debug("%s: afu version %s, interface version 0x%llX\n", __func__, + afu->version, afu->interface_version); + + rc = start_afu(cfg); + if (rc) { + dev_err(dev, "%s: call to start_afu failed, rc=%d!\n", + __func__, rc); + goto err2; + } + + afu_err_intr_init(cfg->afu); + atomic64_set(&afu->room, readq_be(&afu->host_map->cmd_room)); + + /* Restore the LUN mappings */ + cxlflash_restore_luntable(cfg); +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; + +err2: + cxl_psa_unmap((void __iomem *)afu->afu_map); + afu->afu_map = NULL; +err1: + term_mc(cfg, UNDO_START); + goto out; +} + +/** + * cxlflash_afu_sync() - builds and sends an AFU sync command + * @afu: AFU associated with the host. + * @ctx_hndl_u: Identifies context requesting sync. + * @res_hndl_u: Identifies resource requesting sync. + * @mode: Type of sync to issue (lightweight, heavyweight, global). + * + * The AFU can only take 1 sync command at a time. This routine enforces this + * limitation by using a mutex to provide exclusive access to the AFU during + * the sync. This design point requires calling threads to not be on interrupt + * context due to the possibility of sleeping during concurrent sync operations. + * + * AFU sync operations are only necessary and allowed when the device is + * operating normally. When not operating normally, sync requests can occur as + * part of cleaning up resources associated with an adapter prior to removal. + * In this scenario, these requests are simply ignored (safe due to the AFU + * going away). + * + * Return: + * 0 on success + * -1 on failure + */ +int cxlflash_afu_sync(struct afu *afu, ctx_hndl_t ctx_hndl_u, + res_hndl_t res_hndl_u, u8 mode) +{ + struct cxlflash_cfg *cfg = afu->parent; + struct device *dev = &cfg->dev->dev; + struct afu_cmd *cmd = NULL; + int rc = 0; + int retry_cnt = 0; + static DEFINE_MUTEX(sync_active); + + if (cfg->state != STATE_NORMAL) { + pr_debug("%s: Sync not required! (%u)\n", __func__, cfg->state); + return 0; + } + + mutex_lock(&sync_active); +retry: + cmd = cmd_checkout(afu); + if (unlikely(!cmd)) { + retry_cnt++; + udelay(1000 * retry_cnt); + if (retry_cnt < MC_RETRY_CNT) + goto retry; + dev_err(dev, "%s: could not get a free command\n", __func__); + rc = -1; + goto out; + } + + pr_debug("%s: afu=%p cmd=%p %d\n", __func__, afu, cmd, ctx_hndl_u); + + memset(cmd->rcb.cdb, 0, sizeof(cmd->rcb.cdb)); + + cmd->rcb.req_flags = SISL_REQ_FLAGS_AFU_CMD; + cmd->rcb.port_sel = 0x0; /* NA */ + cmd->rcb.lun_id = 0x0; /* NA */ + cmd->rcb.data_len = 0x0; + cmd->rcb.data_ea = 0x0; + cmd->rcb.timeout = MC_AFU_SYNC_TIMEOUT; + + cmd->rcb.cdb[0] = 0xC0; /* AFU Sync */ + cmd->rcb.cdb[1] = mode; + + /* The cdb is aligned, no unaligned accessors required */ + *((__be16 *)&cmd->rcb.cdb[2]) = cpu_to_be16(ctx_hndl_u); + *((__be32 *)&cmd->rcb.cdb[4]) = cpu_to_be32(res_hndl_u); + + rc = send_cmd(afu, cmd); + if (unlikely(rc)) + goto out; + + wait_resp(afu, cmd); + + /* Set on timeout */ + if (unlikely((cmd->sa.ioasc != 0) || + (cmd->sa.host_use_b[0] & B_ERROR))) + rc = -1; +out: + mutex_unlock(&sync_active); + if (cmd) + cmd_checkin(cmd); + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * afu_reset() - resets the AFU + * @cfg: Internal structure associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int afu_reset(struct cxlflash_cfg *cfg) +{ + int rc = 0; + /* Stop the context before the reset. Since the context is + * no longer available restart it after the reset is complete + */ + + term_afu(cfg); + + rc = init_afu(cfg); + + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * cxlflash_eh_device_reset_handler() - reset a single LUN + * @scp: SCSI command to send. + * + * Return: + * SUCCESS as defined in scsi/scsi.h + * FAILED as defined in scsi/scsi.h + */ +static int cxlflash_eh_device_reset_handler(struct scsi_cmnd *scp) +{ + int rc = SUCCESS; + struct Scsi_Host *host = scp->device->host; + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)host->hostdata; + struct afu *afu = cfg->afu; + int rcr = 0; + + pr_debug("%s: (scp=%p) %d/%d/%d/%llu " + "cdb=(%08X-%08X-%08X-%08X)\n", __func__, scp, + host->host_no, scp->device->channel, + scp->device->id, scp->device->lun, + get_unaligned_be32(&((u32 *)scp->cmnd)[0]), + get_unaligned_be32(&((u32 *)scp->cmnd)[1]), + get_unaligned_be32(&((u32 *)scp->cmnd)[2]), + get_unaligned_be32(&((u32 *)scp->cmnd)[3])); + +retry: + switch (cfg->state) { + case STATE_NORMAL: + rcr = send_tmf(afu, scp, TMF_LUN_RESET); + if (unlikely(rcr)) + rc = FAILED; + break; + case STATE_RESET: + wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); + goto retry; + default: + rc = FAILED; + break; + } + + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * cxlflash_eh_host_reset_handler() - reset the host adapter + * @scp: SCSI command from stack identifying host. + * + * Return: + * SUCCESS as defined in scsi/scsi.h + * FAILED as defined in scsi/scsi.h + */ +static int cxlflash_eh_host_reset_handler(struct scsi_cmnd *scp) +{ + int rc = SUCCESS; + int rcr = 0; + struct Scsi_Host *host = scp->device->host; + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)host->hostdata; + + pr_debug("%s: (scp=%p) %d/%d/%d/%llu " + "cdb=(%08X-%08X-%08X-%08X)\n", __func__, scp, + host->host_no, scp->device->channel, + scp->device->id, scp->device->lun, + get_unaligned_be32(&((u32 *)scp->cmnd)[0]), + get_unaligned_be32(&((u32 *)scp->cmnd)[1]), + get_unaligned_be32(&((u32 *)scp->cmnd)[2]), + get_unaligned_be32(&((u32 *)scp->cmnd)[3])); + + switch (cfg->state) { + case STATE_NORMAL: + cfg->state = STATE_RESET; + cxlflash_mark_contexts_error(cfg); + rcr = afu_reset(cfg); + if (rcr) { + rc = FAILED; + cfg->state = STATE_FAILTERM; + } else + cfg->state = STATE_NORMAL; + wake_up_all(&cfg->reset_waitq); + break; + case STATE_RESET: + wait_event(cfg->reset_waitq, cfg->state != STATE_RESET); + if (cfg->state == STATE_NORMAL) + break; + /* fall through */ + default: + rc = FAILED; + break; + } + + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * cxlflash_change_queue_depth() - change the queue depth for the device + * @sdev: SCSI device destined for queue depth change. + * @qdepth: Requested queue depth value to set. + * + * The requested queue depth is capped to the maximum supported value. + * + * Return: The actual queue depth set. + */ +static int cxlflash_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + + if (qdepth > CXLFLASH_MAX_CMDS_PER_LUN) + qdepth = CXLFLASH_MAX_CMDS_PER_LUN; + + scsi_change_queue_depth(sdev, qdepth); + return sdev->queue_depth; +} + +/** + * cxlflash_show_port_status() - queries and presents the current port status + * @port: Desired port for status reporting. + * @afu: AFU owning the specified port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t cxlflash_show_port_status(u32 port, struct afu *afu, char *buf) +{ + char *disp_status; + u64 status; + __be64 __iomem *fc_regs; + + if (port >= NUM_FC_PORTS) + return 0; + + fc_regs = &afu->afu_map->global.fc_regs[port][0]; + status = readq_be(&fc_regs[FC_MTIP_STATUS / 8]); + status &= FC_MTIP_STATUS_MASK; + + if (status == FC_MTIP_STATUS_ONLINE) + disp_status = "online"; + else if (status == FC_MTIP_STATUS_OFFLINE) + disp_status = "offline"; + else + disp_status = "unknown"; + + return scnprintf(buf, PAGE_SIZE, "%s\n", disp_status); +} + +/** + * port0_show() - queries and presents the current status of port 0 + * @dev: Generic device associated with the host owning the port. + * @attr: Device attribute representing the port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t port0_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + + return cxlflash_show_port_status(0, afu, buf); +} + +/** + * port1_show() - queries and presents the current status of port 1 + * @dev: Generic device associated with the host owning the port. + * @attr: Device attribute representing the port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t port1_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + + return cxlflash_show_port_status(1, afu, buf); +} + +/** + * lun_mode_show() - presents the current LUN mode of the host + * @dev: Generic device associated with the host. + * @attr: Device attribute representing the LUN mode. + * @buf: Buffer of length PAGE_SIZE to report back the LUN mode in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t lun_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + + return scnprintf(buf, PAGE_SIZE, "%u\n", afu->internal_lun); +} + +/** + * lun_mode_store() - sets the LUN mode of the host + * @dev: Generic device associated with the host. + * @attr: Device attribute representing the LUN mode. + * @buf: Buffer of length PAGE_SIZE containing the LUN mode in ASCII. + * @count: Length of data resizing in @buf. + * + * The CXL Flash AFU supports a dummy LUN mode where the external + * links and storage are not required. Space on the FPGA is used + * to create 1 or 2 small LUNs which are presented to the system + * as if they were a normal storage device. This feature is useful + * during development and also provides manufacturing with a way + * to test the AFU without an actual device. + * + * 0 = external LUN[s] (default) + * 1 = internal LUN (1 x 64K, 512B blocks, id 0) + * 2 = internal LUN (1 x 64K, 4K blocks, id 0) + * 3 = internal LUN (2 x 32K, 512B blocks, ids 0,1) + * 4 = internal LUN (2 x 32K, 4K blocks, ids 0,1) + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t lun_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + int rc; + u32 lun_mode; + + rc = kstrtouint(buf, 10, &lun_mode); + if (!rc && (lun_mode < 5) && (lun_mode != afu->internal_lun)) { + afu->internal_lun = lun_mode; + afu_reset(cfg); + scsi_scan_host(cfg->host); + } + + return count; +} + +/** + * ioctl_version_show() - presents the current ioctl version of the host + * @dev: Generic device associated with the host. + * @attr: Device attribute representing the ioctl version. + * @buf: Buffer of length PAGE_SIZE to report back the ioctl version. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t ioctl_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u\n", DK_CXLFLASH_VERSION_0); +} + +/** + * cxlflash_show_port_lun_table() - queries and presents the port LUN table + * @port: Desired port for status reporting. + * @afu: AFU owning the specified port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t cxlflash_show_port_lun_table(u32 port, + struct afu *afu, + char *buf) +{ + int i; + ssize_t bytes = 0; + __be64 __iomem *fc_port; + + if (port >= NUM_FC_PORTS) + return 0; + + fc_port = &afu->afu_map->global.fc_port[port][0]; + + for (i = 0; i < CXLFLASH_NUM_VLUNS; i++) + bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes, + "%03d: %016llX\n", i, readq_be(&fc_port[i])); + return bytes; +} + +/** + * port0_lun_table_show() - presents the current LUN table of port 0 + * @dev: Generic device associated with the host owning the port. + * @attr: Device attribute representing the port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t port0_lun_table_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + + return cxlflash_show_port_lun_table(0, afu, buf); +} + +/** + * port1_lun_table_show() - presents the current LUN table of port 1 + * @dev: Generic device associated with the host owning the port. + * @attr: Device attribute representing the port. + * @buf: Buffer of length PAGE_SIZE to report back port status in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t port1_lun_table_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)shost->hostdata; + struct afu *afu = cfg->afu; + + return cxlflash_show_port_lun_table(1, afu, buf); +} + +/** + * mode_show() - presents the current mode of the device + * @dev: Generic device associated with the device. + * @attr: Device attribute representing the device mode. + * @buf: Buffer of length PAGE_SIZE to report back the dev mode in ASCII. + * + * Return: The size of the ASCII string returned in @buf. + */ +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + sdev->hostdata ? "superpipe" : "legacy"); +} + +/* + * Host attributes + */ +static DEVICE_ATTR_RO(port0); +static DEVICE_ATTR_RO(port1); +static DEVICE_ATTR_RW(lun_mode); +static DEVICE_ATTR_RO(ioctl_version); +static DEVICE_ATTR_RO(port0_lun_table); +static DEVICE_ATTR_RO(port1_lun_table); + +static struct device_attribute *cxlflash_host_attrs[] = { + &dev_attr_port0, + &dev_attr_port1, + &dev_attr_lun_mode, + &dev_attr_ioctl_version, + &dev_attr_port0_lun_table, + &dev_attr_port1_lun_table, + NULL +}; + +/* + * Device attributes + */ +static DEVICE_ATTR_RO(mode); + +static struct device_attribute *cxlflash_dev_attrs[] = { + &dev_attr_mode, + NULL +}; + +/* + * Host template + */ +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = CXLFLASH_ADAPTER_NAME, + .info = cxlflash_driver_info, + .ioctl = cxlflash_ioctl, + .proc_name = CXLFLASH_NAME, + .queuecommand = cxlflash_queuecommand, + .eh_device_reset_handler = cxlflash_eh_device_reset_handler, + .eh_host_reset_handler = cxlflash_eh_host_reset_handler, + .change_queue_depth = cxlflash_change_queue_depth, + .cmd_per_lun = 16, + .can_queue = CXLFLASH_MAX_CMDS, + .this_id = -1, + .sg_tablesize = SG_NONE, /* No scatter gather support */ + .max_sectors = CXLFLASH_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = cxlflash_host_attrs, + .sdev_attrs = cxlflash_dev_attrs, +}; + +/* + * Device dependent values + */ +static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS }; + +/* + * PCI device binding table + */ +static struct pci_device_id cxlflash_pci_table[] = { + {PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CORSA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&dev_corsa_vals}, + {} +}; + +MODULE_DEVICE_TABLE(pci, cxlflash_pci_table); + +/** + * cxlflash_worker_thread() - work thread handler for the AFU + * @work: Work structure contained within cxlflash associated with host. + * + * Handles the following events: + * - Link reset which cannot be performed on interrupt context due to + * blocking up to a few seconds + * - Read AFU command room + * - Rescan the host + */ +static void cxlflash_worker_thread(struct work_struct *work) +{ + struct cxlflash_cfg *cfg = container_of(work, struct cxlflash_cfg, + work_q); + struct afu *afu = cfg->afu; + struct device *dev = &cfg->dev->dev; + int port; + ulong lock_flags; + + /* Avoid MMIO if the device has failed */ + + if (cfg->state != STATE_NORMAL) + return; + + spin_lock_irqsave(cfg->host->host_lock, lock_flags); + + if (cfg->lr_state == LINK_RESET_REQUIRED) { + port = cfg->lr_port; + if (port < 0) + dev_err(dev, "%s: invalid port index %d\n", + __func__, port); + else { + spin_unlock_irqrestore(cfg->host->host_lock, + lock_flags); + + /* The reset can block... */ + afu_link_reset(afu, port, + &afu->afu_map->global.fc_regs[port][0]); + spin_lock_irqsave(cfg->host->host_lock, lock_flags); + } + + cfg->lr_state = LINK_RESET_COMPLETE; + } + + if (afu->read_room) { + atomic64_set(&afu->room, readq_be(&afu->host_map->cmd_room)); + afu->read_room = false; + } + + spin_unlock_irqrestore(cfg->host->host_lock, lock_flags); + + if (atomic_dec_if_positive(&cfg->scan_host_needed) >= 0) + scsi_scan_host(cfg->host); +} + +/** + * cxlflash_probe() - PCI entry point to add host + * @pdev: PCI device associated with the host. + * @dev_id: PCI device id associated with device. + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct Scsi_Host *host; + struct cxlflash_cfg *cfg = NULL; + struct device *phys_dev; + struct dev_dependent_vals *ddv; + int rc = 0; + + dev_dbg(&pdev->dev, "%s: Found CXLFLASH with IRQ: %d\n", + __func__, pdev->irq); + + ddv = (struct dev_dependent_vals *)dev_id->driver_data; + driver_template.max_sectors = ddv->max_sectors; + + host = scsi_host_alloc(&driver_template, sizeof(struct cxlflash_cfg)); + if (!host) { + dev_err(&pdev->dev, "%s: call to scsi_host_alloc failed!\n", + __func__); + rc = -ENOMEM; + goto out; + } + + host->max_id = CXLFLASH_MAX_NUM_TARGETS_PER_BUS; + host->max_lun = CXLFLASH_MAX_NUM_LUNS_PER_TARGET; + host->max_channel = NUM_FC_PORTS - 1; + host->unique_id = host->host_no; + host->max_cmd_len = CXLFLASH_MAX_CDB_LEN; + + cfg = (struct cxlflash_cfg *)host->hostdata; + cfg->host = host; + rc = alloc_mem(cfg); + if (rc) { + dev_err(&pdev->dev, "%s: call to alloc_mem failed!\n", + __func__); + rc = -ENOMEM; + scsi_host_put(cfg->host); + goto out; + } + + cfg->init_state = INIT_STATE_NONE; + cfg->dev = pdev; + cfg->cxl_fops = cxlflash_cxl_fops; + + /* + * The promoted LUNs move to the top of the LUN table. The rest stay + * on the bottom half. The bottom half grows from the end + * (index = 255), whereas the top half grows from the beginning + * (index = 0). + */ + cfg->promote_lun_index = 0; + cfg->last_lun_index[0] = CXLFLASH_NUM_VLUNS/2 - 1; + cfg->last_lun_index[1] = CXLFLASH_NUM_VLUNS/2 - 1; + + cfg->dev_id = (struct pci_device_id *)dev_id; + + init_waitqueue_head(&cfg->tmf_waitq); + init_waitqueue_head(&cfg->reset_waitq); + + INIT_WORK(&cfg->work_q, cxlflash_worker_thread); + cfg->lr_state = LINK_RESET_INVALID; + cfg->lr_port = -1; + spin_lock_init(&cfg->tmf_slock); + mutex_init(&cfg->ctx_tbl_list_mutex); + mutex_init(&cfg->ctx_recovery_mutex); + init_rwsem(&cfg->ioctl_rwsem); + INIT_LIST_HEAD(&cfg->ctx_err_recovery); + INIT_LIST_HEAD(&cfg->lluns); + + pci_set_drvdata(pdev, cfg); + + /* + * Use the special service provided to look up the physical + * PCI device, since we are called on the probe of the virtual + * PCI host bus (vphb) + */ + phys_dev = cxl_get_phys_dev(pdev); + if (!dev_is_pci(phys_dev)) { + dev_err(&pdev->dev, "%s: not a pci dev\n", __func__); + rc = -ENODEV; + goto out_remove; + } + cfg->parent_dev = to_pci_dev(phys_dev); + + cfg->cxl_afu = cxl_pci_to_afu(pdev); + + rc = init_pci(cfg); + if (rc) { + dev_err(&pdev->dev, "%s: call to init_pci " + "failed rc=%d!\n", __func__, rc); + goto out_remove; + } + cfg->init_state = INIT_STATE_PCI; + + rc = init_afu(cfg); + if (rc) { + dev_err(&pdev->dev, "%s: call to init_afu " + "failed rc=%d!\n", __func__, rc); + goto out_remove; + } + cfg->init_state = INIT_STATE_AFU; + + rc = init_scsi(cfg); + if (rc) { + dev_err(&pdev->dev, "%s: call to init_scsi " + "failed rc=%d!\n", __func__, rc); + goto out_remove; + } + cfg->init_state = INIT_STATE_SCSI; + +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; + +out_remove: + cxlflash_remove(pdev); + goto out; +} + +/** + * drain_ioctls() - wait until all currently executing ioctls have completed + * @cfg: Internal structure associated with the host. + * + * Obtain write access to read/write semaphore that wraps ioctl + * handling to 'drain' ioctls currently executing. + */ +static void drain_ioctls(struct cxlflash_cfg *cfg) +{ + down_write(&cfg->ioctl_rwsem); + up_write(&cfg->ioctl_rwsem); +} + +/** + * cxlflash_pci_error_detected() - called when a PCI error is detected + * @pdev: PCI device struct. + * @state: PCI channel state. + * + * Return: PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t cxlflash_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + int rc = 0; + struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); + struct device *dev = &cfg->dev->dev; + + dev_dbg(dev, "%s: pdev=%p state=%u\n", __func__, pdev, state); + + switch (state) { + case pci_channel_io_frozen: + cfg->state = STATE_RESET; + scsi_block_requests(cfg->host); + drain_ioctls(cfg); + rc = cxlflash_mark_contexts_error(cfg); + if (unlikely(rc)) + dev_err(dev, "%s: Failed to mark user contexts!(%d)\n", + __func__, rc); + term_mc(cfg, UNDO_START); + stop_afu(cfg); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + cfg->state = STATE_FAILTERM; + wake_up_all(&cfg->reset_waitq); + scsi_unblock_requests(cfg->host); + return PCI_ERS_RESULT_DISCONNECT; + default: + break; + } + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * cxlflash_pci_slot_reset() - called when PCI slot has been reset + * @pdev: PCI device struct. + * + * This routine is called by the pci error recovery code after the PCI + * slot has been reset, just before we should resume normal operations. + * + * Return: PCI_ERS_RESULT_RECOVERED or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t cxlflash_pci_slot_reset(struct pci_dev *pdev) +{ + int rc = 0; + struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); + struct device *dev = &cfg->dev->dev; + + dev_dbg(dev, "%s: pdev=%p\n", __func__, pdev); + + rc = init_afu(cfg); + if (unlikely(rc)) { + dev_err(dev, "%s: EEH recovery failed! (%d)\n", __func__, rc); + return PCI_ERS_RESULT_DISCONNECT; + } + + return PCI_ERS_RESULT_RECOVERED; +} + +/** + * cxlflash_pci_resume() - called when normal operation can resume + * @pdev: PCI device struct + */ +static void cxlflash_pci_resume(struct pci_dev *pdev) +{ + struct cxlflash_cfg *cfg = pci_get_drvdata(pdev); + struct device *dev = &cfg->dev->dev; + + dev_dbg(dev, "%s: pdev=%p\n", __func__, pdev); + + cfg->state = STATE_NORMAL; + wake_up_all(&cfg->reset_waitq); + scsi_unblock_requests(cfg->host); +} + +static const struct pci_error_handlers cxlflash_err_handler = { + .error_detected = cxlflash_pci_error_detected, + .slot_reset = cxlflash_pci_slot_reset, + .resume = cxlflash_pci_resume, +}; + +/* + * PCI device structure + */ +static struct pci_driver cxlflash_driver = { + .name = CXLFLASH_NAME, + .id_table = cxlflash_pci_table, + .probe = cxlflash_probe, + .remove = cxlflash_remove, + .err_handler = &cxlflash_err_handler, +}; + +/** + * init_cxlflash() - module entry point + * + * Return: 0 on success, -errno on failure + */ +static int __init init_cxlflash(void) +{ + pr_info("%s: IBM Power CXL Flash Adapter: %s\n", + __func__, CXLFLASH_DRIVER_DATE); + + cxlflash_list_init(); + + return pci_register_driver(&cxlflash_driver); +} + +/** + * exit_cxlflash() - module exit point + */ +static void __exit exit_cxlflash(void) +{ + cxlflash_term_global_luns(); + cxlflash_free_errpage(); + + pci_unregister_driver(&cxlflash_driver); +} + +module_init(init_cxlflash); +module_exit(exit_cxlflash); diff --git a/kernel/drivers/scsi/cxlflash/main.h b/kernel/drivers/scsi/cxlflash/main.h new file mode 100644 index 000000000..60324566c --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/main.h @@ -0,0 +1,109 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _CXLFLASH_MAIN_H +#define _CXLFLASH_MAIN_H + +#include +#include +#include +#include + +#define CXLFLASH_NAME "cxlflash" +#define CXLFLASH_ADAPTER_NAME "IBM POWER CXL Flash Adapter" +#define CXLFLASH_DRIVER_DATE "(August 13, 2015)" + +#define PCI_DEVICE_ID_IBM_CORSA 0x04F0 +#define CXLFLASH_SUBS_DEV_ID 0x04F0 + +/* Since there is only one target, make it 0 */ +#define CXLFLASH_TARGET 0 +#define CXLFLASH_MAX_CDB_LEN 16 + +/* Really only one target per bus since the Texan is directly attached */ +#define CXLFLASH_MAX_NUM_TARGETS_PER_BUS 1 +#define CXLFLASH_MAX_NUM_LUNS_PER_TARGET 65536 + +#define CXLFLASH_PCI_ERROR_RECOVERY_TIMEOUT (120 * HZ) + +#define NUM_FC_PORTS CXLFLASH_NUM_FC_PORTS /* ports per AFU */ + +/* FC defines */ +#define FC_MTIP_CMDCONFIG 0x010 +#define FC_MTIP_STATUS 0x018 + +#define FC_PNAME 0x300 +#define FC_CONFIG 0x320 +#define FC_CONFIG2 0x328 +#define FC_STATUS 0x330 +#define FC_ERROR 0x380 +#define FC_ERRCAP 0x388 +#define FC_ERRMSK 0x390 +#define FC_CNT_CRCERR 0x538 +#define FC_CRC_THRESH 0x580 + +#define FC_MTIP_CMDCONFIG_ONLINE 0x20ULL +#define FC_MTIP_CMDCONFIG_OFFLINE 0x40ULL + +#define FC_MTIP_STATUS_MASK 0x30ULL +#define FC_MTIP_STATUS_ONLINE 0x20ULL +#define FC_MTIP_STATUS_OFFLINE 0x10ULL + +/* TIMEOUT and RETRY definitions */ + +/* AFU command timeout values */ +#define MC_AFU_SYNC_TIMEOUT 5 /* 5 secs */ + +/* AFU command room retry limit */ +#define MC_ROOM_RETRY_CNT 10 + +/* FC CRC clear periodic timer */ +#define MC_CRC_THRESH 100 /* threshold in 5 mins */ + +#define FC_PORT_STATUS_RETRY_CNT 100 /* 100 100ms retries = 10 seconds */ +#define FC_PORT_STATUS_RETRY_INTERVAL_US 100000 /* microseconds */ + +/* VPD defines */ +#define CXLFLASH_VPD_LEN 256 +#define WWPN_LEN 16 +#define WWPN_BUF_LEN (WWPN_LEN + 1) + +enum undo_level { + RELEASE_CONTEXT = 0, + FREE_IRQ, + UNMAP_ONE, + UNMAP_TWO, + UNMAP_THREE, + UNDO_START +}; + +struct dev_dependent_vals { + u64 max_sectors; +}; + +struct asyc_intr_info { + u64 status; + char *desc; + u8 port; + u8 action; +#define CLR_FC_ERROR 0x01 +#define LINK_RESET 0x02 +#define SCAN_HOST 0x04 +}; + +#ifndef CONFIG_CXL_EEH +#define cxl_perst_reloads_same_image(_a, _b) do { } while (0) +#endif + +#endif /* _CXLFLASH_MAIN_H */ diff --git a/kernel/drivers/scsi/cxlflash/sislite.h b/kernel/drivers/scsi/cxlflash/sislite.h new file mode 100644 index 000000000..0b3366f5e --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/sislite.h @@ -0,0 +1,472 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _SISLITE_H +#define _SISLITE_H + +#include + +typedef u16 ctx_hndl_t; +typedef u32 res_hndl_t; + +#define SIZE_4K 4096 +#define SIZE_64K 65536 + +/* + * IOARCB: 64 bytes, min 16 byte alignment required, host native endianness + * except for SCSI CDB which remains big endian per SCSI standards. + */ +struct sisl_ioarcb { + u16 ctx_id; /* ctx_hndl_t */ + u16 req_flags; +#define SISL_REQ_FLAGS_RES_HNDL 0x8000U /* bit 0 (MSB) */ +#define SISL_REQ_FLAGS_PORT_LUN_ID 0x0000U + +#define SISL_REQ_FLAGS_SUP_UNDERRUN 0x4000U /* bit 1 */ + +#define SISL_REQ_FLAGS_TIMEOUT_SECS 0x0000U /* bits 8,9 */ +#define SISL_REQ_FLAGS_TIMEOUT_MSECS 0x0040U +#define SISL_REQ_FLAGS_TIMEOUT_USECS 0x0080U +#define SISL_REQ_FLAGS_TIMEOUT_CYCLES 0x00C0U + +#define SISL_REQ_FLAGS_TMF_CMD 0x0004u /* bit 13 */ + +#define SISL_REQ_FLAGS_AFU_CMD 0x0002U /* bit 14 */ + +#define SISL_REQ_FLAGS_HOST_WRITE 0x0001U /* bit 15 (LSB) */ +#define SISL_REQ_FLAGS_HOST_READ 0x0000U + + union { + u32 res_hndl; /* res_hndl_t */ + u32 port_sel; /* this is a selection mask: + * 0x1 -> port#0 can be selected, + * 0x2 -> port#1 can be selected. + * Can be bitwise ORed. + */ + }; + u64 lun_id; + u32 data_len; /* 4K for read/write */ + u32 ioadl_len; + union { + u64 data_ea; /* min 16 byte aligned */ + u64 ioadl_ea; + }; + u8 msi; /* LISN to send on RRQ write */ +#define SISL_MSI_CXL_PFAULT 0 /* reserved for CXL page faults */ +#define SISL_MSI_SYNC_ERROR 1 /* recommended for AFU sync error */ +#define SISL_MSI_RRQ_UPDATED 2 /* recommended for IO completion */ +#define SISL_MSI_ASYNC_ERROR 3 /* master only - for AFU async error */ + + u8 rrq; /* 0 for a single RRQ */ + u16 timeout; /* in units specified by req_flags */ + u32 rsvd1; + u8 cdb[16]; /* must be in big endian */ + struct scsi_cmnd *scp; +} __packed; + +struct sisl_rc { + u8 flags; +#define SISL_RC_FLAGS_SENSE_VALID 0x80U +#define SISL_RC_FLAGS_FCP_RSP_CODE_VALID 0x40U +#define SISL_RC_FLAGS_OVERRUN 0x20U +#define SISL_RC_FLAGS_UNDERRUN 0x10U + + u8 afu_rc; +#define SISL_AFU_RC_RHT_INVALID 0x01U /* user error */ +#define SISL_AFU_RC_RHT_UNALIGNED 0x02U /* should never happen */ +#define SISL_AFU_RC_RHT_OUT_OF_BOUNDS 0x03u /* user error */ +#define SISL_AFU_RC_RHT_DMA_ERR 0x04u /* see afu_extra + may retry if afu_retry is off + possible on master exit + */ +#define SISL_AFU_RC_RHT_RW_PERM 0x05u /* no RW perms, user error */ +#define SISL_AFU_RC_LXT_UNALIGNED 0x12U /* should never happen */ +#define SISL_AFU_RC_LXT_OUT_OF_BOUNDS 0x13u /* user error */ +#define SISL_AFU_RC_LXT_DMA_ERR 0x14u /* see afu_extra + may retry if afu_retry is off + possible on master exit + */ +#define SISL_AFU_RC_LXT_RW_PERM 0x15u /* no RW perms, user error */ + +#define SISL_AFU_RC_NOT_XLATE_HOST 0x1au /* possible if master exited */ + + /* NO_CHANNELS means the FC ports selected by dest_port in + * IOARCB or in the LXT entry are down when the AFU tried to select + * a FC port. If the port went down on an active IO, it will set + * fc_rc to =0x54(NOLOGI) or 0x57(LINKDOWN) instead. + */ +#define SISL_AFU_RC_NO_CHANNELS 0x20U /* see afu_extra, may retry */ +#define SISL_AFU_RC_CAP_VIOLATION 0x21U /* either user error or + afu reset/master restart + */ +#define SISL_AFU_RC_OUT_OF_DATA_BUFS 0x30U /* always retry */ +#define SISL_AFU_RC_DATA_DMA_ERR 0x31U /* see afu_extra + may retry if afu_retry is off + */ + + u8 scsi_rc; /* SCSI status byte, retry as appropriate */ +#define SISL_SCSI_RC_CHECK 0x02U +#define SISL_SCSI_RC_BUSY 0x08u + + u8 fc_rc; /* retry */ + /* + * We should only see fc_rc=0x57 (LINKDOWN) or 0x54(NOLOGI) for + * commands that are in flight when a link goes down or is logged out. + * If the link is down or logged out before AFU selects the port, either + * it will choose the other port or we will get afu_rc=0x20 (no_channel) + * if there is no valid port to use. + * + * ABORTPEND/ABORTOK/ABORTFAIL/TGTABORT can be retried, typically these + * would happen if a frame is dropped and something times out. + * NOLOGI or LINKDOWN can be retried if the other port is up. + * RESIDERR can be retried as well. + * + * ABORTFAIL might indicate that lots of frames are getting CRC errors. + * So it maybe retried once and reset the link if it happens again. + * The link can also be reset on the CRC error threshold interrupt. + */ +#define SISL_FC_RC_ABORTPEND 0x52 /* exchange timeout or abort request */ +#define SISL_FC_RC_WRABORTPEND 0x53 /* due to write XFER_RDY invalid */ +#define SISL_FC_RC_NOLOGI 0x54 /* port not logged in, in-flight cmds */ +#define SISL_FC_RC_NOEXP 0x55 /* FC protocol error or HW bug */ +#define SISL_FC_RC_INUSE 0x56 /* tag already in use, HW bug */ +#define SISL_FC_RC_LINKDOWN 0x57 /* link down, in-flight cmds */ +#define SISL_FC_RC_ABORTOK 0x58 /* pending abort completed w/success */ +#define SISL_FC_RC_ABORTFAIL 0x59 /* pending abort completed w/fail */ +#define SISL_FC_RC_RESID 0x5A /* ioasa underrun/overrun flags set */ +#define SISL_FC_RC_RESIDERR 0x5B /* actual data len does not match SCSI + reported len, possibly due to dropped + frames */ +#define SISL_FC_RC_TGTABORT 0x5C /* command aborted by target */ +}; + +#define SISL_SENSE_DATA_LEN 20 /* Sense data length */ + +/* + * IOASA: 64 bytes & must follow IOARCB, min 16 byte alignment required, + * host native endianness + */ +struct sisl_ioasa { + union { + struct sisl_rc rc; + u32 ioasc; +#define SISL_IOASC_GOOD_COMPLETION 0x00000000U + }; + u32 resid; + u8 port; + u8 afu_extra; + /* when afu_rc=0x04, 0x14, 0x31 (_xxx_DMA_ERR): + * afu_exta contains PSL response code. Useful codes are: + */ +#define SISL_AFU_DMA_ERR_PAGE_IN 0x0A /* AFU_retry_on_pagein Action + * Enabled N/A + * Disabled retry + */ +#define SISL_AFU_DMA_ERR_INVALID_EA 0x0B /* this is a hard error + * afu_rc Implies + * 0x04, 0x14 master exit. + * 0x31 user error. + */ + /* when afu rc=0x20 (no channels): + * afu_extra bits [4:5]: available portmask, [6:7]: requested portmask. + */ +#define SISL_AFU_NO_CLANNELS_AMASK(afu_extra) (((afu_extra) & 0x0C) >> 2) +#define SISL_AFU_NO_CLANNELS_RMASK(afu_extra) ((afu_extra) & 0x03) + + u8 scsi_extra; + u8 fc_extra; + u8 sense_data[SISL_SENSE_DATA_LEN]; + + /* These fields are defined by the SISlite architecture for the + * host to use as they see fit for their implementation. + */ + union { + u64 host_use[4]; + u8 host_use_b[32]; + }; +} __packed; + +#define SISL_RESP_HANDLE_T_BIT 0x1ULL /* Toggle bit */ + +/* MMIO space is required to support only 64-bit access */ + +/* + * This AFU has two mechanisms to deal with endian-ness. + * One is a global configuration (in the afu_config) register + * below that specifies the endian-ness of the host. + * The other is a per context (i.e. application) specification + * controlled by the endian_ctrl field here. Since the master + * context is one such application the master context's + * endian-ness is set to be the same as the host. + * + * As per the SISlite spec, the MMIO registers are always + * big endian. + */ +#define SISL_ENDIAN_CTRL_BE 0x8000000000000080ULL +#define SISL_ENDIAN_CTRL_LE 0x0000000000000000ULL + +#ifdef __BIG_ENDIAN +#define SISL_ENDIAN_CTRL SISL_ENDIAN_CTRL_BE +#else +#define SISL_ENDIAN_CTRL SISL_ENDIAN_CTRL_LE +#endif + +/* per context host transport MMIO */ +struct sisl_host_map { + __be64 endian_ctrl; /* Per context Endian Control. The AFU will + * operate on whatever the context is of the + * host application. + */ + + __be64 intr_status; /* this sends LISN# programmed in ctx_ctrl. + * Only recovery in a PERM_ERR is a context + * exit since there is no way to tell which + * command caused the error. + */ +#define SISL_ISTATUS_PERM_ERR_CMDROOM 0x0010ULL /* b59, user error */ +#define SISL_ISTATUS_PERM_ERR_RCB_READ 0x0008ULL /* b60, user error */ +#define SISL_ISTATUS_PERM_ERR_SA_WRITE 0x0004ULL /* b61, user error */ +#define SISL_ISTATUS_PERM_ERR_RRQ_WRITE 0x0002ULL /* b62, user error */ + /* Page in wait accessing RCB/IOASA/RRQ is reported in b63. + * Same error in data/LXT/RHT access is reported via IOASA. + */ +#define SISL_ISTATUS_TEMP_ERR_PAGEIN 0x0001ULL /* b63, can be generated + * only when AFU auto + * retry is disabled. + * If user can determine + * the command that + * caused the error, it + * can be retried. + */ +#define SISL_ISTATUS_UNMASK (0x001FULL) /* 1 means unmasked */ +#define SISL_ISTATUS_MASK ~(SISL_ISTATUS_UNMASK) /* 1 means masked */ + + __be64 intr_clear; + __be64 intr_mask; + __be64 ioarrin; /* only write what cmd_room permits */ + __be64 rrq_start; /* start & end are both inclusive */ + __be64 rrq_end; /* write sequence: start followed by end */ + __be64 cmd_room; + __be64 ctx_ctrl; /* least significant byte or b56:63 is LISN# */ + __be64 mbox_w; /* restricted use */ +}; + +/* per context provisioning & control MMIO */ +struct sisl_ctrl_map { + __be64 rht_start; + __be64 rht_cnt_id; + /* both cnt & ctx_id args must be ULL */ +#define SISL_RHT_CNT_ID(cnt, ctx_id) (((cnt) << 48) | ((ctx_id) << 32)) + + __be64 ctx_cap; /* afu_rc below is when the capability is violated */ +#define SISL_CTX_CAP_PROXY_ISSUE 0x8000000000000000ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_REAL_MODE 0x4000000000000000ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_HOST_XLATE 0x2000000000000000ULL /* afu_rc 0x1a */ +#define SISL_CTX_CAP_PROXY_TARGET 0x1000000000000000ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_AFU_CMD 0x0000000000000008ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_GSCSI_CMD 0x0000000000000004ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_WRITE_CMD 0x0000000000000002ULL /* afu_rc 0x21 */ +#define SISL_CTX_CAP_READ_CMD 0x0000000000000001ULL /* afu_rc 0x21 */ + __be64 mbox_r; +}; + +/* single copy global regs */ +struct sisl_global_regs { + __be64 aintr_status; + /* In cxlflash, each FC port/link gets a byte of status */ +#define SISL_ASTATUS_FC0_OTHER 0x8000ULL /* b48, other err, + FC_ERRCAP[31:20] */ +#define SISL_ASTATUS_FC0_LOGO 0x4000ULL /* b49, target sent FLOGI/PLOGI/LOGO + while logged in */ +#define SISL_ASTATUS_FC0_CRC_T 0x2000ULL /* b50, CRC threshold exceeded */ +#define SISL_ASTATUS_FC0_LOGI_R 0x1000ULL /* b51, login state machine timed out + and retrying */ +#define SISL_ASTATUS_FC0_LOGI_F 0x0800ULL /* b52, login failed, + FC_ERROR[19:0] */ +#define SISL_ASTATUS_FC0_LOGI_S 0x0400ULL /* b53, login succeeded */ +#define SISL_ASTATUS_FC0_LINK_DN 0x0200ULL /* b54, link online to offline */ +#define SISL_ASTATUS_FC0_LINK_UP 0x0100ULL /* b55, link offline to online */ + +#define SISL_ASTATUS_FC1_OTHER 0x0080ULL /* b56 */ +#define SISL_ASTATUS_FC1_LOGO 0x0040ULL /* b57 */ +#define SISL_ASTATUS_FC1_CRC_T 0x0020ULL /* b58 */ +#define SISL_ASTATUS_FC1_LOGI_R 0x0010ULL /* b59 */ +#define SISL_ASTATUS_FC1_LOGI_F 0x0008ULL /* b60 */ +#define SISL_ASTATUS_FC1_LOGI_S 0x0004ULL /* b61 */ +#define SISL_ASTATUS_FC1_LINK_DN 0x0002ULL /* b62 */ +#define SISL_ASTATUS_FC1_LINK_UP 0x0001ULL /* b63 */ + +#define SISL_FC_INTERNAL_UNMASK 0x0000000300000000ULL /* 1 means unmasked */ +#define SISL_FC_INTERNAL_MASK ~(SISL_FC_INTERNAL_UNMASK) +#define SISL_FC_INTERNAL_SHIFT 32 + +#define SISL_ASTATUS_UNMASK 0xFFFFULL /* 1 means unmasked */ +#define SISL_ASTATUS_MASK ~(SISL_ASTATUS_UNMASK) /* 1 means masked */ + + __be64 aintr_clear; + __be64 aintr_mask; + __be64 afu_ctrl; + __be64 afu_hb; + __be64 afu_scratch_pad; + __be64 afu_port_sel; +#define SISL_AFUCONF_AR_IOARCB 0x4000ULL +#define SISL_AFUCONF_AR_LXT 0x2000ULL +#define SISL_AFUCONF_AR_RHT 0x1000ULL +#define SISL_AFUCONF_AR_DATA 0x0800ULL +#define SISL_AFUCONF_AR_RSRC 0x0400ULL +#define SISL_AFUCONF_AR_IOASA 0x0200ULL +#define SISL_AFUCONF_AR_RRQ 0x0100ULL +/* Aggregate all Auto Retry Bits */ +#define SISL_AFUCONF_AR_ALL (SISL_AFUCONF_AR_IOARCB|SISL_AFUCONF_AR_LXT| \ + SISL_AFUCONF_AR_RHT|SISL_AFUCONF_AR_DATA| \ + SISL_AFUCONF_AR_RSRC|SISL_AFUCONF_AR_IOASA| \ + SISL_AFUCONF_AR_RRQ) +#ifdef __BIG_ENDIAN +#define SISL_AFUCONF_ENDIAN 0x0000ULL +#else +#define SISL_AFUCONF_ENDIAN 0x0020ULL +#endif +#define SISL_AFUCONF_MBOX_CLR_READ 0x0010ULL + __be64 afu_config; + __be64 rsvd[0xf8]; + __le64 afu_version; + __be64 interface_version; +}; + +#define CXLFLASH_NUM_FC_PORTS 2 +#define CXLFLASH_MAX_CONTEXT 512 /* how many contexts per afu */ +#define CXLFLASH_NUM_VLUNS 512 + +struct sisl_global_map { + union { + struct sisl_global_regs regs; + char page0[SIZE_4K]; /* page 0 */ + }; + + char page1[SIZE_4K]; /* page 1 */ + + /* pages 2 & 3 */ + __be64 fc_regs[CXLFLASH_NUM_FC_PORTS][CXLFLASH_NUM_VLUNS]; + + /* pages 4 & 5 (lun tbl) */ + __be64 fc_port[CXLFLASH_NUM_FC_PORTS][CXLFLASH_NUM_VLUNS]; + +}; + +/* + * CXL Flash Memory Map + * + * +-------------------------------+ + * | 512 * 64 KB User MMIO | + * | (per context) | + * | User Accessible | + * +-------------------------------+ + * | 512 * 128 B per context | + * | Provisioning and Control | + * | Trusted Process accessible | + * +-------------------------------+ + * | 64 KB Global | + * | Trusted Process accessible | + * +-------------------------------+ +*/ +struct cxlflash_afu_map { + union { + struct sisl_host_map host; + char harea[SIZE_64K]; /* 64KB each */ + } hosts[CXLFLASH_MAX_CONTEXT]; + + union { + struct sisl_ctrl_map ctrl; + char carea[cache_line_size()]; /* 128B each */ + } ctrls[CXLFLASH_MAX_CONTEXT]; + + union { + struct sisl_global_map global; + char garea[SIZE_64K]; /* 64KB single block */ + }; +}; + +/* + * LXT - LBA Translation Table + * LXT control blocks + */ +struct sisl_lxt_entry { + u64 rlba_base; /* bits 0:47 is base + * b48:55 is lun index + * b58:59 is write & read perms + * (if no perm, afu_rc=0x15) + * b60:63 is port_sel mask + */ +}; + +/* + * RHT - Resource Handle Table + * Per the SISlite spec, RHT entries are to be 16-byte aligned + */ +struct sisl_rht_entry { + struct sisl_lxt_entry *lxt_start; + u32 lxt_cnt; + u16 rsvd; + u8 fp; /* format & perm nibbles. + * (if no perm, afu_rc=0x05) + */ + u8 nmask; +} __packed __aligned(16); + +struct sisl_rht_entry_f1 { + u64 lun_id; + union { + struct { + u8 valid; + u8 rsvd[5]; + u8 fp; + u8 port_sel; + }; + + u64 dw; + }; +} __packed __aligned(16); + +/* make the fp byte */ +#define SISL_RHT_FP(fmt, perm) (((fmt) << 4) | (perm)) + +/* make the fp byte for a clone from a source fp and clone flags + * flags must be only 2 LSB bits. + */ +#define SISL_RHT_FP_CLONE(src_fp, cln_flags) ((src_fp) & (0xFC | (cln_flags))) + +#define RHT_PERM_READ 0x01U +#define RHT_PERM_WRITE 0x02U +#define RHT_PERM_RW (RHT_PERM_READ | RHT_PERM_WRITE) + +/* extract the perm bits from a fp */ +#define SISL_RHT_PERM(fp) ((fp) & RHT_PERM_RW) + +#define PORT0 0x01U +#define PORT1 0x02U +#define BOTH_PORTS (PORT0 | PORT1) + +/* AFU Sync Mode byte */ +#define AFU_LW_SYNC 0x0U +#define AFU_HW_SYNC 0x1U +#define AFU_GSYNC 0x2U + +/* Special Task Management Function CDB */ +#define TMF_LUN_RESET 0x1U +#define TMF_CLEAR_ACA 0x2U + + +#define SISLITE_MAX_WS_BLOCKS 512 + +#endif /* _SISLITE_H */ diff --git a/kernel/drivers/scsi/cxlflash/superpipe.c b/kernel/drivers/scsi/cxlflash/superpipe.c new file mode 100644 index 000000000..cac2e6a50 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/superpipe.c @@ -0,0 +1,2143 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sislite.h" +#include "common.h" +#include "vlun.h" +#include "superpipe.h" + +struct cxlflash_global global; + +/** + * marshal_rele_to_resize() - translate release to resize structure + * @rele: Source structure from which to translate/copy. + * @resize: Destination structure for the translate/copy. + */ +static void marshal_rele_to_resize(struct dk_cxlflash_release *release, + struct dk_cxlflash_resize *resize) +{ + resize->hdr = release->hdr; + resize->context_id = release->context_id; + resize->rsrc_handle = release->rsrc_handle; +} + +/** + * marshal_det_to_rele() - translate detach to release structure + * @detach: Destination structure for the translate/copy. + * @rele: Source structure from which to translate/copy. + */ +static void marshal_det_to_rele(struct dk_cxlflash_detach *detach, + struct dk_cxlflash_release *release) +{ + release->hdr = detach->hdr; + release->context_id = detach->context_id; +} + +/** + * cxlflash_free_errpage() - frees resources associated with global error page + */ +void cxlflash_free_errpage(void) +{ + + mutex_lock(&global.mutex); + if (global.err_page) { + __free_page(global.err_page); + global.err_page = NULL; + } + mutex_unlock(&global.mutex); +} + +/** + * cxlflash_stop_term_user_contexts() - stops/terminates known user contexts + * @cfg: Internal structure associated with the host. + * + * When the host needs to go down, all users must be quiesced and their + * memory freed. This is accomplished by putting the contexts in error + * state which will notify the user and let them 'drive' the tear down. + * Meanwhile, this routine camps until all user contexts have been removed. + */ +void cxlflash_stop_term_user_contexts(struct cxlflash_cfg *cfg) +{ + struct device *dev = &cfg->dev->dev; + int i, found; + + cxlflash_mark_contexts_error(cfg); + + while (true) { + found = false; + + for (i = 0; i < MAX_CONTEXT; i++) + if (cfg->ctx_tbl[i]) { + found = true; + break; + } + + if (!found && list_empty(&cfg->ctx_err_recovery)) + return; + + dev_dbg(dev, "%s: Wait for user contexts to quiesce...\n", + __func__); + wake_up_all(&cfg->reset_waitq); + ssleep(1); + } +} + +/** + * find_error_context() - locates a context by cookie on the error recovery list + * @cfg: Internal structure associated with the host. + * @rctxid: Desired context by id. + * @file: Desired context by file. + * + * Return: Found context on success, NULL on failure + */ +static struct ctx_info *find_error_context(struct cxlflash_cfg *cfg, u64 rctxid, + struct file *file) +{ + struct ctx_info *ctxi; + + list_for_each_entry(ctxi, &cfg->ctx_err_recovery, list) + if ((ctxi->ctxid == rctxid) || (ctxi->file == file)) + return ctxi; + + return NULL; +} + +/** + * get_context() - obtains a validated and locked context reference + * @cfg: Internal structure associated with the host. + * @rctxid: Desired context (raw, un-decoded format). + * @arg: LUN information or file associated with request. + * @ctx_ctrl: Control information to 'steer' desired lookup. + * + * NOTE: despite the name pid, in linux, current->pid actually refers + * to the lightweight process id (tid) and can change if the process is + * multi threaded. The tgid remains constant for the process and only changes + * when the process of fork. For all intents and purposes, think of tgid + * as a pid in the traditional sense. + * + * Return: Validated context on success, NULL on failure + */ +struct ctx_info *get_context(struct cxlflash_cfg *cfg, u64 rctxid, + void *arg, enum ctx_ctrl ctx_ctrl) +{ + struct device *dev = &cfg->dev->dev; + struct ctx_info *ctxi = NULL; + struct lun_access *lun_access = NULL; + struct file *file = NULL; + struct llun_info *lli = arg; + u64 ctxid = DECODE_CTXID(rctxid); + int rc; + pid_t pid = current->tgid, ctxpid = 0; + + if (ctx_ctrl & CTX_CTRL_FILE) { + lli = NULL; + file = (struct file *)arg; + } + + if (ctx_ctrl & CTX_CTRL_CLONE) + pid = current->parent->tgid; + + if (likely(ctxid < MAX_CONTEXT)) { + while (true) { + mutex_lock(&cfg->ctx_tbl_list_mutex); + ctxi = cfg->ctx_tbl[ctxid]; + if (ctxi) + if ((file && (ctxi->file != file)) || + (!file && (ctxi->ctxid != rctxid))) + ctxi = NULL; + + if ((ctx_ctrl & CTX_CTRL_ERR) || + (!ctxi && (ctx_ctrl & CTX_CTRL_ERR_FALLBACK))) + ctxi = find_error_context(cfg, rctxid, file); + if (!ctxi) { + mutex_unlock(&cfg->ctx_tbl_list_mutex); + goto out; + } + + /* + * Need to acquire ownership of the context while still + * under the table/list lock to serialize with a remove + * thread. Use the 'try' to avoid stalling the + * table/list lock for a single context. + * + * Note that the lock order is: + * + * cfg->ctx_tbl_list_mutex -> ctxi->mutex + * + * Therefore release ctx_tbl_list_mutex before retrying. + */ + rc = mutex_trylock(&ctxi->mutex); + mutex_unlock(&cfg->ctx_tbl_list_mutex); + if (rc) + break; /* got the context's lock! */ + } + + if (ctxi->unavail) + goto denied; + + ctxpid = ctxi->pid; + if (likely(!(ctx_ctrl & CTX_CTRL_NOPID))) + if (pid != ctxpid) + goto denied; + + if (lli) { + list_for_each_entry(lun_access, &ctxi->luns, list) + if (lun_access->lli == lli) + goto out; + goto denied; + } + } + +out: + dev_dbg(dev, "%s: rctxid=%016llX ctxinfo=%p ctxpid=%u pid=%u " + "ctx_ctrl=%u\n", __func__, rctxid, ctxi, ctxpid, pid, + ctx_ctrl); + + return ctxi; + +denied: + mutex_unlock(&ctxi->mutex); + ctxi = NULL; + goto out; +} + +/** + * put_context() - release a context that was retrieved from get_context() + * @ctxi: Context to release. + * + * For now, releasing the context equates to unlocking it's mutex. + */ +void put_context(struct ctx_info *ctxi) +{ + mutex_unlock(&ctxi->mutex); +} + +/** + * afu_attach() - attach a context to the AFU + * @cfg: Internal structure associated with the host. + * @ctxi: Context to attach. + * + * Upon setting the context capabilities, they must be confirmed with + * a read back operation as the context might have been closed since + * the mailbox was unlocked. When this occurs, registration is failed. + * + * Return: 0 on success, -errno on failure + */ +static int afu_attach(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) +{ + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + struct sisl_ctrl_map __iomem *ctrl_map = ctxi->ctrl_map; + int rc = 0; + u64 val; + + /* Unlock cap and restrict user to read/write cmds in translated mode */ + readq_be(&ctrl_map->mbox_r); + val = (SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD); + writeq_be(val, &ctrl_map->ctx_cap); + val = readq_be(&ctrl_map->ctx_cap); + if (val != (SISL_CTX_CAP_READ_CMD | SISL_CTX_CAP_WRITE_CMD)) { + dev_err(dev, "%s: ctx may be closed val=%016llX\n", + __func__, val); + rc = -EAGAIN; + goto out; + } + + /* Set up MMIO registers pointing to the RHT */ + writeq_be((u64)ctxi->rht_start, &ctrl_map->rht_start); + val = SISL_RHT_CNT_ID((u64)MAX_RHT_PER_CONTEXT, (u64)(afu->ctx_hndl)); + writeq_be(val, &ctrl_map->rht_cnt_id); +out: + dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * read_cap16() - issues a SCSI READ_CAP16 command + * @sdev: SCSI device associated with LUN. + * @lli: LUN destined for capacity request. + * + * The READ_CAP16 can take quite a while to complete. Should an EEH occur while + * in scsi_execute(), the EEH handler will attempt to recover. As part of the + * recovery, the handler drains all currently running ioctls, waiting until they + * have completed before proceeding with a reset. As this routine is used on the + * ioctl path, this can create a condition where the EEH handler becomes stuck, + * infinitely waiting for this ioctl thread. To avoid this behavior, temporarily + * unmark this thread as an ioctl thread by releasing the ioctl read semaphore. + * This will allow the EEH handler to proceed with a recovery while this thread + * is still running. Once the scsi_execute() returns, reacquire the ioctl read + * semaphore and check the adapter state in case it changed while inside of + * scsi_execute(). The state check will wait if the adapter is still being + * recovered or return a failure if the recovery failed. In the event that the + * adapter reset failed, simply return the failure as the ioctl would be unable + * to continue. + * + * Note that the above puts a requirement on this routine to only be called on + * an ioctl thread. + * + * Return: 0 on success, -errno on failure + */ +static int read_cap16(struct scsi_device *sdev, struct llun_info *lli) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct glun_info *gli = lli->parent; + u8 *cmd_buf = NULL; + u8 *scsi_cmd = NULL; + u8 *sense_buf = NULL; + int rc = 0; + int result = 0; + int retry_cnt = 0; + u32 to = CMD_TIMEOUT * HZ; + +retry: + cmd_buf = kzalloc(CMD_BUFSIZE, GFP_KERNEL); + scsi_cmd = kzalloc(MAX_COMMAND_SIZE, GFP_KERNEL); + sense_buf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + if (unlikely(!cmd_buf || !scsi_cmd || !sense_buf)) { + rc = -ENOMEM; + goto out; + } + + scsi_cmd[0] = SERVICE_ACTION_IN_16; /* read cap(16) */ + scsi_cmd[1] = SAI_READ_CAPACITY_16; /* service action */ + put_unaligned_be32(CMD_BUFSIZE, &scsi_cmd[10]); + + dev_dbg(dev, "%s: %ssending cmd(0x%x)\n", __func__, + retry_cnt ? "re" : "", scsi_cmd[0]); + + /* Drop the ioctl read semahpore across lengthy call */ + up_read(&cfg->ioctl_rwsem); + result = scsi_execute(sdev, scsi_cmd, DMA_FROM_DEVICE, cmd_buf, + CMD_BUFSIZE, sense_buf, to, CMD_RETRIES, 0, NULL); + down_read(&cfg->ioctl_rwsem); + rc = check_state(cfg); + if (rc) { + dev_err(dev, "%s: Failed state! result=0x08%X\n", + __func__, result); + rc = -ENODEV; + goto out; + } + + if (driver_byte(result) == DRIVER_SENSE) { + result &= ~(0xFF<<24); /* DRIVER_SENSE is not an error */ + if (result & SAM_STAT_CHECK_CONDITION) { + struct scsi_sense_hdr sshdr; + + scsi_normalize_sense(sense_buf, SCSI_SENSE_BUFFERSIZE, + &sshdr); + switch (sshdr.sense_key) { + case NO_SENSE: + case RECOVERED_ERROR: + /* fall through */ + case NOT_READY: + result &= ~SAM_STAT_CHECK_CONDITION; + break; + case UNIT_ATTENTION: + switch (sshdr.asc) { + case 0x29: /* Power on Reset or Device Reset */ + /* fall through */ + case 0x2A: /* Device capacity changed */ + case 0x3F: /* Report LUNs changed */ + /* Retry the command once more */ + if (retry_cnt++ < 1) { + kfree(cmd_buf); + kfree(scsi_cmd); + kfree(sense_buf); + goto retry; + } + } + break; + default: + break; + } + } + } + + if (result) { + dev_err(dev, "%s: command failed, result=0x%x\n", + __func__, result); + rc = -EIO; + goto out; + } + + /* + * Read cap was successful, grab values from the buffer; + * note that we don't need to worry about unaligned access + * as the buffer is allocated on an aligned boundary. + */ + mutex_lock(&gli->mutex); + gli->max_lba = be64_to_cpu(*((__be64 *)&cmd_buf[0])); + gli->blk_len = be32_to_cpu(*((__be32 *)&cmd_buf[8])); + mutex_unlock(&gli->mutex); + +out: + kfree(cmd_buf); + kfree(scsi_cmd); + kfree(sense_buf); + + dev_dbg(dev, "%s: maxlba=%lld blklen=%d rc=%d\n", + __func__, gli->max_lba, gli->blk_len, rc); + return rc; +} + +/** + * get_rhte() - obtains validated resource handle table entry reference + * @ctxi: Context owning the resource handle. + * @rhndl: Resource handle associated with entry. + * @lli: LUN associated with request. + * + * Return: Validated RHTE on success, NULL on failure + */ +struct sisl_rht_entry *get_rhte(struct ctx_info *ctxi, res_hndl_t rhndl, + struct llun_info *lli) +{ + struct sisl_rht_entry *rhte = NULL; + + if (unlikely(!ctxi->rht_start)) { + pr_debug("%s: Context does not have allocated RHT!\n", + __func__); + goto out; + } + + if (unlikely(rhndl >= MAX_RHT_PER_CONTEXT)) { + pr_debug("%s: Bad resource handle! (%d)\n", __func__, rhndl); + goto out; + } + + if (unlikely(ctxi->rht_lun[rhndl] != lli)) { + pr_debug("%s: Bad resource handle LUN! (%d)\n", + __func__, rhndl); + goto out; + } + + rhte = &ctxi->rht_start[rhndl]; + if (unlikely(rhte->nmask == 0)) { + pr_debug("%s: Unopened resource handle! (%d)\n", + __func__, rhndl); + rhte = NULL; + goto out; + } + +out: + return rhte; +} + +/** + * rhte_checkout() - obtains free/empty resource handle table entry + * @ctxi: Context owning the resource handle. + * @lli: LUN associated with request. + * + * Return: Free RHTE on success, NULL on failure + */ +struct sisl_rht_entry *rhte_checkout(struct ctx_info *ctxi, + struct llun_info *lli) +{ + struct sisl_rht_entry *rhte = NULL; + int i; + + /* Find a free RHT entry */ + for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) + if (ctxi->rht_start[i].nmask == 0) { + rhte = &ctxi->rht_start[i]; + ctxi->rht_out++; + break; + } + + if (likely(rhte)) + ctxi->rht_lun[i] = lli; + + pr_debug("%s: returning rhte=%p (%d)\n", __func__, rhte, i); + return rhte; +} + +/** + * rhte_checkin() - releases a resource handle table entry + * @ctxi: Context owning the resource handle. + * @rhte: RHTE to release. + */ +void rhte_checkin(struct ctx_info *ctxi, + struct sisl_rht_entry *rhte) +{ + u32 rsrc_handle = rhte - ctxi->rht_start; + + rhte->nmask = 0; + rhte->fp = 0; + ctxi->rht_out--; + ctxi->rht_lun[rsrc_handle] = NULL; + ctxi->rht_needs_ws[rsrc_handle] = false; +} + +/** + * rhte_format1() - populates a RHTE for format 1 + * @rhte: RHTE to populate. + * @lun_id: LUN ID of LUN associated with RHTE. + * @perm: Desired permissions for RHTE. + * @port_sel: Port selection mask + */ +static void rht_format1(struct sisl_rht_entry *rhte, u64 lun_id, u32 perm, + u32 port_sel) +{ + /* + * Populate the Format 1 RHT entry for direct access (physical + * LUN) using the synchronization sequence defined in the + * SISLite specification. + */ + struct sisl_rht_entry_f1 dummy = { 0 }; + struct sisl_rht_entry_f1 *rhte_f1 = (struct sisl_rht_entry_f1 *)rhte; + + memset(rhte_f1, 0, sizeof(*rhte_f1)); + rhte_f1->fp = SISL_RHT_FP(1U, 0); + dma_wmb(); /* Make setting of format bit visible */ + + rhte_f1->lun_id = lun_id; + dma_wmb(); /* Make setting of LUN id visible */ + + /* + * Use a dummy RHT Format 1 entry to build the second dword + * of the entry that must be populated in a single write when + * enabled (valid bit set to TRUE). + */ + dummy.valid = 0x80; + dummy.fp = SISL_RHT_FP(1U, perm); + dummy.port_sel = port_sel; + rhte_f1->dw = dummy.dw; + + dma_wmb(); /* Make remaining RHT entry fields visible */ +} + +/** + * cxlflash_lun_attach() - attaches a user to a LUN and manages the LUN's mode + * @gli: LUN to attach. + * @mode: Desired mode of the LUN. + * @locked: Mutex status on current thread. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_lun_attach(struct glun_info *gli, enum lun_mode mode, bool locked) +{ + int rc = 0; + + if (!locked) + mutex_lock(&gli->mutex); + + if (gli->mode == MODE_NONE) + gli->mode = mode; + else if (gli->mode != mode) { + pr_debug("%s: LUN operating in mode %d, requested mode %d\n", + __func__, gli->mode, mode); + rc = -EINVAL; + goto out; + } + + gli->users++; + WARN_ON(gli->users <= 0); +out: + pr_debug("%s: Returning rc=%d gli->mode=%u gli->users=%u\n", + __func__, rc, gli->mode, gli->users); + if (!locked) + mutex_unlock(&gli->mutex); + return rc; +} + +/** + * cxlflash_lun_detach() - detaches a user from a LUN and resets the LUN's mode + * @gli: LUN to detach. + * + * When resetting the mode, terminate block allocation resources as they + * are no longer required (service is safe to call even when block allocation + * resources were not present - such as when transitioning from physical mode). + * These resources will be reallocated when needed (subsequent transition to + * virtual mode). + */ +void cxlflash_lun_detach(struct glun_info *gli) +{ + mutex_lock(&gli->mutex); + WARN_ON(gli->mode == MODE_NONE); + if (--gli->users == 0) { + gli->mode = MODE_NONE; + cxlflash_ba_terminate(&gli->blka.ba_lun); + } + pr_debug("%s: gli->users=%u\n", __func__, gli->users); + WARN_ON(gli->users < 0); + mutex_unlock(&gli->mutex); +} + +/** + * _cxlflash_disk_release() - releases the specified resource entry + * @sdev: SCSI device associated with LUN. + * @ctxi: Context owning resources. + * @release: Release ioctl data structure. + * + * For LUNs in virtual mode, the virtual LUN associated with the specified + * resource handle is resized to 0 prior to releasing the RHTE. Note that the + * AFU sync should _not_ be performed when the context is sitting on the error + * recovery list. A context on the error recovery list is not known to the AFU + * due to reset. When the context is recovered, it will be reattached and made + * known again to the AFU. + * + * Return: 0 on success, -errno on failure + */ +int _cxlflash_disk_release(struct scsi_device *sdev, + struct ctx_info *ctxi, + struct dk_cxlflash_release *release) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct afu *afu = cfg->afu; + bool put_ctx = false; + + struct dk_cxlflash_resize size; + res_hndl_t rhndl = release->rsrc_handle; + + int rc = 0; + u64 ctxid = DECODE_CTXID(release->context_id), + rctxid = release->context_id; + + struct sisl_rht_entry *rhte; + struct sisl_rht_entry_f1 *rhte_f1; + + dev_dbg(dev, "%s: ctxid=%llu rhndl=0x%llx gli->mode=%u gli->users=%u\n", + __func__, ctxid, release->rsrc_handle, gli->mode, gli->users); + + if (!ctxi) { + ctxi = get_context(cfg, rctxid, lli, CTX_CTRL_ERR_FALLBACK); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%llu)\n", + __func__, ctxid); + rc = -EINVAL; + goto out; + } + + put_ctx = true; + } + + rhte = get_rhte(ctxi, rhndl, lli); + if (unlikely(!rhte)) { + dev_dbg(dev, "%s: Bad resource handle! (%d)\n", + __func__, rhndl); + rc = -EINVAL; + goto out; + } + + /* + * Resize to 0 for virtual LUNS by setting the size + * to 0. This will clear LXT_START and LXT_CNT fields + * in the RHT entry and properly sync with the AFU. + * + * Afterwards we clear the remaining fields. + */ + switch (gli->mode) { + case MODE_VIRTUAL: + marshal_rele_to_resize(release, &size); + size.req_size = 0; + rc = _cxlflash_vlun_resize(sdev, ctxi, &size); + if (rc) { + dev_dbg(dev, "%s: resize failed rc %d\n", __func__, rc); + goto out; + } + + break; + case MODE_PHYSICAL: + /* + * Clear the Format 1 RHT entry for direct access + * (physical LUN) using the synchronization sequence + * defined in the SISLite specification. + */ + rhte_f1 = (struct sisl_rht_entry_f1 *)rhte; + + rhte_f1->valid = 0; + dma_wmb(); /* Make revocation of RHT entry visible */ + + rhte_f1->lun_id = 0; + dma_wmb(); /* Make clearing of LUN id visible */ + + rhte_f1->dw = 0; + dma_wmb(); /* Make RHT entry bottom-half clearing visible */ + + if (!ctxi->err_recovery_active) + cxlflash_afu_sync(afu, ctxid, rhndl, AFU_HW_SYNC); + break; + default: + WARN(1, "Unsupported LUN mode!"); + goto out; + } + + rhte_checkin(ctxi, rhte); + cxlflash_lun_detach(gli); + +out: + if (put_ctx) + put_context(ctxi); + dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); + return rc; +} + +int cxlflash_disk_release(struct scsi_device *sdev, + struct dk_cxlflash_release *release) +{ + return _cxlflash_disk_release(sdev, NULL, release); +} + +/** + * destroy_context() - releases a context + * @cfg: Internal structure associated with the host. + * @ctxi: Context to release. + * + * Note that the rht_lun member of the context was cut from a single + * allocation when the context was created and therefore does not need + * to be explicitly freed. Also note that we conditionally check for the + * existence of the context control map before clearing the RHT registers + * and context capabilities because it is possible to destroy a context + * while the context is in the error state (previous mapping was removed + * [so we don't have to worry about clearing] and context is waiting for + * a new mapping). + */ +static void destroy_context(struct cxlflash_cfg *cfg, + struct ctx_info *ctxi) +{ + struct afu *afu = cfg->afu; + + WARN_ON(!list_empty(&ctxi->luns)); + + /* Clear RHT registers and drop all capabilities for this context */ + if (afu->afu_map && ctxi->ctrl_map) { + writeq_be(0, &ctxi->ctrl_map->rht_start); + writeq_be(0, &ctxi->ctrl_map->rht_cnt_id); + writeq_be(0, &ctxi->ctrl_map->ctx_cap); + } + + /* Free memory associated with context */ + free_page((ulong)ctxi->rht_start); + kfree(ctxi->rht_needs_ws); + kfree(ctxi->rht_lun); + kfree(ctxi); +} + +/** + * create_context() - allocates and initializes a context + * @cfg: Internal structure associated with the host. + * @ctx: Previously obtained CXL context reference. + * @ctxid: Previously obtained process element associated with CXL context. + * @adap_fd: Previously obtained adapter fd associated with CXL context. + * @file: Previously obtained file associated with CXL context. + * @perms: User-specified permissions. + * + * The context's mutex is locked when an allocated context is returned. + * + * Return: Allocated context on success, NULL on failure + */ +static struct ctx_info *create_context(struct cxlflash_cfg *cfg, + struct cxl_context *ctx, int ctxid, + int adap_fd, struct file *file, + u32 perms) +{ + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + struct ctx_info *ctxi = NULL; + struct llun_info **lli = NULL; + u8 *ws = NULL; + struct sisl_rht_entry *rhte; + + ctxi = kzalloc(sizeof(*ctxi), GFP_KERNEL); + lli = kzalloc((MAX_RHT_PER_CONTEXT * sizeof(*lli)), GFP_KERNEL); + ws = kzalloc((MAX_RHT_PER_CONTEXT * sizeof(*ws)), GFP_KERNEL); + if (unlikely(!ctxi || !lli || !ws)) { + dev_err(dev, "%s: Unable to allocate context!\n", __func__); + goto err; + } + + rhte = (struct sisl_rht_entry *)get_zeroed_page(GFP_KERNEL); + if (unlikely(!rhte)) { + dev_err(dev, "%s: Unable to allocate RHT!\n", __func__); + goto err; + } + + ctxi->rht_lun = lli; + ctxi->rht_needs_ws = ws; + ctxi->rht_start = rhte; + ctxi->rht_perms = perms; + + ctxi->ctrl_map = &afu->afu_map->ctrls[ctxid].ctrl; + ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); + ctxi->lfd = adap_fd; + ctxi->pid = current->tgid; /* tgid = pid */ + ctxi->ctx = ctx; + ctxi->file = file; + mutex_init(&ctxi->mutex); + INIT_LIST_HEAD(&ctxi->luns); + INIT_LIST_HEAD(&ctxi->list); /* initialize for list_empty() */ + + mutex_lock(&ctxi->mutex); +out: + return ctxi; + +err: + kfree(ws); + kfree(lli); + kfree(ctxi); + ctxi = NULL; + goto out; +} + +/** + * _cxlflash_disk_detach() - detaches a LUN from a context + * @sdev: SCSI device associated with LUN. + * @ctxi: Context owning resources. + * @detach: Detach ioctl data structure. + * + * As part of the detach, all per-context resources associated with the LUN + * are cleaned up. When detaching the last LUN for a context, the context + * itself is cleaned up and released. + * + * Return: 0 on success, -errno on failure + */ +static int _cxlflash_disk_detach(struct scsi_device *sdev, + struct ctx_info *ctxi, + struct dk_cxlflash_detach *detach) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct lun_access *lun_access, *t; + struct dk_cxlflash_release rel; + bool put_ctx = false; + + int i; + int rc = 0; + int lfd; + u64 ctxid = DECODE_CTXID(detach->context_id), + rctxid = detach->context_id; + + dev_dbg(dev, "%s: ctxid=%llu\n", __func__, ctxid); + + if (!ctxi) { + ctxi = get_context(cfg, rctxid, lli, CTX_CTRL_ERR_FALLBACK); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%llu)\n", + __func__, ctxid); + rc = -EINVAL; + goto out; + } + + put_ctx = true; + } + + /* Cleanup outstanding resources tied to this LUN */ + if (ctxi->rht_out) { + marshal_det_to_rele(detach, &rel); + for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) { + if (ctxi->rht_lun[i] == lli) { + rel.rsrc_handle = i; + _cxlflash_disk_release(sdev, ctxi, &rel); + } + + /* No need to loop further if we're done */ + if (ctxi->rht_out == 0) + break; + } + } + + /* Take our LUN out of context, free the node */ + list_for_each_entry_safe(lun_access, t, &ctxi->luns, list) + if (lun_access->lli == lli) { + list_del(&lun_access->list); + kfree(lun_access); + lun_access = NULL; + break; + } + + /* Tear down context following last LUN cleanup */ + if (list_empty(&ctxi->luns)) { + ctxi->unavail = true; + mutex_unlock(&ctxi->mutex); + mutex_lock(&cfg->ctx_tbl_list_mutex); + mutex_lock(&ctxi->mutex); + + /* Might not have been in error list so conditionally remove */ + if (!list_empty(&ctxi->list)) + list_del(&ctxi->list); + cfg->ctx_tbl[ctxid] = NULL; + mutex_unlock(&cfg->ctx_tbl_list_mutex); + mutex_unlock(&ctxi->mutex); + + lfd = ctxi->lfd; + destroy_context(cfg, ctxi); + ctxi = NULL; + put_ctx = false; + + /* + * As a last step, clean up external resources when not + * already on an external cleanup thread, i.e.: close(adap_fd). + * + * NOTE: this will free up the context from the CXL services, + * allowing it to dole out the same context_id on a future + * (or even currently in-flight) disk_attach operation. + */ + if (lfd != -1) + sys_close(lfd); + } + + /* Release the sdev reference that bound this LUN to the context */ + scsi_device_put(sdev); + +out: + if (put_ctx) + put_context(ctxi); + dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); + return rc; +} + +static int cxlflash_disk_detach(struct scsi_device *sdev, + struct dk_cxlflash_detach *detach) +{ + return _cxlflash_disk_detach(sdev, NULL, detach); +} + +/** + * cxlflash_cxl_release() - release handler for adapter file descriptor + * @inode: File-system inode associated with fd. + * @file: File installed with adapter file descriptor. + * + * This routine is the release handler for the fops registered with + * the CXL services on an initial attach for a context. It is called + * when a close is performed on the adapter file descriptor returned + * to the user. Programmatically, the user is not required to perform + * the close, as it is handled internally via the detach ioctl when + * a context is being removed. Note that nothing prevents the user + * from performing a close, but the user should be aware that doing + * so is considered catastrophic and subsequent usage of the superpipe + * API with previously saved off tokens will fail. + * + * When initiated from an external close (either by the user or via + * a process tear down), the routine derives the context reference + * and calls detach for each LUN associated with the context. The + * final detach operation will cause the context itself to be freed. + * Note that the saved off lfd is reset prior to calling detach to + * signify that the final detach should not perform a close. + * + * When initiated from a detach operation as part of the tear down + * of a context, the context is first completely freed and then the + * close is performed. This routine will fail to derive the context + * reference (due to the context having already been freed) and then + * call into the CXL release entry point. + * + * Thus, with exception to when the CXL process element (context id) + * lookup fails (a case that should theoretically never occur), every + * call into this routine results in a complete freeing of a context. + * + * As part of the detach, all per-context resources associated with the LUN + * are cleaned up. When detaching the last LUN for a context, the context + * itself is cleaned up and released. + * + * Return: 0 on success + */ +static int cxlflash_cxl_release(struct inode *inode, struct file *file) +{ + struct cxl_context *ctx = cxl_fops_get_context(file); + struct cxlflash_cfg *cfg = container_of(file->f_op, struct cxlflash_cfg, + cxl_fops); + struct device *dev = &cfg->dev->dev; + struct ctx_info *ctxi = NULL; + struct dk_cxlflash_detach detach = { { 0 }, 0 }; + struct lun_access *lun_access, *t; + enum ctx_ctrl ctrl = CTX_CTRL_ERR_FALLBACK | CTX_CTRL_FILE; + int ctxid; + + ctxid = cxl_process_element(ctx); + if (unlikely(ctxid < 0)) { + dev_err(dev, "%s: Context %p was closed! (%d)\n", + __func__, ctx, ctxid); + goto out; + } + + ctxi = get_context(cfg, ctxid, file, ctrl); + if (unlikely(!ctxi)) { + ctxi = get_context(cfg, ctxid, file, ctrl | CTX_CTRL_CLONE); + if (!ctxi) { + dev_dbg(dev, "%s: Context %d already free!\n", + __func__, ctxid); + goto out_release; + } + + dev_dbg(dev, "%s: Another process owns context %d!\n", + __func__, ctxid); + put_context(ctxi); + goto out; + } + + dev_dbg(dev, "%s: close(%d) for context %d\n", + __func__, ctxi->lfd, ctxid); + + /* Reset the file descriptor to indicate we're on a close() thread */ + ctxi->lfd = -1; + detach.context_id = ctxi->ctxid; + list_for_each_entry_safe(lun_access, t, &ctxi->luns, list) + _cxlflash_disk_detach(lun_access->sdev, ctxi, &detach); +out_release: + cxl_fd_release(inode, file); +out: + dev_dbg(dev, "%s: returning\n", __func__); + return 0; +} + +/** + * unmap_context() - clears a previously established mapping + * @ctxi: Context owning the mapping. + * + * This routine is used to switch between the error notification page + * (dummy page of all 1's) and the real mapping (established by the CXL + * fault handler). + */ +static void unmap_context(struct ctx_info *ctxi) +{ + unmap_mapping_range(ctxi->file->f_mapping, 0, 0, 1); +} + +/** + * get_err_page() - obtains and allocates the error notification page + * + * Return: error notification page on success, NULL on failure + */ +static struct page *get_err_page(void) +{ + struct page *err_page = global.err_page; + + if (unlikely(!err_page)) { + err_page = alloc_page(GFP_KERNEL); + if (unlikely(!err_page)) { + pr_err("%s: Unable to allocate err_page!\n", __func__); + goto out; + } + + memset(page_address(err_page), -1, PAGE_SIZE); + + /* Serialize update w/ other threads to avoid a leak */ + mutex_lock(&global.mutex); + if (likely(!global.err_page)) + global.err_page = err_page; + else { + __free_page(err_page); + err_page = global.err_page; + } + mutex_unlock(&global.mutex); + } + +out: + pr_debug("%s: returning err_page=%p\n", __func__, err_page); + return err_page; +} + +/** + * cxlflash_mmap_fault() - mmap fault handler for adapter file descriptor + * @vma: VM area associated with mapping. + * @vmf: VM fault associated with current fault. + * + * To support error notification via MMIO, faults are 'caught' by this routine + * that was inserted before passing back the adapter file descriptor on attach. + * When a fault occurs, this routine evaluates if error recovery is active and + * if so, installs the error page to 'notify' the user about the error state. + * During normal operation, the fault is simply handled by the original fault + * handler that was installed by CXL services as part of initializing the + * adapter file descriptor. The VMA's page protection bits are toggled to + * indicate cached/not-cached depending on the memory backing the fault. + * + * Return: 0 on success, VM_FAULT_SIGBUS on failure + */ +static int cxlflash_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct file *file = vma->vm_file; + struct cxl_context *ctx = cxl_fops_get_context(file); + struct cxlflash_cfg *cfg = container_of(file->f_op, struct cxlflash_cfg, + cxl_fops); + struct device *dev = &cfg->dev->dev; + struct ctx_info *ctxi = NULL; + struct page *err_page = NULL; + enum ctx_ctrl ctrl = CTX_CTRL_ERR_FALLBACK | CTX_CTRL_FILE; + int rc = 0; + int ctxid; + + ctxid = cxl_process_element(ctx); + if (unlikely(ctxid < 0)) { + dev_err(dev, "%s: Context %p was closed! (%d)\n", + __func__, ctx, ctxid); + goto err; + } + + ctxi = get_context(cfg, ctxid, file, ctrl); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%d)\n", __func__, ctxid); + goto err; + } + + dev_dbg(dev, "%s: fault(%d) for context %d\n", + __func__, ctxi->lfd, ctxid); + + if (likely(!ctxi->err_recovery_active)) { + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + rc = ctxi->cxl_mmap_vmops->fault(vma, vmf); + } else { + dev_dbg(dev, "%s: err recovery active, use err_page!\n", + __func__); + + err_page = get_err_page(); + if (unlikely(!err_page)) { + dev_err(dev, "%s: Could not obtain error page!\n", + __func__); + rc = VM_FAULT_RETRY; + goto out; + } + + get_page(err_page); + vmf->page = err_page; + vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); + } + +out: + if (likely(ctxi)) + put_context(ctxi); + dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); + return rc; + +err: + rc = VM_FAULT_SIGBUS; + goto out; +} + +/* + * Local MMAP vmops to 'catch' faults + */ +static const struct vm_operations_struct cxlflash_mmap_vmops = { + .fault = cxlflash_mmap_fault, +}; + +/** + * cxlflash_cxl_mmap() - mmap handler for adapter file descriptor + * @file: File installed with adapter file descriptor. + * @vma: VM area associated with mapping. + * + * Installs local mmap vmops to 'catch' faults for error notification support. + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_cxl_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cxl_context *ctx = cxl_fops_get_context(file); + struct cxlflash_cfg *cfg = container_of(file->f_op, struct cxlflash_cfg, + cxl_fops); + struct device *dev = &cfg->dev->dev; + struct ctx_info *ctxi = NULL; + enum ctx_ctrl ctrl = CTX_CTRL_ERR_FALLBACK | CTX_CTRL_FILE; + int ctxid; + int rc = 0; + + ctxid = cxl_process_element(ctx); + if (unlikely(ctxid < 0)) { + dev_err(dev, "%s: Context %p was closed! (%d)\n", + __func__, ctx, ctxid); + rc = -EIO; + goto out; + } + + ctxi = get_context(cfg, ctxid, file, ctrl); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%d)\n", __func__, ctxid); + rc = -EIO; + goto out; + } + + dev_dbg(dev, "%s: mmap(%d) for context %d\n", + __func__, ctxi->lfd, ctxid); + + rc = cxl_fd_mmap(file, vma); + if (likely(!rc)) { + /* Insert ourself in the mmap fault handler path */ + ctxi->cxl_mmap_vmops = vma->vm_ops; + vma->vm_ops = &cxlflash_mmap_vmops; + } + +out: + if (likely(ctxi)) + put_context(ctxi); + return rc; +} + +const struct file_operations cxlflash_cxl_fops = { + .owner = THIS_MODULE, + .mmap = cxlflash_cxl_mmap, + .release = cxlflash_cxl_release, +}; + +/** + * cxlflash_mark_contexts_error() - move contexts to error state and list + * @cfg: Internal structure associated with the host. + * + * A context is only moved over to the error list when there are no outstanding + * references to it. This ensures that a running operation has completed. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_mark_contexts_error(struct cxlflash_cfg *cfg) +{ + int i, rc = 0; + struct ctx_info *ctxi = NULL; + + mutex_lock(&cfg->ctx_tbl_list_mutex); + + for (i = 0; i < MAX_CONTEXT; i++) { + ctxi = cfg->ctx_tbl[i]; + if (ctxi) { + mutex_lock(&ctxi->mutex); + cfg->ctx_tbl[i] = NULL; + list_add(&ctxi->list, &cfg->ctx_err_recovery); + ctxi->err_recovery_active = true; + ctxi->ctrl_map = NULL; + unmap_context(ctxi); + mutex_unlock(&ctxi->mutex); + } + } + + mutex_unlock(&cfg->ctx_tbl_list_mutex); + return rc; +} + +/* + * Dummy NULL fops + */ +static const struct file_operations null_fops = { + .owner = THIS_MODULE, +}; + +/** + * check_state() - checks and responds to the current adapter state + * @cfg: Internal structure associated with the host. + * + * This routine can block and should only be used on process context. + * It assumes that the caller is an ioctl thread and holding the ioctl + * read semaphore. This is temporarily let up across the wait to allow + * for draining actively running ioctls. Also note that when waking up + * from waiting in reset, the state is unknown and must be checked again + * before proceeding. + * + * Return: 0 on success, -errno on failure + */ +int check_state(struct cxlflash_cfg *cfg) +{ + struct device *dev = &cfg->dev->dev; + int rc = 0; + +retry: + switch (cfg->state) { + case STATE_RESET: + dev_dbg(dev, "%s: Reset state, going to wait...\n", __func__); + up_read(&cfg->ioctl_rwsem); + rc = wait_event_interruptible(cfg->reset_waitq, + cfg->state != STATE_RESET); + down_read(&cfg->ioctl_rwsem); + if (unlikely(rc)) + break; + goto retry; + case STATE_FAILTERM: + dev_dbg(dev, "%s: Failed/Terminating!\n", __func__); + rc = -ENODEV; + break; + default: + break; + } + + return rc; +} + +/** + * cxlflash_disk_attach() - attach a LUN to a context + * @sdev: SCSI device associated with LUN. + * @attach: Attach ioctl data structure. + * + * Creates a context and attaches LUN to it. A LUN can only be attached + * one time to a context (subsequent attaches for the same context/LUN pair + * are not supported). Additional LUNs can be attached to a context by + * specifying the 'reuse' flag defined in the cxlflash_ioctl.h header. + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_disk_attach(struct scsi_device *sdev, + struct dk_cxlflash_attach *attach) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct cxl_ioctl_start_work *work; + struct ctx_info *ctxi = NULL; + struct lun_access *lun_access = NULL; + int rc = 0; + u32 perms; + int ctxid = -1; + u64 rctxid = 0UL; + struct file *file; + + struct cxl_context *ctx; + + int fd = -1; + + if (attach->num_interrupts > 4) { + dev_dbg(dev, "%s: Cannot support this many interrupts %llu\n", + __func__, attach->num_interrupts); + rc = -EINVAL; + goto out; + } + + if (gli->max_lba == 0) { + dev_dbg(dev, "%s: No capacity info for this LUN (%016llX)\n", + __func__, lli->lun_id[sdev->channel]); + rc = read_cap16(sdev, lli); + if (rc) { + dev_err(dev, "%s: Invalid device! (%d)\n", + __func__, rc); + rc = -ENODEV; + goto out; + } + dev_dbg(dev, "%s: LBA = %016llX\n", __func__, gli->max_lba); + dev_dbg(dev, "%s: BLK_LEN = %08X\n", __func__, gli->blk_len); + } + + if (attach->hdr.flags & DK_CXLFLASH_ATTACH_REUSE_CONTEXT) { + rctxid = attach->context_id; + ctxi = get_context(cfg, rctxid, NULL, 0); + if (!ctxi) { + dev_dbg(dev, "%s: Bad context! (%016llX)\n", + __func__, rctxid); + rc = -EINVAL; + goto out; + } + + list_for_each_entry(lun_access, &ctxi->luns, list) + if (lun_access->lli == lli) { + dev_dbg(dev, "%s: Already attached!\n", + __func__); + rc = -EINVAL; + goto out; + } + } + + rc = scsi_device_get(sdev); + if (unlikely(rc)) { + dev_err(dev, "%s: Unable to get sdev reference!\n", __func__); + goto out; + } + + lun_access = kzalloc(sizeof(*lun_access), GFP_KERNEL); + if (unlikely(!lun_access)) { + dev_err(dev, "%s: Unable to allocate lun_access!\n", __func__); + rc = -ENOMEM; + goto err0; + } + + lun_access->lli = lli; + lun_access->sdev = sdev; + + /* Non-NULL context indicates reuse */ + if (ctxi) { + dev_dbg(dev, "%s: Reusing context for LUN! (%016llX)\n", + __func__, rctxid); + list_add(&lun_access->list, &ctxi->luns); + fd = ctxi->lfd; + goto out_attach; + } + + ctx = cxl_dev_context_init(cfg->dev); + if (unlikely(IS_ERR_OR_NULL(ctx))) { + dev_err(dev, "%s: Could not initialize context %p\n", + __func__, ctx); + rc = -ENODEV; + goto err1; + } + + ctxid = cxl_process_element(ctx); + if (unlikely((ctxid > MAX_CONTEXT) || (ctxid < 0))) { + dev_err(dev, "%s: ctxid (%d) invalid!\n", __func__, ctxid); + rc = -EPERM; + goto err2; + } + + file = cxl_get_fd(ctx, &cfg->cxl_fops, &fd); + if (unlikely(fd < 0)) { + rc = -ENODEV; + dev_err(dev, "%s: Could not get file descriptor\n", __func__); + goto err2; + } + + /* Translate read/write O_* flags from fcntl.h to AFU permission bits */ + perms = SISL_RHT_PERM(attach->hdr.flags + 1); + + ctxi = create_context(cfg, ctx, ctxid, fd, file, perms); + if (unlikely(!ctxi)) { + dev_err(dev, "%s: Failed to create context! (%d)\n", + __func__, ctxid); + goto err3; + } + + work = &ctxi->work; + work->num_interrupts = attach->num_interrupts; + work->flags = CXL_START_WORK_NUM_IRQS; + + rc = cxl_start_work(ctx, work); + if (unlikely(rc)) { + dev_dbg(dev, "%s: Could not start context rc=%d\n", + __func__, rc); + goto err4; + } + + rc = afu_attach(cfg, ctxi); + if (unlikely(rc)) { + dev_err(dev, "%s: Could not attach AFU rc %d\n", __func__, rc); + goto err5; + } + + /* + * No error paths after this point. Once the fd is installed it's + * visible to user space and can't be undone safely on this thread. + * There is no need to worry about a deadlock here because no one + * knows about us yet; we can be the only one holding our mutex. + */ + list_add(&lun_access->list, &ctxi->luns); + mutex_unlock(&ctxi->mutex); + mutex_lock(&cfg->ctx_tbl_list_mutex); + mutex_lock(&ctxi->mutex); + cfg->ctx_tbl[ctxid] = ctxi; + mutex_unlock(&cfg->ctx_tbl_list_mutex); + fd_install(fd, file); + +out_attach: + attach->hdr.return_flags = 0; + attach->context_id = ctxi->ctxid; + attach->block_size = gli->blk_len; + attach->mmio_size = sizeof(afu->afu_map->hosts[0].harea); + attach->last_lba = gli->max_lba; + attach->max_xfer = sdev->host->max_sectors * MAX_SECTOR_UNIT; + attach->max_xfer /= gli->blk_len; + +out: + attach->adap_fd = fd; + + if (ctxi) + put_context(ctxi); + + dev_dbg(dev, "%s: returning ctxid=%d fd=%d bs=%lld rc=%d llba=%lld\n", + __func__, ctxid, fd, attach->block_size, rc, attach->last_lba); + return rc; + +err5: + cxl_stop_context(ctx); +err4: + put_context(ctxi); + destroy_context(cfg, ctxi); + ctxi = NULL; +err3: + /* + * Here, we're overriding the fops with a dummy all-NULL fops because + * fput() calls the release fop, which will cause us to mistakenly + * call into the CXL code. Rather than try to add yet more complexity + * to that routine (cxlflash_cxl_release) we should try to fix the + * issue here. + */ + file->f_op = &null_fops; + fput(file); + put_unused_fd(fd); + fd = -1; +err2: + cxl_release_context(ctx); +err1: + kfree(lun_access); +err0: + scsi_device_put(sdev); + goto out; +} + +/** + * recover_context() - recovers a context in error + * @cfg: Internal structure associated with the host. + * @ctxi: Context to release. + * + * Restablishes the state for a context-in-error. + * + * Return: 0 on success, -errno on failure + */ +static int recover_context(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) +{ + struct device *dev = &cfg->dev->dev; + int rc = 0; + int old_fd, fd = -1; + int ctxid = -1; + struct file *file; + struct cxl_context *ctx; + struct afu *afu = cfg->afu; + + ctx = cxl_dev_context_init(cfg->dev); + if (unlikely(IS_ERR_OR_NULL(ctx))) { + dev_err(dev, "%s: Could not initialize context %p\n", + __func__, ctx); + rc = -ENODEV; + goto out; + } + + ctxid = cxl_process_element(ctx); + if (unlikely((ctxid > MAX_CONTEXT) || (ctxid < 0))) { + dev_err(dev, "%s: ctxid (%d) invalid!\n", __func__, ctxid); + rc = -EPERM; + goto err1; + } + + file = cxl_get_fd(ctx, &cfg->cxl_fops, &fd); + if (unlikely(fd < 0)) { + rc = -ENODEV; + dev_err(dev, "%s: Could not get file descriptor\n", __func__); + goto err1; + } + + rc = cxl_start_work(ctx, &ctxi->work); + if (unlikely(rc)) { + dev_dbg(dev, "%s: Could not start context rc=%d\n", + __func__, rc); + goto err2; + } + + /* Update with new MMIO area based on updated context id */ + ctxi->ctrl_map = &afu->afu_map->ctrls[ctxid].ctrl; + + rc = afu_attach(cfg, ctxi); + if (rc) { + dev_err(dev, "%s: Could not attach AFU rc %d\n", __func__, rc); + goto err3; + } + + /* + * No error paths after this point. Once the fd is installed it's + * visible to user space and can't be undone safely on this thread. + */ + old_fd = ctxi->lfd; + ctxi->ctxid = ENCODE_CTXID(ctxi, ctxid); + ctxi->lfd = fd; + ctxi->ctx = ctx; + ctxi->file = file; + + /* + * Put context back in table (note the reinit of the context list); + * we must first drop the context's mutex and then acquire it in + * order with the table/list mutex to avoid a deadlock - safe to do + * here because no one can find us at this moment in time. + */ + mutex_unlock(&ctxi->mutex); + mutex_lock(&cfg->ctx_tbl_list_mutex); + mutex_lock(&ctxi->mutex); + list_del_init(&ctxi->list); + cfg->ctx_tbl[ctxid] = ctxi; + mutex_unlock(&cfg->ctx_tbl_list_mutex); + fd_install(fd, file); + + /* Release the original adapter fd and associated CXL resources */ + sys_close(old_fd); +out: + dev_dbg(dev, "%s: returning ctxid=%d fd=%d rc=%d\n", + __func__, ctxid, fd, rc); + return rc; + +err3: + cxl_stop_context(ctx); +err2: + fput(file); + put_unused_fd(fd); +err1: + cxl_release_context(ctx); + goto out; +} + +/** + * cxlflash_afu_recover() - initiates AFU recovery + * @sdev: SCSI device associated with LUN. + * @recover: Recover ioctl data structure. + * + * Only a single recovery is allowed at a time to avoid exhausting CXL + * resources (leading to recovery failure) in the event that we're up + * against the maximum number of contexts limit. For similar reasons, + * a context recovery is retried if there are multiple recoveries taking + * place at the same time and the failure was due to CXL services being + * unable to keep up. + * + * Because a user can detect an error condition before the kernel, it is + * quite possible for this routine to act as the kernel's EEH detection + * source (MMIO read of mbox_r). Because of this, there is a window of + * time where an EEH might have been detected but not yet 'serviced' + * (callback invoked, causing the device to enter reset state). To avoid + * looping in this routine during that window, a 1 second sleep is in place + * between the time the MMIO failure is detected and the time a wait on the + * reset wait queue is attempted via check_state(). + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_afu_recover(struct scsi_device *sdev, + struct dk_cxlflash_recover_afu *recover) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct afu *afu = cfg->afu; + struct ctx_info *ctxi = NULL; + struct mutex *mutex = &cfg->ctx_recovery_mutex; + u64 ctxid = DECODE_CTXID(recover->context_id), + rctxid = recover->context_id; + long reg; + int lretry = 20; /* up to 2 seconds */ + int rc = 0; + + atomic_inc(&cfg->recovery_threads); + rc = mutex_lock_interruptible(mutex); + if (rc) + goto out; + + dev_dbg(dev, "%s: reason 0x%016llX rctxid=%016llX\n", + __func__, recover->reason, rctxid); + +retry: + /* Ensure that this process is attached to the context */ + ctxi = get_context(cfg, rctxid, lli, CTX_CTRL_ERR_FALLBACK); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%llu)\n", __func__, ctxid); + rc = -EINVAL; + goto out; + } + + if (ctxi->err_recovery_active) { +retry_recover: + rc = recover_context(cfg, ctxi); + if (unlikely(rc)) { + dev_err(dev, "%s: Recovery failed for context %llu (rc=%d)\n", + __func__, ctxid, rc); + if ((rc == -ENODEV) && + ((atomic_read(&cfg->recovery_threads) > 1) || + (lretry--))) { + dev_dbg(dev, "%s: Going to try again!\n", + __func__); + mutex_unlock(mutex); + msleep(100); + rc = mutex_lock_interruptible(mutex); + if (rc) + goto out; + goto retry_recover; + } + + goto out; + } + + ctxi->err_recovery_active = false; + recover->context_id = ctxi->ctxid; + recover->adap_fd = ctxi->lfd; + recover->mmio_size = sizeof(afu->afu_map->hosts[0].harea); + recover->hdr.return_flags |= + DK_CXLFLASH_RECOVER_AFU_CONTEXT_RESET; + goto out; + } + + /* Test if in error state */ + reg = readq_be(&afu->ctrl_map->mbox_r); + if (reg == -1) { + dev_dbg(dev, "%s: MMIO fail, wait for recovery.\n", __func__); + + /* + * Before checking the state, put back the context obtained with + * get_context() as it is no longer needed and sleep for a short + * period of time (see prolog notes). + */ + put_context(ctxi); + ctxi = NULL; + ssleep(1); + rc = check_state(cfg); + if (unlikely(rc)) + goto out; + goto retry; + } + + dev_dbg(dev, "%s: MMIO working, no recovery required!\n", __func__); +out: + if (likely(ctxi)) + put_context(ctxi); + mutex_unlock(mutex); + atomic_dec_if_positive(&cfg->recovery_threads); + return rc; +} + +/** + * process_sense() - evaluates and processes sense data + * @sdev: SCSI device associated with LUN. + * @verify: Verify ioctl data structure. + * + * Return: 0 on success, -errno on failure + */ +static int process_sense(struct scsi_device *sdev, + struct dk_cxlflash_verify *verify) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + u64 prev_lba = gli->max_lba; + struct scsi_sense_hdr sshdr = { 0 }; + int rc = 0; + + rc = scsi_normalize_sense((const u8 *)&verify->sense_data, + DK_CXLFLASH_VERIFY_SENSE_LEN, &sshdr); + if (!rc) { + dev_err(dev, "%s: Failed to normalize sense data!\n", __func__); + rc = -EINVAL; + goto out; + } + + switch (sshdr.sense_key) { + case NO_SENSE: + case RECOVERED_ERROR: + /* fall through */ + case NOT_READY: + break; + case UNIT_ATTENTION: + switch (sshdr.asc) { + case 0x29: /* Power on Reset or Device Reset */ + /* fall through */ + case 0x2A: /* Device settings/capacity changed */ + rc = read_cap16(sdev, lli); + if (rc) { + rc = -ENODEV; + break; + } + if (prev_lba != gli->max_lba) + dev_dbg(dev, "%s: Capacity changed old=%lld " + "new=%lld\n", __func__, prev_lba, + gli->max_lba); + break; + case 0x3F: /* Report LUNs changed, Rescan. */ + scsi_scan_host(cfg->host); + break; + default: + rc = -EIO; + break; + } + break; + default: + rc = -EIO; + break; + } +out: + dev_dbg(dev, "%s: sense_key %x asc %x ascq %x rc %d\n", __func__, + sshdr.sense_key, sshdr.asc, sshdr.ascq, rc); + return rc; +} + +/** + * cxlflash_disk_verify() - verifies a LUN is the same and handle size changes + * @sdev: SCSI device associated with LUN. + * @verify: Verify ioctl data structure. + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_disk_verify(struct scsi_device *sdev, + struct dk_cxlflash_verify *verify) +{ + int rc = 0; + struct ctx_info *ctxi = NULL; + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct sisl_rht_entry *rhte = NULL; + res_hndl_t rhndl = verify->rsrc_handle; + u64 ctxid = DECODE_CTXID(verify->context_id), + rctxid = verify->context_id; + u64 last_lba = 0; + + dev_dbg(dev, "%s: ctxid=%llu rhndl=%016llX, hint=%016llX, " + "flags=%016llX\n", __func__, ctxid, verify->rsrc_handle, + verify->hint, verify->hdr.flags); + + ctxi = get_context(cfg, rctxid, lli, 0); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%llu)\n", __func__, ctxid); + rc = -EINVAL; + goto out; + } + + rhte = get_rhte(ctxi, rhndl, lli); + if (unlikely(!rhte)) { + dev_dbg(dev, "%s: Bad resource handle! (%d)\n", + __func__, rhndl); + rc = -EINVAL; + goto out; + } + + /* + * Look at the hint/sense to see if it requires us to redrive + * inquiry (i.e. the Unit attention is due to the WWN changing). + */ + if (verify->hint & DK_CXLFLASH_VERIFY_HINT_SENSE) { + /* Can't hold mutex across process_sense/read_cap16, + * since we could have an intervening EEH event. + */ + ctxi->unavail = true; + mutex_unlock(&ctxi->mutex); + rc = process_sense(sdev, verify); + if (unlikely(rc)) { + dev_err(dev, "%s: Failed to validate sense data (%d)\n", + __func__, rc); + mutex_lock(&ctxi->mutex); + ctxi->unavail = false; + goto out; + } + mutex_lock(&ctxi->mutex); + ctxi->unavail = false; + } + + switch (gli->mode) { + case MODE_PHYSICAL: + last_lba = gli->max_lba; + break; + case MODE_VIRTUAL: + /* Cast lxt_cnt to u64 for multiply to be treated as 64bit op */ + last_lba = ((u64)rhte->lxt_cnt * MC_CHUNK_SIZE * gli->blk_len); + last_lba /= CXLFLASH_BLOCK_SIZE; + last_lba--; + break; + default: + WARN(1, "Unsupported LUN mode!"); + } + + verify->last_lba = last_lba; + +out: + if (likely(ctxi)) + put_context(ctxi); + dev_dbg(dev, "%s: returning rc=%d llba=%llX\n", + __func__, rc, verify->last_lba); + return rc; +} + +/** + * decode_ioctl() - translates an encoded ioctl to an easily identifiable string + * @cmd: The ioctl command to decode. + * + * Return: A string identifying the decoded ioctl. + */ +static char *decode_ioctl(int cmd) +{ + switch (cmd) { + case DK_CXLFLASH_ATTACH: + return __stringify_1(DK_CXLFLASH_ATTACH); + case DK_CXLFLASH_USER_DIRECT: + return __stringify_1(DK_CXLFLASH_USER_DIRECT); + case DK_CXLFLASH_USER_VIRTUAL: + return __stringify_1(DK_CXLFLASH_USER_VIRTUAL); + case DK_CXLFLASH_VLUN_RESIZE: + return __stringify_1(DK_CXLFLASH_VLUN_RESIZE); + case DK_CXLFLASH_RELEASE: + return __stringify_1(DK_CXLFLASH_RELEASE); + case DK_CXLFLASH_DETACH: + return __stringify_1(DK_CXLFLASH_DETACH); + case DK_CXLFLASH_VERIFY: + return __stringify_1(DK_CXLFLASH_VERIFY); + case DK_CXLFLASH_VLUN_CLONE: + return __stringify_1(DK_CXLFLASH_VLUN_CLONE); + case DK_CXLFLASH_RECOVER_AFU: + return __stringify_1(DK_CXLFLASH_RECOVER_AFU); + case DK_CXLFLASH_MANAGE_LUN: + return __stringify_1(DK_CXLFLASH_MANAGE_LUN); + } + + return "UNKNOWN"; +} + +/** + * cxlflash_disk_direct_open() - opens a direct (physical) disk + * @sdev: SCSI device associated with LUN. + * @arg: UDirect ioctl data structure. + * + * On successful return, the user is informed of the resource handle + * to be used to identify the direct lun and the size (in blocks) of + * the direct lun in last LBA format. + * + * Return: 0 on success, -errno on failure + */ +static int cxlflash_disk_direct_open(struct scsi_device *sdev, void *arg) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + + struct dk_cxlflash_udirect *pphys = (struct dk_cxlflash_udirect *)arg; + + u64 ctxid = DECODE_CTXID(pphys->context_id), + rctxid = pphys->context_id; + u64 lun_size = 0; + u64 last_lba = 0; + u64 rsrc_handle = -1; + u32 port = CHAN2PORT(sdev->channel); + + int rc = 0; + + struct ctx_info *ctxi = NULL; + struct sisl_rht_entry *rhte = NULL; + + pr_debug("%s: ctxid=%llu ls=0x%llx\n", __func__, ctxid, lun_size); + + rc = cxlflash_lun_attach(gli, MODE_PHYSICAL, false); + if (unlikely(rc)) { + dev_dbg(dev, "%s: Failed to attach to LUN! (PHYSICAL)\n", + __func__); + goto out; + } + + ctxi = get_context(cfg, rctxid, lli, 0); + if (unlikely(!ctxi)) { + dev_dbg(dev, "%s: Bad context! (%llu)\n", __func__, ctxid); + rc = -EINVAL; + goto err1; + } + + rhte = rhte_checkout(ctxi, lli); + if (unlikely(!rhte)) { + dev_dbg(dev, "%s: too many opens for this context\n", __func__); + rc = -EMFILE; /* too many opens */ + goto err1; + } + + rsrc_handle = (rhte - ctxi->rht_start); + + rht_format1(rhte, lli->lun_id[sdev->channel], ctxi->rht_perms, port); + cxlflash_afu_sync(afu, ctxid, rsrc_handle, AFU_LW_SYNC); + + last_lba = gli->max_lba; + pphys->hdr.return_flags = 0; + pphys->last_lba = last_lba; + pphys->rsrc_handle = rsrc_handle; + +out: + if (likely(ctxi)) + put_context(ctxi); + dev_dbg(dev, "%s: returning handle 0x%llx rc=%d llba %lld\n", + __func__, rsrc_handle, rc, last_lba); + return rc; + +err1: + cxlflash_lun_detach(gli); + goto out; +} + +/** + * ioctl_common() - common IOCTL handler for driver + * @sdev: SCSI device associated with LUN. + * @cmd: IOCTL command. + * + * Handles common fencing operations that are valid for multiple ioctls. Always + * allow through ioctls that are cleanup oriented in nature, even when operating + * in a failed/terminating state. + * + * Return: 0 on success, -errno on failure + */ +static int ioctl_common(struct scsi_device *sdev, int cmd) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + int rc = 0; + + if (unlikely(!lli)) { + dev_dbg(dev, "%s: Unknown LUN\n", __func__); + rc = -EINVAL; + goto out; + } + + rc = check_state(cfg); + if (unlikely(rc) && (cfg->state == STATE_FAILTERM)) { + switch (cmd) { + case DK_CXLFLASH_VLUN_RESIZE: + case DK_CXLFLASH_RELEASE: + case DK_CXLFLASH_DETACH: + dev_dbg(dev, "%s: Command override! (%d)\n", + __func__, rc); + rc = 0; + break; + } + } +out: + return rc; +} + +/** + * cxlflash_ioctl() - IOCTL handler for driver + * @sdev: SCSI device associated with LUN. + * @cmd: IOCTL command. + * @arg: Userspace ioctl data structure. + * + * A read/write semaphore is used to implement a 'drain' of currently + * running ioctls. The read semaphore is taken at the beginning of each + * ioctl thread and released upon concluding execution. Additionally the + * semaphore should be released and then reacquired in any ioctl execution + * path which will wait for an event to occur that is outside the scope of + * the ioctl (i.e. an adapter reset). To drain the ioctls currently running, + * a thread simply needs to acquire the write semaphore. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + typedef int (*sioctl) (struct scsi_device *, void *); + + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct afu *afu = cfg->afu; + struct dk_cxlflash_hdr *hdr; + char buf[sizeof(union cxlflash_ioctls)]; + size_t size = 0; + bool known_ioctl = false; + int idx; + int rc = 0; + struct Scsi_Host *shost = sdev->host; + sioctl do_ioctl = NULL; + + static const struct { + size_t size; + sioctl ioctl; + } ioctl_tbl[] = { /* NOTE: order matters here */ + {sizeof(struct dk_cxlflash_attach), (sioctl)cxlflash_disk_attach}, + {sizeof(struct dk_cxlflash_udirect), cxlflash_disk_direct_open}, + {sizeof(struct dk_cxlflash_release), (sioctl)cxlflash_disk_release}, + {sizeof(struct dk_cxlflash_detach), (sioctl)cxlflash_disk_detach}, + {sizeof(struct dk_cxlflash_verify), (sioctl)cxlflash_disk_verify}, + {sizeof(struct dk_cxlflash_recover_afu), (sioctl)cxlflash_afu_recover}, + {sizeof(struct dk_cxlflash_manage_lun), (sioctl)cxlflash_manage_lun}, + {sizeof(struct dk_cxlflash_uvirtual), cxlflash_disk_virtual_open}, + {sizeof(struct dk_cxlflash_resize), (sioctl)cxlflash_vlun_resize}, + {sizeof(struct dk_cxlflash_clone), (sioctl)cxlflash_disk_clone}, + }; + + /* Hold read semaphore so we can drain if needed */ + down_read(&cfg->ioctl_rwsem); + + /* Restrict command set to physical support only for internal LUN */ + if (afu->internal_lun) + switch (cmd) { + case DK_CXLFLASH_RELEASE: + case DK_CXLFLASH_USER_VIRTUAL: + case DK_CXLFLASH_VLUN_RESIZE: + case DK_CXLFLASH_VLUN_CLONE: + dev_dbg(dev, "%s: %s not supported for lun_mode=%d\n", + __func__, decode_ioctl(cmd), afu->internal_lun); + rc = -EINVAL; + goto cxlflash_ioctl_exit; + } + + switch (cmd) { + case DK_CXLFLASH_ATTACH: + case DK_CXLFLASH_USER_DIRECT: + case DK_CXLFLASH_RELEASE: + case DK_CXLFLASH_DETACH: + case DK_CXLFLASH_VERIFY: + case DK_CXLFLASH_RECOVER_AFU: + case DK_CXLFLASH_USER_VIRTUAL: + case DK_CXLFLASH_VLUN_RESIZE: + case DK_CXLFLASH_VLUN_CLONE: + dev_dbg(dev, "%s: %s (%08X) on dev(%d/%d/%d/%llu)\n", + __func__, decode_ioctl(cmd), cmd, shost->host_no, + sdev->channel, sdev->id, sdev->lun); + rc = ioctl_common(sdev, cmd); + if (unlikely(rc)) + goto cxlflash_ioctl_exit; + + /* fall through */ + + case DK_CXLFLASH_MANAGE_LUN: + known_ioctl = true; + idx = _IOC_NR(cmd) - _IOC_NR(DK_CXLFLASH_ATTACH); + size = ioctl_tbl[idx].size; + do_ioctl = ioctl_tbl[idx].ioctl; + + if (likely(do_ioctl)) + break; + + /* fall through */ + default: + rc = -EINVAL; + goto cxlflash_ioctl_exit; + } + + if (unlikely(copy_from_user(&buf, arg, size))) { + dev_err(dev, "%s: copy_from_user() fail! " + "size=%lu cmd=%d (%s) arg=%p\n", + __func__, size, cmd, decode_ioctl(cmd), arg); + rc = -EFAULT; + goto cxlflash_ioctl_exit; + } + + hdr = (struct dk_cxlflash_hdr *)&buf; + if (hdr->version != DK_CXLFLASH_VERSION_0) { + dev_dbg(dev, "%s: Version %u not supported for %s\n", + __func__, hdr->version, decode_ioctl(cmd)); + rc = -EINVAL; + goto cxlflash_ioctl_exit; + } + + if (hdr->rsvd[0] || hdr->rsvd[1] || hdr->rsvd[2] || hdr->return_flags) { + dev_dbg(dev, "%s: Reserved/rflags populated!\n", __func__); + rc = -EINVAL; + goto cxlflash_ioctl_exit; + } + + rc = do_ioctl(sdev, (void *)&buf); + if (likely(!rc)) + if (unlikely(copy_to_user(arg, &buf, size))) { + dev_err(dev, "%s: copy_to_user() fail! " + "size=%lu cmd=%d (%s) arg=%p\n", + __func__, size, cmd, decode_ioctl(cmd), arg); + rc = -EFAULT; + } + + /* fall through to exit */ + +cxlflash_ioctl_exit: + up_read(&cfg->ioctl_rwsem); + if (unlikely(rc && known_ioctl)) + dev_err(dev, "%s: ioctl %s (%08X) on dev(%d/%d/%d/%llu) " + "returned rc %d\n", __func__, + decode_ioctl(cmd), cmd, shost->host_no, + sdev->channel, sdev->id, sdev->lun, rc); + else + dev_dbg(dev, "%s: ioctl %s (%08X) on dev(%d/%d/%d/%llu) " + "returned rc %d\n", __func__, decode_ioctl(cmd), + cmd, shost->host_no, sdev->channel, sdev->id, + sdev->lun, rc); + return rc; +} diff --git a/kernel/drivers/scsi/cxlflash/superpipe.h b/kernel/drivers/scsi/cxlflash/superpipe.h new file mode 100644 index 000000000..bede574bc --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/superpipe.h @@ -0,0 +1,151 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _CXLFLASH_SUPERPIPE_H +#define _CXLFLASH_SUPERPIPE_H + +extern struct cxlflash_global global; + +/* + * Terminology: use afu (and not adapter) to refer to the HW. + * Adapter is the entire slot and includes PSL out of which + * only the AFU is visible to user space. + */ + +/* Chunk size parms: note sislite minimum chunk size is + 0x10000 LBAs corresponding to a NMASK or 16. +*/ +#define MC_CHUNK_SIZE (1 << MC_RHT_NMASK) /* in LBAs */ + +#define CMD_TIMEOUT 30 /* 30 secs */ +#define CMD_RETRIES 5 /* 5 retries for scsi_execute */ + +#define MAX_SECTOR_UNIT 512 /* max_sector is in 512 byte multiples */ + +#define CHAN2PORT(_x) ((_x) + 1) +#define PORT2CHAN(_x) ((_x) - 1) + +enum lun_mode { + MODE_NONE = 0, + MODE_VIRTUAL, + MODE_PHYSICAL +}; + +/* Global (entire driver, spans adapters) lun_info structure */ +struct glun_info { + u64 max_lba; /* from read cap(16) */ + u32 blk_len; /* from read cap(16) */ + enum lun_mode mode; /* NONE, VIRTUAL, PHYSICAL */ + int users; /* Number of users w/ references to LUN */ + + u8 wwid[16]; + + struct mutex mutex; + + struct blka blka; + struct list_head list; +}; + +/* Local (per-adapter) lun_info structure */ +struct llun_info { + u64 lun_id[CXLFLASH_NUM_FC_PORTS]; /* from REPORT_LUNS */ + u32 lun_index; /* Index in the LUN table */ + u32 host_no; /* host_no from Scsi_host */ + u32 port_sel; /* What port to use for this LUN */ + bool in_table; /* Whether a LUN table entry was created */ + + u8 wwid[16]; /* Keep a duplicate copy here? */ + + struct glun_info *parent; /* Pointer to entry in global LUN structure */ + struct scsi_device *sdev; + struct list_head list; +}; + +struct lun_access { + struct llun_info *lli; + struct scsi_device *sdev; + struct list_head list; +}; + +enum ctx_ctrl { + CTX_CTRL_CLONE = (1 << 1), + CTX_CTRL_ERR = (1 << 2), + CTX_CTRL_ERR_FALLBACK = (1 << 3), + CTX_CTRL_NOPID = (1 << 4), + CTX_CTRL_FILE = (1 << 5) +}; + +#define ENCODE_CTXID(_ctx, _id) (((((u64)_ctx) & 0xFFFFFFFF0ULL) << 28) | _id) +#define DECODE_CTXID(_val) (_val & 0xFFFFFFFF) + +struct ctx_info { + struct sisl_ctrl_map __iomem *ctrl_map; /* initialized at startup */ + struct sisl_rht_entry *rht_start; /* 1 page (req'd for alignment), + alloc/free on attach/detach */ + u32 rht_out; /* Number of checked out RHT entries */ + u32 rht_perms; /* User-defined permissions for RHT entries */ + struct llun_info **rht_lun; /* Mapping of RHT entries to LUNs */ + u8 *rht_needs_ws; /* User-desired write-same function per RHTE */ + + struct cxl_ioctl_start_work work; + u64 ctxid; + int lfd; + pid_t pid; + bool unavail; + bool err_recovery_active; + struct mutex mutex; /* Context protection */ + struct cxl_context *ctx; + struct list_head luns; /* LUNs attached to this context */ + const struct vm_operations_struct *cxl_mmap_vmops; + struct file *file; + struct list_head list; /* Link contexts in error recovery */ +}; + +struct cxlflash_global { + struct mutex mutex; + struct list_head gluns;/* list of glun_info structs */ + struct page *err_page; /* One page of all 0xF for error notification */ +}; + +int cxlflash_vlun_resize(struct scsi_device *, struct dk_cxlflash_resize *); +int _cxlflash_vlun_resize(struct scsi_device *, struct ctx_info *, + struct dk_cxlflash_resize *); + +int cxlflash_disk_release(struct scsi_device *, struct dk_cxlflash_release *); +int _cxlflash_disk_release(struct scsi_device *, struct ctx_info *, + struct dk_cxlflash_release *); + +int cxlflash_disk_clone(struct scsi_device *, struct dk_cxlflash_clone *); + +int cxlflash_disk_virtual_open(struct scsi_device *, void *); + +int cxlflash_lun_attach(struct glun_info *, enum lun_mode, bool); +void cxlflash_lun_detach(struct glun_info *); + +struct ctx_info *get_context(struct cxlflash_cfg *, u64, void *, enum ctx_ctrl); +void put_context(struct ctx_info *); + +struct sisl_rht_entry *get_rhte(struct ctx_info *, res_hndl_t, + struct llun_info *); + +struct sisl_rht_entry *rhte_checkout(struct ctx_info *, struct llun_info *); +void rhte_checkin(struct ctx_info *, struct sisl_rht_entry *); + +void cxlflash_ba_terminate(struct ba_lun *); + +int cxlflash_manage_lun(struct scsi_device *, struct dk_cxlflash_manage_lun *); + +int check_state(struct cxlflash_cfg *); + +#endif /* ifndef _CXLFLASH_SUPERPIPE_H */ diff --git a/kernel/drivers/scsi/cxlflash/vlun.c b/kernel/drivers/scsi/cxlflash/vlun.c new file mode 100644 index 000000000..a53f583e2 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/vlun.c @@ -0,0 +1,1273 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "sislite.h" +#include "common.h" +#include "vlun.h" +#include "superpipe.h" + +/** + * marshal_virt_to_resize() - translate uvirtual to resize structure + * @virt: Source structure from which to translate/copy. + * @resize: Destination structure for the translate/copy. + */ +static void marshal_virt_to_resize(struct dk_cxlflash_uvirtual *virt, + struct dk_cxlflash_resize *resize) +{ + resize->hdr = virt->hdr; + resize->context_id = virt->context_id; + resize->rsrc_handle = virt->rsrc_handle; + resize->req_size = virt->lun_size; + resize->last_lba = virt->last_lba; +} + +/** + * marshal_clone_to_rele() - translate clone to release structure + * @clone: Source structure from which to translate/copy. + * @rele: Destination structure for the translate/copy. + */ +static void marshal_clone_to_rele(struct dk_cxlflash_clone *clone, + struct dk_cxlflash_release *release) +{ + release->hdr = clone->hdr; + release->context_id = clone->context_id_dst; +} + +/** + * ba_init() - initializes a block allocator + * @ba_lun: Block allocator to initialize. + * + * Return: 0 on success, -errno on failure + */ +static int ba_init(struct ba_lun *ba_lun) +{ + struct ba_lun_info *bali = NULL; + int lun_size_au = 0, i = 0; + int last_word_underflow = 0; + u64 *lam; + + pr_debug("%s: Initializing LUN: lun_id = %llX, " + "ba_lun->lsize = %lX, ba_lun->au_size = %lX\n", + __func__, ba_lun->lun_id, ba_lun->lsize, ba_lun->au_size); + + /* Calculate bit map size */ + lun_size_au = ba_lun->lsize / ba_lun->au_size; + if (lun_size_au == 0) { + pr_debug("%s: Requested LUN size of 0!\n", __func__); + return -EINVAL; + } + + /* Allocate lun information container */ + bali = kzalloc(sizeof(struct ba_lun_info), GFP_KERNEL); + if (unlikely(!bali)) { + pr_err("%s: Failed to allocate lun_info for lun_id %llX\n", + __func__, ba_lun->lun_id); + return -ENOMEM; + } + + bali->total_aus = lun_size_au; + bali->lun_bmap_size = lun_size_au / BITS_PER_LONG; + + if (lun_size_au % BITS_PER_LONG) + bali->lun_bmap_size++; + + /* Allocate bitmap space */ + bali->lun_alloc_map = kzalloc((bali->lun_bmap_size * sizeof(u64)), + GFP_KERNEL); + if (unlikely(!bali->lun_alloc_map)) { + pr_err("%s: Failed to allocate lun allocation map: " + "lun_id = %llX\n", __func__, ba_lun->lun_id); + kfree(bali); + return -ENOMEM; + } + + /* Initialize the bit map size and set all bits to '1' */ + bali->free_aun_cnt = lun_size_au; + + for (i = 0; i < bali->lun_bmap_size; i++) + bali->lun_alloc_map[i] = 0xFFFFFFFFFFFFFFFFULL; + + /* If the last word not fully utilized, mark extra bits as allocated */ + last_word_underflow = (bali->lun_bmap_size * BITS_PER_LONG); + last_word_underflow -= bali->free_aun_cnt; + if (last_word_underflow > 0) { + lam = &bali->lun_alloc_map[bali->lun_bmap_size - 1]; + for (i = (HIBIT - last_word_underflow + 1); + i < BITS_PER_LONG; + i++) + clear_bit(i, (ulong *)lam); + } + + /* Initialize high elevator index, low/curr already at 0 from kzalloc */ + bali->free_high_idx = bali->lun_bmap_size; + + /* Allocate clone map */ + bali->aun_clone_map = kzalloc((bali->total_aus * sizeof(u8)), + GFP_KERNEL); + if (unlikely(!bali->aun_clone_map)) { + pr_err("%s: Failed to allocate clone map: lun_id = %llX\n", + __func__, ba_lun->lun_id); + kfree(bali->lun_alloc_map); + kfree(bali); + return -ENOMEM; + } + + /* Pass the allocated LUN info as a handle to the user */ + ba_lun->ba_lun_handle = bali; + + pr_debug("%s: Successfully initialized the LUN: " + "lun_id = %llX, bitmap size = %X, free_aun_cnt = %llX\n", + __func__, ba_lun->lun_id, bali->lun_bmap_size, + bali->free_aun_cnt); + return 0; +} + +/** + * find_free_range() - locates a free bit within the block allocator + * @low: First word in block allocator to start search. + * @high: Last word in block allocator to search. + * @bali: LUN information structure owning the block allocator to search. + * @bit_word: Passes back the word in the block allocator owning the free bit. + * + * Return: The bit position within the passed back word, -1 on failure + */ +static int find_free_range(u32 low, + u32 high, + struct ba_lun_info *bali, int *bit_word) +{ + int i; + u64 bit_pos = -1; + ulong *lam, num_bits; + + for (i = low; i < high; i++) + if (bali->lun_alloc_map[i] != 0) { + lam = (ulong *)&bali->lun_alloc_map[i]; + num_bits = (sizeof(*lam) * BITS_PER_BYTE); + bit_pos = find_first_bit(lam, num_bits); + + pr_devel("%s: Found free bit %llX in LUN " + "map entry %llX at bitmap index = %X\n", + __func__, bit_pos, bali->lun_alloc_map[i], + i); + + *bit_word = i; + bali->free_aun_cnt--; + clear_bit(bit_pos, lam); + break; + } + + return bit_pos; +} + +/** + * ba_alloc() - allocates a block from the block allocator + * @ba_lun: Block allocator from which to allocate a block. + * + * Return: The allocated block, -1 on failure + */ +static u64 ba_alloc(struct ba_lun *ba_lun) +{ + u64 bit_pos = -1; + int bit_word = 0; + struct ba_lun_info *bali = NULL; + + bali = ba_lun->ba_lun_handle; + + pr_debug("%s: Received block allocation request: " + "lun_id = %llX, free_aun_cnt = %llX\n", + __func__, ba_lun->lun_id, bali->free_aun_cnt); + + if (bali->free_aun_cnt == 0) { + pr_debug("%s: No space left on LUN: lun_id = %llX\n", + __func__, ba_lun->lun_id); + return -1ULL; + } + + /* Search to find a free entry, curr->high then low->curr */ + bit_pos = find_free_range(bali->free_curr_idx, + bali->free_high_idx, bali, &bit_word); + if (bit_pos == -1) { + bit_pos = find_free_range(bali->free_low_idx, + bali->free_curr_idx, + bali, &bit_word); + if (bit_pos == -1) { + pr_debug("%s: Could not find an allocation unit on LUN:" + " lun_id = %llX\n", __func__, ba_lun->lun_id); + return -1ULL; + } + } + + /* Update the free_curr_idx */ + if (bit_pos == HIBIT) + bali->free_curr_idx = bit_word + 1; + else + bali->free_curr_idx = bit_word; + + pr_debug("%s: Allocating AU number %llX, on lun_id %llX, " + "free_aun_cnt = %llX\n", __func__, + ((bit_word * BITS_PER_LONG) + bit_pos), ba_lun->lun_id, + bali->free_aun_cnt); + + return (u64) ((bit_word * BITS_PER_LONG) + bit_pos); +} + +/** + * validate_alloc() - validates the specified block has been allocated + * @ba_lun_info: LUN info owning the block allocator. + * @aun: Block to validate. + * + * Return: 0 on success, -1 on failure + */ +static int validate_alloc(struct ba_lun_info *bali, u64 aun) +{ + int idx = 0, bit_pos = 0; + + idx = aun / BITS_PER_LONG; + bit_pos = aun % BITS_PER_LONG; + + if (test_bit(bit_pos, (ulong *)&bali->lun_alloc_map[idx])) + return -1; + + return 0; +} + +/** + * ba_free() - frees a block from the block allocator + * @ba_lun: Block allocator from which to allocate a block. + * @to_free: Block to free. + * + * Return: 0 on success, -1 on failure + */ +static int ba_free(struct ba_lun *ba_lun, u64 to_free) +{ + int idx = 0, bit_pos = 0; + struct ba_lun_info *bali = NULL; + + bali = ba_lun->ba_lun_handle; + + if (validate_alloc(bali, to_free)) { + pr_debug("%s: The AUN %llX is not allocated on lun_id %llX\n", + __func__, to_free, ba_lun->lun_id); + return -1; + } + + pr_debug("%s: Received a request to free AU %llX on lun_id %llX, " + "free_aun_cnt = %llX\n", __func__, to_free, ba_lun->lun_id, + bali->free_aun_cnt); + + if (bali->aun_clone_map[to_free] > 0) { + pr_debug("%s: AUN %llX on lun_id %llX has been cloned. Clone " + "count = %X\n", __func__, to_free, ba_lun->lun_id, + bali->aun_clone_map[to_free]); + bali->aun_clone_map[to_free]--; + return 0; + } + + idx = to_free / BITS_PER_LONG; + bit_pos = to_free % BITS_PER_LONG; + + set_bit(bit_pos, (ulong *)&bali->lun_alloc_map[idx]); + bali->free_aun_cnt++; + + if (idx < bali->free_low_idx) + bali->free_low_idx = idx; + else if (idx > bali->free_high_idx) + bali->free_high_idx = idx; + + pr_debug("%s: Successfully freed AU at bit_pos %X, bit map index %X on " + "lun_id %llX, free_aun_cnt = %llX\n", __func__, bit_pos, idx, + ba_lun->lun_id, bali->free_aun_cnt); + + return 0; +} + +/** + * ba_clone() - Clone a chunk of the block allocation table + * @ba_lun: Block allocator from which to allocate a block. + * @to_free: Block to free. + * + * Return: 0 on success, -1 on failure + */ +static int ba_clone(struct ba_lun *ba_lun, u64 to_clone) +{ + struct ba_lun_info *bali = ba_lun->ba_lun_handle; + + if (validate_alloc(bali, to_clone)) { + pr_debug("%s: AUN %llX is not allocated on lun_id %llX\n", + __func__, to_clone, ba_lun->lun_id); + return -1; + } + + pr_debug("%s: Received a request to clone AUN %llX on lun_id %llX\n", + __func__, to_clone, ba_lun->lun_id); + + if (bali->aun_clone_map[to_clone] == MAX_AUN_CLONE_CNT) { + pr_debug("%s: AUN %llX on lun_id %llX hit max clones already\n", + __func__, to_clone, ba_lun->lun_id); + return -1; + } + + bali->aun_clone_map[to_clone]++; + + return 0; +} + +/** + * ba_space() - returns the amount of free space left in the block allocator + * @ba_lun: Block allocator. + * + * Return: Amount of free space in block allocator + */ +static u64 ba_space(struct ba_lun *ba_lun) +{ + struct ba_lun_info *bali = ba_lun->ba_lun_handle; + + return bali->free_aun_cnt; +} + +/** + * cxlflash_ba_terminate() - frees resources associated with the block allocator + * @ba_lun: Block allocator. + * + * Safe to call in a partially allocated state. + */ +void cxlflash_ba_terminate(struct ba_lun *ba_lun) +{ + struct ba_lun_info *bali = ba_lun->ba_lun_handle; + + if (bali) { + kfree(bali->aun_clone_map); + kfree(bali->lun_alloc_map); + kfree(bali); + ba_lun->ba_lun_handle = NULL; + } +} + +/** + * init_vlun() - initializes a LUN for virtual use + * @lun_info: LUN information structure that owns the block allocator. + * + * Return: 0 on success, -errno on failure + */ +static int init_vlun(struct llun_info *lli) +{ + int rc = 0; + struct glun_info *gli = lli->parent; + struct blka *blka = &gli->blka; + + memset(blka, 0, sizeof(*blka)); + mutex_init(&blka->mutex); + + /* LUN IDs are unique per port, save the index instead */ + blka->ba_lun.lun_id = lli->lun_index; + blka->ba_lun.lsize = gli->max_lba + 1; + blka->ba_lun.lba_size = gli->blk_len; + + blka->ba_lun.au_size = MC_CHUNK_SIZE; + blka->nchunk = blka->ba_lun.lsize / MC_CHUNK_SIZE; + + rc = ba_init(&blka->ba_lun); + if (unlikely(rc)) + pr_debug("%s: cannot init block_alloc, rc=%d\n", __func__, rc); + + pr_debug("%s: returning rc=%d lli=%p\n", __func__, rc, lli); + return rc; +} + +/** + * write_same16() - sends a SCSI WRITE_SAME16 (0) command to specified LUN + * @sdev: SCSI device associated with LUN. + * @lba: Logical block address to start write same. + * @nblks: Number of logical blocks to write same. + * + * The SCSI WRITE_SAME16 can take quite a while to complete. Should an EEH occur + * while in scsi_execute(), the EEH handler will attempt to recover. As part of + * the recovery, the handler drains all currently running ioctls, waiting until + * they have completed before proceeding with a reset. As this routine is used + * on the ioctl path, this can create a condition where the EEH handler becomes + * stuck, infinitely waiting for this ioctl thread. To avoid this behavior, + * temporarily unmark this thread as an ioctl thread by releasing the ioctl read + * semaphore. This will allow the EEH handler to proceed with a recovery while + * this thread is still running. Once the scsi_execute() returns, reacquire the + * ioctl read semaphore and check the adapter state in case it changed while + * inside of scsi_execute(). The state check will wait if the adapter is still + * being recovered or return a failure if the recovery failed. In the event that + * the adapter reset failed, simply return the failure as the ioctl would be + * unable to continue. + * + * Note that the above puts a requirement on this routine to only be called on + * an ioctl thread. + * + * Return: 0 on success, -errno on failure + */ +static int write_same16(struct scsi_device *sdev, + u64 lba, + u32 nblks) +{ + u8 *cmd_buf = NULL; + u8 *scsi_cmd = NULL; + u8 *sense_buf = NULL; + int rc = 0; + int result = 0; + int ws_limit = SISLITE_MAX_WS_BLOCKS; + u64 offset = lba; + int left = nblks; + u32 to = sdev->request_queue->rq_timeout; + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + + cmd_buf = kzalloc(CMD_BUFSIZE, GFP_KERNEL); + scsi_cmd = kzalloc(MAX_COMMAND_SIZE, GFP_KERNEL); + sense_buf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + if (unlikely(!cmd_buf || !scsi_cmd || !sense_buf)) { + rc = -ENOMEM; + goto out; + } + + while (left > 0) { + + scsi_cmd[0] = WRITE_SAME_16; + put_unaligned_be64(offset, &scsi_cmd[2]); + put_unaligned_be32(ws_limit < left ? ws_limit : left, + &scsi_cmd[10]); + + /* Drop the ioctl read semahpore across lengthy call */ + up_read(&cfg->ioctl_rwsem); + result = scsi_execute(sdev, scsi_cmd, DMA_TO_DEVICE, cmd_buf, + CMD_BUFSIZE, sense_buf, to, CMD_RETRIES, + 0, NULL); + down_read(&cfg->ioctl_rwsem); + rc = check_state(cfg); + if (rc) { + dev_err(dev, "%s: Failed state! result=0x08%X\n", + __func__, result); + rc = -ENODEV; + goto out; + } + + if (result) { + dev_err_ratelimited(dev, "%s: command failed for " + "offset %lld result=0x%x\n", + __func__, offset, result); + rc = -EIO; + goto out; + } + left -= ws_limit; + offset += ws_limit; + } + +out: + kfree(cmd_buf); + kfree(scsi_cmd); + kfree(sense_buf); + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * grow_lxt() - expands the translation table associated with the specified RHTE + * @afu: AFU associated with the host. + * @sdev: SCSI device associated with LUN. + * @ctxid: Context ID of context owning the RHTE. + * @rhndl: Resource handle associated with the RHTE. + * @rhte: Resource handle entry (RHTE). + * @new_size: Number of translation entries associated with RHTE. + * + * By design, this routine employs a 'best attempt' allocation and will + * truncate the requested size down if there is not sufficient space in + * the block allocator to satisfy the request but there does exist some + * amount of space. The user is made aware of this by returning the size + * allocated. + * + * Return: 0 on success, -errno on failure + */ +static int grow_lxt(struct afu *afu, + struct scsi_device *sdev, + ctx_hndl_t ctxid, + res_hndl_t rhndl, + struct sisl_rht_entry *rhte, + u64 *new_size) +{ + struct sisl_lxt_entry *lxt = NULL, *lxt_old = NULL; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct blka *blka = &gli->blka; + u32 av_size; + u32 ngrps, ngrps_old; + u64 aun; /* chunk# allocated by block allocator */ + u64 delta = *new_size - rhte->lxt_cnt; + u64 my_new_size; + int i, rc = 0; + + /* + * Check what is available in the block allocator before re-allocating + * LXT array. This is done up front under the mutex which must not be + * released until after allocation is complete. + */ + mutex_lock(&blka->mutex); + av_size = ba_space(&blka->ba_lun); + if (unlikely(av_size <= 0)) { + pr_debug("%s: ba_space error: av_size %d\n", __func__, av_size); + mutex_unlock(&blka->mutex); + rc = -ENOSPC; + goto out; + } + + if (av_size < delta) + delta = av_size; + + lxt_old = rhte->lxt_start; + ngrps_old = LXT_NUM_GROUPS(rhte->lxt_cnt); + ngrps = LXT_NUM_GROUPS(rhte->lxt_cnt + delta); + + if (ngrps != ngrps_old) { + /* reallocate to fit new size */ + lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE * ngrps), + GFP_KERNEL); + if (unlikely(!lxt)) { + mutex_unlock(&blka->mutex); + rc = -ENOMEM; + goto out; + } + + /* copy over all old entries */ + memcpy(lxt, lxt_old, (sizeof(*lxt) * rhte->lxt_cnt)); + } else + lxt = lxt_old; + + /* nothing can fail from now on */ + my_new_size = rhte->lxt_cnt + delta; + + /* add new entries to the end */ + for (i = rhte->lxt_cnt; i < my_new_size; i++) { + /* + * Due to the earlier check of available space, ba_alloc + * cannot fail here. If it did due to internal error, + * leave a rlba_base of -1u which will likely be a + * invalid LUN (too large). + */ + aun = ba_alloc(&blka->ba_lun); + if ((aun == -1ULL) || (aun >= blka->nchunk)) + pr_debug("%s: ba_alloc error: allocated chunk# %llX, " + "max %llX\n", __func__, aun, blka->nchunk - 1); + + /* select both ports, use r/w perms from RHT */ + lxt[i].rlba_base = ((aun << MC_CHUNK_SHIFT) | + (lli->lun_index << LXT_LUNIDX_SHIFT) | + (RHT_PERM_RW << LXT_PERM_SHIFT | + lli->port_sel)); + } + + mutex_unlock(&blka->mutex); + + /* + * The following sequence is prescribed in the SISlite spec + * for syncing up with the AFU when adding LXT entries. + */ + dma_wmb(); /* Make LXT updates are visible */ + + rhte->lxt_start = lxt; + dma_wmb(); /* Make RHT entry's LXT table update visible */ + + rhte->lxt_cnt = my_new_size; + dma_wmb(); /* Make RHT entry's LXT table size update visible */ + + cxlflash_afu_sync(afu, ctxid, rhndl, AFU_LW_SYNC); + + /* free old lxt if reallocated */ + if (lxt != lxt_old) + kfree(lxt_old); + *new_size = my_new_size; +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * shrink_lxt() - reduces translation table associated with the specified RHTE + * @afu: AFU associated with the host. + * @sdev: SCSI device associated with LUN. + * @rhndl: Resource handle associated with the RHTE. + * @rhte: Resource handle entry (RHTE). + * @ctxi: Context owning resources. + * @new_size: Number of translation entries associated with RHTE. + * + * Return: 0 on success, -errno on failure + */ +static int shrink_lxt(struct afu *afu, + struct scsi_device *sdev, + res_hndl_t rhndl, + struct sisl_rht_entry *rhte, + struct ctx_info *ctxi, + u64 *new_size) +{ + struct sisl_lxt_entry *lxt, *lxt_old; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct blka *blka = &gli->blka; + ctx_hndl_t ctxid = DECODE_CTXID(ctxi->ctxid); + bool needs_ws = ctxi->rht_needs_ws[rhndl]; + bool needs_sync = !ctxi->err_recovery_active; + u32 ngrps, ngrps_old; + u64 aun; /* chunk# allocated by block allocator */ + u64 delta = rhte->lxt_cnt - *new_size; + u64 my_new_size; + int i, rc = 0; + + lxt_old = rhte->lxt_start; + ngrps_old = LXT_NUM_GROUPS(rhte->lxt_cnt); + ngrps = LXT_NUM_GROUPS(rhte->lxt_cnt - delta); + + if (ngrps != ngrps_old) { + /* Reallocate to fit new size unless new size is 0 */ + if (ngrps) { + lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE * ngrps), + GFP_KERNEL); + if (unlikely(!lxt)) { + rc = -ENOMEM; + goto out; + } + + /* Copy over old entries that will remain */ + memcpy(lxt, lxt_old, + (sizeof(*lxt) * (rhte->lxt_cnt - delta))); + } else + lxt = NULL; + } else + lxt = lxt_old; + + /* Nothing can fail from now on */ + my_new_size = rhte->lxt_cnt - delta; + + /* + * The following sequence is prescribed in the SISlite spec + * for syncing up with the AFU when removing LXT entries. + */ + rhte->lxt_cnt = my_new_size; + dma_wmb(); /* Make RHT entry's LXT table size update visible */ + + rhte->lxt_start = lxt; + dma_wmb(); /* Make RHT entry's LXT table update visible */ + + if (needs_sync) + cxlflash_afu_sync(afu, ctxid, rhndl, AFU_HW_SYNC); + + if (needs_ws) { + /* + * Mark the context as unavailable, so that we can release + * the mutex safely. + */ + ctxi->unavail = true; + mutex_unlock(&ctxi->mutex); + } + + /* Free LBAs allocated to freed chunks */ + mutex_lock(&blka->mutex); + for (i = delta - 1; i >= 0; i--) { + /* Mask the higher 48 bits before shifting, even though + * it is a noop + */ + aun = (lxt_old[my_new_size + i].rlba_base & SISL_ASTATUS_MASK); + aun = (aun >> MC_CHUNK_SHIFT); + if (needs_ws) + write_same16(sdev, aun, MC_CHUNK_SIZE); + ba_free(&blka->ba_lun, aun); + } + mutex_unlock(&blka->mutex); + + if (needs_ws) { + /* Make the context visible again */ + mutex_lock(&ctxi->mutex); + ctxi->unavail = false; + } + + /* Free old lxt if reallocated */ + if (lxt != lxt_old) + kfree(lxt_old); + *new_size = my_new_size; +out: + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * _cxlflash_vlun_resize() - changes the size of a virtual LUN + * @sdev: SCSI device associated with LUN owning virtual LUN. + * @ctxi: Context owning resources. + * @resize: Resize ioctl data structure. + * + * On successful return, the user is informed of the new size (in blocks) + * of the virtual LUN in last LBA format. When the size of the virtual + * LUN is zero, the last LBA is reflected as -1. See comment in the + * prologue for _cxlflash_disk_release() regarding AFU syncs and contexts + * on the error recovery list. + * + * Return: 0 on success, -errno on failure + */ +int _cxlflash_vlun_resize(struct scsi_device *sdev, + struct ctx_info *ctxi, + struct dk_cxlflash_resize *resize) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct afu *afu = cfg->afu; + bool put_ctx = false; + + res_hndl_t rhndl = resize->rsrc_handle; + u64 new_size; + u64 nsectors; + u64 ctxid = DECODE_CTXID(resize->context_id), + rctxid = resize->context_id; + + struct sisl_rht_entry *rhte; + + int rc = 0; + + /* + * The requested size (req_size) is always assumed to be in 4k blocks, + * so we have to convert it here from 4k to chunk size. + */ + nsectors = (resize->req_size * CXLFLASH_BLOCK_SIZE) / gli->blk_len; + new_size = DIV_ROUND_UP(nsectors, MC_CHUNK_SIZE); + + pr_debug("%s: ctxid=%llu rhndl=0x%llx, req_size=0x%llx," + "new_size=%llx\n", __func__, ctxid, resize->rsrc_handle, + resize->req_size, new_size); + + if (unlikely(gli->mode != MODE_VIRTUAL)) { + pr_debug("%s: LUN mode does not support resize! (%d)\n", + __func__, gli->mode); + rc = -EINVAL; + goto out; + + } + + if (!ctxi) { + ctxi = get_context(cfg, rctxid, lli, CTX_CTRL_ERR_FALLBACK); + if (unlikely(!ctxi)) { + pr_debug("%s: Bad context! (%llu)\n", __func__, ctxid); + rc = -EINVAL; + goto out; + } + + put_ctx = true; + } + + rhte = get_rhte(ctxi, rhndl, lli); + if (unlikely(!rhte)) { + pr_debug("%s: Bad resource handle! (%u)\n", __func__, rhndl); + rc = -EINVAL; + goto out; + } + + if (new_size > rhte->lxt_cnt) + rc = grow_lxt(afu, sdev, ctxid, rhndl, rhte, &new_size); + else if (new_size < rhte->lxt_cnt) + rc = shrink_lxt(afu, sdev, rhndl, rhte, ctxi, &new_size); + + resize->hdr.return_flags = 0; + resize->last_lba = (new_size * MC_CHUNK_SIZE * gli->blk_len); + resize->last_lba /= CXLFLASH_BLOCK_SIZE; + resize->last_lba--; + +out: + if (put_ctx) + put_context(ctxi); + pr_debug("%s: resized to %lld returning rc=%d\n", + __func__, resize->last_lba, rc); + return rc; +} + +int cxlflash_vlun_resize(struct scsi_device *sdev, + struct dk_cxlflash_resize *resize) +{ + return _cxlflash_vlun_resize(sdev, NULL, resize); +} + +/** + * cxlflash_restore_luntable() - Restore LUN table to prior state + * @cfg: Internal structure associated with the host. + */ +void cxlflash_restore_luntable(struct cxlflash_cfg *cfg) +{ + struct llun_info *lli, *temp; + u32 chan; + u32 lind; + struct afu *afu = cfg->afu; + struct sisl_global_map __iomem *agm = &afu->afu_map->global; + + mutex_lock(&global.mutex); + + list_for_each_entry_safe(lli, temp, &cfg->lluns, list) { + if (!lli->in_table) + continue; + + lind = lli->lun_index; + + if (lli->port_sel == BOTH_PORTS) { + writeq_be(lli->lun_id[0], &agm->fc_port[0][lind]); + writeq_be(lli->lun_id[1], &agm->fc_port[1][lind]); + pr_debug("%s: Virtual LUN on slot %d id0=%llx, " + "id1=%llx\n", __func__, lind, + lli->lun_id[0], lli->lun_id[1]); + } else { + chan = PORT2CHAN(lli->port_sel); + writeq_be(lli->lun_id[chan], &agm->fc_port[chan][lind]); + pr_debug("%s: Virtual LUN on slot %d chan=%d, " + "id=%llx\n", __func__, lind, chan, + lli->lun_id[chan]); + } + } + + mutex_unlock(&global.mutex); +} + +/** + * init_luntable() - write an entry in the LUN table + * @cfg: Internal structure associated with the host. + * @lli: Per adapter LUN information structure. + * + * On successful return, a LUN table entry is created. + * At the top for LUNs visible on both ports. + * At the bottom for LUNs visible only on one port. + * + * Return: 0 on success, -errno on failure + */ +static int init_luntable(struct cxlflash_cfg *cfg, struct llun_info *lli) +{ + u32 chan; + u32 lind; + int rc = 0; + struct afu *afu = cfg->afu; + struct sisl_global_map __iomem *agm = &afu->afu_map->global; + + mutex_lock(&global.mutex); + + if (lli->in_table) + goto out; + + if (lli->port_sel == BOTH_PORTS) { + /* + * If this LUN is visible from both ports, we will put + * it in the top half of the LUN table. + */ + if ((cfg->promote_lun_index == cfg->last_lun_index[0]) || + (cfg->promote_lun_index == cfg->last_lun_index[1])) { + rc = -ENOSPC; + goto out; + } + + lind = lli->lun_index = cfg->promote_lun_index; + writeq_be(lli->lun_id[0], &agm->fc_port[0][lind]); + writeq_be(lli->lun_id[1], &agm->fc_port[1][lind]); + cfg->promote_lun_index++; + pr_debug("%s: Virtual LUN on slot %d id0=%llx, id1=%llx\n", + __func__, lind, lli->lun_id[0], lli->lun_id[1]); + } else { + /* + * If this LUN is visible only from one port, we will put + * it in the bottom half of the LUN table. + */ + chan = PORT2CHAN(lli->port_sel); + if (cfg->promote_lun_index == cfg->last_lun_index[chan]) { + rc = -ENOSPC; + goto out; + } + + lind = lli->lun_index = cfg->last_lun_index[chan]; + writeq_be(lli->lun_id[chan], &agm->fc_port[chan][lind]); + cfg->last_lun_index[chan]--; + pr_debug("%s: Virtual LUN on slot %d chan=%d, id=%llx\n", + __func__, lind, chan, lli->lun_id[chan]); + } + + lli->in_table = true; +out: + mutex_unlock(&global.mutex); + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; +} + +/** + * cxlflash_disk_virtual_open() - open a virtual disk of specified size + * @sdev: SCSI device associated with LUN owning virtual LUN. + * @arg: UVirtual ioctl data structure. + * + * On successful return, the user is informed of the resource handle + * to be used to identify the virtual LUN and the size (in blocks) of + * the virtual LUN in last LBA format. When the size of the virtual LUN + * is zero, the last LBA is reflected as -1. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_disk_virtual_open(struct scsi_device *sdev, void *arg) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct device *dev = &cfg->dev->dev; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + + struct dk_cxlflash_uvirtual *virt = (struct dk_cxlflash_uvirtual *)arg; + struct dk_cxlflash_resize resize; + + u64 ctxid = DECODE_CTXID(virt->context_id), + rctxid = virt->context_id; + u64 lun_size = virt->lun_size; + u64 last_lba = 0; + u64 rsrc_handle = -1; + + int rc = 0; + + struct ctx_info *ctxi = NULL; + struct sisl_rht_entry *rhte = NULL; + + pr_debug("%s: ctxid=%llu ls=0x%llx\n", __func__, ctxid, lun_size); + + /* Setup the LUNs block allocator on first call */ + mutex_lock(&gli->mutex); + if (gli->mode == MODE_NONE) { + rc = init_vlun(lli); + if (rc) { + dev_err(dev, "%s: call to init_vlun failed rc=%d!\n", + __func__, rc); + rc = -ENOMEM; + goto err0; + } + } + + rc = cxlflash_lun_attach(gli, MODE_VIRTUAL, true); + if (unlikely(rc)) { + dev_err(dev, "%s: Failed to attach to LUN! (VIRTUAL)\n", + __func__); + goto err0; + } + mutex_unlock(&gli->mutex); + + rc = init_luntable(cfg, lli); + if (rc) { + dev_err(dev, "%s: call to init_luntable failed rc=%d!\n", + __func__, rc); + goto err1; + } + + ctxi = get_context(cfg, rctxid, lli, 0); + if (unlikely(!ctxi)) { + dev_err(dev, "%s: Bad context! (%llu)\n", __func__, ctxid); + rc = -EINVAL; + goto err1; + } + + rhte = rhte_checkout(ctxi, lli); + if (unlikely(!rhte)) { + dev_err(dev, "%s: too many opens for this context\n", __func__); + rc = -EMFILE; /* too many opens */ + goto err1; + } + + rsrc_handle = (rhte - ctxi->rht_start); + + /* Populate RHT format 0 */ + rhte->nmask = MC_RHT_NMASK; + rhte->fp = SISL_RHT_FP(0U, ctxi->rht_perms); + + /* Resize even if requested size is 0 */ + marshal_virt_to_resize(virt, &resize); + resize.rsrc_handle = rsrc_handle; + rc = _cxlflash_vlun_resize(sdev, ctxi, &resize); + if (rc) { + dev_err(dev, "%s: resize failed rc %d\n", __func__, rc); + goto err2; + } + last_lba = resize.last_lba; + + if (virt->hdr.flags & DK_CXLFLASH_UVIRTUAL_NEED_WRITE_SAME) + ctxi->rht_needs_ws[rsrc_handle] = true; + + virt->hdr.return_flags = 0; + virt->last_lba = last_lba; + virt->rsrc_handle = rsrc_handle; + +out: + if (likely(ctxi)) + put_context(ctxi); + pr_debug("%s: returning handle 0x%llx rc=%d llba %lld\n", + __func__, rsrc_handle, rc, last_lba); + return rc; + +err2: + rhte_checkin(ctxi, rhte); +err1: + cxlflash_lun_detach(gli); + goto out; +err0: + /* Special common cleanup prior to successful LUN attach */ + cxlflash_ba_terminate(&gli->blka.ba_lun); + mutex_unlock(&gli->mutex); + goto out; +} + +/** + * clone_lxt() - copies translation tables from source to destination RHTE + * @afu: AFU associated with the host. + * @blka: Block allocator associated with LUN. + * @ctxid: Context ID of context owning the RHTE. + * @rhndl: Resource handle associated with the RHTE. + * @rhte: Destination resource handle entry (RHTE). + * @rhte_src: Source resource handle entry (RHTE). + * + * Return: 0 on success, -errno on failure + */ +static int clone_lxt(struct afu *afu, + struct blka *blka, + ctx_hndl_t ctxid, + res_hndl_t rhndl, + struct sisl_rht_entry *rhte, + struct sisl_rht_entry *rhte_src) +{ + struct sisl_lxt_entry *lxt; + u32 ngrps; + u64 aun; /* chunk# allocated by block allocator */ + int i, j; + + ngrps = LXT_NUM_GROUPS(rhte_src->lxt_cnt); + + if (ngrps) { + /* allocate new LXTs for clone */ + lxt = kzalloc((sizeof(*lxt) * LXT_GROUP_SIZE * ngrps), + GFP_KERNEL); + if (unlikely(!lxt)) + return -ENOMEM; + + /* copy over */ + memcpy(lxt, rhte_src->lxt_start, + (sizeof(*lxt) * rhte_src->lxt_cnt)); + + /* clone the LBAs in block allocator via ref_cnt */ + mutex_lock(&blka->mutex); + for (i = 0; i < rhte_src->lxt_cnt; i++) { + aun = (lxt[i].rlba_base >> MC_CHUNK_SHIFT); + if (ba_clone(&blka->ba_lun, aun) == -1ULL) { + /* free the clones already made */ + for (j = 0; j < i; j++) { + aun = (lxt[j].rlba_base >> + MC_CHUNK_SHIFT); + ba_free(&blka->ba_lun, aun); + } + + mutex_unlock(&blka->mutex); + kfree(lxt); + return -EIO; + } + } + mutex_unlock(&blka->mutex); + } else { + lxt = NULL; + } + + /* + * The following sequence is prescribed in the SISlite spec + * for syncing up with the AFU when adding LXT entries. + */ + dma_wmb(); /* Make LXT updates are visible */ + + rhte->lxt_start = lxt; + dma_wmb(); /* Make RHT entry's LXT table update visible */ + + rhte->lxt_cnt = rhte_src->lxt_cnt; + dma_wmb(); /* Make RHT entry's LXT table size update visible */ + + cxlflash_afu_sync(afu, ctxid, rhndl, AFU_LW_SYNC); + + pr_debug("%s: returning\n", __func__); + return 0; +} + +/** + * cxlflash_disk_clone() - clone a context by making snapshot of another + * @sdev: SCSI device associated with LUN owning virtual LUN. + * @clone: Clone ioctl data structure. + * + * This routine effectively performs cxlflash_disk_open operation for each + * in-use virtual resource in the source context. Note that the destination + * context must be in pristine state and cannot have any resource handles + * open at the time of the clone. + * + * Return: 0 on success, -errno on failure + */ +int cxlflash_disk_clone(struct scsi_device *sdev, + struct dk_cxlflash_clone *clone) +{ + struct cxlflash_cfg *cfg = (struct cxlflash_cfg *)sdev->host->hostdata; + struct llun_info *lli = sdev->hostdata; + struct glun_info *gli = lli->parent; + struct blka *blka = &gli->blka; + struct afu *afu = cfg->afu; + struct dk_cxlflash_release release = { { 0 }, 0 }; + + struct ctx_info *ctxi_src = NULL, + *ctxi_dst = NULL; + struct lun_access *lun_access_src, *lun_access_dst; + u32 perms; + u64 ctxid_src = DECODE_CTXID(clone->context_id_src), + ctxid_dst = DECODE_CTXID(clone->context_id_dst), + rctxid_src = clone->context_id_src, + rctxid_dst = clone->context_id_dst; + int adap_fd_src = clone->adap_fd_src; + int i, j; + int rc = 0; + bool found; + LIST_HEAD(sidecar); + + pr_debug("%s: ctxid_src=%llu ctxid_dst=%llu adap_fd_src=%d\n", + __func__, ctxid_src, ctxid_dst, adap_fd_src); + + /* Do not clone yourself */ + if (unlikely(rctxid_src == rctxid_dst)) { + rc = -EINVAL; + goto out; + } + + if (unlikely(gli->mode != MODE_VIRTUAL)) { + rc = -EINVAL; + pr_debug("%s: Clone not supported on physical LUNs! (%d)\n", + __func__, gli->mode); + goto out; + } + + ctxi_src = get_context(cfg, rctxid_src, lli, CTX_CTRL_CLONE); + ctxi_dst = get_context(cfg, rctxid_dst, lli, 0); + if (unlikely(!ctxi_src || !ctxi_dst)) { + pr_debug("%s: Bad context! (%llu,%llu)\n", __func__, + ctxid_src, ctxid_dst); + rc = -EINVAL; + goto out; + } + + if (unlikely(adap_fd_src != ctxi_src->lfd)) { + pr_debug("%s: Invalid source adapter fd! (%d)\n", + __func__, adap_fd_src); + rc = -EINVAL; + goto out; + } + + /* Verify there is no open resource handle in the destination context */ + for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) + if (ctxi_dst->rht_start[i].nmask != 0) { + rc = -EINVAL; + goto out; + } + + /* Clone LUN access list */ + list_for_each_entry(lun_access_src, &ctxi_src->luns, list) { + found = false; + list_for_each_entry(lun_access_dst, &ctxi_dst->luns, list) + if (lun_access_dst->sdev == lun_access_src->sdev) { + found = true; + break; + } + + if (!found) { + lun_access_dst = kzalloc(sizeof(*lun_access_dst), + GFP_KERNEL); + if (unlikely(!lun_access_dst)) { + pr_err("%s: Unable to allocate lun_access!\n", + __func__); + rc = -ENOMEM; + goto out; + } + + *lun_access_dst = *lun_access_src; + list_add(&lun_access_dst->list, &sidecar); + } + } + + if (unlikely(!ctxi_src->rht_out)) { + pr_debug("%s: Nothing to clone!\n", __func__); + goto out_success; + } + + /* User specified permission on attach */ + perms = ctxi_dst->rht_perms; + + /* + * Copy over checked-out RHT (and their associated LXT) entries by + * hand, stopping after we've copied all outstanding entries and + * cleaning up if the clone fails. + * + * Note: This loop is equivalent to performing cxlflash_disk_open and + * cxlflash_vlun_resize. As such, LUN accounting needs to be taken into + * account by attaching after each successful RHT entry clone. In the + * event that a clone failure is experienced, the LUN detach is handled + * via the cleanup performed by _cxlflash_disk_release. + */ + for (i = 0; i < MAX_RHT_PER_CONTEXT; i++) { + if (ctxi_src->rht_out == ctxi_dst->rht_out) + break; + if (ctxi_src->rht_start[i].nmask == 0) + continue; + + /* Consume a destination RHT entry */ + ctxi_dst->rht_out++; + ctxi_dst->rht_start[i].nmask = ctxi_src->rht_start[i].nmask; + ctxi_dst->rht_start[i].fp = + SISL_RHT_FP_CLONE(ctxi_src->rht_start[i].fp, perms); + ctxi_dst->rht_lun[i] = ctxi_src->rht_lun[i]; + + rc = clone_lxt(afu, blka, ctxid_dst, i, + &ctxi_dst->rht_start[i], + &ctxi_src->rht_start[i]); + if (rc) { + marshal_clone_to_rele(clone, &release); + for (j = 0; j < i; j++) { + release.rsrc_handle = j; + _cxlflash_disk_release(sdev, ctxi_dst, + &release); + } + + /* Put back the one we failed on */ + rhte_checkin(ctxi_dst, &ctxi_dst->rht_start[i]); + goto err; + } + + cxlflash_lun_attach(gli, gli->mode, false); + } + +out_success: + list_splice(&sidecar, &ctxi_dst->luns); + sys_close(adap_fd_src); + + /* fall through */ +out: + if (ctxi_src) + put_context(ctxi_src); + if (ctxi_dst) + put_context(ctxi_dst); + pr_debug("%s: returning rc=%d\n", __func__, rc); + return rc; + +err: + list_for_each_entry_safe(lun_access_src, lun_access_dst, &sidecar, list) + kfree(lun_access_src); + goto out; +} diff --git a/kernel/drivers/scsi/cxlflash/vlun.h b/kernel/drivers/scsi/cxlflash/vlun.h new file mode 100644 index 000000000..8b29a7494 --- /dev/null +++ b/kernel/drivers/scsi/cxlflash/vlun.h @@ -0,0 +1,86 @@ +/* + * CXL Flash Device Driver + * + * Written by: Manoj N. Kumar , IBM Corporation + * Matthew R. Ochs , IBM Corporation + * + * Copyright (C) 2015 IBM Corporation + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _CXLFLASH_VLUN_H +#define _CXLFLASH_VLUN_H + +/* RHT - Resource Handle Table */ +#define MC_RHT_NMASK 16 /* in bits */ +#define MC_CHUNK_SHIFT MC_RHT_NMASK /* shift to go from LBA to chunk# */ + +#define HIBIT (BITS_PER_LONG - 1) + +#define MAX_AUN_CLONE_CNT 0xFF + +/* + * LXT - LBA Translation Table + * + * +-------+-------+-------+-------+-------+-------+-------+---+---+ + * | RLBA_BASE |LUN_IDX| P |SEL| + * +-------+-------+-------+-------+-------+-------+-------+---+---+ + * + * The LXT Entry contains the physical LBA where the chunk starts (RLBA_BASE). + * AFU ORes the low order bits from the virtual LBA (offset into the chunk) + * with RLBA_BASE. The result is the physical LBA to be sent to storage. + * The LXT Entry also contains an index to a LUN TBL and a bitmask of which + * outgoing (FC) * ports can be selected. The port select bit-mask is ANDed + * with a global port select bit-mask maintained by the driver. + * In addition, it has permission bits that are ANDed with the + * RHT permissions to arrive at the final permissions for the chunk. + * + * LXT tables are allocated dynamically in groups. This is done to avoid + * a malloc/free overhead each time the LXT has to grow or shrink. + * + * Based on the current lxt_cnt (used), it is always possible to know + * how many are allocated (used+free). The number of allocated entries is + * not stored anywhere. + * + * The LXT table is re-allocated whenever it needs to cross into another group. +*/ +#define LXT_GROUP_SIZE 8 +#define LXT_NUM_GROUPS(lxt_cnt) (((lxt_cnt) + 7)/8) /* alloc'ed groups */ +#define LXT_LUNIDX_SHIFT 8 /* LXT entry, shift for LUN index */ +#define LXT_PERM_SHIFT 4 /* LXT entry, shift for permission bits */ + +struct ba_lun_info { + u64 *lun_alloc_map; + u32 lun_bmap_size; + u32 total_aus; + u64 free_aun_cnt; + + /* indices to be used for elevator lookup of free map */ + u32 free_low_idx; + u32 free_curr_idx; + u32 free_high_idx; + + u8 *aun_clone_map; +}; + +struct ba_lun { + u64 lun_id; + u64 wwpn; + size_t lsize; /* LUN size in number of LBAs */ + size_t lba_size; /* LBA size in number of bytes */ + size_t au_size; /* Allocation Unit size in number of LBAs */ + struct ba_lun_info *ba_lun_handle; +}; + +/* Block Allocator */ +struct blka { + struct ba_lun ba_lun; + u64 nchunk; /* number of chunks */ + struct mutex mutex; +}; + +#endif /* ifndef _CXLFLASH_SUPERPIPE_H */ diff --git a/kernel/drivers/scsi/device_handler/Kconfig b/kernel/drivers/scsi/device_handler/Kconfig index 69abd0ad4..e5647d592 100644 --- a/kernel/drivers/scsi/device_handler/Kconfig +++ b/kernel/drivers/scsi/device_handler/Kconfig @@ -3,7 +3,7 @@ # menuconfig SCSI_DH - tristate "SCSI Device Handlers" + bool "SCSI Device Handlers" depends on SCSI default n help diff --git a/kernel/drivers/scsi/device_handler/Makefile b/kernel/drivers/scsi/device_handler/Makefile index e1d2ea083..09866c50f 100644 --- a/kernel/drivers/scsi/device_handler/Makefile +++ b/kernel/drivers/scsi/device_handler/Makefile @@ -1,7 +1,6 @@ # # SCSI Device Handler # -obj-$(CONFIG_SCSI_DH) += scsi_dh.o obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o diff --git a/kernel/drivers/scsi/device_handler/scsi_dh.c b/kernel/drivers/scsi/device_handler/scsi_dh.c deleted file mode 100644 index 1efebc9ee..000000000 --- a/kernel/drivers/scsi/device_handler/scsi_dh.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * SCSI device handler infrastruture. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - * - * 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. - * - * 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. - * - * Copyright IBM Corporation, 2007 - * Authors: - * Chandra Seetharaman - * Mike Anderson - */ - -#include -#include -#include -#include "../scsi_priv.h" - -static DEFINE_SPINLOCK(list_lock); -static LIST_HEAD(scsi_dh_list); - -static struct scsi_device_handler *get_device_handler(const char *name) -{ - struct scsi_device_handler *tmp, *found = NULL; - - spin_lock(&list_lock); - list_for_each_entry(tmp, &scsi_dh_list, list) { - if (!strncmp(tmp->name, name, strlen(tmp->name))) { - found = tmp; - break; - } - } - spin_unlock(&list_lock); - return found; -} - -/* - * device_handler_match_function - Match a device handler to a device - * @sdev - SCSI device to be tested - * - * Tests @sdev against the match function of all registered device_handler. - * Returns the found device handler or NULL if not found. - */ -static struct scsi_device_handler * -device_handler_match_function(struct scsi_device *sdev) -{ - struct scsi_device_handler *tmp_dh, *found_dh = NULL; - - spin_lock(&list_lock); - list_for_each_entry(tmp_dh, &scsi_dh_list, list) { - if (tmp_dh->match && tmp_dh->match(sdev)) { - found_dh = tmp_dh; - break; - } - } - spin_unlock(&list_lock); - return found_dh; -} - -/* - * device_handler_match - Attach a device handler to a device - * @scsi_dh - The device handler to match against or NULL - * @sdev - SCSI device to be tested against @scsi_dh - * - * Tests @sdev against the device handler @scsi_dh or against - * all registered device_handler if @scsi_dh == NULL. - * Returns the found device handler or NULL if not found. - */ -static struct scsi_device_handler * -device_handler_match(struct scsi_device_handler *scsi_dh, - struct scsi_device *sdev) -{ - struct scsi_device_handler *found_dh; - - found_dh = device_handler_match_function(sdev); - - if (scsi_dh && found_dh != scsi_dh) - found_dh = NULL; - - return found_dh; -} - -/* - * scsi_dh_handler_attach - Attach a device handler to a device - * @sdev - SCSI device the device handler should attach to - * @scsi_dh - The device handler to attach - */ -static int scsi_dh_handler_attach(struct scsi_device *sdev, - struct scsi_device_handler *scsi_dh) -{ - struct scsi_dh_data *d; - - if (sdev->scsi_dh_data) { - if (sdev->scsi_dh_data->scsi_dh != scsi_dh) - return -EBUSY; - - kref_get(&sdev->scsi_dh_data->kref); - return 0; - } - - if (!try_module_get(scsi_dh->module)) - return -EINVAL; - - d = scsi_dh->attach(sdev); - if (IS_ERR(d)) { - sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%ld)\n", - scsi_dh->name, PTR_ERR(d)); - module_put(scsi_dh->module); - return PTR_ERR(d); - } - - d->scsi_dh = scsi_dh; - kref_init(&d->kref); - d->sdev = sdev; - - spin_lock_irq(sdev->request_queue->queue_lock); - sdev->scsi_dh_data = d; - spin_unlock_irq(sdev->request_queue->queue_lock); - return 0; -} - -static void __detach_handler (struct kref *kref) -{ - struct scsi_dh_data *scsi_dh_data = - container_of(kref, struct scsi_dh_data, kref); - struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh; - struct scsi_device *sdev = scsi_dh_data->sdev; - - scsi_dh->detach(sdev); - - spin_lock_irq(sdev->request_queue->queue_lock); - sdev->scsi_dh_data = NULL; - spin_unlock_irq(sdev->request_queue->queue_lock); - - sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name); - module_put(scsi_dh->module); -} - -/* - * scsi_dh_handler_detach - Detach a device handler from a device - * @sdev - SCSI device the device handler should be detached from - * @scsi_dh - Device handler to be detached - * - * Detach from a device handler. If a device handler is specified, - * only detach if the currently attached handler matches @scsi_dh. - */ -static void scsi_dh_handler_detach(struct scsi_device *sdev, - struct scsi_device_handler *scsi_dh) -{ - if (!sdev->scsi_dh_data) - return; - - if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) - return; - - if (!scsi_dh) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - - if (scsi_dh) - kref_put(&sdev->scsi_dh_data->kref, __detach_handler); -} - -/* - * Functions for sysfs attribute 'dh_state' - */ -static ssize_t -store_dh_state(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct scsi_device_handler *scsi_dh; - int err = -EINVAL; - - if (sdev->sdev_state == SDEV_CANCEL || - sdev->sdev_state == SDEV_DEL) - return -ENODEV; - - if (!sdev->scsi_dh_data) { - /* - * Attach to a device handler - */ - if (!(scsi_dh = get_device_handler(buf))) - return err; - err = scsi_dh_handler_attach(sdev, scsi_dh); - } else { - scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (!strncmp(buf, "detach", 6)) { - /* - * Detach from a device handler - */ - scsi_dh_handler_detach(sdev, scsi_dh); - err = 0; - } else if (!strncmp(buf, "activate", 8)) { - /* - * Activate a device handler - */ - if (scsi_dh->activate) - err = scsi_dh->activate(sdev, NULL, NULL); - else - err = 0; - } - } - - return err<0?err:count; -} - -static ssize_t -show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct scsi_device *sdev = to_scsi_device(dev); - - if (!sdev->scsi_dh_data) - return snprintf(buf, 20, "detached\n"); - - return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name); -} - -static struct device_attribute scsi_dh_state_attr = - __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, - store_dh_state); - -/* - * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh - */ -static int scsi_dh_sysfs_attr_add(struct device *dev, void *data) -{ - struct scsi_device *sdev; - int err; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - err = device_create_file(&sdev->sdev_gendev, - &scsi_dh_state_attr); - - return 0; -} - -/* - * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh - */ -static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data) -{ - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - device_remove_file(&sdev->sdev_gendev, - &scsi_dh_state_attr); - - return 0; -} - -/* - * scsi_dh_notifier - notifier chain callback - */ -static int scsi_dh_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct device *dev = data; - struct scsi_device *sdev; - int err = 0; - struct scsi_device_handler *devinfo = NULL; - - if (!scsi_is_sdev_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - if (action == BUS_NOTIFY_ADD_DEVICE) { - err = device_create_file(dev, &scsi_dh_state_attr); - /* don't care about err */ - devinfo = device_handler_match(NULL, sdev); - if (devinfo) - err = scsi_dh_handler_attach(sdev, devinfo); - } else if (action == BUS_NOTIFY_DEL_DEVICE) { - device_remove_file(dev, &scsi_dh_state_attr); - scsi_dh_handler_detach(sdev, NULL); - } - return err; -} - -/* - * scsi_dh_notifier_add - Callback for scsi_register_device_handler - */ -static int scsi_dh_notifier_add(struct device *dev, void *data) -{ - struct scsi_device_handler *scsi_dh = data; - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - if (!get_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - if (device_handler_match(scsi_dh, sdev)) - scsi_dh_handler_attach(sdev, scsi_dh); - - put_device(dev); - - return 0; -} - -/* - * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler - */ -static int scsi_dh_notifier_remove(struct device *dev, void *data) -{ - struct scsi_device_handler *scsi_dh = data; - struct scsi_device *sdev; - - if (!scsi_is_sdev_device(dev)) - return 0; - - if (!get_device(dev)) - return 0; - - sdev = to_scsi_device(dev); - - scsi_dh_handler_detach(sdev, scsi_dh); - - put_device(dev); - - return 0; -} - -/* - * scsi_register_device_handler - register a device handler personality - * module. - * @scsi_dh - device handler to be registered. - * - * Returns 0 on success, -EBUSY if handler already registered. - */ -int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) -{ - - if (get_device_handler(scsi_dh->name)) - return -EBUSY; - - if (!scsi_dh->attach || !scsi_dh->detach) - return -EINVAL; - - spin_lock(&list_lock); - list_add(&scsi_dh->list, &scsi_dh_list); - spin_unlock(&list_lock); - - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); - printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); - - return SCSI_DH_OK; -} -EXPORT_SYMBOL_GPL(scsi_register_device_handler); - -/* - * scsi_unregister_device_handler - register a device handler personality - * module. - * @scsi_dh - device handler to be unregistered. - * - * Returns 0 on success, -ENODEV if handler not registered. - */ -int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) -{ - - if (!get_device_handler(scsi_dh->name)) - return -ENODEV; - - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, - scsi_dh_notifier_remove); - - spin_lock(&list_lock); - list_del(&scsi_dh->list); - spin_unlock(&list_lock); - printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); - - return SCSI_DH_OK; -} -EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); - -/* - * scsi_dh_activate - activate the path associated with the scsi_device - * corresponding to the given request queue. - * Returns immediately without waiting for activation to be completed. - * @q - Request queue that is associated with the scsi_device to be - * activated. - * @fn - Function to be called upon completion of the activation. - * Function fn is called with data (below) and the error code. - * Function fn may be called from the same calling context. So, - * do not hold the lock in the caller which may be needed in fn. - * @data - data passed to the function fn upon completion. - * - */ -int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) -{ - int err = 0; - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - struct device *dev = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev) { - spin_unlock_irqrestore(q->queue_lock, flags); - err = SCSI_DH_NOSYS; - if (fn) - fn(data, err); - return err; - } - - if (sdev->scsi_dh_data) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - dev = get_device(&sdev->sdev_gendev); - if (!scsi_dh || !dev || - sdev->sdev_state == SDEV_CANCEL || - sdev->sdev_state == SDEV_DEL) - err = SCSI_DH_NOSYS; - if (sdev->sdev_state == SDEV_OFFLINE) - err = SCSI_DH_DEV_OFFLINED; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (err) { - if (fn) - fn(data, err); - goto out; - } - - if (scsi_dh->activate) - err = scsi_dh->activate(sdev, fn, data); -out: - put_device(dev); - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_activate); - -/* - * scsi_dh_set_params - set the parameters for the device as per the - * string specified in params. - * @q - Request queue that is associated with the scsi_device for - * which the parameters to be set. - * @params - parameters in the following format - * "no_of_params\0param1\0param2\0param3\0...\0" - * for example, string for 2 parameters with value 10 and 21 - * is specified as "2\010\021\0". - */ -int scsi_dh_set_params(struct request_queue *q, const char *params) -{ - int err = -SCSI_DH_NOSYS; - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (sdev && sdev->scsi_dh_data) - scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev)) - err = 0; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (err) - return err; - err = scsi_dh->set_params(sdev, params); - put_device(&sdev->sdev_gendev); - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_set_params); - -/* - * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for - * the given name. FALSE(0) otherwise. - * @name - name of the device handler. - */ -int scsi_dh_handler_exist(const char *name) -{ - return (get_device_handler(name) != NULL); -} -EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); - -/* - * scsi_dh_attach - Attach device handler - * @q - Request queue that is associated with the scsi_device - * the handler should be attached to - * @name - name of the handler to attach - */ -int scsi_dh_attach(struct request_queue *q, const char *name) -{ - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh; - int err = 0; - - scsi_dh = get_device_handler(name); - if (!scsi_dh) - return -EINVAL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - err = -ENODEV; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!err) { - err = scsi_dh_handler_attach(sdev, scsi_dh); - put_device(&sdev->sdev_gendev); - } - return err; -} -EXPORT_SYMBOL_GPL(scsi_dh_attach); - -/* - * scsi_dh_detach - Detach device handler - * @q - Request queue that is associated with the scsi_device - * the handler should be detached from - * - * This function will detach the device handler only - * if the sdev is not part of the internal list, ie - * if it has been attached manually. - */ -void scsi_dh_detach(struct request_queue *q) -{ - unsigned long flags; - struct scsi_device *sdev; - struct scsi_device_handler *scsi_dh = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!sdev) - return; - - if (sdev->scsi_dh_data) { - scsi_dh = sdev->scsi_dh_data->scsi_dh; - scsi_dh_handler_detach(sdev, scsi_dh); - } - put_device(&sdev->sdev_gendev); -} -EXPORT_SYMBOL_GPL(scsi_dh_detach); - -/* - * scsi_dh_attached_handler_name - Get attached device handler's name - * @q - Request queue that is associated with the scsi_device - * that may have a device handler attached - * @gfp - the GFP mask used in the kmalloc() call when allocating memory - * - * Returns name of attached handler, NULL if no handler is attached. - * Caller must take care to free the returned string. - */ -const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) -{ - unsigned long flags; - struct scsi_device *sdev; - const char *handler_name = NULL; - - spin_lock_irqsave(q->queue_lock, flags); - sdev = q->queuedata; - if (!sdev || !get_device(&sdev->sdev_gendev)) - sdev = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - if (!sdev) - return NULL; - - if (sdev->scsi_dh_data) - handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp); - - put_device(&sdev->sdev_gendev); - return handler_name; -} -EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); - -static struct notifier_block scsi_dh_nb = { - .notifier_call = scsi_dh_notifier -}; - -static int __init scsi_dh_init(void) -{ - int r; - - r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); - - if (!r) - bus_for_each_dev(&scsi_bus_type, NULL, NULL, - scsi_dh_sysfs_attr_add); - - return r; -} - -static void __exit scsi_dh_exit(void) -{ - bus_for_each_dev(&scsi_bus_type, NULL, NULL, - scsi_dh_sysfs_attr_remove); - bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); -} - -module_init(scsi_dh_init); -module_exit(scsi_dh_exit); - -MODULE_DESCRIPTION("SCSI device handler"); -MODULE_AUTHOR("Chandra Seetharaman "); -MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/scsi/device_handler/scsi_dh_alua.c b/kernel/drivers/scsi/device_handler/scsi_dh_alua.c index 854b568b9..cc2773b5d 100644 --- a/kernel/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/kernel/drivers/scsi/device_handler/scsi_dh_alua.c @@ -62,7 +62,6 @@ #define ALUA_OPTIMIZE_STPG 1 struct alua_dh_data { - struct scsi_dh_data dh_data; int group_id; int rel_port; int tpgs; @@ -86,11 +85,6 @@ struct alua_dh_data { static char print_alua_state(int); static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *); -static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct alua_dh_data, dh_data); -} - static int realloc_buffer(struct alua_dh_data *h, unsigned len) { if (h->buff && h->buff != h->inq) @@ -708,7 +702,7 @@ out: */ static int alua_set_params(struct scsi_device *sdev, const char *params) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; unsigned int optimize = 0, argc; const char *p = params; int result = SCSI_DH_OK; @@ -746,7 +740,7 @@ MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than static int alua_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; int stpg = 0; @@ -804,7 +798,7 @@ out: */ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->state == TPGS_STATE_TRANSITIONING) @@ -819,23 +813,18 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) } -static bool alua_match(struct scsi_device *sdev) -{ - return (scsi_device_tpgs(sdev) != 0); -} - /* * alua_bus_attach - Attach device handler * @sdev: device to be attached to */ -static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev) +static int alua_bus_attach(struct scsi_device *sdev) { struct alua_dh_data *h; int err; h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->tpgs = TPGS_MODE_UNINITIALIZED; h->state = TPGS_STATE_OPTIMIZED; h->group_id = -1; @@ -848,11 +837,11 @@ static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev) if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED) goto failed; - sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME); - return &h->dh_data; + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* @@ -861,10 +850,11 @@ failed: */ static void alua_bus_detach(struct scsi_device *sdev) { - struct alua_dh_data *h = get_alua_data(sdev); + struct alua_dh_data *h = sdev->handler_data; if (h->buff && h->inq != h->buff) kfree(h->buff); + sdev->handler_data = NULL; kfree(h); } @@ -877,7 +867,6 @@ static struct scsi_device_handler alua_dh = { .check_sense = alua_check_sense, .activate = alua_activate, .set_params = alua_set_params, - .match = alua_match, }; static int __init alua_init(void) diff --git a/kernel/drivers/scsi/device_handler/scsi_dh_emc.c b/kernel/drivers/scsi/device_handler/scsi_dh_emc.c index 6ed1caadb..e6fb97cb1 100644 --- a/kernel/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/kernel/drivers/scsi/device_handler/scsi_dh_emc.c @@ -72,7 +72,6 @@ static const char * lun_state[] = }; struct clariion_dh_data { - struct scsi_dh_data dh_data; /* * Flags: * CLARIION_SHORT_TRESPASS @@ -114,13 +113,6 @@ struct clariion_dh_data { int current_sp; }; -static inline struct clariion_dh_data - *get_clariion_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct clariion_dh_data, - dh_data); -} - /* * Parse MODE_SELECT cmd reply. */ @@ -450,7 +442,7 @@ static int clariion_check_sense(struct scsi_device *sdev, static int clariion_prep_fn(struct scsi_device *sdev, struct request *req) { - struct clariion_dh_data *h = get_clariion_data(sdev); + struct clariion_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->lun_state != CLARIION_LUN_OWNED) { @@ -533,7 +525,7 @@ retry: static int clariion_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); + struct clariion_dh_data *csdev = sdev->handler_data; int result; result = clariion_send_inquiry(sdev, csdev); @@ -574,7 +566,7 @@ done: */ static int clariion_set_params(struct scsi_device *sdev, const char *params) { - struct clariion_dh_data *csdev = get_clariion_data(sdev); + struct clariion_dh_data *csdev = sdev->handler_data; unsigned int hr = 0, st = 0, argc; const char *p = params; int result = SCSI_DH_OK; @@ -622,42 +614,14 @@ done: return result; } -static const struct { - char *vendor; - char *model; -} clariion_dev_list[] = { - {"DGC", "RAID"}, - {"DGC", "DISK"}, - {"DGC", "VRAID"}, - {NULL, NULL}, -}; - -static bool clariion_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; clariion_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor, - strlen(clariion_dev_list[i].vendor)) && - !strncmp(sdev->model, clariion_dev_list[i].model, - strlen(clariion_dev_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev) +static int clariion_bus_attach(struct scsi_device *sdev) { struct clariion_dh_data *h; int err; h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->lun_state = CLARIION_LUN_UNINITIALIZED; h->default_sp = CLARIION_UNBOUND_LU; h->current_sp = CLARIION_UNBOUND_LU; @@ -675,18 +639,19 @@ static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev) CLARIION_NAME, h->current_sp + 'A', h->port, lun_state[h->lun_state], h->default_sp + 'A'); - return &h->dh_data; + + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void clariion_bus_detach(struct scsi_device *sdev) { - struct clariion_dh_data *h = get_clariion_data(sdev); - - kfree(h); + kfree(sdev->handler_data); + sdev->handler_data = NULL; } static struct scsi_device_handler clariion_dh = { @@ -698,7 +663,6 @@ static struct scsi_device_handler clariion_dh = { .activate = clariion_activate, .prep_fn = clariion_prep_fn, .set_params = clariion_set_params, - .match = clariion_match, }; static int __init clariion_init(void) diff --git a/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.c index 485d99544..9406d5f4a 100644 --- a/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/kernel/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -38,7 +38,6 @@ #define HP_SW_PATH_PASSIVE 1 struct hp_sw_dh_data { - struct scsi_dh_data dh_data; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int path_state; int retries; @@ -50,11 +49,6 @@ struct hp_sw_dh_data { static int hp_sw_start_stop(struct hp_sw_dh_data *); -static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct hp_sw_dh_data, dh_data); -} - /* * tur_done - Handle TEST UNIT READY return status * @sdev: sdev the command has been sent to @@ -267,7 +261,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct hp_sw_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->path_state != HP_SW_PATH_ACTIVE) { @@ -292,7 +286,7 @@ static int hp_sw_activate(struct scsi_device *sdev, activate_complete fn, void *data) { int ret = SCSI_DH_OK; - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); + struct hp_sw_dh_data *h = sdev->handler_data; ret = hp_sw_tur(sdev, h); @@ -311,43 +305,14 @@ static int hp_sw_activate(struct scsi_device *sdev, return 0; } -static const struct { - char *vendor; - char *model; -} hp_sw_dh_data_list[] = { - {"COMPAQ", "MSA1000 VOLUME"}, - {"COMPAQ", "HSV110"}, - {"HP", "HSV100"}, - {"DEC", "HSG80"}, - {NULL, NULL}, -}; - -static bool hp_sw_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, - strlen(hp_sw_dh_data_list[i].vendor)) && - !strncmp(sdev->model, hp_sw_dh_data_list[i].model, - strlen(hp_sw_dh_data_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev) +static int hp_sw_bus_attach(struct scsi_device *sdev) { struct hp_sw_dh_data *h; int ret; h = kzalloc(sizeof(*h), GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->path_state = HP_SW_PATH_UNINITIALIZED; h->retries = HP_SW_RETRIES; h->sdev = sdev; @@ -359,17 +324,18 @@ static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev) sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n", HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE? "active":"passive"); - return &h->dh_data; + + sdev->handler_data = h; + return 0; failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void hp_sw_bus_detach( struct scsi_device *sdev ) { - struct hp_sw_dh_data *h = get_hp_sw_data(sdev); - - kfree(h); + kfree(sdev->handler_data); + sdev->handler_data = NULL; } static struct scsi_device_handler hp_sw_dh = { @@ -379,7 +345,6 @@ static struct scsi_device_handler hp_sw_dh = { .detach = hp_sw_bus_detach, .activate = hp_sw_activate, .prep_fn = hp_sw_prep_fn, - .match = hp_sw_match, }; static int __init hp_sw_init(void) diff --git a/kernel/drivers/scsi/device_handler/scsi_dh_rdac.c b/kernel/drivers/scsi/device_handler/scsi_dh_rdac.c index b46ace3d4..93880ed62 100644 --- a/kernel/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/kernel/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -181,7 +181,6 @@ struct c2_inquiry { }; struct rdac_dh_data { - struct scsi_dh_data dh_data; struct rdac_controller *ctlr; #define UNINITIALIZED_LUN (1 << 8) unsigned lun; @@ -260,11 +259,6 @@ do { \ sdev_printk(KERN_INFO, sdev, RDAC_NAME ": " f "\n", ## arg); \ } while (0); -static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev) -{ - return container_of(sdev->scsi_dh_data, struct rdac_dh_data, dh_data); -} - static struct request *get_rdac_req(struct scsi_device *sdev, void *buffer, unsigned buflen, int rw) { @@ -544,7 +538,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev, { struct scsi_sense_hdr sense_hdr; int err = SCSI_DH_IO, ret; - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!ret) @@ -568,7 +562,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev, /* * Command Lock contention */ - err = SCSI_DH_RETRY; + err = SCSI_DH_IMM_RETRY; break; default: break; @@ -589,7 +583,7 @@ static void send_mode_select(struct work_struct *work) container_of(work, struct rdac_controller, ms_work); struct request *rq; struct scsi_device *sdev = ctlr->ms_sdev; - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; struct request_queue *q = sdev->request_queue; int err, retry_cnt = RDAC_RETRY_COUNT; struct rdac_queue_data *tmp, *qdata; @@ -618,6 +612,8 @@ retry: err = mode_select_handle_sense(sdev, h->sense); if (err == SCSI_DH_RETRY && retry_cnt--) goto retry; + if (err == SCSI_DH_IMM_RETRY) + goto retry; } if (err == SCSI_DH_OK) { h->state = RDAC_STATE_ACTIVE; @@ -648,7 +644,7 @@ static int queue_mode_select(struct scsi_device *sdev, if (!qdata) return SCSI_DH_RETRY; - qdata->h = get_rdac_data(sdev); + qdata->h = sdev->handler_data; qdata->callback_fn = fn; qdata->callback_data = data; @@ -667,7 +663,7 @@ static int queue_mode_select(struct scsi_device *sdev, static int rdac_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; int act = 0; @@ -702,7 +698,7 @@ done: static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; int ret = BLKPREP_OK; if (h->state != RDAC_STATE_ACTIVE) { @@ -716,7 +712,7 @@ static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) static int rdac_check_sense(struct scsi_device *sdev, struct scsi_sense_hdr *sense_hdr) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; RDAC_LOG(RDAC_LOG_SENSE, sdev, "array %s, ctlr %d, " "I/O returned with sense %02x/%02x/%02x", @@ -778,56 +774,7 @@ static int rdac_check_sense(struct scsi_device *sdev, return SCSI_RETURN_NOT_HANDLED; } -static const struct { - char *vendor; - char *model; -} rdac_dev_list[] = { - {"IBM", "1722"}, - {"IBM", "1724"}, - {"IBM", "1726"}, - {"IBM", "1742"}, - {"IBM", "1745"}, - {"IBM", "1746"}, - {"IBM", "1813"}, - {"IBM", "1814"}, - {"IBM", "1815"}, - {"IBM", "1818"}, - {"IBM", "3526"}, - {"SGI", "TP9"}, - {"SGI", "IS"}, - {"STK", "OPENstorage D280"}, - {"STK", "FLEXLINE 380"}, - {"SUN", "CSM"}, - {"SUN", "LCSM100"}, - {"SUN", "STK6580_6780"}, - {"SUN", "SUN_6180"}, - {"SUN", "ArrayStorage"}, - {"DELL", "MD3"}, - {"NETAPP", "INF-01-00"}, - {"LSI", "INF-01-00"}, - {"ENGENIO", "INF-01-00"}, - {NULL, NULL}, -}; - -static bool rdac_match(struct scsi_device *sdev) -{ - int i; - - if (scsi_device_tpgs(sdev)) - return false; - - for (i = 0; rdac_dev_list[i].vendor; i++) { - if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor, - strlen(rdac_dev_list[i].vendor)) && - !strncmp(sdev->model, rdac_dev_list[i].model, - strlen(rdac_dev_list[i].model))) { - return true; - } - } - return false; -} - -static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) +static int rdac_bus_attach(struct scsi_device *sdev) { struct rdac_dh_data *h; int err; @@ -836,7 +783,7 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) h = kzalloc(sizeof(*h) , GFP_KERNEL); if (!h) - return ERR_PTR(-ENOMEM); + return -ENOMEM; h->lun = UNINITIALIZED_LUN; h->state = RDAC_STATE_ACTIVE; @@ -861,7 +808,8 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev) RDAC_NAME, h->lun, mode[(int)h->mode], lun_state[(int)h->lun_state]); - return &h->dh_data; + sdev->handler_data = h; + return 0; clean_ctlr: spin_lock(&list_lock); @@ -870,12 +818,12 @@ clean_ctlr: failed: kfree(h); - return ERR_PTR(-EINVAL); + return -EINVAL; } static void rdac_bus_detach( struct scsi_device *sdev ) { - struct rdac_dh_data *h = get_rdac_data(sdev); + struct rdac_dh_data *h = sdev->handler_data; if (h->ctlr && h->ctlr->ms_queued) flush_workqueue(kmpath_rdacd); @@ -884,6 +832,7 @@ static void rdac_bus_detach( struct scsi_device *sdev ) if (h->ctlr) kref_put(&h->ctlr->kref, release_controller); spin_unlock(&list_lock); + sdev->handler_data = NULL; kfree(h); } @@ -895,7 +844,6 @@ static struct scsi_device_handler rdac_dh = { .attach = rdac_bus_attach, .detach = rdac_bus_detach, .activate = rdac_activate, - .match = rdac_match, }; static int __init rdac_init(void) diff --git a/kernel/drivers/scsi/dpt_i2o.c b/kernel/drivers/scsi/dpt_i2o.c index 2806cfbec..d4cda5e96 100644 --- a/kernel/drivers/scsi/dpt_i2o.c +++ b/kernel/drivers/scsi/dpt_i2o.c @@ -1924,6 +1924,9 @@ static void adpt_alpha_info(sysInfo_S* si) #endif #if defined __i386__ + +#include + static void adpt_i386_info(sysInfo_S* si) { // This is all the info we need for now @@ -3562,7 +3565,6 @@ static struct scsi_host_template driver_template = { .slave_configure = adpt_slave_configure, .can_queue = MAX_TO_IOP_MESSAGES, .this_id = 7, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/esas2r/esas2r_main.c b/kernel/drivers/scsi/esas2r/esas2r_main.c index 31f8966b2..33581ba43 100644 --- a/kernel/drivers/scsi/esas2r/esas2r_main.c +++ b/kernel/drivers/scsi/esas2r/esas2r_main.c @@ -256,7 +256,6 @@ static struct scsi_host_template driver_template = { .proc_name = ESAS2R_DRVR_NAME, .change_queue_depth = scsi_change_queue_depth, .max_sectors = 0xFFFF, - .use_blk_tags = 1, }; int sgl_page_size = 512; diff --git a/kernel/drivers/scsi/esp_scsi.c b/kernel/drivers/scsi/esp_scsi.c index 065b25df7..71cb05b1c 100644 --- a/kernel/drivers/scsi/esp_scsi.c +++ b/kernel/drivers/scsi/esp_scsi.c @@ -2694,7 +2694,6 @@ struct scsi_host_template scsi_esp_template = { .use_clustering = ENABLE_CLUSTERING, .max_sectors = 0xffff, .skip_settle_delay = 1, - .use_blk_tags = 1, }; EXPORT_SYMBOL(scsi_esp_template); diff --git a/kernel/drivers/scsi/fcoe/fcoe.c b/kernel/drivers/scsi/fcoe/fcoe.c index d6b6dde64..f1622a058 100644 --- a/kernel/drivers/scsi/fcoe/fcoe.c +++ b/kernel/drivers/scsi/fcoe/fcoe.c @@ -287,7 +287,6 @@ static struct scsi_host_template fcoe_shost_template = { .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, .max_sectors = 0xffff, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -364,7 +363,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, * on the ethertype for the given device */ fcoe->fcoe_packet_type.func = fcoe_rcv; - fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + fcoe->fcoe_packet_type.type = htons(ETH_P_FCOE); fcoe->fcoe_packet_type.dev = netdev; dev_add_pack(&fcoe->fcoe_packet_type); @@ -1873,7 +1872,6 @@ static int fcoe_percpu_receive_thread(void *arg) set_user_nice(current, MIN_NICE); -retry: while (!kthread_should_stop()) { spin_lock_bh(&p->fcoe_rx_list.lock); @@ -1883,7 +1881,7 @@ retry: set_current_state(TASK_INTERRUPTIBLE); spin_unlock_bh(&p->fcoe_rx_list.lock); schedule(); - goto retry; + continue; } spin_unlock_bh(&p->fcoe_rx_list.lock); diff --git a/kernel/drivers/scsi/fcoe/fcoe_transport.c b/kernel/drivers/scsi/fcoe/fcoe_transport.c index bdc898995..d7597c08f 100644 --- a/kernel/drivers/scsi/fcoe/fcoe_transport.c +++ b/kernel/drivers/scsi/fcoe/fcoe_transport.c @@ -58,7 +58,7 @@ MODULE_PARM_DESC(show, " Show attached FCoE transports"); module_param_call(create, fcoe_transport_create, NULL, (void *)FIP_MODE_FABRIC, S_IWUSR); __MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface"); +MODULE_PARM_DESC(create, " Creates fcoe instance on an ethernet interface"); module_param_call(create_vn2vn, fcoe_transport_create, NULL, (void *)FIP_MODE_VN2VN, S_IWUSR); @@ -68,15 +68,15 @@ MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance " module_param_call(destroy, fcoe_transport_destroy, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface"); +MODULE_PARM_DESC(destroy, " Destroys fcoe instance on an ethernet interface"); module_param_call(enable, fcoe_transport_enable, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(enable, "string"); -MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface."); +MODULE_PARM_DESC(enable, " Enables fcoe on an ethernet interface."); module_param_call(disable, fcoe_transport_disable, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(disable, "string"); -MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface."); +MODULE_PARM_DESC(disable, " Disables fcoe on an ethernet interface."); /* notification function for packets from net device */ static struct notifier_block libfcoe_notifier = { diff --git a/kernel/drivers/scsi/fdomain.c b/kernel/drivers/scsi/fdomain.c index fff682976..eefe14d45 100644 --- a/kernel/drivers/scsi/fdomain.c +++ b/kernel/drivers/scsi/fdomain.c @@ -1764,7 +1764,6 @@ struct scsi_host_template fdomain_driver_template = { .can_queue = 1, .this_id = 6, .sg_tablesize = 64, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/fnic/fnic_debugfs.c b/kernel/drivers/scsi/fnic/fnic_debugfs.c index 5980c10c7..d6498fabe 100644 --- a/kernel/drivers/scsi/fnic/fnic_debugfs.c +++ b/kernel/drivers/scsi/fnic/fnic_debugfs.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "fnic.h" static struct dentry *fnic_trace_debugfs_root; diff --git a/kernel/drivers/scsi/fnic/fnic_fcs.c b/kernel/drivers/scsi/fnic/fnic_fcs.c index bf0bbd42e..67669a9e7 100644 --- a/kernel/drivers/scsi/fnic/fnic_fcs.c +++ b/kernel/drivers/scsi/fnic/fnic_fcs.c @@ -939,6 +939,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) struct sk_buff *skb; u16 len; dma_addr_t pa; + int r; len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM; skb = dev_alloc_skb(len); @@ -952,8 +953,19 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) skb_reset_network_header(skb); skb_put(skb, len); pa = pci_map_single(fnic->pdev, skb->data, len, PCI_DMA_FROMDEVICE); + + r = pci_dma_mapping_error(fnic->pdev, pa); + if (r) { + printk(KERN_ERR "PCI mapping failed with error %d\n", r); + goto free_skb; + } + fnic_queue_rq_desc(rq, skb, pa, len); return 0; + +free_skb: + kfree_skb(skb); + return r; } void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) @@ -981,6 +993,7 @@ void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb) struct ethhdr *eth_hdr; struct vlan_ethhdr *vlan_hdr; unsigned long flags; + int r; if (!fnic->vlan_hw_insert) { eth_hdr = (struct ethhdr *)skb_mac_header(skb); @@ -1003,18 +1016,27 @@ void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb) pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); - spin_lock_irqsave(&fnic->wq_lock[0], flags); - if (!vnic_wq_desc_avail(wq)) { - pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE); - spin_unlock_irqrestore(&fnic->wq_lock[0], flags); - kfree_skb(skb); - return; + r = pci_dma_mapping_error(fnic->pdev, pa); + if (r) { + printk(KERN_ERR "PCI mapping failed with error %d\n", r); + goto free_skb; } + spin_lock_irqsave(&fnic->wq_lock[0], flags); + if (!vnic_wq_desc_avail(wq)) + goto irq_restore; + fnic_queue_wq_eth_desc(wq, skb, pa, skb->len, 0 /* hw inserts cos value */, fnic->vlan_id, 1); spin_unlock_irqrestore(&fnic->wq_lock[0], flags); + return; + +irq_restore: + spin_unlock_irqrestore(&fnic->wq_lock[0], flags); + pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE); +free_skb: + kfree_skb(skb); } /* @@ -1071,6 +1093,12 @@ static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE); + ret = pci_dma_mapping_error(fnic->pdev, pa); + if (ret) { + printk(KERN_ERR "DMA map failed with error %d\n", ret); + goto free_skb_on_err; + } + if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_SEND, (char *)eth_hdr, tot_len)) != 0) { printk(KERN_ERR "fnic ctlr frame trace error!!!"); @@ -1082,15 +1110,17 @@ static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) pci_unmap_single(fnic->pdev, pa, tot_len, PCI_DMA_TODEVICE); ret = -1; - goto fnic_send_frame_end; + goto irq_restore; } fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp), 0 /* hw inserts cos value */, fnic->vlan_id, 1, 1, 1); -fnic_send_frame_end: + +irq_restore: spin_unlock_irqrestore(&fnic->wq_lock[0], flags); +free_skb_on_err: if (ret) dev_kfree_skb_any(fp_skb(fp)); diff --git a/kernel/drivers/scsi/fnic/fnic_main.c b/kernel/drivers/scsi/fnic/fnic_main.c index 8a0d4d7b3..58ce9020d 100644 --- a/kernel/drivers/scsi/fnic/fnic_main.c +++ b/kernel/drivers/scsi/fnic/fnic_main.c @@ -118,7 +118,6 @@ static struct scsi_host_template fnic_host_template = { .sg_tablesize = FNIC_MAX_SG_DESC_CNT, .max_sectors = 0xffff, .shost_attrs = fnic_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -697,13 +696,6 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } fnic->fnic_max_tag_id = host->can_queue; - err = scsi_init_shared_tag_map(host, fnic->fnic_max_tag_id); - if (err) { - shost_printk(KERN_ERR, fnic->lport->host, - "Unable to alloc shared tag map\n"); - goto err_out_dev_close; - } - host->max_lun = fnic->config.luns_per_tgt; host->max_id = FNIC_MAX_FCP_TARGET; host->max_cmd_len = FCOE_MAX_CMD_LEN; diff --git a/kernel/drivers/scsi/fnic/fnic_scsi.c b/kernel/drivers/scsi/fnic/fnic_scsi.c index 25436cd28..266b909fe 100644 --- a/kernel/drivers/scsi/fnic/fnic_scsi.c +++ b/kernel/drivers/scsi/fnic/fnic_scsi.c @@ -330,6 +330,7 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, int flags; u8 exch_flags; struct scsi_lun fc_lun; + int r; if (sg_count) { /* For each SGE, create a device desc entry */ @@ -346,6 +347,12 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, io_req->sgl_list, sizeof(io_req->sgl_list[0]) * sg_count, PCI_DMA_TODEVICE); + + r = pci_dma_mapping_error(fnic->pdev, io_req->sgl_list_pa); + if (r) { + printk(KERN_ERR "PCI mapping failed with error %d\n", r); + return SCSI_MLQUEUE_HOST_BUSY; + } } io_req->sense_buf_pa = pci_map_single(fnic->pdev, @@ -353,6 +360,15 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE); + r = pci_dma_mapping_error(fnic->pdev, io_req->sense_buf_pa); + if (r) { + pci_unmap_single(fnic->pdev, io_req->sgl_list_pa, + sizeof(io_req->sgl_list[0]) * sg_count, + PCI_DMA_TODEVICE); + printk(KERN_ERR "PCI mapping failed with error %d\n", r); + return SCSI_MLQUEUE_HOST_BUSY; + } + int_to_scsilun(sc->device->lun, &fc_lun); /* Enqueue the descriptor in the Copy WQ */ diff --git a/kernel/drivers/scsi/fnic/fnic_trace.c b/kernel/drivers/scsi/fnic/fnic_trace.c index 65a9bde26..4e15c4bf0 100644 --- a/kernel/drivers/scsi/fnic/fnic_trace.c +++ b/kernel/drivers/scsi/fnic/fnic_trace.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "fnic_io.h" #include "fnic.h" diff --git a/kernel/drivers/scsi/hosts.c b/kernel/drivers/scsi/hosts.c index 8bb173e01..82ac1cd81 100644 --- a/kernel/drivers/scsi/hosts.c +++ b/kernel/drivers/scsi/hosts.c @@ -217,6 +217,13 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, error = scsi_mq_setup_tags(shost); if (error) goto fail; + } else { + shost->bqt = blk_init_tags(shost->can_queue, + shost->hostt->tag_alloc_policy); + if (!shost->bqt) { + error = -ENOMEM; + goto fail; + } } /* @@ -326,6 +333,17 @@ static void scsi_host_dev_release(struct device *dev) kfree(queuedata); } + if (shost->shost_state == SHOST_CREATED) { + /* + * Free the shost_dev device name here if scsi_host_alloc() + * and scsi_host_put() have been called but neither + * scsi_host_add() nor scsi_host_remove() has been called. + * This avoids that the memory allocated for the shost_dev + * name is leaked. + */ + kfree(dev_name(&shost->shost_dev)); + } + scsi_destroy_command_freelist(shost); if (shost_use_blk_mq(shost)) { if (shost->tag_set.tags) diff --git a/kernel/drivers/scsi/hpsa.c b/kernel/drivers/scsi/hpsa.c index 8eab107b5..a3860367b 100644 --- a/kernel/drivers/scsi/hpsa.c +++ b/kernel/drivers/scsi/hpsa.c @@ -1,6 +1,7 @@ /* * Disk Array driver for HP Smart Array SAS controllers - * Copyright 2000, 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2014-2015 PMC-Sierra, Inc. + * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P. * * 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 @@ -11,11 +12,7 @@ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more details. * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Questions/Comments/Bugfixes to iss_storagedev@hp.com + * Questions/Comments/Bugfixes to storagedev@pmcs.com * */ @@ -43,6 +40,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -55,8 +55,11 @@ #include "hpsa_cmd.h" #include "hpsa.h" -/* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */ -#define HPSA_DRIVER_VERSION "3.4.4-1" +/* + * HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' + * with an optional trailing '-' followed by a byte value (0-255). + */ +#define HPSA_DRIVER_VERSION "3.4.14-0" #define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")" #define HPSA "hpsa" @@ -129,6 +132,12 @@ static const struct pci_device_id hpsa_pci_device_id[] = { {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CC}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CD}, {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSI, 0x103C, 0x21CE}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0580}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0581}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0582}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0583}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0584}, + {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0585}, {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0076}, {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0087}, {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x007D}, @@ -186,6 +195,12 @@ static struct board_type products[] = { {0x21CC103C, "Smart Array", &SA5_access}, {0x21CD103C, "Smart Array", &SA5_access}, {0x21CE103C, "Smart HBA", &SA5_access}, + {0x05809005, "SmartHBA-SA", &SA5_access}, + {0x05819005, "SmartHBA-SA 8i", &SA5_access}, + {0x05829005, "SmartHBA-SA 8i8e", &SA5_access}, + {0x05839005, "SmartHBA-SA 8e", &SA5_access}, + {0x05849005, "SmartHBA-SA 16i", &SA5_access}, + {0x05859005, "SmartHBA-SA 4i4e", &SA5_access}, {0x00761590, "HP Storage P1224 Array Controller", &SA5_access}, {0x00871590, "HP Storage P1224e Array Controller", &SA5_access}, {0x007D1590, "HP Storage P1228 Array Controller", &SA5_access}, @@ -194,6 +209,20 @@ static struct board_type products[] = { {0xFFFF103C, "Unknown Smart Array", &SA5_access}, }; +static struct scsi_transport_template *hpsa_sas_transport_template; +static int hpsa_add_sas_host(struct ctlr_info *h); +static void hpsa_delete_sas_host(struct ctlr_info *h); +static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node, + struct hpsa_scsi_dev_t *device); +static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device); +static struct hpsa_scsi_dev_t + *hpsa_find_device_by_sas_rphy(struct ctlr_info *h, + struct sas_rphy *rphy); + +#define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy) +static const struct scsi_cmnd hpsa_cmd_busy; +#define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle) +static const struct scsi_cmnd hpsa_cmd_idle; static int number_of_controllers; static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id); @@ -207,11 +236,15 @@ static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, static void cmd_free(struct ctlr_info *h, struct CommandList *c); static struct CommandList *cmd_alloc(struct ctlr_info *h); +static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c); +static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h, + struct scsi_cmnd *scmd); static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, void *buff, size_t size, u16 page_code, unsigned char *scsi3addr, int cmd_type); static void hpsa_free_cmd_pool(struct ctlr_info *h); #define VPD_PAGE (1 << 8) +#define HPSA_SIMPLE_ERROR_BITS 0x03 static int hpsa_scsi_queue_command(struct Scsi_Host *h, struct scsi_cmnd *cmd); static void hpsa_scan_start(struct Scsi_Host *); @@ -222,9 +255,10 @@ static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth); static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd); static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd); static int hpsa_slave_alloc(struct scsi_device *sdev); +static int hpsa_slave_configure(struct scsi_device *sdev); static void hpsa_slave_destroy(struct scsi_device *sdev); -static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno); +static void hpsa_update_scsi_devices(struct ctlr_info *h); static int check_for_unit_attention(struct ctlr_info *h, struct CommandList *c); static void check_ioctl_unit_attention(struct ctlr_info *h, @@ -232,7 +266,8 @@ static void check_ioctl_unit_attention(struct ctlr_info *h, /* performant mode helper functions */ static void calc_bucket_map(int *bucket, int num_buckets, int nsgs, int min_blocks, u32 *bucket_map); -static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h); +static void hpsa_free_performant_mode(struct ctlr_info *h); +static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h); static inline u32 next_command(struct ctlr_info *h, u8 q); static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr, u32 *cfg_base_addr, u64 *cfg_base_addr_index, @@ -252,6 +287,12 @@ static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h, struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len, u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk); static void hpsa_command_resubmit_worker(struct work_struct *work); +static u32 lockup_detected(struct ctlr_info *h); +static int detect_controller_lockup(struct ctlr_info *h); +static void hpsa_disable_rld_caching(struct ctlr_info *h); +static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h, + struct ReportExtendedLUNdata *buf, int bufsize); +static int hpsa_luns_changed(struct ctlr_info *h); static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev) { @@ -265,40 +306,86 @@ static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh) return (struct ctlr_info *) *priv; } +static inline bool hpsa_is_cmd_idle(struct CommandList *c) +{ + return c->scsi_cmd == SCSI_CMD_IDLE; +} + +static inline bool hpsa_is_pending_event(struct CommandList *c) +{ + return c->abort_pending || c->reset_pending; +} + +/* extract sense key, asc, and ascq from sense data. -1 means invalid. */ +static void decode_sense_data(const u8 *sense_data, int sense_data_len, + u8 *sense_key, u8 *asc, u8 *ascq) +{ + struct scsi_sense_hdr sshdr; + bool rc; + + *sense_key = -1; + *asc = -1; + *ascq = -1; + + if (sense_data_len < 1) + return; + + rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr); + if (rc) { + *sense_key = sshdr.sense_key; + *asc = sshdr.asc; + *ascq = sshdr.ascq; + } +} + static int check_for_unit_attention(struct ctlr_info *h, struct CommandList *c) { - if (c->err_info->SenseInfo[2] != UNIT_ATTENTION) + u8 sense_key, asc, ascq; + int sense_len; + + if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo)) + sense_len = sizeof(c->err_info->SenseInfo); + else + sense_len = c->err_info->SenseLen; + + decode_sense_data(c->err_info->SenseInfo, sense_len, + &sense_key, &asc, &ascq); + if (sense_key != UNIT_ATTENTION || asc == 0xff) return 0; - switch (c->err_info->SenseInfo[12]) { + switch (asc) { case STATE_CHANGED: - dev_warn(&h->pdev->dev, HPSA "%d: a state change " - "detected, command retried\n", h->ctlr); + dev_warn(&h->pdev->dev, + "%s: a state change detected, command retried\n", + h->devname); break; case LUN_FAILED: dev_warn(&h->pdev->dev, - HPSA "%d: LUN failure detected\n", h->ctlr); + "%s: LUN failure detected\n", h->devname); break; case REPORT_LUNS_CHANGED: dev_warn(&h->pdev->dev, - HPSA "%d: report LUN data changed\n", h->ctlr); + "%s: report LUN data changed\n", h->devname); /* * Note: this REPORT_LUNS_CHANGED condition only occurs on the external * target (array) devices. */ break; case POWER_OR_RESET: - dev_warn(&h->pdev->dev, HPSA "%d: a power on " - "or device reset detected\n", h->ctlr); + dev_warn(&h->pdev->dev, + "%s: a power on or device reset detected\n", + h->devname); break; case UNIT_ATTENTION_CLEARED: - dev_warn(&h->pdev->dev, HPSA "%d: unit attention " - "cleared by another initiator\n", h->ctlr); + dev_warn(&h->pdev->dev, + "%s: unit attention cleared by another initiator\n", + h->devname); break; default: - dev_warn(&h->pdev->dev, HPSA "%d: unknown " - "unit attention detected\n", h->ctlr); + dev_warn(&h->pdev->dev, + "%s: unknown unit attention detected\n", + h->devname); break; } return 1; @@ -314,6 +401,20 @@ static int check_for_busy(struct ctlr_info *h, struct CommandList *c) return 1; } +static u32 lockup_detected(struct ctlr_info *h); +static ssize_t host_show_lockup_detected(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ld; + struct ctlr_info *h; + struct Scsi_Host *shost = class_to_shost(dev); + + h = shost_to_hba(shost); + ld = lockup_detected(h); + + return sprintf(buf, "ld=%d\n", ld); +} + static ssize_t host_store_hp_ssd_smart_path_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -425,7 +526,7 @@ static ssize_t host_show_hp_ssd_smart_path_status(struct device *dev, /* List of controllers which cannot be hard reset on kexec with reset_devices */ static u32 unresettable_controller[] = { 0x324a103C, /* Smart Array P712m */ - 0x324b103C, /* SmartArray P711m */ + 0x324b103C, /* Smart Array P711m */ 0x3223103C, /* Smart Array P800 */ 0x3234103C, /* Smart Array P400 */ 0x3235103C, /* Smart Array P400i */ @@ -467,24 +568,32 @@ static u32 soft_unresettable_controller[] = { 0x409D0E11, /* Smart Array 6400 EM */ }; -static int ctlr_is_hard_resettable(u32 board_id) +static u32 needs_abort_tags_swizzled[] = { + 0x323D103C, /* Smart Array P700m */ + 0x324a103C, /* Smart Array P712m */ + 0x324b103C, /* SmartArray P711m */ +}; + +static int board_id_in_array(u32 a[], int nelems, u32 board_id) { int i; - for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++) - if (unresettable_controller[i] == board_id) - return 0; - return 1; + for (i = 0; i < nelems; i++) + if (a[i] == board_id) + return 1; + return 0; } -static int ctlr_is_soft_resettable(u32 board_id) +static int ctlr_is_hard_resettable(u32 board_id) { - int i; + return !board_id_in_array(unresettable_controller, + ARRAY_SIZE(unresettable_controller), board_id); +} - for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++) - if (soft_unresettable_controller[i] == board_id) - return 0; - return 1; +static int ctlr_is_soft_resettable(u32 board_id) +{ + return !board_id_in_array(soft_unresettable_controller, + ARRAY_SIZE(soft_unresettable_controller), board_id); } static int ctlr_is_resettable(u32 board_id) @@ -493,6 +602,12 @@ static int ctlr_is_resettable(u32 board_id) ctlr_is_soft_resettable(board_id); } +static int ctlr_needs_abort_tags_swizzled(u32 board_id) +{ + return board_id_in_array(needs_abort_tags_swizzled, + ARRAY_SIZE(needs_abort_tags_swizzled), board_id); +} + static ssize_t host_show_resettable(struct device *dev, struct device_attribute *attr, char *buf) { @@ -509,7 +624,7 @@ static inline int is_logical_dev_addr_mode(unsigned char scsi3addr[]) } static const char * const raid_label[] = { "0", "4", "1(+0)", "5", "5+1", "6", - "1(+0)ADM", "UNKNOWN" + "1(+0)ADM", "UNKNOWN", "PHYS DRV" }; #define HPSA_RAID_0 0 #define HPSA_RAID_4 1 @@ -518,7 +633,13 @@ static const char * const raid_label[] = { "0", "4", "1(+0)", "5", "5+1", "6", #define HPSA_RAID_51 4 #define HPSA_RAID_6 5 /* also used for RAID 60 */ #define HPSA_RAID_ADM 6 /* also used for RAID 1+0 ADM */ -#define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 1) +#define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 2) +#define PHYSICAL_DRIVE (ARRAY_SIZE(raid_label) - 1) + +static inline bool is_logical_device(struct hpsa_scsi_dev_t *device) +{ + return !device->physical_device; +} static ssize_t raid_level_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -540,7 +661,7 @@ static ssize_t raid_level_show(struct device *dev, } /* Is this even a logical drive? */ - if (!is_logical_dev_addr_mode(hdev->scsi3addr)) { + if (!is_logical_device(hdev)) { spin_unlock_irqrestore(&h->lock, flags); l = snprintf(buf, PAGE_SIZE, "N/A\n"); return l; @@ -628,12 +749,102 @@ static ssize_t host_show_hp_ssd_smart_path_enabled(struct device *dev, return snprintf(buf, 20, "%d\n", offload_enabled); } +#define MAX_PATHS 8 + +static ssize_t path_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ctlr_info *h; + struct scsi_device *sdev; + struct hpsa_scsi_dev_t *hdev; + unsigned long flags; + int i; + int output_len = 0; + u8 box; + u8 bay; + u8 path_map_index = 0; + char *active; + unsigned char phys_connector[2]; + + sdev = to_scsi_device(dev); + h = sdev_to_hba(sdev); + spin_lock_irqsave(&h->devlock, flags); + hdev = sdev->hostdata; + if (!hdev) { + spin_unlock_irqrestore(&h->devlock, flags); + return -ENODEV; + } + + bay = hdev->bay; + for (i = 0; i < MAX_PATHS; i++) { + path_map_index = 1<active_path_index) + active = "Active"; + else if (hdev->path_map & path_map_index) + active = "Inactive"; + else + continue; + + output_len += scnprintf(buf + output_len, + PAGE_SIZE - output_len, + "[%d:%d:%d:%d] %20.20s ", + h->scsi_host->host_no, + hdev->bus, hdev->target, hdev->lun, + scsi_device_type(hdev->devtype)); + + if (hdev->external || + hdev->devtype == TYPE_RAID || + is_logical_device(hdev)) { + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, + "%s\n", active); + continue; + } + + box = hdev->box[i]; + memcpy(&phys_connector, &hdev->phys_connector[i], + sizeof(phys_connector)); + if (phys_connector[0] < '0') + phys_connector[0] = '0'; + if (phys_connector[1] < '0') + phys_connector[1] = '0'; + if (hdev->phys_connector[i] > 0) + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, + "PORT: %.2s ", + phys_connector); + if (hdev->devtype == TYPE_DISK && hdev->expose_device) { + if (box == 0 || box == 0xFF) { + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, + "BAY: %hhu %s\n", + bay, active); + } else { + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, + "BOX: %hhu BAY: %hhu %s\n", + box, bay, active); + } + } else if (box != 0 && box != 0xFF) { + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, "BOX: %hhu %s\n", + box, active); + } else + output_len += snprintf(buf + output_len, + PAGE_SIZE - output_len, "%s\n", active); + } + + spin_unlock_irqrestore(&h->devlock, flags); + return output_len; +} + static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); static DEVICE_ATTR(lunid, S_IRUGO, lunid_show, NULL); static DEVICE_ATTR(unique_id, S_IRUGO, unique_id_show, NULL); static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); static DEVICE_ATTR(hp_ssd_smart_path_enabled, S_IRUGO, host_show_hp_ssd_smart_path_enabled, NULL); +static DEVICE_ATTR(path_info, S_IRUGO, path_info_show, NULL); static DEVICE_ATTR(hp_ssd_smart_path_status, S_IWUSR|S_IRUGO|S_IROTH, host_show_hp_ssd_smart_path_status, host_store_hp_ssd_smart_path_status); @@ -647,12 +858,15 @@ static DEVICE_ATTR(transport_mode, S_IRUGO, host_show_transport_mode, NULL); static DEVICE_ATTR(resettable, S_IRUGO, host_show_resettable, NULL); +static DEVICE_ATTR(lockup_detected, S_IRUGO, + host_show_lockup_detected, NULL); static struct device_attribute *hpsa_sdev_attrs[] = { &dev_attr_raid_level, &dev_attr_lunid, &dev_attr_unique_id, &dev_attr_hp_ssd_smart_path_enabled, + &dev_attr_path_info, NULL, }; @@ -664,9 +878,13 @@ static struct device_attribute *hpsa_shost_attrs[] = { &dev_attr_resettable, &dev_attr_hp_ssd_smart_path_status, &dev_attr_raid_offload_debug, + &dev_attr_lockup_detected, NULL, }; +#define HPSA_NRESERVED_CMDS (HPSA_CMDS_RESERVED_FOR_ABORTS + \ + HPSA_CMDS_RESERVED_FOR_DRIVER + HPSA_MAX_CONCURRENT_PASSTHRUS) + static struct scsi_host_template hpsa_driver_template = { .module = THIS_MODULE, .name = HPSA, @@ -681,6 +899,7 @@ static struct scsi_host_template hpsa_driver_template = { .eh_device_reset_handler = hpsa_eh_device_reset_handler, .ioctl = hpsa_ioctl, .slave_alloc = hpsa_slave_alloc, + .slave_configure = hpsa_slave_configure, .slave_destroy = hpsa_slave_destroy, #ifdef CONFIG_COMPAT .compat_ioctl = hpsa_compat_ioctl, @@ -743,30 +962,43 @@ static inline u32 next_command(struct ctlr_info *h, u8 q) * a separate special register for submitting commands. */ -/* set_performant_mode: Modify the tag for cciss performant +/* + * set_performant_mode: Modify the tag for cciss performant * set bit 0 for pull model, bits 3-1 for block fetch * register number */ -static void set_performant_mode(struct ctlr_info *h, struct CommandList *c) +#define DEFAULT_REPLY_QUEUE (-1) +static void set_performant_mode(struct ctlr_info *h, struct CommandList *c, + int reply_queue) { if (likely(h->transMethod & CFGTBL_Trans_Performant)) { c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1); - if (likely(h->msix_vector > 0)) + if (unlikely(!h->msix_vector)) + return; + if (likely(reply_queue == DEFAULT_REPLY_QUEUE)) c->Header.ReplyQueue = raw_smp_processor_id() % h->nreply_queues; + else + c->Header.ReplyQueue = reply_queue % h->nreply_queues; } } static void set_ioaccel1_performant_mode(struct ctlr_info *h, - struct CommandList *c) + struct CommandList *c, + int reply_queue) { struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex]; - /* Tell the controller to post the reply to the queue for this + /* + * Tell the controller to post the reply to the queue for this * processor. This seems to give the best I/O throughput. */ - cp->ReplyQueue = smp_processor_id() % h->nreply_queues; - /* Set the bits in the address sent down to include: + if (likely(reply_queue == DEFAULT_REPLY_QUEUE)) + cp->ReplyQueue = smp_processor_id() % h->nreply_queues; + else + cp->ReplyQueue = reply_queue % h->nreply_queues; + /* + * Set the bits in the address sent down to include: * - performant mode bit (bit 0) * - pull count (bits 1-3) * - command type (bits 4-6) @@ -775,20 +1007,48 @@ static void set_ioaccel1_performant_mode(struct ctlr_info *h, IOACCEL1_BUSADDR_CMDTYPE; } -static void set_ioaccel2_performant_mode(struct ctlr_info *h, - struct CommandList *c) +static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h, + struct CommandList *c, + int reply_queue) { - struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex]; + struct hpsa_tmf_struct *cp = (struct hpsa_tmf_struct *) + &h->ioaccel2_cmd_pool[c->cmdindex]; /* Tell the controller to post the reply to the queue for this * processor. This seems to give the best I/O throughput. */ - cp->reply_queue = smp_processor_id() % h->nreply_queues; + if (likely(reply_queue == DEFAULT_REPLY_QUEUE)) + cp->reply_queue = smp_processor_id() % h->nreply_queues; + else + cp->reply_queue = reply_queue % h->nreply_queues; /* Set the bits in the address sent down to include: * - performant mode bit not used in ioaccel mode 2 * - pull count (bits 0-3) * - command type isn't needed for ioaccel2 */ + c->busaddr |= h->ioaccel2_blockFetchTable[0]; +} + +static void set_ioaccel2_performant_mode(struct ctlr_info *h, + struct CommandList *c, + int reply_queue) +{ + struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex]; + + /* + * Tell the controller to post the reply to the queue for this + * processor. This seems to give the best I/O throughput. + */ + if (likely(reply_queue == DEFAULT_REPLY_QUEUE)) + cp->reply_queue = smp_processor_id() % h->nreply_queues; + else + cp->reply_queue = reply_queue % h->nreply_queues; + /* + * Set the bits in the address sent down to include: + * - performant mode bit not used in ioaccel mode 2 + * - pull count (bits 0-3) + * - command type isn't needed for ioaccel2 + */ c->busaddr |= (h->ioaccel2_blockFetchTable[cp->sg_count]); } @@ -821,26 +1081,38 @@ static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h, h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL; } -static void enqueue_cmd_and_start_io(struct ctlr_info *h, - struct CommandList *c) +static void __enqueue_cmd_and_start_io(struct ctlr_info *h, + struct CommandList *c, int reply_queue) { dial_down_lockup_detection_during_fw_flash(h, c); atomic_inc(&h->commands_outstanding); switch (c->cmd_type) { case CMD_IOACCEL1: - set_ioaccel1_performant_mode(h, c); + set_ioaccel1_performant_mode(h, c, reply_queue); writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); break; case CMD_IOACCEL2: - set_ioaccel2_performant_mode(h, c); + set_ioaccel2_performant_mode(h, c, reply_queue); + writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32); + break; + case IOACCEL2_TMF: + set_ioaccel2_tmf_performant_mode(h, c, reply_queue); writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32); break; default: - set_performant_mode(h, c); + set_performant_mode(h, c, reply_queue); h->access.submit_command(h, c); } } +static void enqueue_cmd_and_start_io(struct ctlr_info *h, struct CommandList *c) +{ + if (unlikely(hpsa_is_pending_event(c))) + return finish_cmd(c); + + __enqueue_cmd_and_start_io(h, c, DEFAULT_REPLY_QUEUE); +} + static inline int is_hba_lunid(unsigned char scsi3addr[]) { return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0; @@ -881,8 +1153,62 @@ static int hpsa_find_target_lun(struct ctlr_info *h, return !found; } +static void hpsa_show_dev_msg(const char *level, struct ctlr_info *h, + struct hpsa_scsi_dev_t *dev, char *description) +{ +#define LABEL_SIZE 25 + char label[LABEL_SIZE]; + + if (h == NULL || h->pdev == NULL || h->scsi_host == NULL) + return; + + switch (dev->devtype) { + case TYPE_RAID: + snprintf(label, LABEL_SIZE, "controller"); + break; + case TYPE_ENCLOSURE: + snprintf(label, LABEL_SIZE, "enclosure"); + break; + case TYPE_DISK: + if (dev->external) + snprintf(label, LABEL_SIZE, "external"); + else if (!is_logical_dev_addr_mode(dev->scsi3addr)) + snprintf(label, LABEL_SIZE, "%s", + raid_label[PHYSICAL_DRIVE]); + else + snprintf(label, LABEL_SIZE, "RAID-%s", + dev->raid_level > RAID_UNKNOWN ? "?" : + raid_label[dev->raid_level]); + break; + case TYPE_ROM: + snprintf(label, LABEL_SIZE, "rom"); + break; + case TYPE_TAPE: + snprintf(label, LABEL_SIZE, "tape"); + break; + case TYPE_MEDIUM_CHANGER: + snprintf(label, LABEL_SIZE, "changer"); + break; + default: + snprintf(label, LABEL_SIZE, "UNKNOWN"); + break; + } + + dev_printk(level, &h->pdev->dev, + "scsi %d:%d:%d:%d: %s %s %.8s %.16s %s SSDSmartPathCap%c En%c Exp=%d\n", + h->scsi_host->host_no, dev->bus, dev->target, dev->lun, + description, + scsi_device_type(dev->devtype), + dev->vendor, + dev->model, + label, + dev->offload_config ? '+' : '-', + dev->offload_enabled ? '+' : '-', + dev->expose_device); +} + /* Add an entry into h->dev[] array. */ -static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno, +static int hpsa_scsi_add_entry(struct ctlr_info *h, struct hpsa_scsi_dev_t *device, struct hpsa_scsi_dev_t *added[], int *nadded) { @@ -917,17 +1243,19 @@ static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno, /* This is a non-zero lun of a multi-lun device. * Search through our list and find the device which - * has the same 8 byte LUN address, excepting byte 4. + * has the same 8 byte LUN address, excepting byte 4 and 5. * Assign the same bus and target for this new LUN. * Use the logical unit number from the firmware. */ memcpy(addr1, device->scsi3addr, 8); addr1[4] = 0; + addr1[5] = 0; for (i = 0; i < n; i++) { sd = h->dev[i]; memcpy(addr2, sd->scsi3addr, 8); addr2[4] = 0; - /* differ only in byte 4? */ + addr2[5] = 0; + /* differ only in byte 4 and 5? */ if (memcmp(addr1, addr2, 8) == 0) { device->bus = sd->bus; device->target = sd->target; @@ -948,22 +1276,18 @@ lun_assigned: h->ndevices++; added[*nadded] = device; (*nadded)++; - - /* initially, (before registering with scsi layer) we don't - * know our hostno and we don't want to print anything first - * time anyway (the scsi layer's inquiries will show that info) - */ - /* if (hostno != -1) */ - dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n", - scsi_device_type(device->devtype), hostno, - device->bus, device->target, device->lun); + hpsa_show_dev_msg(KERN_INFO, h, device, + device->expose_device ? "added" : "masked"); + device->offload_to_be_enabled = device->offload_enabled; + device->offload_enabled = 0; return 0; } /* Update an entry in h->dev[] array. */ -static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno, +static void hpsa_scsi_update_entry(struct ctlr_info *h, int entry, struct hpsa_scsi_dev_t *new_entry) { + int offload_enabled; /* assumes h->devlock is held */ BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES); @@ -982,20 +1306,33 @@ static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno, */ h->dev[entry]->raid_map = new_entry->raid_map; h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle; - wmb(); /* ensure raid map updated prior to ->offload_enabled */ } + if (new_entry->hba_ioaccel_enabled) { + h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle; + wmb(); /* set ioaccel_handle *before* hba_ioaccel_enabled */ + } + h->dev[entry]->hba_ioaccel_enabled = new_entry->hba_ioaccel_enabled; h->dev[entry]->offload_config = new_entry->offload_config; h->dev[entry]->offload_to_mirror = new_entry->offload_to_mirror; - h->dev[entry]->offload_enabled = new_entry->offload_enabled; h->dev[entry]->queue_depth = new_entry->queue_depth; - dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d updated.\n", - scsi_device_type(new_entry->devtype), hostno, new_entry->bus, - new_entry->target, new_entry->lun); + /* + * We can turn off ioaccel offload now, but need to delay turning + * it on until we can update h->dev[entry]->phys_disk[], but we + * can't do that until all the devices are updated. + */ + h->dev[entry]->offload_to_be_enabled = new_entry->offload_enabled; + if (!new_entry->offload_enabled) + h->dev[entry]->offload_enabled = 0; + + offload_enabled = h->dev[entry]->offload_enabled; + h->dev[entry]->offload_enabled = h->dev[entry]->offload_to_be_enabled; + hpsa_show_dev_msg(KERN_INFO, h, h->dev[entry], "updated"); + h->dev[entry]->offload_enabled = offload_enabled; } /* Replace an entry from h->dev[] array. */ -static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno, +static void hpsa_scsi_replace_entry(struct ctlr_info *h, int entry, struct hpsa_scsi_dev_t *new_entry, struct hpsa_scsi_dev_t *added[], int *nadded, struct hpsa_scsi_dev_t *removed[], int *nremoved) @@ -1017,13 +1354,13 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno, h->dev[entry] = new_entry; added[*nadded] = new_entry; (*nadded)++; - dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d changed.\n", - scsi_device_type(new_entry->devtype), hostno, new_entry->bus, - new_entry->target, new_entry->lun); + hpsa_show_dev_msg(KERN_INFO, h, new_entry, "replaced"); + new_entry->offload_to_be_enabled = new_entry->offload_enabled; + new_entry->offload_enabled = 0; } /* Remove an entry from h->dev[] array. */ -static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry, +static void hpsa_scsi_remove_entry(struct ctlr_info *h, int entry, struct hpsa_scsi_dev_t *removed[], int *nremoved) { /* assumes h->devlock is held */ @@ -1039,9 +1376,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry, for (i = entry; i < h->ndevices-1; i++) h->dev[i] = h->dev[i+1]; h->ndevices--; - dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n", - scsi_device_type(sd->devtype), hostno, sd->bus, sd->target, - sd->lun); + hpsa_show_dev_msg(KERN_INFO, h, sd, "removed"); } #define SCSI3ADDR_EQ(a, b) ( \ @@ -1113,8 +1448,9 @@ static inline int device_updated(struct hpsa_scsi_dev_t *dev1, return 1; if (dev1->offload_enabled != dev2->offload_enabled) return 1; - if (dev1->queue_depth != dev2->queue_depth) - return 1; + if (!is_logical_dev_addr_mode(dev1->scsi3addr)) + if (dev1->queue_depth != dev2->queue_depth) + return 1; return 0; } @@ -1135,6 +1471,9 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle, #define DEVICE_CHANGED 1 #define DEVICE_SAME 2 #define DEVICE_UPDATED 3 + if (needle == NULL) + return DEVICE_NOT_FOUND; + for (i = 0; i < haystack_size; i++) { if (haystack[i] == NULL) /* previously removed. */ continue; @@ -1203,17 +1542,23 @@ static void hpsa_show_volume_status(struct ctlr_info *h, h->scsi_host->host_no, sd->bus, sd->target, sd->lun); break; + case HPSA_LV_NOT_AVAILABLE: + dev_info(&h->pdev->dev, + "C%d:B%d:T%d:L%d Volume is waiting for transforming volume.\n", + h->scsi_host->host_no, + sd->bus, sd->target, sd->lun); + break; case HPSA_LV_UNDERGOING_RPI: dev_info(&h->pdev->dev, - "C%d:B%d:T%d:L%d Volume is undergoing rapid parity initialization process.\n", + "C%d:B%d:T%d:L%d Volume is undergoing rapid parity init.\n", h->scsi_host->host_no, sd->bus, sd->target, sd->lun); break; case HPSA_LV_PENDING_RPI: dev_info(&h->pdev->dev, - "C%d:B%d:T%d:L%d Volume is queued for rapid parity initialization process.\n", - h->scsi_host->host_no, - sd->bus, sd->target, sd->lun); + "C%d:B%d:T%d:L%d Volume is queued for rapid parity initialization process.\n", + h->scsi_host->host_no, + sd->bus, sd->target, sd->lun); break; case HPSA_LV_ENCRYPTED_NO_KEY: dev_info(&h->pdev->dev, @@ -1283,15 +1628,19 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h, if (nraid_map_entries > RAID_MAP_MAX_ENTRIES) nraid_map_entries = RAID_MAP_MAX_ENTRIES; + logical_drive->nphysical_disks = nraid_map_entries; + qdepth = 0; for (i = 0; i < nraid_map_entries; i++) { logical_drive->phys_disk[i] = NULL; if (!logical_drive->offload_config) continue; for (j = 0; j < ndevices; j++) { + if (dev[j] == NULL) + continue; if (dev[j]->devtype != TYPE_DISK) continue; - if (is_logical_dev_addr_mode(dev[j]->scsi3addr)) + if (is_logical_device(dev[j])) continue; if (dev[j]->ioaccel_handle != dd[i].ioaccel_handle) continue; @@ -1312,7 +1661,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h, */ if (!logical_drive->phys_disk[i]) { logical_drive->offload_enabled = 0; - logical_drive->queue_depth = h->nr_cmds; + logical_drive->offload_to_be_enabled = 0; + logical_drive->queue_depth = 8; } } if (nraid_map_entries) @@ -1331,15 +1681,70 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h, int i; for (i = 0; i < ndevices; i++) { + if (dev[i] == NULL) + continue; if (dev[i]->devtype != TYPE_DISK) continue; - if (!is_logical_dev_addr_mode(dev[i]->scsi3addr)) + if (!is_logical_device(dev[i])) + continue; + + /* + * If offload is currently enabled, the RAID map and + * phys_disk[] assignment *better* not be changing + * and since it isn't changing, we do not need to + * update it. + */ + if (dev[i]->offload_enabled) continue; + hpsa_figure_phys_disk_ptrs(h, dev, ndevices, dev[i]); } } -static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, +static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device) +{ + int rc = 0; + + if (!h->scsi_host) + return 1; + + if (is_logical_device(device)) /* RAID */ + rc = scsi_add_device(h->scsi_host, device->bus, + device->target, device->lun); + else /* HBA */ + rc = hpsa_add_sas_device(h->sas_host, device); + + return rc; +} + +static void hpsa_remove_device(struct ctlr_info *h, + struct hpsa_scsi_dev_t *device) +{ + struct scsi_device *sdev = NULL; + + if (!h->scsi_host) + return; + + if (is_logical_device(device)) { /* RAID */ + sdev = scsi_device_lookup(h->scsi_host, device->bus, + device->target, device->lun); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } else { + /* + * We don't expect to get here. Future commands + * to this device will get a selection timeout as + * if the device were gone. + */ + hpsa_show_dev_msg(KERN_WARNING, h, device, + "didn't find device for removal."); + } + } else /* HBA */ + hpsa_remove_sas_device(device); +} + +static void adjust_hpsa_scsi_table(struct ctlr_info *h, struct hpsa_scsi_dev_t *sd[], int nsds) { /* sd contains scsi3 addresses and devtypes, and inquiry @@ -1351,7 +1756,15 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, unsigned long flags; struct hpsa_scsi_dev_t **added, **removed; int nadded, nremoved; - struct Scsi_Host *sh = NULL; + + /* + * A reset can cause a device status to change + * re-schedule the scan to see what happened. + */ + if (h->reset_in_progress) { + h->drv_req_rescan = 1; + return; + } added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL); removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL); @@ -1379,19 +1792,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, device_change = hpsa_scsi_find_entry(csd, sd, nsds, &entry); if (device_change == DEVICE_NOT_FOUND) { changes++; - hpsa_scsi_remove_entry(h, hostno, i, - removed, &nremoved); + hpsa_scsi_remove_entry(h, i, removed, &nremoved); continue; /* remove ^^^, hence i not incremented */ } else if (device_change == DEVICE_CHANGED) { changes++; - hpsa_scsi_replace_entry(h, hostno, i, sd[entry], + hpsa_scsi_replace_entry(h, i, sd[entry], added, &nadded, removed, &nremoved); /* Set it to NULL to prevent it from being freed * at the bottom of hpsa_update_scsi_devices() */ sd[entry] = NULL; } else if (device_change == DEVICE_UPDATED) { - hpsa_scsi_update_entry(h, hostno, i, sd[entry]); + hpsa_scsi_update_entry(h, i, sd[entry]); } i++; } @@ -1411,9 +1823,7 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, */ if (sd[i]->volume_offline) { hpsa_show_volume_status(h, sd[i]); - dev_info(&h->pdev->dev, "c%db%dt%dl%d: temporarily offline\n", - h->scsi_host->host_no, - sd[i]->bus, sd[i]->target, sd[i]->lun); + hpsa_show_dev_msg(KERN_INFO, h, sd[i], "offline"); continue; } @@ -1421,8 +1831,7 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, h->ndevices, &entry); if (device_change == DEVICE_NOT_FOUND) { changes++; - if (hpsa_scsi_add_entry(h, hostno, sd[i], - added, &nadded) != 0) + if (hpsa_scsi_add_entry(h, sd[i], added, &nadded) != 0) break; sd[i] = NULL; /* prevent from being freed later. */ } else if (device_change == DEVICE_CHANGED) { @@ -1433,6 +1842,17 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, /* but if it does happen, we just ignore that device */ } } + hpsa_update_log_drive_phys_drive_ptrs(h, h->dev, h->ndevices); + + /* Now that h->dev[]->phys_disk[] is coherent, we can enable + * any logical drives that need it enabled. + */ + for (i = 0; i < h->ndevices; i++) { + if (h->dev[i] == NULL) + continue; + h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled; + } + spin_unlock_irqrestore(&h->devlock, flags); /* Monitor devices which are in one of several NOT READY states to be @@ -1450,43 +1870,37 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno, * (or if there are no changes) scsi_scan_host will do it later the * first time through. */ - if (hostno == -1 || !changes) + if (!changes) goto free_and_out; - sh = h->scsi_host; /* Notify scsi mid layer of any removed devices */ for (i = 0; i < nremoved; i++) { - struct scsi_device *sdev = - scsi_device_lookup(sh, removed[i]->bus, - removed[i]->target, removed[i]->lun); - if (sdev != NULL) { - scsi_remove_device(sdev); - scsi_device_put(sdev); - } else { - /* We don't expect to get here. - * future cmds to this device will get selection - * timeout as if the device was gone. - */ - dev_warn(&h->pdev->dev, "didn't find c%db%dt%dl%d " - " for removal.", hostno, removed[i]->bus, - removed[i]->target, removed[i]->lun); - } + if (removed[i] == NULL) + continue; + if (removed[i]->expose_device) + hpsa_remove_device(h, removed[i]); kfree(removed[i]); removed[i] = NULL; } /* Notify scsi mid layer of any added devices */ for (i = 0; i < nadded; i++) { - if (scsi_add_device(sh, added[i]->bus, - added[i]->target, added[i]->lun) == 0) + int rc = 0; + + if (added[i] == NULL) + continue; + if (!(added[i]->expose_device)) + continue; + rc = hpsa_add_device(h, added[i]); + if (!rc) continue; - dev_warn(&h->pdev->dev, "scsi_add_device c%db%dt%dl%d failed, " - "device not added.\n", hostno, added[i]->bus, - added[i]->target, added[i]->lun); + dev_warn(&h->pdev->dev, + "addition failed %d, device not added.", rc); /* now we have to remove it from h->dev, * since it didn't get added to scsi mid layer */ fixup_botched_add(h, added[i]); + h->drv_req_rescan = 1; } free_and_out: @@ -1512,7 +1926,6 @@ static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h, return NULL; } -/* link sdev->hostdata to our per-device structure. */ static int hpsa_slave_alloc(struct scsi_device *sdev) { struct hpsa_scsi_dev_t *sd; @@ -1521,23 +1934,95 @@ static int hpsa_slave_alloc(struct scsi_device *sdev) h = sdev_to_hba(sdev); spin_lock_irqsave(&h->devlock, flags); - sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev), - sdev_id(sdev), sdev->lun); - if (sd != NULL) { - sdev->hostdata = sd; - if (sd->queue_depth) - scsi_change_queue_depth(sdev, sd->queue_depth); + if (sdev_channel(sdev) == HPSA_PHYSICAL_DEVICE_BUS) { + struct scsi_target *starget; + struct sas_rphy *rphy; + + starget = scsi_target(sdev); + rphy = target_to_rphy(starget); + sd = hpsa_find_device_by_sas_rphy(h, rphy); + if (sd) { + sd->target = sdev_id(sdev); + sd->lun = sdev->lun; + } + } else + sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev), + sdev_id(sdev), sdev->lun); + + if (sd && sd->expose_device) { atomic_set(&sd->ioaccel_cmds_out, 0); - } + sdev->hostdata = sd; + } else + sdev->hostdata = NULL; spin_unlock_irqrestore(&h->devlock, flags); return 0; } +/* configure scsi device based on internal per-device structure */ +static int hpsa_slave_configure(struct scsi_device *sdev) +{ + struct hpsa_scsi_dev_t *sd; + int queue_depth; + + sd = sdev->hostdata; + sdev->no_uld_attach = !sd || !sd->expose_device; + + if (sd) + queue_depth = sd->queue_depth != 0 ? + sd->queue_depth : sdev->host->can_queue; + else + queue_depth = sdev->host->can_queue; + + scsi_change_queue_depth(sdev, queue_depth); + + return 0; +} + static void hpsa_slave_destroy(struct scsi_device *sdev) { /* nothing to do. */ } +static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h) +{ + int i; + + if (!h->ioaccel2_cmd_sg_list) + return; + for (i = 0; i < h->nr_cmds; i++) { + kfree(h->ioaccel2_cmd_sg_list[i]); + h->ioaccel2_cmd_sg_list[i] = NULL; + } + kfree(h->ioaccel2_cmd_sg_list); + h->ioaccel2_cmd_sg_list = NULL; +} + +static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h) +{ + int i; + + if (h->chainsize <= 0) + return 0; + + h->ioaccel2_cmd_sg_list = + kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds, + GFP_KERNEL); + if (!h->ioaccel2_cmd_sg_list) + return -ENOMEM; + for (i = 0; i < h->nr_cmds; i++) { + h->ioaccel2_cmd_sg_list[i] = + kmalloc(sizeof(*h->ioaccel2_cmd_sg_list[i]) * + h->maxsgentries, GFP_KERNEL); + if (!h->ioaccel2_cmd_sg_list[i]) + goto clean; + } + return 0; + +clean: + hpsa_free_ioaccel2_sg_chain_blocks(h); + return -ENOMEM; +} + static void hpsa_free_sg_chain_blocks(struct ctlr_info *h) { int i; @@ -1552,7 +2037,7 @@ static void hpsa_free_sg_chain_blocks(struct ctlr_info *h) h->cmd_sg_list = NULL; } -static int hpsa_allocate_sg_chain_blocks(struct ctlr_info *h) +static int hpsa_alloc_sg_chain_blocks(struct ctlr_info *h) { int i; @@ -1580,12 +2065,45 @@ clean: return -ENOMEM; } -static int hpsa_map_sg_chain_block(struct ctlr_info *h, - struct CommandList *c) +static int hpsa_map_ioaccel2_sg_chain_block(struct ctlr_info *h, + struct io_accel2_cmd *cp, struct CommandList *c) { - struct SGDescriptor *chain_sg, *chain_block; + struct ioaccel2_sg_element *chain_block; u64 temp64; - u32 chain_len; + u32 chain_size; + + chain_block = h->ioaccel2_cmd_sg_list[c->cmdindex]; + chain_size = le32_to_cpu(cp->sg[0].length); + temp64 = pci_map_single(h->pdev, chain_block, chain_size, + PCI_DMA_TODEVICE); + if (dma_mapping_error(&h->pdev->dev, temp64)) { + /* prevent subsequent unmapping */ + cp->sg->address = 0; + return -1; + } + cp->sg->address = cpu_to_le64(temp64); + return 0; +} + +static void hpsa_unmap_ioaccel2_sg_chain_block(struct ctlr_info *h, + struct io_accel2_cmd *cp) +{ + struct ioaccel2_sg_element *chain_sg; + u64 temp64; + u32 chain_size; + + chain_sg = cp->sg; + temp64 = le64_to_cpu(chain_sg->address); + chain_size = le32_to_cpu(cp->sg[0].length); + pci_unmap_single(h->pdev, temp64, chain_size, PCI_DMA_TODEVICE); +} + +static int hpsa_map_sg_chain_block(struct ctlr_info *h, + struct CommandList *c) +{ + struct SGDescriptor *chain_sg, *chain_block; + u64 temp64; + u32 chain_len; chain_sg = &c->SG[h->max_cmd_sg_entries - 1]; chain_block = h->cmd_sg_list[c->cmdindex]; @@ -1629,6 +2147,7 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h, { int data_len; int retry = 0; + u32 ioaccel2_resid = 0; switch (c2->error_data.serv_response) { case IOACCEL2_SERV_RESPONSE_COMPLETE: @@ -1636,9 +2155,6 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h, case IOACCEL2_STATUS_SR_TASK_COMP_GOOD: break; case IOACCEL2_STATUS_SR_TASK_COMP_CHK_COND: - dev_warn(&h->pdev->dev, - "%s: task complete with check condition.\n", - "HP SSD Smart Path"); cmd->result |= SAM_STAT_CHECK_CONDITION; if (c2->error_data.data_present != IOACCEL2_SENSE_DATA_PRESENT) { @@ -1658,58 +2174,56 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h, retry = 1; break; case IOACCEL2_STATUS_SR_TASK_COMP_BUSY: - dev_warn(&h->pdev->dev, - "%s: task complete with BUSY status.\n", - "HP SSD Smart Path"); retry = 1; break; case IOACCEL2_STATUS_SR_TASK_COMP_RES_CON: - dev_warn(&h->pdev->dev, - "%s: task complete with reservation conflict.\n", - "HP SSD Smart Path"); retry = 1; break; case IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL: - /* Make scsi midlayer do unlimited retries */ - cmd->result = DID_IMM_RETRY << 16; + retry = 1; break; case IOACCEL2_STATUS_SR_TASK_COMP_ABORTED: - dev_warn(&h->pdev->dev, - "%s: task complete with aborted status.\n", - "HP SSD Smart Path"); retry = 1; break; default: - dev_warn(&h->pdev->dev, - "%s: task complete with unrecognized status: 0x%02x\n", - "HP SSD Smart Path", c2->error_data.status); retry = 1; break; } break; case IOACCEL2_SERV_RESPONSE_FAILURE: - /* don't expect to get here. */ - dev_warn(&h->pdev->dev, - "unexpected delivery or target failure, status = 0x%02x\n", - c2->error_data.status); - retry = 1; + switch (c2->error_data.status) { + case IOACCEL2_STATUS_SR_IO_ERROR: + case IOACCEL2_STATUS_SR_IO_ABORTED: + case IOACCEL2_STATUS_SR_OVERRUN: + retry = 1; + break; + case IOACCEL2_STATUS_SR_UNDERRUN: + cmd->result = (DID_OK << 16); /* host byte */ + cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */ + ioaccel2_resid = get_unaligned_le32( + &c2->error_data.resid_cnt[0]); + scsi_set_resid(cmd, ioaccel2_resid); + break; + case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE: + case IOACCEL2_STATUS_SR_INVALID_DEVICE: + case IOACCEL2_STATUS_SR_IOACCEL_DISABLED: + /* We will get an event from ctlr to trigger rescan */ + retry = 1; + break; + default: + retry = 1; + } break; case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE: break; case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS: break; case IOACCEL2_SERV_RESPONSE_TMF_REJECTED: - dev_warn(&h->pdev->dev, "task management function rejected.\n"); retry = 1; break; case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN: - dev_warn(&h->pdev->dev, "task management function invalid LUN\n"); break; default: - dev_warn(&h->pdev->dev, - "%s: Unrecognized server response: 0x%02x\n", - "HP SSD Smart Path", - c2->error_data.serv_response); retry = 1; break; } @@ -1717,6 +2231,87 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h, return retry; /* retry on raid path? */ } +static void hpsa_cmd_resolve_events(struct ctlr_info *h, + struct CommandList *c) +{ + bool do_wake = false; + + /* + * Prevent the following race in the abort handler: + * + * 1. LLD is requested to abort a SCSI command + * 2. The SCSI command completes + * 3. The struct CommandList associated with step 2 is made available + * 4. New I/O request to LLD to another LUN re-uses struct CommandList + * 5. Abort handler follows scsi_cmnd->host_scribble and + * finds struct CommandList and tries to aborts it + * Now we have aborted the wrong command. + * + * Reset c->scsi_cmd here so that the abort or reset handler will know + * this command has completed. Then, check to see if the handler is + * waiting for this command, and, if so, wake it. + */ + c->scsi_cmd = SCSI_CMD_IDLE; + mb(); /* Declare command idle before checking for pending events. */ + if (c->abort_pending) { + do_wake = true; + c->abort_pending = false; + } + if (c->reset_pending) { + unsigned long flags; + struct hpsa_scsi_dev_t *dev; + + /* + * There appears to be a reset pending; lock the lock and + * reconfirm. If so, then decrement the count of outstanding + * commands and wake the reset command if this is the last one. + */ + spin_lock_irqsave(&h->lock, flags); + dev = c->reset_pending; /* Re-fetch under the lock. */ + if (dev && atomic_dec_and_test(&dev->reset_cmds_out)) + do_wake = true; + c->reset_pending = NULL; + spin_unlock_irqrestore(&h->lock, flags); + } + + if (do_wake) + wake_up_all(&h->event_sync_wait_queue); +} + +static void hpsa_cmd_resolve_and_free(struct ctlr_info *h, + struct CommandList *c) +{ + hpsa_cmd_resolve_events(h, c); + cmd_tagged_free(h, c); +} + +static void hpsa_cmd_free_and_done(struct ctlr_info *h, + struct CommandList *c, struct scsi_cmnd *cmd) +{ + hpsa_cmd_resolve_and_free(h, c); + cmd->scsi_done(cmd); +} + +static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c) +{ + INIT_WORK(&c->work, hpsa_command_resubmit_worker); + queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work); +} + +static void hpsa_set_scsi_cmd_aborted(struct scsi_cmnd *cmd) +{ + cmd->result = DID_ABORT << 16; +} + +static void hpsa_cmd_abort_and_free(struct ctlr_info *h, struct CommandList *c, + struct scsi_cmnd *cmd) +{ + hpsa_set_scsi_cmd_aborted(cmd); + dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n", + c->Request.CDB, c->err_info->ScsiStatus); + hpsa_cmd_resolve_and_free(h, c); +} + static void process_ioaccel2_completion(struct ctlr_info *h, struct CommandList *c, struct scsi_cmnd *cmd, struct hpsa_scsi_dev_t *dev) @@ -1725,35 +2320,56 @@ static void process_ioaccel2_completion(struct ctlr_info *h, /* check for good status */ if (likely(c2->error_data.serv_response == 0 && - c2->error_data.status == 0)) { - cmd_free(h, c); - cmd->scsi_done(cmd); - return; - } + c2->error_data.status == 0)) + return hpsa_cmd_free_and_done(h, c, cmd); - /* Any RAID offload error results in retry which will use + /* + * Any RAID offload error results in retry which will use * the normal I/O path so the controller can handle whatever's * wrong. */ - if (is_logical_dev_addr_mode(dev->scsi3addr) && + if (is_logical_device(dev) && c2->error_data.serv_response == IOACCEL2_SERV_RESPONSE_FAILURE) { if (c2->error_data.status == IOACCEL2_STATUS_SR_IOACCEL_DISABLED) dev->offload_enabled = 0; - goto retry_cmd; + + return hpsa_retry_cmd(h, c); } if (handle_ioaccel_mode2_error(h, c, cmd, c2)) - goto retry_cmd; + return hpsa_retry_cmd(h, c); - cmd_free(h, c); - cmd->scsi_done(cmd); - return; + return hpsa_cmd_free_and_done(h, c, cmd); +} -retry_cmd: - INIT_WORK(&c->work, hpsa_command_resubmit_worker); - queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work); +/* Returns 0 on success, < 0 otherwise. */ +static int hpsa_evaluate_tmf_status(struct ctlr_info *h, + struct CommandList *cp) +{ + u8 tmf_status = cp->err_info->ScsiStatus; + + switch (tmf_status) { + case CISS_TMF_COMPLETE: + /* + * CISS_TMF_COMPLETE never happens, instead, + * ei->CommandStatus == 0 for this case. + */ + case CISS_TMF_SUCCESS: + return 0; + case CISS_TMF_INVALID_FRAME: + case CISS_TMF_NOT_SUPPORTED: + case CISS_TMF_FAILED: + case CISS_TMF_WRONG_LUN: + case CISS_TMF_OVERLAPPED_TAG: + break; + default: + dev_warn(&h->pdev->dev, "Unknown TMF status: 0x%02x\n", + tmf_status); + break; + } + return -tmf_status; } static void complete_scsi_command(struct CommandList *cp) @@ -1762,51 +2378,58 @@ static void complete_scsi_command(struct CommandList *cp) struct ctlr_info *h; struct ErrorInfo *ei; struct hpsa_scsi_dev_t *dev; + struct io_accel2_cmd *c2; - unsigned char sense_key; - unsigned char asc; /* additional sense code */ - unsigned char ascq; /* additional sense code qualifier */ + u8 sense_key; + u8 asc; /* additional sense code */ + u8 ascq; /* additional sense code qualifier */ unsigned long sense_data_size; ei = cp->err_info; cmd = cp->scsi_cmd; h = cp->h; dev = cmd->device->hostdata; + c2 = &h->ioaccel2_cmd_pool[cp->cmdindex]; scsi_dma_unmap(cmd); /* undo the DMA mappings */ if ((cp->cmd_type == CMD_SCSI) && (le16_to_cpu(cp->Header.SGTotal) > h->max_cmd_sg_entries)) hpsa_unmap_sg_chain_block(h, cp); + if ((cp->cmd_type == CMD_IOACCEL2) && + (c2->sg[0].chain_indicator == IOACCEL2_CHAIN)) + hpsa_unmap_ioaccel2_sg_chain_block(h, c2); + cmd->result = (DID_OK << 16); /* host byte */ cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */ if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1) atomic_dec(&cp->phys_disk->ioaccel_cmds_out); - if (cp->cmd_type == CMD_IOACCEL2) - return process_ioaccel2_completion(h, cp, cmd, dev); - - cmd->result |= ei->ScsiStatus; + /* + * We check for lockup status here as it may be set for + * CMD_SCSI, CMD_IOACCEL1 and CMD_IOACCEL2 commands by + * fail_all_oustanding_cmds() + */ + if (unlikely(ei->CommandStatus == CMD_CTLR_LOCKUP)) { + /* DID_NO_CONNECT will prevent a retry */ + cmd->result = DID_NO_CONNECT << 16; + return hpsa_cmd_free_and_done(h, cp, cmd); + } - scsi_set_resid(cmd, ei->ResidualCnt); - if (ei->CommandStatus == 0) { - if (cp->cmd_type == CMD_IOACCEL1) - atomic_dec(&cp->phys_disk->ioaccel_cmds_out); - cmd_free(h, cp); - cmd->scsi_done(cmd); - return; + if ((unlikely(hpsa_is_pending_event(cp)))) { + if (cp->reset_pending) + return hpsa_cmd_resolve_and_free(h, cp); + if (cp->abort_pending) + return hpsa_cmd_abort_and_free(h, cp, cmd); } - /* copy the sense data */ - if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo)) - sense_data_size = SCSI_SENSE_BUFFERSIZE; - else - sense_data_size = sizeof(ei->SenseInfo); - if (ei->SenseLen < sense_data_size) - sense_data_size = ei->SenseLen; + if (cp->cmd_type == CMD_IOACCEL2) + return process_ioaccel2_completion(h, cp, cmd, dev); - memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size); + scsi_set_resid(cmd, ei->ResidualCnt); + if (ei->CommandStatus == 0) + return hpsa_cmd_free_and_done(h, cp, cmd); /* For I/O accelerator commands, copy over some fields to the normal * CISS header used below for error handling. @@ -1825,13 +2448,10 @@ static void complete_scsi_command(struct CommandList *cp) * the normal I/O path so the controller can handle whatever's * wrong. */ - if (is_logical_dev_addr_mode(dev->scsi3addr)) { + if (is_logical_device(dev)) { if (ei->CommandStatus == CMD_IOACCEL_DISABLED) dev->offload_enabled = 0; - INIT_WORK(&cp->work, hpsa_command_resubmit_worker); - queue_work_on(raw_smp_processor_id(), - h->resubmit_wq, &cp->work); - return; + return hpsa_retry_cmd(h, cp); } } @@ -1839,14 +2459,18 @@ static void complete_scsi_command(struct CommandList *cp) switch (ei->CommandStatus) { case CMD_TARGET_STATUS: - if (ei->ScsiStatus) { - /* Get sense key */ - sense_key = 0xf & ei->SenseInfo[2]; - /* Get additional sense code */ - asc = ei->SenseInfo[12]; - /* Get addition sense code qualifier */ - ascq = ei->SenseInfo[13]; - } + cmd->result |= ei->ScsiStatus; + /* copy the sense data */ + if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo)) + sense_data_size = SCSI_SENSE_BUFFERSIZE; + else + sense_data_size = sizeof(ei->SenseInfo); + if (ei->SenseLen < sense_data_size) + sense_data_size = ei->SenseLen; + memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size); + if (ei->ScsiStatus) + decode_sense_data(ei->SenseInfo, sense_data_size, + &sense_key, &asc, &ascq); if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) { if (sense_key == ABORTED_COMMAND) { cmd->result |= DID_SOFT_ERROR << 16; @@ -1918,10 +2542,8 @@ static void complete_scsi_command(struct CommandList *cp) cp->Request.CDB); break; case CMD_ABORTED: - cmd->result = DID_ABORT << 16; - dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n", - cp->Request.CDB, ei->ScsiStatus); - break; + /* Return now to avoid calling scsi_done(). */ + return hpsa_cmd_abort_and_free(h, cp, cmd); case CMD_ABORT_FAILED: cmd->result = DID_ERROR << 16; dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n", @@ -1941,6 +2563,10 @@ static void complete_scsi_command(struct CommandList *cp) cmd->result = DID_ERROR << 16; dev_warn(&h->pdev->dev, "Command unabortable\n"); break; + case CMD_TMF_STATUS: + if (hpsa_evaluate_tmf_status(h, cp)) /* TMF failed? */ + cmd->result = DID_ERROR << 16; + break; case CMD_IOACCEL_DISABLED: /* This only handles the direct pass-through case since RAID * offload is handled above. Just attempt a retry. @@ -1954,8 +2580,8 @@ static void complete_scsi_command(struct CommandList *cp) dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n", cp, ei->CommandStatus); } - cmd_free(h, cp); - cmd->scsi_done(cmd); + + return hpsa_cmd_free_and_done(h, cp, cmd); } static void hpsa_pci_unmap(struct pci_dev *pdev, @@ -1998,14 +2624,36 @@ static int hpsa_map_one(struct pci_dev *pdev, return 0; } -static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h, - struct CommandList *c) +#define NO_TIMEOUT ((unsigned long) -1) +#define DEFAULT_TIMEOUT 30000 /* milliseconds */ +static int hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h, + struct CommandList *c, int reply_queue, unsigned long timeout_msecs) { DECLARE_COMPLETION_ONSTACK(wait); c->waiting = &wait; - enqueue_cmd_and_start_io(h, c); - wait_for_completion(&wait); + __enqueue_cmd_and_start_io(h, c, reply_queue); + if (timeout_msecs == NO_TIMEOUT) { + /* TODO: get rid of this no-timeout thing */ + wait_for_completion_io(&wait); + return IO_OK; + } + if (!wait_for_completion_io_timeout(&wait, + msecs_to_jiffies(timeout_msecs))) { + dev_warn(&h->pdev->dev, "Command timed out.\n"); + return -ETIMEDOUT; + } + return IO_OK; +} + +static int hpsa_scsi_do_simple_cmd(struct ctlr_info *h, struct CommandList *c, + int reply_queue, unsigned long timeout_msecs) +{ + if (unlikely(lockup_detected(h))) { + c->err_info->CommandStatus = CMD_CTLR_LOCKUP; + return IO_OK; + } + return hpsa_scsi_do_simple_cmd_core(h, c, reply_queue, timeout_msecs); } static u32 lockup_detected(struct ctlr_info *h) @@ -2020,25 +2668,19 @@ static u32 lockup_detected(struct ctlr_info *h) return rc; } -static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h, - struct CommandList *c) -{ - /* If controller lockup detected, fake a hardware error. */ - if (unlikely(lockup_detected(h))) - c->err_info->CommandStatus = CMD_HARDWARE_ERR; - else - hpsa_scsi_do_simple_cmd_core(h, c); -} - #define MAX_DRIVER_CMD_RETRIES 25 -static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h, - struct CommandList *c, int data_direction) +static int hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h, + struct CommandList *c, int data_direction, unsigned long timeout_msecs) { int backoff_time = 10, retry_count = 0; + int rc; do { memset(c->err_info, 0, sizeof(*c->err_info)); - hpsa_scsi_do_simple_cmd_core(h, c); + rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, + timeout_msecs); + if (rc) + break; retry_count++; if (retry_count > 3) { msleep(backoff_time); @@ -2049,6 +2691,9 @@ static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h, check_for_busy(h, c)) && retry_count <= MAX_DRIVER_CMD_RETRIES); hpsa_pci_unmap(h->pdev, c, 1, data_direction); + if (retry_count > MAX_DRIVER_CMD_RETRIES) + rc = -EIO; + return rc; } static void hpsa_print_cmd(struct ctlr_info *h, char *txt, @@ -2072,16 +2717,23 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h, { const struct ErrorInfo *ei = cp->err_info; struct device *d = &cp->h->pdev->dev; - const u8 *sd = ei->SenseInfo; + u8 sense_key, asc, ascq; + int sense_len; switch (ei->CommandStatus) { case CMD_TARGET_STATUS: + if (ei->SenseLen > sizeof(ei->SenseInfo)) + sense_len = sizeof(ei->SenseInfo); + else + sense_len = ei->SenseLen; + decode_sense_data(ei->SenseInfo, sense_len, + &sense_key, &asc, &ascq); hpsa_print_cmd(h, "SCSI status", cp); if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) - dev_warn(d, "SCSI Status = 02, Sense key = %02x, ASC = %02x, ASCQ = %02x\n", - sd[2] & 0x0f, sd[12], sd[13]); + dev_warn(d, "SCSI Status = 02, Sense key = 0x%02x, ASC = 0x%02x, ASCQ = 0x%02x\n", + sense_key, asc, ascq); else - dev_warn(d, "SCSI Status = %02x\n", ei->ScsiStatus); + dev_warn(d, "SCSI Status = 0x%02x\n", ei->ScsiStatus); if (ei->ScsiStatus == 0) dev_warn(d, "SCSI status is abnormally zero. " "(probably indicates selection timeout " @@ -2125,6 +2777,9 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h, case CMD_UNABORTABLE: hpsa_print_cmd(h, "unabortable", cp); break; + case CMD_CTLR_LOCKUP: + hpsa_print_cmd(h, "controller lockup detected", cp); + break; default: hpsa_print_cmd(h, "unknown status", cp); dev_warn(d, "Unknown command status %x\n", @@ -2142,17 +2797,15 @@ static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr, c = cmd_alloc(h); - if (c == NULL) { - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -ENOMEM; - } - if (fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize, page, scsi3addr, TYPE_CMD)) { rc = -1; goto out; } - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if (rc) + goto out; ei = c->err_info; if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { hpsa_scsi_interpret_error(h, c); @@ -2163,63 +2816,150 @@ out: return rc; } -static int hpsa_bmic_ctrl_mode_sense(struct ctlr_info *h, - unsigned char *scsi3addr, unsigned char page, - struct bmic_controller_parameters *buf, size_t bufsize) +static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr, + u8 reset_type, int reply_queue) { int rc = IO_OK; struct CommandList *c; struct ErrorInfo *ei; c = cmd_alloc(h); - if (c == NULL) { /* trouble... */ - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -ENOMEM; - } - if (fill_cmd(c, BMIC_SENSE_CONTROLLER_PARAMETERS, h, buf, bufsize, - page, scsi3addr, TYPE_CMD)) { - rc = -1; + + /* fill_cmd can't fail here, no data buffer to map. */ + (void) fill_cmd(c, reset_type, h, NULL, 0, 0, + scsi3addr, TYPE_MSG); + rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT); + if (rc) { + dev_warn(&h->pdev->dev, "Failed to send reset command\n"); goto out; } - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); + /* no unmap needed here because no data xfer. */ + ei = c->err_info; - if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { + if (ei->CommandStatus != 0) { hpsa_scsi_interpret_error(h, c); rc = -1; } out: cmd_free(h, c); return rc; +} + +static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c, + struct hpsa_scsi_dev_t *dev, + unsigned char *scsi3addr) +{ + int i; + bool match = false; + struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex]; + struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2; + + if (hpsa_is_cmd_idle(c)) + return false; + + switch (c->cmd_type) { + case CMD_SCSI: + case CMD_IOCTL_PEND: + match = !memcmp(scsi3addr, &c->Header.LUN.LunAddrBytes, + sizeof(c->Header.LUN.LunAddrBytes)); + break; + + case CMD_IOACCEL1: + case CMD_IOACCEL2: + if (c->phys_disk == dev) { + /* HBA mode match */ + match = true; + } else { + /* Possible RAID mode -- check each phys dev. */ + /* FIXME: Do we need to take out a lock here? If + * so, we could just call hpsa_get_pdisk_of_ioaccel2() + * instead. */ + for (i = 0; i < dev->nphysical_disks && !match; i++) { + /* FIXME: an alternate test might be + * + * match = dev->phys_disk[i]->ioaccel_handle + * == c2->scsi_nexus; */ + match = dev->phys_disk[i] == c->phys_disk; + } + } + break; + + case IOACCEL2_TMF: + for (i = 0; i < dev->nphysical_disks && !match; i++) { + match = dev->phys_disk[i]->ioaccel_handle == + le32_to_cpu(ac->it_nexus); + } + break; + + case 0: /* The command is in the middle of being initialized. */ + match = false; + break; + + default: + dev_err(&h->pdev->dev, "unexpected cmd_type: %d\n", + c->cmd_type); + BUG(); } -static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr, - u8 reset_type) + return match; +} + +static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev, + unsigned char *scsi3addr, u8 reset_type, int reply_queue) { - int rc = IO_OK; - struct CommandList *c; - struct ErrorInfo *ei; + int i; + int rc = 0; - c = cmd_alloc(h); + /* We can really only handle one reset at a time */ + if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) { + dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n"); + return -EINTR; + } - if (c == NULL) { /* trouble... */ - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -ENOMEM; + BUG_ON(atomic_read(&dev->reset_cmds_out) != 0); + + for (i = 0; i < h->nr_cmds; i++) { + struct CommandList *c = h->cmd_pool + i; + int refcount = atomic_inc_return(&c->refcount); + + if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev, scsi3addr)) { + unsigned long flags; + + /* + * Mark the target command as having a reset pending, + * then lock a lock so that the command cannot complete + * while we're considering it. If the command is not + * idle then count it; otherwise revoke the event. + */ + c->reset_pending = dev; + spin_lock_irqsave(&h->lock, flags); /* Implied MB */ + if (!hpsa_is_cmd_idle(c)) + atomic_inc(&dev->reset_cmds_out); + else + c->reset_pending = NULL; + spin_unlock_irqrestore(&h->lock, flags); + } + + cmd_free(h, c); } - /* fill_cmd can't fail here, no data buffer to map. */ - (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, - scsi3addr, TYPE_MSG); - c->Request.CDB[1] = reset_type; /* fill_cmd defaults to LUN reset */ - hpsa_scsi_do_simple_cmd_core(h, c); - /* no unmap needed here because no data xfer. */ + rc = hpsa_send_reset(h, scsi3addr, reset_type, reply_queue); + if (!rc) + wait_event(h->event_sync_wait_queue, + atomic_read(&dev->reset_cmds_out) == 0 || + lockup_detected(h)); - ei = c->err_info; - if (ei->CommandStatus != 0) { - hpsa_scsi_interpret_error(h, c); - rc = -1; + if (unlikely(lockup_detected(h))) { + dev_warn(&h->pdev->dev, + "Controller lockup detected during reset wait\n"); + rc = -ENODEV; } - cmd_free(h, c); + + if (unlikely(rc)) + atomic_set(&dev->reset_cmds_out, 0); + + mutex_unlock(&h->reset_mutex); return rc; } @@ -2328,23 +3068,23 @@ static int hpsa_get_raid_map(struct ctlr_info *h, struct ErrorInfo *ei; c = cmd_alloc(h); - if (c == NULL) { - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -ENOMEM; - } + if (fill_cmd(c, HPSA_GET_RAID_MAP, h, &this_device->raid_map, sizeof(this_device->raid_map), 0, scsi3addr, TYPE_CMD)) { - dev_warn(&h->pdev->dev, "Out of memory in hpsa_get_raid_map()\n"); + dev_warn(&h->pdev->dev, "hpsa_get_raid_map fill_cmd failed\n"); cmd_free(h, c); - return -ENOMEM; + return -1; } - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if (rc) + goto out; ei = c->err_info; if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { hpsa_scsi_interpret_error(h, c); - cmd_free(h, c); - return -1; + rc = -1; + goto out; } cmd_free(h, c); @@ -2356,18 +3096,22 @@ static int hpsa_get_raid_map(struct ctlr_info *h, } hpsa_debug_map_buff(h, rc, &this_device->raid_map); return rc; +out: + cmd_free(h, c); + return rc; } -static int hpsa_bmic_id_physical_device(struct ctlr_info *h, +static int hpsa_bmic_sense_subsystem_information(struct ctlr_info *h, unsigned char scsi3addr[], u16 bmic_device_index, - struct bmic_identify_physical_device *buf, size_t bufsize) + struct bmic_sense_subsystem_info *buf, size_t bufsize) { int rc = IO_OK; struct CommandList *c; struct ErrorInfo *ei; c = cmd_alloc(h); - rc = fill_cmd(c, BMIC_IDENTIFY_PHYSICAL_DEVICE, h, buf, bufsize, + + rc = fill_cmd(c, BMIC_SENSE_SUBSYSTEM_INFORMATION, h, buf, bufsize, 0, RAID_CTLR_LUNID, TYPE_CMD); if (rc) goto out; @@ -2375,7 +3119,10 @@ static int hpsa_bmic_id_physical_device(struct ctlr_info *h, c->Request.CDB[2] = bmic_device_index & 0xff; c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff; - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if (rc) + goto out; ei = c->err_info; if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { hpsa_scsi_interpret_error(h, c); @@ -2386,26 +3133,145 @@ out: return rc; } -static int hpsa_vpd_page_supported(struct ctlr_info *h, - unsigned char scsi3addr[], u8 page) +static int hpsa_bmic_id_controller(struct ctlr_info *h, + struct bmic_identify_controller *buf, size_t bufsize) { - int rc; - int i; - int pages; - unsigned char *buf, bufsize; + int rc = IO_OK; + struct CommandList *c; + struct ErrorInfo *ei; - buf = kzalloc(256, GFP_KERNEL); - if (!buf) - return 0; + c = cmd_alloc(h); - /* Get the size of the page list first */ - rc = hpsa_scsi_do_inquiry(h, scsi3addr, - VPD_PAGE | HPSA_VPD_SUPPORTED_PAGES, - buf, HPSA_VPD_HEADER_SZ); - if (rc != 0) - goto exit_unsupported; - pages = buf[3]; - if ((pages + HPSA_VPD_HEADER_SZ) <= 255) + rc = fill_cmd(c, BMIC_IDENTIFY_CONTROLLER, h, buf, bufsize, + 0, RAID_CTLR_LUNID, TYPE_CMD); + if (rc) + goto out; + + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if (rc) + goto out; + ei = c->err_info; + if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { + hpsa_scsi_interpret_error(h, c); + rc = -1; + } +out: + cmd_free(h, c); + return rc; +} + +static int hpsa_bmic_id_physical_device(struct ctlr_info *h, + unsigned char scsi3addr[], u16 bmic_device_index, + struct bmic_identify_physical_device *buf, size_t bufsize) +{ + int rc = IO_OK; + struct CommandList *c; + struct ErrorInfo *ei; + + c = cmd_alloc(h); + rc = fill_cmd(c, BMIC_IDENTIFY_PHYSICAL_DEVICE, h, buf, bufsize, + 0, RAID_CTLR_LUNID, TYPE_CMD); + if (rc) + goto out; + + c->Request.CDB[2] = bmic_device_index & 0xff; + c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff; + + hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE, + NO_TIMEOUT); + ei = c->err_info; + if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { + hpsa_scsi_interpret_error(h, c); + rc = -1; + } +out: + cmd_free(h, c); + + return rc; +} + +static u64 hpsa_get_sas_address_from_report_physical(struct ctlr_info *h, + unsigned char *scsi3addr) +{ + struct ReportExtendedLUNdata *physdev; + u32 nphysicals; + u64 sa = 0; + int i; + + physdev = kzalloc(sizeof(*physdev), GFP_KERNEL); + if (!physdev) + return 0; + + if (hpsa_scsi_do_report_phys_luns(h, physdev, sizeof(*physdev))) { + dev_err(&h->pdev->dev, "report physical LUNs failed.\n"); + kfree(physdev); + return 0; + } + nphysicals = get_unaligned_be32(physdev->LUNListLength) / 24; + + for (i = 0; i < nphysicals; i++) + if (!memcmp(&physdev->LUN[i].lunid[0], scsi3addr, 8)) { + sa = get_unaligned_be64(&physdev->LUN[i].wwid[0]); + break; + } + + kfree(physdev); + + return sa; +} + +static void hpsa_get_sas_address(struct ctlr_info *h, unsigned char *scsi3addr, + struct hpsa_scsi_dev_t *dev) +{ + int rc; + u64 sa = 0; + + if (is_hba_lunid(scsi3addr)) { + struct bmic_sense_subsystem_info *ssi; + + ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); + if (ssi == NULL) { + dev_warn(&h->pdev->dev, + "%s: out of memory\n", __func__); + return; + } + + rc = hpsa_bmic_sense_subsystem_information(h, + scsi3addr, 0, ssi, sizeof(*ssi)); + if (rc == 0) { + sa = get_unaligned_be64(ssi->primary_world_wide_id); + h->sas_address = sa; + } + + kfree(ssi); + } else + sa = hpsa_get_sas_address_from_report_physical(h, scsi3addr); + + dev->sas_address = sa; +} + +/* Get a device id from inquiry page 0x83 */ +static int hpsa_vpd_page_supported(struct ctlr_info *h, + unsigned char scsi3addr[], u8 page) +{ + int rc; + int i; + int pages; + unsigned char *buf, bufsize; + + buf = kzalloc(256, GFP_KERNEL); + if (!buf) + return 0; + + /* Get the size of the page list first */ + rc = hpsa_scsi_do_inquiry(h, scsi3addr, + VPD_PAGE | HPSA_VPD_SUPPORTED_PAGES, + buf, HPSA_VPD_HEADER_SZ); + if (rc != 0) + goto exit_unsupported; + pages = buf[3]; + if ((pages + HPSA_VPD_HEADER_SZ) <= 255) bufsize = pages + HPSA_VPD_HEADER_SZ; else bufsize = 255; @@ -2438,6 +3304,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h, this_device->offload_config = 0; this_device->offload_enabled = 0; + this_device->offload_to_be_enabled = 0; buf = kzalloc(64, GFP_KERNEL); if (!buf) @@ -2461,6 +3328,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h, if (hpsa_get_raid_map(h, scsi3addr, this_device)) this_device->offload_enabled = 0; } + this_device->offload_to_be_enabled = this_device->offload_enabled; out: kfree(buf); return; @@ -2468,7 +3336,7 @@ out: /* Get the device id from inquiry page 0x83 */ static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr, - unsigned char *device_id, int buflen) + unsigned char *device_id, int index, int buflen) { int rc; unsigned char *buf; @@ -2480,8 +3348,10 @@ static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr, return -ENOMEM; rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0x83, buf, 64); if (rc == 0) - memcpy(device_id, &buf[8], buflen); + memcpy(device_id, &buf[index], buflen); + kfree(buf); + return rc != 0; } @@ -2495,10 +3365,7 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, struct ErrorInfo *ei; c = cmd_alloc(h); - if (c == NULL) { /* trouble... */ - dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -1; - } + /* address the controller */ memset(scsi3addr, 0, sizeof(scsi3addr)); if (fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h, @@ -2508,7 +3375,10 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical, } if (extended_response) c->Request.CDB[1] = extended_response; - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE); + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if (rc) + goto out; ei = c->err_info; if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) { @@ -2600,8 +3470,10 @@ static int hpsa_volume_offline(struct ctlr_info *h, unsigned char scsi3addr[]) { struct CommandList *c; - unsigned char *sense, sense_key, asc, ascq; - int ldstat = 0; + unsigned char *sense; + u8 sense_key, asc, ascq; + int sense_len; + int rc, ldstat = 0; u16 cmd_status; u8 scsi_status; #define ASC_LUN_NOT_READY 0x04 @@ -2609,14 +3481,19 @@ static int hpsa_volume_offline(struct ctlr_info *h, #define ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ 0x02 c = cmd_alloc(h); - if (!c) - return 0; + (void) fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, scsi3addr, TYPE_CMD); - hpsa_scsi_do_simple_cmd_core(h, c); + rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT); + if (rc) { + cmd_free(h, c); + return 0; + } sense = c->err_info->SenseInfo; - sense_key = sense[2]; - asc = sense[12]; - ascq = sense[13]; + if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo)) + sense_len = sizeof(c->err_info->SenseInfo); + else + sense_len = c->err_info->SenseLen; + decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq); cmd_status = c->err_info->CommandStatus; scsi_status = c->err_info->ScsiStatus; cmd_free(h, c); @@ -2634,6 +3511,7 @@ static int hpsa_volume_offline(struct ctlr_info *h, /* Keep volume offline in certain cases: */ switch (ldstat) { case HPSA_LV_UNDERGOING_ERASE: + case HPSA_LV_NOT_AVAILABLE: case HPSA_LV_UNDERGOING_RPI: case HPSA_LV_PENDING_RPI: case HPSA_LV_ENCRYPTED_NO_KEY: @@ -2656,6 +3534,64 @@ static int hpsa_volume_offline(struct ctlr_info *h, return 0; } +/* + * Find out if a logical device supports aborts by simply trying one. + * Smart Array may claim not to support aborts on logical drives, but + * if a MSA2000 * is connected, the drives on that will be presented + * by the Smart Array as logical drives, and aborts may be sent to + * those devices successfully. So the simplest way to find out is + * to simply try an abort and see how the device responds. + */ +static int hpsa_device_supports_aborts(struct ctlr_info *h, + unsigned char *scsi3addr) +{ + struct CommandList *c; + struct ErrorInfo *ei; + int rc = 0; + + u64 tag = (u64) -1; /* bogus tag */ + + /* Assume that physical devices support aborts */ + if (!is_logical_dev_addr_mode(scsi3addr)) + return 1; + + c = cmd_alloc(h); + + (void) fill_cmd(c, HPSA_ABORT_MSG, h, &tag, 0, 0, scsi3addr, TYPE_MSG); + (void) hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT); + /* no unmap needed here because no data xfer. */ + ei = c->err_info; + switch (ei->CommandStatus) { + case CMD_INVALID: + rc = 0; + break; + case CMD_UNABORTABLE: + case CMD_ABORT_FAILED: + rc = 1; + break; + case CMD_TMF_STATUS: + rc = hpsa_evaluate_tmf_status(h, c); + break; + default: + rc = 0; + break; + } + cmd_free(h, c); + return rc; +} + +static void sanitize_inquiry_string(unsigned char *s, int len) +{ + bool terminated = false; + + for (; len > 0; (--len, ++s)) { + if (*s == 0) + terminated = true; + if (terminated || *s < 0x20 || *s > 0x7e) + *s = ' '; + } +} + static int hpsa_update_device_info(struct ctlr_info *h, unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device, unsigned char *is_OBDR_device) @@ -2668,10 +3604,13 @@ static int hpsa_update_device_info(struct ctlr_info *h, unsigned char *inq_buff; unsigned char *obdr_sig; + int rc = 0; inq_buff = kzalloc(OBDR_TAPE_INQ_SIZE, GFP_KERNEL); - if (!inq_buff) + if (!inq_buff) { + rc = -ENOMEM; goto bail_out; + } /* Do an inquiry to the device to see what it is. */ if (hpsa_scsi_do_inquiry(h, scsi3addr, 0, inq_buff, @@ -2679,9 +3618,13 @@ static int hpsa_update_device_info(struct ctlr_info *h, /* Inquiry failed (msg printed already) */ dev_err(&h->pdev->dev, "hpsa_update_device_info: inquiry failed\n"); + rc = -EIO; goto bail_out; } + sanitize_inquiry_string(&inq_buff[8], 8); + sanitize_inquiry_string(&inq_buff[16], 16); + this_device->devtype = (inq_buff[0] & 0x1f); memcpy(this_device->scsi3addr, scsi3addr, 8); memcpy(this_device->vendor, &inq_buff[8], @@ -2690,7 +3633,7 @@ static int hpsa_update_device_info(struct ctlr_info *h, sizeof(this_device->model)); memset(this_device->device_id, 0, sizeof(this_device->device_id)); - hpsa_get_device_id(h, scsi3addr, this_device->device_id, + hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8, sizeof(this_device->device_id)); if (this_device->devtype == TYPE_DISK && @@ -2708,6 +3651,8 @@ static int hpsa_update_device_info(struct ctlr_info *h, this_device->raid_level = RAID_UNKNOWN; this_device->offload_config = 0; this_device->offload_enabled = 0; + this_device->offload_to_be_enabled = 0; + this_device->hba_ioaccel_enabled = 0; this_device->volume_offline = 0; this_device->queue_depth = h->nr_cmds; } @@ -2721,123 +3666,72 @@ static int hpsa_update_device_info(struct ctlr_info *h, strncmp(obdr_sig, OBDR_TAPE_SIG, OBDR_SIG_LEN) == 0); } - kfree(inq_buff); return 0; bail_out: kfree(inq_buff); - return 1; + return rc; } -static unsigned char *ext_target_model[] = { - "MSA2012", - "MSA2024", - "MSA2312", - "MSA2324", - "P2000 G3 SAS", - "MSA 2040 SAS", - NULL, -}; - -static int is_ext_target(struct ctlr_info *h, struct hpsa_scsi_dev_t *device) +static void hpsa_update_device_supports_aborts(struct ctlr_info *h, + struct hpsa_scsi_dev_t *dev, u8 *scsi3addr) { - int i; - - for (i = 0; ext_target_model[i]; i++) - if (strncmp(device->model, ext_target_model[i], - strlen(ext_target_model[i])) == 0) - return 1; - return 0; + unsigned long flags; + int rc, entry; + /* + * See if this device supports aborts. If we already know + * the device, we already know if it supports aborts, otherwise + * we have to find out if it supports aborts by trying one. + */ + spin_lock_irqsave(&h->devlock, flags); + rc = hpsa_scsi_find_entry(dev, h->dev, h->ndevices, &entry); + if ((rc == DEVICE_SAME || rc == DEVICE_UPDATED) && + entry >= 0 && entry < h->ndevices) { + dev->supports_aborts = h->dev[entry]->supports_aborts; + spin_unlock_irqrestore(&h->devlock, flags); + } else { + spin_unlock_irqrestore(&h->devlock, flags); + dev->supports_aborts = + hpsa_device_supports_aborts(h, scsi3addr); + if (dev->supports_aborts < 0) + dev->supports_aborts = 0; + } } -/* Helper function to assign bus, target, lun mapping of devices. - * Puts non-external target logical volumes on bus 0, external target logical - * volumes on bus 1, physical devices on bus 2. and the hba on bus 3. +/* + * Helper function to assign bus, target, lun mapping of devices. * Logical drive target and lun are assigned at this time, but * physical device lun and target assignment are deferred (assigned * in hpsa_find_target_lun, called by hpsa_scsi_add_entry.) - */ +*/ static void figure_bus_target_lun(struct ctlr_info *h, u8 *lunaddrbytes, struct hpsa_scsi_dev_t *device) { - u32 lunid = le32_to_cpu(*((__le32 *) lunaddrbytes)); + u32 lunid = get_unaligned_le32(lunaddrbytes); if (!is_logical_dev_addr_mode(lunaddrbytes)) { /* physical device, target and lun filled in later */ if (is_hba_lunid(lunaddrbytes)) - hpsa_set_bus_target_lun(device, 3, 0, lunid & 0x3fff); + hpsa_set_bus_target_lun(device, + HPSA_HBA_BUS, 0, lunid & 0x3fff); else /* defer target, lun assignment for physical devices */ - hpsa_set_bus_target_lun(device, 2, -1, -1); + hpsa_set_bus_target_lun(device, + HPSA_PHYSICAL_DEVICE_BUS, -1, -1); return; } /* It's a logical device */ - if (is_ext_target(h, device)) { - /* external target way, put logicals on bus 1 - * and match target/lun numbers box - * reports, other smart array, bus 0, target 0, match lunid - */ + if (device->external) { hpsa_set_bus_target_lun(device, - 1, (lunid >> 16) & 0x3fff, lunid & 0x00ff); + HPSA_EXTERNAL_RAID_VOLUME_BUS, (lunid >> 16) & 0x3fff, + lunid & 0x00ff); return; } - hpsa_set_bus_target_lun(device, 0, 0, lunid & 0x3fff); + hpsa_set_bus_target_lun(device, HPSA_RAID_VOLUME_BUS, + 0, lunid & 0x3fff); } -/* - * If there is no lun 0 on a target, linux won't find any devices. - * For the external targets (arrays), we have to manually detect the enclosure - * which is at lun zero, as CCISS_REPORT_PHYSICAL_LUNS doesn't report - * it for some reason. *tmpdevice is the target we're adding, - * this_device is a pointer into the current element of currentsd[] - * that we're building up in update_scsi_devices(), below. - * lunzerobits is a bitmap that tracks which targets already have a - * lun 0 assigned. - * Returns 1 if an enclosure was added, 0 if not. - */ -static int add_ext_target_dev(struct ctlr_info *h, - struct hpsa_scsi_dev_t *tmpdevice, - struct hpsa_scsi_dev_t *this_device, u8 *lunaddrbytes, - unsigned long lunzerobits[], int *n_ext_target_devs) -{ - unsigned char scsi3addr[8]; - - if (test_bit(tmpdevice->target, lunzerobits)) - return 0; /* There is already a lun 0 on this target. */ - - if (!is_logical_dev_addr_mode(lunaddrbytes)) - return 0; /* It's the logical targets that may lack lun 0. */ - - if (!is_ext_target(h, tmpdevice)) - return 0; /* Only external target devices have this problem. */ - - if (tmpdevice->lun == 0) /* if lun is 0, then we have a lun 0. */ - return 0; - - memset(scsi3addr, 0, 8); - scsi3addr[3] = tmpdevice->target; - if (is_hba_lunid(scsi3addr)) - return 0; /* Don't add the RAID controller here. */ - - if (is_scsi_rev_5(h)) - return 0; /* p1210m doesn't need to do this. */ - - if (*n_ext_target_devs >= MAX_EXT_TARGETS) { - dev_warn(&h->pdev->dev, "Maximum number of external " - "target devices exceeded. Check your hardware " - "configuration."); - return 0; - } - - if (hpsa_update_device_info(h, scsi3addr, this_device, NULL)) - return 0; - (*n_ext_target_devs)++; - hpsa_set_bus_target_lun(this_device, - tmpdevice->bus, tmpdevice->target, 0); - set_bit(tmpdevice->target, lunzerobits); - return 1; -} /* * Get address of physical disk used for an ioaccel2 mode command: @@ -2850,88 +3744,44 @@ static int add_ext_target_dev(struct ctlr_info *h, static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h, struct CommandList *ioaccel2_cmd_to_abort, unsigned char *scsi3addr) { - struct ReportExtendedLUNdata *physicals = NULL; - int responsesize = 24; /* size of physical extended response */ - int reportsize = sizeof(*physicals) + HPSA_MAX_PHYS_LUN * responsesize; - u32 nphysicals = 0; /* number of reported physical devs */ - int found = 0; /* found match (1) or not (0) */ - u32 find; /* handle we need to match */ + struct io_accel2_cmd *c2 = + &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex]; + unsigned long flags; int i; - struct scsi_cmnd *scmd; /* scsi command within request being aborted */ - struct hpsa_scsi_dev_t *d; /* device of request being aborted */ - struct io_accel2_cmd *c2a; /* ioaccel2 command to abort */ - __le32 it_nexus; /* 4 byte device handle for the ioaccel2 cmd */ - __le32 scsi_nexus; /* 4 byte device handle for the ioaccel2 cmd */ - - if (ioaccel2_cmd_to_abort->cmd_type != CMD_IOACCEL2) - return 0; /* no match */ - - /* point to the ioaccel2 device handle */ - c2a = &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex]; - if (c2a == NULL) - return 0; /* no match */ - - scmd = (struct scsi_cmnd *) ioaccel2_cmd_to_abort->scsi_cmd; - if (scmd == NULL) - return 0; /* no match */ - d = scmd->device->hostdata; - if (d == NULL) - return 0; /* no match */ + spin_lock_irqsave(&h->devlock, flags); + for (i = 0; i < h->ndevices; i++) + if (h->dev[i]->ioaccel_handle == le32_to_cpu(c2->scsi_nexus)) { + memcpy(scsi3addr, h->dev[i]->scsi3addr, + sizeof(h->dev[i]->scsi3addr)); + spin_unlock_irqrestore(&h->devlock, flags); + return 1; + } + spin_unlock_irqrestore(&h->devlock, flags); + return 0; +} - it_nexus = cpu_to_le32(d->ioaccel_handle); - scsi_nexus = c2a->scsi_nexus; - find = le32_to_cpu(c2a->scsi_nexus); +static int figure_external_status(struct ctlr_info *h, int raid_ctlr_position, + int i, int nphysicals, int nlocal_logicals) +{ + /* In report logicals, local logicals are listed first, + * then any externals. + */ + int logicals_start = nphysicals + (raid_ctlr_position == 0); - if (h->raid_offload_debug > 0) - dev_info(&h->pdev->dev, - "%s: scsi_nexus:0x%08x device id: 0x%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", - __func__, scsi_nexus, - d->device_id[0], d->device_id[1], d->device_id[2], - d->device_id[3], d->device_id[4], d->device_id[5], - d->device_id[6], d->device_id[7], d->device_id[8], - d->device_id[9], d->device_id[10], d->device_id[11], - d->device_id[12], d->device_id[13], d->device_id[14], - d->device_id[15]); - - /* Get the list of physical devices */ - physicals = kzalloc(reportsize, GFP_KERNEL); - if (physicals == NULL) - return 0; - if (hpsa_scsi_do_report_phys_luns(h, physicals, reportsize)) { - dev_err(&h->pdev->dev, - "Can't lookup %s device handle: report physical LUNs failed.\n", - "HP SSD Smart Path"); - kfree(physicals); + if (i == raid_ctlr_position) return 0; - } - nphysicals = be32_to_cpu(*((__be32 *)physicals->LUNListLength)) / - responsesize; - - /* find ioaccel2 handle in list of physicals: */ - for (i = 0; i < nphysicals; i++) { - struct ext_report_lun_entry *entry = &physicals->LUN[i]; - /* handle is in bytes 28-31 of each lun */ - if (entry->ioaccel_handle != find) - continue; /* didn't match */ - found = 1; - memcpy(scsi3addr, entry->lunid, 8); - if (h->raid_offload_debug > 0) - dev_info(&h->pdev->dev, - "%s: Searched h=0x%08x, Found h=0x%08x, scsiaddr 0x%8phN\n", - __func__, find, - entry->ioaccel_handle, scsi3addr); - break; /* found it */ - } + if (i < logicals_start) + return 0; - kfree(physicals); - if (found) - return 1; - else + /* i is in logicals range, but still within local logicals */ + if ((i - nphysicals - (raid_ctlr_position == 0)) < nlocal_logicals) return 0; + return 1; /* it's an external lun */ } + /* * Do CISS_REPORT_PHYS and CISS_REPORT_LOG. Data is returned in physdev, * logdev. The number of luns in physdev and logdev are returned in @@ -3002,43 +3852,21 @@ static u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, return NULL; } -static int hpsa_hba_mode_enabled(struct ctlr_info *h) -{ - int rc; - int hba_mode_enabled; - struct bmic_controller_parameters *ctlr_params; - ctlr_params = kzalloc(sizeof(struct bmic_controller_parameters), - GFP_KERNEL); - - if (!ctlr_params) - return -ENOMEM; - rc = hpsa_bmic_ctrl_mode_sense(h, RAID_CTLR_LUNID, 0, ctlr_params, - sizeof(struct bmic_controller_parameters)); - if (rc) { - kfree(ctlr_params); - return rc; - } - - hba_mode_enabled = - ((ctlr_params->nvram_flags & HBA_MODE_ENABLED_FLAG) != 0); - kfree(ctlr_params); - return hba_mode_enabled; -} - /* get physical drive ioaccel handle and queue depth */ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev, - u8 *lunaddrbytes, + struct ReportExtendedLUNdata *rlep, int rle_index, struct bmic_identify_physical_device *id_phys) { int rc; - struct ext_report_lun_entry *rle = - (struct ext_report_lun_entry *) lunaddrbytes; + struct ext_report_lun_entry *rle = &rlep->LUN[rle_index]; dev->ioaccel_handle = rle->ioaccel_handle; + if ((rle->device_flags & 0x08) && dev->ioaccel_handle) + dev->hba_ioaccel_enabled = 1; memset(id_phys, 0, sizeof(*id_phys)); - rc = hpsa_bmic_id_physical_device(h, lunaddrbytes, - GET_BMIC_DRIVE_NUMBER(lunaddrbytes), id_phys, + rc = hpsa_bmic_id_physical_device(h, &rle->lunid[0], + GET_BMIC_DRIVE_NUMBER(&rle->lunid[0]), id_phys, sizeof(*id_phys)); if (!rc) /* Reserve space for FW operations */ @@ -3049,10 +3877,61 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h, DRIVE_CMDS_RESERVED_FOR_FW; else dev->queue_depth = DRIVE_QUEUE_DEPTH; /* conservative */ - atomic_set(&dev->ioaccel_cmds_out, 0); } -static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) +static void hpsa_get_path_info(struct hpsa_scsi_dev_t *this_device, + struct ReportExtendedLUNdata *rlep, int rle_index, + struct bmic_identify_physical_device *id_phys) +{ + struct ext_report_lun_entry *rle = &rlep->LUN[rle_index]; + + if ((rle->device_flags & 0x08) && this_device->ioaccel_handle) + this_device->hba_ioaccel_enabled = 1; + + memcpy(&this_device->active_path_index, + &id_phys->active_path_number, + sizeof(this_device->active_path_index)); + memcpy(&this_device->path_map, + &id_phys->redundant_path_present_map, + sizeof(this_device->path_map)); + memcpy(&this_device->box, + &id_phys->alternate_paths_phys_box_on_port, + sizeof(this_device->box)); + memcpy(&this_device->phys_connector, + &id_phys->alternate_paths_phys_connector, + sizeof(this_device->phys_connector)); + memcpy(&this_device->bay, + &id_phys->phys_bay_in_box, + sizeof(this_device->bay)); +} + +/* get number of local logical disks. */ +static int hpsa_set_local_logical_count(struct ctlr_info *h, + struct bmic_identify_controller *id_ctlr, + u32 *nlocals) +{ + int rc; + + if (!id_ctlr) { + dev_warn(&h->pdev->dev, "%s: id_ctlr buffer is NULL.\n", + __func__); + return -ENOMEM; + } + memset(id_ctlr, 0, sizeof(*id_ctlr)); + rc = hpsa_bmic_id_controller(h, id_ctlr, sizeof(*id_ctlr)); + if (!rc) + if (id_ctlr->configured_logical_drive_count < 256) + *nlocals = id_ctlr->configured_logical_drive_count; + else + *nlocals = le16_to_cpu( + id_ctlr->extended_logical_unit_count); + else + *nlocals = -1; + return rc; +} + + +static void hpsa_update_scsi_devices(struct ctlr_info *h) { /* the idea here is we could get notified * that some devices have changed, so we do a report @@ -3067,14 +3946,16 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) struct ReportExtendedLUNdata *physdev_list = NULL; struct ReportLUNdata *logdev_list = NULL; struct bmic_identify_physical_device *id_phys = NULL; + struct bmic_identify_controller *id_ctlr = NULL; u32 nphysicals = 0; u32 nlogicals = 0; + u32 nlocal_logicals = 0; u32 ndev_allocated = 0; struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice; int ncurrent = 0; int i, n_ext_target_devs, ndevs_to_allocate; int raid_ctlr_position; - int rescan_hba_mode; + bool physical_device; DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS); currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL); @@ -3082,28 +3963,29 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) logdev_list = kzalloc(sizeof(*logdev_list), GFP_KERNEL); tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL); id_phys = kzalloc(sizeof(*id_phys), GFP_KERNEL); + id_ctlr = kzalloc(sizeof(*id_ctlr), GFP_KERNEL); if (!currentsd || !physdev_list || !logdev_list || - !tmpdevice || !id_phys) { + !tmpdevice || !id_phys || !id_ctlr) { dev_err(&h->pdev->dev, "out of memory\n"); goto out; } memset(lunzerobits, 0, sizeof(lunzerobits)); - rescan_hba_mode = hpsa_hba_mode_enabled(h); - if (rescan_hba_mode < 0) - goto out; - - if (!h->hba_mode_enabled && rescan_hba_mode) - dev_warn(&h->pdev->dev, "HBA mode enabled\n"); - else if (h->hba_mode_enabled && !rescan_hba_mode) - dev_warn(&h->pdev->dev, "HBA mode disabled\n"); - - h->hba_mode_enabled = rescan_hba_mode; + h->drv_req_rescan = 0; /* cancel scheduled rescan - we're doing it. */ if (hpsa_gather_lun_info(h, physdev_list, &nphysicals, - logdev_list, &nlogicals)) + logdev_list, &nlogicals)) { + h->drv_req_rescan = 1; goto out; + } + + /* Set number of local logicals (non PTRAID) */ + if (hpsa_set_local_logical_count(h, id_ctlr, &nlocal_logicals)) { + dev_warn(&h->pdev->dev, + "%s: Can't determine number of local logical devices.\n", + __func__); + } /* We might see up to the maximum number of logical and physical disks * plus external target devices, and a device for the local RAID @@ -3124,6 +4006,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) if (!currentsd[i]) { dev_warn(&h->pdev->dev, "out of memory at %s:%d\n", __FILE__, __LINE__); + h->drv_req_rescan = 1; goto out; } ndev_allocated++; @@ -3138,37 +4021,74 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) n_ext_target_devs = 0; for (i = 0; i < nphysicals + nlogicals + 1; i++) { u8 *lunaddrbytes, is_OBDR = 0; + int rc = 0; + int phys_dev_index = i - (raid_ctlr_position == 0); + + physical_device = i < nphysicals + (raid_ctlr_position == 0); /* Figure out where the LUN ID info is coming from */ lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position, i, nphysicals, nlogicals, physdev_list, logdev_list); - /* skip masked physical devices. */ - if (lunaddrbytes[3] & 0xC0 && - i < nphysicals + (raid_ctlr_position == 0)) + + /* skip masked non-disk devices */ + if (MASKED_DEVICE(lunaddrbytes) && physical_device && + (physdev_list->LUN[phys_dev_index].device_flags & 0x01)) continue; /* Get device type, vendor, model, device id */ - if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice, - &is_OBDR)) - continue; /* skip it if we can't talk to it. */ - figure_bus_target_lun(h, lunaddrbytes, tmpdevice); - this_device = currentsd[ncurrent]; - - /* - * For external target devices, we have to insert a LUN 0 which - * doesn't show up in CCISS_REPORT_PHYSICAL data, but there - * is nonetheless an enclosure device there. We have to - * present that otherwise linux won't find anything if - * there is no lun 0. - */ - if (add_ext_target_dev(h, tmpdevice, this_device, - lunaddrbytes, lunzerobits, - &n_ext_target_devs)) { - ncurrent++; - this_device = currentsd[ncurrent]; - } + rc = hpsa_update_device_info(h, lunaddrbytes, tmpdevice, + &is_OBDR); + if (rc == -ENOMEM) { + dev_warn(&h->pdev->dev, + "Out of memory, rescan deferred.\n"); + h->drv_req_rescan = 1; + goto out; + } + if (rc) { + dev_warn(&h->pdev->dev, + "Inquiry failed, skipping device.\n"); + continue; + } + + /* Determine if this is a lun from an external target array */ + tmpdevice->external = + figure_external_status(h, raid_ctlr_position, i, + nphysicals, nlocal_logicals); + + figure_bus_target_lun(h, lunaddrbytes, tmpdevice); + hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes); + this_device = currentsd[ncurrent]; + + /* Turn on discovery_polling if there are ext target devices. + * Event-based change notification is unreliable for those. + */ + if (!h->discovery_polling) { + if (tmpdevice->external) { + h->discovery_polling = 1; + dev_info(&h->pdev->dev, + "External target, activate discovery polling.\n"); + } + } + *this_device = *tmpdevice; + this_device->physical_device = physical_device; + + /* + * Expose all devices except for physical devices that + * are masked. + */ + if (MASKED_DEVICE(lunaddrbytes) && this_device->physical_device) + this_device->expose_device = 0; + else + this_device->expose_device = 1; + + + /* + * Get the SAS address for physical devices that are exposed. + */ + if (this_device->physical_device && this_device->expose_device) + hpsa_get_sas_address(h, lunaddrbytes, this_device); switch (this_device->devtype) { case TYPE_ROM: @@ -3183,32 +4103,20 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) ncurrent++; break; case TYPE_DISK: - if (h->hba_mode_enabled) { - /* never use raid mapper in HBA mode */ + if (this_device->physical_device) { + /* The disk is in HBA mode. */ + /* Never use RAID mapper in HBA mode. */ this_device->offload_enabled = 0; - ncurrent++; - break; - } else if (h->acciopath_status) { - if (i >= nphysicals) { - ncurrent++; - break; - } - } else { - if (i < nphysicals) - break; - ncurrent++; - break; - } - if (h->transMethod & CFGTBL_Trans_io_accel1 || - h->transMethod & CFGTBL_Trans_io_accel2) { hpsa_get_ioaccel_drive_info(h, this_device, - lunaddrbytes, id_phys); - atomic_set(&this_device->ioaccel_cmds_out, 0); - ncurrent++; + physdev_list, phys_dev_index, id_phys); + hpsa_get_path_info(this_device, + physdev_list, phys_dev_index, id_phys); } + ncurrent++; break; case TYPE_TAPE: case TYPE_MEDIUM_CHANGER: + case TYPE_ENCLOSURE: ncurrent++; break; case TYPE_RAID: @@ -3227,8 +4135,19 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno) if (ncurrent >= HPSA_MAX_DEVICES) break; } - hpsa_update_log_drive_phys_drive_ptrs(h, currentsd, ncurrent); - adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent); + + if (h->sas_host == NULL) { + int rc = 0; + + rc = hpsa_add_sas_host(h); + if (rc) { + dev_warn(&h->pdev->dev, + "Could not add sas host %d\n", rc); + goto out; + } + } + + adjust_hpsa_scsi_table(h, currentsd, ncurrent); out: kfree(tmpdevice); for (i = 0; i < ndev_allocated; i++) @@ -3236,6 +4155,7 @@ out: kfree(currentsd); kfree(physdev_list); kfree(logdev_list); + kfree(id_ctlr); kfree(id_phys); } @@ -3260,7 +4180,7 @@ static int hpsa_scatter_gather(struct ctlr_info *h, struct scsi_cmnd *cmd) { struct scatterlist *sg; - int use_sg, i, sg_index, chained; + int use_sg, i, sg_limit, chained, last_sg; struct SGDescriptor *curr_sg; BUG_ON(scsi_sg_count(cmd) > h->maxsgentries); @@ -3272,22 +4192,39 @@ static int hpsa_scatter_gather(struct ctlr_info *h, if (!use_sg) goto sglist_finished; + /* + * If the number of entries is greater than the max for a single list, + * then we have a chained list; we will set up all but one entry in the + * first list (the last entry is saved for link information); + * otherwise, we don't have a chained list and we'll set up at each of + * the entries in the one list. + */ curr_sg = cp->SG; - chained = 0; - sg_index = 0; - scsi_for_each_sg(cmd, sg, use_sg, i) { - if (i == h->max_cmd_sg_entries - 1 && - use_sg > h->max_cmd_sg_entries) { - chained = 1; - curr_sg = h->cmd_sg_list[cp->cmdindex]; - sg_index = 0; - } + chained = use_sg > h->max_cmd_sg_entries; + sg_limit = chained ? h->max_cmd_sg_entries - 1 : use_sg; + last_sg = scsi_sg_count(cmd) - 1; + scsi_for_each_sg(cmd, sg, sg_limit, i) { hpsa_set_sg_descriptor(curr_sg, sg); curr_sg++; } + if (chained) { + /* + * Continue with the chained list. Set curr_sg to the chained + * list. Modify the limit to the total count less the entries + * we've already set up. Resume the scan at the list entry + * where the previous loop left off. + */ + curr_sg = h->cmd_sg_list[cp->cmdindex]; + sg_limit = use_sg - sg_limit; + for_each_sg(sg, sg, sg_limit, i) { + hpsa_set_sg_descriptor(curr_sg, sg); + curr_sg++; + } + } + /* Back the pointer up to the last entry and mark it as "last". */ - (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST); + (curr_sg - 1)->Ext = cpu_to_le32(HPSA_SG_LAST); if (use_sg + chained > h->maxSG) h->maxSG = use_sg + chained; @@ -3324,19 +4261,14 @@ static int fixup_ioaccel_cdb(u8 *cdb, int *cdb_len) case READ_6: case READ_12: if (*cdb_len == 6) { - block = (((u32) cdb[2]) << 8) | cdb[3]; + block = get_unaligned_be16(&cdb[2]); block_cnt = cdb[4]; + if (block_cnt == 0) + block_cnt = 256; } else { BUG_ON(*cdb_len != 12); - block = (((u32) cdb[2]) << 24) | - (((u32) cdb[3]) << 16) | - (((u32) cdb[4]) << 8) | - cdb[5]; - block_cnt = - (((u32) cdb[6]) << 24) | - (((u32) cdb[7]) << 16) | - (((u32) cdb[8]) << 8) | - cdb[9]; + block = get_unaligned_be32(&cdb[2]); + block_cnt = get_unaligned_be32(&cdb[6]); } if (block_cnt > 0xffff) return IO_ACCEL_INELIGIBLE; @@ -3530,10 +4462,7 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, u32 len; u32 total_len = 0; - if (scsi_sg_count(cmd) > h->ioaccel_maxsg) { - atomic_dec(&phys_disk->ioaccel_cmds_out); - return IO_ACCEL_INELIGIBLE; - } + BUG_ON(scsi_sg_count(cmd) > h->maxsgentries); if (fixup_ioaccel_cdb(cdb, &cdb_len)) { atomic_dec(&phys_disk->ioaccel_cmds_out); @@ -3556,8 +4485,19 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, } if (use_sg) { - BUG_ON(use_sg > IOACCEL2_MAXSGENTRIES); curr_sg = cp->sg; + if (use_sg > h->ioaccel_maxsg) { + addr64 = le64_to_cpu( + h->ioaccel2_cmd_sg_list[c->cmdindex]->address); + curr_sg->address = cpu_to_le64(addr64); + curr_sg->length = 0; + curr_sg->reserved[0] = 0; + curr_sg->reserved[1] = 0; + curr_sg->reserved[2] = 0; + curr_sg->chain_indicator = 0x80; + + curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex]; + } scsi_for_each_sg(cmd, sg, use_sg, i) { addr64 = (u64) sg_dma_address(sg); len = sg_dma_len(sg); @@ -3602,14 +4542,23 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, cp->Tag = cpu_to_le32(c->cmdindex << DIRECT_LOOKUP_SHIFT); memcpy(cp->cdb, cdb, sizeof(cp->cdb)); - /* fill in sg elements */ - cp->sg_count = (u8) use_sg; - cp->data_len = cpu_to_le32(total_len); cp->err_ptr = cpu_to_le64(c->busaddr + offsetof(struct io_accel2_cmd, error_data)); cp->err_len = cpu_to_le32(sizeof(cp->error_data)); + /* fill in sg elements */ + if (use_sg > h->ioaccel_maxsg) { + cp->sg_count = 1; + cp->sg[0].length = cpu_to_le32(use_sg * sizeof(cp->sg[0])); + if (hpsa_map_ioaccel2_sg_chain_block(h, cp, c)) { + atomic_dec(&phys_disk->ioaccel_cmds_out); + scsi_dma_unmap(cmd); + return -1; + } + } else + cp->sg_count = (u8) use_sg; + enqueue_cmd_and_start_io(h, c); return 0; } @@ -3706,9 +4655,7 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, case WRITE_6: is_write = 1; case READ_6: - first_block = - (((u64) cmd->cmnd[2]) << 8) | - cmd->cmnd[3]; + first_block = get_unaligned_be16(&cmd->cmnd[2]); block_cnt = cmd->cmnd[4]; if (block_cnt == 0) block_cnt = 256; @@ -3992,7 +4939,11 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h, dev->phys_disk[map_index]); } -/* Submit commands down the "normal" RAID stack path */ +/* + * Submit commands down the "normal" RAID stack path + * All callers to hpsa_ciss_submit must check lockup_detected + * beforehand, before (opt.) and after calling cmd_alloc + */ static int hpsa_ciss_submit(struct ctlr_info *h, struct CommandList *c, struct scsi_cmnd *cmd, unsigned char scsi3addr[]) @@ -4007,7 +4958,6 @@ static int hpsa_ciss_submit(struct ctlr_info *h, /* Fill in the request block... */ c->Request.Timeout = 0; - memset(c->Request.CDB, 0, sizeof(c->Request.CDB)); BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB)); c->Request.CDBLen = cmd->cmd_len; memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len); @@ -4050,7 +5000,7 @@ static int hpsa_ciss_submit(struct ctlr_info *h, } if (hpsa_scatter_gather(h, c, cmd) < 0) { /* Fill SG list */ - cmd_free(h, c); + hpsa_cmd_resolve_and_free(h, c); return SCSI_MLQUEUE_HOST_BUSY; } enqueue_cmd_and_start_io(h, c); @@ -4058,25 +5008,125 @@ static int hpsa_ciss_submit(struct ctlr_info *h, return 0; } +static void hpsa_cmd_init(struct ctlr_info *h, int index, + struct CommandList *c) +{ + dma_addr_t cmd_dma_handle, err_dma_handle; + + /* Zero out all of commandlist except the last field, refcount */ + memset(c, 0, offsetof(struct CommandList, refcount)); + c->Header.tag = cpu_to_le64((u64) (index << DIRECT_LOOKUP_SHIFT)); + cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c); + c->err_info = h->errinfo_pool + index; + memset(c->err_info, 0, sizeof(*c->err_info)); + err_dma_handle = h->errinfo_pool_dhandle + + index * sizeof(*c->err_info); + c->cmdindex = index; + c->busaddr = (u32) cmd_dma_handle; + c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle); + c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info)); + c->h = h; + c->scsi_cmd = SCSI_CMD_IDLE; +} + +static void hpsa_preinitialize_commands(struct ctlr_info *h) +{ + int i; + + for (i = 0; i < h->nr_cmds; i++) { + struct CommandList *c = h->cmd_pool + i; + + hpsa_cmd_init(h, i, c); + atomic_set(&c->refcount, 0); + } +} + +static inline void hpsa_cmd_partial_init(struct ctlr_info *h, int index, + struct CommandList *c) +{ + dma_addr_t cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c); + + BUG_ON(c->cmdindex != index); + + memset(c->Request.CDB, 0, sizeof(c->Request.CDB)); + memset(c->err_info, 0, sizeof(*c->err_info)); + c->busaddr = (u32) cmd_dma_handle; +} + +static int hpsa_ioaccel_submit(struct ctlr_info *h, + struct CommandList *c, struct scsi_cmnd *cmd, + unsigned char *scsi3addr) +{ + struct hpsa_scsi_dev_t *dev = cmd->device->hostdata; + int rc = IO_ACCEL_INELIGIBLE; + + cmd->host_scribble = (unsigned char *) c; + + if (dev->offload_enabled) { + hpsa_cmd_init(h, c->cmdindex, c); + c->cmd_type = CMD_SCSI; + c->scsi_cmd = cmd; + rc = hpsa_scsi_ioaccel_raid_map(h, c); + if (rc < 0) /* scsi_dma_map failed. */ + rc = SCSI_MLQUEUE_HOST_BUSY; + } else if (dev->hba_ioaccel_enabled) { + hpsa_cmd_init(h, c->cmdindex, c); + c->cmd_type = CMD_SCSI; + c->scsi_cmd = cmd; + rc = hpsa_scsi_ioaccel_direct_map(h, c); + if (rc < 0) /* scsi_dma_map failed. */ + rc = SCSI_MLQUEUE_HOST_BUSY; + } + return rc; +} + static void hpsa_command_resubmit_worker(struct work_struct *work) { struct scsi_cmnd *cmd; struct hpsa_scsi_dev_t *dev; - struct CommandList *c = - container_of(work, struct CommandList, work); + struct CommandList *c = container_of(work, struct CommandList, work); cmd = c->scsi_cmd; dev = cmd->device->hostdata; if (!dev) { cmd->result = DID_NO_CONNECT << 16; - cmd->scsi_done(cmd); - return; + return hpsa_cmd_free_and_done(c->h, c, cmd); + } + if (c->reset_pending) + return hpsa_cmd_resolve_and_free(c->h, c); + if (c->abort_pending) + return hpsa_cmd_abort_and_free(c->h, c, cmd); + if (c->cmd_type == CMD_IOACCEL2) { + struct ctlr_info *h = c->h; + struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex]; + int rc; + + if (c2->error_data.serv_response == + IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL) { + rc = hpsa_ioaccel_submit(h, c, cmd, dev->scsi3addr); + if (rc == 0) + return; + if (rc == SCSI_MLQUEUE_HOST_BUSY) { + /* + * If we get here, it means dma mapping failed. + * Try again via scsi mid layer, which will + * then get SCSI_MLQUEUE_HOST_BUSY. + */ + cmd->result = DID_IMM_RETRY << 16; + return hpsa_cmd_free_and_done(h, c, cmd); + } + /* else, fall thru and resubmit down CISS path */ + } } + hpsa_cmd_partial_init(c->h, c->cmdindex, c); if (hpsa_ciss_submit(c->h, c, cmd, dev->scsi3addr)) { /* * If we get here, it means dma mapping failed. Try * again via scsi mid layer, which will then get * SCSI_MLQUEUE_HOST_BUSY. + * + * hpsa_ciss_submit will have already freed c + * if it encountered a dma mapping failure. */ cmd->result = DID_IMM_RETRY << 16; cmd->scsi_done(cmd); @@ -4094,30 +5144,24 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd) /* Get the ptr to our adapter structure out of cmd->host. */ h = sdev_to_hba(cmd->device); + + BUG_ON(cmd->request->tag < 0); + dev = cmd->device->hostdata; if (!dev) { cmd->result = DID_NO_CONNECT << 16; cmd->scsi_done(cmd); return 0; } + memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr)); if (unlikely(lockup_detected(h))) { - cmd->result = DID_ERROR << 16; - cmd->scsi_done(cmd); - return 0; - } - c = cmd_alloc(h); - if (c == NULL) { /* trouble... */ - dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return SCSI_MLQUEUE_HOST_BUSY; - } - if (unlikely(lockup_detected(h))) { - cmd->result = DID_ERROR << 16; - cmd_free(h, c); + cmd->result = DID_NO_CONNECT << 16; cmd->scsi_done(cmd); return 0; } + c = cmd_tagged_alloc(h, cmd); /* * Call alternate submit routine for I/O accelerated commands. @@ -4126,27 +5170,12 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd) if (likely(cmd->retries == 0 && cmd->request->cmd_type == REQ_TYPE_FS && h->acciopath_status)) { - - cmd->host_scribble = (unsigned char *) c; - c->cmd_type = CMD_SCSI; - c->scsi_cmd = cmd; - - if (dev->offload_enabled) { - rc = hpsa_scsi_ioaccel_raid_map(h, c); - if (rc == 0) - return 0; /* Sent on ioaccel path */ - if (rc < 0) { /* scsi_dma_map failed. */ - cmd_free(h, c); - return SCSI_MLQUEUE_HOST_BUSY; - } - } else if (dev->ioaccel_handle) { - rc = hpsa_scsi_ioaccel_direct_map(h, c); - if (rc == 0) - return 0; /* Sent on direct map path */ - if (rc < 0) { /* scsi_dma_map failed. */ - cmd_free(h, c); - return SCSI_MLQUEUE_HOST_BUSY; - } + rc = hpsa_ioaccel_submit(h, c, cmd, scsi3addr); + if (rc == 0) + return 0; + if (rc == SCSI_MLQUEUE_HOST_BUSY) { + hpsa_cmd_resolve_and_free(h, c); + return SCSI_MLQUEUE_HOST_BUSY; } } return hpsa_ciss_submit(h, c, cmd, scsi3addr); @@ -4195,7 +5224,7 @@ static void hpsa_scan_start(struct Scsi_Host *sh) if (unlikely(lockup_detected(h))) return hpsa_scan_complete(h); - hpsa_update_scsi_devices(h, h->scsi_host->host_no); + hpsa_update_scsi_devices(h); hpsa_scan_complete(h); } @@ -4228,22 +5257,15 @@ static int hpsa_scan_finished(struct Scsi_Host *sh, return finished; } -static void hpsa_unregister_scsi(struct ctlr_info *h) -{ - /* we are being forcibly unloaded, and may not refuse. */ - scsi_remove_host(h->scsi_host); - scsi_host_put(h->scsi_host); - h->scsi_host = NULL; -} - -static int hpsa_register_scsi(struct ctlr_info *h) +static int hpsa_scsi_host_alloc(struct ctlr_info *h) { struct Scsi_Host *sh; - int error; sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h)); - if (sh == NULL) - goto fail; + if (sh == NULL) { + dev_err(&h->pdev->dev, "scsi_host_alloc failed\n"); + return -ENOMEM; + } sh->io_port = 0; sh->n_io_port = 0; @@ -4252,80 +5274,150 @@ static int hpsa_register_scsi(struct ctlr_info *h) sh->max_cmd_len = MAX_COMMAND_SIZE; sh->max_lun = HPSA_MAX_LUN; sh->max_id = HPSA_MAX_LUN; - sh->can_queue = h->nr_cmds - - HPSA_CMDS_RESERVED_FOR_ABORTS - - HPSA_CMDS_RESERVED_FOR_DRIVER - - HPSA_MAX_CONCURRENT_PASSTHRUS; + sh->can_queue = h->nr_cmds - HPSA_NRESERVED_CMDS; sh->cmd_per_lun = sh->can_queue; sh->sg_tablesize = h->maxsgentries; - h->scsi_host = sh; + sh->transportt = hpsa_sas_transport_template; sh->hostdata[0] = (unsigned long) h; sh->irq = h->intr[h->intr_mode]; sh->unique_id = sh->irq; - error = scsi_add_host(sh, &h->pdev->dev); - if (error) - goto fail_host_put; - scsi_scan_host(sh); + + h->scsi_host = sh; return 0; +} - fail_host_put: - dev_err(&h->pdev->dev, "%s: scsi_add_host" - " failed for controller %d\n", __func__, h->ctlr); - scsi_host_put(sh); - return error; - fail: - dev_err(&h->pdev->dev, "%s: scsi_host_alloc" - " failed for controller %d\n", __func__, h->ctlr); - return -ENOMEM; +static int hpsa_scsi_add_host(struct ctlr_info *h) +{ + int rv; + + rv = scsi_add_host(h->scsi_host, &h->pdev->dev); + if (rv) { + dev_err(&h->pdev->dev, "scsi_add_host failed\n"); + return rv; + } + scsi_scan_host(h->scsi_host); + return 0; } -static int wait_for_device_to_become_ready(struct ctlr_info *h, - unsigned char lunaddr[]) +/* + * The block layer has already gone to the trouble of picking out a unique, + * small-integer tag for this request. We use an offset from that value as + * an index to select our command block. (The offset allows us to reserve the + * low-numbered entries for our own uses.) + */ +static int hpsa_get_cmd_index(struct scsi_cmnd *scmd) +{ + int idx = scmd->request->tag; + + if (idx < 0) + return idx; + + /* Offset to leave space for internal cmds. */ + return idx += HPSA_NRESERVED_CMDS; +} + +/* + * Send a TEST_UNIT_READY command to the specified LUN using the specified + * reply queue; returns zero if the unit is ready, and non-zero otherwise. + */ +static int hpsa_send_test_unit_ready(struct ctlr_info *h, + struct CommandList *c, unsigned char lunaddr[], + int reply_queue) +{ + int rc; + + /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */ + (void) fill_cmd(c, TEST_UNIT_READY, h, + NULL, 0, 0, lunaddr, TYPE_CMD); + rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT); + if (rc) + return rc; + /* no unmap needed here because no data xfer. */ + + /* Check if the unit is already ready. */ + if (c->err_info->CommandStatus == CMD_SUCCESS) + return 0; + + /* + * The first command sent after reset will receive "unit attention" to + * indicate that the LUN has been reset...this is actually what we're + * looking for (but, success is good too). + */ + if (c->err_info->CommandStatus == CMD_TARGET_STATUS && + c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION && + (c->err_info->SenseInfo[2] == NO_SENSE || + c->err_info->SenseInfo[2] == UNIT_ATTENTION)) + return 0; + + return 1; +} + +/* + * Wait for a TEST_UNIT_READY command to complete, retrying as necessary; + * returns zero when the unit is ready, and non-zero when giving up. + */ +static int hpsa_wait_for_test_unit_ready(struct ctlr_info *h, + struct CommandList *c, + unsigned char lunaddr[], int reply_queue) { int rc; int count = 0; int waittime = 1; /* seconds */ - struct CommandList *c; - - c = cmd_alloc(h); - if (!c) { - dev_warn(&h->pdev->dev, "out of memory in " - "wait_for_device_to_become_ready.\n"); - return IO_ERROR; - } /* Send test unit ready until device ready, or give up. */ - while (count < HPSA_TUR_RETRY_LIMIT) { + for (count = 0; count < HPSA_TUR_RETRY_LIMIT; count++) { - /* Wait for a bit. do this first, because if we send + /* + * Wait for a bit. do this first, because if we send * the TUR right away, the reset will just abort it. */ msleep(1000 * waittime); - count++; - rc = 0; /* Device ready. */ + + rc = hpsa_send_test_unit_ready(h, c, lunaddr, reply_queue); + if (!rc) + break; /* Increase wait time with each try, up to a point. */ if (waittime < HPSA_MAX_WAIT_INTERVAL_SECS) - waittime = waittime * 2; + waittime *= 2; - /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */ - (void) fill_cmd(c, TEST_UNIT_READY, h, - NULL, 0, 0, lunaddr, TYPE_CMD); - hpsa_scsi_do_simple_cmd_core(h, c); - /* no unmap needed here because no data xfer. */ + dev_warn(&h->pdev->dev, + "waiting %d secs for device to become ready.\n", + waittime); + } - if (c->err_info->CommandStatus == CMD_SUCCESS) - break; + return rc; +} - if (c->err_info->CommandStatus == CMD_TARGET_STATUS && - c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION && - (c->err_info->SenseInfo[2] == NO_SENSE || - c->err_info->SenseInfo[2] == UNIT_ATTENTION)) - break; +static int wait_for_device_to_become_ready(struct ctlr_info *h, + unsigned char lunaddr[], + int reply_queue) +{ + int first_queue; + int last_queue; + int rq; + int rc = 0; + struct CommandList *c; + + c = cmd_alloc(h); + + /* + * If no specific reply queue was requested, then send the TUR + * repeatedly, requesting a reply on each reply queue; otherwise execute + * the loop exactly once using only the specified queue. + */ + if (reply_queue == DEFAULT_REPLY_QUEUE) { + first_queue = 0; + last_queue = h->nreply_queues - 1; + } else { + first_queue = reply_queue; + last_queue = reply_queue; + } - dev_warn(&h->pdev->dev, "waiting %d secs " - "for device to become ready.\n", waittime); - rc = 1; /* device not ready. */ + for (rq = first_queue; rq <= last_queue; rq++) { + rc = hpsa_wait_for_test_unit_ready(h, c, lunaddr, rq); + if (rc) + break; } if (rc) @@ -4345,6 +5437,8 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) int rc; struct ctlr_info *h; struct hpsa_scsi_dev_t *dev; + u8 reset_type; + char msg[48]; /* find the controller to which the command to be aborted was sent */ h = sdev_to_hba(scsicmd->device); @@ -4356,19 +5450,52 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) dev = scsicmd->device->hostdata; if (!dev) { - dev_err(&h->pdev->dev, "hpsa_eh_device_reset_handler: " - "device lookup failed.\n"); + dev_err(&h->pdev->dev, "%s: device lookup failed\n", __func__); return FAILED; } - dev_warn(&h->pdev->dev, "resetting device %d:%d:%d:%d\n", - h->scsi_host->host_no, dev->bus, dev->target, dev->lun); - /* send a reset to the SCSI LUN which the command was sent to */ - rc = hpsa_send_reset(h, dev->scsi3addr, HPSA_RESET_TYPE_LUN); - if (rc == 0 && wait_for_device_to_become_ready(h, dev->scsi3addr) == 0) + + /* if controller locked up, we can guarantee command won't complete */ + if (lockup_detected(h)) { + snprintf(msg, sizeof(msg), + "cmd %d RESET FAILED, lockup detected", + hpsa_get_cmd_index(scsicmd)); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + return FAILED; + } + + /* this reset request might be the result of a lockup; check */ + if (detect_controller_lockup(h)) { + snprintf(msg, sizeof(msg), + "cmd %d RESET FAILED, new lockup detected", + hpsa_get_cmd_index(scsicmd)); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + return FAILED; + } + + /* Do not attempt on controller */ + if (is_hba_lunid(dev->scsi3addr)) return SUCCESS; - dev_warn(&h->pdev->dev, "resetting device failed.\n"); - return FAILED; + if (is_logical_dev_addr_mode(dev->scsi3addr)) + reset_type = HPSA_DEVICE_RESET_MSG; + else + reset_type = HPSA_PHYS_TARGET_RESET; + + sprintf(msg, "resetting %s", + reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical "); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + + h->reset_in_progress = 1; + + /* send a reset to the SCSI LUN which the command was sent to */ + rc = hpsa_do_reset(h, dev, dev->scsi3addr, reset_type, + DEFAULT_REPLY_QUEUE); + sprintf(msg, "reset %s %s", + reset_type == HPSA_DEVICE_RESET_MSG ? "logical " : "physical ", + rc == 0 ? "completed successfully" : "failed"); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + h->reset_in_progress = 0; + return rc == 0 ? SUCCESS : FAILED; } static void swizzle_abort_tag(u8 *tag) @@ -4412,7 +5539,7 @@ static void hpsa_get_tag(struct ctlr_info *h, } static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, - struct CommandList *abort, int swizzle) + struct CommandList *abort, int reply_queue) { int rc = IO_OK; struct CommandList *c; @@ -4420,19 +5547,15 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, __le32 tagupper, taglower; c = cmd_alloc(h); - if (c == NULL) { /* trouble... */ - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - return -ENOMEM; - } /* fill_cmd can't fail here, no buffer to map */ - (void) fill_cmd(c, HPSA_ABORT_MSG, h, abort, + (void) fill_cmd(c, HPSA_ABORT_MSG, h, &abort->Header.tag, 0, 0, scsi3addr, TYPE_MSG); - if (swizzle) + if (h->needs_abort_tags_swizzled) swizzle_abort_tag(&c->Request.CDB[4]); - hpsa_scsi_do_simple_cmd_core(h, c); + (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT); hpsa_get_tag(h, abort, &taglower, &tagupper); - dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n", + dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd(abort) completed.\n", __func__, tagupper, taglower); /* no unmap needed here because no data xfer. */ @@ -4440,6 +5563,9 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, switch (ei->CommandStatus) { case CMD_SUCCESS: break; + case CMD_TMF_STATUS: + rc = hpsa_evaluate_tmf_status(h, c); + break; case CMD_UNABORTABLE: /* Very common, don't make noise. */ rc = -1; break; @@ -4456,20 +5582,62 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr, return rc; } -/* ioaccel2 path firmware cannot handle abort task requests. - * Change abort requests to physical target reset, and send to the - * address of the physical disk used for the ioaccel 2 command. - * Return 0 on success (IO_OK) - * -1 on failure - */ - -static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, - unsigned char *scsi3addr, struct CommandList *abort) +static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h, + struct CommandList *command_to_abort, int reply_queue) { - int rc = IO_OK; - struct scsi_cmnd *scmd; /* scsi command within request being aborted */ - struct hpsa_scsi_dev_t *dev; /* device to which scsi cmd was sent */ - unsigned char phys_scsi3addr[8]; /* addr of phys disk with volume */ + struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex]; + struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2; + struct io_accel2_cmd *c2a = + &h->ioaccel2_cmd_pool[command_to_abort->cmdindex]; + struct scsi_cmnd *scmd = command_to_abort->scsi_cmd; + struct hpsa_scsi_dev_t *dev = scmd->device->hostdata; + + /* + * We're overlaying struct hpsa_tmf_struct on top of something which + * was allocated as a struct io_accel2_cmd, so we better be sure it + * actually fits, and doesn't overrun the error info space. + */ + BUILD_BUG_ON(sizeof(struct hpsa_tmf_struct) > + sizeof(struct io_accel2_cmd)); + BUG_ON(offsetof(struct io_accel2_cmd, error_data) < + offsetof(struct hpsa_tmf_struct, error_len) + + sizeof(ac->error_len)); + + c->cmd_type = IOACCEL2_TMF; + c->scsi_cmd = SCSI_CMD_BUSY; + + /* Adjust the DMA address to point to the accelerated command buffer */ + c->busaddr = (u32) h->ioaccel2_cmd_pool_dhandle + + (c->cmdindex * sizeof(struct io_accel2_cmd)); + BUG_ON(c->busaddr & 0x0000007F); + + memset(ac, 0, sizeof(*c2)); /* yes this is correct */ + ac->iu_type = IOACCEL2_IU_TMF_TYPE; + ac->reply_queue = reply_queue; + ac->tmf = IOACCEL2_TMF_ABORT; + ac->it_nexus = cpu_to_le32(dev->ioaccel_handle); + memset(ac->lun_id, 0, sizeof(ac->lun_id)); + ac->tag = cpu_to_le64(c->cmdindex << DIRECT_LOOKUP_SHIFT); + ac->abort_tag = cpu_to_le64(le32_to_cpu(c2a->Tag)); + ac->error_ptr = cpu_to_le64(c->busaddr + + offsetof(struct io_accel2_cmd, error_data)); + ac->error_len = cpu_to_le32(sizeof(c2->error_data)); +} + +/* ioaccel2 path firmware cannot handle abort task requests. + * Change abort requests to physical target reset, and send to the + * address of the physical disk used for the ioaccel 2 command. + * Return 0 on success (IO_OK) + * -1 on failure + */ + +static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, + unsigned char *scsi3addr, struct CommandList *abort, int reply_queue) +{ + int rc = IO_OK; + struct scsi_cmnd *scmd; /* scsi command within request being aborted */ + struct hpsa_scsi_dev_t *dev; /* device to which scsi cmd was sent */ + unsigned char phys_scsi3addr[8]; /* addr of phys disk with volume */ unsigned char *psa = &phys_scsi3addr[0]; /* Get a pointer to the hpsa logical device. */ @@ -4483,8 +5651,9 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, if (h->raid_offload_debug > 0) dev_info(&h->pdev->dev, - "Reset as abort: Abort requested on C%d:B%d:T%d:L%d scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + "scsi %d:%d:%d:%d %s scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", h->scsi_host->host_no, dev->bus, dev->target, dev->lun, + "Reset as abort", scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3], scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]); @@ -4506,7 +5675,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, "Reset as abort: Resetting physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", psa[0], psa[1], psa[2], psa[3], psa[4], psa[5], psa[6], psa[7]); - rc = hpsa_send_reset(h, psa, HPSA_RESET_TYPE_TARGET); + rc = hpsa_do_reset(h, dev, psa, HPSA_RESET_TYPE_TARGET, reply_queue); if (rc != 0) { dev_warn(&h->pdev->dev, "Reset as abort: Failed on physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", @@ -4516,7 +5685,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, } /* wait for device to recover */ - if (wait_for_device_to_become_ready(h, psa) != 0) { + if (wait_for_device_to_become_ready(h, psa, reply_queue) != 0) { dev_warn(&h->pdev->dev, "Reset as abort: Failed: Device never recovered from reset: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", psa[0], psa[1], psa[2], psa[3], @@ -4533,25 +5702,94 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h, return rc; /* success */ } -/* Some Smart Arrays need the abort tag swizzled, and some don't. It's hard to - * tell which kind we're dealing with, so we send the abort both ways. There - * shouldn't be any collisions between swizzled and unswizzled tags due to the - * way we construct our tags but we check anyway in case the assumptions which - * make this true someday become false. - */ +static int hpsa_send_abort_ioaccel2(struct ctlr_info *h, + struct CommandList *abort, int reply_queue) +{ + int rc = IO_OK; + struct CommandList *c; + __le32 taglower, tagupper; + struct hpsa_scsi_dev_t *dev; + struct io_accel2_cmd *c2; + + dev = abort->scsi_cmd->device->hostdata; + if (!dev->offload_enabled && !dev->hba_ioaccel_enabled) + return -1; + + c = cmd_alloc(h); + setup_ioaccel2_abort_cmd(c, h, abort, reply_queue); + c2 = &h->ioaccel2_cmd_pool[c->cmdindex]; + (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT); + hpsa_get_tag(h, abort, &taglower, &tagupper); + dev_dbg(&h->pdev->dev, + "%s: Tag:0x%08x:%08x: do_simple_cmd(ioaccel2 abort) completed.\n", + __func__, tagupper, taglower); + /* no unmap needed here because no data xfer. */ + + dev_dbg(&h->pdev->dev, + "%s: Tag:0x%08x:%08x: abort service response = 0x%02x.\n", + __func__, tagupper, taglower, c2->error_data.serv_response); + switch (c2->error_data.serv_response) { + case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE: + case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS: + rc = 0; + break; + case IOACCEL2_SERV_RESPONSE_TMF_REJECTED: + case IOACCEL2_SERV_RESPONSE_FAILURE: + case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN: + rc = -1; + break; + default: + dev_warn(&h->pdev->dev, + "%s: Tag:0x%08x:%08x: unknown abort service response 0x%02x\n", + __func__, tagupper, taglower, + c2->error_data.serv_response); + rc = -1; + } + cmd_free(h, c); + dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__, + tagupper, taglower); + return rc; +} + static int hpsa_send_abort_both_ways(struct ctlr_info *h, - unsigned char *scsi3addr, struct CommandList *abort) + unsigned char *scsi3addr, struct CommandList *abort, int reply_queue) { - /* ioccelerator mode 2 commands should be aborted via the + /* + * ioccelerator mode 2 commands should be aborted via the * accelerated path, since RAID path is unaware of these commands, - * but underlying firmware can't handle abort TMF. - * Change abort to physical device reset. + * but not all underlying firmware can handle abort TMF. + * Change abort to physical device reset when abort TMF is unsupported. */ - if (abort->cmd_type == CMD_IOACCEL2) - return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr, abort); + if (abort->cmd_type == CMD_IOACCEL2) { + if (HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags) + return hpsa_send_abort_ioaccel2(h, abort, + reply_queue); + else + return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr, + abort, reply_queue); + } + return hpsa_send_abort(h, scsi3addr, abort, reply_queue); +} + +/* Find out which reply queue a command was meant to return on */ +static int hpsa_extract_reply_queue(struct ctlr_info *h, + struct CommandList *c) +{ + if (c->cmd_type == CMD_IOACCEL2) + return h->ioaccel2_cmd_pool[c->cmdindex].reply_queue; + return c->Header.ReplyQueue; +} - return hpsa_send_abort(h, scsi3addr, abort, 0) && - hpsa_send_abort(h, scsi3addr, abort, 1); +/* + * Limit concurrency of abort commands to prevent + * over-subscription of commands + */ +static inline int wait_for_available_abort_cmd(struct ctlr_info *h) +{ +#define ABORT_CMD_WAIT_MSECS 5000 + return !wait_event_timeout(h->abort_cmd_wait_queue, + atomic_dec_if_positive(&h->abort_cmds_available) >= 0, + msecs_to_jiffies(ABORT_CMD_WAIT_MSECS)); } /* Send an abort for the specified command. @@ -4561,7 +5799,7 @@ static int hpsa_send_abort_both_ways(struct ctlr_info *h, static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) { - int i, rc; + int rc; struct ctlr_info *h; struct hpsa_scsi_dev_t *dev; struct CommandList *abort; /* pointer to command to be aborted */ @@ -4569,27 +5807,19 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) char msg[256]; /* For debug messaging. */ int ml = 0; __le32 tagupper, taglower; - int refcount; + int refcount, reply_queue; - /* Find the controller of the command to be aborted */ - h = sdev_to_hba(sc->device); - if (WARN(h == NULL, - "ABORT REQUEST FAILED, Controller lookup failed.\n")) + if (sc == NULL) return FAILED; - if (lockup_detected(h)) + if (sc->device == NULL) return FAILED; - /* Check that controller supports some kind of task abort */ - if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) && - !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags)) + /* Find the controller of the command to be aborted */ + h = sdev_to_hba(sc->device); + if (h == NULL) return FAILED; - memset(msg, 0, sizeof(msg)); - ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%llu ", - h->scsi_host->host_no, sc->device->channel, - sc->device->id, sc->device->lun); - /* Find the device of the command to be aborted */ dev = sc->device->hostdata; if (!dev) { @@ -4598,6 +5828,31 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) return FAILED; } + /* If controller locked up, we can guarantee command won't complete */ + if (lockup_detected(h)) { + hpsa_show_dev_msg(KERN_WARNING, h, dev, + "ABORT FAILED, lockup detected"); + return FAILED; + } + + /* This is a good time to check if controller lockup has occurred */ + if (detect_controller_lockup(h)) { + hpsa_show_dev_msg(KERN_WARNING, h, dev, + "ABORT FAILED, new lockup detected"); + return FAILED; + } + + /* Check that controller supports some kind of task abort */ + if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) && + !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags)) + return FAILED; + + memset(msg, 0, sizeof(msg)); + ml += sprintf(msg+ml, "scsi %d:%d:%d:%llu %s %p", + h->scsi_host->host_no, sc->device->channel, + sc->device->id, sc->device->lun, + "Aborting command", sc); + /* Get SCSI command to be aborted */ abort = (struct CommandList *) sc->host_scribble; if (abort == NULL) { @@ -4609,50 +5864,115 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) cmd_free(h, abort); return SUCCESS; } + + /* Don't bother trying the abort if we know it won't work. */ + if (abort->cmd_type != CMD_IOACCEL2 && + abort->cmd_type != CMD_IOACCEL1 && !dev->supports_aborts) { + cmd_free(h, abort); + return FAILED; + } + + /* + * Check that we're aborting the right command. + * It's possible the CommandList already completed and got re-used. + */ + if (abort->scsi_cmd != sc) { + cmd_free(h, abort); + return SUCCESS; + } + + abort->abort_pending = true; hpsa_get_tag(h, abort, &taglower, &tagupper); + reply_queue = hpsa_extract_reply_queue(h, abort); ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower); as = abort->scsi_cmd; if (as != NULL) - ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ", - as->cmnd[0], as->serial_number); - dev_dbg(&h->pdev->dev, "%s\n", msg); - dev_warn(&h->pdev->dev, "Abort request on C%d:B%d:T%d:L%d\n", - h->scsi_host->host_no, dev->bus, dev->target, dev->lun); + ml += sprintf(msg+ml, + "CDBLen: %d CDB: 0x%02x%02x... SN: 0x%lx ", + as->cmd_len, as->cmnd[0], as->cmnd[1], + as->serial_number); + dev_warn(&h->pdev->dev, "%s BEING SENT\n", msg); + hpsa_show_dev_msg(KERN_WARNING, h, dev, "Aborting command"); + /* * Command is in flight, or possibly already completed * by the firmware (but not to the scsi mid layer) but we can't * distinguish which. Send the abort down. */ - rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort); + if (wait_for_available_abort_cmd(h)) { + dev_warn(&h->pdev->dev, + "%s FAILED, timeout waiting for an abort command to become available.\n", + msg); + cmd_free(h, abort); + return FAILED; + } + rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort, reply_queue); + atomic_inc(&h->abort_cmds_available); + wake_up_all(&h->abort_cmd_wait_queue); if (rc != 0) { - dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg); - dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n", - h->scsi_host->host_no, - dev->bus, dev->target, dev->lun); + dev_warn(&h->pdev->dev, "%s SENT, FAILED\n", msg); + hpsa_show_dev_msg(KERN_WARNING, h, dev, + "FAILED to abort command"); cmd_free(h, abort); return FAILED; } - dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg); + dev_info(&h->pdev->dev, "%s SENT, SUCCESS\n", msg); + wait_event(h->event_sync_wait_queue, + abort->scsi_cmd != sc || lockup_detected(h)); + cmd_free(h, abort); + return !lockup_detected(h) ? SUCCESS : FAILED; +} - /* If the abort(s) above completed and actually aborted the - * command, then the command to be aborted should already be - * completed. If not, wait around a bit more to see if they - * manage to complete normally. - */ -#define ABORT_COMPLETE_WAIT_SECS 30 - for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) { - refcount = atomic_read(&abort->refcount); - if (refcount < 2) { - cmd_free(h, abort); - return SUCCESS; - } else { - msleep(100); - } +/* + * For operations with an associated SCSI command, a command block is allocated + * at init, and managed by cmd_tagged_alloc() and cmd_tagged_free() using the + * block request tag as an index into a table of entries. cmd_tagged_free() is + * the complement, although cmd_free() may be called instead. + */ +static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h, + struct scsi_cmnd *scmd) +{ + int idx = hpsa_get_cmd_index(scmd); + struct CommandList *c = h->cmd_pool + idx; + + if (idx < HPSA_NRESERVED_CMDS || idx >= h->nr_cmds) { + dev_err(&h->pdev->dev, "Bad block tag: %d not in [%d..%d]\n", + idx, HPSA_NRESERVED_CMDS, h->nr_cmds - 1); + /* The index value comes from the block layer, so if it's out of + * bounds, it's probably not our bug. + */ + BUG(); } - dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n", - msg, ABORT_COMPLETE_WAIT_SECS); - cmd_free(h, abort); - return FAILED; + + atomic_inc(&c->refcount); + if (unlikely(!hpsa_is_cmd_idle(c))) { + /* + * We expect that the SCSI layer will hand us a unique tag + * value. Thus, there should never be a collision here between + * two requests...because if the selected command isn't idle + * then someone is going to be very disappointed. + */ + dev_err(&h->pdev->dev, + "tag collision (tag=%d) in cmd_tagged_alloc().\n", + idx); + if (c->scsi_cmd != NULL) + scsi_print_command(c->scsi_cmd); + scsi_print_command(scmd); + } + + hpsa_cmd_partial_init(h, idx, c); + return c; +} + +static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c) +{ + /* + * Release our reference to the block. We don't need to do anything + * else to free it, because it is accessed by index. (There's no point + * in checking the result of the decrement, since we cannot guarantee + * that there isn't a concurrent abort which is also accessing it.) + */ + (void)atomic_dec(&c->refcount); } /* @@ -4660,16 +5980,15 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc) * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track * which ones are free or in use. Lock must be held when calling this. * cmd_free() is the complement. + * This function never gives up and returns NULL. If it hangs, + * another thread must call cmd_free() to free some tags. */ static struct CommandList *cmd_alloc(struct ctlr_info *h) { struct CommandList *c; - int i; - union u64bit temp64; - dma_addr_t cmd_dma_handle, err_dma_handle; - int refcount; - unsigned long offset; + int refcount, i; + int offset = 0; /* * There is some *extremely* small but non-zero chance that that @@ -4681,12 +6000,20 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) * very unlucky thread might be starved anyway, never able to * beat the other threads. In reality, this happens so * infrequently as to be indistinguishable from never. + * + * Note that we start allocating commands before the SCSI host structure + * is initialized. Since the search starts at bit zero, this + * all works, since we have at least one command structure available; + * however, it means that the structures with the low indexes have to be + * reserved for driver-initiated requests, while requests from the block + * layer will use the higher indexes. */ - offset = h->last_allocation; /* benignly racy */ for (;;) { - i = find_next_zero_bit(h->cmd_pool_bits, h->nr_cmds, offset); - if (unlikely(i == h->nr_cmds)) { + i = find_next_zero_bit(h->cmd_pool_bits, + HPSA_NRESERVED_CMDS, + offset); + if (unlikely(i >= HPSA_NRESERVED_CMDS)) { offset = 0; continue; } @@ -4694,35 +6021,23 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h) refcount = atomic_inc_return(&c->refcount); if (unlikely(refcount > 1)) { cmd_free(h, c); /* already in use */ - offset = (i + 1) % h->nr_cmds; + offset = (i + 1) % HPSA_NRESERVED_CMDS; continue; } set_bit(i & (BITS_PER_LONG - 1), h->cmd_pool_bits + (i / BITS_PER_LONG)); break; /* it's ours now. */ } - h->last_allocation = i; /* benignly racy */ - - /* Zero out all of commandlist except the last field, refcount */ - memset(c, 0, offsetof(struct CommandList, refcount)); - c->Header.tag = cpu_to_le64((u64) (i << DIRECT_LOOKUP_SHIFT)); - cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(*c); - c->err_info = h->errinfo_pool + i; - memset(c->err_info, 0, sizeof(*c->err_info)); - err_dma_handle = h->errinfo_pool_dhandle - + i * sizeof(*c->err_info); - - c->cmdindex = i; - - c->busaddr = (u32) cmd_dma_handle; - temp64.val = (u64) err_dma_handle; - c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle); - c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info)); - - c->h = h; + hpsa_cmd_partial_init(h, i, c); return c; } +/* + * This is the complementary operation to cmd_alloc(). Note, however, in some + * corner cases it may also be used to free blocks allocated by + * cmd_tagged_alloc() in which case the ref-count decrement does the trick and + * the clear-bit is harmless. + */ static void cmd_free(struct ctlr_info *h, struct CommandList *c) { if (atomic_dec_and_test(&c->refcount)) { @@ -4900,7 +6215,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) if (iocommand.buf_size > 0) { buff = kmalloc(iocommand.buf_size, GFP_KERNEL); if (buff == NULL) - return -EFAULT; + return -ENOMEM; if (iocommand.Request.Type.Direction & XFER_WRITE) { /* Copy the data into the buffer we created */ if (copy_from_user(buff, iocommand.buf, @@ -4913,12 +6228,10 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) } } c = cmd_alloc(h); - if (c == NULL) { - rc = -ENOMEM; - goto out_kfree; - } + /* Fill in the command type */ c->cmd_type = CMD_IOCTL_PEND; + c->scsi_cmd = SCSI_CMD_BUSY; /* Fill in Command Header */ c->Header.ReplyQueue = 0; /* unused in simple mode */ if (iocommand.buf_size > 0) { /* buffer to fill */ @@ -4948,10 +6261,14 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp) c->SG[0].Len = cpu_to_le32(iocommand.buf_size); c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */ } - hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); + rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT); if (iocommand.buf_size > 0) hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); + if (rc) { + rc = -EIO; + goto out; + } /* Copy the error information out */ memcpy(&iocommand.error_info, c->err_info, @@ -5048,11 +6365,9 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) sg_used++; } c = cmd_alloc(h); - if (c == NULL) { - status = -ENOMEM; - goto cleanup1; - } + c->cmd_type = CMD_IOCTL_PEND; + c->scsi_cmd = SCSI_CMD_BUSY; c->Header.ReplyQueue = 0; c->Header.SGList = (u8) sg_used; c->Header.SGTotal = cpu_to_le16(sg_used); @@ -5078,10 +6393,15 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp) } c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST); } - hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c); + status = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT); if (sg_used) hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL); check_ioctl_unit_attention(h, c); + if (status) { + status = -EIO; + goto cleanup0; + } + /* Copy the error information out */ memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info)); if (copy_to_user(argp, ioc, sizeof(*ioc))) { @@ -5163,14 +6483,13 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg) } } -static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr, +static void hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr, u8 reset_type) { struct CommandList *c; c = cmd_alloc(h); - if (!c) - return -ENOMEM; + /* fill_cmd can't fail here, no data buffer to map */ (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0, RAID_CTLR_LUNID, TYPE_MSG); @@ -5181,7 +6500,7 @@ static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr, * the command either. This is the last command we will send before * re-initializing everything, so it doesn't matter and won't leak. */ - return 0; + return; } static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, @@ -5189,9 +6508,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, int cmd_type) { int pci_dir = XFER_NONE; - struct CommandList *a; /* for commands to be aborted */ + u64 tag; /* for commands to be aborted */ c->cmd_type = CMD_IOCTL_PEND; + c->scsi_cmd = SCSI_CMD_BUSY; c->Header.ReplyQueue = 0; if (buff != NULL && size > 0) { c->Header.SGList = 1; @@ -5232,6 +6552,24 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[8] = (size >> 8) & 0xFF; c->Request.CDB[9] = size & 0xFF; break; + case BMIC_SENSE_DIAG_OPTIONS: + c->Request.CDBLen = 16; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); + c->Request.Timeout = 0; + /* Spec says this should be BMIC_WRITE */ + c->Request.CDB[0] = BMIC_READ; + c->Request.CDB[6] = BMIC_SENSE_DIAG_OPTIONS; + break; + case BMIC_SET_DIAG_OPTIONS: + c->Request.CDBLen = 16; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, + ATTR_SIMPLE, XFER_WRITE); + c->Request.Timeout = 0; + c->Request.CDB[0] = BMIC_WRITE; + c->Request.CDB[6] = BMIC_SET_DIAG_OPTIONS; + break; case HPSA_CACHE_FLUSH: c->Request.CDBLen = 12; c->Request.type_attr_dir = @@ -5281,6 +6619,32 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[7] = (size >> 16) & 0xFF; c->Request.CDB[8] = (size >> 8) & 0XFF; break; + case BMIC_SENSE_SUBSYSTEM_INFORMATION: + c->Request.CDBLen = 10; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); + c->Request.Timeout = 0; + c->Request.CDB[0] = BMIC_READ; + c->Request.CDB[6] = BMIC_SENSE_SUBSYSTEM_INFORMATION; + c->Request.CDB[7] = (size >> 16) & 0xFF; + c->Request.CDB[8] = (size >> 8) & 0XFF; + break; + case BMIC_IDENTIFY_CONTROLLER: + c->Request.CDBLen = 10; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ); + c->Request.Timeout = 0; + c->Request.CDB[0] = BMIC_READ; + c->Request.CDB[1] = 0; + c->Request.CDB[2] = 0; + c->Request.CDB[3] = 0; + c->Request.CDB[4] = 0; + c->Request.CDB[5] = 0; + c->Request.CDB[6] = BMIC_IDENTIFY_CONTROLLER; + c->Request.CDB[7] = (size >> 16) & 0xFF; + c->Request.CDB[8] = (size >> 8) & 0XFF; + c->Request.CDB[9] = 0; + break; default: dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd); BUG(); @@ -5289,6 +6653,20 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, } else if (cmd_type == TYPE_MSG) { switch (cmd) { + case HPSA_PHYS_TARGET_RESET: + c->Request.CDBLen = 16; + c->Request.type_attr_dir = + TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE); + c->Request.Timeout = 0; /* Don't time out */ + memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB)); + c->Request.CDB[0] = HPSA_RESET; + c->Request.CDB[1] = HPSA_TARGET_RESET_TYPE; + /* Physical target reset needs no control bytes 4-7*/ + c->Request.CDB[4] = 0x00; + c->Request.CDB[5] = 0x00; + c->Request.CDB[6] = 0x00; + c->Request.CDB[7] = 0x00; + break; case HPSA_DEVICE_RESET_MSG: c->Request.CDBLen = 16; c->Request.type_attr_dir = @@ -5305,10 +6683,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[7] = 0x00; break; case HPSA_ABORT_MSG: - a = buff; /* point to command to be aborted */ + memcpy(&tag, buff, sizeof(tag)); dev_dbg(&h->pdev->dev, - "Abort Tag:0x%016llx request Tag:0x%016llx", - a->Header.tag, c->Header.tag); + "Abort Tag:0x%016llx using rqst Tag:0x%016llx", + tag, c->Header.tag); c->Request.CDBLen = 16; c->Request.type_attr_dir = TYPE_ATTR_DIR(cmd_type, @@ -5319,8 +6697,7 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[2] = 0x00; /* reserved */ c->Request.CDB[3] = 0x00; /* reserved */ /* Tag to abort goes in CDB[4]-CDB[11] */ - memcpy(&c->Request.CDB[4], &a->Header.tag, - sizeof(a->Header.tag)); + memcpy(&c->Request.CDB[4], &tag, sizeof(tag)); c->Request.CDB[12] = 0x00; /* reserved */ c->Request.CDB[13] = 0x00; /* reserved */ c->Request.CDB[14] = 0x00; /* reserved */ @@ -5399,20 +6776,10 @@ static inline void finish_cmd(struct CommandList *c) if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI || c->cmd_type == CMD_IOACCEL2)) complete_scsi_command(c); - else if (c->cmd_type == CMD_IOCTL_PEND) + else if (c->cmd_type == CMD_IOCTL_PEND || c->cmd_type == IOACCEL2_TMF) complete(c->waiting); } - -static inline u32 hpsa_tag_discard_error_bits(struct ctlr_info *h, u32 tag) -{ -#define HPSA_PERF_ERROR_BITS ((1 << DIRECT_LOOKUP_SHIFT) - 1) -#define HPSA_SIMPLE_ERROR_BITS 0x03 - if (unlikely(!(h->transMethod & CFGTBL_Trans_Performant))) - return tag & ~HPSA_SIMPLE_ERROR_BITS; - return tag & ~HPSA_PERF_ERROR_BITS; -} - /* process completion of an indexed ("direct lookup") command */ static inline void process_indexed_cmd(struct ctlr_info *h, u32 raw_tag) @@ -5733,7 +7100,7 @@ static int controller_reset_failed(struct CfgTable __iomem *cfgtable) /* This does a hard reset of the controller using PCI power management * states or the using the doorbell register. */ -static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) +static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev, u32 board_id) { u64 cfg_offset; u32 cfg_base_addr; @@ -5744,7 +7111,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) int rc; struct CfgTable __iomem *cfgtable; u32 use_doorbell; - u32 board_id; u16 command_register; /* For controllers as old as the P600, this is very nearly @@ -5760,11 +7126,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev) * using the doorbell register. */ - rc = hpsa_lookup_board_id(pdev, &board_id); - if (rc < 0) { - dev_warn(&pdev->dev, "Board ID not found\n"); - return rc; - } if (!ctlr_is_resettable(board_id)) { dev_warn(&pdev->dev, "Controller not resettable\n"); return -ENODEV; @@ -5930,10 +7291,22 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr) return -1; } +static void hpsa_disable_interrupt_mode(struct ctlr_info *h) +{ + if (h->msix_vector) { + if (h->pdev->msix_enabled) + pci_disable_msix(h->pdev); + h->msix_vector = 0; + } else if (h->msi_vector) { + if (h->pdev->msi_enabled) + pci_disable_msi(h->pdev); + h->msi_vector = 0; + } +} + /* If MSI/MSI-X is supported by the kernel we will try to enable it on * controllers that are capable. If not, we use legacy INTx mode. */ - static void hpsa_interrupt_mode(struct ctlr_info *h) { #ifdef CONFIG_PCI_MSI @@ -6064,6 +7437,21 @@ static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr, return 0; } +static void hpsa_free_cfgtables(struct ctlr_info *h) +{ + if (h->transtable) { + iounmap(h->transtable); + h->transtable = NULL; + } + if (h->cfgtable) { + iounmap(h->cfgtable); + h->cfgtable = NULL; + } +} + +/* Find and map CISS config table and transfer table ++ * several items must be unmapped (freed) later ++ * */ static int hpsa_find_cfgtables(struct ctlr_info *h) { u64 cfg_offset; @@ -6090,25 +7478,31 @@ static int hpsa_find_cfgtables(struct ctlr_info *h) h->transtable = remap_pci_mem(pci_resource_start(h->pdev, cfg_base_addr_index)+cfg_offset+trans_offset, sizeof(*h->transtable)); - if (!h->transtable) + if (!h->transtable) { + dev_err(&h->pdev->dev, "Failed mapping transfer table\n"); + hpsa_free_cfgtables(h); return -ENOMEM; + } return 0; } static void hpsa_get_max_perf_mode_cmds(struct ctlr_info *h) { - h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands)); +#define MIN_MAX_COMMANDS 16 + BUILD_BUG_ON(MIN_MAX_COMMANDS <= HPSA_NRESERVED_CMDS); + + h->max_commands = readl(&h->cfgtable->MaxPerformantModeCommands); /* Limit commands in memory limited kdump scenario. */ if (reset_devices && h->max_commands > 32) h->max_commands = 32; - if (h->max_commands < 16) { - dev_warn(&h->pdev->dev, "Controller reports " - "max supported commands of %d, an obvious lie. " - "Using 16. Ensure that firmware is up to date.\n", - h->max_commands); - h->max_commands = 16; + if (h->max_commands < MIN_MAX_COMMANDS) { + dev_warn(&h->pdev->dev, + "Controller reports max supported commands of %d Using %d instead. Ensure that firmware is up to date.\n", + h->max_commands, + MIN_MAX_COMMANDS); + h->max_commands = MIN_MAX_COMMANDS; } } @@ -6153,6 +7547,8 @@ static void hpsa_find_board_params(struct ctlr_info *h) dev_warn(&h->pdev->dev, "Physical aborts not supported\n"); if (!(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags)) dev_warn(&h->pdev->dev, "Logical aborts not supported\n"); + if (!(HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags)) + dev_warn(&h->pdev->dev, "HP SSD Smart Path aborts not supported\n"); } static inline bool hpsa_CISS_signature_present(struct ctlr_info *h) @@ -6222,6 +7618,8 @@ static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h) * as we enter this code.) */ for (i = 0; i < MAX_MODE_CHANGE_WAIT; i++) { + if (h->remove_in_progress) + goto done; spin_lock_irqsave(&h->lock, flags); doorbell_value = readl(h->vaddr + SA5_DOORBELL); spin_unlock_irqrestore(&h->lock, flags); @@ -6262,6 +7660,22 @@ error: return -ENODEV; } +/* free items allocated or mapped by hpsa_pci_init */ +static void hpsa_free_pci_init(struct ctlr_info *h) +{ + hpsa_free_cfgtables(h); /* pci_init 4 */ + iounmap(h->vaddr); /* pci_init 3 */ + h->vaddr = NULL; + hpsa_disable_interrupt_mode(h); /* pci_init 2 */ + /* + * call pci_disable_device before pci_release_regions per + * Documentation/PCI/pci.txt + */ + pci_disable_device(h->pdev); /* pci_init 1 */ + pci_release_regions(h->pdev); /* pci_init 2 */ +} + +/* several items must be freed later */ static int hpsa_pci_init(struct ctlr_info *h) { int prod_index, err; @@ -6272,19 +7686,24 @@ static int hpsa_pci_init(struct ctlr_info *h) h->product_name = products[prod_index].product_name; h->access = *(products[prod_index].access); + h->needs_abort_tags_swizzled = + ctlr_needs_abort_tags_swizzled(h->board_id); + pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); err = pci_enable_device(h->pdev); if (err) { - dev_warn(&h->pdev->dev, "unable to enable PCI device\n"); + dev_err(&h->pdev->dev, "failed to enable PCI device\n"); + pci_disable_device(h->pdev); return err; } err = pci_request_regions(h->pdev, HPSA); if (err) { dev_err(&h->pdev->dev, - "cannot obtain PCI resources, aborting\n"); + "failed to obtain PCI resources\n"); + pci_disable_device(h->pdev); return err; } @@ -6293,38 +7712,43 @@ static int hpsa_pci_init(struct ctlr_info *h) hpsa_interrupt_mode(h); err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr); if (err) - goto err_out_free_res; + goto clean2; /* intmode+region, pci */ h->vaddr = remap_pci_mem(h->paddr, 0x250); if (!h->vaddr) { + dev_err(&h->pdev->dev, "failed to remap PCI mem\n"); err = -ENOMEM; - goto err_out_free_res; + goto clean2; /* intmode+region, pci */ } err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY); if (err) - goto err_out_free_res; + goto clean3; /* vaddr, intmode+region, pci */ err = hpsa_find_cfgtables(h); if (err) - goto err_out_free_res; + goto clean3; /* vaddr, intmode+region, pci */ hpsa_find_board_params(h); if (!hpsa_CISS_signature_present(h)) { err = -ENODEV; - goto err_out_free_res; + goto clean4; /* cfgtables, vaddr, intmode+region, pci */ } hpsa_set_driver_support_bits(h); hpsa_p600_dma_prefetch_quirk(h); err = hpsa_enter_simple_mode(h); if (err) - goto err_out_free_res; + goto clean4; /* cfgtables, vaddr, intmode+region, pci */ return 0; -err_out_free_res: - if (h->transtable) - iounmap(h->transtable); - if (h->cfgtable) - iounmap(h->cfgtable); - if (h->vaddr) - iounmap(h->vaddr); +clean4: /* cfgtables, vaddr, intmode+region, pci */ + hpsa_free_cfgtables(h); +clean3: /* vaddr, intmode+region, pci */ + iounmap(h->vaddr); + h->vaddr = NULL; +clean2: /* intmode+region, pci */ + hpsa_disable_interrupt_mode(h); + /* + * call pci_disable_device before pci_release_regions per + * Documentation/PCI/pci.txt + */ pci_disable_device(h->pdev); pci_release_regions(h->pdev); return err; @@ -6346,7 +7770,7 @@ static void hpsa_hba_inquiry(struct ctlr_info *h) } } -static int hpsa_init_reset_devices(struct pci_dev *pdev) +static int hpsa_init_reset_devices(struct pci_dev *pdev, u32 board_id) { int rc, i; void __iomem *vaddr; @@ -6382,7 +7806,7 @@ static int hpsa_init_reset_devices(struct pci_dev *pdev) iounmap(vaddr); /* Reset the controller with a PCI power-cycle or via doorbell */ - rc = hpsa_kdump_hard_reset_controller(pdev); + rc = hpsa_kdump_hard_reset_controller(pdev, board_id); /* -ENOTSUPP here means we cannot reset the controller * but it's already (and still) up and running in @@ -6408,7 +7832,29 @@ out_disable: return rc; } -static int hpsa_allocate_cmd_pool(struct ctlr_info *h) +static void hpsa_free_cmd_pool(struct ctlr_info *h) +{ + kfree(h->cmd_pool_bits); + h->cmd_pool_bits = NULL; + if (h->cmd_pool) { + pci_free_consistent(h->pdev, + h->nr_cmds * sizeof(struct CommandList), + h->cmd_pool, + h->cmd_pool_dhandle); + h->cmd_pool = NULL; + h->cmd_pool_dhandle = 0; + } + if (h->errinfo_pool) { + pci_free_consistent(h->pdev, + h->nr_cmds * sizeof(struct ErrorInfo), + h->errinfo_pool, + h->errinfo_pool_dhandle); + h->errinfo_pool = NULL; + h->errinfo_pool_dhandle = 0; + } +} + +static int hpsa_alloc_cmd_pool(struct ctlr_info *h) { h->cmd_pool_bits = kzalloc( DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) * @@ -6425,34 +7871,13 @@ static int hpsa_allocate_cmd_pool(struct ctlr_info *h) dev_err(&h->pdev->dev, "out of memory in %s", __func__); goto clean_up; } + hpsa_preinitialize_commands(h); return 0; clean_up: hpsa_free_cmd_pool(h); return -ENOMEM; } -static void hpsa_free_cmd_pool(struct ctlr_info *h) -{ - kfree(h->cmd_pool_bits); - if (h->cmd_pool) - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(struct CommandList), - h->cmd_pool, h->cmd_pool_dhandle); - if (h->ioaccel2_cmd_pool) - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool), - h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle); - if (h->errinfo_pool) - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(struct ErrorInfo), - h->errinfo_pool, - h->errinfo_pool_dhandle); - if (h->ioaccel_cmd_pool) - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(struct io_accel1_cmd), - h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle); -} - static void hpsa_irq_affinity_hints(struct ctlr_info *h) { int i, cpu; @@ -6474,12 +7899,14 @@ static void hpsa_free_irqs(struct ctlr_info *h) i = h->intr_mode; irq_set_affinity_hint(h->intr[i], NULL); free_irq(h->intr[i], &h->q[i]); + h->q[i] = 0; return; } for (i = 0; i < h->msix_vector; i++) { irq_set_affinity_hint(h->intr[i], NULL); free_irq(h->intr[i], &h->q[i]); + h->q[i] = 0; } for (; i < MAX_REPLY_QUEUES; i++) h->q[i] = 0; @@ -6502,8 +7929,9 @@ static int hpsa_request_irqs(struct ctlr_info *h, if (h->intr_mode == PERF_MODE_INT && h->msix_vector > 0) { /* If performant mode and MSI-X, use multiple reply queues */ for (i = 0; i < h->msix_vector; i++) { + sprintf(h->intrname[i], "%s-msix%d", h->devname, i); rc = request_irq(h->intr[i], msixhandler, - 0, h->devname, + 0, h->intrname[i], &h->q[i]); if (rc) { int j; @@ -6524,18 +7952,30 @@ static int hpsa_request_irqs(struct ctlr_info *h, } else { /* Use single reply pool */ if (h->msix_vector > 0 || h->msi_vector) { + if (h->msix_vector) + sprintf(h->intrname[h->intr_mode], + "%s-msix", h->devname); + else + sprintf(h->intrname[h->intr_mode], + "%s-msi", h->devname); rc = request_irq(h->intr[h->intr_mode], - msixhandler, 0, h->devname, + msixhandler, 0, + h->intrname[h->intr_mode], &h->q[h->intr_mode]); } else { + sprintf(h->intrname[h->intr_mode], + "%s-intx", h->devname); rc = request_irq(h->intr[h->intr_mode], - intxhandler, IRQF_SHARED, h->devname, + intxhandler, IRQF_SHARED, + h->intrname[h->intr_mode], &h->q[h->intr_mode]); } + irq_set_affinity_hint(h->intr[h->intr_mode], NULL); } if (rc) { - dev_err(&h->pdev->dev, "unable to get irq %d for %s\n", + dev_err(&h->pdev->dev, "failed to get irq %d for %s\n", h->intr[h->intr_mode], h->devname); + hpsa_free_irqs(h); return -ENODEV; } return 0; @@ -6543,42 +7983,27 @@ static int hpsa_request_irqs(struct ctlr_info *h, static int hpsa_kdump_soft_reset(struct ctlr_info *h) { - if (hpsa_send_host_reset(h, RAID_CTLR_LUNID, - HPSA_RESET_TYPE_CONTROLLER)) { - dev_warn(&h->pdev->dev, "Resetting array controller failed.\n"); - return -EIO; - } + int rc; + hpsa_send_host_reset(h, RAID_CTLR_LUNID, HPSA_RESET_TYPE_CONTROLLER); dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n"); - if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) { + rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY); + if (rc) { dev_warn(&h->pdev->dev, "Soft reset had no effect.\n"); - return -1; + return rc; } dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n"); - if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) { + rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY); + if (rc) { dev_warn(&h->pdev->dev, "Board failed to become ready " "after soft reset.\n"); - return -1; + return rc; } return 0; } -static void hpsa_free_irqs_and_disable_msix(struct ctlr_info *h) -{ - hpsa_free_irqs(h); -#ifdef CONFIG_PCI_MSI - if (h->msix_vector) { - if (h->pdev->msix_enabled) - pci_disable_msix(h->pdev); - } else if (h->msi_vector) { - if (h->pdev->msi_enabled) - pci_disable_msi(h->pdev); - } -#endif /* CONFIG_PCI_MSI */ -} - static void hpsa_free_reply_queues(struct ctlr_info *h) { int i; @@ -6586,30 +8011,36 @@ static void hpsa_free_reply_queues(struct ctlr_info *h) for (i = 0; i < h->nreply_queues; i++) { if (!h->reply_queue[i].head) continue; - pci_free_consistent(h->pdev, h->reply_queue_size, - h->reply_queue[i].head, h->reply_queue[i].busaddr); + pci_free_consistent(h->pdev, + h->reply_queue_size, + h->reply_queue[i].head, + h->reply_queue[i].busaddr); h->reply_queue[i].head = NULL; h->reply_queue[i].busaddr = 0; } + h->reply_queue_size = 0; } static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h) { - hpsa_free_irqs_and_disable_msix(h); - hpsa_free_sg_chain_blocks(h); - hpsa_free_cmd_pool(h); - kfree(h->ioaccel1_blockFetchTable); - kfree(h->blockFetchTable); - hpsa_free_reply_queues(h); - if (h->vaddr) - iounmap(h->vaddr); - if (h->transtable) - iounmap(h->transtable); - if (h->cfgtable) - iounmap(h->cfgtable); - pci_disable_device(h->pdev); - pci_release_regions(h->pdev); - kfree(h); + hpsa_free_performant_mode(h); /* init_one 7 */ + hpsa_free_sg_chain_blocks(h); /* init_one 6 */ + hpsa_free_cmd_pool(h); /* init_one 5 */ + hpsa_free_irqs(h); /* init_one 4 */ + scsi_host_put(h->scsi_host); /* init_one 3 */ + h->scsi_host = NULL; /* init_one 3 */ + hpsa_free_pci_init(h); /* init_one 2_5 */ + free_percpu(h->lockup_detected); /* init_one 2 */ + h->lockup_detected = NULL; /* init_one 2 */ + if (h->resubmit_wq) { + destroy_workqueue(h->resubmit_wq); /* init_one 1 */ + h->resubmit_wq = NULL; + } + if (h->rescan_ctlr_wq) { + destroy_workqueue(h->rescan_ctlr_wq); + h->rescan_ctlr_wq = NULL; + } + kfree(h); /* init_one 1 */ } /* Called when controller lockup detected. */ @@ -6617,17 +8048,22 @@ static void fail_all_outstanding_cmds(struct ctlr_info *h) { int i, refcount; struct CommandList *c; + int failcount = 0; flush_workqueue(h->resubmit_wq); /* ensure all cmds are fully built */ for (i = 0; i < h->nr_cmds; i++) { c = h->cmd_pool + i; refcount = atomic_inc_return(&c->refcount); if (refcount > 1) { - c->err_info->CommandStatus = CMD_HARDWARE_ERR; + c->err_info->CommandStatus = CMD_CTLR_LOCKUP; finish_cmd(c); + atomic_dec(&h->commands_outstanding); + failcount++; } cmd_free(h, c); } + dev_warn(&h->pdev->dev, + "failed %d commands in fail_all\n", failcount); } static void set_lockup_detected_for_all_cpus(struct ctlr_info *h, u32 value) @@ -6653,18 +8089,19 @@ static void controller_lockup_detected(struct ctlr_info *h) if (!lockup_detected) { /* no heartbeat, but controller gave us a zero. */ dev_warn(&h->pdev->dev, - "lockup detected but scratchpad register is zero\n"); + "lockup detected after %d but scratchpad register is zero\n", + h->heartbeat_sample_interval / HZ); lockup_detected = 0xffffffff; } set_lockup_detected_for_all_cpus(h, lockup_detected); spin_unlock_irqrestore(&h->lock, flags); - dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n", - lockup_detected); + dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x after %d\n", + lockup_detected, h->heartbeat_sample_interval / HZ); pci_disable_device(h->pdev); fail_all_outstanding_cmds(h); } -static void detect_controller_lockup(struct ctlr_info *h) +static int detect_controller_lockup(struct ctlr_info *h) { u64 now; u32 heartbeat; @@ -6674,7 +8111,7 @@ static void detect_controller_lockup(struct ctlr_info *h) /* If we've received an interrupt recently, we're ok. */ if (time_after64(h->last_intr_timestamp + (h->heartbeat_sample_interval), now)) - return; + return false; /* * If we've already checked the heartbeat recently, we're ok. @@ -6683,7 +8120,7 @@ static void detect_controller_lockup(struct ctlr_info *h) */ if (time_after64(h->last_heartbeat_timestamp + (h->heartbeat_sample_interval), now)) - return; + return false; /* If heartbeat has not changed since we last looked, we're not ok. */ spin_lock_irqsave(&h->lock, flags); @@ -6691,12 +8128,13 @@ static void detect_controller_lockup(struct ctlr_info *h) spin_unlock_irqrestore(&h->lock, flags); if (h->last_heartbeat == heartbeat) { controller_lockup_detected(h); - return; + return true; } /* We're ok. */ h->last_heartbeat = heartbeat; h->last_heartbeat_timestamp = now; + return false; } static void hpsa_ack_ctlr_events(struct ctlr_info *h) @@ -6752,6 +8190,11 @@ static void hpsa_ack_ctlr_events(struct ctlr_info *h) */ static int hpsa_ctlr_needs_rescan(struct ctlr_info *h) { + if (h->drv_req_rescan) { + h->drv_req_rescan = 0; + return 1; + } + if (!(h->fw_support & MISC_FW_EVENT_NOTIFY)) return 0; @@ -6785,6 +8228,41 @@ static int hpsa_offline_devices_ready(struct ctlr_info *h) return 0; } +static int hpsa_luns_changed(struct ctlr_info *h) +{ + int rc = 1; /* assume there are changes */ + struct ReportLUNdata *logdev = NULL; + + /* if we can't find out if lun data has changed, + * assume that it has. + */ + + if (!h->lastlogicals) + goto out; + + logdev = kzalloc(sizeof(*logdev), GFP_KERNEL); + if (!logdev) { + dev_warn(&h->pdev->dev, + "Out of memory, can't track lun changes.\n"); + goto out; + } + if (hpsa_scsi_do_report_luns(h, 1, logdev, sizeof(*logdev), 0)) { + dev_warn(&h->pdev->dev, + "report luns failed, can't track lun changes.\n"); + goto out; + } + if (memcmp(logdev, h->lastlogicals, sizeof(*logdev))) { + dev_info(&h->pdev->dev, + "Lun changes detected.\n"); + memcpy(h->lastlogicals, logdev, sizeof(*logdev)); + goto out; + } else + rc = 0; /* no changes detected. */ +out: + kfree(logdev); + return rc; +} + static void hpsa_rescan_ctlr_worker(struct work_struct *work) { unsigned long flags; @@ -6800,6 +8278,19 @@ static void hpsa_rescan_ctlr_worker(struct work_struct *work) hpsa_ack_ctlr_events(h); hpsa_scan_start(h->scsi_host); scsi_host_put(h->scsi_host); + } else if (h->discovery_polling) { + hpsa_disable_rld_caching(h); + if (hpsa_luns_changed(h)) { + struct Scsi_Host *sh = NULL; + + dev_info(&h->pdev->dev, + "driver discovery polling rescan.\n"); + sh = scsi_host_get(h->scsi_host); + if (sh != NULL) { + hpsa_scan_start(sh); + scsi_host_put(sh); + } + } } spin_lock_irqsave(&h->lock, flags); if (!h->remove_in_progress) @@ -6843,11 +8334,18 @@ static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct ctlr_info *h; int try_soft_reset = 0; unsigned long flags; + u32 board_id; if (number_of_controllers == 0) printk(KERN_INFO DRIVER_NAME "\n"); - rc = hpsa_init_reset_devices(pdev); + rc = hpsa_lookup_board_id(pdev, &board_id); + if (rc < 0) { + dev_warn(&pdev->dev, "Board ID not found\n"); + return rc; + } + + rc = hpsa_init_reset_devices(pdev, board_id); if (rc) { if (rc != -ENOTSUPP) return rc; @@ -6868,42 +8366,41 @@ reinit_after_soft_reset: */ BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT); h = kzalloc(sizeof(*h), GFP_KERNEL); - if (!h) + if (!h) { + dev_err(&pdev->dev, "Failed to allocate controller head\n"); return -ENOMEM; + } h->pdev = pdev; + h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT; INIT_LIST_HEAD(&h->offline_device_list); spin_lock_init(&h->lock); spin_lock_init(&h->offline_device_lock); spin_lock_init(&h->scan_lock); atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS); - - h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan"); - if (!h->rescan_ctlr_wq) { - rc = -ENOMEM; - goto clean1; - } - - h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit"); - if (!h->resubmit_wq) { - rc = -ENOMEM; - goto clean1; - } + atomic_set(&h->abort_cmds_available, HPSA_CMDS_RESERVED_FOR_ABORTS); /* Allocate and clear per-cpu variable lockup_detected */ h->lockup_detected = alloc_percpu(u32); if (!h->lockup_detected) { + dev_err(&h->pdev->dev, "Failed to allocate lockup detector\n"); rc = -ENOMEM; - goto clean1; + goto clean1; /* aer/h */ } set_lockup_detected_for_all_cpus(h, 0); rc = hpsa_pci_init(h); - if (rc != 0) - goto clean1; + if (rc) + goto clean2; /* lu, aer/h */ + + /* relies on h-> settings made by hpsa_pci_init, including + * interrupt_mode h->intr */ + rc = hpsa_scsi_host_alloc(h); + if (rc) + goto clean2_5; /* pci, lu, aer/h */ - sprintf(h->devname, HPSA "%d", number_of_controllers); + sprintf(h->devname, HPSA "%d", h->scsi_host->host_no); h->ctlr = number_of_controllers; number_of_controllers++; @@ -6917,34 +8414,56 @@ reinit_after_soft_reset: dac = 0; } else { dev_err(&pdev->dev, "no suitable DMA available\n"); - goto clean1; + goto clean3; /* shost, pci, lu, aer/h */ } } /* make sure the board interrupts are off */ h->access.set_intr_mask(h, HPSA_INTR_OFF); - if (hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx)) - goto clean2; - dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n", - h->devname, pdev->device, - h->intr[h->intr_mode], dac ? "" : " not"); - rc = hpsa_allocate_cmd_pool(h); + rc = hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx); if (rc) - goto clean2_and_free_irqs; - if (hpsa_allocate_sg_chain_blocks(h)) - goto clean4; + goto clean3; /* shost, pci, lu, aer/h */ + rc = hpsa_alloc_cmd_pool(h); + if (rc) + goto clean4; /* irq, shost, pci, lu, aer/h */ + rc = hpsa_alloc_sg_chain_blocks(h); + if (rc) + goto clean5; /* cmd, irq, shost, pci, lu, aer/h */ init_waitqueue_head(&h->scan_wait_queue); + init_waitqueue_head(&h->abort_cmd_wait_queue); + init_waitqueue_head(&h->event_sync_wait_queue); + mutex_init(&h->reset_mutex); h->scan_finished = 1; /* no scan currently in progress */ pci_set_drvdata(pdev, h); h->ndevices = 0; - h->hba_mode_enabled = 0; - h->scsi_host = NULL; + spin_lock_init(&h->devlock); - hpsa_put_ctlr_into_performant_mode(h); + rc = hpsa_put_ctlr_into_performant_mode(h); + if (rc) + goto clean6; /* sg, cmd, irq, shost, pci, lu, aer/h */ + + /* hook into SCSI subsystem */ + rc = hpsa_scsi_add_host(h); + if (rc) + goto clean7; /* perf, sg, cmd, irq, shost, pci, lu, aer/h */ - /* At this point, the controller is ready to take commands. + /* create the resubmit workqueue */ + h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan"); + if (!h->rescan_ctlr_wq) { + rc = -ENOMEM; + goto clean7; + } + + h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit"); + if (!h->resubmit_wq) { + rc = -ENOMEM; + goto clean7; /* aer/h */ + } + + /* + * At this point, the controller is ready to take commands. * Now, if reset_devices and the hard reset didn't work, try * the soft reset and see if that works. */ @@ -6966,13 +8485,24 @@ reinit_after_soft_reset: if (rc) { dev_warn(&h->pdev->dev, "Failed to request_irq after soft reset.\n"); - goto clean4; + /* + * cannot goto clean7 or free_irqs will be called + * again. Instead, do its work + */ + hpsa_free_performant_mode(h); /* clean7 */ + hpsa_free_sg_chain_blocks(h); /* clean6 */ + hpsa_free_cmd_pool(h); /* clean5 */ + /* + * skip hpsa_free_irqs(h) clean4 since that + * was just called before request_irqs failed + */ + goto clean3; } rc = hpsa_kdump_soft_reset(h); if (rc) /* Neither hard nor soft reset worked, we're hosed. */ - goto clean4; + goto clean7; dev_info(&h->pdev->dev, "Board READY.\n"); dev_info(&h->pdev->dev, @@ -6993,21 +8523,27 @@ reinit_after_soft_reset: hpsa_undo_allocations_after_kdump_soft_reset(h); try_soft_reset = 0; if (rc) - /* don't go to clean4, we already unallocated */ + /* don't goto clean, we already unallocated */ return -ENODEV; goto reinit_after_soft_reset; } - /* Enable Accelerated IO path at driver layer */ - h->acciopath_status = 1; + /* Enable Accelerated IO path at driver layer */ + h->acciopath_status = 1; + /* Disable discovery polling.*/ + h->discovery_polling = 0; /* Turn the interrupts on so we can service requests */ h->access.set_intr_mask(h, HPSA_INTR_ON); hpsa_hba_inquiry(h); - hpsa_register_scsi(h); /* hook ourselves into SCSI subsystem */ + + h->lastlogicals = kzalloc(sizeof(*(h->lastlogicals)), GFP_KERNEL); + if (!h->lastlogicals) + dev_info(&h->pdev->dev, + "Can't track change to report lun data\n"); /* Monitor the controller for firmware lockups */ h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL; @@ -7019,19 +8555,34 @@ reinit_after_soft_reset: h->heartbeat_sample_interval); return 0; -clean4: +clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */ + hpsa_free_performant_mode(h); + h->access.set_intr_mask(h, HPSA_INTR_OFF); +clean6: /* sg, cmd, irq, pci, lockup, wq/aer/h */ hpsa_free_sg_chain_blocks(h); +clean5: /* cmd, irq, shost, pci, lu, aer/h */ hpsa_free_cmd_pool(h); -clean2_and_free_irqs: +clean4: /* irq, shost, pci, lu, aer/h */ hpsa_free_irqs(h); -clean2: -clean1: - if (h->resubmit_wq) +clean3: /* shost, pci, lu, aer/h */ + scsi_host_put(h->scsi_host); + h->scsi_host = NULL; +clean2_5: /* pci, lu, aer/h */ + hpsa_free_pci_init(h); +clean2: /* lu, aer/h */ + if (h->lockup_detected) { + free_percpu(h->lockup_detected); + h->lockup_detected = NULL; + } +clean1: /* wq/aer/h */ + if (h->resubmit_wq) { destroy_workqueue(h->resubmit_wq); - if (h->rescan_ctlr_wq) + h->resubmit_wq = NULL; + } + if (h->rescan_ctlr_wq) { destroy_workqueue(h->rescan_ctlr_wq); - if (h->lockup_detected) - free_percpu(h->lockup_detected); + h->rescan_ctlr_wq = NULL; + } kfree(h); return rc; } @@ -7040,8 +8591,8 @@ static void hpsa_flush_cache(struct ctlr_info *h) { char *flush_buf; struct CommandList *c; + int rc; - /* Don't bother trying to flush the cache if locked up */ if (unlikely(lockup_detected(h))) return; flush_buf = kzalloc(4, GFP_KERNEL); @@ -7049,24 +8600,88 @@ static void hpsa_flush_cache(struct ctlr_info *h) return; c = cmd_alloc(h); - if (!c) { - dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n"); - goto out_of_memory; - } + if (fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0, RAID_CTLR_LUNID, TYPE_CMD)) { goto out; } - hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_TODEVICE); + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_TODEVICE, NO_TIMEOUT); + if (rc) + goto out; if (c->err_info->CommandStatus != 0) out: dev_warn(&h->pdev->dev, "error flushing cache on controller\n"); cmd_free(h, c); -out_of_memory: kfree(flush_buf); } +/* Make controller gather fresh report lun data each time we + * send down a report luns request + */ +static void hpsa_disable_rld_caching(struct ctlr_info *h) +{ + u32 *options; + struct CommandList *c; + int rc; + + /* Don't bother trying to set diag options if locked up */ + if (unlikely(h->lockup_detected)) + return; + + options = kzalloc(sizeof(*options), GFP_KERNEL); + if (!options) { + dev_err(&h->pdev->dev, + "Error: failed to disable rld caching, during alloc.\n"); + return; + } + + c = cmd_alloc(h); + + /* first, get the current diag options settings */ + if (fill_cmd(c, BMIC_SENSE_DIAG_OPTIONS, h, options, 4, 0, + RAID_CTLR_LUNID, TYPE_CMD)) + goto errout; + + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if ((rc != 0) || (c->err_info->CommandStatus != 0)) + goto errout; + + /* Now, set the bit for disabling the RLD caching */ + *options |= HPSA_DIAG_OPTS_DISABLE_RLD_CACHING; + + if (fill_cmd(c, BMIC_SET_DIAG_OPTIONS, h, options, 4, 0, + RAID_CTLR_LUNID, TYPE_CMD)) + goto errout; + + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_TODEVICE, NO_TIMEOUT); + if ((rc != 0) || (c->err_info->CommandStatus != 0)) + goto errout; + + /* Now verify that it got set: */ + if (fill_cmd(c, BMIC_SENSE_DIAG_OPTIONS, h, options, 4, 0, + RAID_CTLR_LUNID, TYPE_CMD)) + goto errout; + + rc = hpsa_scsi_do_simple_cmd_with_retry(h, c, + PCI_DMA_FROMDEVICE, NO_TIMEOUT); + if ((rc != 0) || (c->err_info->CommandStatus != 0)) + goto errout; + + if (*options & HPSA_DIAG_OPTS_DISABLE_RLD_CACHING) + goto out; + +errout: + dev_err(&h->pdev->dev, + "Error: failed to disable report lun data caching.\n"); +out: + cmd_free(h, c); + kfree(options); +} + static void hpsa_shutdown(struct pci_dev *pdev) { struct ctlr_info *h; @@ -7078,15 +8693,18 @@ static void hpsa_shutdown(struct pci_dev *pdev) */ hpsa_flush_cache(h); h->access.set_intr_mask(h, HPSA_INTR_OFF); - hpsa_free_irqs_and_disable_msix(h); + hpsa_free_irqs(h); /* init_one 4 */ + hpsa_disable_interrupt_mode(h); /* pci_init 2 */ } static void hpsa_free_device_info(struct ctlr_info *h) { int i; - for (i = 0; i < h->ndevices; i++) + for (i = 0; i < h->ndevices; i++) { kfree(h->dev[i]); + h->dev[i] = NULL; + } } static void hpsa_remove_one(struct pci_dev *pdev) @@ -7108,29 +8726,44 @@ static void hpsa_remove_one(struct pci_dev *pdev) cancel_delayed_work_sync(&h->rescan_ctlr_work); destroy_workqueue(h->rescan_ctlr_wq); destroy_workqueue(h->resubmit_wq); - hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */ + + /* + * Call before disabling interrupts. + * scsi_remove_host can trigger I/O operations especially + * when multipath is enabled. There can be SYNCHRONIZE CACHE + * operations which cannot complete and will hang the system. + */ + if (h->scsi_host) + scsi_remove_host(h->scsi_host); /* init_one 8 */ + /* includes hpsa_free_irqs - init_one 4 */ + /* includes hpsa_disable_interrupt_mode - pci_init 2 */ hpsa_shutdown(pdev); - iounmap(h->vaddr); - iounmap(h->transtable); - iounmap(h->cfgtable); - hpsa_free_device_info(h); - hpsa_free_sg_chain_blocks(h); - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(struct CommandList), - h->cmd_pool, h->cmd_pool_dhandle); - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(struct ErrorInfo), - h->errinfo_pool, h->errinfo_pool_dhandle); - hpsa_free_reply_queues(h); - kfree(h->cmd_pool_bits); - kfree(h->blockFetchTable); - kfree(h->ioaccel1_blockFetchTable); - kfree(h->ioaccel2_blockFetchTable); - kfree(h->hba_inquiry_data); - pci_disable_device(pdev); - pci_release_regions(pdev); - free_percpu(h->lockup_detected); - kfree(h); + + hpsa_free_device_info(h); /* scan */ + + kfree(h->hba_inquiry_data); /* init_one 10 */ + h->hba_inquiry_data = NULL; /* init_one 10 */ + hpsa_free_ioaccel2_sg_chain_blocks(h); + hpsa_free_performant_mode(h); /* init_one 7 */ + hpsa_free_sg_chain_blocks(h); /* init_one 6 */ + hpsa_free_cmd_pool(h); /* init_one 5 */ + kfree(h->lastlogicals); + + /* hpsa_free_irqs already called via hpsa_shutdown init_one 4 */ + + scsi_host_put(h->scsi_host); /* init_one 3 */ + h->scsi_host = NULL; /* init_one 3 */ + + /* includes hpsa_disable_interrupt_mode - pci_init 2 */ + hpsa_free_pci_init(h); /* init_one 2.5 */ + + free_percpu(h->lockup_detected); /* init_one 2 */ + h->lockup_detected = NULL; /* init_one 2 */ + /* (void) pci_disable_pcie_error_reporting(pdev); */ /* init_one 1 */ + + hpsa_delete_sas_host(h); + + kfree(h); /* init_one 1 */ } static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev, @@ -7188,7 +8821,10 @@ static void calc_bucket_map(int bucket[], int num_buckets, } } -/* return -ENODEV or other reason on error, 0 on success */ +/* + * return -ENODEV on err, 0 on success (or no action) + * allocates numerous items that must be freed later + */ static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support) { int i; @@ -7370,7 +9006,23 @@ static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support) return 0; } -static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h) +/* Free ioaccel1 mode command blocks and block fetch table */ +static void hpsa_free_ioaccel1_cmd_and_bft(struct ctlr_info *h) +{ + if (h->ioaccel_cmd_pool) { + pci_free_consistent(h->pdev, + h->nr_cmds * sizeof(*h->ioaccel_cmd_pool), + h->ioaccel_cmd_pool, + h->ioaccel_cmd_pool_dhandle); + h->ioaccel_cmd_pool = NULL; + h->ioaccel_cmd_pool_dhandle = 0; + } + kfree(h->ioaccel1_blockFetchTable); + h->ioaccel1_blockFetchTable = NULL; +} + +/* Allocate ioaccel1 mode command blocks and block fetch table */ +static int hpsa_alloc_ioaccel1_cmd_and_bft(struct ctlr_info *h) { h->ioaccel_maxsg = readl(&(h->cfgtable->io_accel_max_embedded_sg_count)); @@ -7401,16 +9053,32 @@ static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h) return 0; clean_up: - if (h->ioaccel_cmd_pool) + hpsa_free_ioaccel1_cmd_and_bft(h); + return -ENOMEM; +} + +/* Free ioaccel2 mode command blocks and block fetch table */ +static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h) +{ + hpsa_free_ioaccel2_sg_chain_blocks(h); + + if (h->ioaccel2_cmd_pool) { pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(*h->ioaccel_cmd_pool), - h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle); - kfree(h->ioaccel1_blockFetchTable); - return 1; + h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool), + h->ioaccel2_cmd_pool, + h->ioaccel2_cmd_pool_dhandle); + h->ioaccel2_cmd_pool = NULL; + h->ioaccel2_cmd_pool_dhandle = 0; + } + kfree(h->ioaccel2_blockFetchTable); + h->ioaccel2_blockFetchTable = NULL; } -static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h) +/* Allocate ioaccel2 mode command blocks and block fetch table */ +static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h) { + int rc; + /* Allocate ioaccel2 mode command blocks and block fetch table */ h->ioaccel_maxsg = @@ -7430,7 +9098,13 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h) sizeof(u32)), GFP_KERNEL); if ((h->ioaccel2_cmd_pool == NULL) || - (h->ioaccel2_blockFetchTable == NULL)) + (h->ioaccel2_blockFetchTable == NULL)) { + rc = -ENOMEM; + goto clean_up; + } + + rc = hpsa_allocate_ioaccel2_sg_chain_blocks(h); + if (rc) goto clean_up; memset(h->ioaccel2_cmd_pool, 0, @@ -7438,41 +9112,50 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h) return 0; clean_up: - if (h->ioaccel2_cmd_pool) - pci_free_consistent(h->pdev, - h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool), - h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle); - kfree(h->ioaccel2_blockFetchTable); - return 1; + hpsa_free_ioaccel2_cmd_and_bft(h); + return rc; +} + +/* Free items allocated by hpsa_put_ctlr_into_performant_mode */ +static void hpsa_free_performant_mode(struct ctlr_info *h) +{ + kfree(h->blockFetchTable); + h->blockFetchTable = NULL; + hpsa_free_reply_queues(h); + hpsa_free_ioaccel1_cmd_and_bft(h); + hpsa_free_ioaccel2_cmd_and_bft(h); } -static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) +/* return -ENODEV on error, 0 on success (or no action) + * allocates numerous items that must be freed later + */ +static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) { u32 trans_support; unsigned long transMethod = CFGTBL_Trans_Performant | CFGTBL_Trans_use_short_tags; - int i; + int i, rc; if (hpsa_simple_mode) - return; + return 0; trans_support = readl(&(h->cfgtable->TransportSupport)); if (!(trans_support & PERFORMANT_MODE)) - return; + return 0; /* Check for I/O accelerator mode support */ if (trans_support & CFGTBL_Trans_io_accel1) { transMethod |= CFGTBL_Trans_io_accel1 | CFGTBL_Trans_enable_directed_msix; - if (hpsa_alloc_ioaccel_cmd_and_bft(h)) - goto clean_up; - } else { - if (trans_support & CFGTBL_Trans_io_accel2) { - transMethod |= CFGTBL_Trans_io_accel2 | + rc = hpsa_alloc_ioaccel1_cmd_and_bft(h); + if (rc) + return rc; + } else if (trans_support & CFGTBL_Trans_io_accel2) { + transMethod |= CFGTBL_Trans_io_accel2 | CFGTBL_Trans_enable_directed_msix; - if (ioaccel2_alloc_cmds_and_bft(h)) - goto clean_up; - } + rc = hpsa_alloc_ioaccel2_cmd_and_bft(h); + if (rc) + return rc; } h->nreply_queues = h->msix_vector > 0 ? h->msix_vector : 1; @@ -7484,8 +9167,10 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) h->reply_queue[i].head = pci_alloc_consistent(h->pdev, h->reply_queue_size, &(h->reply_queue[i].busaddr)); - if (!h->reply_queue[i].head) - goto clean_up; + if (!h->reply_queue[i].head) { + rc = -ENOMEM; + goto clean1; /* rq, ioaccel */ + } h->reply_queue[i].size = h->max_commands; h->reply_queue[i].wraparound = 1; /* spec: init to 1 */ h->reply_queue[i].current_entry = 0; @@ -7494,15 +9179,24 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h) /* Need a block fetch table for performant mode */ h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) * sizeof(u32)), GFP_KERNEL); - if (!h->blockFetchTable) - goto clean_up; + if (!h->blockFetchTable) { + rc = -ENOMEM; + goto clean1; /* rq, ioaccel */ + } - hpsa_enter_performant_mode(h, trans_support); - return; + rc = hpsa_enter_performant_mode(h, trans_support); + if (rc) + goto clean2; /* bft, rq, ioaccel */ + return 0; -clean_up: - hpsa_free_reply_queues(h); +clean2: /* bft, rq, ioaccel */ kfree(h->blockFetchTable); + h->blockFetchTable = NULL; +clean1: /* rq, ioaccel */ + hpsa_free_reply_queues(h); + hpsa_free_ioaccel1_cmd_and_bft(h); + hpsa_free_ioaccel2_cmd_and_bft(h); + return rc; } static int is_accelerated_cmd(struct CommandList *c) @@ -7531,18 +9225,369 @@ static void hpsa_drain_accel_commands(struct ctlr_info *h) } while (1); } +static struct hpsa_sas_phy *hpsa_alloc_sas_phy( + struct hpsa_sas_port *hpsa_sas_port) +{ + struct hpsa_sas_phy *hpsa_sas_phy; + struct sas_phy *phy; + + hpsa_sas_phy = kzalloc(sizeof(*hpsa_sas_phy), GFP_KERNEL); + if (!hpsa_sas_phy) + return NULL; + + phy = sas_phy_alloc(hpsa_sas_port->parent_node->parent_dev, + hpsa_sas_port->next_phy_index); + if (!phy) { + kfree(hpsa_sas_phy); + return NULL; + } + + hpsa_sas_port->next_phy_index++; + hpsa_sas_phy->phy = phy; + hpsa_sas_phy->parent_port = hpsa_sas_port; + + return hpsa_sas_phy; +} + +static void hpsa_free_sas_phy(struct hpsa_sas_phy *hpsa_sas_phy) +{ + struct sas_phy *phy = hpsa_sas_phy->phy; + + sas_port_delete_phy(hpsa_sas_phy->parent_port->port, phy); + sas_phy_free(phy); + if (hpsa_sas_phy->added_to_port) + list_del(&hpsa_sas_phy->phy_list_entry); + kfree(hpsa_sas_phy); +} + +static int hpsa_sas_port_add_phy(struct hpsa_sas_phy *hpsa_sas_phy) +{ + int rc; + struct hpsa_sas_port *hpsa_sas_port; + struct sas_phy *phy; + struct sas_identify *identify; + + hpsa_sas_port = hpsa_sas_phy->parent_port; + phy = hpsa_sas_phy->phy; + + identify = &phy->identify; + memset(identify, 0, sizeof(*identify)); + identify->sas_address = hpsa_sas_port->sas_address; + identify->device_type = SAS_END_DEVICE; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; + phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; + phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; + + rc = sas_phy_add(hpsa_sas_phy->phy); + if (rc) + return rc; + + sas_port_add_phy(hpsa_sas_port->port, hpsa_sas_phy->phy); + list_add_tail(&hpsa_sas_phy->phy_list_entry, + &hpsa_sas_port->phy_list_head); + hpsa_sas_phy->added_to_port = true; + + return 0; +} + +static int + hpsa_sas_port_add_rphy(struct hpsa_sas_port *hpsa_sas_port, + struct sas_rphy *rphy) +{ + struct sas_identify *identify; + + identify = &rphy->identify; + identify->sas_address = hpsa_sas_port->sas_address; + identify->initiator_port_protocols = SAS_PROTOCOL_STP; + identify->target_port_protocols = SAS_PROTOCOL_STP; + + return sas_rphy_add(rphy); +} + +static struct hpsa_sas_port + *hpsa_alloc_sas_port(struct hpsa_sas_node *hpsa_sas_node, + u64 sas_address) +{ + int rc; + struct hpsa_sas_port *hpsa_sas_port; + struct sas_port *port; + + hpsa_sas_port = kzalloc(sizeof(*hpsa_sas_port), GFP_KERNEL); + if (!hpsa_sas_port) + return NULL; + + INIT_LIST_HEAD(&hpsa_sas_port->phy_list_head); + hpsa_sas_port->parent_node = hpsa_sas_node; + + port = sas_port_alloc_num(hpsa_sas_node->parent_dev); + if (!port) + goto free_hpsa_port; + + rc = sas_port_add(port); + if (rc) + goto free_sas_port; + + hpsa_sas_port->port = port; + hpsa_sas_port->sas_address = sas_address; + list_add_tail(&hpsa_sas_port->port_list_entry, + &hpsa_sas_node->port_list_head); + + return hpsa_sas_port; + +free_sas_port: + sas_port_free(port); +free_hpsa_port: + kfree(hpsa_sas_port); + + return NULL; +} + +static void hpsa_free_sas_port(struct hpsa_sas_port *hpsa_sas_port) +{ + struct hpsa_sas_phy *hpsa_sas_phy; + struct hpsa_sas_phy *next; + + list_for_each_entry_safe(hpsa_sas_phy, next, + &hpsa_sas_port->phy_list_head, phy_list_entry) + hpsa_free_sas_phy(hpsa_sas_phy); + + sas_port_delete(hpsa_sas_port->port); + list_del(&hpsa_sas_port->port_list_entry); + kfree(hpsa_sas_port); +} + +static struct hpsa_sas_node *hpsa_alloc_sas_node(struct device *parent_dev) +{ + struct hpsa_sas_node *hpsa_sas_node; + + hpsa_sas_node = kzalloc(sizeof(*hpsa_sas_node), GFP_KERNEL); + if (hpsa_sas_node) { + hpsa_sas_node->parent_dev = parent_dev; + INIT_LIST_HEAD(&hpsa_sas_node->port_list_head); + } + + return hpsa_sas_node; +} + +static void hpsa_free_sas_node(struct hpsa_sas_node *hpsa_sas_node) +{ + struct hpsa_sas_port *hpsa_sas_port; + struct hpsa_sas_port *next; + + if (!hpsa_sas_node) + return; + + list_for_each_entry_safe(hpsa_sas_port, next, + &hpsa_sas_node->port_list_head, port_list_entry) + hpsa_free_sas_port(hpsa_sas_port); + + kfree(hpsa_sas_node); +} + +static struct hpsa_scsi_dev_t + *hpsa_find_device_by_sas_rphy(struct ctlr_info *h, + struct sas_rphy *rphy) +{ + int i; + struct hpsa_scsi_dev_t *device; + + for (i = 0; i < h->ndevices; i++) { + device = h->dev[i]; + if (!device->sas_port) + continue; + if (device->sas_port->rphy == rphy) + return device; + } + + return NULL; +} + +static int hpsa_add_sas_host(struct ctlr_info *h) +{ + int rc; + struct device *parent_dev; + struct hpsa_sas_node *hpsa_sas_node; + struct hpsa_sas_port *hpsa_sas_port; + struct hpsa_sas_phy *hpsa_sas_phy; + + parent_dev = &h->scsi_host->shost_gendev; + + hpsa_sas_node = hpsa_alloc_sas_node(parent_dev); + if (!hpsa_sas_node) + return -ENOMEM; + + hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, h->sas_address); + if (!hpsa_sas_port) { + rc = -ENODEV; + goto free_sas_node; + } + + hpsa_sas_phy = hpsa_alloc_sas_phy(hpsa_sas_port); + if (!hpsa_sas_phy) { + rc = -ENODEV; + goto free_sas_port; + } + + rc = hpsa_sas_port_add_phy(hpsa_sas_phy); + if (rc) + goto free_sas_phy; + + h->sas_host = hpsa_sas_node; + + return 0; + +free_sas_phy: + hpsa_free_sas_phy(hpsa_sas_phy); +free_sas_port: + hpsa_free_sas_port(hpsa_sas_port); +free_sas_node: + hpsa_free_sas_node(hpsa_sas_node); + + return rc; +} + +static void hpsa_delete_sas_host(struct ctlr_info *h) +{ + hpsa_free_sas_node(h->sas_host); +} + +static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node, + struct hpsa_scsi_dev_t *device) +{ + int rc; + struct hpsa_sas_port *hpsa_sas_port; + struct sas_rphy *rphy; + + hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, device->sas_address); + if (!hpsa_sas_port) + return -ENOMEM; + + rphy = sas_end_device_alloc(hpsa_sas_port->port); + if (!rphy) { + rc = -ENODEV; + goto free_sas_port; + } + + hpsa_sas_port->rphy = rphy; + device->sas_port = hpsa_sas_port; + + rc = hpsa_sas_port_add_rphy(hpsa_sas_port, rphy); + if (rc) + goto free_sas_port; + + return 0; + +free_sas_port: + hpsa_free_sas_port(hpsa_sas_port); + device->sas_port = NULL; + + return rc; +} + +static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device) +{ + if (device->sas_port) { + hpsa_free_sas_port(device->sas_port); + device->sas_port = NULL; + } +} + +static int +hpsa_sas_get_linkerrors(struct sas_phy *phy) +{ + return 0; +} + +static int +hpsa_sas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) +{ + return 0; +} + +static int +hpsa_sas_get_bay_identifier(struct sas_rphy *rphy) +{ + return -ENXIO; +} + +static int +hpsa_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + return 0; +} + +static int +hpsa_sas_phy_enable(struct sas_phy *phy, int enable) +{ + return 0; +} + +static int +hpsa_sas_phy_setup(struct sas_phy *phy) +{ + return 0; +} + +static void +hpsa_sas_phy_release(struct sas_phy *phy) +{ +} + +static int +hpsa_sas_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) +{ + return -EINVAL; +} + +/* SMP = Serial Management Protocol */ +static int +hpsa_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, +struct request *req) +{ + return -EINVAL; +} + +static struct sas_function_template hpsa_sas_transport_functions = { + .get_linkerrors = hpsa_sas_get_linkerrors, + .get_enclosure_identifier = hpsa_sas_get_enclosure_identifier, + .get_bay_identifier = hpsa_sas_get_bay_identifier, + .phy_reset = hpsa_sas_phy_reset, + .phy_enable = hpsa_sas_phy_enable, + .phy_setup = hpsa_sas_phy_setup, + .phy_release = hpsa_sas_phy_release, + .set_phy_speed = hpsa_sas_phy_speed, + .smp_handler = hpsa_sas_smp_handler, +}; + /* * This is it. Register the PCI driver information for the cards we control * the OS will call our registered routines when it finds one of our cards. */ static int __init hpsa_init(void) { - return pci_register_driver(&hpsa_pci_driver); + int rc; + + hpsa_sas_transport_template = + sas_attach_transport(&hpsa_sas_transport_functions); + if (!hpsa_sas_transport_template) + return -ENODEV; + + rc = pci_register_driver(&hpsa_pci_driver); + + if (rc) + sas_release_transport(hpsa_sas_transport_template); + + return rc; } static void __exit hpsa_cleanup(void) { pci_unregister_driver(&hpsa_pci_driver); + sas_release_transport(hpsa_sas_transport_template); } static void __attribute__((unused)) verify_offsets(void) diff --git a/kernel/drivers/scsi/hpsa.h b/kernel/drivers/scsi/hpsa.h index 657713050..ae5beda1b 100644 --- a/kernel/drivers/scsi/hpsa.h +++ b/kernel/drivers/scsi/hpsa.h @@ -1,6 +1,7 @@ /* * Disk Array driver for HP Smart Array SAS controllers - * Copyright 2000, 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2014-2015 PMC-Sierra, Inc. + * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P. * * 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 @@ -11,11 +12,7 @@ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more details. * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Questions/Comments/Bugfixes to iss_storagedev@hp.com + * Questions/Comments/Bugfixes to storagedev@pmcs.com * */ #ifndef HPSA_H @@ -36,24 +33,58 @@ struct access_method { unsigned long (*command_completed)(struct ctlr_info *h, u8 q); }; +/* for SAS hosts and SAS expanders */ +struct hpsa_sas_node { + struct device *parent_dev; + struct list_head port_list_head; +}; + +struct hpsa_sas_port { + struct list_head port_list_entry; + u64 sas_address; + struct sas_port *port; + int next_phy_index; + struct list_head phy_list_head; + struct hpsa_sas_node *parent_node; + struct sas_rphy *rphy; +}; + +struct hpsa_sas_phy { + struct list_head phy_list_entry; + struct sas_phy *phy; + struct hpsa_sas_port *parent_port; + bool added_to_port; +}; + struct hpsa_scsi_dev_t { - int devtype; + unsigned int devtype; int bus, target, lun; /* as presented to the OS */ unsigned char scsi3addr[8]; /* as presented to the HW */ + u8 physical_device : 1; + u8 expose_device; #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0" unsigned char device_id[16]; /* from inquiry pg. 0x83 */ + u64 sas_address; unsigned char vendor[8]; /* bytes 8-15 of inquiry data */ unsigned char model[16]; /* bytes 16-31 of inquiry data */ unsigned char raid_level; /* from inquiry page 0xC1 */ unsigned char volume_offline; /* discovered via TUR or VPD */ u16 queue_depth; /* max queue_depth for this device */ + atomic_t reset_cmds_out; /* Count of commands to-be affected */ atomic_t ioaccel_cmds_out; /* Only used for physical devices * counts commands sent to physical * device via "ioaccel" path. */ u32 ioaccel_handle; + u8 active_path_index; + u8 path_map; + u8 bay; + u8 box[8]; + u16 phys_connector[8]; int offload_config; /* I/O accel RAID offload configured */ int offload_enabled; /* I/O accel RAID offload enabled */ + int offload_to_be_enabled; + int hba_ioaccel_enabled; int offload_to_mirror; /* Send next I/O accelerator RAID * offload request to mirror drive */ @@ -68,6 +99,10 @@ struct hpsa_scsi_dev_t { * devices in order to honor physical device queue depth limits. */ struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES]; + int nphysical_disks; + int supports_aborts; + struct hpsa_sas_port *sas_port; + int external; /* 1-from external array 0-not <0-unknown */ }; struct reply_queue_buffer { @@ -104,7 +139,6 @@ struct bmic_controller_parameters { u8 automatic_drive_slamming; u8 reserved1; u8 nvram_flags; -#define HBA_MODE_ENABLED_FLAG (1 << 3) u8 cache_nvram_flags; u8 drive_config_flags; u16 reserved2; @@ -125,6 +159,7 @@ struct ctlr_info { char *product_name; struct pci_dev *pdev; u32 board_id; + u64 sas_address; void __iomem *vaddr; unsigned long paddr; int nr_cmds; /* Number of commands allowed on this controller */ @@ -133,7 +168,6 @@ struct ctlr_info { struct CfgTable __iomem *cfgtable; int interrupts_enabled; int max_commands; - int last_allocation; atomic_t commands_outstanding; # define PERF_MODE_INT 0 # define DOORBELL_INT 1 @@ -144,7 +178,6 @@ struct ctlr_info { unsigned int msi_vector; int intr_mode; /* either PERF_MODE_INT or SIMPLE_MODE_INT */ struct access_method access; - char hba_mode_enabled; /* queue and queue Info */ unsigned int Qdepth; @@ -154,6 +187,7 @@ struct ctlr_info { u8 max_cmd_sg_entries; int chainsize; struct SGDescriptor **cmd_sg_list; + struct ioaccel2_sg_element **ioaccel2_cmd_sg_list; /* pointers to command and error info pool */ struct CommandList *cmd_pool; @@ -211,6 +245,7 @@ struct ctlr_info { int remove_in_progress; /* Address of h->q[x] is passed to intr handler to know which queue */ u8 q[MAX_REPLY_QUEUES]; + char intrname[MAX_REPLY_QUEUES][16]; /* "hpsa0-msix00" names */ u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */ #define HPSATMF_BITS_SUPPORTED (1 << 0) #define HPSATMF_PHYS_LUN_RESET (1 << 1) @@ -222,6 +257,7 @@ struct ctlr_info { #define HPSATMF_PHYS_QRY_TASK (1 << 7) #define HPSATMF_PHYS_QRY_TSET (1 << 8) #define HPSATMF_PHYS_QRY_ASYNC (1 << 9) +#define HPSATMF_IOACCEL_ENABLED (1 << 15) #define HPSATMF_MASK_SUPPORTED (1 << 16) #define HPSATMF_LOG_LUN_RESET (1 << 17) #define HPSATMF_LOG_NEX_RESET (1 << 18) @@ -250,9 +286,19 @@ struct ctlr_info { spinlock_t offline_device_lock; struct list_head offline_device_list; int acciopath_status; + int drv_req_rescan; int raid_offload_debug; + int discovery_polling; + struct ReportLUNdata *lastlogicals; + int needs_abort_tags_swizzled; struct workqueue_struct *resubmit_wq; struct workqueue_struct *rescan_ctlr_wq; + atomic_t abort_cmds_available; + wait_queue_head_t abort_cmd_wait_queue; + wait_queue_head_t event_sync_wait_queue; + struct mutex reset_mutex; + u8 reset_in_progress; + struct hpsa_sas_node *sas_host; }; struct offline_device_entry { @@ -266,6 +312,7 @@ struct offline_device_entry { #define HPSA_RESET_TYPE_BUS 0x01 #define HPSA_RESET_TYPE_TARGET 0x03 #define HPSA_RESET_TYPE_LUN 0x04 +#define HPSA_PHYS_TARGET_RESET 0x99 /* not defined by cciss spec */ #define HPSA_MSG_SEND_RETRY_LIMIT 10 #define HPSA_MSG_SEND_RETRY_INTERVAL_MSECS (10000) @@ -350,6 +397,11 @@ struct offline_device_entry { #define IOACCEL2_INBOUND_POSTQ_64_LOW 0xd0 #define IOACCEL2_INBOUND_POSTQ_64_HI 0xd4 +#define HPSA_PHYSICAL_DEVICE_BUS 0 +#define HPSA_RAID_VOLUME_BUS 1 +#define HPSA_EXTERNAL_RAID_VOLUME_BUS 2 +#define HPSA_HBA_BUS 3 + /* Send the command to the hardware */ diff --git a/kernel/drivers/scsi/hpsa_cmd.h b/kernel/drivers/scsi/hpsa_cmd.h index 3a621c74b..d92ef0d35 100644 --- a/kernel/drivers/scsi/hpsa_cmd.h +++ b/kernel/drivers/scsi/hpsa_cmd.h @@ -1,6 +1,7 @@ /* * Disk Array driver for HP Smart Array SAS controllers - * Copyright 2000, 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2014-2015 PMC-Sierra, Inc. + * Copyright 2000,2009-2015 Hewlett-Packard Development Company, L.P. * * 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 @@ -11,11 +12,7 @@ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more details. * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Questions/Comments/Bugfixes to iss_storagedev@hp.com + * Questions/Comments/Bugfixes to storagedev@pmcs.com * */ #ifndef HPSA_CMD_H @@ -42,8 +39,22 @@ #define CMD_UNSOLICITED_ABORT 0x000A #define CMD_TIMEOUT 0x000B #define CMD_UNABORTABLE 0x000C +#define CMD_TMF_STATUS 0x000D #define CMD_IOACCEL_DISABLED 0x000E +#define CMD_CTLR_LOCKUP 0xffff +/* Note: CMD_CTLR_LOCKUP is not a value defined by the CISS spec + * it is a value defined by the driver that commands can be marked + * with when a controller lockup has been detected by the driver + */ +/* TMF function status values */ +#define CISS_TMF_COMPLETE 0x00 +#define CISS_TMF_INVALID_FRAME 0x02 +#define CISS_TMF_NOT_SUPPORTED 0x04 +#define CISS_TMF_FAILED 0x05 +#define CISS_TMF_SUCCESS 0x08 +#define CISS_TMF_WRONG_LUN 0x09 +#define CISS_TMF_OVERLAPPED_TAG 0x0a /* Unit Attentions ASC's as defined for the MSA2012sa */ #define POWER_OR_RESET 0x29 @@ -153,6 +164,7 @@ /* Logical volume states */ #define HPSA_VPD_LV_STATUS_UNSUPPORTED 0xff #define HPSA_LV_OK 0x0 +#define HPSA_LV_NOT_AVAILABLE 0x0b #define HPSA_LV_UNDERGOING_ERASE 0x0F #define HPSA_LV_UNDERGOING_RPI 0x12 #define HPSA_LV_PENDING_RPI 0x13 @@ -240,6 +252,7 @@ struct ReportLUNdata { struct ext_report_lun_entry { u8 lunid[8]; +#define MASKED_DEVICE(x) ((x)[3] & 0xC0) #define GET_BMIC_BUS(lunid) ((lunid)[7] & 0x3F) #define GET_BMIC_LEVEL_TWO_TARGET(lunid) ((lunid)[6]) #define GET_BMIC_DRIVE_NUMBER(lunid) (((GET_BMIC_BUS((lunid)) - 1) << 8) + \ @@ -273,6 +286,11 @@ struct SenseSubsystem_info { #define BMIC_FLASH_FIRMWARE 0xF7 #define BMIC_SENSE_CONTROLLER_PARAMETERS 0x64 #define BMIC_IDENTIFY_PHYSICAL_DEVICE 0x15 +#define BMIC_IDENTIFY_CONTROLLER 0x11 +#define BMIC_SET_DIAG_OPTIONS 0xF4 +#define BMIC_SENSE_DIAG_OPTIONS 0xF5 +#define HPSA_DIAG_OPTS_DISABLE_RLD_CACHING 0x40000000 +#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66 /* Command List Structure */ union SCSI3Addr { @@ -379,6 +397,7 @@ struct ErrorInfo { #define CMD_SCSI 0x03 #define CMD_IOACCEL1 0x04 #define CMD_IOACCEL2 0x05 +#define IOACCEL2_TMF 0x06 #define DIRECT_LOOKUP_SHIFT 4 #define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1)) @@ -421,7 +440,10 @@ struct CommandList { * not used. */ struct hpsa_scsi_dev_t *phys_disk; - atomic_t refcount; /* Must be last to avoid memset in cmd_alloc */ + + int abort_pending; + struct hpsa_scsi_dev_t *reset_pending; + atomic_t refcount; /* Must be last to avoid memset in hpsa_cmd_init() */ } __aligned(COMMANDLIST_ALIGNMENT); /* Max S/G elements in I/O accelerator command */ @@ -515,6 +537,12 @@ struct io_accel2_scsi_response { #define IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL 0x28 #define IOACCEL2_STATUS_SR_TASK_COMP_ABORTED 0x40 #define IOACCEL2_STATUS_SR_IOACCEL_DISABLED 0x0E +#define IOACCEL2_STATUS_SR_IO_ERROR 0x01 +#define IOACCEL2_STATUS_SR_IO_ABORTED 0x02 +#define IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE 0x03 +#define IOACCEL2_STATUS_SR_INVALID_DEVICE 0x04 +#define IOACCEL2_STATUS_SR_UNDERRUN 0x51 +#define IOACCEL2_STATUS_SR_OVERRUN 0x75 u8 data_present; /* low 2 bits */ #define IOACCEL2_NO_DATAPRESENT 0x000 #define IOACCEL2_RESPONSE_DATAPRESENT 0x001 @@ -567,6 +595,7 @@ struct io_accel2_cmd { #define IOACCEL2_DIR_NO_DATA 0x00 #define IOACCEL2_DIR_DATA_IN 0x01 #define IOACCEL2_DIR_DATA_OUT 0x02 +#define IOACCEL2_TMF_ABORT 0x01 /* * SCSI Task Management Request format for Accelerator Mode 2 */ @@ -575,13 +604,13 @@ struct hpsa_tmf_struct { u8 reply_queue; /* Reply Queue ID */ u8 tmf; /* Task Management Function */ u8 reserved1; /* byte 3 Reserved */ - u32 it_nexus; /* SCSI I-T Nexus */ + __le32 it_nexus; /* SCSI I-T Nexus */ u8 lun_id[8]; /* LUN ID for TMF request */ __le64 tag; /* cciss tag associated w/ request */ __le64 abort_tag; /* cciss tag of SCSI cmd or TMF to abort */ __le64 error_ptr; /* Error Pointer */ __le32 error_len; /* Error Length */ -}; +} __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT); /* Configuration Table Structure */ struct HostWrite { @@ -658,6 +687,16 @@ struct hpsa_pci_info { u32 board_id; }; +struct bmic_identify_controller { + u8 configured_logical_drive_count; /* offset 0 */ + u8 pad1[153]; + __le16 extended_logical_unit_count; /* offset 154 */ + u8 pad2[136]; + u8 controller_mode; /* offset 292 */ + u8 pad3[32]; +}; + + struct bmic_identify_physical_device { u8 scsi_bus; /* SCSI Bus number on controller */ u8 scsi_id; /* SCSI ID on this bus */ @@ -790,5 +829,18 @@ struct bmic_identify_physical_device { u8 padding[112]; }; +struct bmic_sense_subsystem_info { + u8 primary_slot_number; + u8 reserved[3]; + u8 chasis_serial_number[32]; + u8 primary_world_wide_id[8]; + u8 primary_array_serial_number[32]; /* NULL terminated */ + u8 primary_cache_serial_number[32]; /* NULL terminated */ + u8 reserved_2[8]; + u8 secondary_array_serial_number[32]; + u8 secondary_cache_serial_number[32]; + u8 pad[332]; +}; + #pragma pack() #endif /* HPSA_CMD_H */ diff --git a/kernel/drivers/scsi/hptiop.c b/kernel/drivers/scsi/hptiop.c index e99521847..a83f705ed 100644 --- a/kernel/drivers/scsi/hptiop.c +++ b/kernel/drivers/scsi/hptiop.c @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2012 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved. * * 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 @@ -42,7 +42,7 @@ MODULE_DESCRIPTION("HighPoint RocketRAID 3xxx/4xxx Controller Driver"); static char driver_name[] = "hptiop"; static const char driver_name_long[] = "RocketRAID 3xxx/4xxx Controller driver"; -static const char driver_ver[] = "v1.8"; +static const char driver_ver[] = "v1.10.0"; static int iop_send_sync_msg(struct hptiop_hba *hba, u32 msg, u32 millisec); static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, @@ -764,9 +764,7 @@ static void hptiop_finish_scsi_req(struct hptiop_hba *hba, u32 tag, scsi_set_resid(scp, scsi_bufflen(scp) - le32_to_cpu(req->dataxfer_length)); scp->result = SAM_STAT_CHECK_CONDITION; - memcpy(scp->sense_buffer, &req->sg_list, - min_t(size_t, SCSI_SENSE_BUFFERSIZE, - le32_to_cpu(req->dataxfer_length))); + memcpy(scp->sense_buffer, &req->sg_list, SCSI_SENSE_BUFFERSIZE); goto skip_resid; break; @@ -1037,8 +1035,9 @@ static int hptiop_queuecommand_lck(struct scsi_cmnd *scp, scp->result = 0; - if (scp->device->channel || scp->device->lun || - scp->device->id > hba->max_devices) { + if (scp->device->channel || + (scp->device->id > hba->max_devices) || + ((scp->device->id == (hba->max_devices-1)) && scp->device->lun)) { scp->result = DID_BAD_TARGET << 16; free_req(hba, _req); goto cmd_done; @@ -1168,6 +1167,14 @@ static struct device_attribute *hptiop_attrs[] = { NULL }; +static int hptiop_slave_config(struct scsi_device *sdev) +{ + if (sdev->type == TYPE_TAPE) + blk_queue_max_hw_sectors(sdev->request_queue, 8192); + + return 0; +} + static struct scsi_host_template driver_template = { .module = THIS_MODULE, .name = driver_name, @@ -1179,6 +1186,7 @@ static struct scsi_host_template driver_template = { .use_clustering = ENABLE_CLUSTERING, .proc_name = driver_name, .shost_attrs = hptiop_attrs, + .slave_configure = hptiop_slave_config, .this_id = -1, .change_queue_depth = hptiop_adjust_disk_queue_depth, }; @@ -1323,6 +1331,7 @@ static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id) } hba = (struct hptiop_hba *)host->hostdata; + memset(hba, 0, sizeof(struct hptiop_hba)); hba->ops = iop_ops; hba->pcidev = pcidev; @@ -1336,7 +1345,7 @@ static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id) init_waitqueue_head(&hba->reset_wq); init_waitqueue_head(&hba->ioctl_wq); - host->max_lun = 1; + host->max_lun = 128; host->max_channel = 0; host->io_port = 0; host->n_io_port = 0; @@ -1428,34 +1437,33 @@ static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id) dprintk("req_size=%d, max_requests=%d\n", req_size, hba->max_requests); hba->req_size = req_size; - start_virt = dma_alloc_coherent(&pcidev->dev, - hba->req_size*hba->max_requests + 0x20, - &start_phy, GFP_KERNEL); + hba->req_list = NULL; - if (!start_virt) { - printk(KERN_ERR "scsi%d: fail to alloc request mem\n", - hba->host->host_no); - goto free_request_irq; - } + for (i = 0; i < hba->max_requests; i++) { + start_virt = dma_alloc_coherent(&pcidev->dev, + hba->req_size + 0x20, + &start_phy, GFP_KERNEL); + + if (!start_virt) { + printk(KERN_ERR "scsi%d: fail to alloc request mem\n", + hba->host->host_no); + goto free_request_mem; + } - hba->dma_coherent = start_virt; - hba->dma_coherent_handle = start_phy; + hba->dma_coherent[i] = start_virt; + hba->dma_coherent_handle[i] = start_phy; - if ((start_phy & 0x1f) != 0) { - offset = ((start_phy + 0x1f) & ~0x1f) - start_phy; - start_phy += offset; - start_virt += offset; - } + if ((start_phy & 0x1f) != 0) { + offset = ((start_phy + 0x1f) & ~0x1f) - start_phy; + start_phy += offset; + start_virt += offset; + } - hba->req_list = NULL; - for (i = 0; i < hba->max_requests; i++) { hba->reqs[i].next = NULL; hba->reqs[i].req_virt = start_virt; hba->reqs[i].req_shifted_phy = start_phy >> 5; hba->reqs[i].index = i; free_req(hba, &hba->reqs[i]); - start_virt = (char *)start_virt + hba->req_size; - start_phy = start_phy + hba->req_size; } /* Enable Interrupt and start background task */ @@ -1474,11 +1482,16 @@ static int hptiop_probe(struct pci_dev *pcidev, const struct pci_device_id *id) return 0; free_request_mem: - dma_free_coherent(&hba->pcidev->dev, - hba->req_size * hba->max_requests + 0x20, - hba->dma_coherent, hba->dma_coherent_handle); + for (i = 0; i < hba->max_requests; i++) { + if (hba->dma_coherent[i] && hba->dma_coherent_handle[i]) + dma_free_coherent(&hba->pcidev->dev, + hba->req_size + 0x20, + hba->dma_coherent[i], + hba->dma_coherent_handle[i]); + else + break; + } -free_request_irq: free_irq(hba->pcidev->irq, hba); unmap_pci_bar: @@ -1546,6 +1559,7 @@ static void hptiop_remove(struct pci_dev *pcidev) { struct Scsi_Host *host = pci_get_drvdata(pcidev); struct hptiop_hba *hba = (struct hptiop_hba *)host->hostdata; + u32 i; dprintk("scsi%d: hptiop_remove\n", hba->host->host_no); @@ -1555,10 +1569,15 @@ static void hptiop_remove(struct pci_dev *pcidev) free_irq(hba->pcidev->irq, hba); - dma_free_coherent(&hba->pcidev->dev, - hba->req_size * hba->max_requests + 0x20, - hba->dma_coherent, - hba->dma_coherent_handle); + for (i = 0; i < hba->max_requests; i++) { + if (hba->dma_coherent[i] && hba->dma_coherent_handle[i]) + dma_free_coherent(&hba->pcidev->dev, + hba->req_size + 0x20, + hba->dma_coherent[i], + hba->dma_coherent_handle[i]); + else + break; + } hba->ops->internal_memfree(hba); @@ -1653,6 +1672,14 @@ static struct pci_device_id hptiop_id_table[] = { { PCI_VDEVICE(TTI, 0x3020), (kernel_ulong_t)&hptiop_mv_ops }, { PCI_VDEVICE(TTI, 0x4520), (kernel_ulong_t)&hptiop_mvfrey_ops }, { PCI_VDEVICE(TTI, 0x4522), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3610), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3611), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3620), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3622), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3640), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3660), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3680), (kernel_ulong_t)&hptiop_mvfrey_ops }, + { PCI_VDEVICE(TTI, 0x3690), (kernel_ulong_t)&hptiop_mvfrey_ops }, {}, }; diff --git a/kernel/drivers/scsi/hptiop.h b/kernel/drivers/scsi/hptiop.h index 020619d60..4d1c51153 100644 --- a/kernel/drivers/scsi/hptiop.h +++ b/kernel/drivers/scsi/hptiop.h @@ -1,6 +1,6 @@ /* * HighPoint RR3xxx/4xxx controller driver for Linux - * Copyright (C) 2006-2012 HighPoint Technologies, Inc. All Rights Reserved. + * Copyright (C) 2006-2015 HighPoint Technologies, Inc. All Rights Reserved. * * 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 @@ -327,8 +327,8 @@ struct hptiop_hba { struct hptiop_request reqs[HPTIOP_MAX_REQUESTS]; /* used to free allocated dma area */ - void *dma_coherent; - dma_addr_t dma_coherent_handle; + void *dma_coherent[HPTIOP_MAX_REQUESTS]; + dma_addr_t dma_coherent_handle[HPTIOP_MAX_REQUESTS]; atomic_t reset_count; atomic_t resetting; diff --git a/kernel/drivers/scsi/ibmvscsi/ibmvfc.c b/kernel/drivers/scsi/ibmvscsi/ibmvfc.c index 057d27721..6aa317c30 100644 --- a/kernel/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/kernel/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3095,7 +3095,6 @@ static struct scsi_host_template driver_template = { .max_sectors = IBMVFC_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = ibmvfc_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; diff --git a/kernel/drivers/scsi/ibmvscsi/ibmvscsi.c b/kernel/drivers/scsi/ibmvscsi/ibmvscsi.c index acea5d6ee..adfef9db6 100644 --- a/kernel/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/kernel/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -106,9 +106,9 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(IBMVSCSI_VERSION); module_param_named(max_id, max_id, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_id, "Largest ID value for each channel"); +MODULE_PARM_DESC(max_id, "Largest ID value for each channel [Default=64]"); module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(max_channel, "Largest channel value"); +MODULE_PARM_DESC(max_channel, "Largest channel value [Default=3]"); module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds"); module_param_named(max_requests, max_requests, int, S_IRUGO); @@ -1053,7 +1053,7 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd, memset(srp_cmd, 0x00, SRP_MAX_IU_LEN); srp_cmd->opcode = SRP_CMD; memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb)); - srp_cmd->lun = cpu_to_be64(((u64)lun) << 48); + int_to_scsilun(lun, &srp_cmd->lun); if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) { if (!firmware_has_feature(FW_FEATURE_CMO)) @@ -1529,7 +1529,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) /* Set up an abort SRP command */ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); tsk_mgmt->opcode = SRP_TSK_MGMT; - tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48); + int_to_scsilun(lun, &tsk_mgmt->lun); tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK; tsk_mgmt->task_tag = (u64) found_evt; @@ -1652,7 +1652,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) /* Set up a lun reset SRP command */ memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); tsk_mgmt->opcode = SRP_TSK_MGMT; - tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48); + int_to_scsilun(lun, &tsk_mgmt->lun); tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET; evt->sync_srp = &srp_rsp; @@ -2289,11 +2289,15 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) goto init_pool_failed; } - host->max_lun = 8; + host->max_lun = IBMVSCSI_MAX_LUN; host->max_id = max_id; host->max_channel = max_channel; host->max_cmd_len = 16; + dev_info(dev, + "Maximum ID: %d Maximum LUN: %llu Maximum Channel: %d\n", + host->max_id, host->max_lun, host->max_channel); + if (scsi_add_host(hostdata->host, hostdata->dev)) goto add_host_failed; diff --git a/kernel/drivers/scsi/ibmvscsi/ibmvscsi.h b/kernel/drivers/scsi/ibmvscsi/ibmvscsi.h index 7d64867c5..106736739 100644 --- a/kernel/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/kernel/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -48,6 +48,7 @@ struct Scsi_Host; #define IBMVSCSI_CMDS_PER_LUN_DEFAULT 16 #define IBMVSCSI_MAX_SECTORS_DEFAULT 256 /* 32 * 8 = default max I/O 32 pages */ #define IBMVSCSI_MAX_CMDS_PER_LUN 64 +#define IBMVSCSI_MAX_LUN 32 /* ------------------------------------------------------------ * Data Structures diff --git a/kernel/drivers/scsi/imm.c b/kernel/drivers/scsi/imm.c index 89a826656..4e1a632cc 100644 --- a/kernel/drivers/scsi/imm.c +++ b/kernel/drivers/scsi/imm.c @@ -1109,7 +1109,6 @@ static struct scsi_host_template imm_template = { .bios_param = imm_biosparam, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, .can_queue = 1, .slave_alloc = imm_adjust_queue, diff --git a/kernel/drivers/scsi/initio.c b/kernel/drivers/scsi/initio.c index e5dae7b54..6a926bae7 100644 --- a/kernel/drivers/scsi/initio.c +++ b/kernel/drivers/scsi/initio.c @@ -2833,7 +2833,6 @@ static struct scsi_host_template initio_template = { .can_queue = MAX_TARGETS * i91u_MAXQUEUE, .this_id = 1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/ipr.c b/kernel/drivers/scsi/ipr.c index a9aa38903..536cd5a80 100644 --- a/kernel/drivers/scsi/ipr.c +++ b/kernel/drivers/scsi/ipr.c @@ -1165,7 +1165,8 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res, if (ioa_cfg->sis64) { proto = cfgtew->u.cfgte64->proto; - res->res_flags = cfgtew->u.cfgte64->res_flags; + res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags); + res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags); res->qmodel = IPR_QUEUEING_MODEL64(res); res->type = cfgtew->u.cfgte64->res_type; @@ -1313,8 +1314,8 @@ static void ipr_update_res_entry(struct ipr_resource_entry *res, int new_path = 0; if (res->ioa_cfg->sis64) { - res->flags = cfgtew->u.cfgte64->flags; - res->res_flags = cfgtew->u.cfgte64->res_flags; + res->flags = be16_to_cpu(cfgtew->u.cfgte64->flags); + res->res_flags = be16_to_cpu(cfgtew->u.cfgte64->res_flags); res->type = cfgtew->u.cfgte64->res_type; memcpy(&res->std_inq_data, &cfgtew->u.cfgte64->std_inq_data, @@ -1900,7 +1901,7 @@ static void ipr_log_array_error(struct ipr_ioa_cfg *ioa_cfg, * Return value: * none **/ -static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, u32 *data, int len) +static void ipr_log_hex_data(struct ipr_ioa_cfg *ioa_cfg, __be32 *data, int len) { int i; @@ -2270,7 +2271,7 @@ static void ipr_log_fabric_error(struct ipr_ioa_cfg *ioa_cfg, ((unsigned long)fabric + be16_to_cpu(fabric->length)); } - ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len); + ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len); } /** @@ -2364,7 +2365,7 @@ static void ipr_log_sis64_fabric_error(struct ipr_ioa_cfg *ioa_cfg, ((unsigned long)fabric + be16_to_cpu(fabric->length)); } - ipr_log_hex_data(ioa_cfg, (u32 *)fabric, add_len); + ipr_log_hex_data(ioa_cfg, (__be32 *)fabric, add_len); } /** @@ -4455,7 +4456,7 @@ static ssize_t ipr_show_device_id(struct device *dev, struct device_attribute *a spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; if (res && ioa_cfg->sis64) - len = snprintf(buf, PAGE_SIZE, "0x%llx\n", res->dev_id); + len = snprintf(buf, PAGE_SIZE, "0x%llx\n", be64_to_cpu(res->dev_id)); else if (res) len = snprintf(buf, PAGE_SIZE, "0x%llx\n", res->lun_wwn); @@ -4554,7 +4555,7 @@ static ssize_t ipr_store_raw_mode(struct device *dev, spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *)sdev->hostdata; if (res) { - if (ioa_cfg->sis64 && ipr_is_af_dasd_device(res)) { + if (ipr_is_af_dasd_device(res)) { res->raw_mode = simple_strtoul(buf, NULL, 10); len = strlen(buf); if (res->sdev) @@ -6362,15 +6363,19 @@ static int ipr_queuecommand(struct Scsi_Host *shost, ipr_cmd->scsi_cmd = scsi_cmd; ipr_cmd->done = ipr_scsi_eh_done; - if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) { + if (ipr_is_gscsi(res)) { if (scsi_cmd->underflow == 0) ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; - ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC; - if (ipr_is_gscsi(res) && res->reset_occurred) { + if (res->reset_occurred) { res->reset_occurred = 0; ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST; } + } + + if (ipr_is_gscsi(res) || ipr_is_vset_device(res)) { + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC; + ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR; if (scsi_cmd->flags & SCMD_TAGGED) ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_SIMPLE_TASK; @@ -6382,9 +6387,13 @@ static int ipr_queuecommand(struct Scsi_Host *shost, (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE)) { ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; } - if (res->raw_mode && ipr_is_af_dasd_device(res)) + if (res->raw_mode && ipr_is_af_dasd_device(res)) { ioarcb->cmd_pkt.request_type = IPR_RQTYPE_PIPE; + if (scsi_cmd->underflow == 0) + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; + } + if (ioa_cfg->sis64) rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd); else @@ -6497,7 +6506,6 @@ static struct scsi_host_template driver_template = { .shost_attrs = ipr_ioa_attrs, .sdev_attrs = ipr_dev_attrs, .proc_name = IPR_NAME, - .use_blk_tags = 1, }; /** @@ -7666,6 +7674,63 @@ static int ipr_ioafp_query_ioa_cfg(struct ipr_cmnd *ipr_cmd) return IPR_RC_JOB_RETURN; } +static int ipr_ioa_service_action_failed(struct ipr_cmnd *ipr_cmd) +{ + u32 ioasc = be32_to_cpu(ipr_cmd->s.ioasa.hdr.ioasc); + + if (ioasc == IPR_IOASC_IR_INVALID_REQ_TYPE_OR_PKT) + return IPR_RC_JOB_CONTINUE; + + return ipr_reset_cmd_failed(ipr_cmd); +} + +static void ipr_build_ioa_service_action(struct ipr_cmnd *ipr_cmd, + __be32 res_handle, u8 sa_code) +{ + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + + ioarcb->res_handle = res_handle; + ioarcb->cmd_pkt.cdb[0] = IPR_IOA_SERVICE_ACTION; + ioarcb->cmd_pkt.cdb[1] = sa_code; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD; +} + +/** + * ipr_ioafp_set_caching_parameters - Issue Set Cache parameters service + * action + * + * Return value: + * none + **/ +static int ipr_ioafp_set_caching_parameters(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data; + + ENTER; + + ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg; + + if (pageC4->cache_cap[0] & IPR_CAP_SYNC_CACHE) { + ipr_build_ioa_service_action(ipr_cmd, + cpu_to_be32(IPR_IOA_RES_HANDLE), + IPR_IOA_SA_CHANGE_CACHE_PARAMS); + + ioarcb->cmd_pkt.cdb[2] = 0x40; + + ipr_cmd->job_step_failed = ipr_ioa_service_action_failed; + ipr_do_req(ipr_cmd, ipr_reset_ioa_job, ipr_timeout, + IPR_SET_SUP_DEVICE_TIMEOUT); + + LEAVE; + return IPR_RC_JOB_RETURN; + } + + LEAVE; + return IPR_RC_JOB_CONTINUE; +} + /** * ipr_ioafp_inquiry - Send an Inquiry to the adapter. * @ipr_cmd: ipr command struct @@ -7716,6 +7781,39 @@ static int ipr_inquiry_page_supported(struct ipr_inquiry_page0 *page0, u8 page) return 0; } +/** + * ipr_ioafp_pageC4_inquiry - Send a Page 0xC4 Inquiry to the adapter. + * @ipr_cmd: ipr command struct + * + * This function sends a Page 0xC4 inquiry to the adapter + * to retrieve software VPD information. + * + * Return value: + * IPR_RC_JOB_CONTINUE / IPR_RC_JOB_RETURN + **/ +static int ipr_ioafp_pageC4_inquiry(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ipr_inquiry_page0 *page0 = &ioa_cfg->vpd_cbs->page0_data; + struct ipr_inquiry_pageC4 *pageC4 = &ioa_cfg->vpd_cbs->pageC4_data; + + ENTER; + ipr_cmd->job_step = ipr_ioafp_set_caching_parameters; + memset(pageC4, 0, sizeof(*pageC4)); + + if (ipr_inquiry_page_supported(page0, 0xC4)) { + ipr_ioafp_inquiry(ipr_cmd, 1, 0xC4, + (ioa_cfg->vpd_cbs_dma + + offsetof(struct ipr_misc_cbs, + pageC4_data)), + sizeof(struct ipr_inquiry_pageC4)); + return IPR_RC_JOB_RETURN; + } + + LEAVE; + return IPR_RC_JOB_CONTINUE; +} + /** * ipr_ioafp_cap_inquiry - Send a Page 0xD0 Inquiry to the adapter. * @ipr_cmd: ipr command struct @@ -7733,7 +7831,7 @@ static int ipr_ioafp_cap_inquiry(struct ipr_cmnd *ipr_cmd) struct ipr_inquiry_cap *cap = &ioa_cfg->vpd_cbs->cap; ENTER; - ipr_cmd->job_step = ipr_ioafp_query_ioa_cfg; + ipr_cmd->job_step = ipr_ioafp_pageC4_inquiry; memset(cap, 0, sizeof(*cap)); if (ipr_inquiry_page_supported(page0, 0xD0)) { @@ -8272,6 +8370,42 @@ static int ipr_reset_get_unit_check_job(struct ipr_cmnd *ipr_cmd) return IPR_RC_JOB_RETURN; } +static int ipr_dump_mailbox_wait(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + + ENTER; + + if (ioa_cfg->sdt_state != GET_DUMP) + return IPR_RC_JOB_RETURN; + + if (!ioa_cfg->sis64 || !ipr_cmd->u.time_left || + (readl(ioa_cfg->regs.sense_interrupt_reg) & + IPR_PCII_MAILBOX_STABLE)) { + + if (!ipr_cmd->u.time_left) + dev_err(&ioa_cfg->pdev->dev, + "Timed out waiting for Mailbox register.\n"); + + ioa_cfg->sdt_state = READ_DUMP; + ioa_cfg->dump_timeout = 0; + if (ioa_cfg->sis64) + ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT); + else + ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT); + ipr_cmd->job_step = ipr_reset_wait_for_dump; + schedule_work(&ioa_cfg->work_q); + + } else { + ipr_cmd->u.time_left -= IPR_CHECK_FOR_RESET_TIMEOUT; + ipr_reset_start_timer(ipr_cmd, + IPR_CHECK_FOR_RESET_TIMEOUT); + } + + LEAVE; + return IPR_RC_JOB_RETURN; +} + /** * ipr_reset_restore_cfg_space - Restore PCI config space. * @ipr_cmd: ipr command struct @@ -8321,20 +8455,11 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd) if (ioa_cfg->in_ioa_bringdown) { ipr_cmd->job_step = ipr_ioa_bringdown_done; + } else if (ioa_cfg->sdt_state == GET_DUMP) { + ipr_cmd->job_step = ipr_dump_mailbox_wait; + ipr_cmd->u.time_left = IPR_WAIT_FOR_MAILBOX; } else { ipr_cmd->job_step = ipr_reset_enable_ioa; - - if (GET_DUMP == ioa_cfg->sdt_state) { - ioa_cfg->sdt_state = READ_DUMP; - ioa_cfg->dump_timeout = 0; - if (ioa_cfg->sis64) - ipr_reset_start_timer(ipr_cmd, IPR_SIS64_DUMP_TIMEOUT); - else - ipr_reset_start_timer(ipr_cmd, IPR_SIS32_DUMP_TIMEOUT); - ipr_cmd->job_step = ipr_reset_wait_for_dump; - schedule_work(&ioa_cfg->work_q); - return IPR_RC_JOB_RETURN; - } } LEAVE; diff --git a/kernel/drivers/scsi/ipr.h b/kernel/drivers/scsi/ipr.h index 6b97ee45c..a34c7a5a9 100644 --- a/kernel/drivers/scsi/ipr.h +++ b/kernel/drivers/scsi/ipr.h @@ -39,8 +39,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.6.1" -#define IPR_DRIVER_DATE "(March 12, 2015)" +#define IPR_DRIVER_VERSION "2.6.3" +#define IPR_DRIVER_DATE "(October 17, 2015)" /* * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding @@ -216,6 +216,10 @@ #define IPR_SET_ALL_SUPPORTED_DEVICES 0x80 #define IPR_IOA_SHUTDOWN 0xF7 #define IPR_WR_BUF_DOWNLOAD_AND_SAVE 0x05 +#define IPR_IOA_SERVICE_ACTION 0xD2 + +/* IOA Service Actions */ +#define IPR_IOA_SA_CHANGE_CACHE_PARAMS 0x14 /* * Timeouts @@ -279,6 +283,9 @@ #define IPR_IPL_INIT_STAGE_TIME_MASK 0x0000ffff #define IPR_PCII_IPL_STAGE_CHANGE (0x80000000 >> 0) +#define IPR_PCII_MAILBOX_STABLE (0x80000000 >> 4) +#define IPR_WAIT_FOR_MAILBOX (2 * HZ) + #define IPR_PCII_IOA_TRANS_TO_OPER (0x80000000 >> 0) #define IPR_PCII_IOARCB_XFER_FAILED (0x80000000 >> 3) #define IPR_PCII_IOA_UNIT_CHECKED (0x80000000 >> 4) @@ -846,6 +853,16 @@ struct ipr_inquiry_page0 { u8 page[IPR_INQUIRY_PAGE0_ENTRIES]; }__attribute__((packed)); +struct ipr_inquiry_pageC4 { + u8 peri_qual_dev_type; + u8 page_code; + u8 reserved1; + u8 len; + u8 cache_cap[4]; +#define IPR_CAP_SYNC_CACHE 0x08 + u8 reserved2[20]; +} __packed; + struct ipr_hostrcb_device_data_entry { struct ipr_vpd vpd; struct ipr_res_addr dev_res_addr; @@ -1005,13 +1022,13 @@ struct ipr_hostrcb_type_24_error { struct ipr_hostrcb_type_07_error { u8 failure_reason[64]; struct ipr_vpd vpd; - u32 data[222]; + __be32 data[222]; }__attribute__((packed, aligned (4))); struct ipr_hostrcb_type_17_error { u8 failure_reason[64]; struct ipr_ext_vpd vpd; - u32 data[476]; + __be32 data[476]; }__attribute__((packed, aligned (4))); struct ipr_hostrcb_config_element { @@ -1289,18 +1306,17 @@ struct ipr_resource_entry { (((res)->bus << 24) | ((res)->target << 8) | (res)->lun) u8 ata_class; - - u8 flags; - __be16 res_flags; - u8 type; + u16 flags; + u16 res_flags; + u8 qmodel; struct ipr_std_inq_data std_inq_data; __be32 res_handle; __be64 dev_id; - __be64 lun_wwn; + u64 lun_wwn; struct scsi_lun dev_lun; u8 res_path[8]; @@ -1320,6 +1336,7 @@ struct ipr_misc_cbs { struct ipr_inquiry_page0 page0_data; struct ipr_inquiry_page3 page3_data; struct ipr_inquiry_cap cap; + struct ipr_inquiry_pageC4 pageC4_data; struct ipr_mode_pages mode_pages; struct ipr_supported_device supp_dev; }; diff --git a/kernel/drivers/scsi/ips.c b/kernel/drivers/scsi/ips.c index 7542f11d3..02cb76fd4 100644 --- a/kernel/drivers/scsi/ips.c +++ b/kernel/drivers/scsi/ips.c @@ -206,10 +206,6 @@ module_param(ips, charp, 0); #define IPS_VERSION_HIGH IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING #define IPS_VERSION_LOW "." IPS_VER_BUILD_STRING " " -#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__) -#warning "This driver has only been tested on the x86/ia64/x86_64 platforms" -#endif - #define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \ DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \ PCI_DMA_BIDIRECTIONAL : \ @@ -6788,6 +6784,11 @@ ips_remove_device(struct pci_dev *pci_dev) static int __init ips_module_init(void) { +#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__) + printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n"); + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); +#endif + if (pci_register_driver(&ips_pci_driver) < 0) return -ENODEV; ips_driver_template.module = THIS_MODULE; diff --git a/kernel/drivers/scsi/isci/init.c b/kernel/drivers/scsi/isci/init.c index cd41b63a2..77128d680 100644 --- a/kernel/drivers/scsi/isci/init.c +++ b/kernel/drivers/scsi/isci/init.c @@ -160,7 +160,6 @@ static struct scsi_host_template isci_sht = { .change_queue_depth = sas_change_queue_depth, .bios_param = sas_bios_param, .can_queue = ISCI_CAN_QUEUE_VAL, - .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, @@ -171,7 +170,6 @@ static struct scsi_host_template isci_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = isci_host_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -273,11 +271,11 @@ static void isci_unregister(struct isci_host *isci_host) if (!isci_host) return; + shost = to_shost(isci_host); + scsi_remove_host(shost); sas_unregister_ha(&isci_host->sas_ha); - shost = to_shost(isci_host); sas_remove_host(shost); - scsi_remove_host(shost); scsi_host_put(shost); } diff --git a/kernel/drivers/scsi/libfc/fc_fcp.c b/kernel/drivers/scsi/libfc/fc_fcp.c index 2d5909c46..5121272f2 100644 --- a/kernel/drivers/scsi/libfc/fc_fcp.c +++ b/kernel/drivers/scsi/libfc/fc_fcp.c @@ -191,7 +191,7 @@ static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp) } /** - * fc_fcp_pkt_destory() - Release hold on a fcp_pkt + * fc_fcp_pkt_destroy() - Release hold on a fcp_pkt * @seq: The sequence that the FCP packet is on (required by destructor API) * @fsp: The FCP packet to be released * diff --git a/kernel/drivers/scsi/libfc/fc_npiv.c b/kernel/drivers/scsi/libfc/fc_npiv.c index 9fbf78ed8..c168321b5 100644 --- a/kernel/drivers/scsi/libfc/fc_npiv.c +++ b/kernel/drivers/scsi/libfc/fc_npiv.c @@ -25,7 +25,7 @@ #include /** - * fc_vport_create() - Create a new NPIV vport instance + * libfc_vport_create() - Create a new NPIV vport instance * @vport: fc_vport structure from scsi_transport_fc * @privsize: driver private data size to allocate along with the Scsi_Host */ diff --git a/kernel/drivers/scsi/libiscsi.c b/kernel/drivers/scsi/libiscsi.c index 98d9bb6ff..6bffd91b9 100644 --- a/kernel/drivers/scsi/libiscsi.c +++ b/kernel/drivers/scsi/libiscsi.c @@ -853,12 +853,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, SAM_STAT_CHECK_CONDITION; scsi_build_sense_buffer(1, sc->sense_buffer, ILLEGAL_REQUEST, 0x10, ascq); - sc->sense_buffer[7] = 0xc; /* Additional sense length */ - sc->sense_buffer[8] = 0; /* Information desc type */ - sc->sense_buffer[9] = 0xa; /* Additional desc length */ - sc->sense_buffer[10] = 0x80; /* Validity bit */ - - put_unaligned_be64(sector, &sc->sense_buffer[12]); + scsi_set_sense_information(sc->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + sector); goto out; } } @@ -979,13 +976,13 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) wake_up(&conn->ehwait); } -static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) +static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) { struct iscsi_nopout hdr; struct iscsi_task *task; if (!rhdr && conn->ping_task) - return; + return -EINVAL; memset(&hdr, 0, sizeof(struct iscsi_nopout)); hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; @@ -999,13 +996,16 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) hdr.ttt = RESERVED_ITT; task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0); - if (!task) + if (!task) { iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); - else if (!rhdr) { + return -EIO; + } else if (!rhdr) { /* only track our nops */ conn->ping_task = task; conn->last_ping = jiffies; } + + return 0; } static int iscsi_nop_out_rsp(struct iscsi_task *task, @@ -2095,8 +2095,10 @@ static void iscsi_check_transport_timeouts(unsigned long data) if (time_before_eq(last_recv + recv_timeout, jiffies)) { /* send a ping to try to provoke some traffic */ ISCSI_DBG_CONN(conn, "Sending nopout as ping\n"); - iscsi_send_nopout(conn, NULL); - next_timeout = conn->last_ping + (conn->ping_timeout * HZ); + if (iscsi_send_nopout(conn, NULL)) + next_timeout = jiffies + (1 * HZ); + else + next_timeout = conn->last_ping + (conn->ping_timeout * HZ); } else next_timeout = last_recv + recv_timeout; diff --git a/kernel/drivers/scsi/lpfc/lpfc.h b/kernel/drivers/scsi/lpfc/lpfc.h index 9b81a34d7..ceee9a3fd 100644 --- a/kernel/drivers/scsi/lpfc/lpfc.h +++ b/kernel/drivers/scsi/lpfc/lpfc.h @@ -230,6 +230,8 @@ struct lpfc_stats { uint32_t elsRcvRRQ; uint32_t elsRcvRTV; uint32_t elsRcvECHO; + uint32_t elsRcvLCB; + uint32_t elsRcvRDP; uint32_t elsXmitFLOGI; uint32_t elsXmitFDISC; uint32_t elsXmitPLOGI; @@ -493,15 +495,17 @@ struct unsol_rcv_ct_ctx { #define LPFC_USER_LINK_SPEED_8G 8 /* 8 Gigabaud */ #define LPFC_USER_LINK_SPEED_10G 10 /* 10 Gigabaud */ #define LPFC_USER_LINK_SPEED_16G 16 /* 16 Gigabaud */ -#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_16G -#define LPFC_USER_LINK_SPEED_BITMAP ((1 << LPFC_USER_LINK_SPEED_16G) | \ +#define LPFC_USER_LINK_SPEED_32G 32 /* 32 Gigabaud */ +#define LPFC_USER_LINK_SPEED_MAX LPFC_USER_LINK_SPEED_32G +#define LPFC_USER_LINK_SPEED_BITMAP ((1ULL << LPFC_USER_LINK_SPEED_32G) | \ + (1 << LPFC_USER_LINK_SPEED_16G) | \ (1 << LPFC_USER_LINK_SPEED_10G) | \ (1 << LPFC_USER_LINK_SPEED_8G) | \ (1 << LPFC_USER_LINK_SPEED_4G) | \ (1 << LPFC_USER_LINK_SPEED_2G) | \ (1 << LPFC_USER_LINK_SPEED_1G) | \ (1 << LPFC_USER_LINK_SPEED_AUTO)) -#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16" +#define LPFC_LINK_SPEED_STRING "0, 1, 2, 4, 8, 10, 16, 32" enum nemb_type { nemb_mse = 1, diff --git a/kernel/drivers/scsi/lpfc/lpfc_attr.c b/kernel/drivers/scsi/lpfc/lpfc_attr.c index d65bd178d..f6446d759 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_attr.c +++ b/kernel/drivers/scsi/lpfc/lpfc_attr.c @@ -1642,8 +1642,6 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ struct lpfc_hba *phba = vport->phba;\ - uint val = 0;\ - val = phba->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n",\ phba->cfg_##attr);\ } @@ -1808,8 +1806,6 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ - uint val = 0;\ - val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%d\n", vport->cfg_##attr);\ } @@ -1835,8 +1831,6 @@ lpfc_##attr##_show(struct device *dev, struct device_attribute *attr, \ { \ struct Scsi_Host *shost = class_to_shost(dev);\ struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;\ - uint val = 0;\ - val = vport->cfg_##attr;\ return snprintf(buf, PAGE_SIZE, "%#x\n", vport->cfg_##attr);\ } @@ -3282,15 +3276,20 @@ lpfc_topology_store(struct device *dev, struct device_attribute *attr, if (val >= 0 && val <= 6) { prev_val = phba->cfg_topology; - phba->cfg_topology = val; if (phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G && val == 4) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "3113 Loop mode not supported at speed %d\n", - phba->cfg_link_speed); - phba->cfg_topology = prev_val; + val); return -EINVAL; } + if (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC && + val == 4) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, + "3114 Loop mode not supported\n"); + return -EINVAL; + } + phba->cfg_topology = val; if (nolip) return strlen(buf); @@ -3731,7 +3730,8 @@ lpfc_link_speed_store(struct device *dev, struct device_attribute *attr, ((val == LPFC_USER_LINK_SPEED_4G) && !(phba->lmt & LMT_4Gb)) || ((val == LPFC_USER_LINK_SPEED_8G) && !(phba->lmt & LMT_8Gb)) || ((val == LPFC_USER_LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)) || - ((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb))) { + ((val == LPFC_USER_LINK_SPEED_16G) && !(phba->lmt & LMT_16Gb)) || + ((val == LPFC_USER_LINK_SPEED_32G) && !(phba->lmt & LMT_32Gb))) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2879 lpfc_link_speed attribute cannot be set " "to %d. Speed is not supported by this port.\n", @@ -5267,6 +5267,9 @@ lpfc_get_host_speed(struct Scsi_Host *shost) case LPFC_LINK_SPEED_16GHZ: fc_host_speed(shost) = FC_PORTSPEED_16GBIT; break; + case LPFC_LINK_SPEED_32GHZ: + fc_host_speed(shost) = FC_PORTSPEED_32GBIT; + break; default: fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; break; diff --git a/kernel/drivers/scsi/lpfc/lpfc_bsg.c b/kernel/drivers/scsi/lpfc/lpfc_bsg.c index b70506807..05dcc2abd 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_bsg.c +++ b/kernel/drivers/scsi/lpfc/lpfc_bsg.c @@ -904,7 +904,6 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, { uint32_t evt_req_id = 0; uint32_t cmd; - uint32_t len; struct lpfc_dmabuf *dmabuf = NULL; struct lpfc_bsg_event *evt; struct event_data *evt_dat = NULL; @@ -946,7 +945,6 @@ lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt; evt_req_id = ct_req->FsType; cmd = ct_req->CommandResponse.bits.CmdRsp; - len = ct_req->CommandResponse.bits.Size; if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)) lpfc_sli_ringpostbuf_put(phba, pring, dmabuf); @@ -2988,7 +2986,6 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct diag_mode_test *diag_mode; struct lpfc_bsg_event *evt; struct event_data *evdat; struct lpfc_sli *psli = &phba->sli; @@ -3031,8 +3028,6 @@ lpfc_bsg_diag_loopback_run(struct fc_bsg_job *job) rc = -EINVAL; goto loopback_test_exit; } - diag_mode = (struct diag_mode_test *) - job->request->rqst_data.h_vendor.vendor_cmd; if ((phba->link_state == LPFC_HBA_ERROR) || (psli->sli_flag & LPFC_BLOCK_MGMT_IO) || @@ -3293,7 +3288,6 @@ lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job) { struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata; struct lpfc_hba *phba = vport->phba; - struct get_mgmt_rev *event_req; struct get_mgmt_rev_reply *event_reply; int rc = 0; @@ -3306,9 +3300,6 @@ lpfc_bsg_get_dfc_rev(struct fc_bsg_job *job) goto job_error; } - event_req = (struct get_mgmt_rev *) - job->request->rqst_data.h_vendor.vendor_cmd; - event_reply = (struct get_mgmt_rev_reply *) job->reply->reply_data.vendor_reply.vendor_rsp; @@ -4348,7 +4339,6 @@ static int lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job, struct lpfc_dmabuf *dmabuf) { - struct lpfc_sli_config_mbox *sli_cfg_mbx; struct bsg_job_data *dd_data = NULL; LPFC_MBOXQ_t *pmboxq = NULL; MAILBOX_t *pmb; @@ -4362,9 +4352,6 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct fc_bsg_job *job, phba->mbox_ext_buf_ctx.seqNum++; nemb_tp = phba->mbox_ext_buf_ctx.nembType; - sli_cfg_mbx = (struct lpfc_sli_config_mbox *) - phba->mbox_ext_buf_ctx.mbx_dmabuf->virt; - dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); if (!dd_data) { rc = -ENOMEM; @@ -4606,7 +4593,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t transmit_length, receive_length, mode; struct lpfc_mbx_sli4_config *sli4_config; struct lpfc_mbx_nembed_cmd *nembed_sge; - struct mbox_header *header; struct ulp_bde64 *bde; uint8_t *ext = NULL; int rc = 0; @@ -4804,8 +4790,6 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, /* rebuild the command for sli4 using our * own buffers like we do for biu diags */ - header = (struct mbox_header *) - &pmb->un.varWords[0]; nembed_sge = (struct lpfc_mbx_nembed_cmd *) &pmb->un.varWords[0]; receive_length = nembed_sge->sge[0].length; @@ -5048,7 +5032,6 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) IOCB_t *cmd; int rc = 0; struct menlo_command *menlo_cmd; - struct menlo_response *menlo_resp; struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL; int request_nseg; int reply_nseg; @@ -5088,9 +5071,6 @@ lpfc_menlo_cmd(struct fc_bsg_job *job) menlo_cmd = (struct menlo_command *) job->request->rqst_data.h_vendor.vendor_cmd; - menlo_resp = (struct menlo_response *) - job->reply->reply_data.vendor_reply.vendor_rsp; - /* allocate our bsg tracking structure */ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); if (!dd_data) { diff --git a/kernel/drivers/scsi/lpfc/lpfc_crtn.h b/kernel/drivers/scsi/lpfc/lpfc_crtn.h index 587e3e962..b0e6fe464 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_crtn.h +++ b/kernel/drivers/scsi/lpfc/lpfc_crtn.h @@ -498,3 +498,5 @@ bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *, bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *, struct lpfc_name *, uint64_t *, struct lpfc_name *, struct lpfc_name *, uint64_t *, uint32_t *); +int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox); +void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb); diff --git a/kernel/drivers/scsi/lpfc/lpfc_ct.c b/kernel/drivers/scsi/lpfc/lpfc_ct.c index af129966b..8fded1f76 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_ct.c +++ b/kernel/drivers/scsi/lpfc/lpfc_ct.c @@ -55,6 +55,7 @@ #define HBA_PORTSPEED_10GBIT 0x0004 /* 10 GBit/sec */ #define HBA_PORTSPEED_8GBIT 0x0010 /* 8 GBit/sec */ #define HBA_PORTSPEED_16GBIT 0x0020 /* 16 GBit/sec */ +#define HBA_PORTSPEED_32GBIT 0x0040 /* 32 GBit/sec */ #define HBA_PORTSPEED_UNKNOWN 0x0800 /* Unknown */ #define FOURBYTES 4 @@ -575,7 +576,6 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_vport *vport = cmdiocb->vport; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); IOCB_t *irsp; - struct lpfc_dmabuf *bmp; struct lpfc_dmabuf *outp; struct lpfc_sli_ct_request *CTrsp; struct lpfc_nodelist *ndlp; @@ -588,7 +588,6 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, cmdiocb->context_un.rsp_iocb = rspiocb; outp = (struct lpfc_dmabuf *) cmdiocb->context2; - bmp = (struct lpfc_dmabuf *) cmdiocb->context3; irsp = &rspiocb->iocb; lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_CT, @@ -1733,12 +1732,9 @@ hba_out: case SLI_MGMT_RPRT: case SLI_MGMT_RPA: { - lpfc_vpd_t *vp; struct serv_parm *hsp; int len = 0; - vp = &phba->vpd; - if (cmdcode == SLI_MGMT_RPRT) { rh = (struct lpfc_fdmi_reg_hba *) &CtReq->un.PortID; @@ -1778,6 +1774,8 @@ hba_out: ad->AttrType = cpu_to_be16(RPRT_SUPPORTED_SPEED); ad->AttrLen = cpu_to_be16(FOURBYTES + 4); ae->un.SupportSpeed = 0; + if (phba->lmt & LMT_32Gb) + ae->un.SupportSpeed |= HBA_PORTSPEED_32GBIT; if (phba->lmt & LMT_16Gb) ae->un.SupportSpeed |= HBA_PORTSPEED_16GBIT; if (phba->lmt & LMT_10Gb) @@ -1821,6 +1819,9 @@ hba_out: case LPFC_LINK_SPEED_16GHZ: ae->un.PortSpeed = HBA_PORTSPEED_16GBIT; break; + case LPFC_LINK_SPEED_32GHZ: + ae->un.PortSpeed = HBA_PORTSPEED_32GBIT; + break; default: ae->un.PortSpeed = HBA_PORTSPEED_UNKNOWN; break; diff --git a/kernel/drivers/scsi/lpfc/lpfc_debugfs.c b/kernel/drivers/scsi/lpfc/lpfc_debugfs.c index 513edcb0c..25aa9b98d 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/kernel/drivers/scsi/lpfc/lpfc_debugfs.c @@ -710,7 +710,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt, * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int @@ -760,7 +760,7 @@ out: * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int @@ -810,7 +810,7 @@ out: * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int @@ -852,7 +852,7 @@ out: * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int @@ -894,7 +894,7 @@ out: * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int @@ -1115,7 +1115,7 @@ lpfc_debugfs_dif_err_release(struct inode *inode, struct file *file) * returns a pointer to that log in the private_data field in @file. * * Returns: - * This function returns zero if successful. On error it will return an negative + * This function returns zero if successful. On error it will return a negative * error value. **/ static int diff --git a/kernel/drivers/scsi/lpfc/lpfc_disc.h b/kernel/drivers/scsi/lpfc/lpfc_disc.h index 697702797..361f5b3d9 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_disc.h +++ b/kernel/drivers/scsi/lpfc/lpfc_disc.h @@ -79,7 +79,6 @@ struct lpfc_nodelist { struct lpfc_name nlp_portname; struct lpfc_name nlp_nodename; uint32_t nlp_flag; /* entry flags */ - uint32_t nlp_add_flag; /* additional flags */ uint32_t nlp_DID; /* FC D_ID of entry */ uint32_t nlp_last_elscmd; /* Last ELS cmd sent */ uint16_t nlp_type; @@ -147,6 +146,7 @@ struct lpfc_node_rrq { #define NLP_LOGO_ACC 0x00100000 /* Process LOGO after ACC completes */ #define NLP_TGT_NO_SCSIID 0x00200000 /* good PRLI but no binding for scsid */ #define NLP_ISSUE_LOGO 0x00400000 /* waiting to issue a LOGO */ +#define NLP_IN_DEV_LOSS 0x00800000 /* devloss in progress */ #define NLP_ACC_REGLOGIN 0x01000000 /* Issue Reg Login after successful ACC */ #define NLP_NPR_ADISC 0x02000000 /* Issue ADISC when dq'ed from @@ -158,8 +158,6 @@ struct lpfc_node_rrq { #define NLP_FIRSTBURST 0x40000000 /* Target supports FirstBurst */ #define NLP_RPI_REGISTERED 0x80000000 /* nlp_rpi is valid */ -/* Defines for nlp_add_flag (uint32) */ -#define NLP_IN_DEV_LOSS 0x00000001 /* Dev Loss processing in progress */ /* ndlp usage management macros */ #define NLP_CHK_NODE_ACT(ndlp) (((ndlp)->nlp_usg_map \ diff --git a/kernel/drivers/scsi/lpfc/lpfc_els.c b/kernel/drivers/scsi/lpfc/lpfc_els.c index 851e8efe3..b6fa257ea 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_els.c +++ b/kernel/drivers/scsi/lpfc/lpfc_els.c @@ -457,11 +457,9 @@ lpfc_issue_reg_vfi(struct lpfc_vport *vport) struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *mboxq; struct lpfc_nodelist *ndlp; - struct serv_parm *sp; struct lpfc_dmabuf *dmabuf; int rc = 0; - sp = &phba->fc_fabparam; /* move forward in case of SLI4 FC port loopback test and pt2pt mode */ if ((phba->sli_rev == LPFC_SLI_REV4) && !(phba->link_flag & LS_LOOPBACK_MODE) && @@ -1028,9 +1026,11 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, stop_rr_fcf_flogi: /* FLOGI failure */ lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS, - "2858 FLOGI failure Status:x%x/x%x TMO:x%x\n", + "2858 FLOGI failure Status:x%x/x%x TMO:x%x " + "Data x%x x%x\n", irsp->ulpStatus, irsp->un.ulpWord[4], - irsp->ulpTimeout); + irsp->ulpTimeout, phba->hba_flag, + phba->fcf.fcf_flag); /* Check for retry */ if (lpfc_els_retry(phba, cmdiocb, rspiocb)) @@ -1154,6 +1154,9 @@ stop_rr_fcf_flogi: } flogifail: + spin_lock_irq(&phba->hbalock); + phba->fcf.fcf_flag &= ~FCF_DISCOVERY; + spin_unlock_irq(&phba->hbalock); lpfc_nlp_put(ndlp); if (!lpfc_error_lost_link(irsp)) { @@ -1205,14 +1208,11 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct serv_parm *sp; IOCB_t *icmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli_ring *pring; uint8_t *pcmd; uint16_t cmdsize; uint32_t tmo; int rc; - pring = &phba->sli.ring[LPFC_ELS_RING]; - cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm)); elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, ndlp->nlp_DID, ELS_CMD_FLOGI); @@ -1454,8 +1454,6 @@ lpfc_initial_fdisc(struct lpfc_vport *vport) void lpfc_more_plogi(struct lpfc_vport *vport) { - int sentplogi; - if (vport->num_disc_nodes) vport->num_disc_nodes--; @@ -1468,7 +1466,7 @@ lpfc_more_plogi(struct lpfc_vport *vport) /* Check to see if there are more PLOGIs to be sent */ if (vport->fc_flag & FC_NLP_MORE) /* go thru NPR nodes and issue any remaining ELS PLOGIs */ - sentplogi = lpfc_els_disc_plogi(vport); + lpfc_els_disc_plogi(vport); return; } @@ -1509,12 +1507,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, struct lpfc_nodelist *ndlp) { struct lpfc_vport *vport = ndlp->vport; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_nodelist *new_ndlp; struct lpfc_rport_data *rdata; struct fc_rport *rport; struct serv_parm *sp; uint8_t name[sizeof(struct lpfc_name)]; - uint32_t rc, keepDID = 0; + uint32_t rc, keepDID = 0, keep_nlp_flag = 0; + uint16_t keep_nlp_state; int put_node; int put_rport; unsigned long *active_rrqs_xri_bitmap = NULL; @@ -1603,11 +1603,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, ndlp->active_rrqs_xri_bitmap, phba->cfg_rrq_xri_bitmap_sz); - if (ndlp->nlp_flag & NLP_NPR_2B_DISC) - new_ndlp->nlp_flag |= NLP_NPR_2B_DISC; - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + spin_lock_irq(shost->host_lock); + keep_nlp_flag = new_ndlp->nlp_flag; + new_ndlp->nlp_flag = ndlp->nlp_flag; + ndlp->nlp_flag = keep_nlp_flag; + spin_unlock_irq(shost->host_lock); - /* Set state will put new_ndlp on to node list if not already done */ + /* Set nlp_states accordingly */ + keep_nlp_state = new_ndlp->nlp_state; lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state); /* Move this back to NPR state */ @@ -1624,8 +1627,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, if (rport) { rdata = rport->dd_data; if (rdata->pnode == ndlp) { - lpfc_nlp_put(ndlp); + /* break the link before dropping the ref */ ndlp->rport = NULL; + lpfc_nlp_put(ndlp); rdata->pnode = lpfc_nlp_get(new_ndlp); new_ndlp->rport = rport; } @@ -1648,7 +1652,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, memcpy(ndlp->active_rrqs_xri_bitmap, active_rrqs_xri_bitmap, phba->cfg_rrq_xri_bitmap_sz); - lpfc_drop_node(vport, ndlp); + + if (!NLP_CHK_NODE_ACT(ndlp)) + lpfc_drop_node(vport, ndlp); } else { lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, @@ -1665,20 +1671,13 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, active_rrqs_xri_bitmap, phba->cfg_rrq_xri_bitmap_sz); - /* Since we are swapping the ndlp passed in with the new one - * and the did has already been swapped, copy over state. - * The new WWNs are already in new_ndlp since thats what - * we looked it up by in the begining of this routine. - */ - new_ndlp->nlp_state = ndlp->nlp_state; - - /* Since we are switching over to the new_ndlp, the old - * ndlp should be put in the NPR state, unless we have - * already started re-discovery on it. + /* Since we are switching over to the new_ndlp, + * reset the old ndlp state */ if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) || (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) - lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); + keep_nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_set_state(vport, ndlp, keep_nlp_state); /* Fix up the rport accordingly */ rport = ndlp->rport; @@ -1955,16 +1954,12 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry) { struct lpfc_hba *phba = vport->phba; struct serv_parm *sp; - IOCB_t *icmd; struct lpfc_nodelist *ndlp; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int ret; - psli = &phba->sli; - ndlp = lpfc_findnode_did(vport, did); if (ndlp && !NLP_CHK_NODE_ACT(ndlp)) ndlp = NULL; @@ -1976,7 +1971,6 @@ lpfc_issue_els_plogi(struct lpfc_vport *vport, uint32_t did, uint8_t retry) if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); /* For PLOGI request, remainder of payload is service parameters */ @@ -2033,10 +2027,8 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_vport *vport = cmdiocb->vport; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); IOCB_t *irsp; - struct lpfc_sli *psli; struct lpfc_nodelist *ndlp; - psli = &phba->sli; /* we pass cmdiocb to state machine which needs rspiocb as well */ cmdiocb->context_un.rsp_iocb = rspiocb; @@ -2116,7 +2108,6 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; PRLI *npr; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; @@ -2127,7 +2118,6 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); /* For PRLI request, remainder of payload is service parameters */ @@ -2412,7 +2402,6 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; ADISC *ap; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; @@ -2423,7 +2412,6 @@ lpfc_issue_els_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); /* For ADISC request, remainder of payload is service parameters */ @@ -2477,12 +2465,10 @@ lpfc_cmpl_els_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, struct lpfc_vport *vport = ndlp->vport; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); IOCB_t *irsp; - struct lpfc_sli *psli; struct lpfcMboxq *mbox; unsigned long flags; uint32_t skip_recovery = 0; - psli = &phba->sli; /* we pass cmdiocb to state machine which needs rspiocb as well */ cmdiocb->context_un.rsp_iocb = rspiocb; @@ -2608,7 +2594,6 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; @@ -2627,7 +2612,6 @@ lpfc_issue_els_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_LOGO; pcmd += sizeof(uint32_t); @@ -2741,14 +2725,11 @@ int lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) { struct lpfc_hba *phba = vport->phba; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; struct lpfc_nodelist *ndlp; - psli = &phba->sli; cmdsize = (sizeof(uint32_t) + sizeof(SCR)); ndlp = lpfc_findnode_did(vport, nportid); @@ -2775,7 +2756,6 @@ lpfc_issue_els_scr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) return 1; } - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_SCR; @@ -2835,9 +2815,7 @@ static int lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) { struct lpfc_hba *phba = vport->phba; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; FARP *fp; uint8_t *pcmd; uint32_t *lp; @@ -2845,7 +2823,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) struct lpfc_nodelist *ondlp; struct lpfc_nodelist *ndlp; - psli = &phba->sli; cmdsize = (sizeof(uint32_t) + sizeof(FARP)); ndlp = lpfc_findnode_did(vport, nportid); @@ -2871,7 +2848,6 @@ lpfc_issue_els_farpr(struct lpfc_vport *vport, uint32_t nportid, uint8_t retry) return 1; } - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_FARPR; @@ -3667,15 +3643,6 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * At this point, the driver is done so release the IOCB */ lpfc_els_free_iocb(phba, cmdiocb); - - /* - * Remove the ndlp reference if it's a fabric node that has - * sent us an unsolicted LOGO. - */ - if (ndlp->nlp_type & NLP_FABRIC) - lpfc_nlp_put(ndlp); - - return; } /** @@ -3930,13 +3897,11 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, IOCB_t *icmd; IOCB_t *oldcmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int rc; ELS_PKT *els_pkt_ptr; - psli = &phba->sli; oldcmd = &oldiocb->iocb; switch (flag) { @@ -4020,7 +3985,9 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag, ndlp->nlp_rpi, vport->fc_flag); if (ndlp->nlp_flag & NLP_LOGO_ACC) { spin_lock_irq(shost->host_lock); - ndlp->nlp_flag &= ~NLP_LOGO_ACC; + if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED || + ndlp->nlp_flag & NLP_REG_LOGIN_SEND)) + ndlp->nlp_flag &= ~NLP_LOGO_ACC; spin_unlock_irq(shost->host_lock); elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc; } else { @@ -4067,12 +4034,10 @@ lpfc_els_rsp_reject(struct lpfc_vport *vport, uint32_t rejectError, IOCB_t *icmd; IOCB_t *oldcmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int rc; - psli = &phba->sli; cmdsize = 2 * sizeof(uint32_t); elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT); @@ -4218,13 +4183,10 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, IOCB_t *icmd; IOCB_t *oldcmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int rc; - psli = &phba->sli; - cmdsize = sizeof(uint32_t) + sizeof(PRLI); elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, oldiocb->retry, ndlp, ndlp->nlp_DID, (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK))); @@ -4321,12 +4283,10 @@ lpfc_els_rsp_rnid_acc(struct lpfc_vport *vport, uint8_t format, RNID *rn; IOCB_t *icmd, *oldcmd; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int rc; - psli = &phba->sli; cmdsize = sizeof(uint32_t) + sizeof(uint32_t) + (2 * sizeof(struct lpfc_name)); if (format) @@ -4453,12 +4413,10 @@ lpfc_els_rsp_echo_acc(struct lpfc_vport *vport, uint8_t *data, { struct lpfc_hba *phba = vport->phba; struct lpfc_iocbq *elsiocb; - struct lpfc_sli *psli; uint8_t *pcmd; uint16_t cmdsize; int rc; - psli = &phba->sli; cmdsize = oldiocb->iocb.unsli3.rcvsli3.acc_len; /* The accumulated length can exceed the BPL_SIZE. For @@ -4587,16 +4545,16 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) if (!NLP_CHK_NODE_ACT(ndlp)) continue; if (ndlp->nlp_state == NLP_STE_NPR_NODE && - (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 && - (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 && - (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) { + (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 && + (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 && + (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) { ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE); lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0); sentplogi++; vport->num_disc_nodes++; if (vport->num_disc_nodes >= - vport->cfg_discovery_threads) { + vport->cfg_discovery_threads) { spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_NLP_MORE; spin_unlock_irq(shost->host_lock); @@ -4615,6 +4573,666 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport) return sentplogi; } +void +lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc, + uint32_t word0) +{ + + desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG); + desc->payload.els_req = word0; + desc->length = cpu_to_be32(sizeof(desc->payload)); +} + +void +lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc, + uint8_t *page_a0, uint8_t *page_a2) +{ + uint16_t wavelength; + uint16_t temperature; + uint16_t rx_power; + uint16_t tx_bias; + uint16_t tx_power; + uint16_t vcc; + uint16_t flag = 0; + struct sff_trasnceiver_codes_byte4 *trasn_code_byte4; + struct sff_trasnceiver_codes_byte5 *trasn_code_byte5; + + desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG); + + trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *) + &page_a0[SSF_TRANSCEIVER_CODE_B4]; + trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *) + &page_a0[SSF_TRANSCEIVER_CODE_B5]; + + if ((trasn_code_byte4->fc_sw_laser) || + (trasn_code_byte5->fc_sw_laser_sl) || + (trasn_code_byte5->fc_sw_laser_sn)) { /* check if its short WL */ + flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT); + } else if (trasn_code_byte4->fc_lw_laser) { + wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) | + page_a0[SSF_WAVELENGTH_B0]; + if (wavelength == SFP_WAVELENGTH_LC1310) + flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT; + if (wavelength == SFP_WAVELENGTH_LL1550) + flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT; + } + /* check if its SFP+ */ + flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ? + SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN) + << SFP_FLAG_CT_SHIFT; + + /* check if its OPTICAL */ + flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ? + SFP_FLAG_IS_OPTICAL_PORT : 0) + << SFP_FLAG_IS_OPTICAL_SHIFT; + + temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 | + page_a2[SFF_TEMPERATURE_B0]); + vcc = (page_a2[SFF_VCC_B1] << 8 | + page_a2[SFF_VCC_B0]); + tx_power = (page_a2[SFF_TXPOWER_B1] << 8 | + page_a2[SFF_TXPOWER_B0]); + tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 | + page_a2[SFF_TX_BIAS_CURRENT_B0]); + rx_power = (page_a2[SFF_RXPOWER_B1] << 8 | + page_a2[SFF_RXPOWER_B0]); + desc->sfp_info.temperature = cpu_to_be16(temperature); + desc->sfp_info.rx_power = cpu_to_be16(rx_power); + desc->sfp_info.tx_bias = cpu_to_be16(tx_bias); + desc->sfp_info.tx_power = cpu_to_be16(tx_power); + desc->sfp_info.vcc = cpu_to_be16(vcc); + + desc->sfp_info.flags = cpu_to_be16(flag); + desc->length = cpu_to_be32(sizeof(desc->sfp_info)); +} + +void +lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc, + READ_LNK_VAR *stat) +{ + uint32_t type; + + desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG); + + type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT; + + desc->info.port_type = cpu_to_be32(type); + + desc->info.link_status.link_failure_cnt = + cpu_to_be32(stat->linkFailureCnt); + desc->info.link_status.loss_of_synch_cnt = + cpu_to_be32(stat->lossSyncCnt); + desc->info.link_status.loss_of_signal_cnt = + cpu_to_be32(stat->lossSignalCnt); + desc->info.link_status.primitive_seq_proto_err = + cpu_to_be32(stat->primSeqErrCnt); + desc->info.link_status.invalid_trans_word = + cpu_to_be32(stat->invalidXmitWord); + desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt); + + desc->length = cpu_to_be32(sizeof(desc->info)); +} + +void +lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba) +{ + uint16_t rdp_cap = 0; + uint16_t rdp_speed; + + desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG); + + switch (phba->sli4_hba.link_state.speed) { + case LPFC_FC_LA_SPEED_1G: + rdp_speed = RDP_PS_1GB; + break; + case LPFC_FC_LA_SPEED_2G: + rdp_speed = RDP_PS_2GB; + break; + case LPFC_FC_LA_SPEED_4G: + rdp_speed = RDP_PS_4GB; + break; + case LPFC_FC_LA_SPEED_8G: + rdp_speed = RDP_PS_8GB; + break; + case LPFC_FC_LA_SPEED_10G: + rdp_speed = RDP_PS_10GB; + break; + case LPFC_FC_LA_SPEED_16G: + rdp_speed = RDP_PS_16GB; + break; + case LPFC_FC_LA_SPEED_32G: + rdp_speed = RDP_PS_32GB; + break; + default: + rdp_speed = RDP_PS_UNKNOWN; + break; + } + + desc->info.port_speed.speed = cpu_to_be16(rdp_speed); + + if (phba->lmt & LMT_32Gb) + rdp_cap |= RDP_PS_32GB; + if (phba->lmt & LMT_16Gb) + rdp_cap |= RDP_PS_16GB; + if (phba->lmt & LMT_10Gb) + rdp_cap |= RDP_PS_10GB; + if (phba->lmt & LMT_8Gb) + rdp_cap |= RDP_PS_8GB; + if (phba->lmt & LMT_4Gb) + rdp_cap |= RDP_PS_4GB; + if (phba->lmt & LMT_2Gb) + rdp_cap |= RDP_PS_2GB; + if (phba->lmt & LMT_1Gb) + rdp_cap |= RDP_PS_1GB; + + if (rdp_cap == 0) + rdp_cap = RDP_CAP_UNKNOWN; + + desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap); + desc->length = cpu_to_be32(sizeof(desc->info)); +} + +void +lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc, + struct lpfc_hba *phba) +{ + + desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG); + + memcpy(desc->port_names.wwnn, phba->wwnn, + sizeof(desc->port_names.wwnn)); + + memcpy(desc->port_names.wwpn, &phba->wwpn, + sizeof(desc->port_names.wwpn)); + + desc->length = cpu_to_be32(sizeof(desc->port_names)); +} + +void +lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc, + struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) +{ + + desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG); + if (vport->fc_flag & FC_FABRIC) { + memcpy(desc->port_names.wwnn, &vport->fabric_nodename, + sizeof(desc->port_names.wwnn)); + + memcpy(desc->port_names.wwpn, &vport->fabric_portname, + sizeof(desc->port_names.wwpn)); + } else { /* Point to Point */ + memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename, + sizeof(desc->port_names.wwnn)); + + memcpy(desc->port_names.wwnn, &ndlp->nlp_portname, + sizeof(desc->port_names.wwpn)); + } + + desc->length = cpu_to_be32(sizeof(desc->port_names)); +} + +void +lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context, + int status) +{ + struct lpfc_nodelist *ndlp = rdp_context->ndlp; + struct lpfc_vport *vport = ndlp->vport; + struct lpfc_iocbq *elsiocb; + IOCB_t *icmd; + uint8_t *pcmd; + struct ls_rjt *stat; + struct fc_rdp_res_frame *rdp_res; + uint32_t cmdsize; + int rc; + + if (status != SUCCESS) + goto error; + cmdsize = sizeof(struct fc_rdp_res_frame); + + elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, + lpfc_max_els_tries, rdp_context->ndlp, + rdp_context->ndlp->nlp_DID, ELS_CMD_ACC); + lpfc_nlp_put(ndlp); + if (!elsiocb) + goto free_rdp_context; + + icmd = &elsiocb->iocb; + icmd->ulpContext = rdp_context->rx_id; + icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "2171 Xmit RDP response tag x%x xri x%x, " + "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x", + elsiocb->iotag, elsiocb->iocb.ulpContext, + ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state, + ndlp->nlp_rpi); + rdp_res = (struct fc_rdp_res_frame *) + (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + memset(pcmd, 0, sizeof(struct fc_rdp_res_frame)); + *((uint32_t *) (pcmd)) = ELS_CMD_ACC; + + /* For RDP payload */ + lpfc_rdp_res_link_service(&rdp_res->link_service_desc, ELS_CMD_RDP); + + lpfc_rdp_res_sfp_desc(&rdp_res->sfp_desc, + rdp_context->page_a0, rdp_context->page_a2); + lpfc_rdp_res_speed(&rdp_res->portspeed_desc, phba); + lpfc_rdp_res_link_error(&rdp_res->link_error_desc, + &rdp_context->link_stat); + lpfc_rdp_res_diag_port_names(&rdp_res->diag_port_names_desc, phba); + lpfc_rdp_res_attach_port_names(&rdp_res->attached_port_names_desc, + vport, ndlp); + rdp_res->length = cpu_to_be32(RDP_DESC_PAYLOAD_SIZE); + + elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp; + + phba->fc_stat.elsXmitACC++; + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); + if (rc == IOCB_ERROR) + lpfc_els_free_iocb(phba, elsiocb); + + kfree(rdp_context); + + return; +error: + cmdsize = 2 * sizeof(uint32_t); + elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries, + ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT); + lpfc_nlp_put(ndlp); + if (!elsiocb) + goto free_rdp_context; + + icmd = &elsiocb->iocb; + icmd->ulpContext = rdp_context->rx_id; + icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id; + pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT; + stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t)); + stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + + phba->fc_stat.elsXmitLSRJT++; + elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp; + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); + + if (rc == IOCB_ERROR) + lpfc_els_free_iocb(phba, elsiocb); +free_rdp_context: + kfree(rdp_context); +} + +int +lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context) +{ + LPFC_MBOXQ_t *mbox = NULL; + int rc; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) { + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS, + "7105 failed to allocate mailbox memory"); + return 1; + } + + if (lpfc_sli4_dump_page_a0(phba, mbox)) + goto prep_mbox_fail; + mbox->vport = rdp_context->ndlp->vport; + mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0; + mbox->context2 = (struct lpfc_rdp_context *) rdp_context; + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) + goto issue_mbox_fail; + + return 0; + +prep_mbox_fail: +issue_mbox_fail: + mempool_free(mbox, phba->mbox_mem_pool); + return 1; +} + +/* + * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS. + * @vport: pointer to a host virtual N_Port data structure. + * @cmdiocb: pointer to lpfc command iocb data structure. + * @ndlp: pointer to a node-list data structure. + * + * This routine processes an unsolicited RDP(Read Diagnostic Parameters) + * IOCB. First, the payload of the unsolicited RDP is checked. + * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3 + * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2, + * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl + * gather all data and send RDP response. + * + * Return code + * 0 - Sent the acc response + * 1 - Sent the reject response. + */ +static int +lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, + struct lpfc_nodelist *ndlp) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_dmabuf *pcmd; + uint8_t rjt_err, rjt_expl = LSEXP_NOTHING_MORE; + struct fc_rdp_req_frame *rdp_req; + struct lpfc_rdp_context *rdp_context; + IOCB_t *cmd = NULL; + struct ls_rjt stat; + + if (phba->sli_rev < LPFC_SLI_REV4 || + (bf_get(lpfc_sli_intf_if_type, + &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2)) { + rjt_err = LSRJT_UNABLE_TPC; + rjt_expl = LSEXP_REQ_UNSUPPORTED; + goto error; + } + + if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) { + rjt_err = LSRJT_UNABLE_TPC; + rjt_expl = LSEXP_REQ_UNSUPPORTED; + goto error; + } + + pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; + rdp_req = (struct fc_rdp_req_frame *) pcmd->virt; + + + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "2422 ELS RDP Request " + "dec len %d tag x%x port_id %d len %d\n", + be32_to_cpu(rdp_req->rdp_des_length), + be32_to_cpu(rdp_req->nport_id_desc.tag), + be32_to_cpu(rdp_req->nport_id_desc.nport_id), + be32_to_cpu(rdp_req->nport_id_desc.length)); + + if (sizeof(struct fc_rdp_nport_desc) != + be32_to_cpu(rdp_req->rdp_des_length)) + goto rjt_logerr; + if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag)) + goto rjt_logerr; + if (RDP_NPORT_ID_SIZE != + be32_to_cpu(rdp_req->nport_id_desc.length)) + goto rjt_logerr; + rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL); + if (!rdp_context) { + rjt_err = LSRJT_UNABLE_TPC; + goto error; + } + + memset(rdp_context, 0, sizeof(struct lpfc_rdp_context)); + cmd = &cmdiocb->iocb; + rdp_context->ndlp = lpfc_nlp_get(ndlp); + rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id; + rdp_context->rx_id = cmd->ulpContext; + rdp_context->cmpl = lpfc_els_rdp_cmpl; + if (lpfc_get_rdp_info(phba, rdp_context)) { + lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS, + "2423 Unable to send mailbox"); + kfree(rdp_context); + rjt_err = LSRJT_UNABLE_TPC; + lpfc_nlp_put(ndlp); + goto error; + } + + return 0; + +rjt_logerr: + rjt_err = LSRJT_LOGICAL_ERR; + +error: + memset(&stat, 0, sizeof(stat)); + stat.un.b.lsRjtRsnCode = rjt_err; + stat.un.b.lsRjtRsnCodeExp = rjt_expl; + lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL); + return 1; +} + + +static void +lpfc_els_lcb_rsp(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) +{ + MAILBOX_t *mb; + IOCB_t *icmd; + uint8_t *pcmd; + struct lpfc_iocbq *elsiocb; + struct lpfc_nodelist *ndlp; + struct ls_rjt *stat; + union lpfc_sli4_cfg_shdr *shdr; + struct lpfc_lcb_context *lcb_context; + struct fc_lcb_res_frame *lcb_res; + uint32_t cmdsize, shdr_status, shdr_add_status; + int rc; + + mb = &pmb->u.mb; + lcb_context = (struct lpfc_lcb_context *)pmb->context1; + ndlp = lcb_context->ndlp; + pmb->context1 = NULL; + pmb->context2 = NULL; + + shdr = (union lpfc_sli4_cfg_shdr *) + &pmb->u.mqe.un.beacon_config.header.cfg_shdr; + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); + + lpfc_printf_log(phba, KERN_INFO, LOG_MBOX, + "0194 SET_BEACON_CONFIG mailbox " + "completed with status x%x add_status x%x," + " mbx status x%x\n", + shdr_status, shdr_add_status, mb->mbxStatus); + + if (mb->mbxStatus && !(shdr_status && + shdr_add_status == ADD_STATUS_OPERATION_ALREADY_ACTIVE)) { + mempool_free(pmb, phba->mbox_mem_pool); + goto error; + } + + mempool_free(pmb, phba->mbox_mem_pool); + cmdsize = sizeof(struct fc_lcb_res_frame); + elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize, + lpfc_max_els_tries, ndlp, + ndlp->nlp_DID, ELS_CMD_ACC); + + /* Decrement the ndlp reference count from previous mbox command */ + lpfc_nlp_put(ndlp); + + if (!elsiocb) + goto free_lcb_context; + + lcb_res = (struct fc_lcb_res_frame *) + (((struct lpfc_dmabuf *)elsiocb->context2)->virt); + + icmd = &elsiocb->iocb; + icmd->ulpContext = lcb_context->rx_id; + icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id; + + pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt); + *((uint32_t *)(pcmd)) = ELS_CMD_ACC; + lcb_res->lcb_sub_command = lcb_context->sub_command; + lcb_res->lcb_type = lcb_context->type; + lcb_res->lcb_frequency = lcb_context->frequency; + elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp; + phba->fc_stat.elsXmitACC++; + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); + if (rc == IOCB_ERROR) + lpfc_els_free_iocb(phba, elsiocb); + + kfree(lcb_context); + return; + +error: + cmdsize = sizeof(struct fc_lcb_res_frame); + elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize, + lpfc_max_els_tries, ndlp, + ndlp->nlp_DID, ELS_CMD_LS_RJT); + lpfc_nlp_put(ndlp); + if (!elsiocb) + goto free_lcb_context; + + icmd = &elsiocb->iocb; + icmd->ulpContext = lcb_context->rx_id; + icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id; + pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt); + + *((uint32_t *)(pcmd)) = ELS_CMD_LS_RJT; + stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t)); + stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC; + + elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp; + phba->fc_stat.elsXmitLSRJT++; + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0); + if (rc == IOCB_ERROR) + lpfc_els_free_iocb(phba, elsiocb); +free_lcb_context: + kfree(lcb_context); +} + +static int +lpfc_sli4_set_beacon(struct lpfc_vport *vport, + struct lpfc_lcb_context *lcb_context, + uint32_t beacon_state) +{ + struct lpfc_hba *phba = vport->phba; + LPFC_MBOXQ_t *mbox = NULL; + uint32_t len; + int rc; + + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); + if (!mbox) + return 1; + + len = sizeof(struct lpfc_mbx_set_beacon_config) - + sizeof(struct lpfc_sli4_cfg_mhdr); + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_SET_BEACON_CONFIG, len, + LPFC_SLI4_MBX_EMBED); + mbox->context1 = (void *)lcb_context; + mbox->vport = phba->pport; + mbox->mbox_cmpl = lpfc_els_lcb_rsp; + bf_set(lpfc_mbx_set_beacon_port_num, &mbox->u.mqe.un.beacon_config, + phba->sli4_hba.physical_port); + bf_set(lpfc_mbx_set_beacon_state, &mbox->u.mqe.un.beacon_config, + beacon_state); + bf_set(lpfc_mbx_set_beacon_port_type, &mbox->u.mqe.un.beacon_config, 1); + bf_set(lpfc_mbx_set_beacon_duration, &mbox->u.mqe.un.beacon_config, 0); + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) { + mempool_free(mbox, phba->mbox_mem_pool); + return 1; + } + + return 0; +} + + +/** + * lpfc_els_rcv_lcb - Process an unsolicited LCB + * @vport: pointer to a host virtual N_Port data structure. + * @cmdiocb: pointer to lpfc command iocb data structure. + * @ndlp: pointer to a node-list data structure. + * + * This routine processes an unsolicited LCB(LINK CABLE BEACON) IOCB. + * First, the payload of the unsolicited LCB is checked. + * Then based on Subcommand beacon will either turn on or off. + * + * Return code + * 0 - Sent the acc response + * 1 - Sent the reject response. + **/ +static int +lpfc_els_rcv_lcb(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, + struct lpfc_nodelist *ndlp) +{ + struct lpfc_hba *phba = vport->phba; + struct lpfc_dmabuf *pcmd; + uint8_t *lp; + struct fc_lcb_request_frame *beacon; + struct lpfc_lcb_context *lcb_context; + uint8_t state, rjt_err; + struct ls_rjt stat; + + pcmd = (struct lpfc_dmabuf *)cmdiocb->context2; + lp = (uint8_t *)pcmd->virt; + beacon = (struct fc_lcb_request_frame *)pcmd->virt; + + lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, + "0192 ELS LCB Data x%x x%x x%x x%x sub x%x " + "type x%x frequency %x duration x%x\n", + lp[0], lp[1], lp[2], + beacon->lcb_command, + beacon->lcb_sub_command, + beacon->lcb_type, + beacon->lcb_frequency, + be16_to_cpu(beacon->lcb_duration)); + + if (phba->sli_rev < LPFC_SLI_REV4 || + (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != + LPFC_SLI_INTF_IF_TYPE_2)) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + + if (phba->hba_flag & HBA_FCOE_MODE) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + if (beacon->lcb_frequency == 0) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + if ((beacon->lcb_type != LPFC_LCB_GREEN) && + (beacon->lcb_type != LPFC_LCB_AMBER)) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + if ((beacon->lcb_sub_command != LPFC_LCB_ON) && + (beacon->lcb_sub_command != LPFC_LCB_OFF)) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + if ((beacon->lcb_sub_command == LPFC_LCB_ON) && + (beacon->lcb_type != LPFC_LCB_GREEN) && + (beacon->lcb_type != LPFC_LCB_AMBER)) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + if (be16_to_cpu(beacon->lcb_duration) != 0) { + rjt_err = LSRJT_CMD_UNSUPPORTED; + goto rjt; + } + + lcb_context = kmalloc(sizeof(*lcb_context), GFP_KERNEL); + if (!lcb_context) { + rjt_err = LSRJT_UNABLE_TPC; + goto rjt; + } + + state = (beacon->lcb_sub_command == LPFC_LCB_ON) ? 1 : 0; + lcb_context->sub_command = beacon->lcb_sub_command; + lcb_context->type = beacon->lcb_type; + lcb_context->frequency = beacon->lcb_frequency; + lcb_context->ox_id = cmdiocb->iocb.unsli3.rcvsli3.ox_id; + lcb_context->rx_id = cmdiocb->iocb.ulpContext; + lcb_context->ndlp = lpfc_nlp_get(ndlp); + if (lpfc_sli4_set_beacon(vport, lcb_context, state)) { + lpfc_printf_vlog(ndlp->vport, KERN_ERR, + LOG_ELS, "0193 failed to send mail box"); + kfree(lcb_context); + lpfc_nlp_put(ndlp); + rjt_err = LSRJT_UNABLE_TPC; + goto rjt; + } + return 0; +rjt: + memset(&stat, 0, sizeof(stat)); + stat.un.b.lsRjtRsnCode = rjt_err; + lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL); + return 1; +} + + /** * lpfc_els_flush_rscn - Clean up any rscn activities with a vport * @vport: pointer to a host virtual N_Port data structure. @@ -4796,7 +5414,7 @@ lpfc_send_rscn_event(struct lpfc_vport *vport, fc_host_post_vendor_event(shost, fc_get_event_number(), - sizeof(struct lpfc_els_event_header) + payload_len, + sizeof(struct lpfc_rscn_event_header) + payload_len, (char *)rscn_event_data, LPFC_NL_VENDOR_ID); @@ -4833,13 +5451,11 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, struct lpfc_hba *phba = vport->phba; struct lpfc_dmabuf *pcmd; uint32_t *lp, *datap; - IOCB_t *icmd; uint32_t payload_len, length, nportid, *cmd; int rscn_cnt; int rscn_id = 0, hba_id = 0; int i; - icmd = &cmdiocb->iocb; pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; lp = (uint32_t *) pcmd->virt; @@ -5245,6 +5861,13 @@ lpfc_els_rcv_flogi(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, return 1; } + /* send our FLOGI first */ + if (vport->port_state < LPFC_FLOGI) { + vport->fc_myDID = 0; + lpfc_initial_flogi(vport); + vport->fc_myDID = Fabric_DID; + } + /* Send back ACC */ lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL); @@ -5295,12 +5918,10 @@ lpfc_els_rcv_rnid(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, { struct lpfc_dmabuf *pcmd; uint32_t *lp; - IOCB_t *icmd; RNID *rn; struct ls_rjt stat; uint32_t cmd; - icmd = &cmdiocb->iocb; pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; lp = (uint32_t *) pcmd->virt; @@ -5611,7 +6232,6 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, { struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *mbox; - struct lpfc_dmabuf *pcmd; struct ls_rjt stat; if ((ndlp->nlp_state != NLP_STE_UNMAPPED_NODE) && @@ -5619,8 +6239,6 @@ lpfc_els_rcv_rls(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, /* reject the unsolicited RPS request and done with it */ goto reject_out; - pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; - mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC); if (mbox) { lpfc_read_lnk_stat(phba, mbox); @@ -5834,7 +6452,6 @@ lpfc_issue_els_rrq(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_hba *phba = vport->phba; struct RRQ *els_rrq; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; @@ -5853,7 +6470,6 @@ lpfc_issue_els_rrq(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); /* For RRQ request, remainder of payload is Exchange IDs */ @@ -6706,8 +7322,13 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, * Do not process any unsolicited ELS commands * if the ndlp is in DEV_LOSS */ - if (ndlp->nlp_add_flag & NLP_IN_DEV_LOSS) + shost = lpfc_shost_from_vport(vport); + spin_lock_irq(shost->host_lock); + if (ndlp->nlp_flag & NLP_IN_DEV_LOSS) { + spin_unlock_irq(shost->host_lock); goto dropit; + } + spin_unlock_irq(shost->host_lock); elsiocb->context1 = lpfc_nlp_get(ndlp); elsiocb->vport = vport; @@ -6721,6 +7342,15 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, "Data: x%x x%x x%x x%x\n", cmd, did, vport->port_state, vport->fc_flag, vport->fc_myDID, vport->fc_prevDID); + + /* reject till our FLOGI completes */ + if ((vport->port_state < LPFC_FABRIC_CFG_LINK) && + (cmd != ELS_CMD_FLOGI)) { + rjt_err = LSRJT_UNABLE_TPC; + rjt_exp = LSEXP_NOTHING_MORE; + goto lsrjt; + } + switch (cmd) { case ELS_CMD_PLOGI: lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL, @@ -6751,7 +7381,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, rjt_exp = LSEXP_NOTHING_MORE; break; } - shost = lpfc_shost_from_vport(vport); if (vport->port_state < LPFC_DISC_AUTH) { if (!(phba->pport->fc_flag & FC_PT2PT) || (phba->pport->fc_flag & FC_PT2PT_PLOGI)) { @@ -6759,20 +7388,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, rjt_exp = LSEXP_NOTHING_MORE; break; } - /* We get here, and drop thru, if we are PT2PT with - * another NPort and the other side has initiated - * the PLOGI before responding to our FLOGI. - */ - if (phba->sli_rev == LPFC_SLI_REV4 && - (phba->fc_topology_changed || - vport->fc_myDID != vport->fc_prevDID)) { - lpfc_unregister_fcf_prep(phba); - spin_lock_irq(shost->host_lock); - vport->fc_flag &= ~FC_VFI_REGISTERED; - spin_unlock_irq(shost->host_lock); - phba->fc_topology_changed = 0; - lpfc_issue_reg_vfi(vport); - } } spin_lock_irq(shost->host_lock); @@ -6821,6 +7436,14 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, } lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLO); break; + case ELS_CMD_LCB: + phba->fc_stat.elsRcvLCB++; + lpfc_els_rcv_lcb(vport, elsiocb, ndlp); + break; + case ELS_CMD_RDP: + phba->fc_stat.elsRcvRDP++; + lpfc_els_rcv_rdp(vport, elsiocb, ndlp); + break; case ELS_CMD_RSCN: phba->fc_stat.elsRcvRSCN++; lpfc_els_rcv_rscn(vport, elsiocb, ndlp); @@ -6995,6 +7618,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, break; } +lsrjt: /* check if need to LS_RJT received ELS cmd */ if (rjt_err) { memset(&stat, 0, sizeof(stat)); @@ -7586,7 +8210,8 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, lpfc_do_scr_ns_plogi(phba, vport); goto out; fdisc_failed: - lpfc_vport_set_state(vport, FC_VPORT_FAILED); + if (vport->fc_vport->vport_state != FC_VPORT_NO_FABRIC_RSCS) + lpfc_vport_set_state(vport, FC_VPORT_FAILED); /* Cancel discovery timer */ lpfc_can_disctmo(vport); lpfc_nlp_put(ndlp); @@ -7739,8 +8364,10 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (irsp->ulpStatus == IOSTAT_SUCCESS) { spin_lock_irq(shost->host_lock); + vport->fc_flag &= ~FC_NDISC_ACTIVE; vport->fc_flag &= ~FC_FABRIC; spin_unlock_irq(shost->host_lock); + lpfc_can_disctmo(vport); } } @@ -7765,7 +8392,6 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; - IOCB_t *icmd; struct lpfc_iocbq *elsiocb; uint8_t *pcmd; uint16_t cmdsize; @@ -7776,7 +8402,6 @@ lpfc_issue_els_npiv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (!elsiocb) return 1; - icmd = &elsiocb->iocb; pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt); *((uint32_t *) (pcmd)) = ELS_CMD_LOGO; pcmd += sizeof(uint32_t); diff --git a/kernel/drivers/scsi/lpfc/lpfc_hbadisc.c b/kernel/drivers/scsi/lpfc/lpfc_hbadisc.c index 2500f15d4..bfc2442dd 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/kernel/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -106,6 +106,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) struct lpfc_rport_data *rdata; struct lpfc_nodelist * ndlp; struct lpfc_vport *vport; + struct Scsi_Host *shost; struct lpfc_hba *phba; struct lpfc_work_evt *evtp; int put_node; @@ -146,48 +147,32 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) if (ndlp->nlp_state == NLP_STE_MAPPED_NODE) return; - if (ndlp->nlp_type & NLP_FABRIC) { - - /* If the WWPN of the rport and ndlp don't match, ignore it */ - if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, - "6789 rport name %lx != node port name %lx", - (unsigned long)rport->port_name, - (unsigned long)wwn_to_u64( - ndlp->nlp_portname.u.wwn)); - put_node = rdata->pnode != NULL; - put_rport = ndlp->rport != NULL; - rdata->pnode = NULL; - ndlp->rport = NULL; - if (put_node) - lpfc_nlp_put(ndlp); - put_device(&rport->dev); - return; - } - - put_node = rdata->pnode != NULL; - put_rport = ndlp->rport != NULL; - rdata->pnode = NULL; - ndlp->rport = NULL; - if (put_node) - lpfc_nlp_put(ndlp); - if (put_rport) - put_device(&rport->dev); - return; - } + if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn)) + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, + "6789 rport name %llx != node port name %llx", + rport->port_name, + wwn_to_u64(ndlp->nlp_portname.u.wwn)); evtp = &ndlp->dev_loss_evt; - if (!list_empty(&evtp->evt_listp)) + if (!list_empty(&evtp->evt_listp)) { + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, + "6790 rport name %llx dev_loss_evt pending", + rport->port_name); return; + } - evtp->evt_arg1 = lpfc_nlp_get(ndlp); - ndlp->nlp_add_flag |= NLP_IN_DEV_LOSS; + shost = lpfc_shost_from_vport(vport); + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_IN_DEV_LOSS; + spin_unlock_irq(shost->host_lock); - spin_lock_irq(&phba->hbalock); /* We need to hold the node by incrementing the reference * count until this queued work is done */ + evtp->evt_arg1 = lpfc_nlp_get(ndlp); + + spin_lock_irq(&phba->hbalock); if (evtp->evt_arg1) { evtp->evt = LPFC_EVT_DEV_LOSS; list_add_tail(&evtp->evt_listp, &phba->work_list); @@ -215,22 +200,24 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) struct fc_rport *rport; struct lpfc_vport *vport; struct lpfc_hba *phba; + struct Scsi_Host *shost; uint8_t *name; int put_node; - int put_rport; int warn_on = 0; int fcf_inuse = 0; rport = ndlp->rport; + vport = ndlp->vport; + shost = lpfc_shost_from_vport(vport); + + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS; + spin_unlock_irq(shost->host_lock); - if (!rport) { - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; + if (!rport) return fcf_inuse; - } - rdata = rport->dd_data; name = (uint8_t *) &ndlp->nlp_portname; - vport = ndlp->vport; phba = vport->phba; if (phba->sli_rev == LPFC_SLI_REV4) @@ -244,6 +231,13 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) "3182 dev_loss_tmo_handler x%06x, rport %p flg x%x\n", ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag); + /* + * lpfc_nlp_remove if reached with dangling rport drops the + * reference. To make sure that does not happen clear rport + * pointer in ndlp before lpfc_nlp_put. + */ + rdata = rport->dd_data; + /* Don't defer this if we are in the process of deleting the vport * or unloading the driver. The unload will cleanup the node * appropriately we just need to cleanup the ndlp rport info here. @@ -256,14 +250,12 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) ndlp->nlp_sid, 0, LPFC_CTX_TGT); } put_node = rdata->pnode != NULL; - put_rport = ndlp->rport != NULL; rdata->pnode = NULL; ndlp->rport = NULL; - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; if (put_node) lpfc_nlp_put(ndlp); - if (put_rport) - put_device(&rport->dev); + put_device(&rport->dev); + return fcf_inuse; } @@ -275,28 +267,21 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) *name, *(name+1), *(name+2), *(name+3), *(name+4), *(name+5), *(name+6), *(name+7), ndlp->nlp_DID); - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; return fcf_inuse; } - if (ndlp->nlp_type & NLP_FABRIC) { - /* We will clean up these Nodes in linkup */ - put_node = rdata->pnode != NULL; - put_rport = ndlp->rport != NULL; - rdata->pnode = NULL; - ndlp->rport = NULL; - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; - if (put_node) - lpfc_nlp_put(ndlp); - if (put_rport) - put_device(&rport->dev); + put_node = rdata->pnode != NULL; + rdata->pnode = NULL; + ndlp->rport = NULL; + if (put_node) + lpfc_nlp_put(ndlp); + put_device(&rport->dev); + + if (ndlp->nlp_type & NLP_FABRIC) return fcf_inuse; - } if (ndlp->nlp_sid != NLP_NO_SID) { warn_on = 1; - /* flush the target */ - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], ndlp->nlp_sid, 0, LPFC_CTX_TGT); } @@ -321,16 +306,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp) ndlp->nlp_state, ndlp->nlp_rpi); } - put_node = rdata->pnode != NULL; - put_rport = ndlp->rport != NULL; - rdata->pnode = NULL; - ndlp->rport = NULL; - ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS; - if (put_node) - lpfc_nlp_put(ndlp); - if (put_rport) - put_device(&rport->dev); - if (!(vport->load_flag & FC_UNLOADING) && !(ndlp->nlp_flag & NLP_DELAY_TMO) && !(ndlp->nlp_flag & NLP_NPR_2B_DISC) && @@ -726,7 +701,7 @@ lpfc_work_done(struct lpfc_hba *phba) HA_RXMASK)); } } - if ((phba->sli_rev == LPFC_SLI_REV4) & + if ((phba->sli_rev == LPFC_SLI_REV4) && (!list_empty(&pring->txq))) lpfc_drain_txq(phba); /* @@ -825,7 +800,6 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp, *next_ndlp; - int rc; list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { if (!NLP_CHK_NODE_ACT(ndlp)) @@ -841,10 +815,10 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove) if ((phba->sli_rev < LPFC_SLI_REV4) && (!remove && ndlp->nlp_type & NLP_FABRIC)) continue; - rc = lpfc_disc_state_machine(vport, ndlp, NULL, - remove - ? NLP_EVT_DEVICE_RM - : NLP_EVT_DEVICE_RECOVERY); + lpfc_disc_state_machine(vport, ndlp, NULL, + remove + ? NLP_EVT_DEVICE_RM + : NLP_EVT_DEVICE_RECOVERY); } if (phba->sli3_options & LPFC_SLI3_VPORT_TEARDOWN) { if (phba->sli_rev == LPFC_SLI_REV4) @@ -1799,10 +1773,9 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, uint16_t *next_fcf_index) { void *virt_addr; - dma_addr_t phys_addr; struct lpfc_mbx_sge sge; struct lpfc_mbx_read_fcf_tbl *read_fcf; - uint32_t shdr_status, shdr_add_status; + uint32_t shdr_status, shdr_add_status, if_type; union lpfc_sli4_cfg_shdr *shdr; struct fcf_record *new_fcf_record; @@ -1810,7 +1783,6 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, * routine only uses a single SGE. */ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); - phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); if (unlikely(!mboxq->sge_array)) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, "2524 Failed to get the non-embedded SGE " @@ -1823,9 +1795,11 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, lpfc_sli_pcimem_bcopy(shdr, shdr, sizeof(union lpfc_sli4_cfg_shdr)); shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); + if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); if (shdr_status || shdr_add_status) { - if (shdr_status == STATUS_FCF_TABLE_EMPTY) + if (shdr_status == STATUS_FCF_TABLE_EMPTY || + if_type == LPFC_SLI_INTF_IF_TYPE_2) lpfc_printf_log(phba, KERN_ERR, LOG_FIP, "2726 READ_FCF_RECORD Indicates empty " "FCF table.\n"); @@ -3000,7 +2974,8 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) MAILBOX_t *mb = &pmb->u.mb; struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1; struct lpfc_vport *vport = pmb->vport; - + struct serv_parm *sp = &vport->fc_sparam; + uint32_t ed_tov; /* Check for error */ if (mb->mbxStatus) { @@ -3015,6 +2990,18 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) memcpy((uint8_t *) &vport->fc_sparam, (uint8_t *) mp->virt, sizeof (struct serv_parm)); + + ed_tov = be32_to_cpu(sp->cmn.e_d_tov); + if (sp->cmn.edtovResolution) /* E_D_TOV ticks are in nanoseconds */ + ed_tov = (ed_tov + 999999) / 1000000; + + phba->fc_edtov = ed_tov; + phba->fc_ratov = (2 * ed_tov) / 1000; + if (phba->fc_ratov < FF_DEF_RATOV) { + /* RA_TOV should be atleast 10sec for initial flogi */ + phba->fc_ratov = FF_DEF_RATOV; + } + lpfc_update_vport_wwn(vport); if (vport->port_type == LPFC_PHYSICAL_PORT) { memcpy(&phba->wwnn, &vport->fc_nodename, sizeof(phba->wwnn)); @@ -3055,6 +3042,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la) case LPFC_LINK_SPEED_8GHZ: case LPFC_LINK_SPEED_10GHZ: case LPFC_LINK_SPEED_16GHZ: + case LPFC_LINK_SPEED_32GHZ: phba->fc_linkspeed = bf_get(lpfc_mbx_read_top_link_spd, la); break; default: @@ -3868,11 +3856,11 @@ out: if (vport->port_state < LPFC_VPORT_READY) { /* Link up discovery requires Fabric registration. */ - lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); /* Do this first! */ lpfc_ns_cmd(vport, SLI_CTNS_RNN_ID, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0); lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0); + lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); /* Issue SCR just before NameServer GID_FT Query */ lpfc_issue_els_scr(vport, SCR_DID, 0); @@ -3918,9 +3906,17 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * registered port, drop the reference that we took the last time we * registered the port. */ - if (ndlp->rport && ndlp->rport->dd_data && - ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp) - lpfc_nlp_put(ndlp); + rport = ndlp->rport; + if (rport) { + rdata = rport->dd_data; + /* break the link before dropping the ref */ + ndlp->rport = NULL; + if (rdata && rdata->pnode == ndlp) + lpfc_nlp_put(ndlp); + rdata->pnode = NULL; + /* drop reference for earlier registeration */ + put_device(&rport->dev); + } lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT, "rport add: did:x%x flg:x%x type x%x", @@ -4296,9 +4292,9 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (vport->phba->sli_rev == LPFC_SLI_REV4) { lpfc_cleanup_vports_rrqs(vport, ndlp); lpfc_unreg_rpi(vport, ndlp); - } else { - lpfc_nlp_put(ndlp); } + + lpfc_nlp_put(ndlp); return; } @@ -4510,7 +4506,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *mbox; - int rc; + int rc, acc_plogi = 1; uint16_t rpi; if (ndlp->nlp_flag & NLP_RPI_REGISTERED || @@ -4543,14 +4539,20 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) mbox->context1 = lpfc_nlp_get(ndlp); mbox->mbox_cmpl = lpfc_sli4_unreg_rpi_cmpl_clr; + /* + * accept PLOGIs after unreg_rpi_cmpl + */ + acc_plogi = 0; } else mbox->mbox_cmpl = lpfc_sli_def_mbox_cmpl; } rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); - if (rc == MBX_NOT_FINISHED) + if (rc == MBX_NOT_FINISHED) { mempool_free(mbox, phba->mbox_mem_pool); + acc_plogi = 1; + } } lpfc_no_rpi(phba, ndlp); @@ -4558,8 +4560,11 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) ndlp->nlp_rpi = 0; ndlp->nlp_flag &= ~NLP_RPI_REGISTERED; ndlp->nlp_flag &= ~NLP_NPR_ADISC; + if (acc_plogi) + ndlp->nlp_flag &= ~NLP_LOGO_ACC; return 1; } + ndlp->nlp_flag &= ~NLP_LOGO_ACC; return 0; } @@ -4761,6 +4766,7 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata; + struct fc_rport *rport; LPFC_MBOXQ_t *mbox; int rc; @@ -4798,14 +4804,24 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_cleanup_node(vport, ndlp); /* - * We can get here with a non-NULL ndlp->rport because when we - * unregister a rport we don't break the rport/node linkage. So if we - * do, make sure we don't leaving any dangling pointers behind. + * ndlp->rport must be set to NULL before it reaches here + * i.e. break rport/node link before doing lpfc_nlp_put for + * registered rport and then drop the reference of rport. */ if (ndlp->rport) { - rdata = ndlp->rport->dd_data; + /* + * extra lpfc_nlp_put dropped the reference of ndlp + * for registered rport so need to cleanup rport + */ + lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, + "0940 removed node x%p DID x%x " + " rport not null %p\n", + ndlp, ndlp->nlp_DID, ndlp->rport); + rport = ndlp->rport; + rdata = rport->dd_data; rdata->pnode = NULL; ndlp->rport = NULL; + put_device(&rport->dev); } } @@ -4833,9 +4849,19 @@ lpfc_matchdid(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (matchdid.un.b.id == ndlpdid.un.b.id) { if ((mydid.un.b.domain == matchdid.un.b.domain) && (mydid.un.b.area == matchdid.un.b.area)) { + /* This code is supposed to match the ID + * for a private loop device that is + * connect to fl_port. But we need to + * check that the port did not just go + * from pt2pt to fabric or we could end + * up matching ndlp->nlp_DID 000001 to + * fabric DID 0x20101 + */ if ((ndlpdid.un.b.domain == 0) && (ndlpdid.un.b.area == 0)) { - if (ndlpdid.un.b.id) + if (ndlpdid.un.b.id && + vport->phba->fc_topology == + LPFC_TOPOLOGY_LOOP) return 1; } return 0; diff --git a/kernel/drivers/scsi/lpfc/lpfc_hw.h b/kernel/drivers/scsi/lpfc/lpfc_hw.h index 37beb9dc1..2cce88e96 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_hw.h +++ b/kernel/drivers/scsi/lpfc/lpfc_hw.h @@ -33,7 +33,7 @@ #define FF_DEF_EDTOV 2000 /* Default E_D_TOV (2000ms) */ #define FF_DEF_ALTOV 15 /* Default AL_TIME (15ms) */ -#define FF_DEF_RATOV 2 /* Default RA_TOV (2s) */ +#define FF_DEF_RATOV 10 /* Default RA_TOV (10s) */ #define FF_DEF_ARBTOV 1900 /* Default ARB_TOV (1900ms) */ #define LPFC_BUF_RING0 64 /* Number of buffers to post to RING @@ -543,6 +543,7 @@ struct fc_vft_header { #define ELS_CMD_TEST 0x11000000 #define ELS_CMD_RRQ 0x12000000 #define ELS_CMD_REC 0x13000000 +#define ELS_CMD_RDP 0x18000000 #define ELS_CMD_PRLI 0x20100014 #define ELS_CMD_PRLO 0x21100014 #define ELS_CMD_PRLO_ACC 0x02100014 @@ -558,6 +559,7 @@ struct fc_vft_header { #define ELS_CMD_SCR 0x62000000 #define ELS_CMD_RNID 0x78000000 #define ELS_CMD_LIRR 0x7A000000 +#define ELS_CMD_LCB 0x81000000 #else /* __LITTLE_ENDIAN_BITFIELD */ #define ELS_CMD_MASK 0xffff #define ELS_RSP_MASK 0xff @@ -580,6 +582,7 @@ struct fc_vft_header { #define ELS_CMD_TEST 0x11 #define ELS_CMD_RRQ 0x12 #define ELS_CMD_REC 0x13 +#define ELS_CMD_RDP 0x18 #define ELS_CMD_PRLI 0x14001020 #define ELS_CMD_PRLO 0x14001021 #define ELS_CMD_PRLO_ACC 0x14001002 @@ -595,6 +598,7 @@ struct fc_vft_header { #define ELS_CMD_SCR 0x62 #define ELS_CMD_RNID 0x78 #define ELS_CMD_LIRR 0x7A +#define ELS_CMD_LCB 0x81 #endif /* @@ -1010,6 +1014,198 @@ typedef struct _ELS_PKT { /* Structure is in Big Endian format */ } un; } ELS_PKT; +/* + * Link Cable Beacon (LCB) ELS Frame + */ + +struct fc_lcb_request_frame { + uint32_t lcb_command; /* ELS command opcode (0x81) */ + uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */ +#define LPFC_LCB_ON 0x1 +#define LPFC_LCB_OFF 0x2 + uint8_t reserved[3]; + + uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */ +#define LPFC_LCB_GREEN 0x1 +#define LPFC_LCB_AMBER 0x2 + uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */ + uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */ +}; + +/* + * Link Cable Beacon (LCB) ELS Response Frame + */ +struct fc_lcb_res_frame { + uint32_t lcb_ls_acc; /* Acceptance of LCB request (0x02) */ + uint8_t lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */ + uint8_t reserved[3]; + uint8_t lcb_type; /* LCB Payload Word 2, bit 24:31 */ + uint8_t lcb_frequency; /* LCB Payload Word 2, bit 16:23 */ + uint16_t lcb_duration; /* LCB Payload Word 2, bit 15:0 */ +}; + +/* + * Read Diagnostic Parameters (RDP) ELS frame. + */ +#define SFF_PG0_IDENT_SFP 0x3 + +#define SFP_FLAG_PT_OPTICAL 0x0 +#define SFP_FLAG_PT_SWLASER 0x01 +#define SFP_FLAG_PT_LWLASER_LC1310 0x02 +#define SFP_FLAG_PT_LWLASER_LL1550 0x03 +#define SFP_FLAG_PT_MASK 0x0F +#define SFP_FLAG_PT_SHIFT 0 + +#define SFP_FLAG_IS_OPTICAL_PORT 0x01 +#define SFP_FLAG_IS_OPTICAL_MASK 0x010 +#define SFP_FLAG_IS_OPTICAL_SHIFT 4 + +#define SFP_FLAG_IS_DESC_VALID 0x01 +#define SFP_FLAG_IS_DESC_VALID_MASK 0x020 +#define SFP_FLAG_IS_DESC_VALID_SHIFT 5 + +#define SFP_FLAG_CT_UNKNOWN 0x0 +#define SFP_FLAG_CT_SFP_PLUS 0x01 +#define SFP_FLAG_CT_MASK 0x3C +#define SFP_FLAG_CT_SHIFT 6 + +struct fc_rdp_port_name_info { + uint8_t wwnn[8]; + uint8_t wwpn[8]; +}; + + +/* + * Link Error Status Block Structure (FC-FS-3) for RDP + * This similar to RPS ELS + */ +struct fc_link_status { + uint32_t link_failure_cnt; + uint32_t loss_of_synch_cnt; + uint32_t loss_of_signal_cnt; + uint32_t primitive_seq_proto_err; + uint32_t invalid_trans_word; + uint32_t invalid_crc_cnt; + +}; + +#define RDP_PORT_NAMES_DESC_TAG 0x00010003 +struct fc_rdp_port_name_desc { + uint32_t tag; /* 0001 0003h */ + uint32_t length; /* set to size of payload struct */ + struct fc_rdp_port_name_info port_names; +}; + + +struct fc_rdp_link_error_status_payload_info { + struct fc_link_status link_status; /* 24 bytes */ + uint32_t port_type; /* bits 31-30 only */ +}; + +#define RDP_LINK_ERROR_STATUS_DESC_TAG 0x00010002 +struct fc_rdp_link_error_status_desc { + uint32_t tag; /* 0001 0002h */ + uint32_t length; /* set to size of payload struct */ + struct fc_rdp_link_error_status_payload_info info; +}; + +#define VN_PT_PHY_UNKNOWN 0x00 +#define VN_PT_PHY_PF_PORT 0x01 +#define VN_PT_PHY_ETH_MAC 0x10 +#define VN_PT_PHY_SHIFT 30 + +#define RDP_PS_1GB 0x8000 +#define RDP_PS_2GB 0x4000 +#define RDP_PS_4GB 0x2000 +#define RDP_PS_10GB 0x1000 +#define RDP_PS_8GB 0x0800 +#define RDP_PS_16GB 0x0400 +#define RDP_PS_32GB 0x0200 + +#define RDP_CAP_UNKNOWN 0x0001 +#define RDP_PS_UNKNOWN 0x0002 +#define RDP_PS_NOT_ESTABLISHED 0x0001 + +struct fc_rdp_port_speed { + uint16_t capabilities; + uint16_t speed; +}; + +struct fc_rdp_port_speed_info { + struct fc_rdp_port_speed port_speed; +}; + +#define RDP_PORT_SPEED_DESC_TAG 0x00010001 +struct fc_rdp_port_speed_desc { + uint32_t tag; /* 00010001h */ + uint32_t length; /* set to size of payload struct */ + struct fc_rdp_port_speed_info info; +}; + +#define RDP_NPORT_ID_SIZE 4 +#define RDP_N_PORT_DESC_TAG 0x00000003 +struct fc_rdp_nport_desc { + uint32_t tag; /* 0000 0003h, big endian */ + uint32_t length; /* size of RDP_N_PORT_ID struct */ + uint32_t nport_id : 12; + uint32_t reserved : 8; +}; + + +struct fc_rdp_link_service_info { + uint32_t els_req; /* Request payload word 0 value.*/ +}; + +#define RDP_LINK_SERVICE_DESC_TAG 0x00000001 +struct fc_rdp_link_service_desc { + uint32_t tag; /* Descriptor tag 1 */ + uint32_t length; /* set to size of payload struct. */ + struct fc_rdp_link_service_info payload; + /* must be ELS req Word 0(0x18) */ +}; + +struct fc_rdp_sfp_info { + uint16_t temperature; + uint16_t vcc; + uint16_t tx_bias; + uint16_t tx_power; + uint16_t rx_power; + uint16_t flags; +}; + +#define RDP_SFP_DESC_TAG 0x00010000 +struct fc_rdp_sfp_desc { + uint32_t tag; + uint32_t length; /* set to size of sfp_info struct */ + struct fc_rdp_sfp_info sfp_info; +}; + +struct fc_rdp_req_frame { + uint32_t rdp_command; /* ELS command opcode (0x18)*/ + uint32_t rdp_des_length; /* RDP Payload Word 1 */ + struct fc_rdp_nport_desc nport_id_desc; /* RDP Payload Word 2 - 4 */ +}; + + +struct fc_rdp_res_frame { + uint32_t reply_sequence; /* FC word0 LS_ACC or LS_RJT */ + uint32_t length; /* FC Word 1 */ + struct fc_rdp_link_service_desc link_service_desc; /* Word 2 -4 */ + struct fc_rdp_sfp_desc sfp_desc; /* Word 5 -9 */ + struct fc_rdp_port_speed_desc portspeed_desc; /* Word 10-12 */ + struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13-21 */ + struct fc_rdp_port_name_desc diag_port_names_desc; /* Word 22-27 */ + struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28-33 */ +}; + + +#define RDP_DESC_PAYLOAD_SIZE (sizeof(struct fc_rdp_link_service_desc) \ + + sizeof(struct fc_rdp_sfp_desc) \ + + sizeof(struct fc_rdp_port_speed_desc) \ + + sizeof(struct fc_rdp_link_error_status_desc) \ + + (sizeof(struct fc_rdp_port_name_desc) * 2)) + + /******** FDMI ********/ /* lpfc_sli_ct_request defines the CT_IU preamble for FDMI commands */ @@ -1204,6 +1400,7 @@ struct lpfc_fdmi_reg_portattr { #define PCI_DEVICE_ID_LANCER_FC_VF 0xe208 #define PCI_DEVICE_ID_LANCER_FCOE 0xe260 #define PCI_DEVICE_ID_LANCER_FCOE_VF 0xe268 +#define PCI_DEVICE_ID_LANCER_G6_FC 0xe300 #define PCI_DEVICE_ID_SAT_SMB 0xf011 #define PCI_DEVICE_ID_SAT_MID 0xf015 #define PCI_DEVICE_ID_RFLY 0xf095 @@ -1586,6 +1783,11 @@ typedef struct { /* FireFly BIU registers */ #define TEMPERATURE_OFFSET 0xB0 /* Slim offset for critical temperature event */ +/* + * return code Fail + */ +#define FAILURE 1 + /* * Begin Structure Definitions for Mailbox Commands */ @@ -1874,6 +2076,7 @@ typedef struct { #define LINK_SPEED_8G 0x8 /* 8 Gigabaud */ #define LINK_SPEED_10G 0x10 /* 10 Gigabaud */ #define LINK_SPEED_16G 0x11 /* 16 Gigabaud */ +#define LINK_SPEED_32G 0x14 /* 32 Gigabaud */ } INIT_LINK_VAR; @@ -2045,6 +2248,7 @@ typedef struct { #define LMT_8Gb 0x080 #define LMT_10Gb 0x100 #define LMT_16Gb 0x200 +#define LMT_32Gb 0x400 uint32_t rsvd2; uint32_t rsvd3; uint32_t max_xri; @@ -2526,6 +2730,7 @@ struct lpfc_mbx_read_top { #define LPFC_LINK_SPEED_8GHZ 0x20 #define LPFC_LINK_SPEED_10GHZ 0x40 #define LPFC_LINK_SPEED_16GHZ 0x80 +#define LPFC_LINK_SPEED_32GHZ 0x90 }; /* Structure for MB Command CLEAR_LA (22) */ diff --git a/kernel/drivers/scsi/lpfc/lpfc_hw4.h b/kernel/drivers/scsi/lpfc/lpfc_hw4.h index 1813c4594..33ec4fa39 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_hw4.h +++ b/kernel/drivers/scsi/lpfc/lpfc_hw4.h @@ -291,7 +291,7 @@ struct sli4_bls_rsp { struct lpfc_eqe { uint32_t word0; #define lpfc_eqe_resource_id_SHIFT 16 -#define lpfc_eqe_resource_id_MASK 0x000000FF +#define lpfc_eqe_resource_id_MASK 0x0000FFFF #define lpfc_eqe_resource_id_WORD word0 #define lpfc_eqe_minor_code_SHIFT 4 #define lpfc_eqe_minor_code_MASK 0x00000FFF @@ -914,6 +914,8 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D #define LPFC_MBOX_OPCODE_SET_PHYSICAL_LINK_CONFIG 0x3E #define LPFC_MBOX_OPCODE_SET_BOOT_CONFIG 0x43 +#define LPFC_MBOX_OPCODE_SET_BEACON_CONFIG 0x45 +#define LPFC_MBOX_OPCODE_GET_BEACON_CONFIG 0x46 #define LPFC_MBOX_OPCODE_GET_PORT_NAME 0x4D #define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A #define LPFC_MBOX_OPCODE_GET_VPD_DATA 0x5B @@ -1479,6 +1481,26 @@ struct lpfc_mbx_query_fw_config { } rsp; }; +struct lpfc_mbx_set_beacon_config { + struct mbox_header header; + uint32_t word4; +#define lpfc_mbx_set_beacon_port_num_SHIFT 0 +#define lpfc_mbx_set_beacon_port_num_MASK 0x0000003F +#define lpfc_mbx_set_beacon_port_num_WORD word4 +#define lpfc_mbx_set_beacon_port_type_SHIFT 6 +#define lpfc_mbx_set_beacon_port_type_MASK 0x00000003 +#define lpfc_mbx_set_beacon_port_type_WORD word4 +#define lpfc_mbx_set_beacon_state_SHIFT 8 +#define lpfc_mbx_set_beacon_state_MASK 0x000000FF +#define lpfc_mbx_set_beacon_state_WORD word4 +#define lpfc_mbx_set_beacon_duration_SHIFT 16 +#define lpfc_mbx_set_beacon_duration_MASK 0x000000FF +#define lpfc_mbx_set_beacon_duration_WORD word4 +#define lpfc_mbx_set_beacon_status_duration_SHIFT 24 +#define lpfc_mbx_set_beacon_status_duration_MASK 0x000000FF +#define lpfc_mbx_set_beacon_status_duration_WORD word4 +}; + struct lpfc_id_range { uint32_t word5; #define lpfc_mbx_rsrc_id_word4_0_SHIFT 0 @@ -1921,6 +1943,12 @@ struct lpfc_mbx_redisc_fcf_tbl { #define STATUS_FCF_IN_USE 0x3a #define STATUS_FCF_TABLE_EMPTY 0x43 +/* + * Additional status field for embedded SLI_CONFIG mailbox + * command. + */ +#define ADD_STATUS_OPERATION_ALREADY_ACTIVE 0x67 + struct lpfc_mbx_sli4_config { struct mbox_header header; }; @@ -2433,6 +2461,205 @@ struct lpfc_mbx_supp_pages { #define LPFC_SLI4_PARAMETERS 2 }; +struct lpfc_mbx_memory_dump_type3 { + uint32_t word1; +#define lpfc_mbx_memory_dump_type3_type_SHIFT 0 +#define lpfc_mbx_memory_dump_type3_type_MASK 0x0000000f +#define lpfc_mbx_memory_dump_type3_type_WORD word1 +#define lpfc_mbx_memory_dump_type3_link_SHIFT 24 +#define lpfc_mbx_memory_dump_type3_link_MASK 0x000000ff +#define lpfc_mbx_memory_dump_type3_link_WORD word1 + uint32_t word2; +#define lpfc_mbx_memory_dump_type3_page_no_SHIFT 0 +#define lpfc_mbx_memory_dump_type3_page_no_MASK 0x0000ffff +#define lpfc_mbx_memory_dump_type3_page_no_WORD word2 +#define lpfc_mbx_memory_dump_type3_offset_SHIFT 16 +#define lpfc_mbx_memory_dump_type3_offset_MASK 0x0000ffff +#define lpfc_mbx_memory_dump_type3_offset_WORD word2 + uint32_t word3; +#define lpfc_mbx_memory_dump_type3_length_SHIFT 0 +#define lpfc_mbx_memory_dump_type3_length_MASK 0x00ffffff +#define lpfc_mbx_memory_dump_type3_length_WORD word3 + uint32_t addr_lo; + uint32_t addr_hi; + uint32_t return_len; +}; + +#define DMP_PAGE_A0 0xa0 +#define DMP_PAGE_A2 0xa2 +#define DMP_SFF_PAGE_A0_SIZE 256 +#define DMP_SFF_PAGE_A2_SIZE 256 + +#define SFP_WAVELENGTH_LC1310 1310 +#define SFP_WAVELENGTH_LL1550 1550 + + +/* + * * SFF-8472 TABLE 3.4 + * */ +#define SFF_PG0_CONNECTOR_UNKNOWN 0x00 /* Unknown */ +#define SFF_PG0_CONNECTOR_SC 0x01 /* SC */ +#define SFF_PG0_CONNECTOR_FC_COPPER1 0x02 /* FC style 1 copper connector */ +#define SFF_PG0_CONNECTOR_FC_COPPER2 0x03 /* FC style 2 copper connector */ +#define SFF_PG0_CONNECTOR_BNC 0x04 /* BNC / TNC */ +#define SFF_PG0_CONNECTOR__FC_COAX 0x05 /* FC coaxial headers */ +#define SFF_PG0_CONNECTOR_FIBERJACK 0x06 /* FiberJack */ +#define SFF_PG0_CONNECTOR_LC 0x07 /* LC */ +#define SFF_PG0_CONNECTOR_MT 0x08 /* MT - RJ */ +#define SFF_PG0_CONNECTOR_MU 0x09 /* MU */ +#define SFF_PG0_CONNECTOR_SF 0x0A /* SG */ +#define SFF_PG0_CONNECTOR_OPTICAL_PIGTAIL 0x0B /* Optical pigtail */ +#define SFF_PG0_CONNECTOR_OPTICAL_PARALLEL 0x0C /* MPO Parallel Optic */ +#define SFF_PG0_CONNECTOR_HSSDC_II 0x20 /* HSSDC II */ +#define SFF_PG0_CONNECTOR_COPPER_PIGTAIL 0x21 /* Copper pigtail */ +#define SFF_PG0_CONNECTOR_RJ45 0x22 /* RJ45 */ + +/* SFF-8472 Table 3.1 Diagnostics: Data Fields Address/Page A0 */ + +#define SSF_IDENTIFIER 0 +#define SSF_EXT_IDENTIFIER 1 +#define SSF_CONNECTOR 2 +#define SSF_TRANSCEIVER_CODE_B0 3 +#define SSF_TRANSCEIVER_CODE_B1 4 +#define SSF_TRANSCEIVER_CODE_B2 5 +#define SSF_TRANSCEIVER_CODE_B3 6 +#define SSF_TRANSCEIVER_CODE_B4 7 +#define SSF_TRANSCEIVER_CODE_B5 8 +#define SSF_TRANSCEIVER_CODE_B6 9 +#define SSF_TRANSCEIVER_CODE_B7 10 +#define SSF_ENCODING 11 +#define SSF_BR_NOMINAL 12 +#define SSF_RATE_IDENTIFIER 13 +#define SSF_LENGTH_9UM_KM 14 +#define SSF_LENGTH_9UM 15 +#define SSF_LENGTH_50UM_OM2 16 +#define SSF_LENGTH_62UM_OM1 17 +#define SFF_LENGTH_COPPER 18 +#define SSF_LENGTH_50UM_OM3 19 +#define SSF_VENDOR_NAME 20 +#define SSF_VENDOR_OUI 36 +#define SSF_VENDOR_PN 40 +#define SSF_VENDOR_REV 56 +#define SSF_WAVELENGTH_B1 60 +#define SSF_WAVELENGTH_B0 61 +#define SSF_CC_BASE 63 +#define SSF_OPTIONS_B1 64 +#define SSF_OPTIONS_B0 65 +#define SSF_BR_MAX 66 +#define SSF_BR_MIN 67 +#define SSF_VENDOR_SN 68 +#define SSF_DATE_CODE 84 +#define SSF_MONITORING_TYPEDIAGNOSTIC 92 +#define SSF_ENHANCED_OPTIONS 93 +#define SFF_8472_COMPLIANCE 94 +#define SSF_CC_EXT 95 +#define SSF_A0_VENDOR_SPECIFIC 96 + +/* SFF-8472 Table 3.1a Diagnostics: Data Fields Address/Page A2 */ + +#define SSF_AW_THRESHOLDS 0 +#define SSF_EXT_CAL_CONSTANTS 56 +#define SSF_CC_DMI 95 +#define SFF_TEMPERATURE_B1 96 +#define SFF_TEMPERATURE_B0 97 +#define SFF_VCC_B1 98 +#define SFF_VCC_B0 99 +#define SFF_TX_BIAS_CURRENT_B1 100 +#define SFF_TX_BIAS_CURRENT_B0 101 +#define SFF_TXPOWER_B1 102 +#define SFF_TXPOWER_B0 103 +#define SFF_RXPOWER_B1 104 +#define SFF_RXPOWER_B0 105 +#define SSF_STATUS_CONTROL 110 +#define SSF_ALARM_FLAGS_B1 112 +#define SSF_ALARM_FLAGS_B0 113 +#define SSF_WARNING_FLAGS_B1 116 +#define SSF_WARNING_FLAGS_B0 117 +#define SSF_EXT_TATUS_CONTROL_B1 118 +#define SSF_EXT_TATUS_CONTROL_B0 119 +#define SSF_A2_VENDOR_SPECIFIC 120 +#define SSF_USER_EEPROM 128 +#define SSF_VENDOR_CONTROL 148 + + +/* + * Tranceiver codes Fibre Channel SFF-8472 + * Table 3.5. + */ + +struct sff_trasnceiver_codes_byte0 { + uint8_t inifiband:4; + uint8_t teng_ethernet:4; +}; + +struct sff_trasnceiver_codes_byte1 { + uint8_t sonet:6; + uint8_t escon:2; +}; + +struct sff_trasnceiver_codes_byte2 { + uint8_t soNet:8; +}; + +struct sff_trasnceiver_codes_byte3 { + uint8_t ethernet:8; +}; + +struct sff_trasnceiver_codes_byte4 { + uint8_t fc_el_lo:1; + uint8_t fc_lw_laser:1; + uint8_t fc_sw_laser:1; + uint8_t fc_md_distance:1; + uint8_t fc_lg_distance:1; + uint8_t fc_int_distance:1; + uint8_t fc_short_distance:1; + uint8_t fc_vld_distance:1; +}; + +struct sff_trasnceiver_codes_byte5 { + uint8_t reserved1:1; + uint8_t reserved2:1; + uint8_t fc_sfp_active:1; /* Active cable */ + uint8_t fc_sfp_passive:1; /* Passive cable */ + uint8_t fc_lw_laser:1; /* Longwave laser */ + uint8_t fc_sw_laser_sl:1; + uint8_t fc_sw_laser_sn:1; + uint8_t fc_el_hi:1; /* Electrical enclosure high bit */ +}; + +struct sff_trasnceiver_codes_byte6 { + uint8_t fc_tm_sm:1; /* Single Mode */ + uint8_t reserved:1; + uint8_t fc_tm_m6:1; /* Multimode, 62.5um (M6) */ + uint8_t fc_tm_tv:1; /* Video Coax (TV) */ + uint8_t fc_tm_mi:1; /* Miniature Coax (MI) */ + uint8_t fc_tm_tp:1; /* Twisted Pair (TP) */ + uint8_t fc_tm_tw:1; /* Twin Axial Pair */ +}; + +struct sff_trasnceiver_codes_byte7 { + uint8_t fc_sp_100MB:1; /* 100 MB/sec */ + uint8_t reserve:1; + uint8_t fc_sp_200mb:1; /* 200 MB/sec */ + uint8_t fc_sp_3200MB:1; /* 3200 MB/sec */ + uint8_t fc_sp_400MB:1; /* 400 MB/sec */ + uint8_t fc_sp_1600MB:1; /* 1600 MB/sec */ + uint8_t fc_sp_800MB:1; /* 800 MB/sec */ + uint8_t fc_sp_1200MB:1; /* 1200 MB/sec */ +}; + +/* User writable non-volatile memory, SFF-8472 Table 3.20 */ +struct user_eeprom { + uint8_t vendor_name[16]; + uint8_t vendor_oui[3]; + uint8_t vendor_pn[816]; + uint8_t vendor_rev[4]; + uint8_t vendor_sn[16]; + uint8_t datecode[6]; + uint8_t lot_code[2]; + uint8_t reserved191[57]; +}; + struct lpfc_mbx_pc_sli4_params { uint32_t word1; #define qs_SHIFT 0 @@ -3021,6 +3248,7 @@ struct lpfc_mqe { struct lpfc_mbx_request_features req_ftrs; struct lpfc_mbx_post_hdr_tmpl hdr_tmpl; struct lpfc_mbx_query_fw_config query_fw_cfg; + struct lpfc_mbx_set_beacon_config beacon_config; struct lpfc_mbx_supp_pages supp_pages; struct lpfc_mbx_pc_sli4_params sli4_params; struct lpfc_mbx_get_sli4_parameters get_sli4_parameters; @@ -3031,6 +3259,7 @@ struct lpfc_mqe { struct lpfc_mbx_get_prof_cfg get_prof_cfg; struct lpfc_mbx_wr_object wr_object; struct lpfc_mbx_get_port_name get_port_name; + struct lpfc_mbx_memory_dump_type3 mem_dump_type3; struct lpfc_mbx_nop nop; } un; }; @@ -3041,8 +3270,8 @@ struct lpfc_mcqe { #define lpfc_mcqe_status_MASK 0x0000FFFF #define lpfc_mcqe_status_WORD word0 #define lpfc_mcqe_ext_status_SHIFT 16 -#define lpfc_mcqe_ext_status_MASK 0x0000FFFF -#define lpfc_mcqe_ext_status_WORD word0 +#define lpfc_mcqe_ext_status_MASK 0x0000FFFF +#define lpfc_mcqe_ext_status_WORD word0 uint32_t mcqe_tag0; uint32_t mcqe_tag1; uint32_t trailer; @@ -3176,6 +3405,7 @@ struct lpfc_acqe_fc_la { #define LPFC_FC_LA_SPEED_8G 0x8 #define LPFC_FC_LA_SPEED_10G 0xA #define LPFC_FC_LA_SPEED_16G 0x10 +#define LPFC_FC_LA_SPEED_32G 0x20 #define lpfc_acqe_fc_la_topology_SHIFT 16 #define lpfc_acqe_fc_la_topology_MASK 0x000000FF #define lpfc_acqe_fc_la_topology_WORD word0 diff --git a/kernel/drivers/scsi/lpfc/lpfc_init.c b/kernel/drivers/scsi/lpfc/lpfc_init.c index e8c8c1ecc..db9446c61 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_init.c +++ b/kernel/drivers/scsi/lpfc/lpfc_init.c @@ -699,7 +699,9 @@ lpfc_hba_init_link_fc_topology(struct lpfc_hba *phba, uint32_t fc_topology, ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_10G) && !(phba->lmt & LMT_10Gb)) || ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_16G) && - !(phba->lmt & LMT_16Gb))) { + !(phba->lmt & LMT_16Gb)) || + ((phba->cfg_link_speed == LPFC_USER_LINK_SPEED_32G) && + !(phba->lmt & LMT_32Gb))) { /* Reset link speed to auto */ lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1302 Invalid speed for this board:%d " @@ -2035,7 +2037,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) && descp && descp[0] != '\0') return; - if (phba->lmt & LMT_16Gb) + if (phba->lmt & LMT_32Gb) + max_speed = 32; + else if (phba->lmt & LMT_16Gb) max_speed = 16; else if (phba->lmt & LMT_10Gb) max_speed = 10; @@ -2229,6 +2233,9 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) m = (typeof(m)){"OCe15100", "PCIe", "Obsolete, Unsupported FCoE"}; break; + case PCI_DEVICE_ID_LANCER_G6_FC: + m = (typeof(m)){"LPe32000", "PCIe", "Fibre Channel Adapter"}; + break; case PCI_DEVICE_ID_SKYHAWK: case PCI_DEVICE_ID_SKYHAWK_VF: oneConnect = 1; @@ -2253,7 +2260,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) phba->Port); else if (max_speed == 0) snprintf(descp, 255, - "Emulex %s %s %s ", + "Emulex %s %s %s", m.name, m.bus, m.function); else snprintf(descp, 255, @@ -3303,6 +3310,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_lun = vport->cfg_max_luns; shost->this_id = -1; shost->max_cmd_len = 16; + shost->nr_hw_queues = phba->cfg_fcp_io_channel; if (phba->sli_rev == LPFC_SLI_REV4) { shost->dma_boundary = phba->sli4_hba.pc_sli4_params.sge_supp_len-1; @@ -3490,6 +3498,8 @@ void lpfc_host_attrib_init(struct Scsi_Host *shost) sizeof fc_host_symbolic_name(shost)); fc_host_supported_speeds(shost) = 0; + if (phba->lmt & LMT_32Gb) + fc_host_supported_speeds(shost) |= FC_PORTSPEED_32GBIT; if (phba->lmt & LMT_16Gb) fc_host_supported_speeds(shost) |= FC_PORTSPEED_16GBIT; if (phba->lmt & LMT_10Gb) @@ -3853,6 +3863,9 @@ lpfc_sli4_port_speed_parse(struct lpfc_hba *phba, uint32_t evt_code, case LPFC_FC_LA_SPEED_16G: port_speed = 16000; break; + case LPFC_FC_LA_SPEED_32G: + port_speed = 32000; + break; default: port_speed = 0; } @@ -4483,7 +4496,13 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, lpfc_destroy_vport_work_array(phba, vports); } - if (active_vlink_present) { + /* + * Don't re-instantiate if vport is marked for deletion. + * If we are here first then vport_delete is going to wait + * for discovery to complete. + */ + if (!(vport->load_flag & FC_UNLOADING) && + active_vlink_present) { /* * If there are other active VLinks present, * re-instantiate the Vlink using FDISC. @@ -4975,8 +4994,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) } if (!phba->sli.ring) - phba->sli.ring = (struct lpfc_sli_ring *) - kzalloc(LPFC_SLI3_MAX_RING * + phba->sli.ring = kzalloc(LPFC_SLI3_MAX_RING * sizeof(struct lpfc_sli_ring), GFP_KERNEL); if (!phba->sli.ring) return -ENOMEM; @@ -4988,7 +5006,7 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) /* Initialize the host templates the configured values. */ lpfc_vport_template.sg_tablesize = phba->cfg_sg_seg_cnt; - lpfc_template.sg_tablesize = phba->cfg_sg_seg_cnt; + lpfc_template_s3.sg_tablesize = phba->cfg_sg_seg_cnt; /* There are going to be 2 reserved BDEs: 1 FCP cmnd + 1 FCP rsp */ if (phba->cfg_enable_bg) { @@ -7500,6 +7518,8 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba) mboxq->u.mqe.un.query_fw_cfg.rsp.function_mode; phba->sli4_hba.ulp0_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp0_mode; phba->sli4_hba.ulp1_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp1_mode; + phba->sli4_hba.physical_port = + mboxq->u.mqe.un.query_fw_cfg.rsp.physical_port; lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "3251 QUERY_FW_CFG: func_mode:x%x, ulp0_mode:x%x, " "ulp1_mode:x%x\n", phba->sli4_hba.fw_func_mode, @@ -8367,7 +8387,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba) /* vector-0 is associated to slow-path handler */ rc = request_irq(phba->msix_entries[0].vector, - &lpfc_sli_sp_intr_handler, IRQF_SHARED, + &lpfc_sli_sp_intr_handler, 0, LPFC_SP_DRIVER_HANDLER_NAME, phba); if (rc) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, @@ -8378,7 +8398,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba) /* vector-1 is associated to fast-path handler */ rc = request_irq(phba->msix_entries[1].vector, - &lpfc_sli_fp_intr_handler, IRQF_SHARED, + &lpfc_sli_fp_intr_handler, 0, LPFC_FP_DRIVER_HANDLER_NAME, phba); if (rc) { @@ -8487,7 +8507,7 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba) } rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler, - IRQF_SHARED, LPFC_DRIVER_NAME, phba); + 0, LPFC_DRIVER_NAME, phba); if (rc) { pci_disable_msi(phba->pcidev); lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, @@ -8670,7 +8690,6 @@ lpfc_sli4_set_affinity(struct lpfc_hba *phba, int vectors) #ifdef CONFIG_X86 struct cpuinfo_x86 *cpuinfo; #endif - struct cpumask *mask; uint8_t chann[LPFC_FCP_IO_CHAN_MAX+1]; /* If there is no mapping, just return */ @@ -8764,11 +8783,8 @@ found: first_cpu = cpu; /* Now affinitize to the selected CPU */ - mask = &cpup->maskbits; - cpumask_clear(mask); - cpumask_set_cpu(cpu, mask); i = irq_set_affinity_hint(phba->sli4_hba.msix_entries[idx]. - vector, mask); + vector, get_cpu_mask(cpu)); lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "3330 Set Affinity: CPU %d channel %d " @@ -8944,13 +8960,13 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) if (phba->cfg_fof && (index == (vectors - 1))) rc = request_irq( phba->sli4_hba.msix_entries[index].vector, - &lpfc_sli4_fof_intr_handler, IRQF_SHARED, + &lpfc_sli4_fof_intr_handler, 0, (char *)&phba->sli4_hba.handler_name[index], &phba->sli4_hba.fcp_eq_hdl[index]); else rc = request_irq( phba->sli4_hba.msix_entries[index].vector, - &lpfc_sli4_hba_intr_handler, IRQF_SHARED, + &lpfc_sli4_hba_intr_handler, 0, (char *)&phba->sli4_hba.handler_name[index], &phba->sli4_hba.fcp_eq_hdl[index]); if (rc) { @@ -8972,7 +8988,8 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) phba->cfg_fcp_io_channel = vectors; } - lpfc_sli4_set_affinity(phba, vectors); + if (!shost_use_blk_mq(lpfc_shost_from_vport(phba->pport))) + lpfc_sli4_set_affinity(phba, vectors); return rc; cfg_fail_out: @@ -9050,7 +9067,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba) } rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler, - IRQF_SHARED, LPFC_DRIVER_NAME, phba); + 0, LPFC_DRIVER_NAME, phba); if (rc) { pci_disable_msi(phba->pcidev); lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, @@ -10277,7 +10294,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; struct Scsi_Host *shost = NULL; - int error, ret; + int error; uint32_t cfg_mode, intr_mode; int adjusted_fcp_io_channel; @@ -10401,7 +10418,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* check for firmware upgrade or downgrade */ if (phba->cfg_request_firmware_upgrade) - ret = lpfc_sli4_request_firmware_update(phba, INT_FW_UPGRADE); + lpfc_sli4_request_firmware_update(phba, INT_FW_UPGRADE); /* Check if there are static vports to be created. */ lpfc_create_static_vport(phba); @@ -11344,6 +11361,8 @@ static struct pci_device_id lpfc_id_table[] = { PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE_VF, PCI_ANY_ID, PCI_ANY_ID, }, + {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G6_FC, + PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK, PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_VF, @@ -11467,6 +11486,7 @@ lpfc_exit(void) free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order); } kfree(lpfc_used_cpu); + idr_destroy(&lpfc_hba_index); } module_init(lpfc_init); diff --git a/kernel/drivers/scsi/lpfc/lpfc_mbox.c b/kernel/drivers/scsi/lpfc/lpfc_mbox.c index 816f596cd..f87f90e9b 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_mbox.c +++ b/kernel/drivers/scsi/lpfc/lpfc_mbox.c @@ -289,9 +289,7 @@ lpfc_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, struct lpfc_dmabuf *mp) { MAILBOX_t *mb; - struct lpfc_sli *psli; - psli = &phba->sli; mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); @@ -483,13 +481,11 @@ lpfc_init_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed) { lpfc_vpd_t *vpd; - struct lpfc_sli *psli; MAILBOX_t *mb; mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); - psli = &phba->sli; switch (topology) { case FLAGS_TOPOLOGY_MODE_LOOP_PT: mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP; @@ -510,6 +506,13 @@ lpfc_init_link(struct lpfc_hba * phba, break; } + if (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC && + mb->un.varInitLnk.link_flags & FLAGS_TOPOLOGY_MODE_LOOP) { + /* Failover is not tried for Lancer G6 */ + mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT; + phba->cfg_topology = FLAGS_TOPOLOGY_MODE_PT_PT; + } + /* Enable asynchronous ABTS responses from firmware */ mb->un.varInitLnk.link_flags |= FLAGS_IMED_ABORT; @@ -543,6 +546,10 @@ lpfc_init_link(struct lpfc_hba * phba, mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED; mb->un.varInitLnk.link_speed = LINK_SPEED_16G; break; + case LPFC_USER_LINK_SPEED_32G: + mb->un.varInitLnk.link_flags |= FLAGS_LINK_SPEED; + mb->un.varInitLnk.link_speed = LINK_SPEED_32G; + break; case LPFC_USER_LINK_SPEED_AUTO: default: mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO; @@ -585,9 +592,7 @@ lpfc_read_sparam(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, int vpi) { struct lpfc_dmabuf *mp; MAILBOX_t *mb; - struct lpfc_sli *psli; - psli = &phba->sli; mb = &pmb->u.mb; memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); @@ -2010,7 +2015,6 @@ lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index) { void *virt_addr; - dma_addr_t phys_addr; uint8_t *bytep; struct lpfc_mbx_sge sge; uint32_t alloc_len, req_len; @@ -2039,7 +2043,6 @@ lpfc_sli4_mbx_read_fcf_rec(struct lpfc_hba *phba, * routine only uses a single SGE. */ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); - phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); virt_addr = mboxq->sge_array->addr[0]; read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr; @@ -2255,6 +2258,159 @@ lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox) return 0; } +void +lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) +{ + MAILBOX_t *mb; + int rc = FAILURE; + struct lpfc_rdp_context *rdp_context = + (struct lpfc_rdp_context *)(mboxq->context2); + + mb = &mboxq->u.mb; + if (mb->mbxStatus) + goto mbx_failed; + + memcpy(&rdp_context->link_stat, &mb->un.varRdLnk, sizeof(READ_LNK_VAR)); + + rc = SUCCESS; + +mbx_failed: + lpfc_sli4_mbox_cmd_free(phba, mboxq); + rdp_context->cmpl(phba, rdp_context, rc); +} + +void +lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) +{ + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1; + struct lpfc_rdp_context *rdp_context = + (struct lpfc_rdp_context *)(mbox->context2); + + if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) + goto error_mbuf_free; + + lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2, + DMP_SFF_PAGE_A2_SIZE); + + /* We don't need dma buffer for link stat. */ + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + + memset(mbox, 0, sizeof(*mbox)); + lpfc_read_lnk_stat(phba, mbox); + mbox->vport = rdp_context->ndlp->vport; + mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat; + mbox->context2 = (struct lpfc_rdp_context *) rdp_context; + if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED) + goto error_cmd_free; + + return; + +error_mbuf_free: + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); +error_cmd_free: + lpfc_sli4_mbox_cmd_free(phba, mbox); + rdp_context->cmpl(phba, rdp_context, FAILURE); +} + +void +lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox) +{ + int rc; + struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (mbox->context1); + struct lpfc_rdp_context *rdp_context = + (struct lpfc_rdp_context *)(mbox->context2); + + if (bf_get(lpfc_mqe_status, &mbox->u.mqe)) + goto error; + + lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0, + DMP_SFF_PAGE_A0_SIZE); + + memset(mbox, 0, sizeof(*mbox)); + + memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE); + INIT_LIST_HEAD(&mp->list); + + /* save address for completion */ + mbox->context1 = mp; + mbox->vport = rdp_context->ndlp->vport; + + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY); + bf_set(lpfc_mbx_memory_dump_type3_type, + &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD); + bf_set(lpfc_mbx_memory_dump_type3_link, + &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port); + bf_set(lpfc_mbx_memory_dump_type3_page_no, + &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2); + bf_set(lpfc_mbx_memory_dump_type3_length, + &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE); + mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys); + mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys); + + mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a2; + mbox->context2 = (struct lpfc_rdp_context *) rdp_context; + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) + goto error; + + return; + +error: + lpfc_mbuf_free(phba, mp->virt, mp->phys); + kfree(mp); + lpfc_sli4_mbox_cmd_free(phba, mbox); + rdp_context->cmpl(phba, rdp_context, FAILURE); +} + + +/* + * lpfc_sli4_dump_sfp_pagea0 - Dump sli4 read SFP Diagnostic. + * @phba: pointer to the hba structure containing. + * @mbox: pointer to lpfc mbox command to initialize. + * + * This function create a SLI4 dump mailbox command to dump configure + * type 3 page 0xA0. + */ +int +lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox) +{ + struct lpfc_dmabuf *mp = NULL; + + memset(mbox, 0, sizeof(*mbox)); + + mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (mp) + mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys); + if (!mp || !mp->virt) { + kfree(mp); + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, + "3569 dump type 3 page 0xA0 allocation failed\n"); + return 1; + } + + memset(mp->virt, 0, LPFC_BPL_SIZE); + INIT_LIST_HEAD(&mp->list); + + bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY); + /* save address for completion */ + mbox->context1 = mp; + + bf_set(lpfc_mbx_memory_dump_type3_type, + &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD); + bf_set(lpfc_mbx_memory_dump_type3_link, + &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port); + bf_set(lpfc_mbx_memory_dump_type3_page_no, + &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A0); + bf_set(lpfc_mbx_memory_dump_type3_length, + &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE); + mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys); + mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys); + + return 0; +} + /** * lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command * @phba: pointer to the hba structure containing the FCF index and RQ ID. diff --git a/kernel/drivers/scsi/lpfc/lpfc_nportdisc.c b/kernel/drivers/scsi/lpfc/lpfc_nportdisc.c index 4cb9882af..ed9a2c80c 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/kernel/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -661,7 +661,13 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_destroy_vport_work_array(phba, vports); } - if (active_vlink_present) { + /* + * Don't re-instantiate if vport is marked for deletion. + * If we are here first then vport_delete is going to wait + * for discovery to complete. + */ + if (!(vport->load_flag & FC_UNLOADING) && + active_vlink_present) { /* * If there are other active VLinks present, * re-instantiate the Vlink using FDISC. @@ -814,7 +820,6 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, { struct lpfc_hba *phba; LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg; - MAILBOX_t *mb; uint16_t rpi; phba = vport->phba; @@ -822,7 +827,6 @@ lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (!(phba->pport->load_flag & FC_UNLOADING) && (evt == NLP_EVT_CMPL_REG_LOGIN) && (!pmb->u.mb.mbxStatus)) { - mb = &pmb->u.mb; rpi = pmb->u.mb.un.varWords[0]; lpfc_release_rpi(phba, vport, rpi); } @@ -1868,7 +1872,7 @@ lpfc_rcv_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg; spin_lock_irq(shost->host_lock); - ndlp->nlp_flag &= NLP_LOGO_ACC; + ndlp->nlp_flag |= NLP_LOGO_ACC; spin_unlock_irq(shost->host_lock); lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); return ndlp->nlp_state; diff --git a/kernel/drivers/scsi/lpfc/lpfc_scsi.c b/kernel/drivers/scsi/lpfc/lpfc_scsi.c index c140f9977..4679ed444 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_scsi.c +++ b/kernel/drivers/scsi/lpfc/lpfc_scsi.c @@ -1293,7 +1293,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t *reftag, uint16_t *apptag, uint32_t new_guard) { struct scatterlist *sgpe; /* s/g prot entry */ - struct scatterlist *sgde; /* s/g data entry */ struct lpfc_scsi_buf *lpfc_cmd = NULL; struct scsi_dif_tuple *src = NULL; struct lpfc_nodelist *ndlp; @@ -1309,7 +1308,6 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, return 0; sgpe = scsi_prot_sglist(sc); - sgde = scsi_sglist(sc); lba = scsi_get_lba(sc); /* First check if we need to match the LBA */ @@ -1882,7 +1880,6 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, #endif uint32_t checking = 1; uint32_t reftag; - unsigned blksize; uint8_t txop, rxop; status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); @@ -1890,7 +1887,6 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, goto out; /* extract some info from the scsi command for pde*/ - blksize = lpfc_cmd_blksize(sc); reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */ #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -2263,7 +2259,6 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, dma_addr_t physaddr; int i = 0, num_sge = 0, status; uint32_t reftag; - unsigned blksize; uint8_t txop, rxop; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint32_t rc; @@ -2277,7 +2272,6 @@ lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc, goto out; /* extract some info from the scsi command for pde*/ - blksize = lpfc_cmd_blksize(sc); reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */ #ifdef CONFIG_SCSI_LPFC_DEBUG_FS @@ -2881,7 +2875,7 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) struct scsi_cmnd *cmd = lpfc_cmd->pCmd; struct scsi_dif_tuple *src = NULL; uint8_t *data_src = NULL; - uint16_t guard_tag, guard_type; + uint16_t guard_tag; uint16_t start_app_tag, app_tag; uint32_t start_ref_tag, ref_tag; int prot, protsegcnt; @@ -2922,7 +2916,6 @@ lpfc_calc_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) data_len = sgde->length; if ((data_len & (blksize - 1)) == 0) chk_guard = 1; - guard_type = scsi_host_get_guard(cmd->device->host); src = (struct scsi_dif_tuple *)sg_virt(sgpe); start_ref_tag = (uint32_t)scsi_get_lba(cmd); /* Truncate LBA */ @@ -3257,7 +3250,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) */ nseg = scsi_dma_map(scsi_cmnd); - if (unlikely(!nseg)) + if (unlikely(nseg <= 0)) return 1; sgl += 1; /* clear the last flag in the fcp_rsp map entry */ @@ -3845,6 +3838,49 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd, lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb); } +/** + * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution + * @phba: Pointer to HBA context object. + * + * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index + * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock + * held. + * If scsi-mq is enabled, get the default block layer mapping of software queues + * to hardware queues. This information is saved in request tag. + * + * Return: index into SLI4 fast-path FCP queue index. + **/ +int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, + struct lpfc_scsi_buf *lpfc_cmd) +{ + struct scsi_cmnd *cmnd = lpfc_cmd->pCmd; + struct lpfc_vector_map_info *cpup; + int chann, cpu; + uint32_t tag; + uint16_t hwq; + + if (shost_use_blk_mq(cmnd->device->host)) { + tag = blk_mq_unique_tag(cmnd->request); + hwq = blk_mq_unique_tag_to_hwq(tag); + + return hwq; + } + + if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU + && phba->cfg_fcp_io_channel > 1) { + cpu = smp_processor_id(); + if (cpu < phba->sli4_hba.num_present_cpu) { + cpup = phba->sli4_hba.cpu_map; + cpup += cpu; + return cpup->channel_id; + } + } + chann = atomic_add_return(1, &phba->fcp_qidx); + chann = (chann % phba->cfg_fcp_io_channel); + return chann; +} + + /** * lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine * @phba: The Hba for which this call is being executed. @@ -3865,12 +3901,10 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, struct lpfc_rport_data *rdata = lpfc_cmd->rdata; struct lpfc_nodelist *pnode = rdata->pnode; struct scsi_cmnd *cmd; - int result; int depth; unsigned long flags; struct lpfc_fast_path_event *fast_path_evt; struct Scsi_Host *shost; - uint32_t queue_depth, scsi_id; uint32_t logit = LOG_FCP; /* Sanity check on return of outstanding command */ @@ -4052,7 +4086,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } lpfc_update_stats(phba, lpfc_cmd); - result = cmd->result; if (vport->cfg_max_scsicmpl_time && time_after(jiffies, lpfc_cmd->start_time + msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { @@ -4089,8 +4122,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); /* The sdev is not guaranteed to be valid post scsi_done upcall. */ - queue_depth = cmd->device->queue_depth; - scsi_id = cmd->device->id; cmd->scsi_done(cmd); if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { @@ -4537,7 +4568,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); - lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, + lpfc_printf_vlog(vport, KERN_INFO, LOG_MISC, "0707 driver's buffer pool is empty, " "IO busied\n"); goto out_host_busy; @@ -4968,13 +4999,16 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, iocbq, iocbqrsp, lpfc_cmd->timeout); if ((status != IOCB_SUCCESS) || (iocbqrsp->iocb.ulpStatus != IOSTAT_SUCCESS)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, - "0727 TMF %s to TGT %d LUN %llu failed (%d, %d) " - "iocb_flag x%x\n", - lpfc_taskmgmt_name(task_mgmt_cmd), - tgt_id, lun_id, iocbqrsp->iocb.ulpStatus, - iocbqrsp->iocb.un.ulpWord[4], - iocbq->iocb_flag); + if (status != IOCB_SUCCESS || + iocbqrsp->iocb.ulpStatus != IOSTAT_FCP_RSP_ERROR) + lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, + "0727 TMF %s to TGT %d LUN %llu " + "failed (%d, %d) iocb_flag x%x\n", + lpfc_taskmgmt_name(task_mgmt_cmd), + tgt_id, lun_id, + iocbqrsp->iocb.ulpStatus, + iocbqrsp->iocb.un.ulpWord[4], + iocbq->iocb_flag); /* if ulpStatus != IOCB_SUCCESS, then status == IOCB_SUCCESS */ if (status == IOCB_SUCCESS) { if (iocbqrsp->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR) @@ -4988,7 +5022,6 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata, } else { ret = FAILED; } - lpfc_cmd->status = IOSTAT_DRIVER_REJECT; } else ret = SUCCESS; @@ -5881,7 +5914,6 @@ struct scsi_host_template lpfc_template_s3 = { .max_sectors = 0xFFFF, .vendor_id = LPFC_NL_VENDOR_ID, .change_queue_depth = scsi_change_queue_depth, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -5907,7 +5939,6 @@ struct scsi_host_template lpfc_template = { .max_sectors = 0xFFFF, .vendor_id = LPFC_NL_VENDOR_ID, .change_queue_depth = scsi_change_queue_depth, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -5931,6 +5962,5 @@ struct scsi_host_template lpfc_vport_template = { .shost_attrs = lpfc_vport_attrs, .max_sectors = 0xFFFF, .change_queue_depth = scsi_change_queue_depth, - .use_blk_tags = 1, .track_queue_depth = 1, }; diff --git a/kernel/drivers/scsi/lpfc/lpfc_scsi.h b/kernel/drivers/scsi/lpfc/lpfc_scsi.h index 474e30cde..18b9260cc 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_scsi.h +++ b/kernel/drivers/scsi/lpfc/lpfc_scsi.h @@ -184,3 +184,6 @@ struct lpfc_scsi_buf { #define FIND_FIRST_OAS_LUN 0 #define NO_MORE_OAS_LUN -1 #define NOT_OAS_ENABLED_LUN NO_MORE_OAS_LUN + +int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, + struct lpfc_scsi_buf *lpfc_cmd); diff --git a/kernel/drivers/scsi/lpfc/lpfc_sli.c b/kernel/drivers/scsi/lpfc/lpfc_sli.c index 56f73682d..f9585cdd8 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_sli.c +++ b/kernel/drivers/scsi/lpfc/lpfc_sli.c @@ -2249,7 +2249,7 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) vport->vpi, ndlp->nlp_rpi, ndlp->nlp_DID, ndlp->nlp_usg_map, ndlp); - + ndlp->nlp_flag &= ~NLP_LOGO_ACC; lpfc_nlp_put(ndlp); } } @@ -6696,7 +6696,7 @@ lpfc_mbox_timeout(unsigned long ptr) * This function checks if any mailbox completions are present on the mailbox * completion queue. **/ -bool +static bool lpfc_sli4_mbox_completions_pending(struct lpfc_hba *phba) { @@ -8137,36 +8137,6 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, return sglq->sli4_xritag; } -/** - * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution - * @phba: Pointer to HBA context object. - * - * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index - * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock - * held. - * - * Return: index into SLI4 fast-path FCP queue index. - **/ -static inline int -lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba) -{ - struct lpfc_vector_map_info *cpup; - int chann, cpu; - - if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU - && phba->cfg_fcp_io_channel > 1) { - cpu = smp_processor_id(); - if (cpu < phba->sli4_hba.num_present_cpu) { - cpup = phba->sli4_hba.cpu_map; - cpup += cpu; - return cpup->channel_id; - } - } - chann = atomic_add_return(1, &phba->fcp_qidx); - chann = (chann % phba->cfg_fcp_io_channel); - return chann; -} - /** * lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry. * @phba: Pointer to HBA context object. @@ -8792,32 +8762,44 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) return 0; } +/** + * lpfc_sli_calc_ring - Calculates which ring to use + * @phba: Pointer to HBA context object. + * @ring_number: Initial ring + * @piocb: Pointer to command iocb. + * + * For SLI4, FCP IO can deferred to one fo many WQs, based on + * fcp_wqidx, thus we need to calculate the corresponding ring. + * Since ABORTS must go on the same WQ of the command they are + * aborting, we use command's fcp_wqidx. + */ int lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number, struct lpfc_iocbq *piocb) { - uint32_t idx; + if (phba->sli_rev < LPFC_SLI_REV4) + return ring_number; - if (phba->sli_rev == LPFC_SLI_REV4) { - if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { + if (piocb->iocb_flag & (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) { + if (!(phba->cfg_fof) || + (!(piocb->iocb_flag & LPFC_IO_FOF))) { + if (unlikely(!phba->sli4_hba.fcp_wq)) + return LPFC_HBA_ERROR; /* - * fcp_wqidx should already be setup based on what - * completion queue we want to use. + * for abort iocb fcp_wqidx should already + * be setup based on what work queue we used. */ - if (!(phba->cfg_fof) || - (!(piocb->iocb_flag & LPFC_IO_FOF))) { - if (unlikely(!phba->sli4_hba.fcp_wq)) - return LPFC_HBA_ERROR; - idx = lpfc_sli4_scmd_to_wqidx_distr(phba); - piocb->fcp_wqidx = idx; - ring_number = MAX_SLI3_CONFIGURED_RINGS + idx; - } else { - if (unlikely(!phba->sli4_hba.oas_wq)) - return LPFC_HBA_ERROR; - idx = 0; - piocb->fcp_wqidx = idx; - ring_number = LPFC_FCP_OAS_RING; - } + if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX)) + piocb->fcp_wqidx = + lpfc_sli4_scmd_to_wqidx_distr(phba, + piocb->context1); + ring_number = MAX_SLI3_CONFIGURED_RINGS + + piocb->fcp_wqidx; + } else { + if (unlikely(!phba->sli4_hba.oas_wq)) + return LPFC_HBA_ERROR; + piocb->fcp_wqidx = 0; + ring_number = LPFC_FCP_OAS_RING; } } return ring_number; @@ -12509,12 +12491,10 @@ lpfc_sli4_fof_intr_handler(int irq, void *dev_id) struct lpfc_eqe *eqe; unsigned long iflag; int ecount = 0; - uint32_t eqidx; /* Get the driver's phba structure from the dev_id */ fcp_eq_hdl = (struct lpfc_fcp_eq_hdl *)dev_id; phba = fcp_eq_hdl->phba; - eqidx = fcp_eq_hdl->idx; if (unlikely(!phba)) return IRQ_NONE; @@ -12849,12 +12829,8 @@ out_fail: static void __iomem * lpfc_dual_chute_pci_bar_map(struct lpfc_hba *phba, uint16_t pci_barset) { - struct pci_dev *pdev; - if (!phba->pcidev) return NULL; - else - pdev = phba->pcidev; switch (pci_barset) { case WQ_PCI_BAR_0_AND_1: @@ -15938,7 +15914,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record) LPFC_MBOXQ_t *mboxq; uint8_t *bytep; void *virt_addr; - dma_addr_t phys_addr; struct lpfc_mbx_sge sge; uint32_t alloc_len, req_len; uint32_t fcfindex; @@ -15971,7 +15946,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record) * routine only uses a single SGE. */ lpfc_sli4_mbx_sge_get(mboxq, 0, &sge); - phys_addr = getPaddr(sge.pa_hi, sge.pa_lo); virt_addr = mboxq->sge_array->addr[0]; /* * Configure the FCF record for FCFI 0. This is the driver's diff --git a/kernel/drivers/scsi/lpfc/lpfc_sli4.h b/kernel/drivers/scsi/lpfc/lpfc_sli4.h index 6eca3b812..1e916e16c 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_sli4.h +++ b/kernel/drivers/scsi/lpfc/lpfc_sli4.h @@ -454,7 +454,6 @@ struct lpfc_vector_map_info { uint16_t core_id; uint16_t irq; uint16_t channel_id; - struct cpumask maskbits; }; #define LPFC_VECTOR_MAP_EMPTY 0xffff @@ -602,6 +601,7 @@ struct lpfc_sli4_hba { struct lpfc_iov iov; spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */ spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */ + uint32_t physical_port; /* CPU to vector mapping information */ struct lpfc_vector_map_info *cpu_map; @@ -651,6 +651,26 @@ struct lpfc_rsrc_blks { uint16_t rsrc_used; }; +struct lpfc_rdp_context { + struct lpfc_nodelist *ndlp; + uint16_t ox_id; + uint16_t rx_id; + READ_LNK_VAR link_stat; + uint8_t page_a0[DMP_SFF_PAGE_A0_SIZE]; + uint8_t page_a2[DMP_SFF_PAGE_A2_SIZE]; + void (*cmpl)(struct lpfc_hba *, struct lpfc_rdp_context*, int); +}; + +struct lpfc_lcb_context { + uint8_t sub_command; + uint8_t type; + uint8_t frequency; + uint16_t ox_id; + uint16_t rx_id; + struct lpfc_nodelist *ndlp; +}; + + /* * SLI4 specific function prototypes */ diff --git a/kernel/drivers/scsi/lpfc/lpfc_version.h b/kernel/drivers/scsi/lpfc/lpfc_version.h index c37bb9f91..ea53aa664 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_version.h +++ b/kernel/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "10.5.0.0." +#define LPFC_DRIVER_VERSION "11.0.0.0." #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ diff --git a/kernel/drivers/scsi/lpfc/lpfc_vport.c b/kernel/drivers/scsi/lpfc/lpfc_vport.c index a87ee33f4..769012663 100644 --- a/kernel/drivers/scsi/lpfc/lpfc_vport.c +++ b/kernel/drivers/scsi/lpfc/lpfc_vport.c @@ -567,8 +567,8 @@ int lpfc_vport_delete(struct fc_vport *fc_vport) { struct lpfc_nodelist *ndlp = NULL; - struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost; struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data; + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; long timeout; bool ns_ndlp_referenced = false; @@ -645,8 +645,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport) } /* Remove FC host and then SCSI host with the vport */ - fc_remove_host(lpfc_shost_from_vport(vport)); - scsi_remove_host(lpfc_shost_from_vport(vport)); + fc_remove_host(shost); + scsi_remove_host(shost); ndlp = lpfc_findnode_did(phba->pport, Fabric_DID); @@ -772,7 +772,8 @@ skip_logo: * Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi) * does the scsi_host_put() to release the vport. */ - if (lpfc_mbx_unreg_vpi(vport)) + if (!(vport->vpi_state & LPFC_VPI_REGISTERED) || + lpfc_mbx_unreg_vpi(vport)) scsi_host_put(shost); } else scsi_host_put(shost); diff --git a/kernel/drivers/scsi/mac53c94.c b/kernel/drivers/scsi/mac53c94.c index 0adb2e015..141226631 100644 --- a/kernel/drivers/scsi/mac53c94.c +++ b/kernel/drivers/scsi/mac53c94.c @@ -403,7 +403,6 @@ static struct scsi_host_template mac53c94_template = { .can_queue = 1, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/megaraid.c b/kernel/drivers/scsi/megaraid.c index bc7b34c02..9d05302a3 100644 --- a/kernel/drivers/scsi/megaraid.c +++ b/kernel/drivers/scsi/megaraid.c @@ -268,8 +268,8 @@ mega_query_adapter(adapter_t *adapter) raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ if ((retval = issue_scb_block(adapter, raw_mbox))) - printk(KERN_WARNING - "megaraid: Product_info cmd failed with error: %d\n", + dev_warn(&adapter->dev->dev, + "Product_info cmd failed with error: %d\n", retval); pci_unmap_single(adapter->dev, prod_info_dma_handle, @@ -334,7 +334,7 @@ mega_query_adapter(adapter_t *adapter) adapter->bios_version[4] = 0; } - printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n", + dev_notice(&adapter->dev->dev, "[%s:%s] detected %d logical drives\n", adapter->fw_version, adapter->bios_version, adapter->numldrv); /* @@ -342,7 +342,7 @@ mega_query_adapter(adapter_t *adapter) */ adapter->support_ext_cdb = mega_support_ext_cdb(adapter); if (adapter->support_ext_cdb) - printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + dev_notice(&adapter->dev->dev, "supports extended CDBs\n"); return 0; @@ -678,11 +678,11 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) if(!(adapter->flag & (1L << cmd->device->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning scsi channel %d ", + dev_notice(&adapter->dev->dev, + "scsi%d: scanning scsi channel %d " + "for logical drives\n", adapter->host->host_no, cmd->device->channel); - printk("for logical drives.\n"); adapter->flag |= (1L << cmd->device->channel); } @@ -983,11 +983,11 @@ mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, case READ_CAPACITY: if(!(adapter->flag & (1L << cmd->device->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning scsi channel %d [P%d] ", + dev_notice(&adapter->dev->dev, + "scsi%d: scanning scsi channel %d [P%d] " + "for physical devices\n", adapter->host->host_no, cmd->device->channel, channel); - printk("for physical devices.\n"); adapter->flag |= (1L << cmd->device->channel); } @@ -1045,11 +1045,11 @@ mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, case READ_CAPACITY: if(!(adapter->flag & (1L << cmd->device->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning scsi channel %d [P%d] ", + dev_notice(&adapter->dev->dev, + "scsi%d: scanning scsi channel %d [P%d] " + "for physical devices\n", adapter->host->host_no, cmd->device->channel, channel); - printk("for physical devices.\n"); adapter->flag |= (1L << cmd->device->channel); } @@ -1241,7 +1241,7 @@ issue_scb_block(adapter_t *adapter, u_char *raw_mbox) return mbox->m_in.status; bug_blocked_mailbox: - printk(KERN_WARNING "megaraid: Blocked mailbox......!!\n"); + dev_warn(&adapter->dev->dev, "Blocked mailbox......!!\n"); udelay (1000); return -1; } @@ -1454,9 +1454,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) * Make sure f/w has completed a valid command */ if( !(scb->state & SCB_ISSUED) || scb->cmd == NULL ) { - printk(KERN_CRIT - "megaraid: invalid command "); - printk("Id %d, scb->state:%x, scsi cmd:%p\n", + dev_crit(&adapter->dev->dev, "invalid command " + "Id %d, scb->state:%x, scsi cmd:%p\n", cmdid, scb->state, scb->cmd); continue; @@ -1467,8 +1466,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) */ if( scb->state & SCB_ABORT ) { - printk(KERN_WARNING - "megaraid: aborted cmd [%x] complete.\n", + dev_warn(&adapter->dev->dev, + "aborted cmd [%x] complete\n", scb->idx); scb->cmd->result = (DID_ABORT << 16); @@ -1486,8 +1485,8 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) */ if( scb->state & SCB_RESET ) { - printk(KERN_WARNING - "megaraid: reset cmd [%x] complete.\n", + dev_warn(&adapter->dev->dev, + "reset cmd [%x] complete\n", scb->idx); scb->cmd->result = (DID_RESET << 16); @@ -1553,8 +1552,7 @@ mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) if( sg_page(sgl) ) { c = *(unsigned char *) sg_virt(&sgl[0]); } else { - printk(KERN_WARNING - "megaraid: invalid sg.\n"); + dev_warn(&adapter->dev->dev, "invalid sg\n"); c = 0; } @@ -1902,11 +1900,10 @@ megaraid_reset(struct scsi_cmnd *cmd) mc.opcode = MEGA_RESET_RESERVATIONS; if( mega_internal_command(adapter, &mc, NULL) != 0 ) { - printk(KERN_WARNING - "megaraid: reservation reset failed.\n"); + dev_warn(&adapter->dev->dev, "reservation reset failed\n"); } else { - printk(KERN_INFO "megaraid: reservation reset.\n"); + dev_info(&adapter->dev->dev, "reservation reset\n"); } #endif @@ -1939,7 +1936,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) struct list_head *pos, *next; scb_t *scb; - printk(KERN_WARNING "megaraid: %s cmd=%x \n", + dev_warn(&adapter->dev->dev, "%s cmd=%x \n", (aor == SCB_ABORT)? "ABORTING":"RESET", cmd->cmnd[0], cmd->device->channel, cmd->device->id, (u32)cmd->device->lun); @@ -1963,8 +1960,8 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) */ if( scb->state & SCB_ISSUED ) { - printk(KERN_WARNING - "megaraid: %s[%x], fw owner.\n", + dev_warn(&adapter->dev->dev, + "%s[%x], fw owner\n", (aor==SCB_ABORT) ? "ABORTING":"RESET", scb->idx); @@ -1976,8 +1973,8 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) * Not yet issued! Remove from the pending * list */ - printk(KERN_WARNING - "megaraid: %s-[%x], driver owner.\n", + dev_warn(&adapter->dev->dev, + "%s-[%x], driver owner\n", (aor==SCB_ABORT) ? "ABORTING":"RESET", scb->idx); @@ -2197,7 +2194,7 @@ proc_show_rebuild_rate(struct seq_file *m, void *v) if( mega_adapinq(adapter, dma_handle) != 0 ) { seq_puts(m, "Adapter inquiry failed.\n"); - printk(KERN_WARNING "megaraid: inquiry failed.\n"); + dev_warn(&adapter->dev->dev, "inquiry failed\n"); goto free_inquiry; } @@ -2241,7 +2238,7 @@ proc_show_battery(struct seq_file *m, void *v) if( mega_adapinq(adapter, dma_handle) != 0 ) { seq_puts(m, "Adapter inquiry failed.\n"); - printk(KERN_WARNING "megaraid: inquiry failed.\n"); + dev_warn(&adapter->dev->dev, "inquiry failed\n"); goto free_inquiry; } @@ -2350,7 +2347,7 @@ proc_show_pdrv(struct seq_file *m, adapter_t *adapter, int channel) if( mega_adapinq(adapter, dma_handle) != 0 ) { seq_puts(m, "Adapter inquiry failed.\n"); - printk(KERN_WARNING "megaraid: inquiry failed.\n"); + dev_warn(&adapter->dev->dev, "inquiry failed\n"); goto free_inquiry; } @@ -2525,7 +2522,7 @@ proc_show_rdrv(struct seq_file *m, adapter_t *adapter, int start, int end ) if( mega_adapinq(adapter, dma_handle) != 0 ) { seq_puts(m, "Adapter inquiry failed.\n"); - printk(KERN_WARNING "megaraid: inquiry failed.\n"); + dev_warn(&adapter->dev->dev, "inquiry failed\n"); goto free_inquiry; } @@ -2799,7 +2796,7 @@ mega_create_proc_entry(int index, struct proc_dir_entry *parent) dir = adapter->controller_proc_dir_entry = proc_mkdir_data(string, 0, parent, adapter); if(!dir) { - printk(KERN_WARNING "\nmegaraid: proc_mkdir failed\n"); + dev_warn(&adapter->dev->dev, "proc_mkdir failed\n"); return; } @@ -2807,7 +2804,7 @@ mega_create_proc_entry(int index, struct proc_dir_entry *parent) de = proc_create_data(f->name, S_IRUSR, dir, &mega_proc_fops, f->show); if (!de) { - printk(KERN_WARNING "\nmegaraid: proc_create failed\n"); + dev_warn(&adapter->dev->dev, "proc_create failed\n"); return; } @@ -2874,9 +2871,9 @@ megaraid_biosparam(struct scsi_device *sdev, struct block_device *bdev, return rval; } - printk(KERN_INFO - "megaraid: invalid partition on this disk on channel %d\n", - sdev->channel); + dev_info(&adapter->dev->dev, + "invalid partition on this disk on channel %d\n", + sdev->channel); /* Default heads (64) & sectors (32) */ heads = 64; @@ -2936,7 +2933,7 @@ mega_init_scb(adapter_t *adapter) scb->sgl = (mega_sglist *)scb->sgl64; if( !scb->sgl ) { - printk(KERN_WARNING "RAID: Can't allocate sglist.\n"); + dev_warn(&adapter->dev->dev, "RAID: Can't allocate sglist\n"); mega_free_sgl(adapter); return -1; } @@ -2946,7 +2943,7 @@ mega_init_scb(adapter_t *adapter) &scb->pthru_dma_addr); if( !scb->pthru ) { - printk(KERN_WARNING "RAID: Can't allocate passthru.\n"); + dev_warn(&adapter->dev->dev, "RAID: Can't allocate passthru\n"); mega_free_sgl(adapter); return -1; } @@ -2956,8 +2953,8 @@ mega_init_scb(adapter_t *adapter) &scb->epthru_dma_addr); if( !scb->epthru ) { - printk(KERN_WARNING - "Can't allocate extended passthru.\n"); + dev_warn(&adapter->dev->dev, + "Can't allocate extended passthru\n"); mega_free_sgl(adapter); return -1; } @@ -3154,8 +3151,8 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) * Do we support this feature */ if( !adapter->support_random_del ) { - printk(KERN_WARNING "megaraid: logdrv "); - printk("delete on non-supporting F/W.\n"); + dev_warn(&adapter->dev->dev, "logdrv " + "delete on non-supporting F/W\n"); return (-EINVAL); } @@ -3179,7 +3176,7 @@ megadev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU64 || uioc.uioc_rmbox[0] == MEGA_MBOXCMD_EXTPTHRU ) { - printk(KERN_WARNING "megaraid: rejected passthru.\n"); + dev_warn(&adapter->dev->dev, "rejected passthru\n"); return (-EINVAL); } @@ -3683,11 +3680,11 @@ mega_enum_raid_scsi(adapter_t *adapter) for( i = 0; i < adapter->product_info.nchannels; i++ ) { if( (adapter->mega_ch_class >> i) & 0x01 ) { - printk(KERN_INFO "megaraid: channel[%d] is raid.\n", + dev_info(&adapter->dev->dev, "channel[%d] is raid\n", i); } else { - printk(KERN_INFO "megaraid: channel[%d] is scsi.\n", + dev_info(&adapter->dev->dev, "channel[%d] is scsi\n", i); } } @@ -3893,7 +3890,7 @@ mega_do_del_logdrv(adapter_t *adapter, int logdrv) /* log this event */ if(rval) { - printk(KERN_WARNING "megaraid: Delete LD-%d failed.", logdrv); + dev_warn(&adapter->dev->dev, "Delete LD-%d failed", logdrv); return rval; } @@ -4161,7 +4158,7 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) * this information. */ if (rval && trace_level) { - printk("megaraid: cmd [%x, %x, %x] status:[%x]\n", + dev_info(&adapter->dev->dev, "cmd [%x, %x, %x] status:[%x]\n", mc->cmd, mc->opcode, mc->subopcode, rval); } @@ -4244,11 +4241,8 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) subsysvid = pdev->subsystem_vendor; subsysid = pdev->subsystem_device; - printk(KERN_NOTICE "megaraid: found 0x%4.04x:0x%4.04x:bus %d:", - id->vendor, id->device, pci_bus); - - printk("slot %d:func %d\n", - PCI_SLOT(pci_dev_func), PCI_FUNC(pci_dev_func)); + dev_notice(&pdev->dev, "found 0x%4.04x:0x%4.04x\n", + id->vendor, id->device); /* Read the base port and IRQ from PCI */ mega_baseport = pci_resource_start(pdev, 0); @@ -4259,14 +4253,13 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) flag |= BOARD_MEMMAP; if (!request_mem_region(mega_baseport, 128, "megaraid")) { - printk(KERN_WARNING "megaraid: mem region busy!\n"); + dev_warn(&pdev->dev, "mem region busy!\n"); goto out_disable_device; } mega_baseport = (unsigned long)ioremap(mega_baseport, 128); if (!mega_baseport) { - printk(KERN_WARNING - "megaraid: could not map hba memory\n"); + dev_warn(&pdev->dev, "could not map hba memory\n"); goto out_release_region; } } else { @@ -4285,7 +4278,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) adapter = (adapter_t *)host->hostdata; memset(adapter, 0, sizeof(adapter_t)); - printk(KERN_NOTICE + dev_notice(&pdev->dev, "scsi%d:Found MegaRAID controller at 0x%lx, IRQ:%d\n", host->host_no, mega_baseport, irq); @@ -4323,21 +4316,20 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) adapter->mega_buffer = pci_alloc_consistent(adapter->dev, MEGA_BUFFER_SIZE, &adapter->buf_dma_handle); if (!adapter->mega_buffer) { - printk(KERN_WARNING "megaraid: out of RAM.\n"); + dev_warn(&pdev->dev, "out of RAM\n"); goto out_host_put; } adapter->scb_list = kmalloc(sizeof(scb_t) * MAX_COMMANDS, GFP_KERNEL); if (!adapter->scb_list) { - printk(KERN_WARNING "megaraid: out of RAM.\n"); + dev_warn(&pdev->dev, "out of RAM\n"); goto out_free_cmd_buffer; } if (request_irq(irq, (adapter->flag & BOARD_MEMMAP) ? megaraid_isr_memmapped : megaraid_isr_iomapped, IRQF_SHARED, "megaraid", adapter)) { - printk(KERN_WARNING - "megaraid: Couldn't register IRQ %d!\n", irq); + dev_warn(&pdev->dev, "Couldn't register IRQ %d!\n", irq); goto out_free_scb_list; } @@ -4357,9 +4349,9 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!strcmp(adapter->fw_version, "3.00") || !strcmp(adapter->fw_version, "3.01")) { - printk( KERN_WARNING - "megaraid: Your card is a Dell PERC " - "2/SC RAID controller with " + dev_warn(&pdev->dev, + "Your card is a Dell PERC " + "2/SC RAID controller with " "firmware\nmegaraid: 3.00 or 3.01. " "This driver is known to have " "corruption issues\nmegaraid: with " @@ -4390,12 +4382,12 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!strcmp(adapter->fw_version, "H01.07") || !strcmp(adapter->fw_version, "H01.08") || !strcmp(adapter->fw_version, "H01.09") ) { - printk(KERN_WARNING - "megaraid: Firmware H.01.07, " + dev_warn(&pdev->dev, + "Firmware H.01.07, " "H.01.08, and H.01.09 on 1M/2M " "controllers\n" - "megaraid: do not support 64 bit " - "addressing.\nmegaraid: DISABLING " + "do not support 64 bit " + "addressing.\nDISABLING " "64 bit support.\n"); adapter->flag &= ~BOARD_64BIT; } @@ -4503,8 +4495,8 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) */ adapter->has_cluster = mega_support_cluster(adapter); if (adapter->has_cluster) { - printk(KERN_NOTICE - "megaraid: Cluster driver, initiator id:%d\n", + dev_notice(&pdev->dev, + "Cluster driver, initiator id:%d\n", adapter->this_id); } #endif @@ -4571,7 +4563,7 @@ __megaraid_shutdown(adapter_t *adapter) issue_scb_block(adapter, raw_mbox); if (atomic_read(&adapter->pend_cmds) > 0) - printk(KERN_WARNING "megaraid: pending commands!!\n"); + dev_warn(&adapter->dev->dev, "pending commands!!\n"); /* * Have a delibrate delay to make sure all the caches are diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas.h b/kernel/drivers/scsi/megaraid/megaraid_sas.h index 14e5c7cea..c0f7c8ce5 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas.h +++ b/kernel/drivers/scsi/megaraid/megaraid_sas.h @@ -35,7 +35,8 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "06.806.08.00-rc1" +#define MEGASAS_VERSION "06.808.16.00-rc1" +#define MEGASAS_RELDATE "Oct. 8, 2015" /* * Device IDs @@ -51,6 +52,10 @@ #define PCI_DEVICE_ID_LSI_PLASMA 0x002f #define PCI_DEVICE_ID_LSI_INVADER 0x005d #define PCI_DEVICE_ID_LSI_FURY 0x005f +#define PCI_DEVICE_ID_LSI_INTRUDER 0x00ce +#define PCI_DEVICE_ID_LSI_INTRUDER_24 0x00cf +#define PCI_DEVICE_ID_LSI_CUTLASS_52 0x0052 +#define PCI_DEVICE_ID_LSI_CUTLASS_53 0x0053 /* * Intel HBA SSDIDs @@ -61,6 +66,14 @@ #define MEGARAID_INTEL_RS3MC044_SSDID 0x9381 #define MEGARAID_INTEL_RS3WC080_SSDID 0x9341 #define MEGARAID_INTEL_RS3WC040_SSDID 0x9343 +#define MEGARAID_INTEL_RMS3BC160_SSDID 0x352B + +/* + * Intruder HBA SSDIDs + */ +#define MEGARAID_INTRUDER_SSDID1 0x9371 +#define MEGARAID_INTRUDER_SSDID2 0x9390 +#define MEGARAID_INTRUDER_SSDID3 0x9370 /* * Intel HBA branding @@ -77,6 +90,8 @@ "Intel(R) RAID Controller RS3WC080" #define MEGARAID_INTEL_RS3WC040_BRANDING \ "Intel(R) RAID Controller RS3WC040" +#define MEGARAID_INTEL_RMS3BC160_BRANDING \ + "Intel(R) Integrated RAID Module RMS3BC160" /* * ===================================== @@ -153,6 +168,9 @@ #define MFI_FRAME_DIR_BOTH 0x0018 #define MFI_FRAME_IEEE 0x0020 +/* Driver internal */ +#define DRV_DCMD_POLLED_MODE 0x1 + /* * Definition for cmd_status */ @@ -269,6 +287,16 @@ enum MFI_STAT { MFI_STAT_INVALID_STATUS = 0xFF }; +enum mfi_evt_class { + MFI_EVT_CLASS_DEBUG = -2, + MFI_EVT_CLASS_PROGRESS = -1, + MFI_EVT_CLASS_INFO = 0, + MFI_EVT_CLASS_WARNING = 1, + MFI_EVT_CLASS_CRITICAL = 2, + MFI_EVT_CLASS_FATAL = 3, + MFI_EVT_CLASS_DEAD = 4 +}; + /* * Crash dump related defines */ @@ -360,6 +388,8 @@ enum MR_EVT_ARGS { MR_EVT_ARGS_GENERIC, }; + +#define SGE_BUFFER_SIZE 4096 /* * define constants for device list query options */ @@ -390,6 +420,7 @@ enum MR_LD_QUERY_TYPE { #define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db #define MR_EVT_LD_OFFLINE 0x00fc #define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 +#define MR_EVT_CTRL_PROP_CHANGED 0x012f enum MR_PD_STATE { MR_PD_STATE_UNCONFIGURED_GOOD = 0x00, @@ -408,7 +439,7 @@ enum MR_PD_STATE { * defines the physical drive address structure */ struct MR_PD_ADDRESS { - u16 deviceId; + __le16 deviceId; u16 enclDeviceId; union { @@ -433,8 +464,8 @@ struct MR_PD_ADDRESS { * defines the physical drive list structure */ struct MR_PD_LIST { - u32 size; - u32 count; + __le32 size; + __le32 count; struct MR_PD_ADDRESS addr[1]; } __packed; @@ -451,28 +482,28 @@ union MR_LD_REF { struct { u8 targetId; u8 reserved; - u16 seqNum; + __le16 seqNum; }; - u32 ref; + __le32 ref; } __packed; /* * defines the logical drive list structure */ struct MR_LD_LIST { - u32 ldCount; - u32 reserved; + __le32 ldCount; + __le32 reserved; struct { union MR_LD_REF ref; u8 state; u8 reserved[3]; - u64 size; + __le64 size; } ldList[MAX_LOGICAL_DRIVES_EXT]; } __packed; struct MR_LD_TARGETID_LIST { - u32 size; - u32 count; + __le32 size; + __le32 count; u8 pad[3]; u8 targetId[MAX_LOGICAL_DRIVES_EXT]; }; @@ -553,7 +584,7 @@ struct megasas_ctrl_prop { } OnOffProperties; u8 autoSnapVDSpace; u8 viewSpace; - u16 spinDownTime; + __le16 spinDownTime; u8 reserved[24]; } __packed; @@ -567,10 +598,10 @@ struct megasas_ctrl_info { */ struct { - u16 vendor_id; - u16 device_id; - u16 sub_vendor_id; - u16 sub_device_id; + __le16 vendor_id; + __le16 device_id; + __le16 sub_vendor_id; + __le16 sub_device_id; u8 reserved[24]; } __attribute__ ((packed)) pci; @@ -611,8 +642,8 @@ struct megasas_ctrl_info { /* * List of components residing in flash. All str are null terminated */ - u32 image_check_word; - u32 image_component_count; + __le32 image_check_word; + __le32 image_component_count; struct { @@ -629,7 +660,7 @@ struct megasas_ctrl_info { * empty if a flash operation has not occurred. All stings are null * terminated */ - u32 pending_image_component_count; + __le32 pending_image_component_count; struct { @@ -662,39 +693,39 @@ struct megasas_ctrl_info { } __attribute__ ((packed)) hw_present; - u32 current_fw_time; + __le32 current_fw_time; /* * Maximum data transfer sizes */ - u16 max_concurrent_cmds; - u16 max_sge_count; - u32 max_request_size; + __le16 max_concurrent_cmds; + __le16 max_sge_count; + __le32 max_request_size; /* * Logical and physical device counts */ - u16 ld_present_count; - u16 ld_degraded_count; - u16 ld_offline_count; + __le16 ld_present_count; + __le16 ld_degraded_count; + __le16 ld_offline_count; - u16 pd_present_count; - u16 pd_disk_present_count; - u16 pd_disk_pred_failure_count; - u16 pd_disk_failed_count; + __le16 pd_present_count; + __le16 pd_disk_present_count; + __le16 pd_disk_pred_failure_count; + __le16 pd_disk_failed_count; /* * Memory size information */ - u16 nvram_size; - u16 memory_size; - u16 flash_size; + __le16 nvram_size; + __le16 memory_size; + __le16 flash_size; /* * Error counters */ - u16 mem_correctable_error_count; - u16 mem_uncorrectable_error_count; + __le16 mem_correctable_error_count; + __le16 mem_uncorrectable_error_count; /* * Cluster information @@ -705,7 +736,7 @@ struct megasas_ctrl_info { /* * Additional max data transfer sizes */ - u16 max_strips_per_io; + __le16 max_strips_per_io; /* * Controller capabilities structures @@ -805,7 +836,7 @@ struct megasas_ctrl_info { * deviceInterface.portAddr, and the rest shall be * populated in deviceInterfacePortAddr2. */ - u64 deviceInterfacePortAddr2[8]; /*6a0h */ + __le64 deviceInterfacePortAddr2[8]; /*6a0h */ u8 reserved3[128]; /*6e0h */ struct { /*760h */ @@ -842,26 +873,26 @@ struct megasas_ctrl_info { u16 reserved[6]; } pdsForRaidLevels; - u16 maxPds; /*780h */ - u16 maxDedHSPs; /*782h */ - u16 maxGlobalHSPs; /*784h */ - u16 ddfSize; /*786h */ + __le16 maxPds; /*780h */ + __le16 maxDedHSPs; /*782h */ + __le16 maxGlobalHSP; /*784h */ + __le16 ddfSize; /*786h */ u8 maxLdsPerArray; /*788h */ u8 partitionsInDDF; /*789h */ u8 lockKeyBinding; /*78ah */ u8 maxPITsPerLd; /*78bh */ u8 maxViewsPerLd; /*78ch */ u8 maxTargetId; /*78dh */ - u16 maxBvlVdSize; /*78eh */ + __le16 maxBvlVdSize; /*78eh */ - u16 maxConfigurableSSCSize; /*790h */ - u16 currentSSCsize; /*792h */ + __le16 maxConfigurableSSCSize; /*790h */ + __le16 currentSSCsize; /*792h */ char expanderFwVersion[12]; /*794h */ - u16 PFKTrialTimeRemaining; /*7A0h */ + __le16 PFKTrialTimeRemaining; /*7A0h */ - u16 cacheMemorySize; /*7A2h */ + __le16 cacheMemorySize; /*7A2h */ struct { /*7A4h */ #if defined(__BIG_ENDIAN_BITFIELD) @@ -931,7 +962,7 @@ struct megasas_ctrl_info { u8 temperatureROC; /*7C9h */ u8 temperatureCtrl; /*7CAh */ u8 reserved4; /*7CBh */ - u16 maxConfigurablePds; /*7CCh */ + __le16 maxConfigurablePds; /*7CCh */ u8 reserved5[2]; /*0x7CDh */ @@ -969,7 +1000,12 @@ struct megasas_ctrl_info { struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:12; + u32 reserved:7; + u32 useSeqNumJbodFP:1; + u32 supportExtendedSSCSize:1; + u32 supportDiskCacheSettingForSysPDs:1; + u32 supportCPLDUpdate:1; + u32 supportTTYLogCompression:1; u32 discardCacheDuringLDDelete:1; u32 supportSecurityonJBOD:1; u32 supportCacheBypassModes:1; @@ -1009,7 +1045,12 @@ struct megasas_ctrl_info { u32 supportCacheBypassModes:1; u32 supportSecurityonJBOD:1; u32 discardCacheDuringLDDelete:1; - u32 reserved:12; + u32 supportTTYLogCompression:1; + u32 supportCPLDUpdate:1; + u32 supportDiskCacheSettingForSysPDs:1; + u32 supportExtendedSSCSize:1; + u32 useSeqNumJbodFP:1; + u32 reserved:7; #endif } adapterOperations3; @@ -1042,11 +1083,6 @@ struct megasas_ctrl_info { #define VD_EXT_DEBUG 0 -enum MR_MFI_MPT_PTHR_FLAGS { - MFI_MPT_DETACHED = 0, - MFI_LIST_ADDED = 1, - MFI_MPT_ATTACHED = 2, -}; enum MR_SCSI_CMD_TYPE { READ_WRITE_LDIO = 0, @@ -1084,6 +1120,7 @@ enum MR_SCSI_CMD_TYPE { #define MEGASAS_SKINNY_INT_CMDS 5 #define MEGASAS_FUSION_INTERNAL_CMDS 5 #define MEGASAS_FUSION_IOCTL_CMDS 3 +#define MEGASAS_MFI_IOCTL_CMDS 27 #define MEGASAS_MAX_MSIX_QUEUES 128 /* @@ -1172,22 +1209,22 @@ struct megasas_register_set { struct megasas_sge32 { - u32 phys_addr; - u32 length; + __le32 phys_addr; + __le32 length; } __attribute__ ((packed)); struct megasas_sge64 { - u64 phys_addr; - u32 length; + __le64 phys_addr; + __le32 length; } __attribute__ ((packed)); struct megasas_sge_skinny { - u64 phys_addr; - u32 length; - u32 flag; + __le64 phys_addr; + __le32 length; + __le32 flag; } __packed; union megasas_sgl { @@ -1210,12 +1247,12 @@ struct megasas_header { u8 cdb_len; /*06h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ - u32 data_xferlen; /*14h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ + __le32 data_xferlen; /*14h */ } __attribute__ ((packed)); @@ -1229,7 +1266,9 @@ union megasas_sgl_frame { typedef union _MFI_CAPABILITIES { struct { #if defined(__BIG_ENDIAN_BITFIELD) - u32 reserved:25; + u32 reserved:23; + u32 support_ext_io_size:1; + u32 support_ext_queue_depth:1; u32 security_protocol_cmds_fw:1; u32 support_core_affinity:1; u32 support_ndrive_r1_lb:1; @@ -1245,10 +1284,12 @@ typedef union _MFI_CAPABILITIES { u32 support_ndrive_r1_lb:1; u32 support_core_affinity:1; u32 security_protocol_cmds_fw:1; - u32 reserved:25; + u32 support_ext_queue_depth:1; + u32 support_ext_io_size:1; + u32 reserved:23; #endif } mfi_capabilities; - u32 reg; + __le32 reg; } MFI_CAPABILITIES; struct megasas_init_frame { @@ -1260,33 +1301,35 @@ struct megasas_init_frame { u8 reserved_1; /*03h */ MFI_CAPABILITIES driver_operations; /*04h*/ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 reserved_3; /*12h */ - u32 data_xfer_len; /*14h */ + __le16 flags; /*10h */ + __le16 reserved_3; /*12h */ + __le32 data_xfer_len; /*14h */ - u32 queue_info_new_phys_addr_lo; /*18h */ - u32 queue_info_new_phys_addr_hi; /*1Ch */ - u32 queue_info_old_phys_addr_lo; /*20h */ - u32 queue_info_old_phys_addr_hi; /*24h */ - - u32 reserved_4[6]; /*28h */ + __le32 queue_info_new_phys_addr_lo; /*18h */ + __le32 queue_info_new_phys_addr_hi; /*1Ch */ + __le32 queue_info_old_phys_addr_lo; /*20h */ + __le32 queue_info_old_phys_addr_hi; /*24h */ + __le32 reserved_4[2]; /*28h */ + __le32 system_info_lo; /*30h */ + __le32 system_info_hi; /*34h */ + __le32 reserved_5[2]; /*38h */ } __attribute__ ((packed)); struct megasas_init_queue_info { - u32 init_flags; /*00h */ - u32 reply_queue_entries; /*04h */ + __le32 init_flags; /*00h */ + __le32 reply_queue_entries; /*04h */ - u32 reply_queue_start_phys_addr_lo; /*08h */ - u32 reply_queue_start_phys_addr_hi; /*0Ch */ - u32 producer_index_phys_addr_lo; /*10h */ - u32 producer_index_phys_addr_hi; /*14h */ - u32 consumer_index_phys_addr_lo; /*18h */ - u32 consumer_index_phys_addr_hi; /*1Ch */ + __le32 reply_queue_start_phys_addr_lo; /*08h */ + __le32 reply_queue_start_phys_addr_hi; /*0Ch */ + __le32 producer_index_phys_addr_lo; /*10h */ + __le32 producer_index_phys_addr_hi; /*14h */ + __le32 consumer_index_phys_addr_lo; /*18h */ + __le32 consumer_index_phys_addr_hi; /*1Ch */ } __attribute__ ((packed)); @@ -1302,18 +1345,18 @@ struct megasas_io_frame { u8 reserved_0; /*06h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ - u32 lba_count; /*14h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ + __le32 lba_count; /*14h */ - u32 sense_buf_phys_addr_lo; /*18h */ - u32 sense_buf_phys_addr_hi; /*1Ch */ + __le32 sense_buf_phys_addr_lo; /*18h */ + __le32 sense_buf_phys_addr_hi; /*1Ch */ - u32 start_lba_lo; /*20h */ - u32 start_lba_hi; /*24h */ + __le32 start_lba_lo; /*20h */ + __le32 start_lba_hi; /*24h */ union megasas_sgl sgl; /*28h */ @@ -1331,15 +1374,15 @@ struct megasas_pthru_frame { u8 cdb_len; /*06h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ - u32 data_xfer_len; /*14h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ + __le32 data_xfer_len; /*14h */ - u32 sense_buf_phys_addr_lo; /*18h */ - u32 sense_buf_phys_addr_hi; /*1Ch */ + __le32 sense_buf_phys_addr_lo; /*18h */ + __le32 sense_buf_phys_addr_hi; /*1Ch */ u8 cdb[16]; /*20h */ union megasas_sgl sgl; /*30h */ @@ -1354,19 +1397,19 @@ struct megasas_dcmd_frame { u8 reserved_1[4]; /*03h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ - u32 data_xfer_len; /*14h */ - u32 opcode; /*18h */ + __le32 data_xfer_len; /*14h */ + __le32 opcode; /*18h */ union { /*1Ch */ u8 b[12]; - u16 s[6]; - u32 w[3]; + __le16 s[6]; + __le32 w[3]; } mbox; union megasas_sgl sgl; /*28h */ @@ -1380,22 +1423,22 @@ struct megasas_abort_frame { u8 cmd_status; /*02h */ u8 reserved_1; /*03h */ - u32 reserved_2; /*04h */ + __le32 reserved_2; /*04h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 reserved_3; /*12h */ - u32 reserved_4; /*14h */ + __le16 flags; /*10h */ + __le16 reserved_3; /*12h */ + __le32 reserved_4; /*14h */ - u32 abort_context; /*18h */ - u32 pad_1; /*1Ch */ + __le32 abort_context; /*18h */ + __le32 pad_1; /*1Ch */ - u32 abort_mfi_phys_addr_lo; /*20h */ - u32 abort_mfi_phys_addr_hi; /*24h */ + __le32 abort_mfi_phys_addr_lo; /*20h */ + __le32 abort_mfi_phys_addr_hi; /*24h */ - u32 reserved_5[6]; /*28h */ + __le32 reserved_5[6]; /*28h */ } __attribute__ ((packed)); @@ -1409,14 +1452,14 @@ struct megasas_smp_frame { u8 reserved_2[3]; /*04h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ - u32 data_xfer_len; /*14h */ - u64 sas_addr; /*18h */ + __le32 data_xfer_len; /*14h */ + __le64 sas_addr; /*18h */ union { struct megasas_sge32 sge32[2]; /* [0]: resp [1]: req */ @@ -1436,16 +1479,16 @@ struct megasas_stp_frame { u8 reserved_3[2]; /*05h */ u8 sge_count; /*07h */ - u32 context; /*08h */ - u32 pad_0; /*0Ch */ + __le32 context; /*08h */ + __le32 pad_0; /*0Ch */ - u16 flags; /*10h */ - u16 timeout; /*12h */ + __le16 flags; /*10h */ + __le16 timeout; /*12h */ - u32 data_xfer_len; /*14h */ + __le32 data_xfer_len; /*14h */ - u16 fis[10]; /*18h */ - u32 stp_flags; + __le16 fis[10]; /*18h */ + __le32 stp_flags; union { struct megasas_sge32 sge32[2]; /* [0]: resp [1]: data */ @@ -1489,18 +1532,18 @@ union megasas_evt_class_locale { } __attribute__ ((packed)); struct megasas_evt_log_info { - u32 newest_seq_num; - u32 oldest_seq_num; - u32 clear_seq_num; - u32 shutdown_seq_num; - u32 boot_seq_num; + __le32 newest_seq_num; + __le32 oldest_seq_num; + __le32 clear_seq_num; + __le32 shutdown_seq_num; + __le32 boot_seq_num; } __attribute__ ((packed)); struct megasas_progress { - u16 progress; - u16 elapsed_seconds; + __le16 progress; + __le16 elapsed_seconds; } __attribute__ ((packed)); @@ -1521,9 +1564,9 @@ struct megasas_evtarg_pd { struct megasas_evt_detail { - u32 seq_num; - u32 time_stamp; - u32 code; + __le32 seq_num; + __le32 time_stamp; + __le32 code; union megasas_evt_class_locale cl; u8 arg_type; u8 reserved1[15]; @@ -1542,18 +1585,18 @@ struct megasas_evt_detail { struct { struct megasas_evtarg_ld ld; - u64 count; + __le64 count; } __attribute__ ((packed)) ld_count; struct { - u64 lba; + __le64 lba; struct megasas_evtarg_ld ld; } __attribute__ ((packed)) ld_lba; struct { struct megasas_evtarg_ld ld; - u32 prevOwner; - u32 newOwner; + __le32 prevOwner; + __le32 newOwner; } __attribute__ ((packed)) ld_owner; struct { @@ -1610,7 +1653,7 @@ struct megasas_evt_detail { struct { u16 vendorId; - u16 deviceId; + __le16 deviceId; u16 subVendorId; u16 subDeviceId; } __attribute__ ((packed)) pci; @@ -1630,9 +1673,9 @@ struct megasas_evt_detail { } __attribute__ ((packed)) ecc; u8 b[96]; - u16 s[48]; - u32 w[24]; - u64 d[12]; + __le16 s[48]; + __le32 w[24]; + __le64 d[12]; } args; char description[128]; @@ -1649,12 +1692,22 @@ struct megasas_irq_context { u32 MSIxIndex; }; +struct MR_DRV_SYSTEM_INFO { + u8 infoVersion; + u8 systemIdLength; + u16 reserved0; + u8 systemId[64]; + u8 reserved[1980]; +}; + struct megasas_instance { - u32 *producer; + __le32 *producer; dma_addr_t producer_h; - u32 *consumer; + __le32 *consumer; dma_addr_t consumer_h; + struct MR_DRV_SYSTEM_INFO *system_info_buf; + dma_addr_t system_info_h; struct MR_LD_VF_AFFILIATION *vf_affiliation; dma_addr_t vf_affiliation_h; struct MR_LD_VF_AFFILIATION_111 *vf_affiliation_111; @@ -1662,7 +1715,7 @@ struct megasas_instance { struct MR_CTRL_HB_HOST_MEM *hb_host_mem; dma_addr_t hb_host_mem_h; - u32 *reply_queue; + __le32 *reply_queue; dma_addr_t reply_queue_h; u32 *crash_dump_buf; @@ -1678,10 +1731,11 @@ struct megasas_instance { u32 crash_dump_drv_support; u32 crash_dump_app_support; u32 secure_jbod_support; + bool use_seqnum_jbod_fp; /* Added for PD sequence */ spinlock_t crashdump_lock; struct megasas_register_set __iomem *reg_set; - u32 *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY]; + u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY]; struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; struct megasas_pd_list local_pd_list[MEGASAS_MAX_PD]; u8 ld_ids[MEGASAS_MAX_LD_IDS]; @@ -1736,6 +1790,7 @@ struct megasas_instance { u8 UnevenSpanSupport; u8 supportmax256vd; + u8 allow_fw_scan; u16 fw_supported_vd_count; u16 fw_supported_pd_count; @@ -1757,7 +1812,9 @@ struct megasas_instance { struct msix_entry msixentry[MEGASAS_MAX_MSIX_QUEUES]; struct megasas_irq_context irq_context[MEGASAS_MAX_MSIX_QUEUES]; u64 map_id; + u64 pd_seq_map_id; struct megasas_cmd *map_update_cmd; + struct megasas_cmd *jbod_seq_cmd; unsigned long bar; long reset_flags; struct mutex reset_mutex; @@ -1768,7 +1825,9 @@ struct megasas_instance { char mpio; u16 throttlequeuedepth; u8 mask_interrupts; + u16 max_chain_frame_sz; u8 is_imr; + bool dev_handle; }; struct MR_LD_VF_MAP { u32 size; @@ -1864,9 +1923,13 @@ struct megasas_instance_template { #define MEGASAS_IS_LOGICAL(scp) \ (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 -#define MEGASAS_DEV_INDEX(inst, scp) \ - ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ - scp->device->id +#define MEGASAS_DEV_INDEX(scp) \ + (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ + scp->device->id) + +#define MEGASAS_PD_INDEX(scp) \ + ((scp->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + \ + scp->device->id) struct megasas_cmd { @@ -1877,17 +1940,14 @@ struct megasas_cmd { u32 index; u8 sync_cmd; - u8 cmd_status; + u8 cmd_status_drv; u8 abort_aen; u8 retry_for_fw_reset; struct list_head list; struct scsi_cmnd *scmd; - - void *mpt_pthr_cmd_blocked; - atomic_t mfi_mpt_pthr; - u8 is_wait_event; + u8 flags; struct megasas_instance *instance; union { @@ -1963,14 +2023,17 @@ u8 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_DRV_RAID_MAP_ALL *map); struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_DRV_RAID_MAP_ALL *map); u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_DRV_RAID_MAP_ALL *map); u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map); -u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map); +__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map); u16 MR_GetLDTgtId(u32 ld, struct MR_DRV_RAID_MAP_ALL *map); -u16 get_updated_dev_handle(struct megasas_instance *instance, +__le16 get_updated_dev_handle(struct megasas_instance *instance, struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *in_info); void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map, struct LD_LOAD_BALANCE_INFO *lbInfo); int megasas_get_ctrl_info(struct megasas_instance *instance); +/* PD sequence */ +int +megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend); int megasas_set_crash_dump_params(struct megasas_instance *instance, u8 crash_buf_state); void megasas_free_host_crash_buffer(struct megasas_instance *instance); @@ -1986,5 +2049,6 @@ void __megasas_return_cmd(struct megasas_instance *instance, void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance, struct megasas_cmd *cmd_mfi, struct megasas_cmd_fusion *cmd_fusion); int megasas_cmd_type(struct scsi_cmnd *cmd); +void megasas_setup_jbod_map(struct megasas_instance *instance); #endif /*LSI_MEGARAID_SAS_H */ diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas_base.c b/kernel/drivers/scsi/megaraid/megaraid_sas_base.c index 890637fdd..97a1c1c33 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/kernel/drivers/scsi/megaraid/megaraid_sas_base.c @@ -94,8 +94,8 @@ MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Defau MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); -MODULE_AUTHOR("megaraidlinux@lsi.com"); -MODULE_DESCRIPTION("LSI MegaRAID SAS Driver"); +MODULE_AUTHOR("megaraidlinux.pdl@avagotech.com"); +MODULE_DESCRIPTION("Avago MegaRAID SAS Driver"); int megasas_transition_to_ready(struct megasas_instance *instance, int ocr); static int megasas_get_pd_list(struct megasas_instance *instance); @@ -135,6 +135,12 @@ static struct pci_device_id megasas_pci_table[] = { /* Invader */ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FURY)}, /* Fury */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER)}, + /* Intruder */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER_24)}, + /* Intruder 24 port*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_52)}, + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_53)}, {} }; @@ -215,9 +221,8 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance cmd = list_entry((&instance->cmd_pool)->next, struct megasas_cmd, list); list_del_init(&cmd->list); - atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_DETACHED); } else { - printk(KERN_ERR "megasas: Command pool empty!\n"); + dev_err(&instance->pdev->dev, "Command pool empty!\n"); } spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); @@ -225,52 +230,101 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance } /** - * __megasas_return_cmd - Return a cmd to free command pool + * megasas_return_cmd - Return a cmd to free command pool * @instance: Adapter soft state * @cmd: Command packet to be returned to free command pool */ inline void -__megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) +megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { - /* - * Don't go ahead and free the MFI frame, if corresponding - * MPT frame is not freed(valid for only fusion adapters). - * In case of MFI adapters, anyways for any allocated MFI - * frame will have cmd->mfi_mpt_mpthr set to MFI_MPT_DETACHED + unsigned long flags; + u32 blk_tags; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion = instance->ctrl_context; + + /* This flag is used only for fusion adapter. + * Wait for Interrupt for Polled mode DCMD */ - if (atomic_read(&cmd->mfi_mpt_pthr) != MFI_MPT_DETACHED) + if (cmd->flags & DRV_DCMD_POLLED_MODE) return; + spin_lock_irqsave(&instance->mfi_pool_lock, flags); + + if (fusion) { + blk_tags = instance->max_scsi_cmds + cmd->index; + cmd_fusion = fusion->cmd_list[blk_tags]; + megasas_return_cmd_fusion(instance, cmd_fusion); + } cmd->scmd = NULL; cmd->frame_count = 0; - cmd->is_wait_event = 0; - cmd->mpt_pthr_cmd_blocked = NULL; - - if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) && - (reset_devices)) + cmd->flags = 0; + if (!fusion && reset_devices) cmd->frame->hdr.cmd = MFI_CMD_INVALID; - - atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED); list_add(&cmd->list, (&instance->cmd_pool)->next); + + spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); + } -/** - * megasas_return_cmd - Return a cmd to free command pool - * @instance: Adapter soft state - * @cmd: Command packet to be returned to free command pool - */ -inline void -megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) +static const char * +format_timestamp(uint32_t timestamp) { - unsigned long flags; + static char buffer[32]; - spin_lock_irqsave(&instance->mfi_pool_lock, flags); - __megasas_return_cmd(instance, cmd); - spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); + if ((timestamp & 0xff000000) == 0xff000000) + snprintf(buffer, sizeof(buffer), "boot + %us", timestamp & + 0x00ffffff); + else + snprintf(buffer, sizeof(buffer), "%us", timestamp); + return buffer; +} + +static const char * +format_class(int8_t class) +{ + static char buffer[6]; + + switch (class) { + case MFI_EVT_CLASS_DEBUG: + return "debug"; + case MFI_EVT_CLASS_PROGRESS: + return "progress"; + case MFI_EVT_CLASS_INFO: + return "info"; + case MFI_EVT_CLASS_WARNING: + return "WARN"; + case MFI_EVT_CLASS_CRITICAL: + return "CRIT"; + case MFI_EVT_CLASS_FATAL: + return "FATAL"; + case MFI_EVT_CLASS_DEAD: + return "DEAD"; + default: + snprintf(buffer, sizeof(buffer), "%d", class); + return buffer; + } } +/** + * megasas_decode_evt: Decode FW AEN event and print critical event + * for information. + * @instance: Adapter soft state + */ +static void +megasas_decode_evt(struct megasas_instance *instance) +{ + struct megasas_evt_detail *evt_detail = instance->evt_detail; + union megasas_evt_class_locale class_locale; + class_locale.word = le32_to_cpu(evt_detail->cl.word); + + if (class_locale.members.class >= MFI_EVT_CLASS_CRITICAL) + dev_info(&instance->pdev->dev, "%d (%s/0x%04x/%s) - %s\n", + le32_to_cpu(evt_detail->seq_num), + format_timestamp(le32_to_cpu(evt_detail->time_stamp)), + (class_locale.members.locale), + format_class(class_locale.members.class), + evt_detail->description); +} /** * The following functions are defined for xscale @@ -285,6 +339,7 @@ static inline void megasas_enable_intr_xscale(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; + regs = instance->reg_set; writel(0, &(regs)->outbound_intr_mask); @@ -301,6 +356,7 @@ megasas_disable_intr_xscale(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; u32 mask = 0x1f; + regs = instance->reg_set; writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ @@ -325,6 +381,7 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) { u32 status; u32 mfiStatus = 0; + /* * Check if it is our interrupt */ @@ -360,6 +417,7 @@ megasas_fire_cmd_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel((frame_phys_addr >> 3)|(frame_count), &(regs)->inbound_queue_port); @@ -376,15 +434,16 @@ megasas_adp_reset_xscale(struct megasas_instance *instance, { u32 i; u32 pcidata; + writel(MFI_ADP_RESET, ®s->inbound_doorbell); for (i = 0; i < 3; i++) msleep(1000); /* sleep for 3 secs */ pcidata = 0; pci_read_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, &pcidata); - printk(KERN_NOTICE "pcidata = %x\n", pcidata); + dev_notice(&instance->pdev->dev, "pcidata = %x\n", pcidata); if (pcidata & 0x2) { - printk(KERN_NOTICE "mfi 1068 offset read=%x\n", pcidata); + dev_notice(&instance->pdev->dev, "mfi 1068 offset read=%x\n", pcidata); pcidata &= ~0x2; pci_write_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, pcidata); @@ -395,9 +454,9 @@ megasas_adp_reset_xscale(struct megasas_instance *instance, pcidata = 0; pci_read_config_dword(instance->pdev, MFI_1068_FW_HANDSHAKE_OFFSET, &pcidata); - printk(KERN_NOTICE "1068 offset handshake read=%x\n", pcidata); + dev_notice(&instance->pdev->dev, "1068 offset handshake read=%x\n", pcidata); if ((pcidata & 0xffff0000) == MFI_1068_FW_READY) { - printk(KERN_NOTICE "1068 offset pcidt=%x\n", pcidata); + dev_notice(&instance->pdev->dev, "1068 offset pcidt=%x\n", pcidata); pcidata = 0; pci_write_config_dword(instance->pdev, MFI_1068_FW_HANDSHAKE_OFFSET, pcidata); @@ -414,7 +473,6 @@ static int megasas_check_reset_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { - if ((instance->adprecovery != MEGASAS_HBA_OPERATIONAL) && (le32_to_cpu(*instance->consumer) == MEGASAS_ADPRESET_INPROG_SIGN)) @@ -445,7 +503,7 @@ static struct megasas_instance_template megasas_instance_template_xscale = { /** * The following functions are defined for ppc (deviceid : 0x60) -* controllers +* controllers */ /** @@ -456,6 +514,7 @@ static inline void megasas_enable_intr_ppc(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; + regs = instance->reg_set; writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); @@ -474,6 +533,7 @@ megasas_disable_intr_ppc(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; u32 mask = 0xFFFFFFFF; + regs = instance->reg_set; writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ @@ -534,6 +594,7 @@ megasas_fire_cmd_ppc(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel((frame_phys_addr | (frame_count<<1))|1, &(regs)->inbound_queue_port); @@ -578,6 +639,7 @@ static inline void megasas_enable_intr_skinny(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; + regs = instance->reg_set; writel(0xFFFFFFFF, &(regs)->outbound_intr_mask); @@ -596,6 +658,7 @@ megasas_disable_intr_skinny(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; u32 mask = 0xFFFFFFFF; + regs = instance->reg_set; writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ @@ -646,8 +709,8 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) writel(status, ®s->outbound_intr_status); /* - * dummy read to flush PCI - */ + * dummy read to flush PCI + */ readl(®s->outbound_intr_status); return mfiStatus; @@ -666,6 +729,7 @@ megasas_fire_cmd_skinny(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel(upper_32_bits(frame_phys_addr), &(regs)->inbound_high_queue_port); @@ -718,6 +782,7 @@ static inline void megasas_enable_intr_gen2(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; + regs = instance->reg_set; writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); @@ -737,6 +802,7 @@ megasas_disable_intr_gen2(struct megasas_instance *instance) { struct megasas_register_set __iomem *regs; u32 mask = 0xFFFFFFFF; + regs = instance->reg_set; writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ @@ -762,6 +828,7 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) { u32 status; u32 mfiStatus = 0; + /* * Check if it is our interrupt */ @@ -798,6 +865,7 @@ megasas_fire_cmd_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel((frame_phys_addr | (frame_count<<1))|1, &(regs)->inbound_queue_port); @@ -812,10 +880,10 @@ static int megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem *reg_set) { - u32 retry = 0 ; - u32 HostDiag; - u32 *seq_offset = ®_set->seq_offset; - u32 *hostdiag_offset = ®_set->host_diag; + u32 retry = 0 ; + u32 HostDiag; + u32 __iomem *seq_offset = ®_set->seq_offset; + u32 __iomem *hostdiag_offset = ®_set->host_diag; if (instance->instancet == &megasas_instance_template_skinny) { seq_offset = ®_set->fusion_seq_offset; @@ -833,10 +901,10 @@ megasas_adp_reset_gen2(struct megasas_instance *instance, HostDiag = (u32)readl(hostdiag_offset); - while ( !( HostDiag & DIAG_WRITE_ENABLE) ) { + while (!(HostDiag & DIAG_WRITE_ENABLE)) { msleep(100); HostDiag = (u32)readl(hostdiag_offset); - printk(KERN_NOTICE "RESETGEN2: retry=%x, hostdiag=%x\n", + dev_notice(&instance->pdev->dev, "RESETGEN2: retry=%x, hostdiag=%x\n", retry, HostDiag); if (retry++ >= 100) @@ -844,17 +912,17 @@ megasas_adp_reset_gen2(struct megasas_instance *instance, } - printk(KERN_NOTICE "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag); + dev_notice(&instance->pdev->dev, "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag); writel((HostDiag | DIAG_RESET_ADAPTER), hostdiag_offset); ssleep(10); HostDiag = (u32)readl(hostdiag_offset); - while ( ( HostDiag & DIAG_RESET_ADAPTER) ) { + while (HostDiag & DIAG_RESET_ADAPTER) { msleep(100); HostDiag = (u32)readl(hostdiag_offset); - printk(KERN_NOTICE "RESET_GEN2: retry=%x, hostdiag=%x\n", + dev_notice(&instance->pdev->dev, "RESET_GEN2: retry=%x, hostdiag=%x\n", retry, HostDiag); if (retry++ >= 1000) @@ -910,13 +978,12 @@ extern struct megasas_instance_template megasas_instance_template_fusion; * @instance: Adapter soft state * @cmd: Command packet to be issued * - * For polling, MFI requires the cmd_status to be set to 0xFF before posting. + * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting. */ int megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) { int seconds; - struct megasas_header *frame_hdr = &cmd->frame->hdr; frame_hdr->cmd_status = MFI_CMD_STATUS_POLL_MODE; @@ -952,20 +1019,21 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, int timeout) { int ret = 0; - cmd->cmd_status = ENODATA; - cmd->is_wait_event = 1; + cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; + instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->int_cmd_wait_q, - cmd->cmd_status != ENODATA, timeout * HZ); + cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); if (!ret) return 1; } else wait_event(instance->int_cmd_wait_q, - cmd->cmd_status != ENODATA); + cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS); - return 0; + return (cmd->cmd_status_drv == MFI_STAT_OK) ? + 0 : 1; } /** @@ -998,7 +1066,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, * Prepare and issue the abort frame */ abort_fr->cmd = MFI_CMD_ABORT; - abort_fr->cmd_status = 0xFF; + abort_fr->cmd_status = MFI_STAT_INVALID_STATUS; abort_fr->flags = cpu_to_le16(0); abort_fr->abort_context = cpu_to_le32(cmd_to_abort->index); abort_fr->abort_mfi_phys_addr_lo = @@ -1007,13 +1075,13 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr)); cmd->sync_cmd = 1; - cmd->cmd_status = ENODATA; + cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; instance->instancet->issue_dcmd(instance, cmd); if (timeout) { ret = wait_event_timeout(instance->abort_cmd_wait_q, - cmd->cmd_status != ENODATA, timeout * HZ); + cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ); if (!ret) { dev_err(&instance->pdev->dev, "Command timedout" "from %s\n", __func__); @@ -1021,7 +1089,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, } } else wait_event(instance->abort_cmd_wait_q, - cmd->cmd_status != ENODATA); + cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS); cmd->sync_cmd = 0; @@ -1132,7 +1200,7 @@ static u32 megasas_get_frame_count(struct megasas_instance *instance, int num_cnt; int sge_bytes; u32 sge_sz; - u32 frame_count=0; + u32 frame_count = 0; sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); @@ -1163,14 +1231,14 @@ static u32 megasas_get_frame_count(struct megasas_instance *instance, num_cnt = sge_count - 3; } - if(num_cnt>0){ + if (num_cnt > 0) { sge_bytes = sge_sz * num_cnt; frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) ; } /* Main frame */ - frame_count +=1; + frame_count += 1; if (frame_count > 7) frame_count = 8; @@ -1196,7 +1264,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_pthru_frame *pthru; is_logical = MEGASAS_IS_LOGICAL(scp); - device_id = MEGASAS_DEV_INDEX(instance, scp); + device_id = MEGASAS_DEV_INDEX(scp); pthru = (struct megasas_pthru_frame *)cmd->frame; if (scp->sc_data_direction == PCI_DMA_TODEVICE) @@ -1227,12 +1295,12 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); /* - * If the command is for the tape device, set the - * pthru timeout to the os layer timeout value. - */ + * If the command is for the tape device, set the + * pthru timeout to the os layer timeout value. + */ if (scp->device->type == TYPE_TAPE) { if ((scp->request->timeout / HZ) > 0xFFFF) - pthru->timeout = 0xFFFF; + pthru->timeout = cpu_to_le16(0xFFFF); else pthru->timeout = cpu_to_le16(scp->request->timeout / HZ); } @@ -1253,7 +1321,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, &pthru->sgl); if (pthru->sge_count > instance->max_num_sge) { - printk(KERN_ERR "megasas: DCDB two many SGE NUM=%x\n", + dev_err(&instance->pdev->dev, "DCDB too many SGE NUM=%x\n", pthru->sge_count); return 0; } @@ -1294,7 +1362,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, u16 flags = 0; struct megasas_io_frame *ldio; - device_id = MEGASAS_DEV_INDEX(instance, scp); + device_id = MEGASAS_DEV_INDEX(scp); ldio = (struct megasas_io_frame *)cmd->frame; if (scp->sc_data_direction == PCI_DMA_TODEVICE) @@ -1394,7 +1462,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); if (ldio->sge_count > instance->max_num_sge) { - printk(KERN_ERR "megasas: build_ld_io: sge_count = %x\n", + dev_err(&instance->pdev->dev, "build_ld_io: sge_count = %x\n", ldio->sge_count); return 0; } @@ -1447,7 +1515,7 @@ inline int megasas_cmd_type(struct scsi_cmnd *cmd) /** * megasas_dump_pending_frames - Dumps the frame address of all pending cmds - * in FW + * in FW * @instance: Adapter soft state */ static inline void @@ -1461,63 +1529,60 @@ megasas_dump_pending_frames(struct megasas_instance *instance) u32 sgcount; u32 max_cmd = instance->max_fw_cmds; - printk(KERN_ERR "\nmegasas[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no); - printk(KERN_ERR "megasas[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding)); + dev_err(&instance->pdev->dev, "[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding)); if (IS_DMA64) - printk(KERN_ERR "\nmegasas[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no); else - printk(KERN_ERR "\nmegasas[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no); - printk(KERN_ERR "megasas[%d]: Pending OS cmds in FW : \n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: Pending OS cmds in FW : \n",instance->host->host_no); for (i = 0; i < max_cmd; i++) { cmd = instance->cmd_list[i]; - if(!cmd->scmd) + if (!cmd->scmd) continue; - printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr); + dev_err(&instance->pdev->dev, "[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr); if (megasas_cmd_type(cmd->scmd) == READ_WRITE_LDIO) { ldio = (struct megasas_io_frame *)cmd->frame; mfi_sgl = &ldio->sgl; sgcount = ldio->sge_count; - printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x," + dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x," " lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n", instance->host->host_no, cmd->frame_count, ldio->cmd, ldio->target_id, le32_to_cpu(ldio->start_lba_lo), le32_to_cpu(ldio->start_lba_hi), le32_to_cpu(ldio->sense_buf_phys_addr_lo), sgcount); - } - else { + } else { pthru = (struct megasas_pthru_frame *) cmd->frame; mfi_sgl = &pthru->sgl; sgcount = pthru->sge_count; - printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, " + dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, " "lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n", instance->host->host_no, cmd->frame_count, pthru->cmd, pthru->target_id, pthru->lun, pthru->cdb_len, le32_to_cpu(pthru->data_xfer_len), le32_to_cpu(pthru->sense_buf_phys_addr_lo), sgcount); } - if(megasas_dbg_lvl & MEGASAS_DBG_LVL){ - for (n = 0; n < sgcount; n++){ - if (IS_DMA64) - printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%llx ", - le32_to_cpu(mfi_sgl->sge64[n].length), - le64_to_cpu(mfi_sgl->sge64[n].phys_addr)); - else - printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%x ", - le32_to_cpu(mfi_sgl->sge32[n].length), - le32_to_cpu(mfi_sgl->sge32[n].phys_addr)); + if (megasas_dbg_lvl & MEGASAS_DBG_LVL) { + for (n = 0; n < sgcount; n++) { + if (IS_DMA64) + dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%llx\n", + le32_to_cpu(mfi_sgl->sge64[n].length), + le64_to_cpu(mfi_sgl->sge64[n].phys_addr)); + else + dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%x\n", + le32_to_cpu(mfi_sgl->sge32[n].length), + le32_to_cpu(mfi_sgl->sge32[n].phys_addr)); } } - printk(KERN_ERR "\n"); } /*for max_cmd*/ - printk(KERN_ERR "\nmegasas[%d]: Pending Internal cmds in FW : \n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: Pending Internal cmds in FW : \n",instance->host->host_no); for (i = 0; i < max_cmd; i++) { cmd = instance->cmd_list[i]; - if(cmd->sync_cmd == 1){ - printk(KERN_ERR "0x%08lx : ", (unsigned long)cmd->frame_phys_addr); - } + if (cmd->sync_cmd == 1) + dev_err(&instance->pdev->dev, "0x%08lx : ", (unsigned long)cmd->frame_phys_addr); } - printk(KERN_ERR "megasas[%d]: Dumping Done.\n\n",instance->host->host_no); + dev_err(&instance->pdev->dev, "[%d]: Dumping Done\n\n",instance->host->host_no); } u32 @@ -1635,7 +1700,7 @@ megasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) } if (instance->instancet->build_and_issue_cmd(instance, scmd)) { - printk(KERN_ERR "megasas: Err returned from build_and_issue_cmd\n"); + dev_err(&instance->pdev->dev, "Err returned from build_and_issue_cmd\n"); return SCSI_MLQUEUE_HOST_BUSY; } @@ -1660,11 +1725,59 @@ static struct megasas_instance *megasas_lookup_instance(u16 host_no) return NULL; } +/* +* megasas_set_dma_alignment - Set DMA alignment for PI enabled VD +* +* @sdev: OS provided scsi device +* +* Returns void +*/ +static void megasas_set_dma_alignment(struct scsi_device *sdev) +{ + u32 device_id, ld; + struct megasas_instance *instance; + struct fusion_context *fusion; + struct MR_LD_RAID *raid; + struct MR_DRV_RAID_MAP_ALL *local_map_ptr; + + instance = megasas_lookup_instance(sdev->host->host_no); + fusion = instance->ctrl_context; + + if (!fusion) + return; + + if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS) { + device_id = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + + sdev->id; + local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; + ld = MR_TargetIdToLdGet(device_id, local_map_ptr); + raid = MR_LdRaidGet(ld, local_map_ptr); + + if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) + blk_queue_update_dma_alignment(sdev->request_queue, 0x7); + } +} + static int megasas_slave_configure(struct scsi_device *sdev) { + u16 pd_index = 0; + struct megasas_instance *instance; + + instance = megasas_lookup_instance(sdev->host->host_no); + if (instance->allow_fw_scan) { + if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && + sdev->type == TYPE_DISK) { + pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + + sdev->id; + if (instance->pd_list[pd_index].driveState != + MR_PD_STATE_SYSTEM) + return -ENXIO; + } + } + megasas_set_dma_alignment(sdev); /* - * The RAID firmware may require extended timeouts. - */ + * The RAID firmware may require extended timeouts. + */ blk_queue_rq_timeout(sdev->request_queue, MEGASAS_DEFAULT_CMD_TIMEOUT * HZ); @@ -1673,8 +1786,9 @@ static int megasas_slave_configure(struct scsi_device *sdev) static int megasas_slave_alloc(struct scsi_device *sdev) { - u16 pd_index = 0; + u16 pd_index = 0; struct megasas_instance *instance ; + instance = megasas_lookup_instance(sdev->host->host_no); if (sdev->channel < MEGASAS_MAX_PD_CHANNELS) { /* @@ -1683,8 +1797,8 @@ static int megasas_slave_alloc(struct scsi_device *sdev) pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; - if (instance->pd_list[pd_index].driveState == - MR_PD_STATE_SYSTEM) { + if ((instance->allow_fw_scan || instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM)) { return 0; } return -ENXIO; @@ -1698,7 +1812,7 @@ static int megasas_slave_alloc(struct scsi_device *sdev) * @instance: Adapter soft state * */ -void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) +static void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) { int i; struct megasas_cmd *cmd_mfi; @@ -1736,12 +1850,8 @@ void megaraid_sas_kill_hba(struct megasas_instance *instance) msleep(1000); if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { - writel(MFI_STOP_ADP, - &instance->reg_set->doorbell); + (instance->ctrl_context)) { + writel(MFI_STOP_ADP, &instance->reg_set->doorbell); /* Flush */ readl(&instance->reg_set->doorbell); if (instance->mpio && instance->requestorId) @@ -1795,7 +1905,7 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) unsigned long flags; /* If we have already declared adapter dead, donot complete cmds */ - if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR ) + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) return; spin_lock_irqsave(&instance->completion_lock, flags); @@ -1806,7 +1916,7 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) while (consumer != producer) { context = le32_to_cpu(instance->reply_queue[consumer]); if (context >= instance->max_fw_cmds) { - printk(KERN_ERR "Unexpected context value %x\n", + dev_err(&instance->pdev->dev, "Unexpected context value %x\n", context); BUG(); } @@ -1885,8 +1995,8 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: megasas_get_ld_vf_affiliation_111:" - "Failed to get cmd for scsi%d.\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation_111:" + "Failed to get cmd for scsi%d\n", instance->host->host_no); return -ENOMEM; } @@ -1894,8 +2004,8 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, dcmd = &cmd->frame->dcmd; if (!instance->vf_affiliation_111) { - printk(KERN_WARNING "megasas: SR-IOV: Couldn't get LD/VF " - "affiliation for scsi%d.\n", instance->host->host_no); + dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " + "affiliation for scsi%d\n", instance->host->host_no); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -1909,8 +2019,8 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, sizeof(struct MR_LD_VF_AFFILIATION_111), &new_affiliation_111_h); if (!new_affiliation_111) { - printk(KERN_DEBUG "megasas: SR-IOV: Couldn't allocate " - "memory for new affiliation for scsi%d.\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " + "memory for new affiliation for scsi%d\n", instance->host->host_no); megasas_return_cmd(instance, cmd); return -ENOMEM; @@ -1922,31 +2032,33 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; - dcmd->flags = MFI_FRAME_DIR_BOTH; + dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); dcmd->timeout = 0; dcmd->pad_0 = 0; - dcmd->data_xfer_len = sizeof(struct MR_LD_VF_AFFILIATION_111); - dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111; + dcmd->data_xfer_len = + cpu_to_le32(sizeof(struct MR_LD_VF_AFFILIATION_111)); + dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111); if (initial) dcmd->sgl.sge32[0].phys_addr = - instance->vf_affiliation_111_h; + cpu_to_le32(instance->vf_affiliation_111_h); else - dcmd->sgl.sge32[0].phys_addr = new_affiliation_111_h; + dcmd->sgl.sge32[0].phys_addr = + cpu_to_le32(new_affiliation_111_h); - dcmd->sgl.sge32[0].length = - sizeof(struct MR_LD_VF_AFFILIATION_111); + dcmd->sgl.sge32[0].length = cpu_to_le32( + sizeof(struct MR_LD_VF_AFFILIATION_111)); - printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for " + dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " "scsi%d\n", instance->host->host_no); megasas_issue_blocked_cmd(instance, cmd, 0); if (dcmd->cmd_status) { - printk(KERN_WARNING "megasas: SR-IOV: LD/VF affiliation DCMD" - " failed with status 0x%x for scsi%d.\n", + dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" + " failed with status 0x%x for scsi%d\n", dcmd->cmd_status, instance->host->host_no); retval = 1; /* Do a scan if we couldn't get affiliation */ goto out; @@ -1957,9 +2069,8 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, for (ld = 0 ; ld < new_affiliation_111->vdCount; ld++) if (instance->vf_affiliation_111->map[ld].policy[thisVf] != new_affiliation_111->map[ld].policy[thisVf]) { - printk(KERN_WARNING "megasas: SR-IOV: " - "Got new LD/VF affiliation " - "for scsi%d.\n", + dev_warn(&instance->pdev->dev, "SR-IOV: " + "Got new LD/VF affiliation for scsi%d\n", instance->host->host_no); memcpy(instance->vf_affiliation_111, new_affiliation_111, @@ -1976,11 +2087,7 @@ out: new_affiliation_111_h); } - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return retval; } @@ -1999,8 +2106,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: megasas_get_ld_vf_affiliation12: " - "Failed to get cmd for scsi%d.\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation12: " + "Failed to get cmd for scsi%d\n", instance->host->host_no); return -ENOMEM; } @@ -2008,8 +2115,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, dcmd = &cmd->frame->dcmd; if (!instance->vf_affiliation) { - printk(KERN_WARNING "megasas: SR-IOV: Couldn't get LD/VF " - "affiliation for scsi%d.\n", instance->host->host_no); + dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " + "affiliation for scsi%d\n", instance->host->host_no); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -2024,8 +2131,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, sizeof(struct MR_LD_VF_AFFILIATION), &new_affiliation_h); if (!new_affiliation) { - printk(KERN_DEBUG "megasas: SR-IOV: Couldn't allocate " - "memory for new affiliation for scsi%d.\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " + "memory for new affiliation for scsi%d\n", instance->host->host_no); megasas_return_cmd(instance, cmd); return -ENOMEM; @@ -2037,31 +2144,33 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; - dcmd->flags = MFI_FRAME_DIR_BOTH; + dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); dcmd->timeout = 0; dcmd->pad_0 = 0; - dcmd->data_xfer_len = (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION); - dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS; + dcmd->data_xfer_len = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) * + sizeof(struct MR_LD_VF_AFFILIATION)); + dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS); if (initial) - dcmd->sgl.sge32[0].phys_addr = instance->vf_affiliation_h; + dcmd->sgl.sge32[0].phys_addr = + cpu_to_le32(instance->vf_affiliation_h); else - dcmd->sgl.sge32[0].phys_addr = new_affiliation_h; + dcmd->sgl.sge32[0].phys_addr = + cpu_to_le32(new_affiliation_h); - dcmd->sgl.sge32[0].length = (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION); + dcmd->sgl.sge32[0].length = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) * + sizeof(struct MR_LD_VF_AFFILIATION)); - printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for " + dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " "scsi%d\n", instance->host->host_no); megasas_issue_blocked_cmd(instance, cmd, 0); if (dcmd->cmd_status) { - printk(KERN_WARNING "megasas: SR-IOV: LD/VF affiliation DCMD" - " failed with status 0x%x for scsi%d.\n", + dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" + " failed with status 0x%x for scsi%d\n", dcmd->cmd_status, instance->host->host_no); retval = 1; /* Do a scan if we couldn't get affiliation */ goto out; @@ -2069,8 +2178,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, if (!initial) { if (!new_affiliation->ldCount) { - printk(KERN_WARNING "megasas: SR-IOV: Got new LD/VF " - "affiliation for passive path for scsi%d.\n", + dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF " + "affiliation for passive path for scsi%d\n", instance->host->host_no); retval = 1; goto out; @@ -2135,8 +2244,8 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, } out: if (doscan) { - printk(KERN_WARNING "megasas: SR-IOV: Got new LD/VF " - "affiliation for scsi%d.\n", instance->host->host_no); + dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF " + "affiliation for scsi%d\n", instance->host->host_no); memcpy(instance->vf_affiliation, new_affiliation, new_affiliation->size); retval = 1; @@ -2147,11 +2256,7 @@ out: (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION), new_affiliation, new_affiliation_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return retval; } @@ -2180,8 +2285,8 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: megasas_sriov_start_heartbeat: " - "Failed to get cmd for scsi%d.\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_sriov_start_heartbeat: " + "Failed to get cmd for scsi%d\n", instance->host->host_no); return -ENOMEM; } @@ -2194,9 +2299,9 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, sizeof(struct MR_CTRL_HB_HOST_MEM), &instance->hb_host_mem_h); if (!instance->hb_host_mem) { - printk(KERN_DEBUG "megasas: SR-IOV: Couldn't allocate" - " memory for heartbeat host memory for " - "scsi%d.\n", instance->host->host_no); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate" + " memory for heartbeat host memory for scsi%d\n", + instance->host->host_no); retval = -ENOMEM; goto out; } @@ -2204,39 +2309,33 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance, memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); - dcmd->mbox.s[0] = sizeof(struct MR_CTRL_HB_HOST_MEM); + dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM)); dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; - dcmd->flags = MFI_FRAME_DIR_BOTH; + dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); dcmd->timeout = 0; dcmd->pad_0 = 0; - dcmd->data_xfer_len = sizeof(struct MR_CTRL_HB_HOST_MEM); - dcmd->opcode = MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC; - dcmd->sgl.sge32[0].phys_addr = instance->hb_host_mem_h; - dcmd->sgl.sge32[0].length = sizeof(struct MR_CTRL_HB_HOST_MEM); + dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM)); + dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC); + dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(instance->hb_host_mem_h); + dcmd->sgl.sge32[0].length = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM)); - printk(KERN_WARNING "megasas: SR-IOV: Starting heartbeat for scsi%d\n", + dev_warn(&instance->pdev->dev, "SR-IOV: Starting heartbeat for scsi%d\n", instance->host->host_no); - if (!megasas_issue_polled(instance, cmd)) { - retval = 0; - } else { - printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST" - "_MEM_ALLOC DCMD timed out for scsi%d\n", - instance->host->host_no); - retval = 1; - goto out; - } - + if (instance->ctrl_context && !instance->mask_interrupts) + retval = megasas_issue_blocked_cmd(instance, cmd, + MEGASAS_ROUTINE_WAIT_TIME_VF); + else + retval = megasas_issue_polled(instance, cmd); - if (dcmd->cmd_status) { - printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST" - "_MEM_ALLOC DCMD failed with status 0x%x for scsi%d\n", - dcmd->cmd_status, - instance->host->host_no); + if (retval) { + dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST" + "_MEM_ALLOC DCMD %s for scsi%d\n", + (dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ? + "timed out" : "failed", instance->host->host_no); retval = 1; - goto out; } out: @@ -2258,7 +2357,7 @@ void megasas_sriov_heartbeat_handler(unsigned long instance_addr) mod_timer(&instance->sriov_heartbeat_timer, jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF); } else { - printk(KERN_WARNING "megasas: SR-IOV: Heartbeat never " + dev_warn(&instance->pdev->dev, "SR-IOV: Heartbeat never " "completed for scsi%d\n", instance->host->host_no); schedule_work(&instance->work_init); } @@ -2296,7 +2395,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) &clist_local); spin_unlock_irqrestore(&instance->hba_lock, flags); - printk(KERN_NOTICE "megasas: HBA reset wait ...\n"); + dev_notice(&instance->pdev->dev, "HBA reset wait ...\n"); for (i = 0; i < wait_time; i++) { msleep(1000); spin_lock_irqsave(&instance->hba_lock, flags); @@ -2307,37 +2406,37 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) } if (adprecovery != MEGASAS_HBA_OPERATIONAL) { - printk(KERN_NOTICE "megasas: reset: Stopping HBA.\n"); + dev_notice(&instance->pdev->dev, "reset: Stopping HBA.\n"); spin_lock_irqsave(&instance->hba_lock, flags); - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; spin_unlock_irqrestore(&instance->hba_lock, flags); return FAILED; } - reset_index = 0; + reset_index = 0; while (!list_empty(&clist_local)) { - reset_cmd = list_entry((&clist_local)->next, + reset_cmd = list_entry((&clist_local)->next, struct megasas_cmd, list); list_del_init(&reset_cmd->list); if (reset_cmd->scmd) { reset_cmd->scmd->result = DID_RESET << 16; - printk(KERN_NOTICE "%d:%p reset [%02x]\n", + dev_notice(&instance->pdev->dev, "%d:%p reset [%02x]\n", reset_index, reset_cmd, reset_cmd->scmd->cmnd[0]); reset_cmd->scmd->scsi_done(reset_cmd->scmd); megasas_return_cmd(instance, reset_cmd); } else if (reset_cmd->sync_cmd) { - printk(KERN_NOTICE "megasas:%p synch cmds" + dev_notice(&instance->pdev->dev, "%p synch cmds" "reset queue\n", reset_cmd); - reset_cmd->cmd_status = ENODATA; + reset_cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; instance->instancet->fire_cmd(instance, reset_cmd->frame_phys_addr, 0, instance->reg_set); } else { - printk(KERN_NOTICE "megasas: %p unexpected" + dev_notice(&instance->pdev->dev, "%p unexpected" "cmds lst\n", reset_cmd); } @@ -2348,14 +2447,13 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) } for (i = 0; i < resetwaittime; i++) { - int outstanding = atomic_read(&instance->fw_outstanding); if (!outstanding) break; if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: [%2d]waiting for %d " + dev_notice(&instance->pdev->dev, "[%2d]waiting for %d " "commands to complete\n",i,outstanding); /* * Call cmd completion routine. Cmd to be @@ -2387,10 +2485,8 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) i++; } while (i <= 3); - if (atomic_read(&instance->fw_outstanding) && - !kill_adapter_flag) { + if (atomic_read(&instance->fw_outstanding) && !kill_adapter_flag) { if (instance->disableOnlineCtrlReset == 0) { - megasas_do_ocr(instance); /* wait for 5 secs to let FW finish the pending cmds */ @@ -2406,11 +2502,11 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) if (atomic_read(&instance->fw_outstanding) || (kill_adapter_flag == 2)) { - printk(KERN_NOTICE "megaraid_sas: pending cmds after reset\n"); + dev_notice(&instance->pdev->dev, "pending cmds after reset\n"); /* - * Send signal to FW to stop processing any pending cmds. - * The controller will be taken offline by the OS now. - */ + * Send signal to FW to stop processing any pending cmds. + * The controller will be taken offline by the OS now. + */ if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == @@ -2423,12 +2519,12 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) } megasas_dump_pending_frames(instance); spin_lock_irqsave(&instance->hba_lock, flags); - instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; spin_unlock_irqrestore(&instance->hba_lock, flags); return FAILED; } - printk(KERN_NOTICE "megaraid_sas: no pending cmds after reset\n"); + dev_notice(&instance->pdev->dev, "no pending cmds after reset\n"); return SUCCESS; } @@ -2452,16 +2548,15 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd) scmd->cmnd[0], scmd->retries); if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { - printk(KERN_ERR "megasas: cannot recover from previous reset " - "failures\n"); + dev_err(&instance->pdev->dev, "cannot recover from previous reset failures\n"); return FAILED; } ret_val = megasas_wait_for_outstanding(instance); if (ret_val == SUCCESS) - printk(KERN_NOTICE "megasas: reset successful \n"); + dev_notice(&instance->pdev->dev, "reset successful\n"); else - printk(KERN_ERR "megasas: failed to do reset\n"); + dev_err(&instance->pdev->dev, "failed to do reset\n"); return ret_val; } @@ -2503,14 +2598,10 @@ blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) */ static int megasas_reset_device(struct scsi_cmnd *scmd) { - int ret; - /* * First wait for all commands to complete */ - ret = megasas_generic_reset(scmd); - - return ret; + return megasas_generic_reset(scmd); } /** @@ -2520,15 +2611,13 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) { int ret; struct megasas_instance *instance; + instance = (struct megasas_instance *)scmd->device->host->hostdata; /* * First wait for all commands to complete */ - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) + if (instance->ctrl_context) ret = megasas_reset_fusion(scmd->device->host, 1); else ret = megasas_generic_reset(scmd); @@ -2538,7 +2627,7 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) /** * megasas_bios_param - Returns disk geometry for a disk - * @sdev: device handle + * @sdev: device handle * @bdev: block device * @capacity: drive capacity * @geom: geometry parameters @@ -2551,6 +2640,7 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, int sectors; sector_t cylinders; unsigned long tmp; + /* Default heads (64) & sectors (32) */ heads = 64; sectors = 32; @@ -2597,6 +2687,7 @@ static void megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) { unsigned long flags; + /* * Don't signal app if it is just an aborted previously registered aen */ @@ -2612,18 +2703,15 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) instance->aen_cmd = NULL; - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); if ((instance->unload == 0) && ((instance->issuepend_done == 1))) { struct megasas_aen_event *ev; + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (!ev) { - printk(KERN_ERR "megasas_service_aen: out of memory\n"); + dev_err(&instance->pdev->dev, "megasas_service_aen: out of memory\n"); } else { ev->instance = instance; instance->ev = ev; @@ -2680,8 +2768,7 @@ megasas_fw_crash_buffer_show(struct device *cdev, buff_addr = (unsigned long) buf; - if (buff_offset > - (instance->fw_crash_buffer_size * dmachunk)) { + if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { dev_err(&instance->pdev->dev, "Firmware crash dump offset is out of range\n"); spin_unlock_irqrestore(&instance->crashdump_lock, flags); @@ -2693,7 +2780,7 @@ megasas_fw_crash_buffer_show(struct device *cdev, src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] + (buff_offset % dmachunk); - memcpy(buf, (void *)src_addr, size); + memcpy(buf, (void *)src_addr, size); spin_unlock_irqrestore(&instance->crashdump_lock, flags); return size; @@ -2753,6 +2840,7 @@ megasas_fw_crash_state_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; + return snprintf(buf, PAGE_SIZE, "%d\n", instance->fw_crash_state); } @@ -2786,7 +2874,7 @@ struct device_attribute *megaraid_host_attrs[] = { static struct scsi_host_template megasas_template = { .module = THIS_MODULE, - .name = "LSI SAS based MegaRAID driver", + .name = "Avago SAS based MegaRAID driver", .proc_name = "megaraid_sas", .slave_configure = megasas_slave_configure, .slave_alloc = megasas_slave_alloc, @@ -2815,11 +2903,7 @@ static void megasas_complete_int_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { - cmd->cmd_status = cmd->frame->io.cmd_status; - - if (cmd->cmd_status == ENODATA) { - cmd->cmd_status = 0; - } + cmd->cmd_status_drv = cmd->frame->io.cmd_status; wake_up(&instance->int_cmd_wait_q); } @@ -2838,11 +2922,9 @@ megasas_complete_abort(struct megasas_instance *instance, { if (cmd->sync_cmd) { cmd->sync_cmd = 0; - cmd->cmd_status = 0; + cmd->cmd_status_drv = 0; wake_up(&instance->abort_cmd_wait_q); } - - return; } /** @@ -2850,10 +2932,10 @@ megasas_complete_abort(struct megasas_instance *instance, * @instance: Adapter soft state * @cmd: Command to be completed * @alt_status: If non-zero, use this value as status to - * SCSI mid-layer instead of the value returned - * by the FW. This should be used if caller wants - * an alternate status (as in the case of aborted - * commands) + * SCSI mid-layer instead of the value returned + * by the FW. This should be used if caller wants + * an alternate status (as in the case of aborted + * commands) */ void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, @@ -2863,7 +2945,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, struct megasas_header *hdr = &cmd->frame->hdr; unsigned long flags; struct fusion_context *fusion = instance->ctrl_context; - u32 opcode; + u32 opcode, status; /* flag for the retry reset */ cmd->retry_for_fw_reset = 0; @@ -2877,10 +2959,10 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, MR_DCMD_CTRL_EVENT_GET_INFO left over from the main kernel when booting the kdump kernel. Ignore this command to prevent a kernel panic on shutdown of the kdump kernel. */ - printk(KERN_WARNING "megaraid_sas: MFI_CMD_INVALID command " - "completed.\n"); - printk(KERN_WARNING "megaraid_sas: If you have a controller " - "other than PERC5, please upgrade your firmware.\n"); + dev_warn(&instance->pdev->dev, "MFI_CMD_INVALID command " + "completed\n"); + dev_warn(&instance->pdev->dev, "If you have a controller " + "other than PERC5, please upgrade your firmware\n"); break; case MFI_CMD_PD_SCSI_IO: case MFI_CMD_LD_SCSI_IO: @@ -2948,7 +3030,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, break; default: - printk(KERN_DEBUG "megasas: MFI FW status %#x\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "MFI FW status %#x\n", hdr->cmd_status); cmd->scmd->result = DID_ERROR << 16; break; @@ -2971,15 +3053,14 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, && (cmd->frame->dcmd.mbox.b[1] == 1)) { fusion->fast_path_io = 0; spin_lock_irqsave(instance->host->host_lock, flags); + instance->map_update_cmd = NULL; if (cmd->frame->hdr.cmd_status != 0) { if (cmd->frame->hdr.cmd_status != MFI_STAT_NOT_FOUND) - printk(KERN_WARNING "megasas: map sync" - "failed, status = 0x%x.\n", + dev_warn(&instance->pdev->dev, "map syncfailed, status = 0x%x\n", cmd->frame->hdr.cmd_status); else { - megasas_return_mfi_mpt_pthr(instance, - cmd, cmd->mpt_pthr_cmd_blocked); + megasas_return_cmd(instance, cmd); spin_unlock_irqrestore( instance->host->host_lock, flags); @@ -2987,8 +3068,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, } } else instance->map_id++; - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); + megasas_return_cmd(instance, cmd); /* * Set fast path IO to ZERO. @@ -3011,6 +3091,27 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, spin_unlock_irqrestore(&poll_aen_lock, flags); } + /* FW has an updated PD sequence */ + if ((opcode == MR_DCMD_SYSTEM_PD_MAP_GET_INFO) && + (cmd->frame->dcmd.mbox.b[0] == 1)) { + + spin_lock_irqsave(instance->host->host_lock, flags); + status = cmd->frame->hdr.cmd_status; + instance->jbod_seq_cmd = NULL; + megasas_return_cmd(instance, cmd); + + if (status == MFI_STAT_OK) { + instance->pd_seq_map_id++; + /* Re-register a pd sync seq num cmd */ + if (megasas_sync_pd_seq_num(instance, true)) + instance->use_seqnum_jbod_fp = false; + } else + instance->use_seqnum_jbod_fp = false; + + spin_unlock_irqrestore(instance->host->host_lock, flags); + break; + } + /* * See if got an event notification */ @@ -3029,7 +3130,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, break; default: - printk("megasas: Unknown command completed! [0x%X]\n", + dev_info(&instance->pdev->dev, "Unknown command completed! [0x%X]\n", hdr->cmd); break; } @@ -3037,7 +3138,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, /** * megasas_issue_pending_cmds_again - issue all pending cmds - * in FW again because of the fw reset + * in FW again because of the fw reset * @instance: Adapter soft state */ static inline void @@ -3055,19 +3156,19 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) spin_unlock_irqrestore(&instance->hba_lock, flags); while (!list_empty(&clist_local)) { - cmd = list_entry((&clist_local)->next, + cmd = list_entry((&clist_local)->next, struct megasas_cmd, list); list_del_init(&cmd->list); if (cmd->sync_cmd || cmd->scmd) { - printk(KERN_NOTICE "megaraid_sas: command %p, %p:%d" - "detected to be pending while HBA reset.\n", + dev_notice(&instance->pdev->dev, "command %p, %p:%d" + "detected to be pending while HBA reset\n", cmd, cmd->scmd, cmd->sync_cmd); cmd->retry_for_fw_reset++; if (cmd->retry_for_fw_reset == 3) { - printk(KERN_NOTICE "megaraid_sas: cmd %p, %p:%d" + dev_notice(&instance->pdev->dev, "cmd %p, %p:%d" "was tried multiple times during reset." "Shutting down the HBA\n", cmd, cmd->scmd, cmd->sync_cmd); @@ -3080,18 +3181,18 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) if (cmd->sync_cmd == 1) { if (cmd->scmd) { - printk(KERN_NOTICE "megaraid_sas: unexpected" + dev_notice(&instance->pdev->dev, "unexpected" "cmd attached to internal command!\n"); } - printk(KERN_NOTICE "megasas: %p synchronous cmd" + dev_notice(&instance->pdev->dev, "%p synchronous cmd" "on the internal reset queue," "issue it again.\n", cmd); - cmd->cmd_status = ENODATA; + cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS; instance->instancet->fire_cmd(instance, - cmd->frame_phys_addr , + cmd->frame_phys_addr, 0, instance->reg_set); } else if (cmd->scmd) { - printk(KERN_NOTICE "megasas: %p scsi cmd [%02x]" + dev_notice(&instance->pdev->dev, "%p scsi cmd [%02x]" "detected on the internal queue, issue again.\n", cmd, cmd->scmd->cmnd[0]); @@ -3100,22 +3201,22 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance) cmd->frame_phys_addr, cmd->frame_count-1, instance->reg_set); } else { - printk(KERN_NOTICE "megasas: %p unexpected cmd on the" + dev_notice(&instance->pdev->dev, "%p unexpected cmd on the" "internal reset defer list while re-issue!!\n", cmd); } } if (instance->aen_cmd) { - printk(KERN_NOTICE "megaraid_sas: aen_cmd in def process\n"); + dev_notice(&instance->pdev->dev, "aen_cmd in def process\n"); megasas_return_cmd(instance, instance->aen_cmd); - instance->aen_cmd = NULL; + instance->aen_cmd = NULL; } /* - * Initiate AEN (Asynchronous Event Notification) - */ + * Initiate AEN (Asynchronous Event Notification) + */ seq_num = instance->last_seq_num; class_locale.members.reserved = 0; class_locale.members.locale = MR_EVT_LOCALE_ALL; @@ -3142,17 +3243,17 @@ megasas_internal_reset_defer_cmds(struct megasas_instance *instance) u32 defer_index; unsigned long flags; - defer_index = 0; + defer_index = 0; spin_lock_irqsave(&instance->mfi_pool_lock, flags); for (i = 0; i < max_cmd; i++) { cmd = instance->cmd_list[i]; if (cmd->sync_cmd == 1 || cmd->scmd) { - printk(KERN_NOTICE "megasas: moving cmd[%d]:%p:%d:%p" + dev_notice(&instance->pdev->dev, "moving cmd[%d]:%p:%d:%p" "on the defer queue as internal\n", defer_index, cmd, cmd->sync_cmd, cmd->scmd); if (!list_empty(&cmd->list)) { - printk(KERN_NOTICE "megaraid_sas: ERROR while" + dev_notice(&instance->pdev->dev, "ERROR while" " moving this cmd:%p, %d %p, it was" "discovered on some list?\n", cmd, cmd->sync_cmd, cmd->scmd); @@ -3177,13 +3278,13 @@ process_fw_state_change_wq(struct work_struct *work) unsigned long flags; if (instance->adprecovery != MEGASAS_ADPRESET_SM_INFAULT) { - printk(KERN_NOTICE "megaraid_sas: error, recovery st %x \n", + dev_notice(&instance->pdev->dev, "error, recovery st %x\n", instance->adprecovery); return ; } if (instance->adprecovery == MEGASAS_ADPRESET_SM_INFAULT) { - printk(KERN_NOTICE "megaraid_sas: FW detected to be in fault" + dev_notice(&instance->pdev->dev, "FW detected to be in fault" "state, restarting it...\n"); instance->instancet->disable_intr(instance); @@ -3191,21 +3292,21 @@ process_fw_state_change_wq(struct work_struct *work) atomic_set(&instance->fw_reset_no_pci_access, 1); instance->instancet->adp_reset(instance, instance->reg_set); - atomic_set(&instance->fw_reset_no_pci_access, 0 ); + atomic_set(&instance->fw_reset_no_pci_access, 0); - printk(KERN_NOTICE "megaraid_sas: FW restarted successfully," + dev_notice(&instance->pdev->dev, "FW restarted successfully," "initiating next stage...\n"); - printk(KERN_NOTICE "megaraid_sas: HBA recovery state machine," + dev_notice(&instance->pdev->dev, "HBA recovery state machine," "state 2 starting...\n"); - /*waitting for about 20 second before start the second init*/ + /* waiting for about 20 second before start the second init */ for (wait = 0; wait < 30; wait++) { msleep(1000); } if (megasas_transition_to_ready(instance, 1)) { - printk(KERN_NOTICE "megaraid_sas:adapter not ready\n"); + dev_notice(&instance->pdev->dev, "adapter not ready\n"); atomic_set(&instance->fw_reset_no_pci_access, 1); megaraid_sas_kill_hba(instance); @@ -3232,15 +3333,14 @@ process_fw_state_change_wq(struct work_struct *work) megasas_issue_pending_cmds_again(instance); instance->issuepend_done = 1; } - return ; } /** * megasas_deplete_reply_queue - Processes all completed commands * @instance: Adapter soft state * @alt_status: Alternate status to be returned to - * SCSI mid-layer instead of the status - * returned by the FW + * SCSI mid-layer instead of the status + * returned by the FW * Note: this must be called with hba lock held */ static int @@ -3270,13 +3370,13 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, instance->reg_set) & MFI_STATE_MASK; if (fw_state != MFI_STATE_FAULT) { - printk(KERN_NOTICE "megaraid_sas: fw state:%x\n", + dev_notice(&instance->pdev->dev, "fw state:%x\n", fw_state); } if ((fw_state == MFI_STATE_FAULT) && (instance->disableOnlineCtrlReset == 0)) { - printk(KERN_NOTICE "megaraid_sas: wait adp restart\n"); + dev_notice(&instance->pdev->dev, "wait adp restart\n"); if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || @@ -3297,14 +3397,14 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, atomic_set(&instance->fw_outstanding, 0); megasas_internal_reset_defer_cmds(instance); - printk(KERN_NOTICE "megasas: fwState=%x, stage:%d\n", + dev_notice(&instance->pdev->dev, "fwState=%x, stage:%d\n", fw_state, instance->adprecovery); schedule_work(&instance->work_init); return IRQ_HANDLED; } else { - printk(KERN_NOTICE "megasas: fwstate:%x, dis_OCR=%x\n", + dev_notice(&instance->pdev->dev, "fwstate:%x, dis_OCR=%x\n", fw_state, instance->disableOnlineCtrlReset); } } @@ -3320,13 +3420,13 @@ static irqreturn_t megasas_isr(int irq, void *devp) struct megasas_irq_context *irq_context = devp; struct megasas_instance *instance = irq_context->instance; unsigned long flags; - irqreturn_t rc; + irqreturn_t rc; if (atomic_read(&instance->fw_reset_no_pci_access)) return IRQ_HANDLED; spin_lock_irqsave(&instance->hba_lock, flags); - rc = megasas_deplete_reply_queue(instance, DID_OK); + rc = megasas_deplete_reply_queue(instance, DID_OK); spin_unlock_irqrestore(&instance->hba_lock, flags); return rc; @@ -3354,7 +3454,7 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) fw_state = abs_state & MFI_STATE_MASK; if (fw_state != MFI_STATE_READY) - printk(KERN_INFO "megasas: Waiting for FW to come to ready" + dev_info(&instance->pdev->dev, "Waiting for FW to come to ready" " state\n"); while (fw_state != MFI_STATE_READY) { @@ -3362,7 +3462,7 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) switch (fw_state) { case MFI_STATE_FAULT: - printk(KERN_DEBUG "megasas: FW in FAULT state!!\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW in FAULT state!!\n"); if (ocr) { max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FAULT; @@ -3378,22 +3478,14 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) { + (instance->ctrl_context)) writel( MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, &instance->reg_set->doorbell); - } else { + else writel( MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, &instance->reg_set->inbound_doorbell); - } max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_WAIT_HANDSHAKE; @@ -3404,17 +3496,10 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) { + (instance->ctrl_context)) writel(MFI_INIT_HOTPLUG, &instance->reg_set->doorbell); - } else + else writel(MFI_INIT_HOTPLUG, &instance->reg_set->inbound_doorbell); @@ -3431,24 +3516,11 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || - (instance->pdev->device - == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device - == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device - == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device - == PCI_DEVICE_ID_LSI_FURY)) { + (instance->ctrl_context)) { writel(MFI_RESET_FLAGS, &instance->reg_set->doorbell); - if ((instance->pdev->device == - PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) { + + if (instance->ctrl_context) { for (i = 0; i < (10 * 1000); i += 20) { if (readl( &instance-> @@ -3501,7 +3573,7 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) break; default: - printk(KERN_DEBUG "megasas: Unknown state 0x%x\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Unknown state 0x%x\n", fw_state); return -ENODEV; } @@ -3523,7 +3595,7 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) * Return error if fw_state hasn't changed after max_wait */ if (curr_abs_state == abs_state) { - printk(KERN_DEBUG "FW state [%d] hasn't changed " + dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW state [%d] hasn't changed " "in %d secs\n", fw_state, max_wait); return -ENODEV; } @@ -3531,7 +3603,7 @@ megasas_transition_to_ready(struct megasas_instance *instance, int ocr) abs_state = curr_abs_state; fw_state = curr_abs_state & MFI_STATE_MASK; } - printk(KERN_INFO "megasas: FW now in Ready state\n"); + dev_info(&instance->pdev->dev, "FW now in Ready state\n"); return 0; } @@ -3602,9 +3674,8 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); - if (instance->flag_ieee) { + if (instance->flag_ieee) sge_sz = sizeof(struct megasas_sge_skinny); - } /* * For MFI controllers. @@ -3626,7 +3697,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) instance->pdev, total_sz, 256, 0); if (!instance->frame_dma_pool) { - printk(KERN_DEBUG "megasas: failed to setup frame pool\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup frame pool\n"); return -ENOMEM; } @@ -3634,7 +3705,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) instance->pdev, 128, 4, 0); if (!instance->sense_dma_pool) { - printk(KERN_DEBUG "megasas: failed to setup sense pool\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool\n"); pci_pool_destroy(instance->frame_dma_pool); instance->frame_dma_pool = NULL; @@ -3662,7 +3733,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) * whatever has been allocated */ if (!cmd->frame || !cmd->sense) { - printk(KERN_DEBUG "megasas: pci_pool_alloc failed \n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "pci_pool_alloc failed\n"); megasas_teardown_frame_pool(instance); return -ENOMEM; } @@ -3670,11 +3741,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) memset(cmd->frame, 0, total_sz); cmd->frame->io.context = cpu_to_le32(cmd->index); cmd->frame->io.pad_0 = 0; - if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_PLASMA) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) && - (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) && - (reset_devices)) + if (!instance->ctrl_context && reset_devices) cmd->frame->hdr.cmd = MFI_CMD_INVALID; } @@ -3688,6 +3755,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) void megasas_free_cmds(struct megasas_instance *instance) { int i; + /* First free the MFI frame pool */ megasas_teardown_frame_pool(instance); @@ -3740,7 +3808,7 @@ int megasas_alloc_cmds(struct megasas_instance *instance) instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL); if (!instance->cmd_list) { - printk(KERN_DEBUG "megasas: out of memory\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "out of memory\n"); return -ENOMEM; } @@ -3766,7 +3834,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance) cmd = instance->cmd_list[i]; memset(cmd, 0, sizeof(struct megasas_cmd)); cmd->index = i; - atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED); cmd->scmd = NULL; cmd->instance = instance; @@ -3777,7 +3844,7 @@ int megasas_alloc_cmds(struct megasas_instance *instance) * Create a frame pool and assign one frame to each cmd */ if (megasas_create_frame_pool(instance)) { - printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n"); megasas_free_cmds(instance); } @@ -3806,7 +3873,7 @@ megasas_get_pd_list(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "(get_pd_list): Failed to get cmd\n"); return -ENOMEM; } @@ -3816,7 +3883,7 @@ megasas_get_pd_list(struct megasas_instance *instance) MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h); if (!ci) { - printk(KERN_DEBUG "Failed to alloc mem for pd_list\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem for pd_list\n"); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -3827,7 +3894,7 @@ megasas_get_pd_list(struct megasas_instance *instance) dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST; dcmd->mbox.b[1] = 0; dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ); dcmd->timeout = 0; @@ -3844,12 +3911,12 @@ megasas_get_pd_list(struct megasas_instance *instance) ret = megasas_issue_polled(instance, cmd); /* - * the following function will get the instance PD LIST. - */ + * the following function will get the instance PD LIST. + */ pd_addr = ci->addr; - if ( ret == 0 && + if (ret == 0 && (le32_to_cpu(ci->count) < (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) { @@ -3874,11 +3941,7 @@ megasas_get_pd_list(struct megasas_instance *instance) MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), ci, ci_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -3905,7 +3968,7 @@ megasas_get_ld_list(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas_get_ld_list: Failed to get cmd\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_list: Failed to get cmd\n"); return -ENOMEM; } @@ -3916,7 +3979,7 @@ megasas_get_ld_list(struct megasas_instance *instance) &ci_h); if (!ci) { - printk(KERN_DEBUG "Failed to alloc mem in get_ld_list\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem in get_ld_list\n"); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -3927,7 +3990,7 @@ megasas_get_ld_list(struct megasas_instance *instance) if (instance->supportmax256vd) dcmd->mbox.b[0] = 1; dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ); dcmd->timeout = 0; @@ -3965,11 +4028,7 @@ megasas_get_ld_list(struct megasas_instance *instance) ci, ci_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -3995,8 +4054,8 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_WARNING - "megasas:(megasas_ld_list_query): Failed to get cmd\n"); + dev_warn(&instance->pdev->dev, + "megasas_ld_list_query: Failed to get cmd\n"); return -ENOMEM; } @@ -4006,8 +4065,8 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) sizeof(struct MR_LD_TARGETID_LIST), &ci_h); if (!ci) { - printk(KERN_WARNING - "megasas: Failed to alloc mem for ld_list_query\n"); + dev_warn(&instance->pdev->dev, + "Failed to alloc mem for ld_list_query\n"); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -4020,7 +4079,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) dcmd->mbox.b[2] = 1; dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ); dcmd->timeout = 0; @@ -4050,11 +4109,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type) pci_free_consistent(instance->pdev, sizeof(struct MR_LD_TARGETID_LIST), ci, ci_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -4091,18 +4146,17 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance) instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES; instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; } - dev_info(&instance->pdev->dev, "Firmware supports %d VD %d PD\n", - instance->fw_supported_vd_count, - instance->fw_supported_pd_count); - dev_info(&instance->pdev->dev, "Driver supports %d VD %d PD\n", - instance->drv_supported_vd_count, - instance->drv_supported_pd_count); - old_map_sz = sizeof(struct MR_FW_RAID_MAP) + + dev_info(&instance->pdev->dev, + "firmware type\t: %s\n", + instance->supportmax256vd ? "Extended VD(240 VD)firmware" : + "Legacy(64 VD) firmware"); + + old_map_sz = sizeof(struct MR_FW_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) * (instance->fw_supported_vd_count - 1)); - new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT); - fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP) + + new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT); + fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) * (instance->drv_supported_vd_count - 1)); @@ -4113,7 +4167,6 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance) fusion->current_map_sz = new_map_sz; else fusion->current_map_sz = old_map_sz; - } /** @@ -4139,7 +4192,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: Failed to get a free cmd\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a free cmd\n"); return -ENOMEM; } @@ -4149,7 +4202,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance) sizeof(struct megasas_ctrl_info), &ci_h); if (!ci) { - printk(KERN_DEBUG "Failed to alloc mem for ctrl info\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem for ctrl info\n"); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -4158,7 +4211,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance) memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ); dcmd->timeout = 0; @@ -4181,16 +4234,27 @@ megasas_get_ctrl_info(struct megasas_instance *instance) le32_to_cpus((u32 *)&ctrl_info->adapterOperations2); le32_to_cpus((u32 *)&ctrl_info->adapterOperations3); megasas_update_ext_vd_details(instance); + instance->use_seqnum_jbod_fp = + ctrl_info->adapterOperations3.useSeqNumJbodFP; + instance->is_imr = (ctrl_info->memory_size ? 0 : 1); + dev_info(&instance->pdev->dev, + "controller type\t: %s(%dMB)\n", + instance->is_imr ? "iMR" : "MR", + le16_to_cpu(ctrl_info->memory_size)); + instance->disableOnlineCtrlReset = + ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset; + dev_info(&instance->pdev->dev, "Online Controller Reset(OCR)\t: %s\n", + instance->disableOnlineCtrlReset ? "Disabled" : "Enabled"); + instance->secure_jbod_support = + ctrl_info->adapterOperations3.supportSecurityonJBOD; + dev_info(&instance->pdev->dev, "Secure JBOD support\t: %s\n", + instance->secure_jbod_support ? "Yes" : "No"); } pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info), ci, ci_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -4229,7 +4293,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance, memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->mbox.b[0] = crash_buf_state; dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0xFF; + dcmd->cmd_status = MFI_STAT_INVALID_STATUS; dcmd->sge_count = 1; dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE); dcmd->timeout = 0; @@ -4245,11 +4309,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance, else ret = megasas_issue_polled(instance, cmd); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -4262,10 +4322,8 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance, static int megasas_issue_init_mfi(struct megasas_instance *instance) { - u32 context; - + __le32 context; struct megasas_cmd *cmd; - struct megasas_init_frame *init_frame; struct megasas_init_queue_info *initq_info; dma_addr_t init_frame_h; @@ -4300,7 +4358,7 @@ megasas_issue_init_mfi(struct megasas_instance *instance) initq_info->consumer_index_phys_addr_lo = cpu_to_le32(instance->consumer_h); init_frame->cmd = MFI_CMD_INIT; - init_frame->cmd_status = 0xFF; + init_frame->cmd_status = MFI_STAT_INVALID_STATUS; init_frame->queue_info_new_phys_addr_lo = cpu_to_le32(lower_32_bits(initq_info_h)); init_frame->queue_info_new_phys_addr_hi = @@ -4318,7 +4376,7 @@ megasas_issue_init_mfi(struct megasas_instance *instance) */ if (megasas_issue_polled(instance, cmd)) { - printk(KERN_ERR "megasas: Failed to init firmware\n"); + dev_err(&instance->pdev->dev, "Failed to init firmware\n"); megasas_return_cmd(instance, cmd); goto fail_fw_init; } @@ -4353,6 +4411,21 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) instance->max_mfi_cmds = instance->max_fw_cmds; instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> 0x10; + /* + * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands + * are reserved for IOCTL + driver's internal DCMDs. + */ + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->max_scsi_cmds = (instance->max_fw_cmds - + MEGASAS_SKINNY_INT_CMDS); + sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); + } else { + instance->max_scsi_cmds = (instance->max_fw_cmds - + MEGASAS_INT_CMDS); + sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS)); + } + /* * Create a pool of commands */ @@ -4376,7 +4449,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) &instance->reply_queue_h); if (!instance->reply_queue) { - printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Out of DMA mem for reply queue\n"); goto fail_reply_queue; } @@ -4395,7 +4468,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) (instance->instancet->read_fw_status_reg(reg_set) & 0x04000000); - printk(KERN_NOTICE "megasas_init_mfi: fw_support_ieee=%d", + dev_notice(&instance->pdev->dev, "megasas_init_mfi: fw_support_ieee=%d", instance->fw_support_ieee); if (instance->fw_support_ieee) @@ -4414,6 +4487,163 @@ fail_alloc_cmds: return 1; } +/* + * megasas_setup_irqs_msix - register legacy interrupts. + * @instance: Adapter soft state + * + * Do not enable interrupt, only setup ISRs. + * + * Return 0 on success. + */ +static int +megasas_setup_irqs_ioapic(struct megasas_instance *instance) +{ + struct pci_dev *pdev; + + pdev = instance->pdev; + instance->irq_context[0].instance = instance; + instance->irq_context[0].MSIxIndex = 0; + if (request_irq(pdev->irq, instance->instancet->service_isr, + IRQF_SHARED, "megasas", &instance->irq_context[0])) { + dev_err(&instance->pdev->dev, + "Failed to register IRQ from %s %d\n", + __func__, __LINE__); + return -1; + } + return 0; +} + +/** + * megasas_setup_irqs_msix - register MSI-x interrupts. + * @instance: Adapter soft state + * @is_probe: Driver probe check + * + * Do not enable interrupt, only setup ISRs. + * + * Return 0 on success. + */ +static int +megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) +{ + int i, j, cpu; + struct pci_dev *pdev; + + pdev = instance->pdev; + + /* Try MSI-x */ + cpu = cpumask_first(cpu_online_mask); + for (i = 0; i < instance->msix_vectors; i++) { + instance->irq_context[i].instance = instance; + instance->irq_context[i].MSIxIndex = i; + if (request_irq(instance->msixentry[i].vector, + instance->instancet->service_isr, 0, "megasas", + &instance->irq_context[i])) { + dev_err(&instance->pdev->dev, + "Failed to register IRQ for vector %d.\n", i); + for (j = 0; j < i; j++) { + if (smp_affinity_enable) + irq_set_affinity_hint( + instance->msixentry[j].vector, NULL); + free_irq(instance->msixentry[j].vector, + &instance->irq_context[j]); + } + /* Retry irq register for IO_APIC*/ + instance->msix_vectors = 0; + if (is_probe) + return megasas_setup_irqs_ioapic(instance); + else + return -1; + } + if (smp_affinity_enable) { + if (irq_set_affinity_hint(instance->msixentry[i].vector, + get_cpu_mask(cpu))) + dev_err(&instance->pdev->dev, + "Failed to set affinity hint" + " for cpu %d\n", cpu); + cpu = cpumask_next(cpu, cpu_online_mask); + } + } + return 0; +} + +/* + * megasas_destroy_irqs- unregister interrupts. + * @instance: Adapter soft state + * return: void + */ +static void +megasas_destroy_irqs(struct megasas_instance *instance) { + + int i; + + if (instance->msix_vectors) + for (i = 0; i < instance->msix_vectors; i++) { + if (smp_affinity_enable) + irq_set_affinity_hint( + instance->msixentry[i].vector, NULL); + free_irq(instance->msixentry[i].vector, + &instance->irq_context[i]); + } + else + free_irq(instance->pdev->irq, &instance->irq_context[0]); +} + +/** + * megasas_setup_jbod_map - setup jbod map for FP seq_number. + * @instance: Adapter soft state + * @is_probe: Driver probe check + * + * Return 0 on success. + */ +void +megasas_setup_jbod_map(struct megasas_instance *instance) +{ + int i; + struct fusion_context *fusion = instance->ctrl_context; + u32 pd_seq_map_sz; + + pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + + (sizeof(struct MR_PD_CFG_SEQ) * (MAX_PHYSICAL_DEVICES - 1)); + + if (reset_devices || !fusion || + !instance->ctrl_info->adapterOperations3.useSeqNumJbodFP) { + dev_info(&instance->pdev->dev, + "Jbod map is not supported %s %d\n", + __func__, __LINE__); + instance->use_seqnum_jbod_fp = false; + return; + } + + if (fusion->pd_seq_sync[0]) + goto skip_alloc; + + for (i = 0; i < JBOD_MAPS_COUNT; i++) { + fusion->pd_seq_sync[i] = dma_alloc_coherent + (&instance->pdev->dev, pd_seq_map_sz, + &fusion->pd_seq_phys[i], GFP_KERNEL); + if (!fusion->pd_seq_sync[i]) { + dev_err(&instance->pdev->dev, + "Failed to allocate memory from %s %d\n", + __func__, __LINE__); + if (i == 1) { + dma_free_coherent(&instance->pdev->dev, + pd_seq_map_sz, fusion->pd_seq_sync[0], + fusion->pd_seq_phys[0]); + fusion->pd_seq_sync[0] = NULL; + } + instance->use_seqnum_jbod_fp = false; + return; + } + } + +skip_alloc: + if (!megasas_sync_pd_seq_num(instance, false) && + !megasas_sync_pd_seq_num(instance, true)) + instance->use_seqnum_jbod_fp = true; + else + instance->use_seqnum_jbod_fp = false; +} + /** * megasas_init_fw - Initializes the FW * @instance: Adapter soft state @@ -4432,13 +4662,16 @@ static int megasas_init_fw(struct megasas_instance *instance) unsigned long bar_list; int i, loop, fw_msix_count = 0; struct IOV_111 *iovPtr; + struct fusion_context *fusion; + + fusion = instance->ctrl_context; /* Find first memory bar */ bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM); instance->bar = find_first_bit(&bar_list, sizeof(unsigned long)); if (pci_request_selected_regions(instance->pdev, instance->bar, "megasas: LSI")) { - printk(KERN_DEBUG "megasas: IO memory region busy!\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "IO memory region busy!\n"); return -EBUSY; } @@ -4446,7 +4679,7 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->reg_set = ioremap_nocache(base_addr, 8192); if (!instance->reg_set) { - printk(KERN_DEBUG "megasas: Failed to map IO mem\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to map IO mem\n"); goto fail_ioremap; } @@ -4457,6 +4690,10 @@ static int megasas_init_fw(struct megasas_instance *instance) case PCI_DEVICE_ID_LSI_PLASMA: case PCI_DEVICE_ID_LSI_INVADER: case PCI_DEVICE_ID_LSI_FURY: + case PCI_DEVICE_ID_LSI_INTRUDER: + case PCI_DEVICE_ID_LSI_INTRUDER_24: + case PCI_DEVICE_ID_LSI_CUTLASS_52: + case PCI_DEVICE_ID_LSI_CUTLASS_53: instance->instancet = &megasas_instance_template_fusion; break; case PCI_DEVICE_ID_LSI_SAS1078R: @@ -4475,6 +4712,7 @@ static int megasas_init_fw(struct megasas_instance *instance) case PCI_DEVICE_ID_DELL_PERC5: default: instance->instancet = &megasas_instance_template_xscale; + instance->allow_fw_scan = 1; break; } @@ -4484,7 +4722,7 @@ static int megasas_init_fw(struct megasas_instance *instance) (instance, instance->reg_set); atomic_set(&instance->fw_reset_no_pci_access, 0); dev_info(&instance->pdev->dev, - "megasas: FW restarted successfully from %s!\n", + "FW restarted successfully from %s!\n", __func__); /*waitting for about 30 second before retry*/ @@ -4499,7 +4737,7 @@ static int megasas_init_fw(struct megasas_instance *instance) * It is used for all MPT based Adapters. */ instance->reply_post_host_index_addr[0] = - (u32 *)((u8 *)instance->reg_set + + (u32 __iomem *)((u8 __iomem *)instance->reg_set + MPI2_REPLY_POST_HOST_INDEX_OFFSET); /* Check if MSI-X is supported while in ready state */ @@ -4509,36 +4747,32 @@ static int megasas_init_fw(struct megasas_instance *instance) scratch_pad_2 = readl (&instance->reg_set->outbound_scratch_pad_2); /* Check max MSI-X vectors */ - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA)) { - instance->msix_vectors = (scratch_pad_2 - & MR_MAX_REPLY_QUEUES_OFFSET) + 1; - fw_msix_count = instance->msix_vectors; - if (msix_vectors) - instance->msix_vectors = - min(msix_vectors, - instance->msix_vectors); - } else if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) - || (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { - /* Invader/Fury supports more than 8 MSI-X */ - instance->msix_vectors = ((scratch_pad_2 - & MR_MAX_REPLY_QUEUES_EXT_OFFSET) - >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1; - fw_msix_count = instance->msix_vectors; - /* Save 1-15 reply post index address to local memory - * Index 0 is already saved from reg offset - * MPI2_REPLY_POST_HOST_INDEX_OFFSET - */ - for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) { - instance->reply_post_host_index_addr[loop] = - (u32 *)((u8 *)instance->reg_set + - MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET - + (loop * 0x10)); + if (fusion) { + if (fusion->adapter_type == THUNDERBOLT_SERIES) { /* Thunderbolt Series*/ + instance->msix_vectors = (scratch_pad_2 + & MR_MAX_REPLY_QUEUES_OFFSET) + 1; + fw_msix_count = instance->msix_vectors; + } else { /* Invader series supports more than 8 MSI-x vectors*/ + instance->msix_vectors = ((scratch_pad_2 + & MR_MAX_REPLY_QUEUES_EXT_OFFSET) + >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1; + fw_msix_count = instance->msix_vectors; + /* Save 1-15 reply post index address to local memory + * Index 0 is already saved from reg offset + * MPI2_REPLY_POST_HOST_INDEX_OFFSET + */ + for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) { + instance->reply_post_host_index_addr[loop] = + (u32 __iomem *) + ((u8 __iomem *)instance->reg_set + + MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET + + (loop * 0x10)); + } } if (msix_vectors) instance->msix_vectors = min(msix_vectors, instance->msix_vectors); - } else + } else /* MFI adapters */ instance->msix_vectors = 1; /* Don't bother allocating more MSI-X vectors than cpus */ instance->msix_vectors = min(instance->msix_vectors, @@ -4551,14 +4785,22 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->msix_vectors = i; else instance->msix_vectors = 0; - - dev_info(&instance->pdev->dev, "[scsi%d]: FW supports" - "<%d> MSIX vector,Online CPUs: <%d>," - "Current MSIX <%d>\n", instance->host->host_no, - fw_msix_count, (unsigned int)num_online_cpus(), - instance->msix_vectors); } + dev_info(&instance->pdev->dev, + "firmware supports msix\t: (%d)", fw_msix_count); + dev_info(&instance->pdev->dev, + "current msix/online cpus\t: (%d/%d)\n", + instance->msix_vectors, (unsigned int)num_online_cpus()); + + tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, + (unsigned long)instance); + + if (instance->msix_vectors ? + megasas_setup_irqs_msix(instance, 1) : + megasas_setup_irqs_ioapic(instance)) + goto fail_setup_irqs; + instance->ctrl_info = kzalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); if (instance->ctrl_info == NULL) @@ -4574,17 +4816,21 @@ static int megasas_init_fw(struct megasas_instance *instance) if (instance->instancet->init_adapter(instance)) goto fail_init_adapter; - printk(KERN_ERR "megasas: INIT adapter done\n"); - /** for passthrough - * the following function will get the PD LIST. - */ + instance->instancet->enable_intr(instance); + + dev_err(&instance->pdev->dev, "INIT adapter done\n"); - memset(instance->pd_list, 0 , + megasas_setup_jbod_map(instance); + + /** for passthrough + * the following function will get the PD LIST. + */ + memset(instance->pd_list, 0, (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list))); if (megasas_get_pd_list(instance) < 0) { - printk(KERN_ERR "megasas: failed to get PD list\n"); - goto fail_init_adapter; + dev_err(&instance->pdev->dev, "failed to get PD list\n"); + goto fail_get_pd_list; } memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); @@ -4608,29 +4854,13 @@ static int megasas_init_fw(struct megasas_instance *instance) le16_to_cpu(ctrl_info->max_strips_per_io); max_sectors_2 = le32_to_cpu(ctrl_info->max_request_size); - tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2); + tmp_sectors = min_t(u32, max_sectors_1, max_sectors_2); - /*Check whether controller is iMR or MR */ - if (ctrl_info->memory_size) { - instance->is_imr = 0; - dev_info(&instance->pdev->dev, "Controller type: MR," - "Memory size is: %dMB\n", - le16_to_cpu(ctrl_info->memory_size)); - } else { - instance->is_imr = 1; - dev_info(&instance->pdev->dev, - "Controller type: iMR\n"); - } - instance->disableOnlineCtrlReset = - ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset; instance->mpio = ctrl_info->adapterOperations2.mpio; instance->UnevenSpanSupport = ctrl_info->adapterOperations2.supportUnevenSpans; if (instance->UnevenSpanSupport) { struct fusion_context *fusion = instance->ctrl_context; - - dev_info(&instance->pdev->dev, "FW supports: " - "UnevenSpanSupport=%x\n", instance->UnevenSpanSupport); if (MR_ValidateMapInfo(instance)) fusion->fast_path_io = 1; else @@ -4638,18 +4868,22 @@ static int megasas_init_fw(struct megasas_instance *instance) } if (ctrl_info->host_interface.SRIOV) { - if (!ctrl_info->adapterOperations2.activePassive) - instance->PlasmaFW111 = 1; - - if (!instance->PlasmaFW111) - instance->requestorId = - ctrl_info->iov.requestorId; - else { - iovPtr = (struct IOV_111 *)((unsigned char *)ctrl_info + IOV_111_OFFSET); - instance->requestorId = iovPtr->requestorId; + instance->requestorId = ctrl_info->iov.requestorId; + if (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) { + if (!ctrl_info->adapterOperations2.activePassive) + instance->PlasmaFW111 = 1; + + dev_info(&instance->pdev->dev, "SR-IOV: firmware type: %s\n", + instance->PlasmaFW111 ? "1.11" : "new"); + + if (instance->PlasmaFW111) { + iovPtr = (struct IOV_111 *) + ((unsigned char *)ctrl_info + IOV_111_OFFSET); + instance->requestorId = iovPtr->requestorId; + } } - dev_warn(&instance->pdev->dev, "I am VF " - "requestorId %d\n", instance->requestorId); + dev_info(&instance->pdev->dev, "SRIOV: VF requestorId %d\n", + instance->requestorId); } instance->crash_dump_fw_support = @@ -4657,13 +4891,11 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->crash_dump_drv_support = (instance->crash_dump_fw_support && instance->crash_dump_buf); - if (instance->crash_dump_drv_support) { - dev_info(&instance->pdev->dev, "Firmware Crash dump " - "feature is supported\n"); + if (instance->crash_dump_drv_support) megasas_set_crash_dump_params(instance, MR_CRASH_BUF_TURN_OFF); - } else { + else { if (instance->crash_dump_buf) pci_free_consistent(instance->pdev, CRASH_DMA_BUF_SIZE, @@ -4672,39 +4904,26 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->crash_dump_buf = NULL; } - instance->secure_jbod_support = - ctrl_info->adapterOperations3.supportSecurityonJBOD; - if (instance->secure_jbod_support) - dev_info(&instance->pdev->dev, "Firmware supports Secure JBOD\n"); + + dev_info(&instance->pdev->dev, + "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n", + le16_to_cpu(ctrl_info->pci.vendor_id), + le16_to_cpu(ctrl_info->pci.device_id), + le16_to_cpu(ctrl_info->pci.sub_vendor_id), + le16_to_cpu(ctrl_info->pci.sub_device_id)); + dev_info(&instance->pdev->dev, "unevenspan support : %s\n", + instance->UnevenSpanSupport ? "yes" : "no"); + dev_info(&instance->pdev->dev, "firmware crash dump : %s\n", + instance->crash_dump_drv_support ? "yes" : "no"); + dev_info(&instance->pdev->dev, "jbod sync map : %s\n", + instance->use_seqnum_jbod_fp ? "yes" : "no"); + + instance->max_sectors_per_req = instance->max_num_sge * - PAGE_SIZE / 512; + SGE_BUFFER_SIZE / 512; if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) instance->max_sectors_per_req = tmp_sectors; - /* - * 1. For fusion adapters, 3 commands for IOCTL and 5 commands - * for driver's internal DCMDs. - * 2. For MFI skinny adapters, 5 commands for IOCTL + driver's - * internal DCMDs. - * 3. For rest of MFI adapters, 27 commands reserved for IOCTLs - * and 5 commands for drivers's internal DCMD. - */ - if (instance->ctrl_context) { - instance->max_scsi_cmds = instance->max_fw_cmds - - (MEGASAS_FUSION_INTERNAL_CMDS + - MEGASAS_FUSION_IOCTL_CMDS); - sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS); - } else if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { - instance->max_scsi_cmds = instance->max_fw_cmds - - MEGASAS_SKINNY_INT_CMDS; - sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); - } else { - instance->max_scsi_cmds = instance->max_fw_cmds - - MEGASAS_INT_CMDS; - sema_init(&instance->ioctl_sem, (MEGASAS_INT_CMDS - 5)); - } - /* Check for valid throttlequeuedepth module parameter */ if (throttlequeuedepth && throttlequeuedepth <= instance->max_scsi_cmds) @@ -4713,12 +4932,6 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; - /* - * Setup tasklet for cmd completion - */ - - tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, - (unsigned long)instance); /* Launch SR-IOV heartbeat timer */ if (instance->requestorId) { @@ -4733,7 +4946,14 @@ static int megasas_init_fw(struct megasas_instance *instance) return 0; +fail_get_pd_list: + instance->instancet->disable_intr(instance); fail_init_adapter: + megasas_destroy_irqs(instance); +fail_setup_irqs: + if (instance->msix_vectors) + pci_disable_msix(instance->pdev); + instance->msix_vectors = 0; fail_ready_state: kfree(instance->ctrl_info); instance->ctrl_info = NULL; @@ -4747,7 +4967,7 @@ fail_ready_state: /** * megasas_release_mfi - Reverses the FW initialization - * @intance: Adapter soft state + * @instance: Adapter soft state */ static void megasas_release_mfi(struct megasas_instance *instance) { @@ -4822,21 +5042,17 @@ megasas_get_seq_num(struct megasas_instance *instance, /* * Copy the data back into callers buffer */ - eli->newest_seq_num = le32_to_cpu(el_info->newest_seq_num); - eli->oldest_seq_num = le32_to_cpu(el_info->oldest_seq_num); - eli->clear_seq_num = le32_to_cpu(el_info->clear_seq_num); - eli->shutdown_seq_num = le32_to_cpu(el_info->shutdown_seq_num); - eli->boot_seq_num = le32_to_cpu(el_info->boot_seq_num); + eli->newest_seq_num = el_info->newest_seq_num; + eli->oldest_seq_num = el_info->oldest_seq_num; + eli->clear_seq_num = el_info->clear_seq_num; + eli->shutdown_seq_num = el_info->shutdown_seq_num; + eli->boot_seq_num = el_info->boot_seq_num; } pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info), el_info, el_info_h); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return 0; } @@ -4877,8 +5093,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, if (instance->aen_cmd) { - prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1]; - prev_aen.members.locale = le16_to_cpu(prev_aen.members.locale); + prev_aen.word = + le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]); /* * A class whose enum value is smaller is inclusive of all @@ -4910,7 +5126,7 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, aen_cmd, 30); if (ret_val) { - printk(KERN_DEBUG "megasas: Failed to abort " + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to abort " "previous AEN command\n"); return ret_val; } @@ -4990,7 +5206,7 @@ static int megasas_start_aen(struct megasas_instance *instance) class_locale.members.class = MR_EVT_CLASS_DEBUG; return megasas_register_aen(instance, - eli.newest_seq_num + 1, + le32_to_cpu(eli.newest_seq_num) + 1, class_locale.word); } @@ -5028,7 +5244,7 @@ static int megasas_io_attach(struct megasas_instance *instance) (max_sectors <= MEGASAS_MAX_SECTORS)) { instance->max_sectors_per_req = max_sectors; } else { - printk(KERN_INFO "megasas: max_sectors should be > 0" + dev_info(&instance->pdev->dev, "max_sectors should be > 0" "and <= %d (or < 1MB for GEN2 controller)\n", instance->max_sectors_per_req); } @@ -5043,10 +5259,7 @@ static int megasas_io_attach(struct megasas_instance *instance) host->max_cmd_len = 16; /* Fusion only supports host reset */ - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (instance->ctrl_context) { host->hostt->eh_device_reset_handler = NULL; host->hostt->eh_bus_reset_handler = NULL; } @@ -5055,7 +5268,9 @@ static int megasas_io_attach(struct megasas_instance *instance) * Notify the mid-layer about the new controller */ if (scsi_add_host(host, &instance->pdev->dev)) { - printk(KERN_DEBUG "megasas: scsi_add_host failed\n"); + dev_err(&instance->pdev->dev, + "Failed to add host from %s %d\n", + __func__, __LINE__); return -ENODEV; } @@ -5066,7 +5281,7 @@ static int megasas_set_dma_mask(struct pci_dev *pdev) { /* - * All our contollers are capable of performing 64-bit DMA + * All our controllers are capable of performing 64-bit DMA */ if (IS_DMA64) { if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) { @@ -5106,7 +5321,7 @@ fail_set_dma_mask: static int megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { - int rval, pos, i, j, cpu; + int rval, pos; struct Scsi_Host *host; struct megasas_instance *instance; u16 control = 0; @@ -5128,16 +5343,6 @@ static int megasas_probe_one(struct pci_dev *pdev, } } - /* - * Announce PCI information - */ - printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", - pdev->vendor, pdev->device, pdev->subsystem_vendor, - pdev->subsystem_device); - - printk("bus %d:slot %d:func %d\n", - pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - /* * PCI prepping: enable device set bus mastering and dma mask */ @@ -5156,13 +5361,13 @@ static int megasas_probe_one(struct pci_dev *pdev, sizeof(struct megasas_instance)); if (!host) { - printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "scsi_host_alloc failed\n"); goto fail_alloc_instance; } instance = (struct megasas_instance *)host->hostdata; memset(instance, 0, sizeof(*instance)); - atomic_set( &instance->fw_reset_no_pci_access, 0 ); + atomic_set(&instance->fw_reset_no_pci_access, 0); instance->pdev = pdev; switch (instance->pdev->device) { @@ -5170,21 +5375,28 @@ static int megasas_probe_one(struct pci_dev *pdev, case PCI_DEVICE_ID_LSI_PLASMA: case PCI_DEVICE_ID_LSI_INVADER: case PCI_DEVICE_ID_LSI_FURY: + case PCI_DEVICE_ID_LSI_INTRUDER: + case PCI_DEVICE_ID_LSI_INTRUDER_24: + case PCI_DEVICE_ID_LSI_CUTLASS_52: + case PCI_DEVICE_ID_LSI_CUTLASS_53: { instance->ctrl_context_pages = get_order(sizeof(struct fusion_context)); instance->ctrl_context = (void *)__get_free_pages(GFP_KERNEL, instance->ctrl_context_pages); if (!instance->ctrl_context) { - printk(KERN_DEBUG "megasas: Failed to allocate " + dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate " "memory for Fusion context info\n"); goto fail_alloc_dma_buf; } fusion = instance->ctrl_context; memset(fusion, 0, ((1 << PAGE_SHIFT) << instance->ctrl_context_pages)); - INIT_LIST_HEAD(&fusion->cmd_pool); - spin_lock_init(&fusion->mpt_pool_lock); + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA)) + fusion->adapter_type = THUNDERBOLT_SERIES; + else + fusion->adapter_type = INVADER_SERIES; } break; default: /* For all other supported controllers */ @@ -5197,7 +5409,7 @@ static int megasas_probe_one(struct pci_dev *pdev, &instance->consumer_h); if (!instance->producer || !instance->consumer) { - printk(KERN_DEBUG "megasas: Failed to allocate" + dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate" "memory for producer, consumer\n"); goto fail_alloc_dma_buf; } @@ -5207,6 +5419,13 @@ static int megasas_probe_one(struct pci_dev *pdev, break; } + instance->system_info_buf = pci_zalloc_consistent(pdev, + sizeof(struct MR_DRV_SYSTEM_INFO), + &instance->system_info_h); + + if (!instance->system_info_buf) + dev_info(&instance->pdev->dev, "Can't allocate system info buffer\n"); + /* Crash dump feature related initialisation*/ instance->drv_buf_index = 0; instance->drv_buf_alloc = 0; @@ -5221,7 +5440,7 @@ static int megasas_probe_one(struct pci_dev *pdev, CRASH_DMA_BUF_SIZE, &instance->crash_dump_h); if (!instance->crash_dump_buf) - dev_err(&instance->pdev->dev, "Can't allocate Firmware " + dev_err(&pdev->dev, "Can't allocate Firmware " "crash dump DMA buffer\n"); megasas_poll_wait_aen = 0; @@ -5237,7 +5456,7 @@ static int megasas_probe_one(struct pci_dev *pdev, &instance->evt_detail_h); if (!instance->evt_detail) { - printk(KERN_DEBUG "megasas: Failed to allocate memory for " + dev_printk(KERN_DEBUG, &pdev->dev, "Failed to allocate memory for " "event detail structure\n"); goto fail_alloc_dma_buf; } @@ -5280,10 +5499,7 @@ static int megasas_probe_one(struct pci_dev *pdev, instance->disableOnlineCtrlReset = 1; instance->UnevenSpanSupport = 0; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (instance->ctrl_context) { INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); INIT_WORK(&instance->crash_init, megasas_fusion_crash_dump_wq); } else @@ -5301,7 +5517,7 @@ static int megasas_probe_one(struct pci_dev *pdev, pci_alloc_consistent(pdev, sizeof(struct MR_LD_VF_AFFILIATION_111), &instance->vf_affiliation_111_h); if (!instance->vf_affiliation_111) - printk(KERN_WARNING "megasas: Can't allocate " + dev_warn(&pdev->dev, "Can't allocate " "memory for VF affiliation buffer\n"); } else { instance->vf_affiliation = @@ -5310,60 +5526,11 @@ static int megasas_probe_one(struct pci_dev *pdev, sizeof(struct MR_LD_VF_AFFILIATION), &instance->vf_affiliation_h); if (!instance->vf_affiliation) - printk(KERN_WARNING "megasas: Can't allocate " + dev_warn(&pdev->dev, "Can't allocate " "memory for VF affiliation buffer\n"); } } -retry_irq_register: - /* - * Register IRQ - */ - if (instance->msix_vectors) { - cpu = cpumask_first(cpu_online_mask); - for (i = 0; i < instance->msix_vectors; i++) { - instance->irq_context[i].instance = instance; - instance->irq_context[i].MSIxIndex = i; - if (request_irq(instance->msixentry[i].vector, - instance->instancet->service_isr, 0, - "megasas", - &instance->irq_context[i])) { - printk(KERN_DEBUG "megasas: Failed to " - "register IRQ for vector %d.\n", i); - for (j = 0; j < i; j++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[j].vector, NULL); - free_irq( - instance->msixentry[j].vector, - &instance->irq_context[j]); - } - /* Retry irq register for IO_APIC */ - instance->msix_vectors = 0; - goto retry_irq_register; - } - if (smp_affinity_enable) { - if (irq_set_affinity_hint(instance->msixentry[i].vector, - get_cpu_mask(cpu))) - dev_err(&instance->pdev->dev, - "Error setting affinity hint " - "for cpu %d\n", cpu); - cpu = cpumask_next(cpu, cpu_online_mask); - } - } - } else { - instance->irq_context[0].instance = instance; - instance->irq_context[0].MSIxIndex = 0; - if (request_irq(pdev->irq, instance->instancet->service_isr, - IRQF_SHARED, "megasas", - &instance->irq_context[0])) { - printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); - goto fail_irq; - } - } - - instance->instancet->enable_intr(instance); - /* * Store instance in PCI softstate */ @@ -5393,7 +5560,7 @@ retry_irq_register: * Initiate AEN (Asynchronous Event Notification) */ if (megasas_start_aen(instance)) { - printk(KERN_DEBUG "megasas: start aen failed\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "start aen failed\n"); goto fail_start_aen; } @@ -5403,35 +5570,23 @@ retry_irq_register: return 0; - fail_start_aen: - fail_io_attach: +fail_start_aen: +fail_io_attach: megasas_mgmt_info.count--; megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; megasas_mgmt_info.max_index--; instance->instancet->disable_intr(instance); - if (instance->msix_vectors) - for (i = 0; i < instance->msix_vectors; i++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[i].vector, NULL); - free_irq(instance->msixentry[i].vector, - &instance->irq_context[i]); - } - else - free_irq(instance->pdev->irq, &instance->irq_context[0]); -fail_irq: - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) + megasas_destroy_irqs(instance); + + if (instance->ctrl_context) megasas_release_fusion(instance); else megasas_release_mfi(instance); - fail_init_mfi: if (instance->msix_vectors) pci_disable_msix(instance->pdev); - fail_alloc_dma_buf: +fail_init_mfi: +fail_alloc_dma_buf: if (instance->evt_detail) pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), instance->evt_detail, @@ -5445,8 +5600,8 @@ fail_irq: instance->consumer_h); scsi_host_put(host); - fail_alloc_instance: - fail_set_dma_mask: +fail_alloc_instance: +fail_set_dma_mask: pci_disable_device(pdev); return -ENODEV; @@ -5487,13 +5642,7 @@ static void megasas_flush_cache(struct megasas_instance *instance) dev_err(&instance->pdev->dev, "Command timedout" " from %s\n", __func__); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); - - return; + megasas_return_cmd(instance, cmd); } /** @@ -5517,10 +5666,14 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, if (instance->aen_cmd) megasas_issue_blocked_abort_cmd(instance, - instance->aen_cmd, 30); + instance->aen_cmd, MEGASAS_BLOCKED_CMD_TIMEOUT); if (instance->map_update_cmd) megasas_issue_blocked_abort_cmd(instance, - instance->map_update_cmd, 30); + instance->map_update_cmd, MEGASAS_BLOCKED_CMD_TIMEOUT); + if (instance->jbod_seq_cmd) + megasas_issue_blocked_abort_cmd(instance, + instance->jbod_seq_cmd, MEGASAS_BLOCKED_CMD_TIMEOUT); + dcmd = &cmd->frame->dcmd; memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -5538,13 +5691,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, dev_err(&instance->pdev->dev, "Command timedout" "from %s\n", __func__); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); - - return; + megasas_return_cmd(instance, cmd); } #ifdef CONFIG_PM @@ -5558,7 +5705,6 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *host; struct megasas_instance *instance; - int i; instance = pci_get_drvdata(pdev); host = instance->host; @@ -5583,16 +5729,8 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) pci_set_drvdata(instance->pdev, instance); instance->instancet->disable_intr(instance); - if (instance->msix_vectors) - for (i = 0; i < instance->msix_vectors; i++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[i].vector, NULL); - free_irq(instance->msixentry[i].vector, - &instance->irq_context[i]); - } - else - free_irq(instance->pdev->irq, &instance->irq_context[0]); + megasas_destroy_irqs(instance); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); @@ -5611,7 +5749,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state) static int megasas_resume(struct pci_dev *pdev) { - int rval, i, j, cpu; + int rval; struct Scsi_Host *host; struct megasas_instance *instance; @@ -5627,7 +5765,7 @@ megasas_resume(struct pci_dev *pdev) rval = pci_enable_device_mem(pdev); if (rval) { - printk(KERN_ERR "megasas: Enable device failed\n"); + dev_err(&pdev->dev, "Enable device failed\n"); return rval; } @@ -5654,12 +5792,7 @@ megasas_resume(struct pci_dev *pdev) instance->msix_vectors)) goto fail_reenable_msix; - switch (instance->pdev->device) { - case PCI_DEVICE_ID_LSI_FUSION: - case PCI_DEVICE_ID_LSI_PLASMA: - case PCI_DEVICE_ID_LSI_INVADER: - case PCI_DEVICE_ID_LSI_FURY: - { + if (instance->ctrl_context) { megasas_reset_reply_desc(instance); if (megasas_ioc_init_fusion(instance)) { megasas_free_cmds(instance); @@ -5668,63 +5801,20 @@ megasas_resume(struct pci_dev *pdev) } if (!megasas_get_map_info(instance)) megasas_sync_map_info(instance); - } - break; - default: + } else { *instance->producer = 0; *instance->consumer = 0; if (megasas_issue_init_mfi(instance)) goto fail_init_mfi; - break; } tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, (unsigned long)instance); - /* - * Register IRQ - */ - if (instance->msix_vectors) { - cpu = cpumask_first(cpu_online_mask); - for (i = 0 ; i < instance->msix_vectors; i++) { - instance->irq_context[i].instance = instance; - instance->irq_context[i].MSIxIndex = i; - if (request_irq(instance->msixentry[i].vector, - instance->instancet->service_isr, 0, - "megasas", - &instance->irq_context[i])) { - printk(KERN_DEBUG "megasas: Failed to " - "register IRQ for vector %d.\n", i); - for (j = 0; j < i; j++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[j].vector, NULL); - free_irq( - instance->msixentry[j].vector, - &instance->irq_context[j]); - } - goto fail_irq; - } - - if (smp_affinity_enable) { - if (irq_set_affinity_hint(instance->msixentry[i].vector, - get_cpu_mask(cpu))) - dev_err(&instance->pdev->dev, "Error " - "setting affinity hint for cpu " - "%d\n", cpu); - cpu = cpumask_next(cpu, cpu_online_mask); - } - } - } else { - instance->irq_context[0].instance = instance; - instance->irq_context[0].MSIxIndex = 0; - if (request_irq(pdev->irq, instance->instancet->service_isr, - IRQF_SHARED, "megasas", - &instance->irq_context[0])) { - printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); - goto fail_irq; - } - } + if (instance->msix_vectors ? + megasas_setup_irqs_msix(instance, 0) : + megasas_setup_irqs_ioapic(instance)) + goto fail_init_mfi; /* Re-launch SR-IOV heartbeat timer */ if (instance->requestorId) { @@ -5733,22 +5823,24 @@ megasas_resume(struct pci_dev *pdev) &instance->sriov_heartbeat_timer, megasas_sriov_heartbeat_handler, MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF); - else + else { instance->skip_heartbeat_timer_del = 1; + goto fail_init_mfi; + } } instance->instancet->enable_intr(instance); + megasas_setup_jbod_map(instance); instance->unload = 0; /* * Initiate AEN (Asynchronous Event Notification) */ if (megasas_start_aen(instance)) - printk(KERN_ERR "megasas: Start AEN failed\n"); + dev_err(&instance->pdev->dev, "Start AEN failed\n"); return 0; -fail_irq: fail_init_mfi: if (instance->evt_detail) pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), @@ -5786,6 +5878,7 @@ static void megasas_detach_one(struct pci_dev *pdev) struct Scsi_Host *host; struct megasas_instance *instance; struct fusion_context *fusion; + u32 pd_seq_map_sz; instance = pci_get_drvdata(pdev); instance->unload = 1; @@ -5829,25 +5922,16 @@ static void megasas_detach_one(struct pci_dev *pdev) instance->instancet->disable_intr(instance); - if (instance->msix_vectors) - for (i = 0; i < instance->msix_vectors; i++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[i].vector, NULL); - free_irq(instance->msixentry[i].vector, - &instance->irq_context[i]); - } - else - free_irq(instance->pdev->irq, &instance->irq_context[0]); + megasas_destroy_irqs(instance); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); - switch (instance->pdev->device) { - case PCI_DEVICE_ID_LSI_FUSION: - case PCI_DEVICE_ID_LSI_PLASMA: - case PCI_DEVICE_ID_LSI_INVADER: - case PCI_DEVICE_ID_LSI_FURY: + if (instance->ctrl_context) { megasas_release_fusion(instance); + pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + + (sizeof(struct MR_PD_CFG_SEQ) * + (MAX_PHYSICAL_DEVICES - 1)); for (i = 0; i < 2 ; i++) { if (fusion->ld_map[i]) dma_free_coherent(&instance->pdev->dev, @@ -5857,11 +5941,15 @@ static void megasas_detach_one(struct pci_dev *pdev) if (fusion->ld_drv_map[i]) free_pages((ulong)fusion->ld_drv_map[i], fusion->drv_map_pages); + if (fusion->pd_seq_sync) + dma_free_coherent(&instance->pdev->dev, + pd_seq_map_sz, + fusion->pd_seq_sync[i], + fusion->pd_seq_phys[i]); } free_pages((ulong)instance->ctrl_context, instance->ctrl_context_pages); - break; - default: + } else { megasas_release_mfi(instance); pci_free_consistent(pdev, sizeof(u32), instance->producer, @@ -5869,7 +5957,6 @@ static void megasas_detach_one(struct pci_dev *pdev) pci_free_consistent(pdev, sizeof(u32), instance->consumer, instance->consumer_h); - break; } kfree(instance->ctrl_info); @@ -5899,11 +5986,13 @@ static void megasas_detach_one(struct pci_dev *pdev) pci_free_consistent(pdev, CRASH_DMA_BUF_SIZE, instance->crash_dump_buf, instance->crash_dump_h); + if (instance->system_info_buf) + pci_free_consistent(pdev, sizeof(struct MR_DRV_SYSTEM_INFO), + instance->system_info_buf, instance->system_info_h); + scsi_host_put(host); pci_disable_device(pdev); - - return; } /** @@ -5912,23 +6001,14 @@ static void megasas_detach_one(struct pci_dev *pdev) */ static void megasas_shutdown(struct pci_dev *pdev) { - int i; struct megasas_instance *instance = pci_get_drvdata(pdev); instance->unload = 1; megasas_flush_cache(instance); megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); instance->instancet->disable_intr(instance); - if (instance->msix_vectors) - for (i = 0; i < instance->msix_vectors; i++) { - if (smp_affinity_enable) - irq_set_affinity_hint( - instance->msixentry[i].vector, NULL); - free_irq(instance->msixentry[i].vector, - &instance->irq_context[i]); - } - else - free_irq(instance->pdev->irq, &instance->irq_context[0]); + megasas_destroy_irqs(instance); + if (instance->msix_vectors) pci_disable_msix(instance->pdev); } @@ -5981,11 +6061,11 @@ static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) { unsigned int mask; unsigned long flags; + poll_wait(file, &megasas_poll_wait, wait); spin_lock_irqsave(&poll_aen_lock, flags); if (megasas_poll_wait_aen) - mask = (POLLIN | POLLRDNORM); - + mask = (POLLIN | POLLRDNORM); else mask = 0; megasas_poll_wait_aen = 0; @@ -5999,8 +6079,7 @@ static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) * @cmd: MFI command frame */ -static int megasas_set_crash_dump_params_ioctl( - struct megasas_cmd *cmd) +static int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd) { struct megasas_instance *local_instance; int i, error = 0; @@ -6054,14 +6133,14 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, memset(kbuff_arr, 0, sizeof(kbuff_arr)); if (ioc->sge_count > MAX_IOCTL_SGE) { - printk(KERN_DEBUG "megasas: SGE count [%d] > max limit [%d]\n", + dev_printk(KERN_DEBUG, &instance->pdev->dev, "SGE count [%d] > max limit [%d]\n", ioc->sge_count, MAX_IOCTL_SGE); return -EINVAL; } cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: Failed to get a cmd packet\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a cmd packet\n"); return -ENOMEM; } @@ -6106,8 +6185,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, ioc->sgl[i].iov_len, &buf_handle, GFP_KERNEL); if (!kbuff_arr[i]) { - printk(KERN_DEBUG "megasas: Failed to alloc " - "kernel SGL buffer for IOCTL \n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc " + "kernel SGL buffer for IOCTL\n"); error = -ENOMEM; goto out; } @@ -6180,7 +6259,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)), sense, ioc->sense_len)) { - printk(KERN_ERR "megasas: Failed to copy out to user " + dev_err(&instance->pdev->dev, "Failed to copy out to user " "sense data\n"); error = -EFAULT; goto out; @@ -6192,11 +6271,11 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, */ if (copy_to_user(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status, sizeof(u8))) { - printk(KERN_DEBUG "megasas: Error copying out cmd_status\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error copying out cmd_status\n"); error = -EFAULT; } - out: +out: if (sense) { dma_free_coherent(&instance->pdev->dev, ioc->sense_len, sense, sense_handle); @@ -6211,11 +6290,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, kbuff_arr[i] = NULL; } - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return error; } @@ -6256,7 +6331,7 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) } if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { - printk(KERN_ERR "Controller in crit error\n"); + dev_err(&instance->pdev->dev, "Controller in crit error\n"); error = -ENODEV; goto out_kfree_ioc; } @@ -6281,7 +6356,7 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) spin_unlock_irqrestore(&instance->hba_lock, flags); if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: waiting" + dev_notice(&instance->pdev->dev, "waiting" "for controller reset to finish\n"); } @@ -6292,7 +6367,7 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { spin_unlock_irqrestore(&instance->hba_lock, flags); - printk(KERN_ERR "megaraid_sas: timed out while" + dev_err(&instance->pdev->dev, "timed out while" "waiting for HBA to recover\n"); error = -ENODEV; goto out_up; @@ -6300,10 +6375,10 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) spin_unlock_irqrestore(&instance->hba_lock, flags); error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); - out_up: +out_up: up(&instance->ioctl_sem); - out_kfree_ioc: +out_kfree_ioc: kfree(ioc); return error; } @@ -6351,7 +6426,7 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) spin_unlock_irqrestore(&instance->hba_lock, flags); if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: waiting for" + dev_notice(&instance->pdev->dev, "waiting for" "controller reset to finish\n"); } @@ -6361,8 +6436,8 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) spin_lock_irqsave(&instance->hba_lock, flags); if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { spin_unlock_irqrestore(&instance->hba_lock, flags); - printk(KERN_ERR "megaraid_sas: timed out while waiting" - "for HBA to recover.\n"); + dev_err(&instance->pdev->dev, "timed out while waiting" + "for HBA to recover\n"); return -ENODEV; } spin_unlock_irqrestore(&instance->hba_lock, flags); @@ -6401,6 +6476,9 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) int i; int error = 0; compat_uptr_t ptr; + unsigned long local_raw_ptr; + u32 local_sense_off; + u32 local_sense_len; if (clear_user(ioc, sizeof(*ioc))) return -EFAULT; @@ -6418,9 +6496,15 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) * sense_len is not null, so prepare the 64bit value under * the same condition. */ - if (ioc->sense_len) { + if (get_user(local_raw_ptr, ioc->frame.raw) || + get_user(local_sense_off, &ioc->sense_off) || + get_user(local_sense_len, &ioc->sense_len)) + return -EFAULT; + + + if (local_sense_len) { void __user **sense_ioc_ptr = - (void __user **)(ioc->frame.raw + ioc->sense_off); + (void __user **)((u8*)local_raw_ptr + local_sense_off); compat_uptr_t *sense_cioc_ptr = (compat_uptr_t *)(cioc->frame.raw + cioc->sense_off); if (get_user(ptr, sense_cioc_ptr) || @@ -6501,6 +6585,15 @@ static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf) static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL); +static ssize_t +megasas_sysfs_show_release_date(struct device_driver *dd, char *buf) +{ + return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", + MEGASAS_RELDATE); +} + +static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, NULL); + static ssize_t megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf) { @@ -6529,7 +6622,8 @@ static ssize_t megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t count) { int retval = count; - if(sscanf(buf,"%u",&megasas_dbg_lvl)<1){ + + if (sscanf(buf, "%u", &megasas_dbg_lvl) < 1) { printk(KERN_ERR "megasas: could not set dbg_lvl\n"); retval = -EINVAL; } @@ -6569,7 +6663,7 @@ megasas_aen_polling(struct work_struct *work) if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) break; if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: %s waiting for " + dev_notice(&instance->pdev->dev, "%s waiting for " "controller reset to finish for scsi%d\n", __func__, instance->host->host_no); } @@ -6579,6 +6673,7 @@ megasas_aen_polling(struct work_struct *work) instance->ev = NULL; host = instance->host; if (instance->evt_detail) { + megasas_decode_evt(instance); switch (le32_to_cpu(instance->evt_detail->code)) { case MR_EVT_PD_INSERTED: @@ -6591,14 +6686,12 @@ megasas_aen_polling(struct work_struct *work) pd_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; - sdev1 = - scsi_device_lookup(host, i, j, 0); + sdev1 = scsi_device_lookup(host, i, j, 0); if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { - if (!sdev1) { + if (!sdev1) scsi_add_device(host, i, j, 0); - } if (sdev1) scsi_device_put(sdev1); @@ -6619,14 +6712,12 @@ megasas_aen_polling(struct work_struct *work) pd_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; - sdev1 = - scsi_device_lookup(host, i, j, 0); + sdev1 = scsi_device_lookup(host, i, j, 0); if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { - if (sdev1) { + if (sdev1) scsi_device_put(sdev1); - } } else { if (sdev1) { scsi_remove_device(sdev1); @@ -6643,8 +6734,7 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_CFG_CLEARED: case MR_EVT_LD_DELETED: if (!instance->requestorId || - (instance->requestorId && - megasas_get_ld_vf_affiliation(instance, 0))) { + megasas_get_ld_vf_affiliation(instance, 0)) { if (megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) megasas_get_ld_list(instance); @@ -6675,8 +6765,7 @@ megasas_aen_polling(struct work_struct *work) break; case MR_EVT_LD_CREATED: if (!instance->requestorId || - (instance->requestorId && - megasas_get_ld_vf_affiliation(instance, 0))) { + megasas_get_ld_vf_affiliation(instance, 0)) { if (megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) megasas_get_ld_list(instance); @@ -6706,18 +6795,21 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_LD_STATE_CHANGE: doscan = 1; break; + case MR_EVT_CTRL_PROP_CHANGED: + megasas_get_ctrl_info(instance); + break; default: doscan = 0; break; } } else { - printk(KERN_ERR "invalid evt_detail!\n"); + dev_err(&instance->pdev->dev, "invalid evt_detail!\n"); kfree(ev); return; } if (doscan) { - printk(KERN_INFO "megaraid_sas: scanning for scsi%d...\n", + dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", instance->host->host_no); if (megasas_get_pd_list(instance) == 0) { for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { @@ -6742,8 +6834,7 @@ megasas_aen_polling(struct work_struct *work) } if (!instance->requestorId || - (instance->requestorId && - megasas_get_ld_vf_affiliation(instance, 0))) { + megasas_get_ld_vf_affiliation(instance, 0)) { if (megasas_ld_list_query(instance, MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) megasas_get_ld_list(instance); @@ -6772,7 +6863,7 @@ megasas_aen_polling(struct work_struct *work) } } - if ( instance->aen_cmd != NULL ) { + if (instance->aen_cmd != NULL) { kfree(ev); return ; } @@ -6789,7 +6880,7 @@ megasas_aen_polling(struct work_struct *work) mutex_unlock(&instance->aen_mutex); if (error) - printk(KERN_ERR "register aen failed error %x\n", error); + dev_err(&instance->pdev->dev, "register aen failed error %x\n", error); kfree(ev); } @@ -6840,6 +6931,11 @@ static int __init megasas_init(void) if (rval) goto err_dcf_attr_ver; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_release_date); + if (rval) + goto err_dcf_rel_date; + rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_support_poll_for_event); if (rval) @@ -6863,6 +6959,9 @@ err_dcf_dbg_lvl: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_poll_for_event); err_dcf_support_poll_for_event: + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_release_date); +err_dcf_rel_date: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); err_dcf_attr_ver: pci_unregister_driver(&megasas_pci_driver); @@ -6882,6 +6981,8 @@ static void __exit megasas_exit(void) &driver_attr_support_poll_for_event); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_support_device_change); + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_release_date); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); pci_unregister_driver(&megasas_pci_driver); diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas_fp.c b/kernel/drivers/scsi/megaraid/megaraid_sas_fp.c index 4f7228786..741509b35 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/kernel/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -66,7 +66,15 @@ MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding " #define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) #define MR_LD_STATE_OPTIMAL 3 + +#ifdef FALSE +#undef FALSE +#endif #define FALSE 0 + +#ifdef TRUE +#undef TRUE +#endif #define TRUE 1 #define SPAN_DEBUG 0 @@ -142,7 +150,7 @@ u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map) return le16_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef); } -u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map) +__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map) { return map->raidMap.devHndlInfo[pd].curDevHdl; } @@ -733,14 +741,12 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld, u8 physArm, span; u64 row; u8 retval = TRUE; - u8 do_invader = 0; u64 *pdBlock = &io_info->pdBlock; - u16 *pDevHandle = &io_info->devHandle; + __le16 *pDevHandle = &io_info->devHandle; u32 logArm, rowMod, armQ, arm; + struct fusion_context *fusion; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER || - instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - do_invader = 1; + fusion = instance->ctrl_context; /*Get row and span from io_info for Uneven Span IO.*/ row = io_info->start_row; @@ -769,9 +775,10 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld, if (pd != MR_PD_INVALID) *pDevHandle = MR_PdDevHandleGet(pd, map); else { - *pDevHandle = MR_PD_INVALID; + *pDevHandle = cpu_to_le16(MR_PD_INVALID); if ((raid->level >= 5) && - (!do_invader || (do_invader && + ((fusion->adapter_type == THUNDERBOLT_SERIES) || + ((fusion->adapter_type == INVADER_SERIES) && (raid->regTypeReqOnRead != REGION_TYPE_UNUSED)))) pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE; else if (raid->level == 1) { @@ -815,13 +822,12 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow, u8 physArm, span; u64 row; u8 retval = TRUE; - u8 do_invader = 0; u64 *pdBlock = &io_info->pdBlock; - u16 *pDevHandle = &io_info->devHandle; + __le16 *pDevHandle = &io_info->devHandle; + struct fusion_context *fusion; + + fusion = instance->ctrl_context; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER || - instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - do_invader = 1; row = mega_div64_32(stripRow, raid->rowDataSize); @@ -864,9 +870,11 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow, /* Get dev handle from Pd. */ *pDevHandle = MR_PdDevHandleGet(pd, map); else { - *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */ + /* set dev handle as invalid. */ + *pDevHandle = cpu_to_le16(MR_PD_INVALID); if ((raid->level >= 5) && - (!do_invader || (do_invader && + ((fusion->adapter_type == THUNDERBOLT_SERIES) || + ((fusion->adapter_type == INVADER_SERIES) && (raid->regTypeReqOnRead != REGION_TYPE_UNUSED)))) pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE; else if (raid->level == 1) { @@ -900,6 +908,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, struct RAID_CONTEXT *pRAID_Context, struct MR_DRV_RAID_MAP_ALL *map, u8 **raidLUN) { + struct fusion_context *fusion; struct MR_LD_RAID *raid; u32 ld, stripSize, stripe_mask; u64 endLba, endStrip, endRow, start_row, start_strip; @@ -920,6 +929,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, isRead = io_info->isRead; io_info->IoforUnevenSpan = 0; io_info->start_span = SPAN_INVALID; + fusion = instance->ctrl_context; ld = MR_TargetIdToLdGet(ldTgtId, map); raid = MR_LdRaidGet(ld, map); @@ -1083,8 +1093,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, cpu_to_le16(raid->fpIoTimeoutForLd ? raid->fpIoTimeoutForLd : map->raidMap.fpPdIoTimeoutSec); - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) + if (fusion->adapter_type == INVADER_SERIES) pRAID_Context->regLockFlags = (isRead) ? raid->regTypeReqOnRead : raid->regTypeReqOnWrite; else @@ -1109,7 +1118,7 @@ MR_BuildRaidContext(struct megasas_instance *instance, ref_in_start_stripe, io_info, pRAID_Context, map); /* If IO on an invalid Pd, then FP is not possible.*/ - if (io_info->devHandle == MR_PD_INVALID) + if (io_info->devHandle == cpu_to_le16(MR_PD_INVALID)) io_info->fpOkForIo = FALSE; return retval; } else if (isRead) { @@ -1189,10 +1198,6 @@ void mr_update_span_set(struct MR_DRV_RAID_MAP_ALL *map, span_row_width += MR_LdSpanPtrGet (ld, count, map)->spanRowDataSize; - printk(KERN_INFO "megasas:" - "span %x rowDataSize %x\n", - count, MR_LdSpanPtrGet - (ld, count, map)->spanRowDataSize); } } @@ -1341,11 +1346,11 @@ u8 megasas_get_best_arm_pd(struct megasas_instance *instance, return io_info->pd_after_lb; } -u16 get_updated_dev_handle(struct megasas_instance *instance, +__le16 get_updated_dev_handle(struct megasas_instance *instance, struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *io_info) { u8 arm_pd; - u16 devHandle; + __le16 devHandle; struct fusion_context *fusion; struct MR_DRV_RAID_MAP_ALL *drv_map; diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.c b/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.c index 5a0800d19..8d630a552 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -53,10 +53,12 @@ #include #include #include +#include #include "megaraid_sas_fusion.h" #include "megaraid_sas.h" + extern void megasas_free_cmds(struct megasas_instance *instance); extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance *instance); @@ -156,28 +158,15 @@ megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs) * megasas_get_cmd_fusion - Get a command from the free pool * @instance: Adapter soft state * - * Returns a free command from the pool + * Returns a blk_tag indexed mpt frame */ -struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance - *instance) +inline struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance + *instance, u32 blk_tag) { - unsigned long flags; - struct fusion_context *fusion = - (struct fusion_context *)instance->ctrl_context; - struct megasas_cmd_fusion *cmd = NULL; - - spin_lock_irqsave(&fusion->mpt_pool_lock, flags); - - if (!list_empty(&fusion->cmd_pool)) { - cmd = list_entry((&fusion->cmd_pool)->next, - struct megasas_cmd_fusion, list); - list_del_init(&cmd->list); - } else { - printk(KERN_ERR "megasas: Command pool (fusion) empty!\n"); - } + struct fusion_context *fusion; - spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags); - return cmd; + fusion = instance->ctrl_context; + return fusion->cmd_list[blk_tag]; } /** @@ -188,47 +177,35 @@ struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance inline void megasas_return_cmd_fusion(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd) { - unsigned long flags; - struct fusion_context *fusion = - (struct fusion_context *)instance->ctrl_context; - - spin_lock_irqsave(&fusion->mpt_pool_lock, flags); - cmd->scmd = NULL; - cmd->sync_cmd_idx = (u32)ULONG_MAX; memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST)); - list_add(&cmd->list, (&fusion->cmd_pool)->next); - - spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags); } /** - * megasas_return_mfi_mpt_pthr - Return a mfi and mpt to free command pool - * @instance: Adapter soft state - * @cmd_mfi: MFI Command packet to be returned to free command pool - * @cmd_mpt: MPT Command packet to be returned to free command pool + * megasas_fire_cmd_fusion - Sends command to the FW */ -inline void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance, - struct megasas_cmd *cmd_mfi, - struct megasas_cmd_fusion *cmd_fusion) +static void +megasas_fire_cmd_fusion(struct megasas_instance *instance, + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc) { +#if defined(writeq) && defined(CONFIG_64BIT) + u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) | + le32_to_cpu(req_desc->u.low)); + + writeq(req_data, &instance->reg_set->inbound_low_queue_port); +#else unsigned long flags; - /* - * TO DO: optimize this code and use only one lock instead of two - * locks being used currently- mpt_pool_lock is acquired - * inside mfi_pool_lock - */ - spin_lock_irqsave(&instance->mfi_pool_lock, flags); - megasas_return_cmd_fusion(instance, cmd_fusion); - if (atomic_read(&cmd_mfi->mfi_mpt_pthr) != MFI_MPT_ATTACHED) - dev_err(&instance->pdev->dev, "Possible bug from %s %d\n", - __func__, __LINE__); - atomic_set(&cmd_mfi->mfi_mpt_pthr, MFI_MPT_DETACHED); - __megasas_return_cmd(instance, cmd_mfi); - spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); + spin_lock_irqsave(&instance->hba_lock, flags); + writel(le32_to_cpu(req_desc->u.low), + &instance->reg_set->inbound_low_queue_port); + writel(le32_to_cpu(req_desc->u.high), + &instance->reg_set->inbound_high_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); +#endif } + /** * megasas_teardown_frame_pool_fusion - Destroy the cmd frame DMA pool * @instance: Adapter soft state @@ -244,7 +221,7 @@ static void megasas_teardown_frame_pool_fusion( struct megasas_cmd_fusion *cmd; if (!fusion->sg_dma_pool || !fusion->sense_dma_pool) { - printk(KERN_ERR "megasas: dma pool is null. SG Pool %p, " + dev_err(&instance->pdev->dev, "dma pool is null. SG Pool %p, " "sense pool : %p\n", fusion->sg_dma_pool, fusion->sense_dma_pool); return; @@ -326,7 +303,6 @@ megasas_free_cmds_fusion(struct megasas_instance *instance) kfree(fusion->cmd_list); fusion->cmd_list = NULL; - INIT_LIST_HEAD(&fusion->cmd_pool); } /** @@ -340,33 +316,28 @@ static int megasas_create_frame_pool_fusion(struct megasas_instance *instance) u32 max_cmd; struct fusion_context *fusion; struct megasas_cmd_fusion *cmd; - u32 total_sz_chain_frame; fusion = instance->ctrl_context; max_cmd = instance->max_fw_cmds; - total_sz_chain_frame = MEGASAS_MAX_SZ_CHAIN_FRAME; /* * Use DMA pool facility provided by PCI layer */ - fusion->sg_dma_pool = pci_pool_create("megasas sg pool fusion", - instance->pdev, - total_sz_chain_frame, 4, - 0); + fusion->sg_dma_pool = pci_pool_create("sg_pool_fusion", instance->pdev, + instance->max_chain_frame_sz, + 4, 0); if (!fusion->sg_dma_pool) { - printk(KERN_DEBUG "megasas: failed to setup request pool " - "fusion\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup request pool fusion\n"); return -ENOMEM; } - fusion->sense_dma_pool = pci_pool_create("megasas sense pool fusion", + fusion->sense_dma_pool = pci_pool_create("sense pool fusion", instance->pdev, SCSI_SENSE_BUFFERSIZE, 64, 0); if (!fusion->sense_dma_pool) { - printk(KERN_DEBUG "megasas: failed to setup sense pool " - "fusion\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool fusion\n"); pci_pool_destroy(fusion->sg_dma_pool); fusion->sg_dma_pool = NULL; return -ENOMEM; @@ -390,7 +361,7 @@ static int megasas_create_frame_pool_fusion(struct megasas_instance *instance) * whatever has been allocated */ if (!cmd->sg_frame || !cmd->sense) { - printk(KERN_DEBUG "megasas: pci_pool_alloc failed\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "pci_pool_alloc failed\n"); megasas_teardown_frame_pool_fusion(instance); return -ENOMEM; } @@ -436,7 +407,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) &fusion->req_frames_desc_phys, GFP_KERNEL); if (!fusion->req_frames_desc) { - printk(KERN_ERR "megasas; Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "request_frames\n"); goto fail_req_desc; } @@ -447,7 +418,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) fusion->reply_alloc_sz * count, 16, 0); if (!fusion->reply_frames_desc_pool) { - printk(KERN_ERR "megasas; Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "reply_frame pool\n"); goto fail_reply_desc; } @@ -456,7 +427,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) pci_pool_alloc(fusion->reply_frames_desc_pool, GFP_KERNEL, &fusion->reply_frames_desc_phys); if (!fusion->reply_frames_desc) { - printk(KERN_ERR "megasas; Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "reply_frame pool\n"); pci_pool_destroy(fusion->reply_frames_desc_pool); goto fail_reply_desc; @@ -464,7 +435,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) reply_desc = fusion->reply_frames_desc; for (i = 0; i < fusion->reply_q_depth * count; i++, reply_desc++) - reply_desc->Words = ULLONG_MAX; + reply_desc->Words = cpu_to_le64(ULLONG_MAX); io_frames_sz = fusion->io_frames_alloc_sz; @@ -473,7 +444,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) fusion->io_frames_alloc_sz, 16, 0); if (!fusion->io_request_frames_pool) { - printk(KERN_ERR "megasas: Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "io_request_frame pool\n"); goto fail_io_frames; } @@ -482,7 +453,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) pci_pool_alloc(fusion->io_request_frames_pool, GFP_KERNEL, &fusion->io_request_frames_phys); if (!fusion->io_request_frames) { - printk(KERN_ERR "megasas: Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "io_request_frames frames\n"); pci_pool_destroy(fusion->io_request_frames_pool); goto fail_io_frames; @@ -497,7 +468,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) * max_cmd, GFP_KERNEL); if (!fusion->cmd_list) { - printk(KERN_DEBUG "megasas: out of memory. Could not alloc " + dev_printk(KERN_DEBUG, &instance->pdev->dev, "out of memory. Could not alloc " "memory for cmd_list_fusion\n"); goto fail_cmd_list; } @@ -507,7 +478,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) fusion->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd_fusion), GFP_KERNEL); if (!fusion->cmd_list[i]) { - printk(KERN_ERR "Could not alloc cmd list fusion\n"); + dev_err(&instance->pdev->dev, "Could not alloc cmd list fusion\n"); for (j = 0; j < i; j++) kfree(fusion->cmd_list[j]); @@ -535,7 +506,9 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) memset(cmd, 0, sizeof(struct megasas_cmd_fusion)); cmd->index = i + 1; cmd->scmd = NULL; - cmd->sync_cmd_idx = (u32)ULONG_MAX; /* Set to Invalid */ + cmd->sync_cmd_idx = (i >= instance->max_scsi_cmds) ? + (i - instance->max_scsi_cmds) : + (u32)ULONG_MAX; /* Set to Invalid */ cmd->instance = instance; cmd->io_request = (struct MPI2_RAID_SCSI_IO_REQUEST *) @@ -543,15 +516,13 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance) memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST)); cmd->io_request_phys_addr = io_req_base_phys + offset; - - list_add_tail(&cmd->list, &fusion->cmd_pool); } /* * Create a frame pool and assign one frame to each cmd */ if (megasas_create_frame_pool_fusion(instance)) { - printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n"); megasas_free_cmds_fusion(instance); goto fail_req_desc; } @@ -605,14 +576,11 @@ wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, msleep(20); } - if (frame_hdr->cmd_status == 0xff) { - if (fusion) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); + if (frame_hdr->cmd_status == 0xff) return -ETIME; - } - return 0; + return (frame_hdr->cmd_status == MFI_STAT_OK) ? + 0 : 1; } /** @@ -633,13 +601,15 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc; int i; struct megasas_header *frame_hdr; + const char *sys_info; + MFI_CAPABILITIES *drv_ops; fusion = instance->ctrl_context; cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_ERR "Could not allocate cmd for INIT Frame\n"); + dev_err(&instance->pdev->dev, "Could not allocate cmd for INIT Frame\n"); ret = 1; goto fail_get_cmd; } @@ -650,7 +620,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) &ioc_init_handle, GFP_KERNEL); if (!IOCInitMessage) { - printk(KERN_ERR "Could not allocate memory for " + dev_err(&instance->pdev->dev, "Could not allocate memory for " "IOCInitMessage\n"); ret = 1; goto fail_fw_init; @@ -673,28 +643,41 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) frame_hdr = &cmd->frame->hdr; frame_hdr->cmd_status = 0xFF; - frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE); + frame_hdr->flags = cpu_to_le16( + le16_to_cpu(frame_hdr->flags) | + MFI_FRAME_DONT_POST_IN_REPLY_QUEUE); init_frame->cmd = MFI_CMD_INIT; init_frame->cmd_status = 0xFF; + drv_ops = (MFI_CAPABILITIES *) &(init_frame->driver_operations); + /* driver support Extended MSIX */ - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - init_frame->driver_operations. - mfi_capabilities.support_additional_msix = 1; + if (fusion->adapter_type == INVADER_SERIES) + drv_ops->mfi_capabilities.support_additional_msix = 1; /* driver supports HA / Remote LUN over Fast Path interface */ - init_frame->driver_operations.mfi_capabilities.support_fp_remote_lun - = 1; - init_frame->driver_operations.mfi_capabilities.support_max_255lds - = 1; - init_frame->driver_operations.mfi_capabilities.support_ndrive_r1_lb - = 1; - init_frame->driver_operations.mfi_capabilities.security_protocol_cmds_fw - = 1; + drv_ops->mfi_capabilities.support_fp_remote_lun = 1; + + drv_ops->mfi_capabilities.support_max_255lds = 1; + drv_ops->mfi_capabilities.support_ndrive_r1_lb = 1; + drv_ops->mfi_capabilities.security_protocol_cmds_fw = 1; + + if (instance->max_chain_frame_sz > MEGASAS_CHAIN_FRAME_SZ_MIN) + drv_ops->mfi_capabilities.support_ext_io_size = 1; + /* Convert capability to LE32 */ cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities); + sys_info = dmi_get_system_info(DMI_PRODUCT_UUID); + if (instance->system_info_buf && sys_info) { + memcpy(instance->system_info_buf->systemId, sys_info, + strlen(sys_info) > 64 ? 64 : strlen(sys_info)); + instance->system_info_buf->systemIdLength = + strlen(sys_info) > 64 ? 64 : strlen(sys_info); + init_frame->system_info_lo = instance->system_info_h; + init_frame->system_info_hi = 0; + } + init_frame->queue_info_new_phys_addr_hi = cpu_to_le32(upper_32_bits(ioc_init_handle)); init_frame->queue_info_new_phys_addr_lo = @@ -719,8 +702,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) break; } - instance->instancet->fire_cmd(instance, req_desc.u.low, - req_desc.u.high, instance->reg_set); + megasas_fire_cmd_fusion(instance, &req_desc); wait_and_poll(instance, cmd, MFI_POLL_TIMEOUT_SECS); @@ -729,7 +711,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance) ret = 1; goto fail_fw_init; } - printk(KERN_ERR "megasas:IOC Init cmd success\n"); + dev_err(&instance->pdev->dev, "Init cmd success\n"); ret = 0; @@ -743,6 +725,83 @@ fail_get_cmd: return ret; } +/** + * megasas_sync_pd_seq_num - JBOD SEQ MAP + * @instance: Adapter soft state + * @pend: set to 1, if it is pended jbod map. + * + * Issue Jbod map to the firmware. If it is pended command, + * issue command and return. If it is first instance of jbod map + * issue and receive command. + */ +int +megasas_sync_pd_seq_num(struct megasas_instance *instance, bool pend) { + int ret = 0; + u32 pd_seq_map_sz; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct fusion_context *fusion = instance->ctrl_context; + struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync; + dma_addr_t pd_seq_h; + + pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id & 1)]; + pd_seq_h = fusion->pd_seq_phys[(instance->pd_seq_map_id & 1)]; + pd_seq_map_sz = sizeof(struct MR_PD_CFG_SEQ_NUM_SYNC) + + (sizeof(struct MR_PD_CFG_SEQ) * + (MAX_PHYSICAL_DEVICES - 1)); + + cmd = megasas_get_cmd(instance); + if (!cmd) { + dev_err(&instance->pdev->dev, + "Could not get mfi cmd. Fail from %s %d\n", + __func__, __LINE__); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + memset(pd_sync, 0, pd_seq_map_sz); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = cpu_to_le32(pd_seq_map_sz); + dcmd->opcode = cpu_to_le32(MR_DCMD_SYSTEM_PD_MAP_GET_INFO); + dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(pd_seq_h); + dcmd->sgl.sge32[0].length = cpu_to_le32(pd_seq_map_sz); + + if (pend) { + dcmd->mbox.b[0] = MEGASAS_DCMD_MBOX_PEND_FLAG; + dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_WRITE); + instance->jbod_seq_cmd = cmd; + instance->instancet->issue_dcmd(instance, cmd); + return 0; + } + + dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ); + + /* Below code is only for non pended DCMD */ + if (instance->ctrl_context && !instance->mask_interrupts) + ret = megasas_issue_blocked_cmd(instance, cmd, 60); + else + ret = megasas_issue_polled(instance, cmd); + + if (le32_to_cpu(pd_sync->count) > MAX_PHYSICAL_DEVICES) { + dev_warn(&instance->pdev->dev, + "driver supports max %d JBOD, but FW reports %d\n", + MAX_PHYSICAL_DEVICES, le32_to_cpu(pd_sync->count)); + ret = -EINVAL; + } + + if (!ret) + instance->pd_seq_map_id++; + + megasas_return_cmd(instance, cmd); + return ret; +} + /* * megasas_get_ld_map_info - Returns FW's ld_map structure * @instance: Adapter soft state @@ -772,7 +831,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: Failed to get cmd for map info.\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for map info\n"); return -ENOMEM; } @@ -791,7 +850,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance) ci_h = fusion->ld_map_phys[(instance->map_id & 1)]; if (!ci) { - printk(KERN_DEBUG "Failed to alloc mem for ld_map_info\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc mem for ld_map_info\n"); megasas_return_cmd(instance, cmd); return -ENOMEM; } @@ -820,11 +879,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance) else ret = megasas_issue_polled(instance, cmd); - if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked) - megasas_return_mfi_mpt_pthr(instance, cmd, - cmd->mpt_pthr_cmd_blocked); - else - megasas_return_cmd(instance, cmd); + megasas_return_cmd(instance, cmd); return ret; } @@ -870,8 +925,7 @@ megasas_sync_map_info(struct megasas_instance *instance) cmd = megasas_get_cmd(instance); if (!cmd) { - printk(KERN_DEBUG "megasas: Failed to get cmd for sync" - "info.\n"); + dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get cmd for sync info\n"); return -ENOMEM; } @@ -983,6 +1037,18 @@ megasas_display_intel_branding(struct megasas_instance *instance) break; } break; + case PCI_DEVICE_ID_LSI_CUTLASS_52: + case PCI_DEVICE_ID_LSI_CUTLASS_53: + switch (instance->pdev->subsystem_device) { + case MEGARAID_INTEL_RMS3BC160_SSDID: + dev_info(&instance->pdev->dev, "scsi host %d: %s\n", + instance->host->host_no, + MEGARAID_INTEL_RMS3BC160_BRANDING); + break; + default: + break; + } + break; default: break; } @@ -999,7 +1065,7 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) { struct megasas_register_set __iomem *reg_set; struct fusion_context *fusion; - u32 max_cmd; + u32 max_cmd, scratch_pad_2; int i = 0, count; fusion = instance->ctrl_context; @@ -1038,15 +1104,40 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE * (max_cmd + 1)); /* Extra 1 for SMID 0 */ + scratch_pad_2 = readl(&instance->reg_set->outbound_scratch_pad_2); + /* If scratch_pad_2 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK is set, + * Firmware support extended IO chain frame which is 4 times more than + * legacy Firmware. + * Legacy Firmware - Frame size is (8 * 128) = 1K + * 1M IO Firmware - Frame size is (8 * 128 * 4) = 4K + */ + if (scratch_pad_2 & MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK) + instance->max_chain_frame_sz = + ((scratch_pad_2 & MEGASAS_MAX_CHAIN_SIZE_MASK) >> + MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_1MB_IO; + else + instance->max_chain_frame_sz = + ((scratch_pad_2 & MEGASAS_MAX_CHAIN_SIZE_MASK) >> + MEGASAS_MAX_CHAIN_SHIFT) * MEGASAS_256K_IO; + + if (instance->max_chain_frame_sz < MEGASAS_CHAIN_FRAME_SZ_MIN) { + dev_warn(&instance->pdev->dev, "frame size %d invalid, fall back to legacy max frame size %d\n", + instance->max_chain_frame_sz, + MEGASAS_CHAIN_FRAME_SZ_MIN); + instance->max_chain_frame_sz = MEGASAS_CHAIN_FRAME_SZ_MIN; + } + fusion->max_sge_in_main_msg = - (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE - - offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16; + (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE + - offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16; fusion->max_sge_in_chain = - MEGASAS_MAX_SZ_CHAIN_FRAME / sizeof(union MPI2_SGE_IO_UNION); + instance->max_chain_frame_sz + / sizeof(union MPI2_SGE_IO_UNION); - instance->max_num_sge = rounddown_pow_of_two( - fusion->max_sge_in_main_msg + fusion->max_sge_in_chain - 2); + instance->max_num_sge = + rounddown_pow_of_two(fusion->max_sge_in_main_msg + + fusion->max_sge_in_chain - 2); /* Used for pass thru MFI frame (DCMD) */ fusion->chain_offset_mfi_pthru = @@ -1060,6 +1151,15 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) for (i = 0 ; i < count; i++) fusion->last_reply_idx[i] = 0; + /* + * For fusion adapters, 3 commands for IOCTL and 5 commands + * for driver's internal DCMDs. + */ + instance->max_scsi_cmds = instance->max_fw_cmds - + (MEGASAS_FUSION_INTERNAL_CMDS + + MEGASAS_FUSION_IOCTL_CMDS); + sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS); + /* * Allocate memory for descriptors * Create a pool of commands @@ -1107,7 +1207,7 @@ megasas_init_adapter_fusion(struct megasas_instance *instance) &fusion->ld_map_phys[i], GFP_KERNEL); if (!fusion->ld_map[i]) { - printk(KERN_ERR "megasas: Could not allocate memory " + dev_err(&instance->pdev->dev, "Could not allocate memory " "for map info\n"); goto fail_map_info; } @@ -1130,34 +1230,6 @@ fail_alloc_mfi_cmds: return 1; } -/** - * megasas_fire_cmd_fusion - Sends command to the FW - * @frame_phys_addr : Physical address of cmd - * @frame_count : Number of frames for the command - * @regs : MFI register set - */ -void -megasas_fire_cmd_fusion(struct megasas_instance *instance, - dma_addr_t req_desc_lo, - u32 req_desc_hi, - struct megasas_register_set __iomem *regs) -{ -#if defined(writeq) && defined(CONFIG_64BIT) - u64 req_data = (((u64)le32_to_cpu(req_desc_hi) << 32) | - le32_to_cpu(req_desc_lo)); - - writeq(req_data, &(regs)->inbound_low_queue_port); -#else - unsigned long flags; - - spin_lock_irqsave(&instance->hba_lock, flags); - - writel(le32_to_cpu(req_desc_lo), &(regs)->inbound_low_queue_port); - writel(le32_to_cpu(req_desc_hi), &(regs)->inbound_high_queue_port); - spin_unlock_irqrestore(&instance->hba_lock, flags); -#endif -} - /** * map_cmd_status - Maps FW cmd status to OS cmd status * @cmd : Pointer to cmd @@ -1200,7 +1272,7 @@ map_cmd_status(struct megasas_cmd_fusion *cmd, u8 status, u8 ext_status) cmd->scmd->result = DID_IMM_RETRY << 16; break; default: - printk(KERN_DEBUG "megasas: FW status %#x\n", status); + dev_printk(KERN_DEBUG, &cmd->instance->pdev->dev, "FW status %#x\n", status); cmd->scmd->result = DID_ERROR << 16; break; } @@ -1227,8 +1299,7 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, fusion = instance->ctrl_context; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) { struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end = sgl_ptr; sgl_ptr_end += fusion->max_sge_in_main_msg - 1; sgl_ptr_end->Flags = 0; @@ -1245,11 +1316,9 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, sgl_ptr->Length = cpu_to_le32(sg_dma_len(os_sgl)); sgl_ptr->Address = cpu_to_le64(sg_dma_address(os_sgl)); sgl_ptr->Flags = 0; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) if (i == sge_count - 1) sgl_ptr->Flags = IEEE_SGE_FLAGS_END_OF_LIST; - } sgl_ptr++; sg_processed = i + 1; @@ -1258,10 +1327,7 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, (sge_count > fusion->max_sge_in_main_msg)) { struct MPI25_IEEE_SGE_CHAIN64 *sg_chain; - if ((instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) { if ((le16_to_cpu(cmd->io_request->IoFlags) & MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) != MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) @@ -1277,10 +1343,7 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, sg_chain = sgl_ptr; /* Prepare chain element */ sg_chain->NextChainOffset = 0; - if ((instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) + if (fusion->adapter_type == INVADER_SERIES) sg_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT; else sg_chain->Flags = @@ -1291,7 +1354,7 @@ megasas_make_sgl_fusion(struct megasas_instance *instance, sgl_ptr = (struct MPI25_IEEE_SGE_CHAIN64 *)cmd->sg_frame; - memset(sgl_ptr, 0, MEGASAS_MAX_SZ_CHAIN_FRAME); + memset(sgl_ptr, 0, instance->max_chain_frame_sz); } } @@ -1497,7 +1560,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, struct MR_DRV_RAID_MAP_ALL *local_map_ptr; u8 *raidLUN; - device_id = MEGASAS_DEV_INDEX(instance, scp); + device_id = MEGASAS_DEV_INDEX(scp); fusion = instance->ctrl_context; @@ -1597,8 +1660,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) { if (io_request->RaidContext.regLockFlags == REGION_TYPE_UNUSED) cmd->request_desc->SCSIIO.RequestFlags = @@ -1621,6 +1683,14 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, cmd->pd_r1_lb = io_info.pd_after_lb; } else scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG; + + if ((raidLUN[0] == 1) && + (local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].validHandles > 1)) { + instance->dev_handle = !(instance->dev_handle); + io_info.devHandle = + local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].devHandle[instance->dev_handle]; + } + cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle; io_request->DevHandle = io_info.devHandle; /* populate the LUN field */ @@ -1631,8 +1701,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, cmd->request_desc->SCSIIO.RequestFlags = (MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) { if (io_request->RaidContext.regLockFlags == REGION_TYPE_UNUSED) cmd->request_desc->SCSIIO.RequestFlags = @@ -1650,121 +1719,68 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, } /** - * megasas_build_dcdb_fusion - Prepares IOs to devices + * megasas_build_ld_nonrw_fusion - prepares non rw ios for virtual disk * @instance: Adapter soft state * @scp: SCSI command * @cmd: Command to be prepared * - * Prepares the io_request frame for non-io cmds + * Prepares the io_request frame for non-rw io cmds for vd. */ -static void -megasas_build_dcdb_fusion(struct megasas_instance *instance, - struct scsi_cmnd *scmd, - struct megasas_cmd_fusion *cmd) +static void megasas_build_ld_nonrw_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd) { u32 device_id; struct MPI2_RAID_SCSI_IO_REQUEST *io_request; u16 pd_index = 0; - u16 os_timeout_value; - u16 timeout_limit; struct MR_DRV_RAID_MAP_ALL *local_map_ptr; struct fusion_context *fusion = instance->ctrl_context; u8 span, physArm; - u16 devHandle; + __le16 devHandle; u32 ld, arRef, pd; struct MR_LD_RAID *raid; struct RAID_CONTEXT *pRAID_Context; + u8 fp_possible = 1; io_request = cmd->io_request; - device_id = MEGASAS_DEV_INDEX(instance, scmd); - pd_index = (scmd->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) - +scmd->device->id; + device_id = MEGASAS_DEV_INDEX(scmd); + pd_index = MEGASAS_PD_INDEX(scmd); local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; - io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); + /* get RAID_Context pointer */ + pRAID_Context = &io_request->RaidContext; + /* Check with FW team */ + pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); + pRAID_Context->regLockRowLBA = 0; + pRAID_Context->regLockLength = 0; - if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS && - instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { - if (fusion->fast_path_io) - io_request->DevHandle = - local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; - io_request->RaidContext.RAIDFlags = - MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD - << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; - cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle; - cmd->request_desc->SCSIIO.MSIxIndex = - instance->msix_vectors ? - raw_smp_processor_id() % - instance->msix_vectors : - 0; - os_timeout_value = scmd->request->timeout / HZ; - - if (instance->secure_jbod_support && - (megasas_cmd_type(scmd) == NON_READ_WRITE_SYSPDIO)) { - /* system pd firmware path */ - io_request->Function = - MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; - cmd->request_desc->SCSIIO.RequestFlags = - (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - io_request->RaidContext.timeoutValue = - cpu_to_le16(os_timeout_value); - } else { - /* system pd Fast Path */ - io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; - io_request->RaidContext.regLockFlags = 0; - io_request->RaidContext.regLockRowLBA = 0; - io_request->RaidContext.regLockLength = 0; - timeout_limit = (scmd->device->type == TYPE_DISK) ? - 255 : 0xFFFF; - io_request->RaidContext.timeoutValue = - cpu_to_le16((os_timeout_value > timeout_limit) ? - timeout_limit : os_timeout_value); - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) - io_request->IoFlags |= - cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); - - cmd->request_desc->SCSIIO.RequestFlags = - (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); - } - } else { - if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS) - goto NonFastPath; - - /* - * For older firmware, Driver should not access ldTgtIdToLd - * beyond index 127 and for Extended VD firmware, ldTgtIdToLd - * should not go beyond 255. - */ - - if ((!fusion->fast_path_io) || - (device_id >= instance->fw_supported_vd_count)) - goto NonFastPath; + if (fusion->fast_path_io && ( + device_id < instance->fw_supported_vd_count)) { ld = MR_TargetIdToLdGet(device_id, local_map_ptr); - if (ld >= instance->fw_supported_vd_count) - goto NonFastPath; + fp_possible = 0; raid = MR_LdRaidGet(ld, local_map_ptr); - - /* check if this LD is FP capable */ if (!(raid->capability.fpNonRWCapable)) - /* not FP capable, send as non-FP */ - goto NonFastPath; + fp_possible = 0; + } else + fp_possible = 0; - /* get RAID_Context pointer */ - pRAID_Context = &io_request->RaidContext; + if (!fp_possible) { + io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; + io_request->DevHandle = cpu_to_le16(device_id); + io_request->LUN[1] = scmd->device->lun; + pRAID_Context->timeoutValue = + cpu_to_le16 (scmd->request->timeout / HZ); + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + } else { /* set RAID context values */ - pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ; - pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd); - pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); - pRAID_Context->regLockRowLBA = 0; - pRAID_Context->regLockLength = 0; - pRAID_Context->configSeqNum = raid->seqNum; + pRAID_Context->configSeqNum = raid->seqNum; + pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ; + pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd); /* get the DevHandle for the PD (since this is fpNonRWCapable, this is a single disk RAID0) */ @@ -1776,7 +1792,7 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, /* build request descriptor */ cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); cmd->request_desc->SCSIIO.DevHandle = devHandle; /* populate the LUN field */ @@ -1785,18 +1801,105 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, /* build the raidScsiIO structure */ io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; io_request->DevHandle = devHandle; + } +} - return; +/** + * megasas_build_syspd_fusion - prepares rw/non-rw ios for syspd + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to be prepared + * @fp_possible: parameter to detect fast path or firmware path io. + * + * Prepares the io_request frame for rw/non-rw io cmds for syspds + */ +static void +megasas_build_syspd_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd, u8 fp_possible) +{ + u32 device_id; + struct MPI2_RAID_SCSI_IO_REQUEST *io_request; + u16 pd_index = 0; + u16 os_timeout_value; + u16 timeout_limit; + struct MR_DRV_RAID_MAP_ALL *local_map_ptr; + struct RAID_CONTEXT *pRAID_Context; + struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync; + struct fusion_context *fusion = instance->ctrl_context; + pd_sync = (void *)fusion->pd_seq_sync[(instance->pd_seq_map_id - 1) & 1]; + + device_id = MEGASAS_DEV_INDEX(scmd); + pd_index = MEGASAS_PD_INDEX(scmd); + os_timeout_value = scmd->request->timeout / HZ; -NonFastPath: + io_request = cmd->io_request; + /* get RAID_Context pointer */ + pRAID_Context = &io_request->RaidContext; + pRAID_Context->regLockFlags = 0; + pRAID_Context->regLockRowLBA = 0; + pRAID_Context->regLockLength = 0; + io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); + io_request->LUN[1] = scmd->device->lun; + pRAID_Context->RAIDFlags = MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD + << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; + + /* If FW supports PD sequence number */ + if (instance->use_seqnum_jbod_fp && + instance->pd_list[pd_index].driveType == TYPE_DISK) { + /* TgtId must be incremented by 255 as jbod seq number is index + * below raid map + */ + pRAID_Context->VirtualDiskTgtId = + cpu_to_le16(device_id + (MAX_PHYSICAL_DEVICES - 1)); + pRAID_Context->configSeqNum = pd_sync->seq[pd_index].seqNum; + io_request->DevHandle = pd_sync->seq[pd_index].devHandle; + pRAID_Context->regLockFlags |= + (MR_RL_FLAGS_SEQ_NUM_ENABLE|MR_RL_FLAGS_GRANT_DESTINATION_CUDA); + } else if (fusion->fast_path_io) { + pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); + pRAID_Context->configSeqNum = 0; + local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; + io_request->DevHandle = + local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; + } else { + /* Want to send all IO via FW path */ + pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); + pRAID_Context->configSeqNum = 0; + io_request->DevHandle = cpu_to_le16(0xFFFF); + } + + cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle; + cmd->request_desc->SCSIIO.MSIxIndex = + instance->msix_vectors ? + (raw_smp_processor_id() % instance->msix_vectors) : 0; + + + if (!fp_possible) { + /* system pd firmware path */ io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; - io_request->DevHandle = cpu_to_le16(device_id); cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << - MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + pRAID_Context->timeoutValue = cpu_to_le16(os_timeout_value); + pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id); + } else { + /* system pd Fast Path */ + io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + timeout_limit = (scmd->device->type == TYPE_DISK) ? + 255 : 0xFFFF; + pRAID_Context->timeoutValue = + cpu_to_le16((os_timeout_value > timeout_limit) ? + timeout_limit : os_timeout_value); + if (fusion->adapter_type == INVADER_SERIES) { + pRAID_Context->Type = MPI2_TYPE_CUDA; + pRAID_Context->nseg = 0x1; + io_request->IoFlags |= + cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH); + } + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); } - io_request->RaidContext.VirtualDiskTgtId = cpu_to_le16(device_id); - int_to_scsilun(scmd->device->lun, (struct scsi_lun *)io_request->LUN); } /** @@ -1813,11 +1916,10 @@ megasas_build_io_fusion(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd_fusion *cmd) { - u32 device_id, sge_count; + u16 sge_count; + u8 cmd_type; struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; - device_id = MEGASAS_DEV_INDEX(instance, scp); - /* Zero out some fields so they don't get reused */ memset(io_request->LUN, 0x0, 8); io_request->CDB.EEDP32.PrimaryReferenceTag = 0; @@ -1837,10 +1939,24 @@ megasas_build_io_fusion(struct megasas_instance *instance, */ io_request->IoFlags = cpu_to_le16(scp->cmd_len); - if (megasas_cmd_type(scp) == READ_WRITE_LDIO) + switch (cmd_type = megasas_cmd_type(scp)) { + case READ_WRITE_LDIO: megasas_build_ldio_fusion(instance, scp, cmd); - else - megasas_build_dcdb_fusion(instance, scp, cmd); + break; + case NON_READ_WRITE_LDIO: + megasas_build_ld_nonrw_fusion(instance, scp, cmd); + break; + case READ_WRITE_SYSPDIO: + case NON_READ_WRITE_SYSPDIO: + if (instance->secure_jbod_support && + (cmd_type == NON_READ_WRITE_SYSPDIO)) + megasas_build_syspd_fusion(instance, scp, cmd, 0); + else + megasas_build_syspd_fusion(instance, scp, cmd, 1); + break; + default: + break; + } /* * Construct SGL @@ -1852,13 +1968,17 @@ megasas_build_io_fusion(struct megasas_instance *instance, &io_request->SGL, cmd); if (sge_count > instance->max_num_sge) { - printk(KERN_ERR "megasas: Error. sge_count (0x%x) exceeds " + dev_err(&instance->pdev->dev, "Error. sge_count (0x%x) exceeds " "max (0x%x) allowed\n", sge_count, instance->max_num_sge); return 1; } + /* numSGE store lower 8 bit of sge_count. + * numSGEExt store higher 8 bit of sge_count + */ io_request->RaidContext.numSGE = sge_count; + io_request->RaidContext.numSGEExt = (u8)(sge_count >> 8); io_request->SGLFlags = cpu_to_le16(MPI2_SGE_FLAGS_64_BIT_ADDRESSING); @@ -1886,7 +2006,7 @@ megasas_get_request_descriptor(struct megasas_instance *instance, u16 index) struct fusion_context *fusion; if (index >= instance->max_fw_cmds) { - printk(KERN_ERR "megasas: Invalid SMID (0x%x)request for " + dev_err(&instance->pdev->dev, "Invalid SMID (0x%x)request for " "descriptor for scsi%d\n", index, instance->host->host_no); return NULL; @@ -1915,9 +2035,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, fusion = instance->ctrl_context; - cmd = megasas_get_cmd_fusion(instance); - if (!cmd) - return SCSI_MLQUEUE_HOST_BUSY; + cmd = megasas_get_cmd_fusion(instance, scmd->request->tag); index = cmd->index; @@ -1930,7 +2048,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, if (megasas_build_io_fusion(instance, scmd, cmd)) { megasas_return_cmd_fusion(instance, cmd); - printk(KERN_ERR "megasas: Error building command.\n"); + dev_err(&instance->pdev->dev, "Error building command\n"); cmd->request_desc = NULL; return 1; } @@ -1940,7 +2058,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, if (cmd->io_request->ChainOffset != 0 && cmd->io_request->ChainOffset != 0xF) - printk(KERN_ERR "megasas: The chain offset value is not " + dev_err(&instance->pdev->dev, "The chain offset value is not " "correct : %x\n", cmd->io_request->ChainOffset); /* @@ -1948,9 +2066,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, */ atomic_inc(&instance->fw_outstanding); - instance->instancet->fire_cmd(instance, - req_desc->u.low, req_desc->u.high, - instance->reg_set); + megasas_fire_cmd_fusion(instance, req_desc); return 0; } @@ -1975,6 +2091,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) union desc_value d_val; struct LD_LOAD_BALANCE_INFO *lbinfo; int threshold_reply_count = 0; + struct scsi_cmnd *scmd_local = NULL; fusion = instance->ctrl_context; @@ -1998,7 +2115,8 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) num_completed = 0; - while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) { + while (d_val.u.low != cpu_to_le32(UINT_MAX) && + d_val.u.high != cpu_to_le32(UINT_MAX)) { smid = le16_to_cpu(reply_desc->SMID); cmd_fusion = fusion->cmd_list[smid - 1]; @@ -2010,14 +2128,14 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) if (cmd_fusion->scmd) cmd_fusion->scmd->SCp.ptr = NULL; + scmd_local = cmd_fusion->scmd; status = scsi_io_req->RaidContext.status; extStatus = scsi_io_req->RaidContext.exStatus; switch (scsi_io_req->Function) { case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/ /* Update load balancing info */ - device_id = MEGASAS_DEV_INDEX(instance, - cmd_fusion->scmd); + device_id = MEGASAS_DEV_INDEX(scmd_local); lbinfo = &fusion->load_balance_info[device_id]; if (cmd_fusion->scmd->SCp.Status & MEGASAS_LOAD_BALANCE_FLAG) { @@ -2028,36 +2146,32 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) { if (megasas_dbg_lvl == 5) - printk(KERN_ERR "\nmegasas: FAST Path " + dev_err(&instance->pdev->dev, "\nFAST Path " "IO Success\n"); } /* Fall thru and complete IO */ case MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST: /* LD-IO Path */ /* Map the FW Cmd Status */ map_cmd_status(cmd_fusion, status, extStatus); - scsi_dma_unmap(cmd_fusion->scmd); - cmd_fusion->scmd->scsi_done(cmd_fusion->scmd); scsi_io_req->RaidContext.status = 0; scsi_io_req->RaidContext.exStatus = 0; megasas_return_cmd_fusion(instance, cmd_fusion); + scsi_dma_unmap(scmd_local); + scmd_local->scsi_done(scmd_local); atomic_dec(&instance->fw_outstanding); break; case MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /*MFI command */ cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; - if (!cmd_mfi->mpt_pthr_cmd_blocked) { - if (megasas_dbg_lvl == 5) - dev_info(&instance->pdev->dev, - "freeing mfi/mpt pass-through " - "from %s %d\n", - __func__, __LINE__); - megasas_return_mfi_mpt_pthr(instance, cmd_mfi, - cmd_fusion); - } - - megasas_complete_cmd(instance, cmd_mfi, DID_OK); - cmd_fusion->flags = 0; + /* Poll mode. Dummy free. + * In case of Interrupt mode, caller has reverse check. + */ + if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) { + cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE; + megasas_return_cmd(instance, cmd_mfi); + } else + megasas_complete_cmd(instance, cmd_mfi, DID_OK); break; } @@ -2066,7 +2180,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) fusion->reply_q_depth) fusion->last_reply_idx[MSIxIndex] = 0; - desc->Words = ULLONG_MAX; + desc->Words = cpu_to_le64(ULLONG_MAX); num_completed++; threshold_reply_count++; @@ -2094,10 +2208,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) * pending to be completed */ if (threshold_reply_count >= THRESHOLD_REPLY_COUNT) { - if ((instance->pdev->device == - PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_FURY)) + if (fusion->adapter_type == INVADER_SERIES) writel(((MSIxIndex & 0x7) << 24) | fusion->last_reply_idx[MSIxIndex], instance->reply_post_host_index_addr[MSIxIndex/8]); @@ -2113,8 +2224,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) return IRQ_NONE; wmb(); - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) + if (fusion->adapter_type == INVADER_SERIES) writel(((MSIxIndex & 0x7) << 24) | fusion->last_reply_idx[MSIxIndex], instance->reply_post_host_index_addr[MSIxIndex/8]); @@ -2193,7 +2303,7 @@ irqreturn_t megasas_isr_fusion(int irq, void *devp) else if (fw_state == MFI_STATE_FAULT) schedule_work(&instance->work_init); } else if (fw_state == MFI_STATE_FAULT) { - printk(KERN_WARNING "megaraid_sas: Iop2SysDoorbellInt" + dev_warn(&instance->pdev->dev, "Iop2SysDoorbellInt" "for scsi%d\n", instance->host->host_no); schedule_work(&instance->work_init); } @@ -2217,27 +2327,14 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd; struct fusion_context *fusion; struct megasas_header *frame_hdr = &mfi_cmd->frame->hdr; - u32 opcode; - cmd = megasas_get_cmd_fusion(instance); - if (!cmd) - return 1; + fusion = instance->ctrl_context; + + cmd = megasas_get_cmd_fusion(instance, + instance->max_scsi_cmds + mfi_cmd->index); /* Save the smid. To be used for returning the cmd */ mfi_cmd->context.smid = cmd->index; - cmd->sync_cmd_idx = mfi_cmd->index; - - /* Set this only for Blocked commands */ - opcode = le32_to_cpu(mfi_cmd->frame->dcmd.opcode); - if ((opcode == MR_DCMD_LD_MAP_GET_INFO) - && (mfi_cmd->frame->dcmd.mbox.b[1] == 1)) - mfi_cmd->is_wait_event = 1; - - if (opcode == MR_DCMD_CTRL_EVENT_WAIT) - mfi_cmd->is_wait_event = 1; - - if (mfi_cmd->is_wait_event) - mfi_cmd->mpt_pthr_cmd_blocked = cmd; /* * For cmds where the flag is set, store the flag and check @@ -2246,13 +2343,11 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance, */ if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE)) - cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + mfi_cmd->flags |= DRV_DCMD_POLLED_MODE; - fusion = instance->ctrl_context; io_req = cmd->io_request; - if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) || - (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) { + if (fusion->adapter_type == INVADER_SERIES) { struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr_end = (struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL; sgl_ptr_end += fusion->max_sge_in_main_msg - 1; @@ -2272,7 +2367,7 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance, mpi25_ieee_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT | MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR; - mpi25_ieee_chain->Length = cpu_to_le32(MEGASAS_MAX_SZ_CHAIN_FRAME); + mpi25_ieee_chain->Length = cpu_to_le32(instance->max_chain_frame_sz); return 0; } @@ -2290,7 +2385,7 @@ build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) u16 index; if (build_mpt_mfi_pass_thru(instance, cmd)) { - printk(KERN_ERR "Couldn't build MFI pass thru cmd\n"); + dev_err(&instance->pdev->dev, "Couldn't build MFI pass thru cmd\n"); return NULL; } @@ -2324,17 +2419,15 @@ megasas_issue_dcmd_fusion(struct megasas_instance *instance, req_desc = build_mpt_cmd(instance, cmd); if (!req_desc) { - printk(KERN_ERR "Couldn't issue MFI pass thru cmd\n"); + dev_err(&instance->pdev->dev, "Couldn't issue MFI pass thru cmd\n"); return; } - atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_ATTACHED); - instance->instancet->fire_cmd(instance, req_desc->u.low, - req_desc->u.high, instance->reg_set); + megasas_fire_cmd_fusion(instance, req_desc); } /** * megasas_release_fusion - Reverses the FW initialization - * @intance: Adapter soft state + * @instance: Adapter soft state */ void megasas_release_fusion(struct megasas_instance *instance) @@ -2410,6 +2503,70 @@ static int megasas_adp_reset_fusion(struct megasas_instance *instance, struct megasas_register_set __iomem *regs) { + u32 host_diag, abs_state, retry; + + /* Now try to reset the chip */ + writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_1ST_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_2ND_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_3RD_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_4TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_5TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_6TH_KEY_VALUE, &instance->reg_set->fusion_seq_offset); + + /* Check that the diag write enable (DRWE) bit is on */ + host_diag = readl(&instance->reg_set->fusion_host_diag); + retry = 0; + while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) { + msleep(100); + host_diag = readl(&instance->reg_set->fusion_host_diag); + if (retry++ == 100) { + dev_warn(&instance->pdev->dev, + "Host diag unlock failed from %s %d\n", + __func__, __LINE__); + break; + } + } + if (!(host_diag & HOST_DIAG_WRITE_ENABLE)) + return -1; + + /* Send chip reset command */ + writel(host_diag | HOST_DIAG_RESET_ADAPTER, + &instance->reg_set->fusion_host_diag); + msleep(3000); + + /* Make sure reset adapter bit is cleared */ + host_diag = readl(&instance->reg_set->fusion_host_diag); + retry = 0; + while (host_diag & HOST_DIAG_RESET_ADAPTER) { + msleep(100); + host_diag = readl(&instance->reg_set->fusion_host_diag); + if (retry++ == 1000) { + dev_warn(&instance->pdev->dev, + "Diag reset adapter never cleared %s %d\n", + __func__, __LINE__); + break; + } + } + if (host_diag & HOST_DIAG_RESET_ADAPTER) + return -1; + + abs_state = instance->instancet->read_fw_status_reg(instance->reg_set) + & MFI_STATE_MASK; + retry = 0; + + while ((abs_state <= MFI_STATE_FW_INIT) && (retry++ < 1000)) { + msleep(100); + abs_state = instance->instancet-> + read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + } + if (abs_state <= MFI_STATE_FW_INIT) { + dev_warn(&instance->pdev->dev, + "fw state < MFI_STATE_FW_INIT, state = 0x%x %s %d\n", + abs_state, __func__, __LINE__); + return -1; + } + return 0; } @@ -2436,7 +2593,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, fw_state = instance->instancet->read_fw_status_reg( instance->reg_set) & MFI_STATE_MASK; if (fw_state == MFI_STATE_FAULT) { - printk(KERN_WARNING "megasas: Found FW in FAULT state," + dev_warn(&instance->pdev->dev, "Found FW in FAULT state," " will reset adapter scsi%d.\n", instance->host->host_no); retval = 1; @@ -2459,7 +2616,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, hb_seconds_missed++; if (hb_seconds_missed == (MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF/HZ)) { - printk(KERN_WARNING "megasas: SR-IOV:" + dev_warn(&instance->pdev->dev, "SR-IOV:" " Heartbeat never completed " " while polling during I/O " " timeout handling for " @@ -2477,7 +2634,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, goto out; if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: [%2d]waiting for %d " + dev_notice(&instance->pdev->dev, "[%2d]waiting for %d " "commands to complete for scsi%d\n", i, outstanding, instance->host->host_no); megasas_complete_cmd_dpc_fusion( @@ -2487,7 +2644,7 @@ int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance, } if (atomic_read(&instance->fw_outstanding)) { - printk("megaraid_sas: pending commands remain after waiting, " + dev_err(&instance->pdev->dev, "pending commands remain after waiting, " "will reset adapter scsi%d.\n", instance->host->host_no); retval = 1; @@ -2508,7 +2665,44 @@ void megasas_reset_reply_desc(struct megasas_instance *instance) fusion->last_reply_idx[i] = 0; reply_desc = fusion->reply_frames_desc; for (i = 0 ; i < fusion->reply_q_depth * count; i++, reply_desc++) - reply_desc->Words = ULLONG_MAX; + reply_desc->Words = cpu_to_le64(ULLONG_MAX); +} + +/* + * megasas_refire_mgmt_cmd : Re-fire management commands + * @instance: Controller's soft instance +*/ +void megasas_refire_mgmt_cmd(struct megasas_instance *instance) +{ + int j; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion; + struct megasas_cmd *cmd_mfi; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + u16 smid; + + fusion = instance->ctrl_context; + + /* Re-fire management commands. + * Do not traverse complet MPT frame pool. Start from max_scsi_cmds. + */ + for (j = instance->max_scsi_cmds ; j < instance->max_fw_cmds; j++) { + cmd_fusion = fusion->cmd_list[j]; + cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; + smid = le16_to_cpu(cmd_mfi->context.smid); + + if (!smid) + continue; + req_desc = megasas_get_request_descriptor + (instance, smid - 1); + if (req_desc && ((cmd_mfi->frame->dcmd.opcode != + cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)) && + (cmd_mfi->frame->dcmd.opcode != + cpu_to_le32(MR_DCMD_SYSTEM_PD_MAP_GET_INFO)))) + megasas_fire_cmd_fusion(instance, req_desc); + else + megasas_return_cmd(instance, cmd_mfi); + } } /* Check for a second path that is currently UP */ @@ -2538,14 +2732,13 @@ out: /* Core fusion reset function */ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) { - int retval = SUCCESS, i, j, retry = 0, convert = 0; + int retval = SUCCESS, i, convert = 0; struct megasas_instance *instance; struct megasas_cmd_fusion *cmd_fusion; struct fusion_context *fusion; - struct megasas_cmd *cmd_mfi; - union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; - u32 host_diag, abs_state, status_reg, reset_adapter; + u32 abs_state, status_reg, reset_adapter; u32 io_timeout_in_crash_mode = 0; + struct scsi_cmnd *scmd_local = NULL; instance = (struct megasas_instance *)shost->hostdata; fusion = instance->ctrl_context; @@ -2553,7 +2746,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) mutex_lock(&instance->reset_mutex); if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { - printk(KERN_WARNING "megaraid_sas: Hardware critical error, " + dev_warn(&instance->pdev->dev, "Hardware critical error, " "returning FAILED for scsi%d.\n", instance->host->host_no); mutex_unlock(&instance->reset_mutex); @@ -2607,21 +2800,22 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) if (megasas_wait_for_outstanding_fusion(instance, iotimeout, &convert)) { instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; - printk(KERN_WARNING "megaraid_sas: resetting fusion " + dev_warn(&instance->pdev->dev, "resetting fusion " "adapter scsi%d.\n", instance->host->host_no); if (convert) iotimeout = 0; /* Now return commands back to the OS */ - for (i = 0 ; i < instance->max_fw_cmds; i++) { + for (i = 0 ; i < instance->max_scsi_cmds; i++) { cmd_fusion = fusion->cmd_list[i]; + scmd_local = cmd_fusion->scmd; if (cmd_fusion->scmd) { - scsi_dma_unmap(cmd_fusion->scmd); - cmd_fusion->scmd->result = + scmd_local->result = megasas_check_mpio_paths(instance, - cmd_fusion->scmd); - cmd_fusion->scmd->scsi_done(cmd_fusion->scmd); + scmd_local); megasas_return_cmd_fusion(instance, cmd_fusion); + scsi_dma_unmap(scmd_local); + scmd_local->scsi_done(scmd_local); atomic_dec(&instance->fw_outstanding); } } @@ -2633,7 +2827,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) if (instance->disableOnlineCtrlReset || (abs_state == MFI_STATE_FAULT && !reset_adapter)) { /* Reset not supported, kill adapter */ - printk(KERN_WARNING "megaraid_sas: Reset not supported" + dev_warn(&instance->pdev->dev, "Reset not supported" ", killing adapter scsi%d.\n", instance->host->host_no); megaraid_sas_kill_hba(instance); @@ -2651,7 +2845,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) instance->hb_host_mem->HB.driverCounter)) { instance->hb_host_mem->HB.driverCounter = instance->hb_host_mem->HB.fwCounter; - printk(KERN_WARNING "megasas: SR-IOV:" + dev_warn(&instance->pdev->dev, "SR-IOV:" "Late FW heartbeat update for " "scsi%d.\n", instance->host->host_no); @@ -2667,8 +2861,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) abs_state = status_reg & MFI_STATE_MASK; if (abs_state == MFI_STATE_READY) { - printk(KERN_WARNING "megasas" - ": SR-IOV: FW was found" + dev_warn(&instance->pdev->dev, + "SR-IOV: FW was found" "to be in ready state " "for scsi%d.\n", instance->host->host_no); @@ -2677,7 +2871,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) msleep(20); } if (abs_state != MFI_STATE_READY) { - printk(KERN_WARNING "megasas: SR-IOV: " + dev_warn(&instance->pdev->dev, "SR-IOV: " "FW not in ready state after %d" " seconds for scsi%d, status_reg = " "0x%x.\n", @@ -2696,85 +2890,14 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) /* Now try to reset the chip */ for (i = 0; i < MEGASAS_FUSION_MAX_RESET_TRIES; i++) { - writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_1ST_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_2ND_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_3RD_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_4TH_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_5TH_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - writel(MPI2_WRSEQ_6TH_KEY_VALUE, - &instance->reg_set->fusion_seq_offset); - - /* Check that the diag write enable (DRWE) bit is on */ - host_diag = readl(&instance->reg_set->fusion_host_diag); - retry = 0; - while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) { - msleep(100); - host_diag = - readl(&instance->reg_set->fusion_host_diag); - if (retry++ == 100) { - printk(KERN_WARNING "megaraid_sas: " - "Host diag unlock failed! " - "for scsi%d\n", - instance->host->host_no); - break; - } - } - if (!(host_diag & HOST_DIAG_WRITE_ENABLE)) - continue; - /* Send chip reset command */ - writel(host_diag | HOST_DIAG_RESET_ADAPTER, - &instance->reg_set->fusion_host_diag); - msleep(3000); - - /* Make sure reset adapter bit is cleared */ - host_diag = readl(&instance->reg_set->fusion_host_diag); - retry = 0; - while (host_diag & HOST_DIAG_RESET_ADAPTER) { - msleep(100); - host_diag = - readl(&instance->reg_set->fusion_host_diag); - if (retry++ == 1000) { - printk(KERN_WARNING "megaraid_sas: " - "Diag reset adapter never " - "cleared for scsi%d!\n", - instance->host->host_no); - break; - } - } - if (host_diag & HOST_DIAG_RESET_ADAPTER) + if (instance->instancet->adp_reset + (instance, instance->reg_set)) continue; - abs_state = - instance->instancet->read_fw_status_reg( - instance->reg_set) & MFI_STATE_MASK; - retry = 0; - - while ((abs_state <= MFI_STATE_FW_INIT) && - (retry++ < 1000)) { - msleep(100); - abs_state = - instance->instancet->read_fw_status_reg( - instance->reg_set) & MFI_STATE_MASK; - } - if (abs_state <= MFI_STATE_FW_INIT) { - printk(KERN_WARNING "megaraid_sas: firmware " - "state < MFI_STATE_FW_INIT, state = " - "0x%x for scsi%d\n", abs_state, - instance->host->host_no); - continue; - } - /* Wait for FW to become ready */ if (megasas_transition_to_ready(instance, 1)) { - printk(KERN_WARNING "megaraid_sas: Failed to " + dev_warn(&instance->pdev->dev, "Failed to " "transition controller to ready " "for scsi%d.\n", instance->host->host_no); @@ -2783,51 +2906,14 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) megasas_reset_reply_desc(instance); if (megasas_ioc_init_fusion(instance)) { - printk(KERN_WARNING "megaraid_sas: " + dev_warn(&instance->pdev->dev, "megasas_ioc_init_fusion() failed!" " for scsi%d\n", instance->host->host_no); continue; } - /* Re-fire management commands */ - for (j = 0 ; j < instance->max_fw_cmds; j++) { - cmd_fusion = fusion->cmd_list[j]; - if (cmd_fusion->sync_cmd_idx != - (u32)ULONG_MAX) { - cmd_mfi = - instance-> - cmd_list[cmd_fusion->sync_cmd_idx]; - if (cmd_mfi->frame->dcmd.opcode == - cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)) { - megasas_return_mfi_mpt_pthr(instance, cmd_mfi, cmd_fusion); - } else { - req_desc = - megasas_get_request_descriptor( - instance, - cmd_mfi->context.smid - -1); - if (!req_desc) { - printk(KERN_WARNING - "req_desc NULL" - " for scsi%d\n", - instance->host->host_no); - /* Return leaked MPT - frame */ - megasas_return_cmd_fusion(instance, cmd_fusion); - } else { - instance->instancet-> - fire_cmd(instance, - req_desc-> - u.low, - req_desc-> - u.high, - instance-> - reg_set); - } - } - } - } + megasas_refire_mgmt_cmd(instance); if (megasas_get_ctrl_info(instance)) { dev_info(&instance->pdev->dev, @@ -2844,6 +2930,8 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) if (!megasas_get_map_info(instance)) megasas_sync_map_info(instance); + megasas_setup_jbod_map(instance); + clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); instance->instancet->enable_intr(instance); @@ -2861,7 +2949,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) } /* Adapter reset completed successfully */ - printk(KERN_WARNING "megaraid_sas: Reset " + dev_warn(&instance->pdev->dev, "Reset " "successful for scsi%d.\n", instance->host->host_no); @@ -2877,7 +2965,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout) goto out; } /* Reset failed, kill the adapter */ - printk(KERN_WARNING "megaraid_sas: Reset failed, killing " + dev_warn(&instance->pdev->dev, "Reset failed, killing " "adapter scsi%d.\n", instance->host->host_no); megaraid_sas_kill_hba(instance); instance->skip_heartbeat_timer_del = 1; @@ -2978,7 +3066,6 @@ void megasas_fusion_ocr_wq(struct work_struct *work) } struct megasas_instance_template megasas_instance_template_fusion = { - .fire_cmd = megasas_fire_cmd_fusion, .enable_intr = megasas_enable_intr_fusion, .disable_intr = megasas_disable_intr_fusion, .clear_intr = megasas_clear_intr_fusion, diff --git a/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.h b/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.h index 56e6db2d5..473005c99 100644 --- a/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/kernel/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -35,8 +35,13 @@ #define _MEGARAID_SAS_FUSION_H_ /* Fusion defines */ -#define MEGASAS_MAX_SZ_CHAIN_FRAME 1024 +#define MEGASAS_CHAIN_FRAME_SZ_MIN 1024 #define MFI_FUSION_ENABLE_INTERRUPT_MASK (0x00000009) +#define MEGASAS_MAX_CHAIN_SHIFT 5 +#define MEGASAS_MAX_CHAIN_SIZE_UNITS_MASK 0x400000 +#define MEGASAS_MAX_CHAIN_SIZE_MASK 0x3E0 +#define MEGASAS_256K_IO 128 +#define MEGASAS_1MB_IO (MEGASAS_256K_IO * 4) #define MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE 256 #define MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST 0xF0 #define MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST 0xF1 @@ -89,6 +94,12 @@ enum MR_RAID_FLAGS_IO_SUB_TYPE { #define MEGASAS_FP_CMD_LEN 16 #define MEGASAS_FUSION_IN_RESET 0 #define THRESHOLD_REPLY_COUNT 50 +#define JBOD_MAPS_COUNT 2 + +enum MR_FUSION_ADAPTER_TYPE { + THUNDERBOLT_SERIES = 0, + INVADER_SERIES = 1, +}; /* * Raid Context structure which describes MegaRAID specific IO Parameters @@ -104,20 +115,22 @@ struct RAID_CONTEXT { u8 nseg:4; #endif u8 resvd0; - u16 timeoutValue; + __le16 timeoutValue; u8 regLockFlags; u8 resvd1; - u16 VirtualDiskTgtId; - u64 regLockRowLBA; - u32 regLockLength; - u16 nextLMId; + __le16 VirtualDiskTgtId; + __le64 regLockRowLBA; + __le32 regLockLength; + __le16 nextLMId; u8 exStatus; u8 status; u8 RAIDFlags; u8 numSGE; - u16 configSeqNum; + __le16 configSeqNum; u8 spanArm; - u8 resvd2[3]; + u8 priority; + u8 numSGEExt; + u8 resvd2; }; #define RAID_CTX_SPANARM_ARM_SHIFT (0) @@ -182,61 +195,61 @@ enum REGION_TYPE { #define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) struct MPI25_IEEE_SGE_CHAIN64 { - u64 Address; - u32 Length; - u16 Reserved1; + __le64 Address; + __le32 Length; + __le16 Reserved1; u8 NextChainOffset; u8 Flags; }; struct MPI2_SGE_SIMPLE_UNION { - u32 FlagsLength; + __le32 FlagsLength; union { - u32 Address32; - u64 Address64; + __le32 Address32; + __le64 Address64; } u; }; struct MPI2_SCSI_IO_CDB_EEDP32 { u8 CDB[20]; /* 0x00 */ - u32 PrimaryReferenceTag; /* 0x14 */ - u16 PrimaryApplicationTag; /* 0x18 */ - u16 PrimaryApplicationTagMask; /* 0x1A */ - u32 TransferLength; /* 0x1C */ + __be32 PrimaryReferenceTag; /* 0x14 */ + __be16 PrimaryApplicationTag; /* 0x18 */ + __be16 PrimaryApplicationTagMask; /* 0x1A */ + __le32 TransferLength; /* 0x1C */ }; struct MPI2_SGE_CHAIN_UNION { - u16 Length; + __le16 Length; u8 NextChainOffset; u8 Flags; union { - u32 Address32; - u64 Address64; + __le32 Address32; + __le64 Address64; } u; }; struct MPI2_IEEE_SGE_SIMPLE32 { - u32 Address; - u32 FlagsLength; + __le32 Address; + __le32 FlagsLength; }; struct MPI2_IEEE_SGE_CHAIN32 { - u32 Address; - u32 FlagsLength; + __le32 Address; + __le32 FlagsLength; }; struct MPI2_IEEE_SGE_SIMPLE64 { - u64 Address; - u32 Length; - u16 Reserved1; + __le64 Address; + __le32 Length; + __le16 Reserved1; u8 Reserved2; u8 Flags; }; struct MPI2_IEEE_SGE_CHAIN64 { - u64 Address; - u32 Length; - u16 Reserved1; + __le64 Address; + __le32 Length; + __le16 Reserved1; u8 Reserved2; u8 Flags; }; @@ -269,34 +282,34 @@ union MPI2_SCSI_IO_CDB_UNION { * Total SGE count will be one less than _MPI2_SCSI_IO_REQUEST */ struct MPI2_RAID_SCSI_IO_REQUEST { - u16 DevHandle; /* 0x00 */ + __le16 DevHandle; /* 0x00 */ u8 ChainOffset; /* 0x02 */ u8 Function; /* 0x03 */ - u16 Reserved1; /* 0x04 */ + __le16 Reserved1; /* 0x04 */ u8 Reserved2; /* 0x06 */ u8 MsgFlags; /* 0x07 */ u8 VP_ID; /* 0x08 */ u8 VF_ID; /* 0x09 */ - u16 Reserved3; /* 0x0A */ - u32 SenseBufferLowAddress; /* 0x0C */ - u16 SGLFlags; /* 0x10 */ + __le16 Reserved3; /* 0x0A */ + __le32 SenseBufferLowAddress; /* 0x0C */ + __le16 SGLFlags; /* 0x10 */ u8 SenseBufferLength; /* 0x12 */ u8 Reserved4; /* 0x13 */ u8 SGLOffset0; /* 0x14 */ u8 SGLOffset1; /* 0x15 */ u8 SGLOffset2; /* 0x16 */ u8 SGLOffset3; /* 0x17 */ - u32 SkipCount; /* 0x18 */ - u32 DataLength; /* 0x1C */ - u32 BidirectionalDataLength; /* 0x20 */ - u16 IoFlags; /* 0x24 */ - u16 EEDPFlags; /* 0x26 */ - u32 EEDPBlockSize; /* 0x28 */ - u32 SecondaryReferenceTag; /* 0x2C */ - u16 SecondaryApplicationTag; /* 0x30 */ - u16 ApplicationTagTranslationMask; /* 0x32 */ + __le32 SkipCount; /* 0x18 */ + __le32 DataLength; /* 0x1C */ + __le32 BidirectionalDataLength; /* 0x20 */ + __le16 IoFlags; /* 0x24 */ + __le16 EEDPFlags; /* 0x26 */ + __le32 EEDPBlockSize; /* 0x28 */ + __le32 SecondaryReferenceTag; /* 0x2C */ + __le16 SecondaryApplicationTag; /* 0x30 */ + __le16 ApplicationTagTranslationMask; /* 0x32 */ u8 LUN[8]; /* 0x34 */ - u32 Control; /* 0x3C */ + __le32 Control; /* 0x3C */ union MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ struct RAID_CONTEXT RaidContext; /* 0x60 */ union MPI2_SGE_IO_UNION SGL; /* 0x80 */ @@ -315,45 +328,45 @@ struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR { struct MPI2_DEFAULT_REQUEST_DESCRIPTOR { u8 RequestFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 LMID; /* 0x04 */ - u16 DescriptorTypeDependent; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 LMID; /* 0x04 */ + __le16 DescriptorTypeDependent; /* 0x06 */ }; /* High Priority Request Descriptor */ struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { u8 RequestFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 LMID; /* 0x04 */ - u16 Reserved1; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 LMID; /* 0x04 */ + __le16 Reserved1; /* 0x06 */ }; /* SCSI IO Request Descriptor */ struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR { u8 RequestFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 LMID; /* 0x04 */ - u16 DevHandle; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 LMID; /* 0x04 */ + __le16 DevHandle; /* 0x06 */ }; /* SCSI Target Request Descriptor */ struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { u8 RequestFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 LMID; /* 0x04 */ - u16 IoIndex; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 LMID; /* 0x04 */ + __le16 IoIndex; /* 0x06 */ }; /* RAID Accelerator Request Descriptor */ struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { u8 RequestFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 LMID; /* 0x04 */ - u16 Reserved; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 LMID; /* 0x04 */ + __le16 Reserved; /* 0x06 */ }; /* union of Request Descriptors */ @@ -366,10 +379,10 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION { struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR MFAIo; union { struct { - u32 low; - u32 high; + __le32 low; + __le32 high; } u; - u64 Words; + __le64 Words; }; }; @@ -377,35 +390,35 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION { struct MPI2_DEFAULT_REPLY_DESCRIPTOR { u8 ReplyFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 DescriptorTypeDependent1; /* 0x02 */ - u32 DescriptorTypeDependent2; /* 0x04 */ + __le16 DescriptorTypeDependent1; /* 0x02 */ + __le32 DescriptorTypeDependent2; /* 0x04 */ }; /* Address Reply Descriptor */ struct MPI2_ADDRESS_REPLY_DESCRIPTOR { u8 ReplyFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u32 ReplyFrameAddress; /* 0x04 */ + __le16 SMID; /* 0x02 */ + __le32 ReplyFrameAddress; /* 0x04 */ }; /* SCSI IO Success Reply Descriptor */ struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { u8 ReplyFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u16 TaskTag; /* 0x04 */ - u16 Reserved1; /* 0x06 */ + __le16 SMID; /* 0x02 */ + __le16 TaskTag; /* 0x04 */ + __le16 Reserved1; /* 0x06 */ }; /* TargetAssist Success Reply Descriptor */ struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { u8 ReplyFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ + __le16 SMID; /* 0x02 */ u8 SequenceNumber; /* 0x04 */ u8 Reserved1; /* 0x05 */ - u16 IoIndex; /* 0x06 */ + __le16 IoIndex; /* 0x06 */ }; /* Target Command Buffer Reply Descriptor */ @@ -414,16 +427,16 @@ struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { u8 MSIxIndex; /* 0x01 */ u8 VP_ID; /* 0x02 */ u8 Flags; /* 0x03 */ - u16 InitiatorDevHandle; /* 0x04 */ - u16 IoIndex; /* 0x06 */ + __le16 InitiatorDevHandle; /* 0x04 */ + __le16 IoIndex; /* 0x06 */ }; /* RAID Accelerator Success Reply Descriptor */ struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { u8 ReplyFlags; /* 0x00 */ u8 MSIxIndex; /* 0x01 */ - u16 SMID; /* 0x02 */ - u32 Reserved; /* 0x04 */ + __le16 SMID; /* 0x02 */ + __le32 Reserved; /* 0x04 */ }; /* union of Reply Descriptors */ @@ -435,7 +448,7 @@ union MPI2_REPLY_DESCRIPTORS_UNION { struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; - u64 Words; + __le64 Words; }; /* IOCInit Request message */ @@ -444,28 +457,28 @@ struct MPI2_IOC_INIT_REQUEST { u8 Reserved1; /* 0x01 */ u8 ChainOffset; /* 0x02 */ u8 Function; /* 0x03 */ - u16 Reserved2; /* 0x04 */ + __le16 Reserved2; /* 0x04 */ u8 Reserved3; /* 0x06 */ u8 MsgFlags; /* 0x07 */ u8 VP_ID; /* 0x08 */ u8 VF_ID; /* 0x09 */ - u16 Reserved4; /* 0x0A */ - u16 MsgVersion; /* 0x0C */ - u16 HeaderVersion; /* 0x0E */ + __le16 Reserved4; /* 0x0A */ + __le16 MsgVersion; /* 0x0C */ + __le16 HeaderVersion; /* 0x0E */ u32 Reserved5; /* 0x10 */ - u16 Reserved6; /* 0x14 */ + __le16 Reserved6; /* 0x14 */ u8 Reserved7; /* 0x16 */ u8 HostMSIxVectors; /* 0x17 */ - u16 Reserved8; /* 0x18 */ - u16 SystemRequestFrameSize; /* 0x1A */ - u16 ReplyDescriptorPostQueueDepth; /* 0x1C */ - u16 ReplyFreeQueueDepth; /* 0x1E */ - u32 SenseBufferAddressHigh; /* 0x20 */ - u32 SystemReplyAddressHigh; /* 0x24 */ - u64 SystemRequestFrameBaseAddress; /* 0x28 */ - u64 ReplyDescriptorPostQueueAddress;/* 0x30 */ - u64 ReplyFreeQueueAddress; /* 0x38 */ - u64 TimeStamp; /* 0x40 */ + __le16 Reserved8; /* 0x18 */ + __le16 SystemRequestFrameSize; /* 0x1A */ + __le16 ReplyDescriptorPostQueueDepth; /* 0x1C */ + __le16 ReplyFreeQueueDepth; /* 0x1E */ + __le32 SenseBufferAddressHigh; /* 0x20 */ + __le32 SystemReplyAddressHigh; /* 0x24 */ + __le64 SystemRequestFrameBaseAddress; /* 0x28 */ + __le64 ReplyDescriptorPostQueueAddress;/* 0x30 */ + __le64 ReplyFreeQueueAddress; /* 0x38 */ + __le64 TimeStamp; /* 0x40 */ }; /* mrpriv defines */ @@ -486,46 +499,47 @@ struct MPI2_IOC_INIT_REQUEST { #define MAX_PHYSICAL_DEVICES 256 #define MAX_RAIDMAP_PHYSICAL_DEVICES (MAX_PHYSICAL_DEVICES) #define MR_DCMD_LD_MAP_GET_INFO 0x0300e101 +#define MR_DCMD_SYSTEM_PD_MAP_GET_INFO 0x0200e102 #define MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC 0x010e8485 /* SR-IOV HB alloc*/ #define MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111 0x03200200 #define MR_DCMD_LD_VF_MAP_GET_ALL_LDS 0x03150200 struct MR_DEV_HANDLE_INFO { - u16 curDevHdl; + __le16 curDevHdl; u8 validHandles; u8 reserved; - u16 devHandle[2]; + __le16 devHandle[2]; }; struct MR_ARRAY_INFO { - u16 pd[MAX_RAIDMAP_ROW_SIZE]; + __le16 pd[MAX_RAIDMAP_ROW_SIZE]; }; struct MR_QUAD_ELEMENT { - u64 logStart; - u64 logEnd; - u64 offsetInSpan; - u32 diff; - u32 reserved1; + __le64 logStart; + __le64 logEnd; + __le64 offsetInSpan; + __le32 diff; + __le32 reserved1; }; struct MR_SPAN_INFO { - u32 noElements; - u32 reserved1; + __le32 noElements; + __le32 reserved1; struct MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH]; }; struct MR_LD_SPAN { - u64 startBlk; - u64 numBlks; - u16 arrayRef; + __le64 startBlk; + __le64 numBlks; + __le16 arrayRef; u8 spanRowSize; u8 spanRowDataSize; u8 reserved[4]; }; struct MR_SPAN_BLOCK_INFO { - u64 num_rows; + __le64 num_rows; struct MR_LD_SPAN span; struct MR_SPAN_INFO block_span_info; }; @@ -558,8 +572,8 @@ struct MR_LD_RAID { u32 reserved4:7; #endif } capability; - u32 reserved6; - u64 size; + __le32 reserved6; + __le64 size; u8 spanDepth; u8 level; u8 stripeShift; @@ -568,12 +582,12 @@ struct MR_LD_RAID { u8 writeMode; u8 PRL; u8 SRL; - u16 targetId; + __le16 targetId; u8 ldState; u8 regTypeReqOnWrite; u8 modFactor; u8 regTypeReqOnRead; - u16 seqNum; + __le16 seqNum; struct { u32 ldSyncRequired:1; @@ -592,20 +606,20 @@ struct MR_LD_SPAN_MAP { }; struct MR_FW_RAID_MAP { - u32 totalSize; + __le32 totalSize; union { struct { - u32 maxLd; - u32 maxSpanDepth; - u32 maxRowSize; - u32 maxPdCount; - u32 maxArrays; + __le32 maxLd; + __le32 maxSpanDepth; + __le32 maxRowSize; + __le32 maxPdCount; + __le32 maxArrays; } validationInfo; - u32 version[5]; + __le32 version[5]; }; - u32 ldCount; - u32 Reserved1; + __le32 ldCount; + __le32 Reserved1; u8 ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+ MAX_RAIDMAP_VIEWS]; u8 fpPdIoTimeoutSec; @@ -620,7 +634,7 @@ struct IO_REQUEST_INFO { u32 numBlocks; u16 ldTgtId; u8 isRead; - u16 devHandle; + __le16 devHandle; u64 pdBlock; u8 fpOkForIo; u8 IoforUnevenSpan; @@ -634,7 +648,7 @@ struct IO_REQUEST_INFO { struct MR_LD_TARGET_SYNC { u8 targetId; u8 reserved; - u16 seqNum; + __le16 seqNum; }; #define IEEE_SGE_FLAGS_ADDR_MASK (0x03) @@ -679,7 +693,6 @@ struct megasas_cmd_fusion { */ u32 sync_cmd_idx; u32 index; - u8 flags; u8 pd_r1_lb; }; @@ -720,27 +733,27 @@ struct MR_DRV_RAID_MAP { * This feild will be manupulated by driver for ext raid map, * else pick the value from firmware raid map. */ - u32 totalSize; + __le32 totalSize; union { struct { - u32 maxLd; - u32 maxSpanDepth; - u32 maxRowSize; - u32 maxPdCount; - u32 maxArrays; + __le32 maxLd; + __le32 maxSpanDepth; + __le32 maxRowSize; + __le32 maxPdCount; + __le32 maxArrays; } validationInfo; - u32 version[5]; + __le32 version[5]; }; /* timeout value used by driver in FP IOs*/ u8 fpPdIoTimeoutSec; u8 reserved2[7]; - u16 ldCount; - u16 arCount; - u16 spanCount; - u16 reserve3; + __le16 ldCount; + __le16 arCount; + __le16 spanCount; + __le16 reserve3; struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES]; u8 ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT]; @@ -779,10 +792,10 @@ struct MR_FW_RAID_MAP_EXT { u8 fpPdIoTimeoutSec; u8 reserved2[7]; - u16 ldCount; - u16 arCount; - u16 spanCount; - u16 reserve3; + __le16 ldCount; + __le16 arCount; + __le16 spanCount; + __le16 reserve3; struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES]; u8 ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT]; @@ -790,12 +803,23 @@ struct MR_FW_RAID_MAP_EXT { struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES_EXT]; }; -struct fusion_context { - struct megasas_cmd_fusion **cmd_list; - struct list_head cmd_pool; +/* + * * define MR_PD_CFG_SEQ structure for system PDs + * */ +struct MR_PD_CFG_SEQ { + __le16 seqNum; + __le16 devHandle; + u8 reserved[4]; +} __packed; - spinlock_t mpt_pool_lock; +struct MR_PD_CFG_SEQ_NUM_SYNC { + __le32 size; + __le32 count; + struct MR_PD_CFG_SEQ seq[1]; +} __packed; +struct fusion_context { + struct megasas_cmd_fusion **cmd_list; dma_addr_t req_frames_desc_phys; u8 *req_frames_desc; @@ -833,16 +857,19 @@ struct fusion_context { u32 current_map_sz; u32 drv_map_sz; u32 drv_map_pages; + struct MR_PD_CFG_SEQ_NUM_SYNC *pd_seq_sync[JBOD_MAPS_COUNT]; + dma_addr_t pd_seq_phys[JBOD_MAPS_COUNT]; u8 fast_path_io; struct LD_LOAD_BALANCE_INFO load_balance_info[MAX_LOGICAL_DRIVES_EXT]; LD_SPAN_INFO log_to_span[MAX_LOGICAL_DRIVES_EXT]; + u8 adapter_type; }; union desc_value { - u64 word; + __le64 word; struct { - u32 low; - u32 high; + __le32 low; + __le32 high; } u; }; diff --git a/kernel/drivers/scsi/mpt2sas/Kconfig b/kernel/drivers/scsi/mpt2sas/Kconfig deleted file mode 100644 index 657b45ca0..000000000 --- a/kernel/drivers/scsi/mpt2sas/Kconfig +++ /dev/null @@ -1,67 +0,0 @@ -# -# Kernel configuration file for the MPT2SAS -# -# This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2014 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; either version 2 -# of the License, or (at your option) any later version. - -# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. - -config SCSI_MPT2SAS - tristate "LSI MPT Fusion SAS 2.0 Device Driver" - depends on PCI && SCSI - select SCSI_SAS_ATTRS - select RAID_ATTRS - ---help--- - This driver supports PCI-Express SAS 6Gb/s Host Adapters. - -config SCSI_MPT2SAS_MAX_SGE - int "LSI MPT Fusion Max number of SG Entries (16 - 128)" - depends on PCI && SCSI && SCSI_MPT2SAS - default "128" - range 16 128 - ---help--- - This option allows you to specify the maximum number of scatter- - gather entries per I/O. The driver default is 128, which matches - SAFE_PHYS_SEGMENTS. However, it may decreased down to 16. - Decreasing this parameter will reduce memory requirements - on a per controller instance. - -config SCSI_MPT2SAS_LOGGING - bool "LSI MPT Fusion logging facility" - depends on PCI && SCSI && SCSI_MPT2SAS - ---help--- - This turns on a logging facility. diff --git a/kernel/drivers/scsi/mpt2sas/Makefile b/kernel/drivers/scsi/mpt2sas/Makefile deleted file mode 100644 index 728f04757..000000000 --- a/kernel/drivers/scsi/mpt2sas/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# mpt2sas makefile -obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o -mpt2sas-y += mpt2sas_base.o \ - mpt2sas_config.o \ - mpt2sas_scsih.o \ - mpt2sas_transport.o \ - mpt2sas_ctl.o diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2.h deleted file mode 100644 index 7fc6f23bd..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2.h +++ /dev/null @@ -1,1170 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2.h - * Title: MPI Message independent structures and definitions - * including System Interface Register Set and - * scatter/gather formats. - * Creation Date: June 21, 2006 - * - * mpi2.h Version: 02.00.35 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. - * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. - * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. - * Moved ReplyPostHostIndex register to offset 0x6C of the - * MPI2_SYSTEM_INTERFACE_REGS and modified the define for - * MPI2_REPLY_POST_HOST_INDEX_OFFSET. - * Added union of request descriptors. - * Added union of reply descriptors. - * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. - * Added define for MPI2_VERSION_02_00. - * Fixed the size of the FunctionDependent5 field in the - * MPI2_DEFAULT_REPLY structure. - * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. - * Removed the MPI-defined Fault Codes and extended the - * product specific codes up to 0xEFFF. - * Added a sixth key value for the WriteSequence register - * and changed the flush value to 0x0. - * Added message function codes for Diagnostic Buffer Post - * and Diagnsotic Release. - * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED - * Moved MPI2_VERSION_UNION from mpi2_ioc.h. - * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. - * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. - * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. - * Added #defines for marking a reply descriptor as unused. - * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. - * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. - * Moved LUN field defines from mpi2_init.h. - * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. - * 05-06-09 02.00.12 Bumped MPI2_HEADER_VERSION_UNIT. - * In all request and reply descriptors, replaced VF_ID - * field with MSIxIndex field. - * Removed DevHandle field from - * MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those - * bytes reserved. - * Added RAID Accelerator functionality. - * 07-30-09 02.00.13 Bumped MPI2_HEADER_VERSION_UNIT. - * 10-28-09 02.00.14 Bumped MPI2_HEADER_VERSION_UNIT. - * Added MSI-x index mask and shift for Reply Post Host - * Index register. - * Added function code for Host Based Discovery Action. - * 02-10-10 02.00.15 Bumped MPI2_HEADER_VERSION_UNIT. - * Added define for MPI2_FUNCTION_PWR_MGMT_CONTROL. - * Added defines for product-specific range of message - * function codes, 0xF0 to 0xFF. - * 05-12-10 02.00.16 Bumped MPI2_HEADER_VERSION_UNIT. - * Added alternative defines for the SGE Direction bit. - * 08-11-10 02.00.17 Bumped MPI2_HEADER_VERSION_UNIT. - * 11-10-10 02.00.18 Bumped MPI2_HEADER_VERSION_UNIT. - * Added MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR define. - * 02-23-11 02.00.19 Bumped MPI2_HEADER_VERSION_UNIT. - * Added MPI2_FUNCTION_SEND_HOST_MESSAGE. - * 03-09-11 02.00.20 Bumped MPI2_HEADER_VERSION_UNIT. - * 05-25-11 02.00.21 Bumped MPI2_HEADER_VERSION_UNIT. - * 08-24-11 02.00.22 Bumped MPI2_HEADER_VERSION_UNIT. - * 11-18-11 02.00.23 Bumped MPI2_HEADER_VERSION_UNIT. - * 02-06-12 02.00.24 Bumped MPI2_HEADER_VERSION_UNIT. - * 03-29-12 02.00.25 Bumped MPI2_HEADER_VERSION_UNIT. - * Added Hard Reset delay timings. - * 07-10-12 02.00.26 Bumped MPI2_HEADER_VERSION_UNIT. - * 07-26-12 02.00.27 Bumped MPI2_HEADER_VERSION_UNIT. - * 11-27-12 02.00.28 Bumped MPI2_HEADER_VERSION_UNIT. - * 12-20-12 02.00.29 Bumped MPI2_HEADER_VERSION_UNIT. - * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET. - * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. - * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. - * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. - * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. - * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT. - * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_H -#define MPI2_H - - -/***************************************************************************** -* -* MPI Version Definitions -* -*****************************************************************************/ - -#define MPI2_VERSION_MAJOR (0x02) -#define MPI2_VERSION_MINOR (0x00) -#define MPI2_VERSION_MAJOR_MASK (0xFF00) -#define MPI2_VERSION_MAJOR_SHIFT (8) -#define MPI2_VERSION_MINOR_MASK (0x00FF) -#define MPI2_VERSION_MINOR_SHIFT (0) -#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ - MPI2_VERSION_MINOR) - -#define MPI2_VERSION_02_00 (0x0200) - -/* versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x23) -#define MPI2_HEADER_VERSION_DEV (0x00) -#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) -#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) -#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) -#define MPI2_HEADER_VERSION_DEV_SHIFT (0) -#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | MPI2_HEADER_VERSION_DEV) - - -/***************************************************************************** -* -* IOC State Definitions -* -*****************************************************************************/ - -#define MPI2_IOC_STATE_RESET (0x00000000) -#define MPI2_IOC_STATE_READY (0x10000000) -#define MPI2_IOC_STATE_OPERATIONAL (0x20000000) -#define MPI2_IOC_STATE_FAULT (0x40000000) - -#define MPI2_IOC_STATE_MASK (0xF0000000) -#define MPI2_IOC_STATE_SHIFT (28) - -/* Fault state range for prodcut specific codes */ -#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000) -#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF) - - -/***************************************************************************** -* -* System Interface Register Definitions -* -*****************************************************************************/ - -typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS -{ - U32 Doorbell; /* 0x00 */ - U32 WriteSequence; /* 0x04 */ - U32 HostDiagnostic; /* 0x08 */ - U32 Reserved1; /* 0x0C */ - U32 DiagRWData; /* 0x10 */ - U32 DiagRWAddressLow; /* 0x14 */ - U32 DiagRWAddressHigh; /* 0x18 */ - U32 Reserved2[5]; /* 0x1C */ - U32 HostInterruptStatus; /* 0x30 */ - U32 HostInterruptMask; /* 0x34 */ - U32 DCRData; /* 0x38 */ - U32 DCRAddress; /* 0x3C */ - U32 Reserved3[2]; /* 0x40 */ - U32 ReplyFreeHostIndex; /* 0x48 */ - U32 Reserved4[8]; /* 0x4C */ - U32 ReplyPostHostIndex; /* 0x6C */ - U32 Reserved5; /* 0x70 */ - U32 HCBSize; /* 0x74 */ - U32 HCBAddressLow; /* 0x78 */ - U32 HCBAddressHigh; /* 0x7C */ - U32 Reserved6[16]; /* 0x80 */ - U32 RequestDescriptorPostLow; /* 0xC0 */ - U32 RequestDescriptorPostHigh; /* 0xC4 */ - U32 Reserved7[14]; /* 0xC8 */ -} MPI2_SYSTEM_INTERFACE_REGS, MPI2_POINTER PTR_MPI2_SYSTEM_INTERFACE_REGS, - Mpi2SystemInterfaceRegs_t, MPI2_POINTER pMpi2SystemInterfaceRegs_t; - -/* - * Defines for working with the Doorbell register. - */ -#define MPI2_DOORBELL_OFFSET (0x00000000) - -/* IOC --> System values */ -#define MPI2_DOORBELL_USED (0x08000000) -#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000) -#define MPI2_DOORBELL_WHO_INIT_SHIFT (24) -#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF) -#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF) - -/* System --> IOC values */ -#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000) -#define MPI2_DOORBELL_FUNCTION_SHIFT (24) -#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) -#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16) - - -/* - * Defines for the WriteSequence register - */ -#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004) -#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F) -#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0) -#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF) -#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4) -#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB) -#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2) -#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7) -#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) - -/* - * Defines for the HostDiagnostic register - */ -#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008) - -#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800) -#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000) -#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800) - -#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) -#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200) -#define MPI2_DIAG_HCB_MODE (0x00000100) -#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080) -#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040) -#define MPI2_DIAG_RESET_HISTORY (0x00000020) -#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010) -#define MPI2_DIAG_RESET_ADAPTER (0x00000004) -#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002) - -/* - * Offsets for DiagRWData and address - */ -#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010) -#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014) -#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018) - -/* - * Defines for the HostInterruptStatus register - */ -#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) -#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000) -#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS -#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000) -#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008) -#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001) -#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS - -/* - * Defines for the HostInterruptMask register - */ -#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034) -#define MPI2_HIM_RESET_IRQ_MASK (0x40000000) -#define MPI2_HIM_REPLY_INT_MASK (0x00000008) -#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK -#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001) -#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK - -/* - * Offsets for DCRData and address - */ -#define MPI2_DCR_DATA_OFFSET (0x00000038) -#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C) - -/* - * Offset for the Reply Free Queue - */ -#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048) - -/* - * Defines for the Reply Descriptor Post Queue - */ -#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C) -#define MPI2_REPLY_POST_HOST_INDEX_MASK (0x00FFFFFF) -#define MPI2_RPHI_MSIX_INDEX_MASK (0xFF000000) -#define MPI2_RPHI_MSIX_INDEX_SHIFT (24) -#define MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET (0x0000030C) /* MPI v2.5 only */ - -/* - * Defines for the HCBSize and address - */ -#define MPI2_HCB_SIZE_OFFSET (0x00000074) -#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000) -#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001) - -#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078) -#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C) - -/* - * Offsets for the Request Queue - */ -#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0) -#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) - - -/* Hard Reset delay timings */ -#define MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC (50000) -#define MPI2_HARD_RESET_PCIE_RESET_READ_WINDOW_MICRO_SEC (255000) -#define MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC (256000) - -/***************************************************************************** -* -* Message Descriptors -* -*****************************************************************************/ - -/* Request Descriptors */ - -/* Default Request Descriptor */ -typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR -{ - U8 RequestFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 LMID; /* 0x04 */ - U16 DescriptorTypeDependent; /* 0x06 */ -} MPI2_DEFAULT_REQUEST_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR, - Mpi2DefaultRequestDescriptor_t, MPI2_POINTER pMpi2DefaultRequestDescriptor_t; - -/* defines for the RequestFlags field */ -#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E) -#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) -#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) -#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) -#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) -#define MPI2_REQ_DESCRIPT_FLAGS_RAID_ACCELERATOR (0x0A) - -#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) - - -/* High Priority Request Descriptor */ -typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR -{ - U8 RequestFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 LMID; /* 0x04 */ - U16 Reserved1; /* 0x06 */ -} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, - Mpi2HighPriorityRequestDescriptor_t, - MPI2_POINTER pMpi2HighPriorityRequestDescriptor_t; - - -/* SCSI IO Request Descriptor */ -typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR -{ - U8 RequestFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 LMID; /* 0x04 */ - U16 DevHandle; /* 0x06 */ -} MPI2_SCSI_IO_REQUEST_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR, - Mpi2SCSIIORequestDescriptor_t, MPI2_POINTER pMpi2SCSIIORequestDescriptor_t; - - -/* SCSI Target Request Descriptor */ -typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR -{ - U8 RequestFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 LMID; /* 0x04 */ - U16 IoIndex; /* 0x06 */ -} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, - Mpi2SCSITargetRequestDescriptor_t, - MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t; - - -/* RAID Accelerator Request Descriptor */ -typedef struct _MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { - U8 RequestFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 LMID; /* 0x04 */ - U16 Reserved; /* 0x06 */ -} MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR, - Mpi2RAIDAcceleratorRequestDescriptor_t, - MPI2_POINTER pMpi2RAIDAcceleratorRequestDescriptor_t; - - -/* union of Request Descriptors */ -typedef union _MPI2_REQUEST_DESCRIPTOR_UNION -{ - MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; - MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; - MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; - MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; - MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; - U64 Words; -} MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION, - Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t; - - -/* Reply Descriptors */ - -/* Default Reply Descriptor */ -typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR -{ - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 DescriptorTypeDependent1; /* 0x02 */ - U32 DescriptorTypeDependent2; /* 0x04 */ -} MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, - Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t; - -/* defines for the ReplyFlags field */ -#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) -#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) -#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) -#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) -#define MPI2_RPY_DESCRIPT_FLAGS_RAID_ACCELERATOR_SUCCESS (0x05) -#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) - -/* values for marking a reply descriptor as unused */ -#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) -#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF) - -/* Address Reply Descriptor */ -typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR -{ - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U32 ReplyFrameAddress; /* 0x04 */ -} MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, - Mpi2AddressReplyDescriptor_t, MPI2_POINTER pMpi2AddressReplyDescriptor_t; - -#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00) - - -/* SCSI IO Success Reply Descriptor */ -typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR -{ - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U16 TaskTag; /* 0x04 */ - U16 Reserved1; /* 0x06 */ -} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, - Mpi2SCSIIOSuccessReplyDescriptor_t, - MPI2_POINTER pMpi2SCSIIOSuccessReplyDescriptor_t; - - -/* TargetAssist Success Reply Descriptor */ -typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR -{ - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U8 SequenceNumber; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 IoIndex; /* 0x06 */ -} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, - Mpi2TargetAssistSuccessReplyDescriptor_t, - MPI2_POINTER pMpi2TargetAssistSuccessReplyDescriptor_t; - - -/* Target Command Buffer Reply Descriptor */ -typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR -{ - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U8 VP_ID; /* 0x02 */ - U8 Flags; /* 0x03 */ - U16 InitiatorDevHandle; /* 0x04 */ - U16 IoIndex; /* 0x06 */ -} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, - Mpi2TargetCommandBufferReplyDescriptor_t, - MPI2_POINTER pMpi2TargetCommandBufferReplyDescriptor_t; - -/* defines for Flags field */ -#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) - - -/* RAID Accelerator Success Reply Descriptor */ -typedef struct _MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { - U8 ReplyFlags; /* 0x00 */ - U8 MSIxIndex; /* 0x01 */ - U16 SMID; /* 0x02 */ - U32 Reserved; /* 0x04 */ -} MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, - MPI2_POINTER PTR_MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR, - Mpi2RAIDAcceleratorSuccessReplyDescriptor_t, - MPI2_POINTER pMpi2RAIDAcceleratorSuccessReplyDescriptor_t; - - -/* union of Reply Descriptors */ -typedef union _MPI2_REPLY_DESCRIPTORS_UNION -{ - MPI2_DEFAULT_REPLY_DESCRIPTOR Default; - MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; - MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; - MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; - MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; - MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR RAIDAcceleratorSuccess; - U64 Words; -} MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, -Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; - - - -/***************************************************************************** -* -* Message Functions -* -*****************************************************************************/ - -#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */ -#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) /* SCSI Task Management */ -#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */ -#define MPI2_FUNCTION_IOC_FACTS (0x03) /* IOC Facts */ -#define MPI2_FUNCTION_CONFIG (0x04) /* Configuration */ -#define MPI2_FUNCTION_PORT_FACTS (0x05) /* Port Facts */ -#define MPI2_FUNCTION_PORT_ENABLE (0x06) /* Port Enable */ -#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) /* Event Notification */ -#define MPI2_FUNCTION_EVENT_ACK (0x08) /* Event Acknowledge */ -#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) /* FW Download */ -#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) /* Target Assist */ -#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) /* Target Status Send */ -#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) /* Target Mode Abort */ -#define MPI2_FUNCTION_FW_UPLOAD (0x12) /* FW Upload */ -#define MPI2_FUNCTION_RAID_ACTION (0x15) /* RAID Action */ -#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) /* SCSI IO RAID Passthrough */ -#define MPI2_FUNCTION_TOOLBOX (0x17) /* Toolbox */ -#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) /* SCSI Enclosure Processor */ -#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) /* SMP Passthrough */ -#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) /* SAS IO Unit Control */ -#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) /* SATA Passthrough */ -#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) /* Diagnostic Buffer Post */ -#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */ -#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ -#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ -#define MPI2_FUNCTION_RAID_ACCELERATOR (0x2C) /* RAID Accelerator*/ -/* Host Based Discovery Action */ -#define MPI2_FUNCTION_HOST_BASED_DISCOVERY_ACTION (0x2F) -/* Power Management Control */ -#define MPI2_FUNCTION_PWR_MGMT_CONTROL (0x30) -/* Send Host Message */ -#define MPI2_FUNCTION_SEND_HOST_MESSAGE (0x31) -/* beginning of product-specific range */ -#define MPI2_FUNCTION_MIN_PRODUCT_SPECIFIC (0xF0) -/* end of product-specific range */ -#define MPI2_FUNCTION_MAX_PRODUCT_SPECIFIC (0xFF) - - - - -/* Doorbell functions */ -#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) -#define MPI2_FUNCTION_HANDSHAKE (0x42) - - -/***************************************************************************** -* -* IOC Status Values -* -*****************************************************************************/ - -/* mask for IOCStatus status value */ -#define MPI2_IOCSTATUS_MASK (0x7FFF) - -/**************************************************************************** -* Common IOCStatus values for all replies -****************************************************************************/ - -#define MPI2_IOCSTATUS_SUCCESS (0x0000) -#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001) -#define MPI2_IOCSTATUS_BUSY (0x0002) -#define MPI2_IOCSTATUS_INVALID_SGL (0x0003) -#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004) -#define MPI2_IOCSTATUS_INVALID_VPID (0x0005) -#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) -#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007) -#define MPI2_IOCSTATUS_INVALID_STATE (0x0008) -#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) - -/**************************************************************************** -* Config IOCStatus values -****************************************************************************/ - -#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) -#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) -#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) -#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) -#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) -#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) - -/**************************************************************************** -* SCSI IO Reply -****************************************************************************/ - -#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) -#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) -#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) -#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) -#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) -#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) -#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) -#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) -#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) -#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) -#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) -#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) - -/**************************************************************************** -* For use by SCSI Initiator and SCSI Target end-to-end data protection -****************************************************************************/ - -#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) -#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) -#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) - -/**************************************************************************** -* SCSI Target values -****************************************************************************/ - -#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) -#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063) -#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) -#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) -#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) -#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) -#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) -#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) -#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) -#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) - -/**************************************************************************** -* Serial Attached SCSI values -****************************************************************************/ - -#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) -#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) - -/**************************************************************************** -* Diagnostic Buffer Post / Diagnostic Release values -****************************************************************************/ - -#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) - -/**************************************************************************** -* RAID Accelerator values -****************************************************************************/ - -#define MPI2_IOCSTATUS_RAID_ACCEL_ERROR (0x00B0) - -/**************************************************************************** -* IOCStatus flag to indicate that log info is available -****************************************************************************/ - -#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) - -/**************************************************************************** -* IOCLogInfo Types -****************************************************************************/ - -#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000) -#define MPI2_IOCLOGINFO_TYPE_SHIFT (28) -#define MPI2_IOCLOGINFO_TYPE_NONE (0x0) -#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1) -#define MPI2_IOCLOGINFO_TYPE_FC (0x2) -#define MPI2_IOCLOGINFO_TYPE_SAS (0x3) -#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4) -#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) - - -/***************************************************************************** -* -* Standard Message Structures -* -*****************************************************************************/ - -/**************************************************************************** -* Request Message Header for all request messages -****************************************************************************/ - -typedef struct _MPI2_REQUEST_HEADER -{ - U16 FunctionDependent1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 FunctionDependent2; /* 0x04 */ - U8 FunctionDependent3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ -} MPI2_REQUEST_HEADER, MPI2_POINTER PTR_MPI2_REQUEST_HEADER, - MPI2RequestHeader_t, MPI2_POINTER pMPI2RequestHeader_t; - - -/**************************************************************************** -* Default Reply -****************************************************************************/ - -typedef struct _MPI2_DEFAULT_REPLY -{ - U16 FunctionDependent1; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 FunctionDependent2; /* 0x04 */ - U8 FunctionDependent3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U16 FunctionDependent5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_DEFAULT_REPLY, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY, - MPI2DefaultReply_t, MPI2_POINTER pMPI2DefaultReply_t; - - -/* common version structure/union used in messages and configuration pages */ - -typedef struct _MPI2_VERSION_STRUCT -{ - U8 Dev; /* 0x00 */ - U8 Unit; /* 0x01 */ - U8 Minor; /* 0x02 */ - U8 Major; /* 0x03 */ -} MPI2_VERSION_STRUCT; - -typedef union _MPI2_VERSION_UNION -{ - MPI2_VERSION_STRUCT Struct; - U32 Word; -} MPI2_VERSION_UNION; - - -/* LUN field defines, common to many structures */ -#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) -#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) -#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) -#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) -#define MPI2_LUN_LEVEL_1_WORD (0xFF00) -#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00) - - -/***************************************************************************** -* -* Fusion-MPT MPI Scatter Gather Elements -* -*****************************************************************************/ - -/**************************************************************************** -* MPI Simple Element structures -****************************************************************************/ - -typedef struct _MPI2_SGE_SIMPLE32 -{ - U32 FlagsLength; - U32 Address; -} MPI2_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_SGE_SIMPLE32, - Mpi2SGESimple32_t, MPI2_POINTER pMpi2SGESimple32_t; - -typedef struct _MPI2_SGE_SIMPLE64 -{ - U32 FlagsLength; - U64 Address; -} MPI2_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_SGE_SIMPLE64, - Mpi2SGESimple64_t, MPI2_POINTER pMpi2SGESimple64_t; - -typedef struct _MPI2_SGE_SIMPLE_UNION -{ - U32 FlagsLength; - union - { - U32 Address32; - U64 Address64; - } u; -} MPI2_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_SIMPLE_UNION, - Mpi2SGESimpleUnion_t, MPI2_POINTER pMpi2SGESimpleUnion_t; - - -/**************************************************************************** -* MPI Chain Element structures -****************************************************************************/ - -typedef struct _MPI2_SGE_CHAIN32 -{ - U16 Length; - U8 NextChainOffset; - U8 Flags; - U32 Address; -} MPI2_SGE_CHAIN32, MPI2_POINTER PTR_MPI2_SGE_CHAIN32, - Mpi2SGEChain32_t, MPI2_POINTER pMpi2SGEChain32_t; - -typedef struct _MPI2_SGE_CHAIN64 -{ - U16 Length; - U8 NextChainOffset; - U8 Flags; - U64 Address; -} MPI2_SGE_CHAIN64, MPI2_POINTER PTR_MPI2_SGE_CHAIN64, - Mpi2SGEChain64_t, MPI2_POINTER pMpi2SGEChain64_t; - -typedef struct _MPI2_SGE_CHAIN_UNION -{ - U16 Length; - U8 NextChainOffset; - U8 Flags; - union - { - U32 Address32; - U64 Address64; - } u; -} MPI2_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_SGE_CHAIN_UNION, - Mpi2SGEChainUnion_t, MPI2_POINTER pMpi2SGEChainUnion_t; - - -/**************************************************************************** -* MPI Transaction Context Element structures -****************************************************************************/ - -typedef struct _MPI2_SGE_TRANSACTION32 -{ - U8 Reserved; - U8 ContextSize; - U8 DetailsLength; - U8 Flags; - U32 TransactionContext[1]; - U32 TransactionDetails[1]; -} MPI2_SGE_TRANSACTION32, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION32, - Mpi2SGETransaction32_t, MPI2_POINTER pMpi2SGETransaction32_t; - -typedef struct _MPI2_SGE_TRANSACTION64 -{ - U8 Reserved; - U8 ContextSize; - U8 DetailsLength; - U8 Flags; - U32 TransactionContext[2]; - U32 TransactionDetails[1]; -} MPI2_SGE_TRANSACTION64, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION64, - Mpi2SGETransaction64_t, MPI2_POINTER pMpi2SGETransaction64_t; - -typedef struct _MPI2_SGE_TRANSACTION96 -{ - U8 Reserved; - U8 ContextSize; - U8 DetailsLength; - U8 Flags; - U32 TransactionContext[3]; - U32 TransactionDetails[1]; -} MPI2_SGE_TRANSACTION96, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION96, - Mpi2SGETransaction96_t, MPI2_POINTER pMpi2SGETransaction96_t; - -typedef struct _MPI2_SGE_TRANSACTION128 -{ - U8 Reserved; - U8 ContextSize; - U8 DetailsLength; - U8 Flags; - U32 TransactionContext[4]; - U32 TransactionDetails[1]; -} MPI2_SGE_TRANSACTION128, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION128, - Mpi2SGETransaction_t128, MPI2_POINTER pMpi2SGETransaction_t128; - -typedef struct _MPI2_SGE_TRANSACTION_UNION -{ - U8 Reserved; - U8 ContextSize; - U8 DetailsLength; - U8 Flags; - union - { - U32 TransactionContext32[1]; - U32 TransactionContext64[2]; - U32 TransactionContext96[3]; - U32 TransactionContext128[4]; - } u; - U32 TransactionDetails[1]; -} MPI2_SGE_TRANSACTION_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION_UNION, - Mpi2SGETransactionUnion_t, MPI2_POINTER pMpi2SGETransactionUnion_t; - - -/**************************************************************************** -* MPI SGE union for IO SGL's -****************************************************************************/ - -typedef struct _MPI2_MPI_SGE_IO_UNION -{ - union - { - MPI2_SGE_SIMPLE_UNION Simple; - MPI2_SGE_CHAIN_UNION Chain; - } u; -} MPI2_MPI_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_IO_UNION, - Mpi2MpiSGEIOUnion_t, MPI2_POINTER pMpi2MpiSGEIOUnion_t; - - -/**************************************************************************** -* MPI SGE union for SGL's with Simple and Transaction elements -****************************************************************************/ - -typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION -{ - union - { - MPI2_SGE_SIMPLE_UNION Simple; - MPI2_SGE_TRANSACTION_UNION Transaction; - } u; -} MPI2_SGE_TRANS_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANS_SIMPLE_UNION, - Mpi2SGETransSimpleUnion_t, MPI2_POINTER pMpi2SGETransSimpleUnion_t; - - -/**************************************************************************** -* All MPI SGE types union -****************************************************************************/ - -typedef struct _MPI2_MPI_SGE_UNION -{ - union - { - MPI2_SGE_SIMPLE_UNION Simple; - MPI2_SGE_CHAIN_UNION Chain; - MPI2_SGE_TRANSACTION_UNION Transaction; - } u; -} MPI2_MPI_SGE_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_UNION, - Mpi2MpiSgeUnion_t, MPI2_POINTER pMpi2MpiSgeUnion_t; - - -/**************************************************************************** -* MPI SGE field definition and masks -****************************************************************************/ - -/* Flags field bit definitions */ - -#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80) -#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40) -#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) -#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08) -#define MPI2_SGE_FLAGS_DIRECTION (0x04) -#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02) -#define MPI2_SGE_FLAGS_END_OF_LIST (0x01) - -#define MPI2_SGE_FLAGS_SHIFT (24) - -#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF) -#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) - -/* Element Type */ - -#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) -#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10) -#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30) -#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30) - -/* Address location */ - -#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00) - -/* Direction */ - -#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00) -#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04) - -#define MPI2_SGE_FLAGS_DEST (MPI2_SGE_FLAGS_IOC_TO_HOST) -#define MPI2_SGE_FLAGS_SOURCE (MPI2_SGE_FLAGS_HOST_TO_IOC) - -/* Address Size */ - -#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) -#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) - -/* Context Size */ - -#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00) -#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02) -#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04) -#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06) - -#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000) -#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16) - -/**************************************************************************** -* MPI SGE operation Macros -****************************************************************************/ - -/* SIMPLE FlagsLength manipulations... */ -#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT) -#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> MPI2_SGE_FLAGS_SHIFT) -#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK) -#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK) - -#define MPI2_SGE_SET_FLAGS_LENGTH(f,l) (MPI2_SGE_SET_FLAGS(f) | MPI2_SGE_LENGTH(l)) - -#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength) -#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength) -#define MPI2_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_SGE_SET_FLAGS_LENGTH(f,l) - -/* CAUTION - The following are READ-MODIFY-WRITE! */ -#define MPI2_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_SGE_SET_FLAGS(f) -#define MPI2_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_SGE_LENGTH(l) - -#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> MPI2_SGE_CHAIN_OFFSET_SHIFT) - - -/***************************************************************************** -* -* Fusion-MPT IEEE Scatter Gather Elements -* -*****************************************************************************/ - -/**************************************************************************** -* IEEE Simple Element structures -****************************************************************************/ - -typedef struct _MPI2_IEEE_SGE_SIMPLE32 -{ - U32 Address; - U32 FlagsLength; -} MPI2_IEEE_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE32, - Mpi2IeeeSgeSimple32_t, MPI2_POINTER pMpi2IeeeSgeSimple32_t; - -typedef struct _MPI2_IEEE_SGE_SIMPLE64 -{ - U64 Address; - U32 Length; - U16 Reserved1; - U8 Reserved2; - U8 Flags; -} MPI2_IEEE_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE64, - Mpi2IeeeSgeSimple64_t, MPI2_POINTER pMpi2IeeeSgeSimple64_t; - -typedef union _MPI2_IEEE_SGE_SIMPLE_UNION -{ - MPI2_IEEE_SGE_SIMPLE32 Simple32; - MPI2_IEEE_SGE_SIMPLE64 Simple64; -} MPI2_IEEE_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE_UNION, - Mpi2IeeeSgeSimpleUnion_t, MPI2_POINTER pMpi2IeeeSgeSimpleUnion_t; - - -/**************************************************************************** -* IEEE Chain Element structures -****************************************************************************/ - -typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32; - -typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64; - -typedef union _MPI2_IEEE_SGE_CHAIN_UNION -{ - MPI2_IEEE_SGE_CHAIN32 Chain32; - MPI2_IEEE_SGE_CHAIN64 Chain64; -} MPI2_IEEE_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_CHAIN_UNION, - Mpi2IeeeSgeChainUnion_t, MPI2_POINTER pMpi2IeeeSgeChainUnion_t; - - -/**************************************************************************** -* All IEEE SGE types union -****************************************************************************/ - -typedef struct _MPI2_IEEE_SGE_UNION -{ - union - { - MPI2_IEEE_SGE_SIMPLE_UNION Simple; - MPI2_IEEE_SGE_CHAIN_UNION Chain; - } u; -} MPI2_IEEE_SGE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_UNION, - Mpi2IeeeSgeUnion_t, MPI2_POINTER pMpi2IeeeSgeUnion_t; - - -/**************************************************************************** -* IEEE SGE field definitions and masks -****************************************************************************/ - -/* Flags field bit definitions */ - -#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80) - -#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24) - -#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF) - -/* Element Type */ - -#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00) -#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) - -/* Data Location Address Space */ - -#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) -#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) - /* IEEE Simple Element only */ -#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) - /* IEEE Simple Element only */ -#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) -#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) - /* IEEE Simple Element only */ -#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR (0x03) - /* IEEE Chain Element only */ -#define MPI2_IEEE_SGE_FLAGS_SYSTEMPLBCPI_ADDR \ - (MPI2_IEEE_SGE_FLAGS_SYSTEMPLBPCI_ADDR) /* typo in name */ - -/**************************************************************************** -* IEEE SGE operation Macros -****************************************************************************/ - -/* SIMPLE FlagsLength manipulations... */ -#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT) -#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) >> MPI2_IEEE32_SGE_FLAGS_SHIFT) -#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK) - -#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) | MPI2_IEEE32_SGE_LENGTH(l)) - -#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength) -#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength) -#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f,l) - -/* CAUTION - The following are READ-MODIFY-WRITE! */ -#define MPI2_IEEE32_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_IEEE32_SGE_SET_FLAGS(f) -#define MPI2_IEEE32_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_IEEE32_SGE_LENGTH(l) - - - - -/***************************************************************************** -* -* Fusion-MPT MPI/IEEE Scatter Gather Unions -* -*****************************************************************************/ - -typedef union _MPI2_SIMPLE_SGE_UNION -{ - MPI2_SGE_SIMPLE_UNION MpiSimple; - MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; -} MPI2_SIMPLE_SGE_UNION, MPI2_POINTER PTR_MPI2_SIMPLE_SGE_UNION, - Mpi2SimpleSgeUntion_t, MPI2_POINTER pMpi2SimpleSgeUntion_t; - - -typedef union _MPI2_SGE_IO_UNION -{ - MPI2_SGE_SIMPLE_UNION MpiSimple; - MPI2_SGE_CHAIN_UNION MpiChain; - MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; - MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; -} MPI2_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_SGE_IO_UNION, - Mpi2SGEIOUnion_t, MPI2_POINTER pMpi2SGEIOUnion_t; - - -/**************************************************************************** -* -* Values for SGLFlags field, used in many request messages with an SGL -* -****************************************************************************/ - -/* values for MPI SGL Data Location Address Space subfield */ -#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C) -#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00) -#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04) -#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) -#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C) -/* values for SGL Type subfield */ -#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03) -#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00) -#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01) -#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02) - - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h deleted file mode 100644 index ee8d2d695..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ /dev/null @@ -1,3068 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_cnfg.h - * Title: MPI Configuration messages and pages - * Creation Date: November 10, 2006 - * - * mpi2_cnfg.h Version: 02.00.29 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. - * Added Manufacturing Page 11. - * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE - * define. - * 06-26-07 02.00.02 Adding generic structure for product-specific - * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. - * Rework of BIOS Page 2 configuration page. - * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the - * forms. - * Added configuration pages IOC Page 8 and Driver - * Persistent Mapping Page 0. - * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated - * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, - * RAID Physical Disk Pages 0 and 1, RAID Configuration - * Page 0). - * Added new value for AccessStatus field of SAS Device - * Page 0 (_SATA_NEEDS_INITIALIZATION). - * 10-31-07 02.00.04 Added missing SEPDevHandle field to - * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. - * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for - * NVDATA. - * Modified IOC Page 7 to use masks and added field for - * SASBroadcastPrimitiveMasks. - * Added MPI2_CONFIG_PAGE_BIOS_4. - * Added MPI2_CONFIG_PAGE_LOG_0. - * 02-29-08 02.00.06 Modified various names to make them 32-character unique. - * Added SAS Device IDs. - * Updated Integrated RAID configuration pages including - * Manufacturing Page 4, IOC Page 6, and RAID Configuration - * Page 0. - * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. - * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. - * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. - * Added missing MaxNumRoutedSasAddresses field to - * MPI2_CONFIG_PAGE_EXPANDER_0. - * Added SAS Port Page 0. - * Modified structure layout for - * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. - * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use - * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. - * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF - * to 0x000000FF. - * Added two new values for the Physical Disk Coercion Size - * bits in the Flags field of Manufacturing Page 4. - * Added product-specific Manufacturing pages 16 to 31. - * Modified Flags bits for controlling write cache on SATA - * drives in IO Unit Page 1. - * Added new bit to AdditionalControlFlags of SAS IO Unit - * Page 1 to control Invalid Topology Correction. - * Added additional defines for RAID Volume Page 0 - * VolumeStatusFlags field. - * Modified meaning of RAID Volume Page 0 VolumeSettings - * define for auto-configure of hot-swap drives. - * Added SupportedPhysDisks field to RAID Volume Page 1 and - * added related defines. - * Added PhysDiskAttributes field (and related defines) to - * RAID Physical Disk Page 0. - * Added MPI2_SAS_PHYINFO_PHY_VACANT define. - * Added three new DiscoveryStatus bits for SAS IO Unit - * Page 0 and SAS Expander Page 0. - * Removed multiplexing information from SAS IO Unit pages. - * Added BootDeviceWaitTime field to SAS IO Unit Page 4. - * Removed Zone Address Resolved bit from PhyInfo and from - * Expander Page 0 Flags field. - * Added two new AccessStatus values to SAS Device Page 0 - * for indicating routing problems. Added 3 reserved words - * to this page. - * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. - * Inserted missing reserved field into structure for IOC - * Page 6. - * Added more pending task bits to RAID Volume Page 0 - * VolumeStatusFlags defines. - * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. - * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 - * and SAS Expander Page 0 to flag a downstream initiator - * when in simplified routing mode. - * Removed SATA Init Failure defines for DiscoveryStatus - * fields of SAS IO Unit Page 0 and SAS Expander Page 0. - * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. - * Added PortGroups, DmaGroup, and ControlGroup fields to - * SAS Device Page 0. - * 05-06-09 02.00.11 Added structures and defines for IO Unit Page 5 and IO - * Unit Page 6. - * Added expander reduced functionality data to SAS - * Expander Page 0. - * Added SAS PHY Page 2 and SAS PHY Page 3. - * 07-30-09 02.00.12 Added IO Unit Page 7. - * Added new device ids. - * Added SAS IO Unit Page 5. - * Added partial and slumber power management capable flags - * to SAS Device Page 0 Flags field. - * Added PhyInfo defines for power condition. - * Added Ethernet configuration pages. - * 10-28-09 02.00.13 Added MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY. - * Added SAS PHY Page 4 structure and defines. - * 02-10-10 02.00.14 Modified the comments for the configuration page - * structures that contain an array of data. The host - * should use the "count" field in the page data (e.g. the - * NumPhys field) to determine the number of valid elements - * in the array. - * Added/modified some MPI2_MFGPAGE_DEVID_SAS defines. - * Added PowerManagementCapabilities to IO Unit Page 7. - * Added PortWidthModGroup field to - * MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS. - * Added MPI2_CONFIG_PAGE_SASIOUNIT_6 and related defines. - * Added MPI2_CONFIG_PAGE_SASIOUNIT_7 and related defines. - * Added MPI2_CONFIG_PAGE_SASIOUNIT_8 and related defines. - * 05-12-10 02.00.15 Added MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT - * define. - * Added MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE define. - * Added MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY define. - * 08-11-10 02.00.16 Removed IO Unit Page 1 device path (multi-pathing) - * defines. - * 11-10-10 02.00.17 Added ReceptacleID field (replacing Reserved1) to - * MPI2_MANPAGE7_CONNECTOR_INFO and reworked defines for - * the Pinout field. - * Added BoardTemperature and BoardTemperatureUnits fields - * to MPI2_CONFIG_PAGE_IO_UNIT_7. - * Added MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING define - * and MPI2_CONFIG_PAGE_EXT_MAN_PS structure. - * 02-23-11 02.00.18 Added ProxyVF_ID field to MPI2_CONFIG_REQUEST. - * Added IO Unit Page 8, IO Unit Page 9, - * and IO Unit Page 10. - * Added SASNotifyPrimitiveMasks field to - * MPI2_CONFIG_PAGE_IOC_7. - * 03-09-11 02.00.19 Fixed IO Unit Page 10 (to match the spec). - * 05-25-11 02.00.20 Cleaned up a few comments. - * 08-24-11 02.00.21 Marked the IO Unit Page 7 PowerManagementCapabilities - * for PCIe link as obsolete. - * Added SpinupFlags field containing a Disable Spin-up - * bit to the MPI2_SAS_IOUNIT4_SPINUP_GROUP fields of - * SAS IO Unit Page 4. - * 11-18-11 02.00.22 Added define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT. - * Added UEFIVersion field to BIOS Page 1 and defined new - * BiosOptions bits. - * 11-27-12 02.00.23 Added MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER. - * Added MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID. - * 12-20-12 02.00.24 Marked MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION as - * obsolete for MPI v2.5 and later. - * Added some defines for 12G SAS speeds. - * 04-09-13 02.00.25 Added MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK. - * Fixed MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS to - * match the specification. - * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for - * MPI2_CONFIG_PAGE_MAN_7. - * Added EnclosureLevel and ConnectorName fields to - * MPI2_CONFIG_PAGE_SAS_DEV_0. - * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for - * MPI2_CONFIG_PAGE_SAS_DEV_0. - * Added EnclosureLevel field to - * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. - * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for - * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. - * 01-08-14 02.00.28 Added more defines for the BiosOptions field of - * MPI2_CONFIG_PAGE_BIOS_1. - * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and - * more defines for the BiosOptions field. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_CNFG_H -#define MPI2_CNFG_H - -/***************************************************************************** -* Configuration Page Header and defines -*****************************************************************************/ - -/* Config Page Header */ -typedef struct _MPI2_CONFIG_PAGE_HEADER -{ - U8 PageVersion; /* 0x00 */ - U8 PageLength; /* 0x01 */ - U8 PageNumber; /* 0x02 */ - U8 PageType; /* 0x03 */ -} MPI2_CONFIG_PAGE_HEADER, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER, - Mpi2ConfigPageHeader_t, MPI2_POINTER pMpi2ConfigPageHeader_t; - -typedef union _MPI2_CONFIG_PAGE_HEADER_UNION -{ - MPI2_CONFIG_PAGE_HEADER Struct; - U8 Bytes[4]; - U16 Word16[2]; - U32 Word32; -} MPI2_CONFIG_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER_UNION, - Mpi2ConfigPageHeaderUnion, MPI2_POINTER pMpi2ConfigPageHeaderUnion; - -/* Extended Config Page Header */ -typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER -{ - U8 PageVersion; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 PageNumber; /* 0x02 */ - U8 PageType; /* 0x03 */ - U16 ExtPageLength; /* 0x04 */ - U8 ExtPageType; /* 0x06 */ - U8 Reserved2; /* 0x07 */ -} MPI2_CONFIG_EXTENDED_PAGE_HEADER, - MPI2_POINTER PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER, - Mpi2ConfigExtendedPageHeader_t, MPI2_POINTER pMpi2ConfigExtendedPageHeader_t; - -typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION -{ - MPI2_CONFIG_PAGE_HEADER Struct; - MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext; - U8 Bytes[8]; - U16 Word16[4]; - U32 Word32[2]; -} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION, - Mpi2ConfigPageExtendedHeaderUnion, MPI2_POINTER pMpi2ConfigPageExtendedHeaderUnion; - - -/* PageType field values */ -#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00) -#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10) -#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20) -#define MPI2_CONFIG_PAGEATTR_MASK (0xF0) - -#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00) -#define MPI2_CONFIG_PAGETYPE_IOC (0x01) -#define MPI2_CONFIG_PAGETYPE_BIOS (0x02) -#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08) -#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09) -#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) -#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F) -#define MPI2_CONFIG_PAGETYPE_MASK (0x0F) - -#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF) - - -/* ExtPageType field values */ -#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10) -#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11) -#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12) -#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13) -#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14) -#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15) -#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) -#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) -#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) -#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET (0x19) -#define MPI2_CONFIG_EXTPAGETYPE_EXT_MANUFACTURING (0x1A) - - -/***************************************************************************** -* PageAddress defines -*****************************************************************************/ - -/* RAID Volume PageAddress format */ -#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000) -#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) -#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000) - -#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF) - - -/* RAID Physical Disk PageAddress format */ -#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000) -#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000) -#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000) -#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000) - -#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) -#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF) - - -/* SAS Expander PageAddress format */ -#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000) -#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000) -#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000) -#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000) - -#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF) -#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000) -#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16) - - -/* SAS Device PageAddress format */ -#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000) -#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) -#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000) - -#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF) - - -/* SAS PHY PageAddress format */ -#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000) -#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000) -#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000) - -#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF) -#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF) - - -/* SAS Port PageAddress format */ -#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000) -#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000) -#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000) - -#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF) - - -/* SAS Enclosure PageAddress format */ -#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000) -#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) -#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000) - -#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) - - -/* RAID Configuration PageAddress format */ -#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000) -#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000) -#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000) -#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000) - -#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF) - - -/* Driver Persistent Mapping PageAddress format */ -#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000) -#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000) - -#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000) -#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16) -#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF) - - -/* Ethernet PageAddress format */ -#define MPI2_ETHERNET_PGAD_FORM_MASK (0xF0000000) -#define MPI2_ETHERNET_PGAD_FORM_IF_NUM (0x00000000) - -#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK (0x000000FF) - - - -/**************************************************************************** -* Configuration messages -****************************************************************************/ - -/* Configuration Request Message */ -typedef struct _MPI2_CONFIG_REQUEST -{ - U8 Action; /* 0x00 */ - U8 SGLFlags; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 ExtPageLength; /* 0x04 */ - U8 ExtPageType; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U8 Reserved2; /* 0x0C */ - U8 ProxyVF_ID; /* 0x0D */ - U16 Reserved4; /* 0x0E */ - U32 Reserved3; /* 0x10 */ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */ - U32 PageAddress; /* 0x18 */ - MPI2_SGE_IO_UNION PageBufferSGE; /* 0x1C */ -} MPI2_CONFIG_REQUEST, MPI2_POINTER PTR_MPI2_CONFIG_REQUEST, - Mpi2ConfigRequest_t, MPI2_POINTER pMpi2ConfigRequest_t; - -/* values for the Action field */ -#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00) -#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) -#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) -#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03) -#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) -#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) -#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) -#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07) - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - - -/* Config Reply Message */ -typedef struct _MPI2_CONFIG_REPLY -{ - U8 Action; /* 0x00 */ - U8 SGLFlags; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 ExtPageLength; /* 0x04 */ - U8 ExtPageType; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U16 Reserved2; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */ -} MPI2_CONFIG_REPLY, MPI2_POINTER PTR_MPI2_CONFIG_REPLY, - Mpi2ConfigReply_t, MPI2_POINTER pMpi2ConfigReply_t; - - - -/***************************************************************************** -* -* C o n f i g u r a t i o n P a g e s -* -*****************************************************************************/ - -/**************************************************************************** -* Manufacturing Config pages -****************************************************************************/ - -#define MPI2_MFGPAGE_VENDORID_LSI (0x1000) - -/* SAS */ -#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070) -#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072) -#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074) -#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076) -#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077) -#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) -#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) - -#define MPI2_MFGPAGE_DEVID_SSS6200 (0x007E) - -#define MPI2_MFGPAGE_DEVID_SAS2208_1 (0x0080) -#define MPI2_MFGPAGE_DEVID_SAS2208_2 (0x0081) -#define MPI2_MFGPAGE_DEVID_SAS2208_3 (0x0082) -#define MPI2_MFGPAGE_DEVID_SAS2208_4 (0x0083) -#define MPI2_MFGPAGE_DEVID_SAS2208_5 (0x0084) -#define MPI2_MFGPAGE_DEVID_SAS2208_6 (0x0085) -#define MPI2_MFGPAGE_DEVID_SAS2308_1 (0x0086) -#define MPI2_MFGPAGE_DEVID_SAS2308_2 (0x0087) -#define MPI2_MFGPAGE_DEVID_SAS2308_3 (0x006E) - - - - -/* Manufacturing Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_MAN_0 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 ChipName[16]; /* 0x04 */ - U8 ChipRevision[8]; /* 0x14 */ - U8 BoardName[16]; /* 0x1C */ - U8 BoardAssembly[16]; /* 0x2C */ - U8 BoardTracerNumber[16]; /* 0x3C */ -} MPI2_CONFIG_PAGE_MAN_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_0, - Mpi2ManufacturingPage0_t, MPI2_POINTER pMpi2ManufacturingPage0_t; - -#define MPI2_MANUFACTURING0_PAGEVERSION (0x00) - - -/* Manufacturing Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_MAN_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 VPD[256]; /* 0x04 */ -} MPI2_CONFIG_PAGE_MAN_1, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_1, - Mpi2ManufacturingPage1_t, MPI2_POINTER pMpi2ManufacturingPage1_t; - -#define MPI2_MANUFACTURING1_PAGEVERSION (0x00) - - -typedef struct _MPI2_CHIP_REVISION_ID -{ - U16 DeviceID; /* 0x00 */ - U8 PCIRevisionID; /* 0x02 */ - U8 Reserved; /* 0x03 */ -} MPI2_CHIP_REVISION_ID, MPI2_POINTER PTR_MPI2_CHIP_REVISION_ID, - Mpi2ChipRevisionId_t, MPI2_POINTER pMpi2ChipRevisionId_t; - - -/* Manufacturing Page 2 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength at runtime. - */ -#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS -#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_MAN_2 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */ - U32 HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 0x08 */ -} MPI2_CONFIG_PAGE_MAN_2, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_2, - Mpi2ManufacturingPage2_t, MPI2_POINTER pMpi2ManufacturingPage2_t; - -#define MPI2_MANUFACTURING2_PAGEVERSION (0x00) - - -/* Manufacturing Page 3 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check Header.PageLength at runtime. - */ -#ifndef MPI2_MAN_PAGE_3_INFO_WORDS -#define MPI2_MAN_PAGE_3_INFO_WORDS (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_MAN_3 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */ - U32 Info[MPI2_MAN_PAGE_3_INFO_WORDS];/* 0x08 */ -} MPI2_CONFIG_PAGE_MAN_3, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_3, - Mpi2ManufacturingPage3_t, MPI2_POINTER pMpi2ManufacturingPage3_t; - -#define MPI2_MANUFACTURING3_PAGEVERSION (0x00) - - -/* Manufacturing Page 4 */ - -typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS -{ - U8 PowerSaveFlags; /* 0x00 */ - U8 InternalOperationsSleepTime; /* 0x01 */ - U8 InternalOperationsRunTime; /* 0x02 */ - U8 HostIdleTime; /* 0x03 */ -} MPI2_MANPAGE4_PWR_SAVE_SETTINGS, - MPI2_POINTER PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS, - Mpi2ManPage4PwrSaveSettings_t, MPI2_POINTER pMpi2ManPage4PwrSaveSettings_t; - -/* defines for the PowerSaveFlags field */ -#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03) -#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00) -#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01) -#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02) - -typedef struct _MPI2_CONFIG_PAGE_MAN_4 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Flags; /* 0x08 */ - U8 InquirySize; /* 0x0C */ - U8 Reserved2; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - U8 InquiryData[56]; /* 0x10 */ - U32 RAID0VolumeSettings; /* 0x48 */ - U32 RAID1EVolumeSettings; /* 0x4C */ - U32 RAID1VolumeSettings; /* 0x50 */ - U32 RAID10VolumeSettings; /* 0x54 */ - U32 Reserved4; /* 0x58 */ - U32 Reserved5; /* 0x5C */ - MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /* 0x60 */ - U8 MaxOCEDisks; /* 0x64 */ - U8 ResyncRate; /* 0x65 */ - U16 DataScrubDuration; /* 0x66 */ - U8 MaxHotSpares; /* 0x68 */ - U8 MaxPhysDisksPerVol; /* 0x69 */ - U8 MaxPhysDisks; /* 0x6A */ - U8 MaxVolumes; /* 0x6B */ -} MPI2_CONFIG_PAGE_MAN_4, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_4, - Mpi2ManufacturingPage4_t, MPI2_POINTER pMpi2ManufacturingPage4_t; - -#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A) - -/* Manufacturing Page 4 Flags field */ -#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000) -#define MPI2_MANPAGE4_METADATA_512MB (0x00000000) - -#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000) -#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000) -#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000) - -#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00) -#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000) -#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400) -#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800) -#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00) - -#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300) -#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000) -#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100) -#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200) - -#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080) -#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040) -#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020) -#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010) -#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008) -#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004) -#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002) -#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001) - - -/* Manufacturing Page 5 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES -#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) -#endif - -typedef struct _MPI2_MANUFACTURING5_ENTRY -{ - U64 WWID; /* 0x00 */ - U64 DeviceName; /* 0x08 */ -} MPI2_MANUFACTURING5_ENTRY, MPI2_POINTER PTR_MPI2_MANUFACTURING5_ENTRY, - Mpi2Manufacturing5Entry_t, MPI2_POINTER pMpi2Manufacturing5Entry_t; - -typedef struct _MPI2_CONFIG_PAGE_MAN_5 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 NumPhys; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U32 Reserved3; /* 0x08 */ - U32 Reserved4; /* 0x0C */ - MPI2_MANUFACTURING5_ENTRY Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/* 0x08 */ -} MPI2_CONFIG_PAGE_MAN_5, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_5, - Mpi2ManufacturingPage5_t, MPI2_POINTER pMpi2ManufacturingPage5_t; - -#define MPI2_MANUFACTURING5_PAGEVERSION (0x03) - - -/* Manufacturing Page 6 */ - -typedef struct _MPI2_CONFIG_PAGE_MAN_6 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 ProductSpecificInfo;/* 0x04 */ -} MPI2_CONFIG_PAGE_MAN_6, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_6, - Mpi2ManufacturingPage6_t, MPI2_POINTER pMpi2ManufacturingPage6_t; - -#define MPI2_MANUFACTURING6_PAGEVERSION (0x00) - - -/* Manufacturing Page 7 */ - -typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO -{ - U32 Pinout; /* 0x00 */ - U8 Connector[16]; /* 0x04 */ - U8 Location; /* 0x14 */ - U8 ReceptacleID; /* 0x15 */ - U16 Slot; /* 0x16 */ - U32 Reserved2; /* 0x18 */ -} MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO, - Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t; - -/* defines for the Pinout field */ -#define MPI2_MANPAGE7_PINOUT_LANE_MASK (0x0000FF00) -#define MPI2_MANPAGE7_PINOUT_LANE_SHIFT (8) - -#define MPI2_MANPAGE7_PINOUT_TYPE_MASK (0x000000FF) -#define MPI2_MANPAGE7_PINOUT_TYPE_UNKNOWN (0x00) -#define MPI2_MANPAGE7_PINOUT_SATA_SINGLE (0x01) -#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x02) -#define MPI2_MANPAGE7_PINOUT_SFF_8486 (0x03) -#define MPI2_MANPAGE7_PINOUT_SFF_8484 (0x04) -#define MPI2_MANPAGE7_PINOUT_SFF_8087 (0x05) -#define MPI2_MANPAGE7_PINOUT_SFF_8643_4I (0x06) -#define MPI2_MANPAGE7_PINOUT_SFF_8643_8I (0x07) -#define MPI2_MANPAGE7_PINOUT_SFF_8470 (0x08) -#define MPI2_MANPAGE7_PINOUT_SFF_8088 (0x09) -#define MPI2_MANPAGE7_PINOUT_SFF_8644_4X (0x0A) -#define MPI2_MANPAGE7_PINOUT_SFF_8644_8X (0x0B) -#define MPI2_MANPAGE7_PINOUT_SFF_8644_16X (0x0C) -#define MPI2_MANPAGE7_PINOUT_SFF_8436 (0x0D) - -/* defines for the Location field */ -#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) -#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02) -#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04) -#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08) -#define MPI2_MANPAGE7_LOCATION_AUTO (0x10) -#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20) -#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX -#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_MAN_7 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U32 Flags; /* 0x0C */ - U8 EnclosureName[16]; /* 0x10 */ - U8 NumPhys; /* 0x20 */ - U8 Reserved3; /* 0x21 */ - U16 Reserved4; /* 0x22 */ - MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /* 0x24 */ -} MPI2_CONFIG_PAGE_MAN_7, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7, - Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t; - -#define MPI2_MANUFACTURING7_PAGEVERSION (0x01) - -/* defines for the Flags field */ -#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) -#define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) -#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) - - -/* - * Generic structure to use for product-specific manufacturing pages - * (currently Manufacturing Page 8 through Manufacturing Page 31). - */ - -typedef struct _MPI2_CONFIG_PAGE_MAN_PS -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 ProductSpecificInfo;/* 0x04 */ -} MPI2_CONFIG_PAGE_MAN_PS, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_PS, - Mpi2ManufacturingPagePS_t, MPI2_POINTER pMpi2ManufacturingPagePS_t; - -#define MPI2_MANUFACTURING8_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING9_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING10_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING11_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING12_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING13_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING14_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING15_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING16_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING17_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING18_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING19_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING20_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING21_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING22_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING23_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING24_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING25_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING26_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING27_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING28_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING29_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING30_PAGEVERSION (0x00) -#define MPI2_MANUFACTURING31_PAGEVERSION (0x00) - - -/**************************************************************************** -* IO Unit Config Pages -****************************************************************************/ - -/* IO Unit Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U64 UniqueValue; /* 0x04 */ - MPI2_VERSION_UNION NvdataVersionDefault; /* 0x08 */ - MPI2_VERSION_UNION NvdataVersionPersistent; /* 0x0A */ -} MPI2_CONFIG_PAGE_IO_UNIT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_0, - Mpi2IOUnitPage0_t, MPI2_POINTER pMpi2IOUnitPage0_t; - -#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02) - - -/* IO Unit Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Flags; /* 0x04 */ -} MPI2_CONFIG_PAGE_IO_UNIT_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_1, - Mpi2IOUnitPage1_t, MPI2_POINTER pMpi2IOUnitPage1_t; - -#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04) - -/* IO Unit Page 1 Flags defines */ -#define MPI2_IOUNITPAGE1_ATA_SECURITY_FREEZE_LOCK (0x00004000) -#define MPI2_IOUNITPAGE1_ENABLE_HOST_BASED_DISCOVERY (0x00000800) -#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600) -#define MPI2_IOUNITPAGE1_SATA_WRITE_CACHE_SHIFT (9) -#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000) -#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200) -#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400) -#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100) -#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040) -#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020) -#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) - - -/* IO Unit Page 3 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for GPIOCount at runtime. - */ -#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX -#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 GPIOCount; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U16 GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/* 0x08 */ -} MPI2_CONFIG_PAGE_IO_UNIT_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_3, - Mpi2IOUnitPage3_t, MPI2_POINTER pMpi2IOUnitPage3_t; - -#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01) - -/* defines for IO Unit Page 3 GPIOVal field */ -#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC) -#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2) -#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000) -#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) - - -/* IO Unit Page 5 */ - -/* - * Upper layer code (drivers, utilities, etc.) should leave this define set to - * one and check the value returned for NumDmaEngines at runtime. - */ -#ifndef MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES -#define MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_5 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U64 RaidAcceleratorBufferBaseAddress; /* 0x04 */ - U64 RaidAcceleratorBufferSize; /* 0x0C */ - U64 RaidAcceleratorControlBaseAddress; /* 0x14 */ - U8 RAControlSize; /* 0x1C */ - U8 NumDmaEngines; /* 0x1D */ - U8 RAMinControlSize; /* 0x1E */ - U8 RAMaxControlSize; /* 0x1F */ - U32 Reserved1; /* 0x20 */ - U32 Reserved2; /* 0x24 */ - U32 Reserved3; /* 0x28 */ - U32 DmaEngineCapabilities - [MPI2_IOUNITPAGE5_DMAENGINE_ENTRIES]; /* 0x2C */ -} MPI2_CONFIG_PAGE_IO_UNIT_5, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_5, - Mpi2IOUnitPage5_t, MPI2_POINTER pMpi2IOUnitPage5_t; - -#define MPI2_IOUNITPAGE5_PAGEVERSION (0x00) - -/* defines for IO Unit Page 5 DmaEngineCapabilities field */ -#define MPI2_IOUNITPAGE5_DMA_CAP_MASK_MAX_REQUESTS (0xFFFF0000) -#define MPI2_IOUNITPAGE5_DMA_CAP_SHIFT_MAX_REQUESTS (16) - -#define MPI2_IOUNITPAGE5_DMA_CAP_EEDP (0x0008) -#define MPI2_IOUNITPAGE5_DMA_CAP_PARITY_GENERATION (0x0004) -#define MPI2_IOUNITPAGE5_DMA_CAP_HASHING (0x0002) -#define MPI2_IOUNITPAGE5_DMA_CAP_ENCRYPTION (0x0001) - - -/* IO Unit Page 6 */ - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U16 Flags; /* 0x04 */ - U8 RAHostControlSize; /* 0x06 */ - U8 Reserved0; /* 0x07 */ - U64 RaidAcceleratorHostControlBaseAddress; /* 0x08 */ - U32 Reserved1; /* 0x10 */ - U32 Reserved2; /* 0x14 */ - U32 Reserved3; /* 0x18 */ -} MPI2_CONFIG_PAGE_IO_UNIT_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_6, - Mpi2IOUnitPage6_t, MPI2_POINTER pMpi2IOUnitPage6_t; - -#define MPI2_IOUNITPAGE6_PAGEVERSION (0x00) - -/* defines for IO Unit Page 6 Flags field */ -#define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR (0x0001) - - -/* IO Unit Page 7 */ - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U16 Reserved1; /* 0x04 */ - U8 PCIeWidth; /* 0x06 */ - U8 PCIeSpeed; /* 0x07 */ - U32 ProcessorState; /* 0x08 */ - U32 PowerManagementCapabilities; /* 0x0C */ - U16 IOCTemperature; /* 0x10 */ - U8 IOCTemperatureUnits; /* 0x12 */ - U8 IOCSpeed; /* 0x13 */ - U16 BoardTemperature; /* 0x14 */ - U8 BoardTemperatureUnits; /* 0x16 */ - U8 Reserved3; /* 0x17 */ - U32 Reserved4; /* 0x18 */ - U32 Reserved5; /* 0x1C */ - U32 Reserved6; /* 0x20 */ - U32 Reserved7; /* 0x24 */ -} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7, - Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t; - -#define MPI2_IOUNITPAGE7_PAGEVERSION (0x04) - -/* defines for IO Unit Page 7 PCIeWidth field */ -#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1 (0x01) -#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2 (0x02) -#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4 (0x04) -#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8 (0x08) - -/* defines for IO Unit Page 7 PCIeSpeed field */ -#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS (0x00) -#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS (0x01) -#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS (0x02) - -/* defines for IO Unit Page 7 ProcessorState field */ -#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND (0x0000000F) -#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND (0) - -#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT (0x00) -#define MPI2_IOUNITPAGE7_PSTATE_DISABLED (0x01) -#define MPI2_IOUNITPAGE7_PSTATE_ENABLED (0x02) - -/* defines for IO Unit Page 7 PowerManagementCapabilities field */ -#define MPI2_IOUNITPAGE7_PMCAP_12_5_PCT_IOCSPEED (0x00000400) -#define MPI2_IOUNITPAGE7_PMCAP_25_0_PCT_IOCSPEED (0x00000200) -#define MPI2_IOUNITPAGE7_PMCAP_50_0_PCT_IOCSPEED (0x00000100) -#define MPI2_IOUNITPAGE7_PMCAP_PCIE_WIDTH_CHANGE (0x00000008) /* obsolete */ -#define MPI2_IOUNITPAGE7_PMCAP_PCIE_SPEED_CHANGE (0x00000004) /* obsolete */ - -/* defines for IO Unit Page 7 IOCTemperatureUnits field */ -#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00) -#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01) -#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02) - -/* defines for IO Unit Page 7 IOCSpeed field */ -#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL (0x01) -#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF (0x02) -#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER (0x04) -#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH (0x08) - -/* defines for IO Unit Page 7 BoardTemperatureUnits field */ -#define MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT (0x00) -#define MPI2_IOUNITPAGE7_BOARD_TEMP_FAHRENHEIT (0x01) -#define MPI2_IOUNITPAGE7_BOARD_TEMP_CELSIUS (0x02) - -/* IO Unit Page 8 */ - -#define MPI2_IOUNIT8_NUM_THRESHOLDS (4) - -typedef struct _MPI2_IOUNIT8_SENSOR { - U16 Flags; /* 0x00 */ - U16 Reserved1; /* 0x02 */ - U16 - Threshold[MPI2_IOUNIT8_NUM_THRESHOLDS]; /* 0x04 */ - U32 Reserved2; /* 0x0C */ - U32 Reserved3; /* 0x10 */ - U32 Reserved4; /* 0x14 */ -} MPI2_IOUNIT8_SENSOR, MPI2_POINTER PTR_MPI2_IOUNIT8_SENSOR, -Mpi2IOUnit8Sensor_t, MPI2_POINTER pMpi2IOUnit8Sensor_t; - -/* defines for IO Unit Page 8 Sensor Flags field */ -#define MPI2_IOUNIT8_SENSOR_FLAGS_T3_ENABLE (0x0008) -#define MPI2_IOUNIT8_SENSOR_FLAGS_T2_ENABLE (0x0004) -#define MPI2_IOUNIT8_SENSOR_FLAGS_T1_ENABLE (0x0002) -#define MPI2_IOUNIT8_SENSOR_FLAGS_T0_ENABLE (0x0001) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumSensors at runtime. - */ -#ifndef MPI2_IOUNITPAGE8_SENSOR_ENTRIES -#define MPI2_IOUNITPAGE8_SENSOR_ENTRIES (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_8 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U8 NumSensors; /* 0x0C */ - U8 PollingInterval; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - MPI2_IOUNIT8_SENSOR - Sensor[MPI2_IOUNITPAGE8_SENSOR_ENTRIES];/* 0x10 */ -} MPI2_CONFIG_PAGE_IO_UNIT_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_8, -Mpi2IOUnitPage8_t, MPI2_POINTER pMpi2IOUnitPage8_t; - -#define MPI2_IOUNITPAGE8_PAGEVERSION (0x00) - - -/* IO Unit Page 9 */ - -typedef struct _MPI2_IOUNIT9_SENSOR { - U16 CurrentTemperature; /* 0x00 */ - U16 Reserved1; /* 0x02 */ - U8 Flags; /* 0x04 */ - U8 Reserved2; /* 0x05 */ - U16 Reserved3; /* 0x06 */ - U32 Reserved4; /* 0x08 */ - U32 Reserved5; /* 0x0C */ -} MPI2_IOUNIT9_SENSOR, MPI2_POINTER PTR_MPI2_IOUNIT9_SENSOR, -Mpi2IOUnit9Sensor_t, MPI2_POINTER pMpi2IOUnit9Sensor_t; - -/* defines for IO Unit Page 9 Sensor Flags field */ -#define MPI2_IOUNIT9_SENSOR_FLAGS_TEMP_VALID (0x01) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumSensors at runtime. - */ -#ifndef MPI2_IOUNITPAGE9_SENSOR_ENTRIES -#define MPI2_IOUNITPAGE9_SENSOR_ENTRIES (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_9 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U8 NumSensors; /* 0x0C */ - U8 Reserved4; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - MPI2_IOUNIT9_SENSOR - Sensor[MPI2_IOUNITPAGE9_SENSOR_ENTRIES];/* 0x10 */ -} MPI2_CONFIG_PAGE_IO_UNIT_9, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_9, -Mpi2IOUnitPage9_t, MPI2_POINTER pMpi2IOUnitPage9_t; - -#define MPI2_IOUNITPAGE9_PAGEVERSION (0x00) - - -/* IO Unit Page 10 */ - -typedef struct _MPI2_IOUNIT10_FUNCTION { - U8 CreditPercent; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ -} MPI2_IOUNIT10_FUNCTION, MPI2_POINTER PTR_MPI2_IOUNIT10_FUNCTION, -Mpi2IOUnit10Function_t, MPI2_POINTER pMpi2IOUnit10Function_t; - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumFunctions at runtime. - */ -#ifndef MPI2_IOUNITPAGE10_FUNCTION_ENTRIES -#define MPI2_IOUNITPAGE10_FUNCTION_ENTRIES (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_10 { - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 NumFunctions; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U32 Reserved3; /* 0x08 */ - U32 Reserved4; /* 0x0C */ - MPI2_IOUNIT10_FUNCTION - Function[MPI2_IOUNITPAGE10_FUNCTION_ENTRIES];/* 0x10 */ -} MPI2_CONFIG_PAGE_IO_UNIT_10, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_10, -Mpi2IOUnitPage10_t, MPI2_POINTER pMpi2IOUnitPage10_t; - -#define MPI2_IOUNITPAGE10_PAGEVERSION (0x01) - - - -/**************************************************************************** -* IOC Config Pages -****************************************************************************/ - -/* IOC Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_IOC_0 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U16 VendorID; /* 0x0C */ - U16 DeviceID; /* 0x0E */ - U8 RevisionID; /* 0x10 */ - U8 Reserved3; /* 0x11 */ - U16 Reserved4; /* 0x12 */ - U32 ClassCode; /* 0x14 */ - U16 SubsystemVendorID; /* 0x18 */ - U16 SubsystemID; /* 0x1A */ -} MPI2_CONFIG_PAGE_IOC_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_0, - Mpi2IOCPage0_t, MPI2_POINTER pMpi2IOCPage0_t; - -#define MPI2_IOCPAGE0_PAGEVERSION (0x02) - - -/* IOC Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_IOC_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Flags; /* 0x04 */ - U32 CoalescingTimeout; /* 0x08 */ - U8 CoalescingDepth; /* 0x0C */ - U8 PCISlotNum; /* 0x0D */ - U8 PCIBusNum; /* 0x0E */ - U8 PCIDomainSegment; /* 0x0F */ - U32 Reserved1; /* 0x10 */ - U32 Reserved2; /* 0x14 */ -} MPI2_CONFIG_PAGE_IOC_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_1, - Mpi2IOCPage1_t, MPI2_POINTER pMpi2IOCPage1_t; - -#define MPI2_IOCPAGE1_PAGEVERSION (0x05) - -/* defines for IOC Page 1 Flags field */ -#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001) - -#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) -#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF) -#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF) - -/* IOC Page 6 */ - -typedef struct _MPI2_CONFIG_PAGE_IOC_6 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 CapabilitiesFlags; /* 0x04 */ - U8 MaxDrivesRAID0; /* 0x08 */ - U8 MaxDrivesRAID1; /* 0x09 */ - U8 MaxDrivesRAID1E; /* 0x0A */ - U8 MaxDrivesRAID10; /* 0x0B */ - U8 MinDrivesRAID0; /* 0x0C */ - U8 MinDrivesRAID1; /* 0x0D */ - U8 MinDrivesRAID1E; /* 0x0E */ - U8 MinDrivesRAID10; /* 0x0F */ - U32 Reserved1; /* 0x10 */ - U8 MaxGlobalHotSpares; /* 0x14 */ - U8 MaxPhysDisks; /* 0x15 */ - U8 MaxVolumes; /* 0x16 */ - U8 MaxConfigs; /* 0x17 */ - U8 MaxOCEDisks; /* 0x18 */ - U8 Reserved2; /* 0x19 */ - U16 Reserved3; /* 0x1A */ - U32 SupportedStripeSizeMapRAID0; /* 0x1C */ - U32 SupportedStripeSizeMapRAID1E; /* 0x20 */ - U32 SupportedStripeSizeMapRAID10; /* 0x24 */ - U32 Reserved4; /* 0x28 */ - U32 Reserved5; /* 0x2C */ - U16 DefaultMetadataSize; /* 0x30 */ - U16 Reserved6; /* 0x32 */ - U16 MaxBadBlockTableEntries; /* 0x34 */ - U16 Reserved7; /* 0x36 */ - U32 IRNvsramVersion; /* 0x38 */ -} MPI2_CONFIG_PAGE_IOC_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_6, - Mpi2IOCPage6_t, MPI2_POINTER pMpi2IOCPage6_t; - -#define MPI2_IOCPAGE6_PAGEVERSION (0x05) - -/* defines for IOC Page 6 CapabilitiesFlags */ -#define MPI2_IOCPAGE6_CAP_FLAGS_4K_SECTORS_SUPPORT (0x00000020) -#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010) -#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008) -#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004) -#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002) -#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001) - - -/* IOC Page 7 */ - -#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4) - -typedef struct _MPI2_CONFIG_PAGE_IOC_7 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/* 0x08 */ - U16 SASBroadcastPrimitiveMasks; /* 0x18 */ - U16 SASNotifyPrimitiveMasks; /* 0x1A */ - U32 Reserved3; /* 0x1C */ -} MPI2_CONFIG_PAGE_IOC_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_7, - Mpi2IOCPage7_t, MPI2_POINTER pMpi2IOCPage7_t; - -#define MPI2_IOCPAGE7_PAGEVERSION (0x02) - - -/* IOC Page 8 */ - -typedef struct _MPI2_CONFIG_PAGE_IOC_8 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 NumDevsPerEnclosure; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U16 MaxPersistentEntries; /* 0x08 */ - U16 MaxNumPhysicalMappedIDs; /* 0x0A */ - U16 Flags; /* 0x0C */ - U16 Reserved3; /* 0x0E */ - U16 IRVolumeMappingFlags; /* 0x10 */ - U16 Reserved4; /* 0x12 */ - U32 Reserved5; /* 0x14 */ -} MPI2_CONFIG_PAGE_IOC_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_8, - Mpi2IOCPage8_t, MPI2_POINTER pMpi2IOCPage8_t; - -#define MPI2_IOCPAGE8_PAGEVERSION (0x00) - -/* defines for IOC Page 8 Flags field */ -#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020) -#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010) - -#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E) -#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000) -#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002) - -#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001) -#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000) - -/* defines for IOC Page 8 IRVolumeMappingFlags */ -#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003) -#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000) -#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001) - - -/**************************************************************************** -* BIOS Config Pages -****************************************************************************/ - -/* BIOS Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_BIOS_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 BiosOptions; /* 0x04 */ - U32 IOCSettings; /* 0x08 */ - U8 SSUTimeout; /* 0x0C */ - U8 Reserved1; /* 0x0D */ - U16 Reserved2; /* 0x0E */ - U32 DeviceSettings; /* 0x10 */ - U16 NumberOfDevices; /* 0x14 */ - U16 UEFIVersion; /* 0x16 */ - U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */ - U16 IOTimeoutSequential; /* 0x1A */ - U16 IOTimeoutOther; /* 0x1C */ - U16 IOTimeoutBlockDevicesRM; /* 0x1E */ -} MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1, - Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t; - -#define MPI2_BIOSPAGE1_PAGEVERSION (0x07) - -/* values for BIOS Page 1 BiosOptions field */ -#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) -#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000) -#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800) -#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000) -#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800) -#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000) - -#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) - -#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) -#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) -#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) -#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) -#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) - -#define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) -#define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) - -#define MPI2_BIOSPAGE1_OPTIONS_MASK_UEFI_HII_REGISTRATION (0x00000006) -#define MPI2_BIOSPAGE1_OPTIONS_ENABLE_UEFI_HII (0x00000000) -#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_UEFI_HII (0x00000002) -#define MPI2_BIOSPAGE1_OPTIONS_VERSION_CHECK_UEFI_HII (0x00000004) - -#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) - -/* values for BIOS Page 1 IOCSettings field */ -#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000) -#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000) -#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000) - -#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0) -#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000) -#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040) -#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080) - -#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030) -#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000) -#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010) -#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020) -#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030) - -#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008) - -/* values for BIOS Page 1 DeviceSettings field */ -#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010) -#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008) -#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004) -#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) -#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) - -/* defines for BIOS Page 1 UEFIVersion field */ -#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_MASK (0xFF00) -#define MPI2_BIOSPAGE1_UEFI_VER_MAJOR_SHIFT (8) -#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_MASK (0x00FF) -#define MPI2_BIOSPAGE1_UEFI_VER_MINOR_SHIFT (0) - - - -/* BIOS Page 2 */ - -typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER -{ - U32 Reserved1; /* 0x00 */ - U32 Reserved2; /* 0x04 */ - U32 Reserved3; /* 0x08 */ - U32 Reserved4; /* 0x0C */ - U32 Reserved5; /* 0x10 */ - U32 Reserved6; /* 0x14 */ -} MPI2_BOOT_DEVICE_ADAPTER_ORDER, - MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER, - Mpi2BootDeviceAdapterOrder_t, MPI2_POINTER pMpi2BootDeviceAdapterOrder_t; - -typedef struct _MPI2_BOOT_DEVICE_SAS_WWID -{ - U64 SASAddress; /* 0x00 */ - U8 LUN[8]; /* 0x08 */ - U32 Reserved1; /* 0x10 */ - U32 Reserved2; /* 0x14 */ -} MPI2_BOOT_DEVICE_SAS_WWID, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_SAS_WWID, - Mpi2BootDeviceSasWwid_t, MPI2_POINTER pMpi2BootDeviceSasWwid_t; - -typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT -{ - U64 EnclosureLogicalID; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 Reserved2; /* 0x0C */ - U16 SlotNumber; /* 0x10 */ - U16 Reserved3; /* 0x12 */ - U32 Reserved4; /* 0x14 */ -} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, - MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, - Mpi2BootDeviceEnclosureSlot_t, MPI2_POINTER pMpi2BootDeviceEnclosureSlot_t; - -typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME -{ - U64 DeviceName; /* 0x00 */ - U8 LUN[8]; /* 0x08 */ - U32 Reserved1; /* 0x10 */ - U32 Reserved2; /* 0x14 */ -} MPI2_BOOT_DEVICE_DEVICE_NAME, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_DEVICE_NAME, - Mpi2BootDeviceDeviceName_t, MPI2_POINTER pMpi2BootDeviceDeviceName_t; - -typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE -{ - MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder; - MPI2_BOOT_DEVICE_SAS_WWID SasWwid; - MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot; - MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName; -} MPI2_BIOSPAGE2_BOOT_DEVICE, MPI2_POINTER PTR_MPI2_BIOSPAGE2_BOOT_DEVICE, - Mpi2BiosPage2BootDevice_t, MPI2_POINTER pMpi2BiosPage2BootDevice_t; - -typedef struct _MPI2_CONFIG_PAGE_BIOS_2 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U32 Reserved3; /* 0x0C */ - U32 Reserved4; /* 0x10 */ - U32 Reserved5; /* 0x14 */ - U32 Reserved6; /* 0x18 */ - U8 ReqBootDeviceForm; /* 0x1C */ - U8 Reserved7; /* 0x1D */ - U16 Reserved8; /* 0x1E */ - MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /* 0x20 */ - U8 ReqAltBootDeviceForm; /* 0x38 */ - U8 Reserved9; /* 0x39 */ - U16 Reserved10; /* 0x3A */ - MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /* 0x3C */ - U8 CurrentBootDeviceForm; /* 0x58 */ - U8 Reserved11; /* 0x59 */ - U16 Reserved12; /* 0x5A */ - MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /* 0x58 */ -} MPI2_CONFIG_PAGE_BIOS_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_2, - Mpi2BiosPage2_t, MPI2_POINTER pMpi2BiosPage2_t; - -#define MPI2_BIOSPAGE2_PAGEVERSION (0x04) - -/* values for BIOS Page 2 BootDeviceForm fields */ -#define MPI2_BIOSPAGE2_FORM_MASK (0x0F) -#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00) -#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05) -#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06) -#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07) - - -/* BIOS Page 3 */ - -typedef struct _MPI2_ADAPTER_INFO -{ - U8 PciBusNumber; /* 0x00 */ - U8 PciDeviceAndFunctionNumber; /* 0x01 */ - U16 AdapterFlags; /* 0x02 */ -} MPI2_ADAPTER_INFO, MPI2_POINTER PTR_MPI2_ADAPTER_INFO, - Mpi2AdapterInfo_t, MPI2_POINTER pMpi2AdapterInfo_t; - -#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) -#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) - -typedef struct _MPI2_CONFIG_PAGE_BIOS_3 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U32 GlobalFlags; /* 0x04 */ - U32 BiosVersion; /* 0x08 */ - MPI2_ADAPTER_INFO AdapterOrder[4]; /* 0x0C */ - U32 Reserved1; /* 0x1C */ -} MPI2_CONFIG_PAGE_BIOS_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_3, - Mpi2BiosPage3_t, MPI2_POINTER pMpi2BiosPage3_t; - -#define MPI2_BIOSPAGE3_PAGEVERSION (0x00) - -/* values for BIOS Page 3 GlobalFlags */ -#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002) -#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004) -#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010) - -#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0) -#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000) -#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020) -#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040) - - -/* BIOS Page 4 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES -#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) -#endif - -typedef struct _MPI2_BIOS4_ENTRY -{ - U64 ReassignmentWWID; /* 0x00 */ - U64 ReassignmentDeviceName; /* 0x08 */ -} MPI2_BIOS4_ENTRY, MPI2_POINTER PTR_MPI2_BIOS4_ENTRY, - Mpi2MBios4Entry_t, MPI2_POINTER pMpi2Bios4Entry_t; - -typedef struct _MPI2_CONFIG_PAGE_BIOS_4 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 NumPhys; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - MPI2_BIOS4_ENTRY Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /* 0x08 */ -} MPI2_CONFIG_PAGE_BIOS_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_4, - Mpi2BiosPage4_t, MPI2_POINTER pMpi2BiosPage4_t; - -#define MPI2_BIOSPAGE4_PAGEVERSION (0x01) - - -/**************************************************************************** -* RAID Volume Config Pages -****************************************************************************/ - -/* RAID Volume Page 0 */ - -typedef struct _MPI2_RAIDVOL0_PHYS_DISK -{ - U8 RAIDSetNum; /* 0x00 */ - U8 PhysDiskMap; /* 0x01 */ - U8 PhysDiskNum; /* 0x02 */ - U8 Reserved; /* 0x03 */ -} MPI2_RAIDVOL0_PHYS_DISK, MPI2_POINTER PTR_MPI2_RAIDVOL0_PHYS_DISK, - Mpi2RaidVol0PhysDisk_t, MPI2_POINTER pMpi2RaidVol0PhysDisk_t; - -/* defines for the PhysDiskMap field */ -#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01) -#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02) - -typedef struct _MPI2_RAIDVOL0_SETTINGS -{ - U16 Settings; /* 0x00 */ - U8 HotSparePool; /* 0x01 */ - U8 Reserved; /* 0x02 */ -} MPI2_RAIDVOL0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDVOL0_SETTINGS, - Mpi2RaidVol0Settings_t, MPI2_POINTER pMpi2RaidVol0Settings_t; - -/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */ -#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01) -#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02) -#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04) -#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08) -#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10) -#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20) -#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40) -#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80) - -/* RAID Volume Page 0 VolumeSettings defines */ -#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008) -#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004) - -#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003) -#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000) -#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001) -#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhysDisks at runtime. - */ -#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX -#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U16 DevHandle; /* 0x04 */ - U8 VolumeState; /* 0x06 */ - U8 VolumeType; /* 0x07 */ - U32 VolumeStatusFlags; /* 0x08 */ - MPI2_RAIDVOL0_SETTINGS VolumeSettings; /* 0x0C */ - U64 MaxLBA; /* 0x10 */ - U32 StripeSize; /* 0x18 */ - U16 BlockSize; /* 0x1C */ - U16 Reserved1; /* 0x1E */ - U8 SupportedPhysDisks; /* 0x20 */ - U8 ResyncRate; /* 0x21 */ - U16 DataScrubDuration; /* 0x22 */ - U8 NumPhysDisks; /* 0x24 */ - U8 Reserved2; /* 0x25 */ - U8 Reserved3; /* 0x26 */ - U8 InactiveStatus; /* 0x27 */ - MPI2_RAIDVOL0_PHYS_DISK PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /* 0x28 */ -} MPI2_CONFIG_PAGE_RAID_VOL_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_0, - Mpi2RaidVolPage0_t, MPI2_POINTER pMpi2RaidVolPage0_t; - -#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A) - -/* values for RAID VolumeState */ -#define MPI2_RAID_VOL_STATE_MISSING (0x00) -#define MPI2_RAID_VOL_STATE_FAILED (0x01) -#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02) -#define MPI2_RAID_VOL_STATE_ONLINE (0x03) -#define MPI2_RAID_VOL_STATE_DEGRADED (0x04) -#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05) - -/* values for RAID VolumeType */ -#define MPI2_RAID_VOL_TYPE_RAID0 (0x00) -#define MPI2_RAID_VOL_TYPE_RAID1E (0x01) -#define MPI2_RAID_VOL_TYPE_RAID1 (0x02) -#define MPI2_RAID_VOL_TYPE_RAID10 (0x05) -#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF) - -/* values for RAID Volume Page 0 VolumeStatusFlags field */ -#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000) -#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000) -#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000) -#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000) -#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000) -#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000) -#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000) -#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000) -#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000) -#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000) -#define MPI2_RAIDVOL0_STATUS_FLAG_VOL_NOT_CONSISTENT (0x00000080) -#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040) -#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020) -#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000) -#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010) -#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008) -#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004) -#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002) -#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001) - -/* values for RAID Volume Page 0 SupportedPhysDisks field */ -#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08) -#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04) -#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02) -#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01) - -/* values for RAID Volume Page 0 InactiveStatus field */ -#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00) -#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01) -#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02) -#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03) -#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04) -#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05) -#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06) - - -/* RAID Volume Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U16 DevHandle; /* 0x04 */ - U16 Reserved0; /* 0x06 */ - U8 GUID[24]; /* 0x08 */ - U8 Name[16]; /* 0x20 */ - U64 WWID; /* 0x30 */ - U32 Reserved1; /* 0x38 */ - U32 Reserved2; /* 0x3C */ -} MPI2_CONFIG_PAGE_RAID_VOL_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_1, - Mpi2RaidVolPage1_t, MPI2_POINTER pMpi2RaidVolPage1_t; - -#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03) - - -/**************************************************************************** -* RAID Physical Disk Config Pages -****************************************************************************/ - -/* RAID Physical Disk Page 0 */ - -typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS -{ - U16 Reserved1; /* 0x00 */ - U8 HotSparePool; /* 0x02 */ - U8 Reserved2; /* 0x03 */ -} MPI2_RAIDPHYSDISK0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_SETTINGS, - Mpi2RaidPhysDisk0Settings_t, MPI2_POINTER pMpi2RaidPhysDisk0Settings_t; - -/* use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */ - -typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA -{ - U8 VendorID[8]; /* 0x00 */ - U8 ProductID[16]; /* 0x08 */ - U8 ProductRevLevel[4]; /* 0x18 */ - U8 SerialNum[32]; /* 0x1C */ -} MPI2_RAIDPHYSDISK0_INQUIRY_DATA, - MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA, - Mpi2RaidPhysDisk0InquiryData_t, MPI2_POINTER pMpi2RaidPhysDisk0InquiryData_t; - -typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U16 DevHandle; /* 0x04 */ - U8 Reserved1; /* 0x06 */ - U8 PhysDiskNum; /* 0x07 */ - MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /* 0x08 */ - U32 Reserved2; /* 0x0C */ - MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /* 0x10 */ - U32 Reserved3; /* 0x4C */ - U8 PhysDiskState; /* 0x50 */ - U8 OfflineReason; /* 0x51 */ - U8 IncompatibleReason; /* 0x52 */ - U8 PhysDiskAttributes; /* 0x53 */ - U32 PhysDiskStatusFlags; /* 0x54 */ - U64 DeviceMaxLBA; /* 0x58 */ - U64 HostMaxLBA; /* 0x60 */ - U64 CoercedMaxLBA; /* 0x68 */ - U16 BlockSize; /* 0x70 */ - U16 Reserved5; /* 0x72 */ - U32 Reserved6; /* 0x74 */ -} MPI2_CONFIG_PAGE_RD_PDISK_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_0, - Mpi2RaidPhysDiskPage0_t, MPI2_POINTER pMpi2RaidPhysDiskPage0_t; - -#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05) - -/* PhysDiskState defines */ -#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00) -#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01) -#define MPI2_RAID_PD_STATE_OFFLINE (0x02) -#define MPI2_RAID_PD_STATE_ONLINE (0x03) -#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04) -#define MPI2_RAID_PD_STATE_DEGRADED (0x05) -#define MPI2_RAID_PD_STATE_REBUILDING (0x06) -#define MPI2_RAID_PD_STATE_OPTIMAL (0x07) - -/* OfflineReason defines */ -#define MPI2_PHYSDISK0_ONLINE (0x00) -#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01) -#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03) -#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04) -#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05) -#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06) -#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF) - -/* IncompatibleReason defines */ -#define MPI2_PHYSDISK0_COMPATIBLE (0x00) -#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01) -#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02) -#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03) -#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04) -#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05) -#define MPI2_PHYSDISK0_INCOMPATIBLE_MEDIA_TYPE (0x06) -#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF) - -/* PhysDiskAttributes defines */ -#define MPI2_PHYSDISK0_ATTRIB_MEDIA_MASK (0x0C) -#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08) -#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04) - -#define MPI2_PHYSDISK0_ATTRIB_PROTOCOL_MASK (0x03) -#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02) -#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01) - -/* PhysDiskStatusFlags defines */ -#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040) -#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020) -#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010) -#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000) -#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008) -#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004) -#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002) -#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001) - - -/* RAID Physical Disk Page 1 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhysDiskPaths at runtime. - */ -#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX -#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) -#endif - -typedef struct _MPI2_RAIDPHYSDISK1_PATH -{ - U16 DevHandle; /* 0x00 */ - U16 Reserved1; /* 0x02 */ - U64 WWID; /* 0x04 */ - U64 OwnerWWID; /* 0x0C */ - U8 OwnerIdentifier; /* 0x14 */ - U8 Reserved2; /* 0x15 */ - U16 Flags; /* 0x16 */ -} MPI2_RAIDPHYSDISK1_PATH, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK1_PATH, - Mpi2RaidPhysDisk1Path_t, MPI2_POINTER pMpi2RaidPhysDisk1Path_t; - -/* RAID Physical Disk Page 1 Physical Disk Path Flags field defines */ -#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004) -#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002) -#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001) - -typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 -{ - MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ - U8 NumPhysDiskPaths; /* 0x04 */ - U8 PhysDiskNum; /* 0x05 */ - U16 Reserved1; /* 0x06 */ - U32 Reserved2; /* 0x08 */ - MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/* 0x0C */ -} MPI2_CONFIG_PAGE_RD_PDISK_1, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_1, - Mpi2RaidPhysDiskPage1_t, MPI2_POINTER pMpi2RaidPhysDiskPage1_t; - -#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02) - - -/**************************************************************************** -* values for fields used by several types of SAS Config Pages -****************************************************************************/ - -/* values for NegotiatedLinkRates fields */ -#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0) -#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4) -#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F) -/* link rates used for Negotiated Physical and Logical Link Rate */ -#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00) -#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01) -#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02) -#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) -#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) -#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) -#define MPI2_SAS_NEG_LINK_RATE_UNSUPPORTED_PHY (0x06) -#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08) -#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) -#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) - - -/* values for AttachedPhyInfo fields */ -#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040) -#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020) -#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010) - -#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F) -#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000) -#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001) -#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002) -#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003) -#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004) -#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005) -#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006) -#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007) -#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008) - - -/* values for PhyInfo fields */ -#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) - -#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK (0x18000000) -#define MPI2_SAS_PHYINFO_SHIFT_PHY_POWER_CONDITION (27) -#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE (0x00000000) -#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL (0x08000000) -#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER (0x10000000) - -#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000) -#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000) -#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000) -#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000) -#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000) -#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000) - -#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000) -#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000) -#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000) -#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000) -#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000) -#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000) -#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000) -#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000) -#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000) -#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000) - -#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000) -#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000) -#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000) -#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000) - -#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00) -#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8) - -#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0) -#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000) -#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010) -#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020) - - -/* values for SAS ProgrammedLinkRate fields */ -#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0) -#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) -#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80) -#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90) -#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0) -#define MPI25_SAS_PRATE_MAX_RATE_12_0 (0xB0) -#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F) -#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) -#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08) -#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09) -#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A) - - -/* values for SAS HwLinkRate fields */ -#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0) -#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80) -#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90) -#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0) -#define MPI25_SAS_HWRATE_MAX_RATE_12_0 (0xB0) -#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F) -#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08) -#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09) -#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A) - - - -/**************************************************************************** -* SAS IO Unit Config Pages -****************************************************************************/ - -/* SAS IO Unit Page 0 */ - -typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA -{ - U8 Port; /* 0x00 */ - U8 PortFlags; /* 0x01 */ - U8 PhyFlags; /* 0x02 */ - U8 NegotiatedLinkRate; /* 0x03 */ - U32 ControllerPhyDeviceInfo;/* 0x04 */ - U16 AttachedDevHandle; /* 0x08 */ - U16 ControllerDevHandle; /* 0x0A */ - U32 DiscoveryStatus; /* 0x0C */ - U32 Reserved; /* 0x10 */ -} MPI2_SAS_IO_UNIT0_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT0_PHY_DATA, - Mpi2SasIOUnit0PhyData_t, MPI2_POINTER pMpi2SasIOUnit0PhyData_t; - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_SAS_IOUNIT0_PHY_MAX -#define MPI2_SAS_IOUNIT0_PHY_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U8 NumPhys; /* 0x0C */ - U8 Reserved2; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /* 0x10 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0, - Mpi2SasIOUnitPage0_t, MPI2_POINTER pMpi2SasIOUnitPage0_t; - -#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05) - -/* values for SAS IO Unit Page 0 PortFlags */ -#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08) -#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01) - -/* values for SAS IO Unit Page 0 PhyFlags */ -#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10) -#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) - -/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ - -/* see mpi2_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */ - -/* values for SAS IO Unit Page 0 DiscoveryStatus */ -#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) -#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000) -#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000) -#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) -#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000) -#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) -#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) -#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000) -#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) -#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800) -#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400) -#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200) -#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100) -#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080) -#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040) -#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020) -#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010) -#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004) -#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002) -#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001) - - -/* SAS IO Unit Page 1 */ - -typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA -{ - U8 Port; /* 0x00 */ - U8 PortFlags; /* 0x01 */ - U8 PhyFlags; /* 0x02 */ - U8 MaxMinLinkRate; /* 0x03 */ - U32 ControllerPhyDeviceInfo; /* 0x04 */ - U16 MaxTargetPortConnectTime; /* 0x08 */ - U16 Reserved1; /* 0x0A */ -} MPI2_SAS_IO_UNIT1_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT1_PHY_DATA, - Mpi2SasIOUnit1PhyData_t, MPI2_POINTER pMpi2SasIOUnit1PhyData_t; - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_SAS_IOUNIT1_PHY_MAX -#define MPI2_SAS_IOUNIT1_PHY_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U16 ControlFlags; /* 0x08 */ - U16 SASNarrowMaxQueueDepth; /* 0x0A */ - U16 AdditionalControlFlags; /* 0x0C */ - U16 SASWideMaxQueueDepth; /* 0x0E */ - U8 NumPhys; /* 0x10 */ - U8 SATAMaxQDepth; /* 0x11 */ - U8 ReportDeviceMissingDelay; /* 0x12 */ - U8 IODeviceMissingDelay; /* 0x13 */ - MPI2_SAS_IO_UNIT1_PHY_DATA PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /* 0x14 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_1, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1, - Mpi2SasIOUnitPage1_t, MPI2_POINTER pMpi2SasIOUnitPage1_t; - -#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09) - -/* values for SAS IO Unit Page 1 ControlFlags */ -#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000) -#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000) -#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000) -#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000) - -#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600) -#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9) -#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0) -#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1) -#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2) - -#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080) -#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040) -#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020) -#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010) -#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008) -#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004) -#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002) -#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001) - -/* values for SAS IO Unit Page 1 AdditionalControlFlags */ -#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) -#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) -#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020) -#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010) -#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008) -#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004) -#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002) -#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001) - -/* defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */ -#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F) -#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80) - -/* values for SAS IO Unit Page 1 PortFlags */ -#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) - -/* values for SAS IO Unit Page 1 PhyFlags */ -#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10) -#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) - -/* values for SAS IO Unit Page 1 MaxMinLinkRate */ -#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0) -#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80) -#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) -#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0) -#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F) -#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08) -#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09) -#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A) - -/* see mpi2_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ - - -/* SAS IO Unit Page 4 */ - -typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP -{ - U8 MaxTargetSpinup; /* 0x00 */ - U8 SpinupDelay; /* 0x01 */ - U8 SpinupFlags; /* 0x02 */ - U8 Reserved1; /* 0x03 */ -} MPI2_SAS_IOUNIT4_SPINUP_GROUP, MPI2_POINTER PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP, - Mpi2SasIOUnit4SpinupGroup_t, MPI2_POINTER pMpi2SasIOUnit4SpinupGroup_t; - -/* defines for SAS IO Unit Page 4 SpinupFlags */ -#define MPI2_SASIOUNIT4_SPINUP_DISABLE_FLAG (0x01) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_SAS_IOUNIT4_PHY_MAX -#define MPI2_SAS_IOUNIT4_PHY_MAX (4) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - MPI2_SAS_IOUNIT4_SPINUP_GROUP SpinupGroupParameters[4]; /* 0x08 */ - U32 Reserved1; /* 0x18 */ - U32 Reserved2; /* 0x1C */ - U32 Reserved3; /* 0x20 */ - U8 BootDeviceWaitTime; /* 0x24 */ - U8 Reserved4; /* 0x25 */ - U16 Reserved5; /* 0x26 */ - U8 NumPhys; /* 0x28 */ - U8 PEInitialSpinupDelay; /* 0x29 */ - U8 PEReplyDelay; /* 0x2A */ - U8 Flags; /* 0x2B */ - U8 PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /* 0x2C */ -} MPI2_CONFIG_PAGE_SASIOUNIT_4, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4, - Mpi2SasIOUnitPage4_t, MPI2_POINTER pMpi2SasIOUnitPage4_t; - -#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02) - -/* defines for Flags field */ -#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01) - -/* defines for PHY field */ -#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03) - - -/* SAS IO Unit Page 5 */ - -typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS { - U8 ControlFlags; /* 0x00 */ - U8 PortWidthModGroup; /* 0x01 */ - U16 InactivityTimerExponent; /* 0x02 */ - U8 SATAPartialTimeout; /* 0x04 */ - U8 Reserved2; /* 0x05 */ - U8 SATASlumberTimeout; /* 0x06 */ - U8 Reserved3; /* 0x07 */ - U8 SASPartialTimeout; /* 0x08 */ - U8 Reserved4; /* 0x09 */ - U8 SASSlumberTimeout; /* 0x0A */ - U8 Reserved5; /* 0x0B */ -} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, - MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS, - Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER pMpi2SasIOUnit5PhyPmSettings_t; - -/* defines for ControlFlags field */ -#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE (0x08) -#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE (0x04) -#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE (0x02) -#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE (0x01) - -/* defines for PortWidthModeGroup field */ -#define MPI2_SASIOUNIT5_PWMG_DISABLE (0xFF) - -/* defines for InactivityTimerExponent field */ -#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER (0x7000) -#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER (12) -#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL (0x0700) -#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL (8) -#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER (0x0070) -#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER (4) -#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL (0x0007) -#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL (0) - -#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS (7) -#define MPI2_SASIOUNIT5_ITE_ONE_SECOND (6) -#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS (5) -#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS (4) -#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND (3) -#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS (2) -#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS (1) -#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND (0) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhys at runtime. - */ -#ifndef MPI2_SAS_IOUNIT5_PHY_MAX -#define MPI2_SAS_IOUNIT5_PHY_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 NumPhys; /* 0x08 */ - U8 Reserved1; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U32 Reserved3; /* 0x0C */ - MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS SASPhyPowerManagementSettings - [MPI2_SAS_IOUNIT5_PHY_MAX]; /* 0x10 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_5, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5, - Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t; - -#define MPI2_SASIOUNITPAGE5_PAGEVERSION (0x01) - - -/* SAS IO Unit Page 6 */ - -typedef struct _MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS { - U8 CurrentStatus; /* 0x00 */ - U8 CurrentModulation; /* 0x01 */ - U8 CurrentUtilization; /* 0x02 */ - U8 Reserved1; /* 0x03 */ - U32 Reserved2; /* 0x04 */ -} MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, - MPI2_POINTER PTR_MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS, - Mpi2SasIOUnit6PortWidthModGroupStatus_t, - MPI2_POINTER pMpi2SasIOUnit6PortWidthModGroupStatus_t; - -/* defines for CurrentStatus field */ -#define MPI2_SASIOUNIT6_STATUS_UNAVAILABLE (0x00) -#define MPI2_SASIOUNIT6_STATUS_UNCONFIGURED (0x01) -#define MPI2_SASIOUNIT6_STATUS_INVALID_CONFIG (0x02) -#define MPI2_SASIOUNIT6_STATUS_LINK_DOWN (0x03) -#define MPI2_SASIOUNIT6_STATUS_OBSERVATION_ONLY (0x04) -#define MPI2_SASIOUNIT6_STATUS_INACTIVE (0x05) -#define MPI2_SASIOUNIT6_STATUS_ACTIVE_IOUNIT (0x06) -#define MPI2_SASIOUNIT6_STATUS_ACTIVE_HOST (0x07) - -/* defines for CurrentModulation field */ -#define MPI2_SASIOUNIT6_MODULATION_25_PERCENT (0x00) -#define MPI2_SASIOUNIT6_MODULATION_50_PERCENT (0x01) -#define MPI2_SASIOUNIT6_MODULATION_75_PERCENT (0x02) -#define MPI2_SASIOUNIT6_MODULATION_100_PERCENT (0x03) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumGroups at runtime. - */ -#ifndef MPI2_SAS_IOUNIT6_GROUP_MAX -#define MPI2_SAS_IOUNIT6_GROUP_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_6 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 Reserved2; /* 0x0C */ - U8 NumGroups; /* 0x10 */ - U8 Reserved3; /* 0x11 */ - U16 Reserved4; /* 0x12 */ - MPI2_SAS_IO_UNIT6_PORT_WIDTH_MOD_GROUP_STATUS - PortWidthModulationGroupStatus[MPI2_SAS_IOUNIT6_GROUP_MAX]; /* 0x14 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_6, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_6, - Mpi2SasIOUnitPage6_t, MPI2_POINTER pMpi2SasIOUnitPage6_t; - -#define MPI2_SASIOUNITPAGE6_PAGEVERSION (0x00) - - -/* SAS IO Unit Page 7 */ - -typedef struct _MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS { - U8 Flags; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U8 Threshold75Pct; /* 0x04 */ - U8 Threshold50Pct; /* 0x05 */ - U8 Threshold25Pct; /* 0x06 */ - U8 Reserved3; /* 0x07 */ -} MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, - MPI2_POINTER PTR_MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS, - Mpi2SasIOUnit7PortWidthModGroupSettings_t, - MPI2_POINTER pMpi2SasIOUnit7PortWidthModGroupSettings_t; - -/* defines for Flags field */ -#define MPI2_SASIOUNIT7_FLAGS_ENABLE_PORT_WIDTH_MODULATION (0x01) - - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumGroups at runtime. - */ -#ifndef MPI2_SAS_IOUNIT7_GROUP_MAX -#define MPI2_SAS_IOUNIT7_GROUP_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_7 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 SamplingInterval; /* 0x08 */ - U8 WindowLength; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U32 Reserved2; /* 0x0C */ - U32 Reserved3; /* 0x10 */ - U8 NumGroups; /* 0x14 */ - U8 Reserved4; /* 0x15 */ - U16 Reserved5; /* 0x16 */ - MPI2_SAS_IO_UNIT7_PORT_WIDTH_MOD_GROUP_SETTINGS - PortWidthModulationGroupSettings[MPI2_SAS_IOUNIT7_GROUP_MAX]; /* 0x18 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_7, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_7, - Mpi2SasIOUnitPage7_t, MPI2_POINTER pMpi2SasIOUnitPage7_t; - -#define MPI2_SASIOUNITPAGE7_PAGEVERSION (0x00) - - -/* SAS IO Unit Page 8 */ - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_8 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 PowerManagementCapabilities;/* 0x0C */ - U32 Reserved2; /* 0x10 */ -} MPI2_CONFIG_PAGE_SASIOUNIT_8, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_8, - Mpi2SasIOUnitPage8_t, MPI2_POINTER pMpi2SasIOUnitPage8_t; - -#define MPI2_SASIOUNITPAGE8_PAGEVERSION (0x00) - -/* defines for PowerManagementCapabilities field */ -#define MPI2_SASIOUNIT8_PM_HOST_PORT_WIDTH_MOD (0x00001000) -#define MPI2_SASIOUNIT8_PM_HOST_SAS_SLUMBER_MODE (0x00000800) -#define MPI2_SASIOUNIT8_PM_HOST_SAS_PARTIAL_MODE (0x00000400) -#define MPI2_SASIOUNIT8_PM_HOST_SATA_SLUMBER_MODE (0x00000200) -#define MPI2_SASIOUNIT8_PM_HOST_SATA_PARTIAL_MODE (0x00000100) -#define MPI2_SASIOUNIT8_PM_IOUNIT_PORT_WIDTH_MOD (0x00000010) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_SLUMBER_MODE (0x00000008) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SAS_PARTIAL_MODE (0x00000004) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_SLUMBER_MODE (0x00000002) -#define MPI2_SASIOUNIT8_PM_IOUNIT_SATA_PARTIAL_MODE (0x00000001) - - - -/* SAS IO Unit Page 16 */ - -typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT16 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U64 TimeStamp; /* 0x08 */ - U32 Reserved1; /* 0x10 */ - U32 Reserved2; /* 0x14 */ - U32 FastPathPendedRequests; /* 0x18 */ - U32 FastPathUnPendedRequests; /* 0x1C */ - U32 FastPathHostRequestStarts; /* 0x20 */ - U32 FastPathFirmwareRequestStarts; /* 0x24 */ - U32 FastPathHostCompletions; /* 0x28 */ - U32 FastPathFirmwareCompletions; /* 0x2C */ - U32 NonFastPathRequestStarts; /* 0x30 */ - U32 NonFastPathHostCompletions; /* 0x30 */ -} MPI2_CONFIG_PAGE_SASIOUNIT16, -MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT16, -Mpi2SasIOUnitPage16_t, MPI2_POINTER pMpi2SasIOUnitPage16_t; - -#define MPI2_SASIOUNITPAGE16_PAGEVERSION (0x00) - - -/**************************************************************************** -* SAS Expander Config Pages -****************************************************************************/ - -/* SAS Expander Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 PhysicalPort; /* 0x08 */ - U8 ReportGenLength; /* 0x09 */ - U16 EnclosureHandle; /* 0x0A */ - U64 SASAddress; /* 0x0C */ - U32 DiscoveryStatus; /* 0x14 */ - U16 DevHandle; /* 0x18 */ - U16 ParentDevHandle; /* 0x1A */ - U16 ExpanderChangeCount; /* 0x1C */ - U16 ExpanderRouteIndexes; /* 0x1E */ - U8 NumPhys; /* 0x20 */ - U8 SASLevel; /* 0x21 */ - U16 Flags; /* 0x22 */ - U16 STPBusInactivityTimeLimit; /* 0x24 */ - U16 STPMaxConnectTimeLimit; /* 0x26 */ - U16 STP_SMP_NexusLossTime; /* 0x28 */ - U16 MaxNumRoutedSasAddresses; /* 0x2A */ - U64 ActiveZoneManagerSASAddress;/* 0x2C */ - U16 ZoneLockInactivityLimit; /* 0x34 */ - U16 Reserved1; /* 0x36 */ - U8 TimeToReducedFunc; /* 0x38 */ - U8 InitialTimeToReducedFunc; /* 0x39 */ - U8 MaxReducedFuncTime; /* 0x3A */ - U8 Reserved2; /* 0x3B */ -} MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0, - Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t; - -#define MPI2_SASEXPANDER0_PAGEVERSION (0x06) - -/* values for SAS Expander Page 0 DiscoveryStatus field */ -#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) -#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000) -#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000) -#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) -#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000) -#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) -#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) -#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000) -#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) -#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800) -#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400) -#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200) -#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100) -#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080) -#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040) -#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020) -#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010) -#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004) -#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002) -#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) - -/* values for SAS Expander Page 0 Flags field */ -#define MPI2_SAS_EXPANDER0_FLAGS_REDUCED_FUNCTIONALITY (0x2000) -#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) -#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) -#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) -#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200) -#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100) -#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080) -#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010) -#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004) -#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002) -#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001) - - -/* SAS Expander Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 PhysicalPort; /* 0x08 */ - U8 Reserved1; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U8 NumPhys; /* 0x0C */ - U8 Phy; /* 0x0D */ - U16 NumTableEntriesProgrammed; /* 0x0E */ - U8 ProgrammedLinkRate; /* 0x10 */ - U8 HwLinkRate; /* 0x11 */ - U16 AttachedDevHandle; /* 0x12 */ - U32 PhyInfo; /* 0x14 */ - U32 AttachedDeviceInfo; /* 0x18 */ - U16 ExpanderDevHandle; /* 0x1C */ - U8 ChangeCount; /* 0x1E */ - U8 NegotiatedLinkRate; /* 0x1F */ - U8 PhyIdentifier; /* 0x20 */ - U8 AttachedPhyIdentifier; /* 0x21 */ - U8 Reserved3; /* 0x22 */ - U8 DiscoveryInfo; /* 0x23 */ - U32 AttachedPhyInfo; /* 0x24 */ - U8 ZoneGroup; /* 0x28 */ - U8 SelfConfigStatus; /* 0x29 */ - U16 Reserved4; /* 0x2A */ -} MPI2_CONFIG_PAGE_EXPANDER_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_1, - Mpi2ExpanderPage1_t, MPI2_POINTER pMpi2ExpanderPage1_t; - -#define MPI2_SASEXPANDER1_PAGEVERSION (0x02) - -/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ - -/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ - -/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ - -/* see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines used for the AttachedDeviceInfo field */ - -/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ - -/* values for SAS Expander Page 1 DiscoveryInfo field */ -#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) -#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) -#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) - -/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ - -/**************************************************************************** -* SAS Device Config Pages -****************************************************************************/ - -/* SAS Device Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U16 Slot; /* 0x08 */ - U16 EnclosureHandle; /* 0x0A */ - U64 SASAddress; /* 0x0C */ - U16 ParentDevHandle; /* 0x14 */ - U8 PhyNum; /* 0x16 */ - U8 AccessStatus; /* 0x17 */ - U16 DevHandle; /* 0x18 */ - U8 AttachedPhyIdentifier; /* 0x1A */ - U8 ZoneGroup; /* 0x1B */ - U32 DeviceInfo; /* 0x1C */ - U16 Flags; /* 0x20 */ - U8 PhysicalPort; /* 0x22 */ - U8 MaxPortConnections; /* 0x23 */ - U64 DeviceName; /* 0x24 */ - U8 PortGroups; /* 0x2C */ - U8 DmaGroup; /* 0x2D */ - U8 ControlGroup; /* 0x2E */ - U8 EnclosureLevel; /* 0x2F */ - U8 ConnectorName[4]; /* 0x30 */ - U32 Reserved3; /* 0x34 */ -} MPI2_CONFIG_PAGE_SAS_DEV_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_0, - Mpi2SasDevicePage0_t, MPI2_POINTER pMpi2SasDevicePage0_t; - -#define MPI2_SASDEVICE0_PAGEVERSION (0x09) - -/* values for SAS Device Page 0 AccessStatus field */ -#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) -#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01) -#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02) -#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03) -#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04) -#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05) -#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06) -#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07) -/* specific values for SATA Init failures */ -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19) -#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F) - -/* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ - -/* values for SAS Device Page 0 Flags field */ -#define MPI2_SAS_DEVICE0_FLAGS_UNAUTHORIZED_DEVICE (0x8000) -#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE (0x1000) -#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE (0x0800) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200) -#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) -#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) -#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) -#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) -#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) - - -/* SAS Device Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U64 SASAddress; /* 0x0C */ - U32 Reserved2; /* 0x14 */ - U16 DevHandle; /* 0x18 */ - U16 Reserved3; /* 0x1A */ - U8 InitialRegDeviceFIS[20];/* 0x1C */ -} MPI2_CONFIG_PAGE_SAS_DEV_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_1, - Mpi2SasDevicePage1_t, MPI2_POINTER pMpi2SasDevicePage1_t; - -#define MPI2_SASDEVICE1_PAGEVERSION (0x01) - - -/**************************************************************************** -* SAS PHY Config Pages -****************************************************************************/ - -/* SAS PHY Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U16 OwnerDevHandle; /* 0x08 */ - U16 Reserved1; /* 0x0A */ - U16 AttachedDevHandle; /* 0x0C */ - U8 AttachedPhyIdentifier; /* 0x0E */ - U8 Reserved2; /* 0x0F */ - U32 AttachedPhyInfo; /* 0x10 */ - U8 ProgrammedLinkRate; /* 0x14 */ - U8 HwLinkRate; /* 0x15 */ - U8 ChangeCount; /* 0x16 */ - U8 Flags; /* 0x17 */ - U32 PhyInfo; /* 0x18 */ - U8 NegotiatedLinkRate; /* 0x1C */ - U8 Reserved3; /* 0x1D */ - U16 Reserved4; /* 0x1E */ -} MPI2_CONFIG_PAGE_SAS_PHY_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_0, - Mpi2SasPhyPage0_t, MPI2_POINTER pMpi2SasPhyPage0_t; - -#define MPI2_SASPHY0_PAGEVERSION (0x03) - -/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ - -/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ - -/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ - -/* values for SAS PHY Page 0 Flags field */ -#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) - -/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ - -/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ - - -/* SAS PHY Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 InvalidDwordCount; /* 0x0C */ - U32 RunningDisparityErrorCount; /* 0x10 */ - U32 LossDwordSynchCount; /* 0x14 */ - U32 PhyResetProblemCount; /* 0x18 */ -} MPI2_CONFIG_PAGE_SAS_PHY_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_1, - Mpi2SasPhyPage1_t, MPI2_POINTER pMpi2SasPhyPage1_t; - -#define MPI2_SASPHY1_PAGEVERSION (0x01) - - -/* SAS PHY Page 2 */ - -typedef struct _MPI2_SASPHY2_PHY_EVENT { - U8 PhyEventCode; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 PhyEventInfo; /* 0x04 */ -} MPI2_SASPHY2_PHY_EVENT, MPI2_POINTER PTR_MPI2_SASPHY2_PHY_EVENT, - Mpi2SasPhy2PhyEvent_t, MPI2_POINTER pMpi2SasPhy2PhyEvent_t; - -/* use MPI2_SASPHY3_EVENT_CODE_ for the PhyEventCode field */ - - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhyEvents at runtime. - */ -#ifndef MPI2_SASPHY2_PHY_EVENT_MAX -#define MPI2_SASPHY2_PHY_EVENT_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_2 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U8 NumPhyEvents; /* 0x0C */ - U8 Reserved2; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - MPI2_SASPHY2_PHY_EVENT PhyEvent[MPI2_SASPHY2_PHY_EVENT_MAX]; - /* 0x10 */ -} MPI2_CONFIG_PAGE_SAS_PHY_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_2, - Mpi2SasPhyPage2_t, MPI2_POINTER pMpi2SasPhyPage2_t; - -#define MPI2_SASPHY2_PAGEVERSION (0x00) - - -/* SAS PHY Page 3 */ - -typedef struct _MPI2_SASPHY3_PHY_EVENT_CONFIG { - U8 PhyEventCode; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U8 CounterType; /* 0x04 */ - U8 ThresholdWindow; /* 0x05 */ - U8 TimeUnits; /* 0x06 */ - U8 Reserved3; /* 0x07 */ - U32 EventThreshold; /* 0x08 */ - U16 ThresholdFlags; /* 0x0C */ - U16 Reserved4; /* 0x0E */ -} MPI2_SASPHY3_PHY_EVENT_CONFIG, MPI2_POINTER PTR_MPI2_SASPHY3_PHY_EVENT_CONFIG, - Mpi2SasPhy3PhyEventConfig_t, MPI2_POINTER pMpi2SasPhy3PhyEventConfig_t; - -/* values for PhyEventCode field */ -#define MPI2_SASPHY3_EVENT_CODE_NO_EVENT (0x00) -#define MPI2_SASPHY3_EVENT_CODE_INVALID_DWORD (0x01) -#define MPI2_SASPHY3_EVENT_CODE_RUNNING_DISPARITY_ERROR (0x02) -#define MPI2_SASPHY3_EVENT_CODE_LOSS_DWORD_SYNC (0x03) -#define MPI2_SASPHY3_EVENT_CODE_PHY_RESET_PROBLEM (0x04) -#define MPI2_SASPHY3_EVENT_CODE_ELASTICITY_BUF_OVERFLOW (0x05) -#define MPI2_SASPHY3_EVENT_CODE_RX_ERROR (0x06) -#define MPI2_SASPHY3_EVENT_CODE_RX_ADDR_FRAME_ERROR (0x20) -#define MPI2_SASPHY3_EVENT_CODE_TX_AC_OPEN_REJECT (0x21) -#define MPI2_SASPHY3_EVENT_CODE_RX_AC_OPEN_REJECT (0x22) -#define MPI2_SASPHY3_EVENT_CODE_TX_RC_OPEN_REJECT (0x23) -#define MPI2_SASPHY3_EVENT_CODE_RX_RC_OPEN_REJECT (0x24) -#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_PARTIAL_WAITING_ON (0x25) -#define MPI2_SASPHY3_EVENT_CODE_RX_AIP_CONNECT_WAITING_ON (0x26) -#define MPI2_SASPHY3_EVENT_CODE_TX_BREAK (0x27) -#define MPI2_SASPHY3_EVENT_CODE_RX_BREAK (0x28) -#define MPI2_SASPHY3_EVENT_CODE_BREAK_TIMEOUT (0x29) -#define MPI2_SASPHY3_EVENT_CODE_CONNECTION (0x2A) -#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_PATHWAY_BLOCKED (0x2B) -#define MPI2_SASPHY3_EVENT_CODE_PEAKTX_ARB_WAIT_TIME (0x2C) -#define MPI2_SASPHY3_EVENT_CODE_PEAK_ARB_WAIT_TIME (0x2D) -#define MPI2_SASPHY3_EVENT_CODE_PEAK_CONNECT_TIME (0x2E) -#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_FRAMES (0x40) -#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_FRAMES (0x41) -#define MPI2_SASPHY3_EVENT_CODE_TX_SSP_ERROR_FRAMES (0x42) -#define MPI2_SASPHY3_EVENT_CODE_RX_SSP_ERROR_FRAMES (0x43) -#define MPI2_SASPHY3_EVENT_CODE_TX_CREDIT_BLOCKED (0x44) -#define MPI2_SASPHY3_EVENT_CODE_RX_CREDIT_BLOCKED (0x45) -#define MPI2_SASPHY3_EVENT_CODE_TX_SATA_FRAMES (0x50) -#define MPI2_SASPHY3_EVENT_CODE_RX_SATA_FRAMES (0x51) -#define MPI2_SASPHY3_EVENT_CODE_SATA_OVERFLOW (0x52) -#define MPI2_SASPHY3_EVENT_CODE_TX_SMP_FRAMES (0x60) -#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_FRAMES (0x61) -#define MPI2_SASPHY3_EVENT_CODE_RX_SMP_ERROR_FRAMES (0x63) -#define MPI2_SASPHY3_EVENT_CODE_HOTPLUG_TIMEOUT (0xD0) -#define MPI2_SASPHY3_EVENT_CODE_MISALIGNED_MUX_PRIMITIVE (0xD1) -#define MPI2_SASPHY3_EVENT_CODE_RX_AIP (0xD2) - -/* values for the CounterType field */ -#define MPI2_SASPHY3_COUNTER_TYPE_WRAPPING (0x00) -#define MPI2_SASPHY3_COUNTER_TYPE_SATURATING (0x01) -#define MPI2_SASPHY3_COUNTER_TYPE_PEAK_VALUE (0x02) - -/* values for the TimeUnits field */ -#define MPI2_SASPHY3_TIME_UNITS_10_MICROSECONDS (0x00) -#define MPI2_SASPHY3_TIME_UNITS_100_MICROSECONDS (0x01) -#define MPI2_SASPHY3_TIME_UNITS_1_MILLISECOND (0x02) -#define MPI2_SASPHY3_TIME_UNITS_10_MILLISECONDS (0x03) - -/* values for the ThresholdFlags field */ -#define MPI2_SASPHY3_TFLAGS_PHY_RESET (0x0002) -#define MPI2_SASPHY3_TFLAGS_EVENT_NOTIFY (0x0001) - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumPhyEvents at runtime. - */ -#ifndef MPI2_SASPHY3_PHY_EVENT_MAX -#define MPI2_SASPHY3_PHY_EVENT_MAX (1) -#endif - -typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_3 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U8 NumPhyEvents; /* 0x0C */ - U8 Reserved2; /* 0x0D */ - U16 Reserved3; /* 0x0E */ - MPI2_SASPHY3_PHY_EVENT_CONFIG PhyEventConfig - [MPI2_SASPHY3_PHY_EVENT_MAX]; /* 0x10 */ -} MPI2_CONFIG_PAGE_SAS_PHY_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_3, - Mpi2SasPhyPage3_t, MPI2_POINTER pMpi2SasPhyPage3_t; - -#define MPI2_SASPHY3_PAGEVERSION (0x00) - - -/* SAS PHY Page 4 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_4 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U16 Reserved1; /* 0x08 */ - U8 Reserved2; /* 0x0A */ - U8 Flags; /* 0x0B */ - U8 InitialFrame[28]; /* 0x0C */ -} MPI2_CONFIG_PAGE_SAS_PHY_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_4, - Mpi2SasPhyPage4_t, MPI2_POINTER pMpi2SasPhyPage4_t; - -#define MPI2_SASPHY4_PAGEVERSION (0x00) - -/* values for the Flags field */ -#define MPI2_SASPHY4_FLAGS_FRAME_VALID (0x02) -#define MPI2_SASPHY4_FLAGS_SATA_FRAME (0x01) - - - - -/**************************************************************************** -* SAS Port Config Pages -****************************************************************************/ - -/* SAS Port Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 PortNumber; /* 0x08 */ - U8 PhysicalPort; /* 0x09 */ - U8 PortWidth; /* 0x0A */ - U8 PhysicalPortWidth; /* 0x0B */ - U8 ZoneGroup; /* 0x0C */ - U8 Reserved1; /* 0x0D */ - U16 Reserved2; /* 0x0E */ - U64 SASAddress; /* 0x10 */ - U32 DeviceInfo; /* 0x18 */ - U32 Reserved3; /* 0x1C */ - U32 Reserved4; /* 0x20 */ -} MPI2_CONFIG_PAGE_SAS_PORT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PORT_0, - Mpi2SasPortPage0_t, MPI2_POINTER pMpi2SasPortPage0_t; - -#define MPI2_SASPORT0_PAGEVERSION (0x00) - -/* see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */ - - -/**************************************************************************** -* SAS Enclosure Config Pages -****************************************************************************/ - -/* SAS Enclosure Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U64 EnclosureLogicalID; /* 0x0C */ - U16 Flags; /* 0x14 */ - U16 EnclosureHandle; /* 0x16 */ - U16 NumSlots; /* 0x18 */ - U16 StartSlot; /* 0x1A */ - U8 Reserved2; /* 0x1C */ - U8 EnclosureLevel; /* 0x1D */ - U16 SEPDevHandle; /* 0x1E */ - U32 Reserved3; /* 0x20 */ - U32 Reserved4; /* 0x24 */ -} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, - Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t; - -#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) - -/* values for SAS Enclosure Page 0 Flags field */ -#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) -#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) - - -/**************************************************************************** -* Log Config Page -****************************************************************************/ - -/* Log Page 0 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumLogEntries at runtime. - */ -#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES -#define MPI2_LOG_0_NUM_LOG_ENTRIES (1) -#endif - -#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C) - -typedef struct _MPI2_LOG_0_ENTRY -{ - U64 TimeStamp; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U16 LogSequence; /* 0x0C */ - U16 LogEntryQualifier; /* 0x0E */ - U8 VP_ID; /* 0x10 */ - U8 VF_ID; /* 0x11 */ - U16 Reserved2; /* 0x12 */ - U8 LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/* 0x14 */ -} MPI2_LOG_0_ENTRY, MPI2_POINTER PTR_MPI2_LOG_0_ENTRY, - Mpi2Log0Entry_t, MPI2_POINTER pMpi2Log0Entry_t; - -/* values for Log Page 0 LogEntry LogEntryQualifier field */ -#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000) -#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001) -#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002) -#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000) -#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF) - -typedef struct _MPI2_CONFIG_PAGE_LOG_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 Reserved2; /* 0x0C */ - U16 NumLogEntries; /* 0x10 */ - U16 Reserved3; /* 0x12 */ - MPI2_LOG_0_ENTRY LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /* 0x14 */ -} MPI2_CONFIG_PAGE_LOG_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_LOG_0, - Mpi2LogPage0_t, MPI2_POINTER pMpi2LogPage0_t; - -#define MPI2_LOG_0_PAGEVERSION (0x02) - - -/**************************************************************************** -* RAID Config Page -****************************************************************************/ - -/* RAID Page 0 */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check the value returned for NumElements at runtime. - */ -#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS -#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) -#endif - -typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT -{ - U16 ElementFlags; /* 0x00 */ - U16 VolDevHandle; /* 0x02 */ - U8 HotSparePool; /* 0x04 */ - U8 PhysDiskNum; /* 0x05 */ - U16 PhysDiskDevHandle; /* 0x06 */ -} MPI2_RAIDCONFIG0_CONFIG_ELEMENT, - MPI2_POINTER PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT, - Mpi2RaidConfig0ConfigElement_t, MPI2_POINTER pMpi2RaidConfig0ConfigElement_t; - -/* values for the ElementFlags field */ -#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F) -#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000) -#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001) -#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002) -#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003) - - -typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 NumHotSpares; /* 0x08 */ - U8 NumPhysDisks; /* 0x09 */ - U8 NumVolumes; /* 0x0A */ - U8 ConfigNum; /* 0x0B */ - U32 Flags; /* 0x0C */ - U8 ConfigGUID[24]; /* 0x10 */ - U32 Reserved1; /* 0x28 */ - U8 NumElements; /* 0x2C */ - U8 Reserved2; /* 0x2D */ - U16 Reserved3; /* 0x2E */ - MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /* 0x30 */ -} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, - Mpi2RaidConfigurationPage0_t, MPI2_POINTER pMpi2RaidConfigurationPage0_t; - -#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00) - -/* values for RAID Configuration Page 0 Flags field */ -#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001) - - -/**************************************************************************** -* Driver Persistent Mapping Config Pages -****************************************************************************/ - -/* Driver Persistent Mapping Page 0 */ - -typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY -{ - U64 PhysicalIdentifier; /* 0x00 */ - U16 MappingInformation; /* 0x08 */ - U16 DeviceIndex; /* 0x0A */ - U32 PhysicalBitsMapping; /* 0x0C */ - U32 Reserved1; /* 0x10 */ -} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, - Mpi2DriverMap0Entry_t, MPI2_POINTER pMpi2DriverMap0Entry_t; - -typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 -{ - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /* 0x08 */ -} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, - Mpi2DriverMappingPage0_t, MPI2_POINTER pMpi2DriverMappingPage0_t; - -#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00) - -/* values for Driver Persistent Mapping Page 0 MappingInformation field */ -#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0) -#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4) -#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F) - - -/**************************************************************************** -* Ethernet Config Pages -****************************************************************************/ - -/* Ethernet Page 0 */ - -/* IP address (union of IPv4 and IPv6) */ -typedef union _MPI2_ETHERNET_IP_ADDR { - U32 IPv4Addr; - U32 IPv6Addr[4]; -} MPI2_ETHERNET_IP_ADDR, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR, - Mpi2EthernetIpAddr_t, MPI2_POINTER pMpi2EthernetIpAddr_t; - -#define MPI2_ETHERNET_HOST_NAME_LENGTH (32) - -typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U8 NumInterfaces; /* 0x08 */ - U8 Reserved0; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U32 Status; /* 0x0C */ - U8 MediaState; /* 0x10 */ - U8 Reserved2; /* 0x11 */ - U16 Reserved3; /* 0x12 */ - U8 MacAddress[6]; /* 0x14 */ - U8 Reserved4; /* 0x1A */ - U8 Reserved5; /* 0x1B */ - MPI2_ETHERNET_IP_ADDR IpAddress; /* 0x1C */ - MPI2_ETHERNET_IP_ADDR SubnetMask; /* 0x2C */ - MPI2_ETHERNET_IP_ADDR GatewayIpAddress; /* 0x3C */ - MPI2_ETHERNET_IP_ADDR DNS1IpAddress; /* 0x4C */ - MPI2_ETHERNET_IP_ADDR DNS2IpAddress; /* 0x5C */ - MPI2_ETHERNET_IP_ADDR DhcpIpAddress; /* 0x6C */ - U8 HostName - [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */ -} MPI2_CONFIG_PAGE_ETHERNET_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0, - Mpi2EthernetPage0_t, MPI2_POINTER pMpi2EthernetPage0_t; - -#define MPI2_ETHERNETPAGE0_PAGEVERSION (0x00) - -/* values for Ethernet Page 0 Status field */ -#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE (0x80000000) -#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE (0x40000000) -#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED (0x20000000) -#define MPI2_ETHPG0_STATUS_DEFAULT_IF (0x00000100) -#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED (0x00000080) -#define MPI2_ETHPG0_STATUS_TELNET_ENABLED (0x00000040) -#define MPI2_ETHPG0_STATUS_SSH2_ENABLED (0x00000020) -#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED (0x00000010) -#define MPI2_ETHPG0_STATUS_IPV6_ENABLED (0x00000008) -#define MPI2_ETHPG0_STATUS_IPV4_ENABLED (0x00000004) -#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES (0x00000002) -#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED (0x00000001) - -/* values for Ethernet Page 0 MediaState field */ -#define MPI2_ETHPG0_MS_DUPLEX_MASK (0x80) -#define MPI2_ETHPG0_MS_HALF_DUPLEX (0x00) -#define MPI2_ETHPG0_MS_FULL_DUPLEX (0x80) - -#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK (0x07) -#define MPI2_ETHPG0_MS_NOT_CONNECTED (0x00) -#define MPI2_ETHPG0_MS_10MBIT (0x01) -#define MPI2_ETHPG0_MS_100MBIT (0x02) -#define MPI2_ETHPG0_MS_1GBIT (0x03) - - -/* Ethernet Page 1 */ - -typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 Reserved0; /* 0x08 */ - U32 Flags; /* 0x0C */ - U8 MediaState; /* 0x10 */ - U8 Reserved1; /* 0x11 */ - U16 Reserved2; /* 0x12 */ - U8 MacAddress[6]; /* 0x14 */ - U8 Reserved3; /* 0x1A */ - U8 Reserved4; /* 0x1B */ - MPI2_ETHERNET_IP_ADDR StaticIpAddress; /* 0x1C */ - MPI2_ETHERNET_IP_ADDR StaticSubnetMask; /* 0x2C */ - MPI2_ETHERNET_IP_ADDR StaticGatewayIpAddress; /* 0x3C */ - MPI2_ETHERNET_IP_ADDR StaticDNS1IpAddress; /* 0x4C */ - MPI2_ETHERNET_IP_ADDR StaticDNS2IpAddress; /* 0x5C */ - U32 Reserved5; /* 0x6C */ - U32 Reserved6; /* 0x70 */ - U32 Reserved7; /* 0x74 */ - U32 Reserved8; /* 0x78 */ - U8 HostName - [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */ -} MPI2_CONFIG_PAGE_ETHERNET_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1, - Mpi2EthernetPage1_t, MPI2_POINTER pMpi2EthernetPage1_t; - -#define MPI2_ETHERNETPAGE1_PAGEVERSION (0x00) - -/* values for Ethernet Page 1 Flags field */ -#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF (0x00000100) -#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD (0x00000080) -#define MPI2_ETHPG1_FLAG_ENABLE_TELNET (0x00000040) -#define MPI2_ETHPG1_FLAG_ENABLE_SSH2 (0x00000020) -#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT (0x00000010) -#define MPI2_ETHPG1_FLAG_ENABLE_IPV6 (0x00000008) -#define MPI2_ETHPG1_FLAG_ENABLE_IPV4 (0x00000004) -#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES (0x00000002) -#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF (0x00000001) - -/* values for Ethernet Page 1 MediaState field */ -#define MPI2_ETHPG1_MS_DUPLEX_MASK (0x80) -#define MPI2_ETHPG1_MS_HALF_DUPLEX (0x00) -#define MPI2_ETHPG1_MS_FULL_DUPLEX (0x80) - -#define MPI2_ETHPG1_MS_DATA_RATE_MASK (0x07) -#define MPI2_ETHPG1_MS_DATA_RATE_AUTO (0x00) -#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT (0x01) -#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT (0x02) -#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT (0x03) - - -/**************************************************************************** -* Extended Manufacturing Config Pages -****************************************************************************/ - -/* - * Generic structure to use for product-specific extended manufacturing pages - * (currently Extended Manufacturing Page 40 through Extended Manufacturing - * Page 60). - */ - -typedef struct _MPI2_CONFIG_PAGE_EXT_MAN_PS { - MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ - U32 ProductSpecificInfo; /* 0x08 */ -} MPI2_CONFIG_PAGE_EXT_MAN_PS, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXT_MAN_PS, - Mpi2ExtManufacturingPagePS_t, - MPI2_POINTER pMpi2ExtManufacturingPagePS_t; - -/* PageVersion should be provided by product-specific code */ - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_init.h deleted file mode 100644 index eea1a16b1..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_init.h - * Title: MPI SCSI initiator mode messages and structures - * Creation Date: June 23, 2006 - * - * mpi2_init.h Version: 02.00.15 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. - * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. - * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. - * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. - * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. - * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO - * Control field Task Attribute flags. - * Moved LUN field defines to mpi2.h because they are - * common to many structures. - * 05-06-09 02.00.07 Changed task management type of Query Unit Attention to - * Query Asynchronous Event. - * Defined two new bits in the SlotStatus field of the SCSI - * Enclosure Processor Request and Reply. - * 10-28-09 02.00.08 Added defines for decoding the ResponseInfo bytes for - * both SCSI IO Error Reply and SCSI Task Management Reply. - * Added ResponseInfo field to MPI2_SCSI_TASK_MANAGE_REPLY. - * Added MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG define. - * 02-10-10 02.00.09 Removed unused structure that had "#if 0" around it. - * 05-12-10 02.00.10 Added optional vendor-unique region to SCSI IO Request. - * 11-10-10 02.00.11 Added MPI2_SCSIIO_NUM_SGLOFFSETS define. - * 02-06-12 02.00.13 Added alternate defines for Task Priority / Command - * Priority to match SAM-4. - * 07-10-12 02.00.14 Added MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION. - * 04-09-13 02.00.15 Added SCSIStatusQualifier field to MPI2_SCSI_IO_REPLY, - * replacing the Reserved4 field. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_INIT_H -#define MPI2_INIT_H - -/***************************************************************************** -* -* SCSI Initiator Messages -* -*****************************************************************************/ - -/**************************************************************************** -* SCSI IO messages and associated structures -****************************************************************************/ - -typedef struct -{ - U8 CDB[20]; /* 0x00 */ - U32 PrimaryReferenceTag; /* 0x14 */ - U16 PrimaryApplicationTag; /* 0x18 */ - U16 PrimaryApplicationTagMask; /* 0x1A */ - U32 TransferLength; /* 0x1C */ -} MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32, - Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t; - -typedef union -{ - U8 CDB32[32]; - MPI2_SCSI_IO_CDB_EEDP32 EEDP32; - MPI2_SGE_SIMPLE_UNION SGE; -} MPI2_SCSI_IO_CDB_UNION, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_UNION, - Mpi2ScsiIoCdb_t, MPI2_POINTER pMpi2ScsiIoCdb_t; - -/* SCSI IO Request Message */ -typedef struct _MPI2_SCSI_IO_REQUEST -{ - U16 DevHandle; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved1; /* 0x04 */ - U8 Reserved2; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U32 SenseBufferLowAddress; /* 0x0C */ - U16 SGLFlags; /* 0x10 */ - U8 SenseBufferLength; /* 0x12 */ - U8 Reserved4; /* 0x13 */ - U8 SGLOffset0; /* 0x14 */ - U8 SGLOffset1; /* 0x15 */ - U8 SGLOffset2; /* 0x16 */ - U8 SGLOffset3; /* 0x17 */ - U32 SkipCount; /* 0x18 */ - U32 DataLength; /* 0x1C */ - U32 BidirectionalDataLength; /* 0x20 */ - U16 IoFlags; /* 0x24 */ - U16 EEDPFlags; /* 0x26 */ - U32 EEDPBlockSize; /* 0x28 */ - U32 SecondaryReferenceTag; /* 0x2C */ - U16 SecondaryApplicationTag; /* 0x30 */ - U16 ApplicationTagTranslationMask; /* 0x32 */ - U8 LUN[8]; /* 0x34 */ - U32 Control; /* 0x3C */ - MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ - -#ifdef MPI2_SCSI_IO_VENDOR_UNIQUE_REGION /* typically this is left undefined */ - MPI2_SCSI_IO_VENDOR_UNIQUE VendorRegion; -#endif - - MPI2_SGE_IO_UNION SGL; /* 0x60 */ - -} MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST, - Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t; - -/* SCSI IO MsgFlags bits */ - -/* MsgFlags for SenseBufferAddressSpace */ -#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C) -#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00) -#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04) -#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08) -#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C) - -/* SCSI IO SGLFlags bits */ - -/* base values for Data Location Address Space */ -#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C) -#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00) -#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04) -#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08) -#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C) - -/* base values for Type */ -#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03) -#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00) -#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01) -#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02) - -/* shift values for each sub-field */ -#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12) -#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8) -#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4) -#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0) - -/* number of SGLOffset fields */ -#define MPI2_SCSIIO_NUM_SGLOFFSETS (4) - -/* SCSI IO IoFlags bits */ - -/* Large CDB Address Space */ -#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000) -#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000) -#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000) -#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000) -#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000) - -#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) -#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) -#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400) -#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200) -#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) - -/* SCSI IO EEDPFlags bits */ - -#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000) -#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000) -#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000) -#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000) - -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400) -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200) -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100) - -#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008) - -#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007) -#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000) -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001) -#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002) -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003) -#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004) -#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006) -#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007) - -/* SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */ - -/* SCSI IO Control bits */ -#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000) -#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) - -#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) -#define MPI2_SCSIIO_CONTROL_SHIFT_DATADIRECTION (24) -#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) -#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) -#define MPI2_SCSIIO_CONTROL_READ (0x02000000) -#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000) - -#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) -#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) -/* alternate name for the previous field; called Command Priority in SAM-4 */ -#define MPI2_SCSIIO_CONTROL_CMDPRI_MASK (0x00007800) -#define MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT (11) - -#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) -#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) -#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100) -#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200) -#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400) - -#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0) -#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000) -#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040) -#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080) - - -/* SCSI IO Error Reply Message */ -typedef struct _MPI2_SCSI_IO_REPLY -{ - U16 DevHandle; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved1; /* 0x04 */ - U8 Reserved2; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U8 SCSIStatus; /* 0x0C */ - U8 SCSIState; /* 0x0D */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 TransferCount; /* 0x14 */ - U32 SenseCount; /* 0x18 */ - U32 ResponseInfo; /* 0x1C */ - U16 TaskTag; /* 0x20 */ - U16 SCSIStatusQualifier; /* 0x22 */ - U32 BidirectionalTransferCount; /* 0x24 */ - U32 Reserved5; /* 0x28 */ - U32 Reserved6; /* 0x2C */ -} MPI2_SCSI_IO_REPLY, MPI2_POINTER PTR_MPI2_SCSI_IO_REPLY, - Mpi2SCSIIOReply_t, MPI2_POINTER pMpi2SCSIIOReply_t; - -/* SCSI IO Reply SCSIStatus values (SAM-4 status codes) */ - -#define MPI2_SCSI_STATUS_GOOD (0x00) -#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02) -#define MPI2_SCSI_STATUS_CONDITION_MET (0x04) -#define MPI2_SCSI_STATUS_BUSY (0x08) -#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10) -#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) -#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18) -#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /* obsolete */ -#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28) -#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30) -#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40) - -/* SCSI IO Reply SCSIState flags */ - -#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10) -#define MPI2_SCSI_STATE_TERMINATED (0x08) -#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04) -#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02) -#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01) - -/* masks and shifts for the ResponseInfo field */ - -#define MPI2_SCSI_RI_MASK_REASONCODE (0x000000FF) -#define MPI2_SCSI_RI_SHIFT_REASONCODE (0) - -#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF) - - -/**************************************************************************** -* SCSI Task Management messages -****************************************************************************/ - -/* SCSI Task Management Request Message */ -typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST -{ - U16 DevHandle; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 Reserved1; /* 0x04 */ - U8 TaskType; /* 0x05 */ - U8 Reserved2; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U8 LUN[8]; /* 0x0C */ - U32 Reserved4[7]; /* 0x14 */ - U16 TaskMID; /* 0x30 */ - U16 Reserved5; /* 0x32 */ -} MPI2_SCSI_TASK_MANAGE_REQUEST, - MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REQUEST, - Mpi2SCSITaskManagementRequest_t, - MPI2_POINTER pMpi2SCSITaskManagementRequest_t; - -/* TaskType values */ - -#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) -#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02) -#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) -#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) -#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) -#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) -#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) -#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) -#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT (0x0A) - -/* obsolete TaskType name */ -#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION \ - (MPI2_SCSITASKMGMT_TASKTYPE_QRY_ASYNC_EVENT) - -/* MsgFlags bits */ - -#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) -#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) -#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) -#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) - -#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01) - - - -/* SCSI Task Management Reply Message */ -typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY -{ - U16 DevHandle; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 ResponseCode; /* 0x04 */ - U8 TaskType; /* 0x05 */ - U8 Reserved1; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U16 Reserved3; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 TerminationCount; /* 0x14 */ - U32 ResponseInfo; /* 0x18 */ -} MPI2_SCSI_TASK_MANAGE_REPLY, - MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY, - Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t; - -/* ResponseCode values */ - -#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00) -#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02) -#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04) -#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05) -#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08) -#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09) -#define MPI2_SCSITASKMGMT_RSP_TM_OVERLAPPED_TAG (0x0A) -#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80) - -/* masks and shifts for the ResponseInfo field */ - -#define MPI2_SCSITASKMGMT_RI_MASK_REASONCODE (0x000000FF) -#define MPI2_SCSITASKMGMT_RI_SHIFT_REASONCODE (0) -#define MPI2_SCSITASKMGMT_RI_MASK_ARI2 (0x0000FF00) -#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI2 (8) -#define MPI2_SCSITASKMGMT_RI_MASK_ARI1 (0x00FF0000) -#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI1 (16) -#define MPI2_SCSITASKMGMT_RI_MASK_ARI0 (0xFF000000) -#define MPI2_SCSITASKMGMT_RI_SHIFT_ARI0 (24) - - -/**************************************************************************** -* SCSI Enclosure Processor messages -****************************************************************************/ - -/* SCSI Enclosure Processor Request Message */ -typedef struct _MPI2_SEP_REQUEST -{ - U16 DevHandle; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 Action; /* 0x04 */ - U8 Flags; /* 0x05 */ - U8 Reserved1; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U32 SlotStatus; /* 0x0C */ - U32 Reserved3; /* 0x10 */ - U32 Reserved4; /* 0x14 */ - U32 Reserved5; /* 0x18 */ - U16 Slot; /* 0x1C */ - U16 EnclosureHandle; /* 0x1E */ -} MPI2_SEP_REQUEST, MPI2_POINTER PTR_MPI2_SEP_REQUEST, - Mpi2SepRequest_t, MPI2_POINTER pMpi2SepRequest_t; - -/* Action defines */ -#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00) -#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01) - -/* Flags defines */ -#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00) -#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01) - -/* SlotStatus defines */ -#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000) -#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) -#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200) -#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) -#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) -#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) -#define MPI2_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) -#define MPI2_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) -#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) -#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) -#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) - - -/* SCSI Enclosure Processor Reply Message */ -typedef struct _MPI2_SEP_REPLY -{ - U16 DevHandle; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 Action; /* 0x04 */ - U8 Flags; /* 0x05 */ - U8 Reserved1; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U16 Reserved3; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 SlotStatus; /* 0x14 */ - U32 Reserved4; /* 0x18 */ - U16 Slot; /* 0x1C */ - U16 EnclosureHandle; /* 0x1E */ -} MPI2_SEP_REPLY, MPI2_POINTER PTR_MPI2_SEP_REPLY, - Mpi2SepReply_t, MPI2_POINTER pMpi2SepReply_t; - -/* SlotStatus defines */ -#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000) -#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) -#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200) -#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) -#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) -#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) -#define MPI2_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) -#define MPI2_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) -#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) -#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) -#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) - - -#endif - - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h deleted file mode 100644 index b02de48be..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ /dev/null @@ -1,1708 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_ioc.h - * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages - * Creation Date: October 11, 2006 - * - * mpi2_ioc.h Version: 02.00.24 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to - * MaxTargets. - * Added TotalImageSize field to FWDownload Request. - * Added reserved words to FWUpload Request. - * 06-26-07 02.00.02 Added IR Configuration Change List Event. - * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit - * request and replaced it with - * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. - * Replaced the MinReplyQueueDepth field of the IOCFacts - * reply with MaxReplyDescriptorPostQueueDepth. - * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum - * depth for the Reply Descriptor Post Queue. - * Added SASAddress field to Initiator Device Table - * Overflow Event data. - * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING - * for SAS Initiator Device Status Change Event data. - * Modified Reason Code defines for SAS Topology Change - * List Event data, including adding a bit for PHY Vacant - * status, and adding a mask for the Reason Code. - * Added define for - * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. - * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. - * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of - * the IOCFacts Reply. - * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. - * Moved MPI2_VERSION_UNION to mpi2.h. - * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks - * instead of enables, and added SASBroadcastPrimitiveMasks - * field. - * Added Log Entry Added Event and related structure. - * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. - * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. - * Added MaxVolumes and MaxPersistentEntries fields to - * IOCFacts reply. - * Added ProtocalFlags and IOCCapabilities fields to - * MPI2_FW_IMAGE_HEADER. - * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. - * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to - * a U16 (from a U32). - * Removed extra 's' from EventMasks name. - * 06-27-08 02.00.08 Fixed an offset in a comment. - * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. - * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and - * renamed MinReplyFrameSize to ReplyFrameSize. - * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. - * Added two new RAIDOperation values for Integrated RAID - * Operations Status Event data. - * Added four new IR Configuration Change List Event data - * ReasonCode values. - * Added two new ReasonCode defines for SAS Device Status - * Change Event data. - * Added three new DiscoveryStatus bits for the SAS - * Discovery event data. - * Added Multiplexing Status Change bit to the PhyStatus - * field of the SAS Topology Change List event data. - * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. - * BootFlags are now product-specific. - * Added defines for the indivdual signature bytes - * for MPI2_INIT_IMAGE_FOOTER. - * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. - * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR - * define. - * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE - * define. - * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. - * 05-06-09 02.00.11 Added MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR define. - * Added MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX define. - * Added two new reason codes for SAS Device Status Change - * Event. - * Added new event: SAS PHY Counter. - * 07-30-09 02.00.12 Added GPIO Interrupt event define and structure. - * Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. - * Added new product id family for 2208. - * 10-28-09 02.00.13 Added HostMSIxVectors field to MPI2_IOC_INIT_REQUEST. - * Added MaxMSIxVectors field to MPI2_IOC_FACTS_REPLY. - * Added MinDevHandle field to MPI2_IOC_FACTS_REPLY. - * Added MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY. - * Added MPI2_EVENT_HOST_BASED_DISCOVERY_PHY define. - * Added MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER define. - * Added Host Based Discovery Phy Event data. - * Added defines for ProductID Product field - * (MPI2_FW_HEADER_PID_). - * Modified values for SAS ProductID Family - * (MPI2_FW_HEADER_PID_FAMILY_). - * 02-10-10 02.00.14 Added SAS Quiesce Event structure and defines. - * Added PowerManagementControl Request structures and - * defines. - * 05-12-10 02.00.15 Marked Task Set Full Event as obsolete. - * Added MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY define. - * 11-10-10 02.00.16 Added MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC. - * 02-23-11 02.00.17 Added SAS NOTIFY Primitive event, and added - * SASNotifyPrimitiveMasks field to - * MPI2_EVENT_NOTIFICATION_REQUEST. - * Added Temperature Threshold Event. - * Added Host Message Event. - * Added Send Host Message request and reply. - * 05-25-11 02.00.18 For Extended Image Header, added - * MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC and - * MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC defines. - * Deprecated MPI2_EXT_IMAGE_TYPE_MAX define. - * 08-24-11 02.00.19 Added PhysicalPort field to - * MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE structure. - * Marked MPI2_PM_CONTROL_FEATURE_PCIE_LINK as obsolete. - * 03-29-12 02.00.21 Added a product specific range to event values. - * 07-26-12 02.00.22 Added MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE. - * Added ElapsedSeconds field to - * MPI2_EVENT_DATA_IR_OPERATION_STATUS. - * 08-19-13 02.00.23 For IOCInit, added MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE - * and MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY. - * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. - * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. - * Added Encrypted Hash Extended Image. - * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_IOC_H -#define MPI2_IOC_H - -/***************************************************************************** -* -* IOC Messages -* -*****************************************************************************/ - -/**************************************************************************** -* IOCInit message -****************************************************************************/ - -/* IOCInit Request message */ -typedef struct _MPI2_IOC_INIT_REQUEST -{ - U8 WhoInit; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 MsgVersion; /* 0x0C */ - U16 HeaderVersion; /* 0x0E */ - U32 Reserved5; /* 0x10 */ - U16 Reserved6; /* 0x14 */ - U8 Reserved7; /* 0x16 */ - U8 HostMSIxVectors; /* 0x17 */ - U16 Reserved8; /* 0x18 */ - U16 SystemRequestFrameSize; /* 0x1A */ - U16 ReplyDescriptorPostQueueDepth; /* 0x1C */ - U16 ReplyFreeQueueDepth; /* 0x1E */ - U32 SenseBufferAddressHigh; /* 0x20 */ - U32 SystemReplyAddressHigh; /* 0x24 */ - U64 SystemRequestFrameBaseAddress; /* 0x28 */ - U64 ReplyDescriptorPostQueueAddress;/* 0x30 */ - U64 ReplyFreeQueueAddress; /* 0x38 */ - U64 TimeStamp; /* 0x40 */ -} MPI2_IOC_INIT_REQUEST, MPI2_POINTER PTR_MPI2_IOC_INIT_REQUEST, - Mpi2IOCInitRequest_t, MPI2_POINTER pMpi2IOCInitRequest_t; - -/* WhoInit values */ -#define MPI2_WHOINIT_NOT_INITIALIZED (0x00) -#define MPI2_WHOINIT_SYSTEM_BIOS (0x01) -#define MPI2_WHOINIT_ROM_BIOS (0x02) -#define MPI2_WHOINIT_PCI_PEER (0x03) -#define MPI2_WHOINIT_HOST_DRIVER (0x04) -#define MPI2_WHOINIT_MANUFACTURER (0x05) - -/* MsgFlags */ -#define MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE (0x01) - -/* MsgVersion */ -#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00) -#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8) -#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF) -#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0) - -/* HeaderVersion */ -#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00) -#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8) -#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF) -#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0) - -/* minimum depth for a Reply Descriptor Post Queue */ -#define MPI2_RDPQ_DEPTH_MIN (16) - -/* Reply Descriptor Post Queue Array Entry */ -typedef struct _MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY { - U64 RDPQBaseAddress; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U32 Reserved2; /* 0x0C */ -} MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, -MPI2_POINTER PTR_MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY, -Mpi2IOCInitRDPQArrayEntry, MPI2_POINTER pMpi2IOCInitRDPQArrayEntry; - -/* IOCInit Reply message */ -typedef struct _MPI2_IOC_INIT_REPLY -{ - U8 WhoInit; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_IOC_INIT_REPLY, MPI2_POINTER PTR_MPI2_IOC_INIT_REPLY, - Mpi2IOCInitReply_t, MPI2_POINTER pMpi2IOCInitReply_t; - - -/**************************************************************************** -* IOCFacts message -****************************************************************************/ - -/* IOCFacts Request message */ -typedef struct _MPI2_IOC_FACTS_REQUEST -{ - U16 Reserved1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ -} MPI2_IOC_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_IOC_FACTS_REQUEST, - Mpi2IOCFactsRequest_t, MPI2_POINTER pMpi2IOCFactsRequest_t; - - -/* IOCFacts Reply message */ -typedef struct _MPI2_IOC_FACTS_REPLY -{ - U16 MsgVersion; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 HeaderVersion; /* 0x04 */ - U8 IOCNumber; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U16 IOCExceptions; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U8 MaxChainDepth; /* 0x14 */ - U8 WhoInit; /* 0x15 */ - U8 NumberOfPorts; /* 0x16 */ - U8 MaxMSIxVectors; /* 0x17 */ - U16 RequestCredit; /* 0x18 */ - U16 ProductID; /* 0x1A */ - U32 IOCCapabilities; /* 0x1C */ - MPI2_VERSION_UNION FWVersion; /* 0x20 */ - U16 IOCRequestFrameSize; /* 0x24 */ - U16 Reserved3; /* 0x26 */ - U16 MaxInitiators; /* 0x28 */ - U16 MaxTargets; /* 0x2A */ - U16 MaxSasExpanders; /* 0x2C */ - U16 MaxEnclosures; /* 0x2E */ - U16 ProtocolFlags; /* 0x30 */ - U16 HighPriorityCredit; /* 0x32 */ - U16 MaxReplyDescriptorPostQueueDepth; /* 0x34 */ - U8 ReplyFrameSize; /* 0x36 */ - U8 MaxVolumes; /* 0x37 */ - U16 MaxDevHandle; /* 0x38 */ - U16 MaxPersistentEntries; /* 0x3A */ - U16 MinDevHandle; /* 0x3C */ - U16 Reserved4; /* 0x3E */ -} MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY, - Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t; - -/* MsgVersion */ -#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) -#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8) -#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) -#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0) - -/* HeaderVersion */ -#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00) -#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8) -#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF) -#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) - -/* IOCExceptions */ -#define MPI2_IOCFACTS_EXCEPT_PARTIAL_MEMORY_FAILURE (0x0200) -#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) - -#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0) -#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000) -#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020) -#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040) -#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060) - -#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010) -#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008) -#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004) -#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) -#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) - -/* defines for WhoInit field are after the IOCInit Request */ - -/* ProductID field uses MPI2_FW_HEADER_PID_ */ - -/* IOCCapabilities */ -#define MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE (0x00040000) -#define MPI2_IOCFACTS_CAPABILITY_HOST_BASED_DISCOVERY (0x00010000) -#define MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX (0x00008000) -#define MPI2_IOCFACTS_CAPABILITY_RAID_ACCELERATOR (0x00004000) -#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) -#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) -#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) -#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100) -#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080) -#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040) -#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020) -#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) -#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) -#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) - -/* ProtocolFlags */ -#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) -#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) - - -/**************************************************************************** -* PortFacts message -****************************************************************************/ - -/* PortFacts Request message */ -typedef struct _MPI2_PORT_FACTS_REQUEST -{ - U16 Reserved1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 PortNumber; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ -} MPI2_PORT_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_PORT_FACTS_REQUEST, - Mpi2PortFactsRequest_t, MPI2_POINTER pMpi2PortFactsRequest_t; - -/* PortFacts Reply message */ -typedef struct _MPI2_PORT_FACTS_REPLY -{ - U16 Reserved1; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 PortNumber; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U16 Reserved4; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U8 Reserved5; /* 0x14 */ - U8 PortType; /* 0x15 */ - U16 Reserved6; /* 0x16 */ - U16 MaxPostedCmdBuffers; /* 0x18 */ - U16 Reserved7; /* 0x1A */ -} MPI2_PORT_FACTS_REPLY, MPI2_POINTER PTR_MPI2_PORT_FACTS_REPLY, - Mpi2PortFactsReply_t, MPI2_POINTER pMpi2PortFactsReply_t; - -/* PortType values */ -#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00) -#define MPI2_PORTFACTS_PORTTYPE_FC (0x10) -#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20) -#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30) -#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31) - - -/**************************************************************************** -* PortEnable message -****************************************************************************/ - -/* PortEnable Request message */ -typedef struct _MPI2_PORT_ENABLE_REQUEST -{ - U16 Reserved1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 Reserved2; /* 0x04 */ - U8 PortFlags; /* 0x05 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ -} MPI2_PORT_ENABLE_REQUEST, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REQUEST, - Mpi2PortEnableRequest_t, MPI2_POINTER pMpi2PortEnableRequest_t; - - -/* PortEnable Reply message */ -typedef struct _MPI2_PORT_ENABLE_REPLY -{ - U16 Reserved1; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U8 Reserved2; /* 0x04 */ - U8 PortFlags; /* 0x05 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_PORT_ENABLE_REPLY, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REPLY, - Mpi2PortEnableReply_t, MPI2_POINTER pMpi2PortEnableReply_t; - - -/**************************************************************************** -* EventNotification message -****************************************************************************/ - -/* EventNotification Request message */ -#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4) - -typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST -{ - U16 Reserved1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U32 Reserved5; /* 0x0C */ - U32 Reserved6; /* 0x10 */ - U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];/* 0x14 */ - U16 SASBroadcastPrimitiveMasks; /* 0x24 */ - U16 SASNotifyPrimitiveMasks; /* 0x26 */ - U32 Reserved8; /* 0x28 */ -} MPI2_EVENT_NOTIFICATION_REQUEST, - MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REQUEST, - Mpi2EventNotificationRequest_t, MPI2_POINTER pMpi2EventNotificationRequest_t; - - -/* EventNotification Reply message */ -typedef struct _MPI2_EVENT_NOTIFICATION_REPLY -{ - U16 EventDataLength; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved1; /* 0x04 */ - U8 AckRequired; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U16 Reserved3; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U16 Event; /* 0x14 */ - U16 Reserved4; /* 0x16 */ - U32 EventContext; /* 0x18 */ - U32 EventData[1]; /* 0x1C */ -} MPI2_EVENT_NOTIFICATION_REPLY, MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REPLY, - Mpi2EventNotificationReply_t, MPI2_POINTER pMpi2EventNotificationReply_t; - -/* AckRequired */ -#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) -#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) - -/* Event */ -#define MPI2_EVENT_LOG_DATA (0x0001) -#define MPI2_EVENT_STATE_CHANGE (0x0002) -#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005) -#define MPI2_EVENT_EVENT_CHANGE (0x000A) -#define MPI2_EVENT_TASK_SET_FULL (0x000E) /* obsolete */ -#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F) -#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014) -#define MPI2_EVENT_SAS_DISCOVERY (0x0016) -#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017) -#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018) -#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019) -#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C) -#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D) -#define MPI2_EVENT_IR_VOLUME (0x001E) -#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) -#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) -#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) -#define MPI2_EVENT_SAS_PHY_COUNTER (0x0022) -#define MPI2_EVENT_GPIO_INTERRUPT (0x0023) -#define MPI2_EVENT_HOST_BASED_DISCOVERY_PHY (0x0024) -#define MPI2_EVENT_SAS_QUIESCE (0x0025) -#define MPI2_EVENT_SAS_NOTIFY_PRIMITIVE (0x0026) -#define MPI2_EVENT_TEMP_THRESHOLD (0x0027) -#define MPI2_EVENT_HOST_MESSAGE (0x0028) -#define MPI2_EVENT_MIN_PRODUCT_SPECIFIC (0x006E) -#define MPI2_EVENT_MAX_PRODUCT_SPECIFIC (0x007F) - -/* Log Entry Added Event data */ - -/* the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */ -#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C) - -typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED -{ - U64 TimeStamp; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U16 LogSequence; /* 0x0C */ - U16 LogEntryQualifier; /* 0x0E */ - U8 VP_ID; /* 0x10 */ - U8 VF_ID; /* 0x11 */ - U16 Reserved2; /* 0x12 */ - U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH];/* 0x14 */ -} MPI2_EVENT_DATA_LOG_ENTRY_ADDED, - MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED, - Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t; - -/* GPIO Interrupt Event data */ - -typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT { - U8 GPIONum; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ -} MPI2_EVENT_DATA_GPIO_INTERRUPT, - MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT, - Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t; - -/* Temperature Threshold Event data */ - -typedef struct _MPI2_EVENT_DATA_TEMPERATURE { - U16 Status; /* 0x00 */ - U8 SensorNum; /* 0x02 */ - U8 Reserved1; /* 0x03 */ - U16 CurrentTemperature; /* 0x04 */ - U16 Reserved2; /* 0x06 */ - U32 Reserved3; /* 0x08 */ - U32 Reserved4; /* 0x0C */ -} MPI2_EVENT_DATA_TEMPERATURE, -MPI2_POINTER PTR_MPI2_EVENT_DATA_TEMPERATURE, -Mpi2EventDataTemperature_t, MPI2_POINTER pMpi2EventDataTemperature_t; - -/* Temperature Threshold Event data Status bits */ -#define MPI2_EVENT_TEMPERATURE3_EXCEEDED (0x0008) -#define MPI2_EVENT_TEMPERATURE2_EXCEEDED (0x0004) -#define MPI2_EVENT_TEMPERATURE1_EXCEEDED (0x0002) -#define MPI2_EVENT_TEMPERATURE0_EXCEEDED (0x0001) - - -/* Host Message Event data */ - -typedef struct _MPI2_EVENT_DATA_HOST_MESSAGE { - U8 SourceVF_ID; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 Reserved3; /* 0x04 */ - U32 HostData[1]; /* 0x08 */ -} MPI2_EVENT_DATA_HOST_MESSAGE, MPI2_POINTER PTR_MPI2_EVENT_DATA_HOST_MESSAGE, -Mpi2EventDataHostMessage_t, MPI2_POINTER pMpi2EventDataHostMessage_t; - - -/* Hard Reset Received Event data */ - -typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED -{ - U8 Reserved1; /* 0x00 */ - U8 Port; /* 0x01 */ - U16 Reserved2; /* 0x02 */ -} MPI2_EVENT_DATA_HARD_RESET_RECEIVED, - MPI2_POINTER PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED, - Mpi2EventDataHardResetReceived_t, - MPI2_POINTER pMpi2EventDataHardResetReceived_t; - -/* Task Set Full Event data */ -/* this event is obsolete */ - -typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL -{ - U16 DevHandle; /* 0x00 */ - U16 CurrentDepth; /* 0x02 */ -} MPI2_EVENT_DATA_TASK_SET_FULL, MPI2_POINTER PTR_MPI2_EVENT_DATA_TASK_SET_FULL, - Mpi2EventDataTaskSetFull_t, MPI2_POINTER pMpi2EventDataTaskSetFull_t; - - -/* SAS Device Status Change Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE -{ - U16 TaskTag; /* 0x00 */ - U8 ReasonCode; /* 0x02 */ - U8 PhysicalPort; /* 0x03 */ - U8 ASC; /* 0x04 */ - U8 ASCQ; /* 0x05 */ - U16 DevHandle; /* 0x06 */ - U32 Reserved2; /* 0x08 */ - U64 SASAddress; /* 0x0C */ - U8 LUN[8]; /* 0x14 */ -} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, - Mpi2EventDataSasDeviceStatusChange_t, - MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t; - -/* SAS Device Status Change Event data ReasonCode values */ -#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) -#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) -#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) -#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) -#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) -#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) -#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) -#define MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY (0x11) -#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY (0x12) - - -/* Integrated RAID Operation Status Event data */ - -typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS -{ - U16 VolDevHandle; /* 0x00 */ - U16 Reserved1; /* 0x02 */ - U8 RAIDOperation; /* 0x04 */ - U8 PercentComplete; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U32 ElapsedSeconds; /* 0x08 */ -} MPI2_EVENT_DATA_IR_OPERATION_STATUS, - MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS, - Mpi2EventDataIrOperationStatus_t, - MPI2_POINTER pMpi2EventDataIrOperationStatus_t; - -/* Integrated RAID Operation Status Event data RAIDOperation values */ -#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00) -#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01) -#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02) -#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03) -#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04) - - -/* Integrated RAID Volume Event data */ - -typedef struct _MPI2_EVENT_DATA_IR_VOLUME -{ - U16 VolDevHandle; /* 0x00 */ - U8 ReasonCode; /* 0x02 */ - U8 Reserved1; /* 0x03 */ - U32 NewValue; /* 0x04 */ - U32 PreviousValue; /* 0x08 */ -} MPI2_EVENT_DATA_IR_VOLUME, MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_VOLUME, - Mpi2EventDataIrVolume_t, MPI2_POINTER pMpi2EventDataIrVolume_t; - -/* Integrated RAID Volume Event data ReasonCode values */ -#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01) -#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02) -#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03) - - -/* Integrated RAID Physical Disk Event data */ - -typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK -{ - U16 Reserved1; /* 0x00 */ - U8 ReasonCode; /* 0x02 */ - U8 PhysDiskNum; /* 0x03 */ - U16 PhysDiskDevHandle; /* 0x04 */ - U16 Reserved2; /* 0x06 */ - U16 Slot; /* 0x08 */ - U16 EnclosureHandle; /* 0x0A */ - U32 NewValue; /* 0x0C */ - U32 PreviousValue; /* 0x10 */ -} MPI2_EVENT_DATA_IR_PHYSICAL_DISK, - MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK, - Mpi2EventDataIrPhysicalDisk_t, MPI2_POINTER pMpi2EventDataIrPhysicalDisk_t; - -/* Integrated RAID Physical Disk Event data ReasonCode values */ -#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01) -#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02) -#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03) - - -/* Integrated RAID Configuration Change List Event data */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check NumElements at runtime. - */ -#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT -#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1) -#endif - -typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT -{ - U16 ElementFlags; /* 0x00 */ - U16 VolDevHandle; /* 0x02 */ - U8 ReasonCode; /* 0x04 */ - U8 PhysDiskNum; /* 0x05 */ - U16 PhysDiskDevHandle; /* 0x06 */ -} MPI2_EVENT_IR_CONFIG_ELEMENT, MPI2_POINTER PTR_MPI2_EVENT_IR_CONFIG_ELEMENT, - Mpi2EventIrConfigElement_t, MPI2_POINTER pMpi2EventIrConfigElement_t; - -/* IR Configuration Change List Event data ElementFlags values */ -#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F) -#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000) -#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001) -#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002) - -/* IR Configuration Change List Event data ReasonCode values */ -#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01) -#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02) -#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03) -#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04) -#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05) -#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06) -#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07) -#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08) -#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09) - -typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST -{ - U8 NumElements; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 Reserved2; /* 0x02 */ - U8 ConfigNum; /* 0x03 */ - U32 Flags; /* 0x04 */ - MPI2_EVENT_IR_CONFIG_ELEMENT ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT]; /* 0x08 */ -} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, - MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, - Mpi2EventDataIrConfigChangeList_t, - MPI2_POINTER pMpi2EventDataIrConfigChangeList_t; - -/* IR Configuration Change List Event data Flags values */ -#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001) - - -/* SAS Discovery Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY -{ - U8 Flags; /* 0x00 */ - U8 ReasonCode; /* 0x01 */ - U8 PhysicalPort; /* 0x02 */ - U8 Reserved1; /* 0x03 */ - U32 DiscoveryStatus; /* 0x04 */ -} MPI2_EVENT_DATA_SAS_DISCOVERY, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DISCOVERY, - Mpi2EventDataSasDiscovery_t, MPI2_POINTER pMpi2EventDataSasDiscovery_t; - -/* SAS Discovery Event data Flags values */ -#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02) -#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01) - -/* SAS Discovery Event data ReasonCode values */ -#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01) -#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02) - -/* SAS Discovery Event data DiscoveryStatus values */ -#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000) -#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000) -#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000) -#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) -#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000) -#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) -#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) -#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000) -#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) -#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800) -#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400) -#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200) -#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100) -#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080) -#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040) -#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020) -#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010) -#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004) -#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002) -#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001) - - -/* SAS Broadcast Primitive Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE -{ - U8 PhyNum; /* 0x00 */ - U8 Port; /* 0x01 */ - U8 PortWidth; /* 0x02 */ - U8 Primitive; /* 0x03 */ -} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, - Mpi2EventDataSasBroadcastPrimitive_t, - MPI2_POINTER pMpi2EventDataSasBroadcastPrimitive_t; - -/* defines for the Primitive field */ -#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01) -#define MPI2_EVENT_PRIMITIVE_SES (0x02) -#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03) -#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04) -#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05) -#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06) -#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07) -#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08) - -/* SAS Notify Primitive Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE { - U8 PhyNum; /* 0x00 */ - U8 Port; /* 0x01 */ - U8 Reserved1; /* 0x02 */ - U8 Primitive; /* 0x03 */ -} MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, -MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_NOTIFY_PRIMITIVE, -Mpi2EventDataSasNotifyPrimitive_t, -MPI2_POINTER pMpi2EventDataSasNotifyPrimitive_t; - -/* defines for the Primitive field */ -#define MPI2_EVENT_NOTIFY_ENABLE_SPINUP (0x01) -#define MPI2_EVENT_NOTIFY_POWER_LOSS_EXPECTED (0x02) -#define MPI2_EVENT_NOTIFY_RESERVED1 (0x03) -#define MPI2_EVENT_NOTIFY_RESERVED2 (0x04) - - -/* SAS Initiator Device Status Change Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE -{ - U8 ReasonCode; /* 0x00 */ - U8 PhysicalPort; /* 0x01 */ - U16 DevHandle; /* 0x02 */ - U64 SASAddress; /* 0x04 */ -} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, - Mpi2EventDataSasInitDevStatusChange_t, - MPI2_POINTER pMpi2EventDataSasInitDevStatusChange_t; - -/* SAS Initiator Device Status Change event ReasonCode values */ -#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01) -#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02) - - -/* SAS Initiator Device Table Overflow Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW -{ - U16 MaxInit; /* 0x00 */ - U16 CurrentInit; /* 0x02 */ - U64 SASAddress; /* 0x04 */ -} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, - Mpi2EventDataSasInitTableOverflow_t, - MPI2_POINTER pMpi2EventDataSasInitTableOverflow_t; - - -/* SAS Topology Change List Event data */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check NumEntries at runtime. - */ -#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT -#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1) -#endif - -typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY -{ - U16 AttachedDevHandle; /* 0x00 */ - U8 LinkRate; /* 0x02 */ - U8 PhyStatus; /* 0x03 */ -} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, MPI2_POINTER PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY, - Mpi2EventSasTopoPhyEntry_t, MPI2_POINTER pMpi2EventSasTopoPhyEntry_t; - -typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST -{ - U16 EnclosureHandle; /* 0x00 */ - U16 ExpanderDevHandle; /* 0x02 */ - U8 NumPhys; /* 0x04 */ - U8 Reserved1; /* 0x05 */ - U16 Reserved2; /* 0x06 */ - U8 NumEntries; /* 0x08 */ - U8 StartPhyNum; /* 0x09 */ - U8 ExpStatus; /* 0x0A */ - U8 PhysicalPort; /* 0x0B */ - MPI2_EVENT_SAS_TOPO_PHY_ENTRY PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /* 0x0C*/ -} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, - Mpi2EventDataSasTopologyChangeList_t, - MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t; - -/* values for the ExpStatus field */ -#define MPI2_EVENT_SAS_TOPO_ES_NO_EXPANDER (0x00) -#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01) -#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) -#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03) -#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04) - -/* defines for the LinkRate field */ -#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0) -#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4) -#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F) -#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0) - -#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00) -#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01) -#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02) -#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) -#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) -#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) -#define MPI2_EVENT_SAS_TOPO_LR_UNSUPPORTED_PHY (0x06) -#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08) -#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) -#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) - -/* values for the PhyStatus field */ -#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80) -#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10) -/* values for the PhyStatus ReasonCode sub-field */ -#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F) -#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01) -#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02) -#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03) -#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04) -#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05) - - -/* SAS Enclosure Device Status Change Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE -{ - U16 EnclosureHandle; /* 0x00 */ - U8 ReasonCode; /* 0x02 */ - U8 PhysicalPort; /* 0x03 */ - U64 EnclosureLogicalID; /* 0x04 */ - U16 NumSlots; /* 0x0C */ - U16 StartSlot; /* 0x0E */ - U32 PhyBits; /* 0x10 */ -} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, - Mpi2EventDataSasEnclDevStatusChange_t, - MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t; - -/* SAS Enclosure Device Status Change event ReasonCode values */ -#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01) -#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) - - -/* SAS PHY Counter Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_PHY_COUNTER { - U64 TimeStamp; /* 0x00 */ - U32 Reserved1; /* 0x08 */ - U8 PhyEventCode; /* 0x0C */ - U8 PhyNum; /* 0x0D */ - U16 Reserved2; /* 0x0E */ - U32 PhyEventInfo; /* 0x10 */ - U8 CounterType; /* 0x14 */ - U8 ThresholdWindow; /* 0x15 */ - U8 TimeUnits; /* 0x16 */ - U8 Reserved3; /* 0x17 */ - U32 EventThreshold; /* 0x18 */ - U16 ThresholdFlags; /* 0x1C */ - U16 Reserved4; /* 0x1E */ -} MPI2_EVENT_DATA_SAS_PHY_COUNTER, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_PHY_COUNTER, - Mpi2EventDataSasPhyCounter_t, MPI2_POINTER pMpi2EventDataSasPhyCounter_t; - -/* use MPI2_SASPHY3_EVENT_CODE_ values from mpi2_cnfg.h for the - * PhyEventCode field - * use MPI2_SASPHY3_COUNTER_TYPE_ values from mpi2_cnfg.h for the - * CounterType field - * use MPI2_SASPHY3_TIME_UNITS_ values from mpi2_cnfg.h for the - * TimeUnits field - * use MPI2_SASPHY3_TFLAGS_ values from mpi2_cnfg.h for the - * ThresholdFlags field - * */ - - -/* SAS Quiesce Event data */ - -typedef struct _MPI2_EVENT_DATA_SAS_QUIESCE { - U8 ReasonCode; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 Reserved3; /* 0x04 */ -} MPI2_EVENT_DATA_SAS_QUIESCE, - MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_QUIESCE, - Mpi2EventDataSasQuiesce_t, MPI2_POINTER pMpi2EventDataSasQuiesce_t; - -/* SAS Quiesce Event data ReasonCode values */ -#define MPI2_EVENT_SAS_QUIESCE_RC_STARTED (0x01) -#define MPI2_EVENT_SAS_QUIESCE_RC_COMPLETED (0x02) - - -/* Host Based Discovery Phy Event data */ - -typedef struct _MPI2_EVENT_HBD_PHY_SAS { - U8 Flags; /* 0x00 */ - U8 NegotiatedLinkRate; /* 0x01 */ - U8 PhyNum; /* 0x02 */ - U8 PhysicalPort; /* 0x03 */ - U32 Reserved1; /* 0x04 */ - U8 InitialFrame[28]; /* 0x08 */ -} MPI2_EVENT_HBD_PHY_SAS, MPI2_POINTER PTR_MPI2_EVENT_HBD_PHY_SAS, - Mpi2EventHbdPhySas_t, MPI2_POINTER pMpi2EventHbdPhySas_t; - -/* values for the Flags field */ -#define MPI2_EVENT_HBD_SAS_FLAGS_FRAME_VALID (0x02) -#define MPI2_EVENT_HBD_SAS_FLAGS_SATA_FRAME (0x01) - -/* use MPI2_SAS_NEG_LINK_RATE_ defines from mpi2_cnfg.h for - * the NegotiatedLinkRate field */ - -typedef union _MPI2_EVENT_HBD_DESCRIPTOR { - MPI2_EVENT_HBD_PHY_SAS Sas; -} MPI2_EVENT_HBD_DESCRIPTOR, MPI2_POINTER PTR_MPI2_EVENT_HBD_DESCRIPTOR, - Mpi2EventHbdDescriptor_t, MPI2_POINTER pMpi2EventHbdDescriptor_t; - -typedef struct _MPI2_EVENT_DATA_HBD_PHY { - U8 DescriptorType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 Reserved3; /* 0x04 */ - MPI2_EVENT_HBD_DESCRIPTOR Descriptor; /* 0x08 */ -} MPI2_EVENT_DATA_HBD_PHY, MPI2_POINTER PTR_MPI2_EVENT_DATA_HBD_PHY, - Mpi2EventDataHbdPhy_t, MPI2_POINTER pMpi2EventDataMpi2EventDataHbdPhy_t; - -/* values for the DescriptorType field */ -#define MPI2_EVENT_HBD_DT_SAS (0x01) - - - -/**************************************************************************** -* EventAck message -****************************************************************************/ - -/* EventAck Request message */ -typedef struct _MPI2_EVENT_ACK_REQUEST -{ - U16 Reserved1; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Event; /* 0x0C */ - U16 Reserved5; /* 0x0E */ - U32 EventContext; /* 0x10 */ -} MPI2_EVENT_ACK_REQUEST, MPI2_POINTER PTR_MPI2_EVENT_ACK_REQUEST, - Mpi2EventAckRequest_t, MPI2_POINTER pMpi2EventAckRequest_t; - - -/* EventAck Reply message */ -typedef struct _MPI2_EVENT_ACK_REPLY -{ - U16 Reserved1; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_EVENT_ACK_REPLY, MPI2_POINTER PTR_MPI2_EVENT_ACK_REPLY, - Mpi2EventAckReply_t, MPI2_POINTER pMpi2EventAckReply_t; - - -/**************************************************************************** -* SendHostMessage message -****************************************************************************/ - -/* SendHostMessage Request message */ -typedef struct _MPI2_SEND_HOST_MESSAGE_REQUEST { - U16 HostDataLength; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved1; /* 0x04 */ - U8 Reserved2; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U8 Reserved4; /* 0x0C */ - U8 DestVF_ID; /* 0x0D */ - U16 Reserved5; /* 0x0E */ - U32 Reserved6; /* 0x10 */ - U32 Reserved7; /* 0x14 */ - U32 Reserved8; /* 0x18 */ - U32 Reserved9; /* 0x1C */ - U32 Reserved10; /* 0x20 */ - U32 HostData[1]; /* 0x24 */ -} MPI2_SEND_HOST_MESSAGE_REQUEST, -MPI2_POINTER PTR_MPI2_SEND_HOST_MESSAGE_REQUEST, -Mpi2SendHostMessageRequest_t, MPI2_POINTER pMpi2SendHostMessageRequest_t; - - -/* SendHostMessage Reply message */ -typedef struct _MPI2_SEND_HOST_MESSAGE_REPLY { - U16 HostDataLength; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved1; /* 0x04 */ - U8 Reserved2; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U16 Reserved4; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_SEND_HOST_MESSAGE_REPLY, MPI2_POINTER PTR_MPI2_SEND_HOST_MESSAGE_REPLY, -Mpi2SendHostMessageReply_t, MPI2_POINTER pMpi2SendHostMessageReply_t; - - -/**************************************************************************** -* FWDownload message -****************************************************************************/ - -/* FWDownload Request message */ -typedef struct _MPI2_FW_DOWNLOAD_REQUEST -{ - U8 ImageType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U32 TotalImageSize; /* 0x0C */ - U32 Reserved5; /* 0x10 */ - MPI2_MPI_SGE_UNION SGL; /* 0x14 */ -} MPI2_FW_DOWNLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REQUEST, - Mpi2FWDownloadRequest, MPI2_POINTER pMpi2FWDownloadRequest; - -#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01) - -#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01) -#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02) -#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06) -#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07) -#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08) -#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09) -#define MPI2_FW_DOWNLOAD_ITYPE_COMPLETE (0x0A) -#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) -#define MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY (0x0C) -#define MPI2_FW_DOWNLOAD_ITYPE_MIN_PRODUCT_SPECIFIC (0xF0) - -/* FWDownload TransactionContext Element */ -typedef struct _MPI2_FW_DOWNLOAD_TCSGE -{ - U8 Reserved1; /* 0x00 */ - U8 ContextSize; /* 0x01 */ - U8 DetailsLength; /* 0x02 */ - U8 Flags; /* 0x03 */ - U32 Reserved2; /* 0x04 */ - U32 ImageOffset; /* 0x08 */ - U32 ImageSize; /* 0x0C */ -} MPI2_FW_DOWNLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_TCSGE, - Mpi2FWDownloadTCSGE_t, MPI2_POINTER pMpi2FWDownloadTCSGE_t; - -/* FWDownload Reply message */ -typedef struct _MPI2_FW_DOWNLOAD_REPLY -{ - U8 ImageType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_FW_DOWNLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REPLY, - Mpi2FWDownloadReply_t, MPI2_POINTER pMpi2FWDownloadReply_t; - - -/**************************************************************************** -* FWUpload message -****************************************************************************/ - -/* FWUpload Request message */ -typedef struct _MPI2_FW_UPLOAD_REQUEST -{ - U8 ImageType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U32 Reserved5; /* 0x0C */ - U32 Reserved6; /* 0x10 */ - MPI2_MPI_SGE_UNION SGL; /* 0x14 */ -} MPI2_FW_UPLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REQUEST, - Mpi2FWUploadRequest_t, MPI2_POINTER pMpi2FWUploadRequest_t; - -#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00) -#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01) -#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) -#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05) -#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06) -#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07) -#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08) -#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09) -#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A) -#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) - -typedef struct _MPI2_FW_UPLOAD_TCSGE -{ - U8 Reserved1; /* 0x00 */ - U8 ContextSize; /* 0x01 */ - U8 DetailsLength; /* 0x02 */ - U8 Flags; /* 0x03 */ - U32 Reserved2; /* 0x04 */ - U32 ImageOffset; /* 0x08 */ - U32 ImageSize; /* 0x0C */ -} MPI2_FW_UPLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_UPLOAD_TCSGE, - Mpi2FWUploadTCSGE_t, MPI2_POINTER pMpi2FWUploadTCSGE_t; - -/* FWUpload Reply message */ -typedef struct _MPI2_FW_UPLOAD_REPLY -{ - U8 ImageType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 ActualImageSize; /* 0x14 */ -} MPI2_FW_UPLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REPLY, - Mpi2FWUploadReply_t, MPI2_POINTER pMPi2FWUploadReply_t; - - -/* FW Image Header */ -typedef struct _MPI2_FW_IMAGE_HEADER -{ - U32 Signature; /* 0x00 */ - U32 Signature0; /* 0x04 */ - U32 Signature1; /* 0x08 */ - U32 Signature2; /* 0x0C */ - MPI2_VERSION_UNION MPIVersion; /* 0x10 */ - MPI2_VERSION_UNION FWVersion; /* 0x14 */ - MPI2_VERSION_UNION NVDATAVersion; /* 0x18 */ - MPI2_VERSION_UNION PackageVersion; /* 0x1C */ - U16 VendorID; /* 0x20 */ - U16 ProductID; /* 0x22 */ - U16 ProtocolFlags; /* 0x24 */ - U16 Reserved26; /* 0x26 */ - U32 IOCCapabilities; /* 0x28 */ - U32 ImageSize; /* 0x2C */ - U32 NextImageHeaderOffset; /* 0x30 */ - U32 Checksum; /* 0x34 */ - U32 Reserved38; /* 0x38 */ - U32 Reserved3C; /* 0x3C */ - U32 Reserved40; /* 0x40 */ - U32 Reserved44; /* 0x44 */ - U32 Reserved48; /* 0x48 */ - U32 Reserved4C; /* 0x4C */ - U32 Reserved50; /* 0x50 */ - U32 Reserved54; /* 0x54 */ - U32 Reserved58; /* 0x58 */ - U32 Reserved5C; /* 0x5C */ - U32 Reserved60; /* 0x60 */ - U32 FirmwareVersionNameWhat; /* 0x64 */ - U8 FirmwareVersionName[32]; /* 0x68 */ - U32 VendorNameWhat; /* 0x88 */ - U8 VendorName[32]; /* 0x8C */ - U32 PackageNameWhat; /* 0x88 */ - U8 PackageName[32]; /* 0x8C */ - U32 ReservedD0; /* 0xD0 */ - U32 ReservedD4; /* 0xD4 */ - U32 ReservedD8; /* 0xD8 */ - U32 ReservedDC; /* 0xDC */ - U32 ReservedE0; /* 0xE0 */ - U32 ReservedE4; /* 0xE4 */ - U32 ReservedE8; /* 0xE8 */ - U32 ReservedEC; /* 0xEC */ - U32 ReservedF0; /* 0xF0 */ - U32 ReservedF4; /* 0xF4 */ - U32 ReservedF8; /* 0xF8 */ - U32 ReservedFC; /* 0xFC */ -} MPI2_FW_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_FW_IMAGE_HEADER, - Mpi2FWImageHeader_t, MPI2_POINTER pMpi2FWImageHeader_t; - -/* Signature field */ -#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00) -#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000) -#define MPI2_FW_HEADER_SIGNATURE (0xEA000000) - -/* Signature0 field */ -#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04) -#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A) - -/* Signature1 field */ -#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08) -#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5) - -/* Signature2 field */ -#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C) -#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA) - - -/* defines for using the ProductID field */ -#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000) -#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000) - -#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) -#define MPI2_FW_HEADER_PID_PROD_A (0x0000) -#define MPI2_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200) -#define MPI2_FW_HEADER_PID_PROD_IR_SCSI (0x0700) - - -#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) -/* SAS */ -#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0013) -#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS (0x0014) - -/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ - -/* use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */ - - -#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C) -#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30) -#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64) - -#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840) - -#define MPI2_FW_HEADER_SIZE (0x100) - - -/* Extended Image Header */ -typedef struct _MPI2_EXT_IMAGE_HEADER - -{ - U8 ImageType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 Checksum; /* 0x04 */ - U32 ImageSize; /* 0x08 */ - U32 NextImageHeaderOffset; /* 0x0C */ - U32 PackageVersion; /* 0x10 */ - U32 Reserved3; /* 0x14 */ - U32 Reserved4; /* 0x18 */ - U32 Reserved5; /* 0x1C */ - U8 IdentifyString[32]; /* 0x20 */ -} MPI2_EXT_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_EXT_IMAGE_HEADER, - Mpi2ExtImageHeader_t, MPI2_POINTER pMpi2ExtImageHeader_t; - -/* useful offsets */ -#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00) -#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08) -#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C) - -#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40) - -/* defines for the ImageType field */ -#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) -#define MPI2_EXT_IMAGE_TYPE_FW (0x01) -#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) -#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) -#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) -#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) -#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) -#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) -#define MPI2_EXT_IMAGE_TYPE_ENCRYPTED_HASH (0x09) -#define MPI2_EXT_IMAGE_TYPE_MIN_PRODUCT_SPECIFIC (0x80) -#define MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC (0xFF) -#define MPI2_EXT_IMAGE_TYPE_MAX \ - (MPI2_EXT_IMAGE_TYPE_MAX_PRODUCT_SPECIFIC) /* deprecated */ - - - -/* FLASH Layout Extended Image Data */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check RegionsPerLayout at runtime. - */ -#ifndef MPI2_FLASH_NUMBER_OF_REGIONS -#define MPI2_FLASH_NUMBER_OF_REGIONS (1) -#endif - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check NumberOfLayouts at runtime. - */ -#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS -#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1) -#endif - -typedef struct _MPI2_FLASH_REGION -{ - U8 RegionType; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 RegionOffset; /* 0x04 */ - U32 RegionSize; /* 0x08 */ - U32 Reserved3; /* 0x0C */ -} MPI2_FLASH_REGION, MPI2_POINTER PTR_MPI2_FLASH_REGION, - Mpi2FlashRegion_t, MPI2_POINTER pMpi2FlashRegion_t; - -typedef struct _MPI2_FLASH_LAYOUT -{ - U32 FlashSize; /* 0x00 */ - U32 Reserved1; /* 0x04 */ - U32 Reserved2; /* 0x08 */ - U32 Reserved3; /* 0x0C */ - MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS];/* 0x10 */ -} MPI2_FLASH_LAYOUT, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT, - Mpi2FlashLayout_t, MPI2_POINTER pMpi2FlashLayout_t; - -typedef struct _MPI2_FLASH_LAYOUT_DATA -{ - U8 ImageRevision; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 SizeOfRegion; /* 0x02 */ - U8 Reserved2; /* 0x03 */ - U16 NumberOfLayouts; /* 0x04 */ - U16 RegionsPerLayout; /* 0x06 */ - U16 MinimumSectorAlignment; /* 0x08 */ - U16 Reserved3; /* 0x0A */ - U32 Reserved4; /* 0x0C */ - MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS];/* 0x10 */ -} MPI2_FLASH_LAYOUT_DATA, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT_DATA, - Mpi2FlashLayoutData_t, MPI2_POINTER pMpi2FlashLayoutData_t; - -/* defines for the RegionType field */ -#define MPI2_FLASH_REGION_UNUSED (0x00) -#define MPI2_FLASH_REGION_FIRMWARE (0x01) -#define MPI2_FLASH_REGION_BIOS (0x02) -#define MPI2_FLASH_REGION_NVDATA (0x03) -#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05) -#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06) -#define MPI2_FLASH_REGION_CONFIG_1 (0x07) -#define MPI2_FLASH_REGION_CONFIG_2 (0x08) -#define MPI2_FLASH_REGION_MEGARAID (0x09) -#define MPI2_FLASH_REGION_INIT (0x0A) - -/* ImageRevision */ -#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) - - - -/* Supported Devices Extended Image Data */ - -/* - * Host code (drivers, BIOS, utilities, etc.) should leave this define set to - * one and check NumberOfDevices at runtime. - */ -#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES -#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1) -#endif - -typedef struct _MPI2_SUPPORTED_DEVICE -{ - U16 DeviceID; /* 0x00 */ - U16 VendorID; /* 0x02 */ - U16 DeviceIDMask; /* 0x04 */ - U16 Reserved1; /* 0x06 */ - U8 LowPCIRev; /* 0x08 */ - U8 HighPCIRev; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U32 Reserved3; /* 0x0C */ -} MPI2_SUPPORTED_DEVICE, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICE, - Mpi2SupportedDevice_t, MPI2_POINTER pMpi2SupportedDevice_t; - -typedef struct _MPI2_SUPPORTED_DEVICES_DATA -{ - U8 ImageRevision; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 NumberOfDevices; /* 0x02 */ - U8 Reserved2; /* 0x03 */ - U32 Reserved3; /* 0x04 */ - MPI2_SUPPORTED_DEVICE SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES]; /* 0x08 */ -} MPI2_SUPPORTED_DEVICES_DATA, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICES_DATA, - Mpi2SupportedDevicesData_t, MPI2_POINTER pMpi2SupportedDevicesData_t; - -/* ImageRevision */ -#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00) - - -/* Init Extended Image Data */ - -typedef struct _MPI2_INIT_IMAGE_FOOTER - -{ - U32 BootFlags; /* 0x00 */ - U32 ImageSize; /* 0x04 */ - U32 Signature0; /* 0x08 */ - U32 Signature1; /* 0x0C */ - U32 Signature2; /* 0x10 */ - U32 ResetVector; /* 0x14 */ -} MPI2_INIT_IMAGE_FOOTER, MPI2_POINTER PTR_MPI2_INIT_IMAGE_FOOTER, - Mpi2InitImageFooter_t, MPI2_POINTER pMpi2InitImageFooter_t; - -/* defines for the BootFlags field */ -#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00) - -/* defines for the ImageSize field */ -#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04) - -/* defines for the Signature0 field */ -#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08) -#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA) - -/* defines for the Signature1 field */ -#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C) -#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5) - -/* defines for the Signature2 field */ -#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10) -#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A) - -/* Signature fields as individual bytes */ -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A) - -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5) - -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA) -#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A) - -/* defines for the ResetVector field */ -#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) - - -/* Encrypted Hash Extended Image Data */ - -typedef struct _MPI25_ENCRYPTED_HASH_ENTRY { - U8 HashImageType; /* 0x00 */ - U8 HashAlgorithm; /* 0x01 */ - U8 EncryptionAlgorithm; /* 0x02 */ - U8 Reserved1; /* 0x03 */ - U32 Reserved2; /* 0x04 */ - U32 EncryptedHash[1]; /* 0x08 */ -} MPI25_ENCRYPTED_HASH_ENTRY, MPI2_POINTER PTR_MPI25_ENCRYPTED_HASH_ENTRY, -Mpi25EncryptedHashEntry_t, MPI2_POINTER pMpi25EncryptedHashEntry_t; - -/* values for HashImageType */ -#define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) -#define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) -#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) - -/* values for HashAlgorithm */ -#define MPI25_HASH_ALGORITHM_UNUSED (0x00) -#define MPI25_HASH_ALGORITHM_SHA256 (0x01) - -/* values for EncryptionAlgorithm */ -#define MPI25_ENCRYPTION_ALG_UNUSED (0x00) -#define MPI25_ENCRYPTION_ALG_RSA256 (0x01) - -typedef struct _MPI25_ENCRYPTED_HASH_DATA { - U8 ImageVersion; /* 0x00 */ - U8 NumHash; /* 0x01 */ - U16 Reserved1; /* 0x02 */ - U32 Reserved2; /* 0x04 */ - MPI25_ENCRYPTED_HASH_ENTRY EncryptedHashEntry[1]; /* 0x08 */ -} MPI25_ENCRYPTED_HASH_DATA, MPI2_POINTER PTR_MPI25_ENCRYPTED_HASH_DATA, -Mpi25EncryptedHashData_t, MPI2_POINTER pMpi25EncryptedHashData_t; - -/**************************************************************************** -* PowerManagementControl message -****************************************************************************/ - -/* PowerManagementControl Request message */ -typedef struct _MPI2_PWR_MGMT_CONTROL_REQUEST { - U8 Feature; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U8 Parameter1; /* 0x0C */ - U8 Parameter2; /* 0x0D */ - U8 Parameter3; /* 0x0E */ - U8 Parameter4; /* 0x0F */ - U32 Reserved5; /* 0x10 */ - U32 Reserved6; /* 0x14 */ -} MPI2_PWR_MGMT_CONTROL_REQUEST, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REQUEST, - Mpi2PwrMgmtControlRequest_t, MPI2_POINTER pMpi2PwrMgmtControlRequest_t; - -/* defines for the Feature field */ -#define MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND (0x01) -#define MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION (0x02) -#define MPI2_PM_CONTROL_FEATURE_PCIE_LINK (0x03) /* obsolete */ -#define MPI2_PM_CONTROL_FEATURE_IOC_SPEED (0x04) -#define MPI2_PM_CONTROL_FEATURE_MIN_PRODUCT_SPECIFIC (0x80) -#define MPI2_PM_CONTROL_FEATURE_MAX_PRODUCT_SPECIFIC (0xFF) - -/* parameter usage for the MPI2_PM_CONTROL_FEATURE_DA_PHY_POWER_COND Feature */ -/* Parameter1 contains a PHY number */ -/* Parameter2 indicates power condition action using these defines */ -#define MPI2_PM_CONTROL_PARAM2_PARTIAL (0x01) -#define MPI2_PM_CONTROL_PARAM2_SLUMBER (0x02) -#define MPI2_PM_CONTROL_PARAM2_EXIT_PWR_MGMT (0x03) -/* Parameter3 and Parameter4 are reserved */ - -/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PORT_WIDTH_MODULATION - * Feature */ -/* Parameter1 contains SAS port width modulation group number */ -/* Parameter2 indicates IOC action using these defines */ -#define MPI2_PM_CONTROL_PARAM2_REQUEST_OWNERSHIP (0x01) -#define MPI2_PM_CONTROL_PARAM2_CHANGE_MODULATION (0x02) -#define MPI2_PM_CONTROL_PARAM2_RELINQUISH_OWNERSHIP (0x03) -/* Parameter3 indicates desired modulation level using these defines */ -#define MPI2_PM_CONTROL_PARAM3_25_PERCENT (0x00) -#define MPI2_PM_CONTROL_PARAM3_50_PERCENT (0x01) -#define MPI2_PM_CONTROL_PARAM3_75_PERCENT (0x02) -#define MPI2_PM_CONTROL_PARAM3_100_PERCENT (0x03) -/* Parameter4 is reserved */ - -/* parameter usage for the MPI2_PM_CONTROL_FEATURE_PCIE_LINK Feature */ -/* Parameter1 indicates desired PCIe link speed using these defines */ -#define MPI2_PM_CONTROL_PARAM1_PCIE_2_5_GBPS (0x00) /* obsolete */ -#define MPI2_PM_CONTROL_PARAM1_PCIE_5_0_GBPS (0x01) /* obsolete */ -#define MPI2_PM_CONTROL_PARAM1_PCIE_8_0_GBPS (0x02) /* obsolete */ -/* Parameter2 indicates desired PCIe link width using these defines */ -#define MPI2_PM_CONTROL_PARAM2_WIDTH_X1 (0x01) /* obsolete */ -#define MPI2_PM_CONTROL_PARAM2_WIDTH_X2 (0x02) /* obsolete */ -#define MPI2_PM_CONTROL_PARAM2_WIDTH_X4 (0x04) /* obsolete */ -#define MPI2_PM_CONTROL_PARAM2_WIDTH_X8 (0x08) /* obsolete */ -/* Parameter3 and Parameter4 are reserved */ - -/* parameter usage for the MPI2_PM_CONTROL_FEATURE_IOC_SPEED Feature */ -/* Parameter1 indicates desired IOC hardware clock speed using these defines */ -#define MPI2_PM_CONTROL_PARAM1_FULL_IOC_SPEED (0x01) -#define MPI2_PM_CONTROL_PARAM1_HALF_IOC_SPEED (0x02) -#define MPI2_PM_CONTROL_PARAM1_QUARTER_IOC_SPEED (0x04) -#define MPI2_PM_CONTROL_PARAM1_EIGHTH_IOC_SPEED (0x08) -/* Parameter2, Parameter3, and Parameter4 are reserved */ - - -/* PowerManagementControl Reply message */ -typedef struct _MPI2_PWR_MGMT_CONTROL_REPLY { - U8 Feature; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_PWR_MGMT_CONTROL_REPLY, MPI2_POINTER PTR_MPI2_PWR_MGMT_CONTROL_REPLY, - Mpi2PwrMgmtControlReply_t, MPI2_POINTER pMpi2PwrMgmtControlReply_t; - - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_raid.h deleted file mode 100644 index 7efa58ff0..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_raid.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_raid.h - * Title: MPI Integrated RAID messages and structures - * Creation Date: April 26, 2007 - * - * mpi2_raid.h Version: 02.00.10 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 08-31-07 02.00.01 Modifications to RAID Action request and reply, - * including the Actions and ActionData. - * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. - * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that - * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT - * can be sized by the build environment. - * 07-30-09 02.00.04 Added proper define for the Use Default Settings bit of - * VolumeCreationFlags and marked the old one as obsolete. - * 05-12-10 02.00.05 Added MPI2_RAID_VOL_FLAGS_OP_MDC define. - * 08-24-10 02.00.06 Added MPI2_RAID_ACTION_COMPATIBILITY_CHECK along with - * related structures and defines. - * Added product-specific range to RAID Action values. - * 02-06-12 02.00.08 Added MPI2_RAID_ACTION_PHYSDISK_HIDDEN. - * 07-26-12 02.00.09 Added ElapsedSeconds field to MPI2_RAID_VOL_INDICATOR. - * Added MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID define. - * 04-17-13 02.00.10 Added MPI25_RAID_ACTION_ADATA_ALLOW_PI. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_RAID_H -#define MPI2_RAID_H - -/***************************************************************************** -* -* Integrated RAID Messages -* -*****************************************************************************/ - -/**************************************************************************** -* RAID Action messages -****************************************************************************/ - -/* ActionDataWord defines for use with MPI2_RAID_ACTION_CREATE_VOLUME action */ -#define MPI25_RAID_ACTION_ADATA_ALLOW_PI (0x80000000) - -/* ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */ -#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000) -#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001) - -/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ - -/* ActionDataWord defines for use with MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */ -#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001) - -/* ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */ -typedef struct _MPI2_RAID_ACTION_RATE_DATA -{ - U8 RateToChange; /* 0x00 */ - U8 RateOrMode; /* 0x01 */ - U16 DataScrubDuration; /* 0x02 */ -} MPI2_RAID_ACTION_RATE_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_RATE_DATA, - Mpi2RaidActionRateData_t, MPI2_POINTER pMpi2RaidActionRateData_t; - -#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00) -#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01) -#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02) - -/* ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */ -typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION -{ - U8 RAIDFunction; /* 0x00 */ - U8 Flags; /* 0x01 */ - U16 Reserved1; /* 0x02 */ -} MPI2_RAID_ACTION_START_RAID_FUNCTION, - MPI2_POINTER PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION, - Mpi2RaidActionStartRaidFunction_t, - MPI2_POINTER pMpi2RaidActionStartRaidFunction_t; - -/* defines for the RAIDFunction field */ -#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00) -#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01) -#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02) - -/* defines for the Flags field */ -#define MPI2_RAID_ACTION_START_NEW (0x00) -#define MPI2_RAID_ACTION_START_RESUME (0x01) - -/* ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */ -typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION -{ - U8 RAIDFunction; /* 0x00 */ - U8 Flags; /* 0x01 */ - U16 Reserved1; /* 0x02 */ -} MPI2_RAID_ACTION_STOP_RAID_FUNCTION, - MPI2_POINTER PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION, - Mpi2RaidActionStopRaidFunction_t, - MPI2_POINTER pMpi2RaidActionStopRaidFunction_t; - -/* defines for the RAIDFunction field */ -#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00) -#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01) -#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02) - -/* defines for the Flags field */ -#define MPI2_RAID_ACTION_STOP_ABORT (0x00) -#define MPI2_RAID_ACTION_STOP_PAUSE (0x01) - -/* ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */ -typedef struct _MPI2_RAID_ACTION_HOT_SPARE -{ - U8 HotSparePool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 DevHandle; /* 0x02 */ -} MPI2_RAID_ACTION_HOT_SPARE, MPI2_POINTER PTR_MPI2_RAID_ACTION_HOT_SPARE, - Mpi2RaidActionHotSpare_t, MPI2_POINTER pMpi2RaidActionHotSpare_t; - -/* ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */ -typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE -{ - U8 Flags; /* 0x00 */ - U8 DeviceFirmwareUpdateModeTimeout; /* 0x01 */ - U16 Reserved1; /* 0x02 */ -} MPI2_RAID_ACTION_FW_UPDATE_MODE, - MPI2_POINTER PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE, - Mpi2RaidActionFwUpdateMode_t, MPI2_POINTER pMpi2RaidActionFwUpdateMode_t; - -/* ActionDataWord defines for use with MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */ -#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00) -#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01) - -typedef union _MPI2_RAID_ACTION_DATA -{ - U32 Word; - MPI2_RAID_ACTION_RATE_DATA Rates; - MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction; - MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction; - MPI2_RAID_ACTION_HOT_SPARE HotSpare; - MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode; -} MPI2_RAID_ACTION_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_DATA, - Mpi2RaidActionData_t, MPI2_POINTER pMpi2RaidActionData_t; - - -/* RAID Action Request Message */ -typedef struct _MPI2_RAID_ACTION_REQUEST -{ - U8 Action; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 VolDevHandle; /* 0x04 */ - U8 PhysDiskNum; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U32 Reserved3; /* 0x0C */ - MPI2_RAID_ACTION_DATA ActionDataWord; /* 0x10 */ - MPI2_SGE_SIMPLE_UNION ActionDataSGE; /* 0x14 */ -} MPI2_RAID_ACTION_REQUEST, MPI2_POINTER PTR_MPI2_RAID_ACTION_REQUEST, - Mpi2RaidActionRequest_t, MPI2_POINTER pMpi2RaidActionRequest_t; - -/* RAID Action request Action values */ - -#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01) -#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02) -#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03) -#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04) -#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05) -#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A) -#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B) -#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F) -#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11) -#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15) -#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17) -#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18) -#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19) -#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C) -#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D) -#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E) -#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20) -#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21) -#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22) -#define MPI2_RAID_ACTION_COMPATIBILITY_CHECK (0x23) -#define MPI2_RAID_ACTION_PHYSDISK_HIDDEN (0x24) -#define MPI2_RAID_ACTION_MIN_PRODUCT_SPECIFIC (0x80) -#define MPI2_RAID_ACTION_MAX_PRODUCT_SPECIFIC (0xFF) - -/* RAID Volume Creation Structure */ - -/* - * The following define can be customized for the targeted product. - */ -#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS -#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1) -#endif - -typedef struct _MPI2_RAID_VOLUME_PHYSDISK -{ - U8 RAIDSetNum; /* 0x00 */ - U8 PhysDiskMap; /* 0x01 */ - U16 PhysDiskDevHandle; /* 0x02 */ -} MPI2_RAID_VOLUME_PHYSDISK, MPI2_POINTER PTR_MPI2_RAID_VOLUME_PHYSDISK, - Mpi2RaidVolumePhysDisk_t, MPI2_POINTER pMpi2RaidVolumePhysDisk_t; - -/* defines for the PhysDiskMap field */ -#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01) -#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02) - -typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT -{ - U8 NumPhysDisks; /* 0x00 */ - U8 VolumeType; /* 0x01 */ - U16 Reserved1; /* 0x02 */ - U32 VolumeCreationFlags; /* 0x04 */ - U32 VolumeSettings; /* 0x08 */ - U8 Reserved2; /* 0x0C */ - U8 ResyncRate; /* 0x0D */ - U16 DataScrubDuration; /* 0x0E */ - U64 VolumeMaxLBA; /* 0x10 */ - U32 StripeSize; /* 0x18 */ - U8 Name[16]; /* 0x1C */ - MPI2_RAID_VOLUME_PHYSDISK PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS];/* 0x2C */ -} MPI2_RAID_VOLUME_CREATION_STRUCT, - MPI2_POINTER PTR_MPI2_RAID_VOLUME_CREATION_STRUCT, - Mpi2RaidVolumeCreationStruct_t, MPI2_POINTER pMpi2RaidVolumeCreationStruct_t; - -/* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */ - -/* defines for the VolumeCreationFlags field */ -#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS (0x80000000) -#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x00000004) -#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x00000002) -#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x00000001) -/* The following is an obsolete define. - * It must be shifted left 24 bits in order to set the proper bit. - */ -#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80) - - -/* RAID Online Capacity Expansion Structure */ - -typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION -{ - U32 Flags; /* 0x00 */ - U16 DevHandle0; /* 0x04 */ - U16 Reserved1; /* 0x06 */ - U16 DevHandle1; /* 0x08 */ - U16 Reserved2; /* 0x0A */ -} MPI2_RAID_ONLINE_CAPACITY_EXPANSION, - MPI2_POINTER PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION, - Mpi2RaidOnlineCapacityExpansion_t, - MPI2_POINTER pMpi2RaidOnlineCapacityExpansion_t; - -/* RAID Compatibility Input Structure */ - -typedef struct _MPI2_RAID_COMPATIBILITY_INPUT_STRUCT { - U16 SourceDevHandle; /* 0x00 */ - U16 CandidateDevHandle; /* 0x02 */ - U32 Flags; /* 0x04 */ - U32 Reserved1; /* 0x08 */ - U32 Reserved2; /* 0x0C */ -} MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, -MPI2_POINTER PTR_MPI2_RAID_COMPATIBILITY_INPUT_STRUCT, -Mpi2RaidCompatibilityInputStruct_t, -MPI2_POINTER pMpi2RaidCompatibilityInputStruct_t; - -/* defines for RAID Compatibility Structure Flags field */ -#define MPI2_RAID_COMPAT_SOURCE_IS_VOLUME_FLAG (0x00000002) -#define MPI2_RAID_COMPAT_REPORT_SOURCE_INFO_FLAG (0x00000001) - - -/* RAID Volume Indicator Structure */ - -typedef struct _MPI2_RAID_VOL_INDICATOR -{ - U64 TotalBlocks; /* 0x00 */ - U64 BlocksRemaining; /* 0x08 */ - U32 Flags; /* 0x10 */ - U32 ElapsedSeconds; /* 0x14 */ -} MPI2_RAID_VOL_INDICATOR, MPI2_POINTER PTR_MPI2_RAID_VOL_INDICATOR, - Mpi2RaidVolIndicator_t, MPI2_POINTER pMpi2RaidVolIndicator_t; - -/* defines for RAID Volume Indicator Flags field */ -#define MPI2_RAID_VOL_FLAGS_ELAPSED_SECONDS_VALID (0x80000000) - -#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F) -#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000) -#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001) -#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002) -#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003) -#define MPI2_RAID_VOL_FLAGS_OP_MDC (0x00000004) - -/* RAID Compatibility Result Structure */ - -typedef struct _MPI2_RAID_COMPATIBILITY_RESULT_STRUCT { - U8 State; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U16 Reserved2; /* 0x02 */ - U32 GenericAttributes; /* 0x04 */ - U32 OEMSpecificAttributes; /* 0x08 */ - U32 Reserved3; /* 0x0C */ - U32 Reserved4; /* 0x10 */ -} MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, -MPI2_POINTER PTR_MPI2_RAID_COMPATIBILITY_RESULT_STRUCT, -Mpi2RaidCompatibilityResultStruct_t, -MPI2_POINTER pMpi2RaidCompatibilityResultStruct_t; - -/* defines for RAID Compatibility Result Structure State field */ -#define MPI2_RAID_COMPAT_STATE_COMPATIBLE (0x00) -#define MPI2_RAID_COMPAT_STATE_NOT_COMPATIBLE (0x01) - -/* defines for RAID Compatibility Result Structure GenericAttributes field */ -#define MPI2_RAID_COMPAT_GENATTRIB_4K_SECTOR (0x00000010) - -#define MPI2_RAID_COMPAT_GENATTRIB_MEDIA_MASK (0x0000000C) -#define MPI2_RAID_COMPAT_GENATTRIB_SOLID_STATE_DRIVE (0x00000008) -#define MPI2_RAID_COMPAT_GENATTRIB_HARD_DISK_DRIVE (0x00000004) - -#define MPI2_RAID_COMPAT_GENATTRIB_PROTOCOL_MASK (0x00000003) -#define MPI2_RAID_COMPAT_GENATTRIB_SAS_PROTOCOL (0x00000002) -#define MPI2_RAID_COMPAT_GENATTRIB_SATA_PROTOCOL (0x00000001) - -/* RAID Action Reply ActionData union */ -typedef union _MPI2_RAID_ACTION_REPLY_DATA -{ - U32 Word[6]; - MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; - U16 VolDevHandle; - U8 VolumeState; - U8 PhysDiskNum; - MPI2_RAID_COMPATIBILITY_RESULT_STRUCT RaidCompatibilityResult; -} MPI2_RAID_ACTION_REPLY_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY_DATA, - Mpi2RaidActionReplyData_t, MPI2_POINTER pMpi2RaidActionReplyData_t; - -/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ - - -/* RAID Action Reply Message */ -typedef struct _MPI2_RAID_ACTION_REPLY -{ - U8 Action; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 VolDevHandle; /* 0x04 */ - U8 PhysDiskNum; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved2; /* 0x0A */ - U16 Reserved3; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - MPI2_RAID_ACTION_REPLY_DATA ActionData; /* 0x14 */ -} MPI2_RAID_ACTION_REPLY, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY, - Mpi2RaidActionReply_t, MPI2_POINTER pMpi2RaidActionReply_t; - - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_sas.h deleted file mode 100644 index 45b6fa10b..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_sas.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_sas.h - * Title: MPI Serial Attached SCSI structures and definitions - * Creation Date: February 9, 2007 - * - * mpi2_sas.h Version: 02.00.05 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit - * Control Request. - * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control - * Request. - * 10-28-09 02.00.03 Changed the type of SGL in MPI2_SATA_PASSTHROUGH_REQUEST - * to MPI2_SGE_IO_UNION since it supports chained SGLs. - * 05-12-10 02.00.04 Modified some comments. - * 08-11-10 02.00.05 Added NCQ operations to SAS IO Unit Control. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_SAS_H -#define MPI2_SAS_H - -/* - * Values for SASStatus. - */ -#define MPI2_SASSTATUS_SUCCESS (0x00) -#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01) -#define MPI2_SASSTATUS_INVALID_FRAME (0x02) -#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03) -#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04) -#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05) -#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06) -#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07) -#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08) -#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09) -#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A) -#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B) -#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C) -#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D) -#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E) -#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F) -#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10) -#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11) -#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12) -#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13) -#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14) - - -/* - * Values for the SAS DeviceInfo field used in SAS Device Status Change Event - * data and SAS Configuration pages. - */ -#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000) -#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000) -#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000) -#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800) -#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400) -#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200) -#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100) -#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080) -#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040) -#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020) -#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010) -#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008) - -#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007) -#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000) -#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001) -#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002) -#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003) - - -/***************************************************************************** -* -* SAS Messages -* -*****************************************************************************/ - -/**************************************************************************** -* SMP Passthrough messages -****************************************************************************/ - -/* SMP Passthrough Request Message */ -typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST -{ - U8 PassthroughFlags; /* 0x00 */ - U8 PhysicalPort; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 RequestDataLength; /* 0x04 */ - U8 SGLFlags; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U32 Reserved2; /* 0x0C */ - U64 SASAddress; /* 0x10 */ - U32 Reserved3; /* 0x18 */ - U32 Reserved4; /* 0x1C */ - MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */ -} MPI2_SMP_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REQUEST, - Mpi2SmpPassthroughRequest_t, MPI2_POINTER pMpi2SmpPassthroughRequest_t; - -/* values for PassthroughFlags field */ -#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - - -/* SMP Passthrough Reply Message */ -typedef struct _MPI2_SMP_PASSTHROUGH_REPLY -{ - U8 PassthroughFlags; /* 0x00 */ - U8 PhysicalPort; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 ResponseDataLength; /* 0x04 */ - U8 SGLFlags; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U8 Reserved2; /* 0x0C */ - U8 SASStatus; /* 0x0D */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 Reserved3; /* 0x14 */ - U8 ResponseData[4]; /* 0x18 */ -} MPI2_SMP_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REPLY, - Mpi2SmpPassthroughReply_t, MPI2_POINTER pMpi2SmpPassthroughReply_t; - -/* values for PassthroughFlags field */ -#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80) - -/* values for SASStatus field are at the top of this file */ - - -/**************************************************************************** -* SATA Passthrough messages -****************************************************************************/ - -/* SATA Passthrough Request Message */ -typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST -{ - U16 DevHandle; /* 0x00 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 PassthroughFlags; /* 0x04 */ - U8 SGLFlags; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U32 Reserved2; /* 0x0C */ - U32 Reserved3; /* 0x10 */ - U32 Reserved4; /* 0x14 */ - U32 DataLength; /* 0x18 */ - U8 CommandFIS[20]; /* 0x1C */ - MPI2_SGE_IO_UNION SGL; /* 0x30 */ -} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST, - Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t; - -/* values for PassthroughFlags field */ -#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100) -#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020) -#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010) -#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004) -#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002) -#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001) - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - - -/* SATA Passthrough Reply Message */ -typedef struct _MPI2_SATA_PASSTHROUGH_REPLY -{ - U16 DevHandle; /* 0x00 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 PassthroughFlags; /* 0x04 */ - U8 SGLFlags; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved1; /* 0x0A */ - U8 Reserved2; /* 0x0C */ - U8 SASStatus; /* 0x0D */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U8 StatusFIS[20]; /* 0x14 */ - U32 StatusControlRegisters; /* 0x28 */ - U32 TransferCount; /* 0x2C */ -} MPI2_SATA_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REPLY, - Mpi2SataPassthroughReply_t, MPI2_POINTER pMpi2SataPassthroughReply_t; - -/* values for SASStatus field are at the top of this file */ - - -/**************************************************************************** -* SAS IO Unit Control messages -****************************************************************************/ - -/* SAS IO Unit Control Request Message */ -typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST -{ - U8 Operation; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 DevHandle; /* 0x04 */ - U8 IOCParameter; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U16 Reserved4; /* 0x0C */ - U8 PhyNum; /* 0x0E */ - U8 PrimFlags; /* 0x0F */ - U32 Primitive; /* 0x10 */ - U8 LookupMethod; /* 0x14 */ - U8 Reserved5; /* 0x15 */ - U16 SlotNumber; /* 0x16 */ - U64 LookupAddress; /* 0x18 */ - U32 IOCParameterValue; /* 0x20 */ - U32 Reserved7; /* 0x24 */ - U32 Reserved8; /* 0x28 */ -} MPI2_SAS_IOUNIT_CONTROL_REQUEST, - MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST, - Mpi2SasIoUnitControlRequest_t, MPI2_POINTER pMpi2SasIoUnitControlRequest_t; - -/* values for the Operation field */ -#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02) -#define MPI2_SAS_OP_PHY_LINK_RESET (0x06) -#define MPI2_SAS_OP_PHY_HARD_RESET (0x07) -#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08) -#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A) -#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B) -#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C) -#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D) -#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E) -#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F) -#define MPI2_SAS_OP_DEV_ENABLE_NCQ (0x14) -#define MPI2_SAS_OP_DEV_DISABLE_NCQ (0x15) -#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80) - -/* values for the PrimFlags field */ -#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08) -#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02) -#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01) - -/* values for the LookupMethod field */ -#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01) -#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02) -#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) - - -/* SAS IO Unit Control Reply Message */ -typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY -{ - U8 Operation; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 DevHandle; /* 0x04 */ - U8 IOCParameter; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved3; /* 0x0A */ - U16 Reserved4; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_SAS_IOUNIT_CONTROL_REPLY, - MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY, - Mpi2SasIoUnitControlReply_t, MPI2_POINTER pMpi2SasIoUnitControlReply_t; - - -#endif - - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_tool.h deleted file mode 100644 index 659b8ac83..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_tool.h - * Title: MPI diagnostic tool structures and definitions - * Creation Date: March 26, 2007 - * - * mpi2_tool.h Version: 02.00.12 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release - * structures and defines. - * 02-29-08 02.00.02 Modified various names to make them 32-character unique. - * 05-06-09 02.00.03 Added ISTWI Read Write Tool and Diagnostic CLI Tool. - * 07-30-09 02.00.04 Added ExtendedType field to DiagnosticBufferPost request - * and reply messages. - * Added MPI2_DIAG_BUF_TYPE_EXTENDED. - * Incremented MPI2_DIAG_BUF_TYPE_COUNT. - * 05-12-10 02.00.05 Added Diagnostic Data Upload tool. - * 08-11-10 02.00.06 Added defines that were missing for Diagnostic Buffer - * Post Request. - * 05-25-11 02.00.07 Added Flags field and related defines to - * MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST. - * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that - * it uses MPI Chain SGE as well as MPI Simple SGE. - * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. - * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_TOOL_H -#define MPI2_TOOL_H - -/***************************************************************************** -* -* Toolbox Messages -* -*****************************************************************************/ - -/* defines for the Tools */ -#define MPI2_TOOLBOX_CLEAN_TOOL (0x00) -#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) -#define MPI2_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02) -#define MPI2_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) -#define MPI2_TOOLBOX_BEACON_TOOL (0x05) -#define MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL (0x06) -#define MPI2_TOOLBOX_TEXT_DISPLAY_TOOL (0x07) - - -/**************************************************************************** -* Toolbox reply -****************************************************************************/ - -typedef struct _MPI2_TOOLBOX_REPLY -{ - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_TOOLBOX_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_REPLY, - Mpi2ToolboxReply_t, MPI2_POINTER pMpi2ToolboxReply_t; - - -/**************************************************************************** -* Toolbox Clean Tool request -****************************************************************************/ - -typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST -{ - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U32 Flags; /* 0x0C */ - } MPI2_TOOLBOX_CLEAN_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_CLEAN_REQUEST, - Mpi2ToolboxCleanRequest_t, MPI2_POINTER pMpi2ToolboxCleanRequest_t; - -/* values for the Flags field */ -#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000) -#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000) -#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) -#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) -#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) -#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) -#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) -#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) -#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) -#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002) -#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001) - - -/**************************************************************************** -* Toolbox Memory Move request -****************************************************************************/ - -typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - MPI2_SGE_SIMPLE_UNION SGL; /* 0x0C */ -} MPI2_TOOLBOX_MEM_MOVE_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST, - Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t; - - -/**************************************************************************** -* Toolbox Diagnostic Data Upload request -****************************************************************************/ - -typedef struct _MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U8 SGLFlags; /* 0x0C */ - U8 Reserved5; /* 0x0D */ - U16 Reserved6; /* 0x0E */ - U32 Flags; /* 0x10 */ - U32 DataLength; /* 0x14 */ - MPI2_SGE_SIMPLE_UNION SGL; /* 0x18 */ -} MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, -MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_DATA_UPLOAD_REQUEST, -Mpi2ToolboxDiagDataUploadRequest_t, -MPI2_POINTER pMpi2ToolboxDiagDataUploadRequest_t; - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - - -typedef struct _MPI2_DIAG_DATA_UPLOAD_HEADER { - U32 DiagDataLength; /* 00h */ - U8 FormatCode; /* 04h */ - U8 Reserved1; /* 05h */ - U16 Reserved2; /* 06h */ -} MPI2_DIAG_DATA_UPLOAD_HEADER, MPI2_POINTER PTR_MPI2_DIAG_DATA_UPLOAD_HEADER, -Mpi2DiagDataUploadHeader_t, MPI2_POINTER pMpi2DiagDataUploadHeader_t; - - -/**************************************************************************** -* Toolbox ISTWI Read Write Tool -****************************************************************************/ - -/* Toolbox ISTWI Read Write Tool request message */ -typedef struct _MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U32 Reserved5; /* 0x0C */ - U32 Reserved6; /* 0x10 */ - U8 DevIndex; /* 0x14 */ - U8 Action; /* 0x15 */ - U8 SGLFlags; /* 0x16 */ - U8 Flags; /* 0x17 */ - U16 TxDataLength; /* 0x18 */ - U16 RxDataLength; /* 0x1A */ - U32 Reserved8; /* 0x1C */ - U32 Reserved9; /* 0x20 */ - U32 Reserved10; /* 0x24 */ - U32 Reserved11; /* 0x28 */ - U32 Reserved12; /* 0x2C */ - MPI2_SGE_SIMPLE_UNION SGL; /* 0x30 */ -} MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, - MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_READ_WRITE_REQUEST, - Mpi2ToolboxIstwiReadWriteRequest_t, - MPI2_POINTER pMpi2ToolboxIstwiReadWriteRequest_t; - -/* values for the Action field */ -#define MPI2_TOOL_ISTWI_ACTION_READ_DATA (0x01) -#define MPI2_TOOL_ISTWI_ACTION_WRITE_DATA (0x02) -#define MPI2_TOOL_ISTWI_ACTION_SEQUENCE (0x03) -#define MPI2_TOOL_ISTWI_ACTION_RESERVE_BUS (0x10) -#define MPI2_TOOL_ISTWI_ACTION_RELEASE_BUS (0x11) -#define MPI2_TOOL_ISTWI_ACTION_RESET (0x12) - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - -/* values for the Flags field */ -#define MPI2_TOOL_ISTWI_FLAG_AUTO_RESERVE_RELEASE (0x80) -#define MPI2_TOOL_ISTWI_FLAG_PAGE_ADDR_MASK (0x07) - -/* Toolbox ISTWI Read Write Tool reply message */ -typedef struct _MPI2_TOOLBOX_ISTWI_REPLY { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U8 DevIndex; /* 0x14 */ - U8 Action; /* 0x15 */ - U8 IstwiStatus; /* 0x16 */ - U8 Reserved6; /* 0x17 */ - U16 TxDataCount; /* 0x18 */ - U16 RxDataCount; /* 0x1A */ -} MPI2_TOOLBOX_ISTWI_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_ISTWI_REPLY, - Mpi2ToolboxIstwiReply_t, MPI2_POINTER pMpi2ToolboxIstwiReply_t; - - -/**************************************************************************** -* Toolbox Beacon Tool request -****************************************************************************/ - -typedef struct _MPI2_TOOLBOX_BEACON_REQUEST -{ - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U8 Reserved5; /* 0x0C */ - U8 PhysicalPort; /* 0x0D */ - U8 Reserved6; /* 0x0E */ - U8 Flags; /* 0x0F */ -} MPI2_TOOLBOX_BEACON_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_BEACON_REQUEST, - Mpi2ToolboxBeaconRequest_t, MPI2_POINTER pMpi2ToolboxBeaconRequest_t; - -/* values for the Flags field */ -#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00) -#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) - - -/**************************************************************************** -* Toolbox Diagnostic CLI Tool -****************************************************************************/ - -#define MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH (0x5C) - -/* MPI v2.0 Toolbox Diagnostic CLI Tool request message */ -typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U8 SGLFlags; /* 0x0C */ - U8 Reserved5; /* 0x0D */ - U16 Reserved6; /* 0x0E */ - U32 DataLength; /* 0x10 */ - U8 DiagnosticCliCommand - [MPI2_TOOLBOX_DIAG_CLI_CMD_LENGTH]; /* 0x14 */ - MPI2_MPI_SGE_IO_UNION SGL; /* 0x70 */ -} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, - MPI2_POINTER PTR_MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST, - Mpi2ToolboxDiagnosticCliRequest_t, - MPI2_POINTER pMpi2ToolboxDiagnosticCliRequest_t; - -/* use MPI2_SGLFLAGS_ defines from mpi2.h for the SGLFlags field */ - - -/* Toolbox Diagnostic CLI Tool reply message */ -typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 ReturnedDataLength; /* 0x14 */ -} MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY, - MPI2_POINTER PTR_MPI2_TOOLBOX_DIAG_CLI_REPLY, - Mpi2ToolboxDiagnosticCliReply_t, - MPI2_POINTER pMpi2ToolboxDiagnosticCliReply_t; - - -/**************************************************************************** -* Toolbox Console Text Display Tool -****************************************************************************/ - -/* Toolbox Console Text Display Tool request message */ -typedef struct _MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST { - U8 Tool; /* 0x00 */ - U8 Reserved1; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U8 Console; /* 0x0C */ - U8 Flags; /* 0x0D */ - U16 Reserved6; /* 0x0E */ - U8 TextToDisplay[4]; /* 0x10 */ -} MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, -MPI2_POINTER PTR_MPI2_TOOLBOX_TEXT_DISPLAY_REQUEST, -Mpi2ToolboxTextDisplayRequest_t, -MPI2_POINTER pMpi2ToolboxTextDisplayRequest_t; - -/* defines for the Console field */ -#define MPI2_TOOLBOX_CONSOLE_TYPE_MASK (0xF0) -#define MPI2_TOOLBOX_CONSOLE_TYPE_DEFAULT (0x00) -#define MPI2_TOOLBOX_CONSOLE_TYPE_UART (0x10) -#define MPI2_TOOLBOX_CONSOLE_TYPE_ETHERNET (0x20) - -#define MPI2_TOOLBOX_CONSOLE_NUMBER_MASK (0x0F) - -/* defines for the Flags field */ -#define MPI2_TOOLBOX_CONSOLE_FLAG_TIMESTAMP (0x01) - - - -/***************************************************************************** -* -* Diagnostic Buffer Messages -* -*****************************************************************************/ - - -/**************************************************************************** -* Diagnostic Buffer Post request -****************************************************************************/ - -typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST -{ - U8 ExtendedType; /* 0x00 */ - U8 BufferType; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U64 BufferAddress; /* 0x0C */ - U32 BufferLength; /* 0x14 */ - U32 Reserved5; /* 0x18 */ - U32 Reserved6; /* 0x1C */ - U32 Flags; /* 0x20 */ - U32 ProductSpecific[23]; /* 0x24 */ -} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST, - Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t; - -/* values for the ExtendedType field */ -#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION (0x02) - -/* values for the BufferType field */ -#define MPI2_DIAG_BUF_TYPE_TRACE (0x00) -#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01) -#define MPI2_DIAG_BUF_TYPE_EXTENDED (0x02) -/* count of the number of buffer types */ -#define MPI2_DIAG_BUF_TYPE_COUNT (0x03) - -/* values for the Flags field */ -#define MPI2_DIAG_BUF_FLAG_RELEASE_ON_FULL (0x00000002) -#define MPI2_DIAG_BUF_FLAG_IMMEDIATE_RELEASE (0x00000001) - - -/**************************************************************************** -* Diagnostic Buffer Post reply -****************************************************************************/ - -typedef struct _MPI2_DIAG_BUFFER_POST_REPLY -{ - U8 ExtendedType; /* 0x00 */ - U8 BufferType; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ - U32 TransferLength; /* 0x14 */ -} MPI2_DIAG_BUFFER_POST_REPLY, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REPLY, - Mpi2DiagBufferPostReply_t, MPI2_POINTER pMpi2DiagBufferPostReply_t; - - -/**************************************************************************** -* Diagnostic Release request -****************************************************************************/ - -typedef struct _MPI2_DIAG_RELEASE_REQUEST -{ - U8 Reserved1; /* 0x00 */ - U8 BufferType; /* 0x01 */ - U8 ChainOffset; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ -} MPI2_DIAG_RELEASE_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REQUEST, - Mpi2DiagReleaseRequest_t, MPI2_POINTER pMpi2DiagReleaseRequest_t; - - -/**************************************************************************** -* Diagnostic Buffer Post reply -****************************************************************************/ - -typedef struct _MPI2_DIAG_RELEASE_REPLY -{ - U8 Reserved1; /* 0x00 */ - U8 BufferType; /* 0x01 */ - U8 MsgLength; /* 0x02 */ - U8 Function; /* 0x03 */ - U16 Reserved2; /* 0x04 */ - U8 Reserved3; /* 0x06 */ - U8 MsgFlags; /* 0x07 */ - U8 VP_ID; /* 0x08 */ - U8 VF_ID; /* 0x09 */ - U16 Reserved4; /* 0x0A */ - U16 Reserved5; /* 0x0C */ - U16 IOCStatus; /* 0x0E */ - U32 IOCLogInfo; /* 0x10 */ -} MPI2_DIAG_RELEASE_REPLY, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REPLY, - Mpi2DiagReleaseReply_t, MPI2_POINTER pMpi2DiagReleaseReply_t; - - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/kernel/drivers/scsi/mpt2sas/mpi/mpi2_type.h deleted file mode 100644 index 6b0dcdd02..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpi/mpi2_type.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2000-2014 LSI Corporation. - * - * - * Name: mpi2_type.h - * Title: MPI basic type definitions - * Creation Date: August 16, 2006 - * - * mpi2_type.h Version: 02.00.00 - * - * Version History - * --------------- - * - * Date Version Description - * -------- -------- ------------------------------------------------------ - * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. - * -------------------------------------------------------------------------- - */ - -#ifndef MPI2_TYPE_H -#define MPI2_TYPE_H - - -/******************************************************************************* - * Define MPI2_POINTER if it hasn't already been defined. By default - * MPI2_POINTER is defined to be a near pointer. MPI2_POINTER can be defined as - * a far pointer by defining MPI2_POINTER as "far *" before this header file is - * included. - */ -#ifndef MPI2_POINTER -#define MPI2_POINTER * -#endif - -/* the basic types may have already been included by mpi_type.h */ -#ifndef MPI_TYPE_H -/***************************************************************************** -* -* Basic Types -* -*****************************************************************************/ - -typedef u8 U8; -typedef __le16 U16; -typedef __le32 U32; -typedef __le64 U64 __attribute__((aligned(4))); - -/***************************************************************************** -* -* Pointer Types -* -*****************************************************************************/ - -typedef U8 *PU8; -typedef U16 *PU16; -typedef U32 *PU32; -typedef U64 *PU64; - -#endif - -#endif - diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_base.c b/kernel/drivers/scsi/mpt2sas/mpt2sas_base.c deleted file mode 100644 index 11248de92..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_base.c +++ /dev/null @@ -1,4891 +0,0 @@ -/* - * This is the Fusion MPT base driver providing common API layer interface - * for access to MPT (Message Passing Technology) firmware. - * - * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mpt2sas_base.h" - -static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; - -#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ - -#define MAX_HBA_QUEUE_DEPTH 30000 -#define MAX_CHAIN_DEPTH 100000 -static int max_queue_depth = -1; -module_param(max_queue_depth, int, 0); -MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); - -static int max_sgl_entries = -1; -module_param(max_sgl_entries, int, 0); -MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); - -static int msix_disable = -1; -module_param(msix_disable, int, 0); -MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); - -static int max_msix_vectors = -1; -module_param(max_msix_vectors, int, 0); -MODULE_PARM_DESC(max_msix_vectors, " max msix vectors "); - -static int mpt2sas_fwfault_debug; -MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault " - "and halt firmware - (default=0)"); - -static int disable_discovery = -1; -module_param(disable_discovery, int, 0); -MODULE_PARM_DESC(disable_discovery, " disable discovery "); - -static int -_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag); - -static int -_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag); - -/** - * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug. - * - */ -static int -_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - struct MPT2SAS_ADAPTER *ioc; - - if (ret) - return ret; - - printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug); - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) - ioc->fwfault_debug = mpt2sas_fwfault_debug; - return 0; -} - -module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug, - param_get_int, &mpt2sas_fwfault_debug, 0644); - -/** - * mpt2sas_remove_dead_ioc_func - kthread context to remove dead ioc - * @arg: input argument, used to derive ioc - * - * Return 0 if controller is removed from pci subsystem. - * Return -1 for other case. - */ -static int mpt2sas_remove_dead_ioc_func(void *arg) -{ - struct MPT2SAS_ADAPTER *ioc = (struct MPT2SAS_ADAPTER *)arg; - struct pci_dev *pdev; - - if ((ioc == NULL)) - return -1; - - pdev = ioc->pdev; - if ((pdev == NULL)) - return -1; - pci_stop_and_remove_bus_device_locked(pdev); - return 0; -} - - -/** - * _base_fault_reset_work - workq handling ioc fault conditions - * @work: input argument, used to derive ioc - * Context: sleep. - * - * Return nothing. - */ -static void -_base_fault_reset_work(struct work_struct *work) -{ - struct MPT2SAS_ADAPTER *ioc = - container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work); - unsigned long flags; - u32 doorbell; - int rc; - struct task_struct *p; - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery || ioc->pci_error_recovery) - goto rearm_timer; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - - doorbell = mpt2sas_base_get_iocstate(ioc, 0); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_MASK) { - printk(MPT2SAS_INFO_FMT "%s : SAS host is non-operational !!!!\n", - ioc->name, __func__); - - /* It may be possible that EEH recovery can resolve some of - * pci bus failure issues rather removing the dead ioc function - * by considering controller is in a non-operational state. So - * here priority is given to the EEH recovery. If it doesn't - * not resolve this issue, mpt2sas driver will consider this - * controller to non-operational state and remove the dead ioc - * function. - */ - if (ioc->non_operational_loop++ < 5) { - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, - flags); - goto rearm_timer; - } - - /* - * Call _scsih_flush_pending_cmds callback so that we flush all - * pending commands back to OS. This call is required to aovid - * deadlock at block layer. Dead IOC will fail to do diag reset, - * and this call is safe since dead ioc will never return any - * command back from HW. - */ - ioc->schedule_dead_ioc_flush_running_cmds(ioc); - /* - * Set remove_host flag early since kernel thread will - * take some time to execute. - */ - ioc->remove_host = 1; - /*Remove the Dead Host */ - p = kthread_run(mpt2sas_remove_dead_ioc_func, ioc, - "mpt2sas_dead_ioc_%d", ioc->id); - if (IS_ERR(p)) { - printk(MPT2SAS_ERR_FMT - "%s: Running mpt2sas_dead_ioc thread failed !!!!\n", - ioc->name, __func__); - } else { - printk(MPT2SAS_ERR_FMT - "%s: Running mpt2sas_dead_ioc thread success !!!!\n", - ioc->name, __func__); - } - - return; /* don't rearm timer */ - } - - ioc->non_operational_loop = 0; - - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - printk(MPT2SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name, - __func__, (rc == 0) ? "success" : "failed"); - doorbell = mpt2sas_base_get_iocstate(ioc, 0); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt2sas_base_fault_info(ioc, doorbell & - MPI2_DOORBELL_DATA_MASK); - } - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - rearm_timer: - if (ioc->fault_reset_work_q) - queue_delayed_work(ioc->fault_reset_work_q, - &ioc->fault_reset_work, - msecs_to_jiffies(FAULT_POLLING_INTERVAL)); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); -} - -/** - * mpt2sas_base_start_watchdog - start the fault_reset_work_q - * @ioc: per adapter object - * Context: sleep. - * - * Return nothing. - */ -void -mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; - - if (ioc->fault_reset_work_q) - return; - - /* initialize fault polling */ - INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); - snprintf(ioc->fault_reset_work_q_name, - sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); - ioc->fault_reset_work_q = - create_singlethread_workqueue(ioc->fault_reset_work_q_name); - if (!ioc->fault_reset_work_q) { - printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n", - ioc->name, __func__, __LINE__); - return; - } - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->fault_reset_work_q) - queue_delayed_work(ioc->fault_reset_work_q, - &ioc->fault_reset_work, - msecs_to_jiffies(FAULT_POLLING_INTERVAL)); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); -} - -/** - * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q - * @ioc: per adapter object - * Context: sleep. - * - * Return nothing. - */ -void -mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; - struct workqueue_struct *wq; - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - wq = ioc->fault_reset_work_q; - ioc->fault_reset_work_q = NULL; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - if (wq) { - if (!cancel_delayed_work_sync(&ioc->fault_reset_work)) - flush_workqueue(wq); - destroy_workqueue(wq); - } -} - -/** - * mpt2sas_base_fault_info - verbose translation of firmware FAULT code - * @ioc: per adapter object - * @fault_code: fault code - * - * Return nothing. - */ -void -mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) -{ - printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n", - ioc->name, fault_code); -} - -/** - * mpt2sas_halt_firmware - halt's mpt controller firmware - * @ioc: per adapter object - * - * For debugging timeout related issues. Writing 0xCOFFEE00 - * to the doorbell register will halt controller firmware. With - * the purpose to stop both driver and firmware, the enduser can - * obtain a ring buffer from controller UART. - */ -void -mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc) -{ - u32 doorbell; - - if (!ioc->fwfault_debug) - return; - - dump_stack(); - - doorbell = readl(&ioc->chip->Doorbell); - if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) - mpt2sas_base_fault_info(ioc , doorbell); - else { - writel(0xC0FFEE00, &ioc->chip->Doorbell); - printk(MPT2SAS_ERR_FMT "Firmware is halted due to command " - "timeout\n", ioc->name); - } - - panic("panic in %s\n", __func__); -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _base_sas_ioc_info - verbose translation of the ioc status - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @request_hdr: request mf - * - * Return nothing. - */ -static void -_base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, - MPI2RequestHeader_t *request_hdr) -{ - u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & - MPI2_IOCSTATUS_MASK; - char *desc = NULL; - u16 frame_sz; - char *func_str = NULL; - - /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ - if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || - request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) - return; - - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) - return; - - switch (ioc_status) { - -/**************************************************************************** -* Common IOCStatus values for all replies -****************************************************************************/ - - case MPI2_IOCSTATUS_INVALID_FUNCTION: - desc = "invalid function"; - break; - case MPI2_IOCSTATUS_BUSY: - desc = "busy"; - break; - case MPI2_IOCSTATUS_INVALID_SGL: - desc = "invalid sgl"; - break; - case MPI2_IOCSTATUS_INTERNAL_ERROR: - desc = "internal error"; - break; - case MPI2_IOCSTATUS_INVALID_VPID: - desc = "invalid vpid"; - break; - case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: - desc = "insufficient resources"; - break; - case MPI2_IOCSTATUS_INVALID_FIELD: - desc = "invalid field"; - break; - case MPI2_IOCSTATUS_INVALID_STATE: - desc = "invalid state"; - break; - case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: - desc = "op state not supported"; - break; - -/**************************************************************************** -* Config IOCStatus values -****************************************************************************/ - - case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION: - desc = "config invalid action"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: - desc = "config invalid type"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: - desc = "config invalid page"; - break; - case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: - desc = "config invalid data"; - break; - case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: - desc = "config no defaults"; - break; - case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: - desc = "config cant commit"; - break; - -/**************************************************************************** -* SCSI IO Reply -****************************************************************************/ - - case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: - case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: - case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: - case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: - case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: - case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: - case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: - case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: - case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: - case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: - case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: - break; - -/**************************************************************************** -* For use by SCSI Initiator and SCSI Target end-to-end data protection -****************************************************************************/ - - case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: - desc = "eedp guard error"; - break; - case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: - desc = "eedp ref tag error"; - break; - case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: - desc = "eedp app tag error"; - break; - -/**************************************************************************** -* SCSI Target values -****************************************************************************/ - - case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX: - desc = "target invalid io index"; - break; - case MPI2_IOCSTATUS_TARGET_ABORTED: - desc = "target aborted"; - break; - case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: - desc = "target no conn retryable"; - break; - case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: - desc = "target no connection"; - break; - case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: - desc = "target xfer count mismatch"; - break; - case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: - desc = "target data offset error"; - break; - case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: - desc = "target too much write data"; - break; - case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: - desc = "target iu too short"; - break; - case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: - desc = "target ack nak timeout"; - break; - case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: - desc = "target nak received"; - break; - -/**************************************************************************** -* Serial Attached SCSI values -****************************************************************************/ - - case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED: - desc = "smp request failed"; - break; - case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN: - desc = "smp data overrun"; - break; - -/**************************************************************************** -* Diagnostic Buffer Post / Diagnostic Release values -****************************************************************************/ - - case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED: - desc = "diagnostic released"; - break; - default: - break; - } - - if (!desc) - return; - - switch (request_hdr->Function) { - case MPI2_FUNCTION_CONFIG: - frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size; - func_str = "config_page"; - break; - case MPI2_FUNCTION_SCSI_TASK_MGMT: - frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t); - func_str = "task_mgmt"; - break; - case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: - frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t); - func_str = "sas_iounit_ctl"; - break; - case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR: - frame_sz = sizeof(Mpi2SepRequest_t); - func_str = "enclosure"; - break; - case MPI2_FUNCTION_IOC_INIT: - frame_sz = sizeof(Mpi2IOCInitRequest_t); - func_str = "ioc_init"; - break; - case MPI2_FUNCTION_PORT_ENABLE: - frame_sz = sizeof(Mpi2PortEnableRequest_t); - func_str = "port_enable"; - break; - case MPI2_FUNCTION_SMP_PASSTHROUGH: - frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size; - func_str = "smp_passthru"; - break; - default: - frame_sz = 32; - func_str = "unknown"; - break; - } - - printk(MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p)," - " (%s)\n", ioc->name, desc, ioc_status, request_hdr, func_str); - - _debug_dump_mf(request_hdr, frame_sz/4); -} - -/** - * _base_display_event_data - verbose translation of firmware asyn events - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * - * Return nothing. - */ -static void -_base_display_event_data(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventNotificationReply_t *mpi_reply) -{ - char *desc = NULL; - u16 event; - - if (!(ioc->logging_level & MPT_DEBUG_EVENTS)) - return; - - event = le16_to_cpu(mpi_reply->Event); - - switch (event) { - case MPI2_EVENT_LOG_DATA: - desc = "Log Data"; - break; - case MPI2_EVENT_STATE_CHANGE: - desc = "Status Change"; - break; - case MPI2_EVENT_HARD_RESET_RECEIVED: - desc = "Hard Reset Received"; - break; - case MPI2_EVENT_EVENT_CHANGE: - desc = "Event Change"; - break; - case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - desc = "Device Status Change"; - break; - case MPI2_EVENT_IR_OPERATION_STATUS: - if (!ioc->hide_ir_msg) - desc = "IR Operation Status"; - break; - case MPI2_EVENT_SAS_DISCOVERY: - { - Mpi2EventDataSasDiscovery_t *event_data = - (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; - printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name, - (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? - "start" : "stop"); - if (event_data->DiscoveryStatus) - printk("discovery_status(0x%08x)", - le32_to_cpu(event_data->DiscoveryStatus)); - printk("\n"); - return; - } - case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - desc = "SAS Broadcast Primitive"; - break; - case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: - desc = "SAS Init Device Status Change"; - break; - case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: - desc = "SAS Init Table Overflow"; - break; - case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - desc = "SAS Topology Change List"; - break; - case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - desc = "SAS Enclosure Device Status Change"; - break; - case MPI2_EVENT_IR_VOLUME: - if (!ioc->hide_ir_msg) - desc = "IR Volume"; - break; - case MPI2_EVENT_IR_PHYSICAL_DISK: - if (!ioc->hide_ir_msg) - desc = "IR Physical Disk"; - break; - case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - if (!ioc->hide_ir_msg) - desc = "IR Configuration Change List"; - break; - case MPI2_EVENT_LOG_ENTRY_ADDED: - if (!ioc->hide_ir_msg) - desc = "Log Entry Added"; - break; - case MPI2_EVENT_TEMP_THRESHOLD: - desc = "Temperature Threshold"; - break; - } - - if (!desc) - return; - - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, desc); -} -#endif - -/** - * _base_sas_log_info - verbose translation of firmware log info - * @ioc: per adapter object - * @log_info: log info - * - * Return nothing. - */ -static void -_base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) -{ - union loginfo_type { - u32 loginfo; - struct { - u32 subcode:16; - u32 code:8; - u32 originator:4; - u32 bus_type:4; - } dw; - }; - union loginfo_type sas_loginfo; - char *originator_str = NULL; - - sas_loginfo.loginfo = log_info; - if (sas_loginfo.dw.bus_type != 3 /*SAS*/) - return; - - /* each nexus loss loginfo */ - if (log_info == 0x31170000) - return; - - /* eat the loginfos associated with task aborts */ - if (ioc->ignore_loginfos && (log_info == 0x30050000 || log_info == - 0x31140000 || log_info == 0x31130000)) - return; - - switch (sas_loginfo.dw.originator) { - case 0: - originator_str = "IOP"; - break; - case 1: - originator_str = "PL"; - break; - case 2: - if (!ioc->hide_ir_msg) - originator_str = "IR"; - else - originator_str = "WarpDrive"; - break; - } - - printk(MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), " - "code(0x%02x), sub_code(0x%04x)\n", ioc->name, log_info, - originator_str, sas_loginfo.dw.code, - sas_loginfo.dw.subcode); -} - -/** - * _base_display_reply_info - - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Return nothing. - */ -static void -_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - u16 ioc_status; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - ioc_status = le16_to_cpu(mpi_reply->IOCStatus); -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if ((ioc_status & MPI2_IOCSTATUS_MASK) && - (ioc->logging_level & MPT_DEBUG_REPLY)) { - _base_sas_ioc_info(ioc , mpi_reply, - mpt2sas_base_get_msg_frame(ioc, smid)); - } -#endif - if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) - _base_sas_log_info(ioc, le32_to_cpu(mpi_reply->IOCLogInfo)); -} - -/** - * mpt2sas_base_done - base internal command completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) - return 1; - - if (ioc->base_cmds.status == MPT2_CMD_NOT_USED) - return 1; - - ioc->base_cmds.status |= MPT2_CMD_COMPLETE; - if (mpi_reply) { - ioc->base_cmds.status |= MPT2_CMD_REPLY_VALID; - memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); - } - ioc->base_cmds.status &= ~MPT2_CMD_PENDING; - - complete(&ioc->base_cmds.done); - return 1; -} - -/** - * _base_async_event - main callback handler for firmware asyn events - * @ioc: per adapter object - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Returns void. - */ -static void -_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply) -{ - Mpi2EventNotificationReply_t *mpi_reply; - Mpi2EventAckRequest_t *ack_request; - u16 smid; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (!mpi_reply) - return; - if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) - return; -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _base_display_event_data(ioc, mpi_reply); -#endif - if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) - goto out; - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - goto out; - } - - ack_request = mpt2sas_base_get_msg_frame(ioc, smid); - memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); - ack_request->Function = MPI2_FUNCTION_EVENT_ACK; - ack_request->Event = mpi_reply->Event; - ack_request->EventContext = mpi_reply->EventContext; - ack_request->VF_ID = 0; /* TODO */ - ack_request->VP_ID = 0; - mpt2sas_base_put_smid_default(ioc, smid); - - out: - - /* scsih callback handler */ - mpt2sas_scsih_event_callback(ioc, msix_index, reply); - - /* ctl callback handler */ - mpt2sas_ctl_event_callback(ioc, msix_index, reply); - - return; -} - -/** - * _base_get_cb_idx - obtain the callback index - * @ioc: per adapter object - * @smid: system request message index - * - * Return callback index. - */ -static u8 -_base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - int i; - u8 cb_idx; - - if (smid < ioc->hi_priority_smid) { - i = smid - 1; - cb_idx = ioc->scsi_lookup[i].cb_idx; - } else if (smid < ioc->internal_smid) { - i = smid - ioc->hi_priority_smid; - cb_idx = ioc->hpr_lookup[i].cb_idx; - } else if (smid <= ioc->hba_queue_depth) { - i = smid - ioc->internal_smid; - cb_idx = ioc->internal_lookup[i].cb_idx; - } else - cb_idx = 0xFF; - return cb_idx; -} - -/** - * _base_mask_interrupts - disable interrupts - * @ioc: per adapter object - * - * Disabling ResetIRQ, Reply and Doorbell Interrupts - * - * Return nothing. - */ -static void -_base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc) -{ - u32 him_register; - - ioc->mask_interrupts = 1; - him_register = readl(&ioc->chip->HostInterruptMask); - him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK; - writel(him_register, &ioc->chip->HostInterruptMask); - readl(&ioc->chip->HostInterruptMask); -} - -/** - * _base_unmask_interrupts - enable interrupts - * @ioc: per adapter object - * - * Enabling only Reply Interrupts - * - * Return nothing. - */ -static void -_base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc) -{ - u32 him_register; - - him_register = readl(&ioc->chip->HostInterruptMask); - him_register &= ~MPI2_HIM_RIM; - writel(him_register, &ioc->chip->HostInterruptMask); - ioc->mask_interrupts = 0; -} - -union reply_descriptor { - u64 word; - struct { - u32 low; - u32 high; - } u; -}; - -/** - * _base_interrupt - MPT adapter (IOC) specific interrupt handler. - * @irq: irq number (not used) - * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure - * @r: pt_regs pointer (not used) - * - * Return IRQ_HANDLE if processed, else IRQ_NONE. - */ -static irqreturn_t -_base_interrupt(int irq, void *bus_id) -{ - struct adapter_reply_queue *reply_q = bus_id; - union reply_descriptor rd; - u32 completed_cmds; - u8 request_desript_type; - u16 smid; - u8 cb_idx; - u32 reply; - u8 msix_index = reply_q->msix_index; - struct MPT2SAS_ADAPTER *ioc = reply_q->ioc; - Mpi2ReplyDescriptorsUnion_t *rpf; - u8 rc; - - if (ioc->mask_interrupts) - return IRQ_NONE; - - if (!atomic_add_unless(&reply_q->busy, 1, 1)) - return IRQ_NONE; - - rpf = &reply_q->reply_post_free[reply_q->reply_post_host_index]; - request_desript_type = rpf->Default.ReplyFlags - & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) { - atomic_dec(&reply_q->busy); - return IRQ_NONE; - } - - completed_cmds = 0; - cb_idx = 0xFF; - do { - rd.word = le64_to_cpu(rpf->Words); - if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX) - goto out; - reply = 0; - smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1); - if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { - reply = le32_to_cpu - (rpf->AddressReply.ReplyFrameAddress); - if (reply > ioc->reply_dma_max_address || - reply < ioc->reply_dma_min_address) - reply = 0; - } else if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER) - goto next; - else if (request_desript_type == - MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) - goto next; - if (smid) { - cb_idx = _base_get_cb_idx(ioc, smid); - if ((likely(cb_idx < MPT_MAX_CALLBACKS)) - && (likely(mpt_callbacks[cb_idx] != NULL))) { - rc = mpt_callbacks[cb_idx](ioc, smid, - msix_index, reply); - if (reply) - _base_display_reply_info(ioc, smid, - msix_index, reply); - if (rc) - mpt2sas_base_free_smid(ioc, smid); - } - } - if (!smid) - _base_async_event(ioc, msix_index, reply); - - /* reply free queue handling */ - if (reply) { - ioc->reply_free_host_index = - (ioc->reply_free_host_index == - (ioc->reply_free_queue_depth - 1)) ? - 0 : ioc->reply_free_host_index + 1; - ioc->reply_free[ioc->reply_free_host_index] = - cpu_to_le32(reply); - wmb(); - writel(ioc->reply_free_host_index, - &ioc->chip->ReplyFreeHostIndex); - } - - next: - - rpf->Words = cpu_to_le64(ULLONG_MAX); - reply_q->reply_post_host_index = - (reply_q->reply_post_host_index == - (ioc->reply_post_queue_depth - 1)) ? 0 : - reply_q->reply_post_host_index + 1; - request_desript_type = - reply_q->reply_post_free[reply_q->reply_post_host_index]. - Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; - completed_cmds++; - if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) - goto out; - if (!reply_q->reply_post_host_index) - rpf = reply_q->reply_post_free; - else - rpf++; - } while (1); - - out: - - if (!completed_cmds) { - atomic_dec(&reply_q->busy); - return IRQ_NONE; - } - wmb(); - if (ioc->is_warpdrive) { - writel(reply_q->reply_post_host_index, - ioc->reply_post_host_index[msix_index]); - atomic_dec(&reply_q->busy); - return IRQ_HANDLED; - } - writel(reply_q->reply_post_host_index | (msix_index << - MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); - atomic_dec(&reply_q->busy); - return IRQ_HANDLED; -} - -/** - * _base_is_controller_msix_enabled - is controller support muli-reply queues - * @ioc: per adapter object - * - */ -static inline int -_base_is_controller_msix_enabled(struct MPT2SAS_ADAPTER *ioc) -{ - return (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable; -} - -/** - * mpt2sas_base_flush_reply_queues - flushing the MSIX reply queues - * @ioc: per adapter object - * Context: ISR conext - * - * Called when a Task Management request has completed. We want - * to flush the other reply queues so all the outstanding IO has been - * completed back to OS before we process the TM completetion. - * - * Return nothing. - */ -void -mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc) -{ - struct adapter_reply_queue *reply_q; - - /* If MSIX capability is turned off - * then multi-queues are not enabled - */ - if (!_base_is_controller_msix_enabled(ioc)) - return; - - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - if (ioc->shost_recovery) - return; - /* TMs are on msix_index == 0 */ - if (reply_q->msix_index == 0) - continue; - _base_interrupt(reply_q->vector, (void *)reply_q); - } -} - -/** - * mpt2sas_base_release_callback_handler - clear interrupt callback handler - * @cb_idx: callback index - * - * Return nothing. - */ -void -mpt2sas_base_release_callback_handler(u8 cb_idx) -{ - mpt_callbacks[cb_idx] = NULL; -} - -/** - * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler - * @cb_func: callback function - * - * Returns cb_func. - */ -u8 -mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func) -{ - u8 cb_idx; - - for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--) - if (mpt_callbacks[cb_idx] == NULL) - break; - - mpt_callbacks[cb_idx] = cb_func; - return cb_idx; -} - -/** - * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler - * - * Return nothing. - */ -void -mpt2sas_base_initialize_callback_handler(void) -{ - u8 cb_idx; - - for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++) - mpt2sas_base_release_callback_handler(cb_idx); -} - -/** - * mpt2sas_base_build_zero_len_sge - build zero length sg entry - * @ioc: per adapter object - * @paddr: virtual address for SGE - * - * Create a zero length scatter gather entry to insure the IOCs hardware has - * something to use if the target device goes brain dead and tries - * to send data even when none is asked for. - * - * Return nothing. - */ -void -mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr) -{ - u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | - MPI2_SGE_FLAGS_SIMPLE_ELEMENT) << - MPI2_SGE_FLAGS_SHIFT); - ioc->base_add_sg_single(paddr, flags_length, -1); -} - -/** - * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. - * @paddr: virtual address for SGE - * @flags_length: SGE flags and data transfer length - * @dma_addr: Physical address - * - * Return nothing. - */ -static void -_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr) -{ - Mpi2SGESimple32_t *sgel = paddr; - - flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING | - MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; - sgel->FlagsLength = cpu_to_le32(flags_length); - sgel->Address = cpu_to_le32(dma_addr); -} - - -/** - * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr. - * @paddr: virtual address for SGE - * @flags_length: SGE flags and data transfer length - * @dma_addr: Physical address - * - * Return nothing. - */ -static void -_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr) -{ - Mpi2SGESimple64_t *sgel = paddr; - - flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING | - MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; - sgel->FlagsLength = cpu_to_le32(flags_length); - sgel->Address = cpu_to_le64(dma_addr); -} - -#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10)) - -/** - * _base_config_dma_addressing - set dma addressing - * @ioc: per adapter object - * @pdev: PCI device struct - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev) -{ - struct sysinfo s; - u64 consistent_dma_mask; - - if (ioc->dma_mask) - consistent_dma_mask = DMA_BIT_MASK(64); - else - consistent_dma_mask = DMA_BIT_MASK(32); - - if (sizeof(dma_addr_t) > 4) { - const uint64_t required_mask = - dma_get_required_mask(&pdev->dev); - if ((required_mask > DMA_BIT_MASK(32)) && - !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && - !pci_set_consistent_dma_mask(pdev, consistent_dma_mask)) { - ioc->base_add_sg_single = &_base_add_sg_single_64; - ioc->sge_size = sizeof(Mpi2SGESimple64_t); - ioc->dma_mask = 64; - goto out; - } - } - - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) - && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { - ioc->base_add_sg_single = &_base_add_sg_single_32; - ioc->sge_size = sizeof(Mpi2SGESimple32_t); - ioc->dma_mask = 32; - } else - return -ENODEV; - - out: - si_meminfo(&s); - printk(MPT2SAS_INFO_FMT - "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n", - ioc->name, ioc->dma_mask, convert_to_kb(s.totalram)); - - return 0; -} - -static int -_base_change_consistent_dma_mask(struct MPT2SAS_ADAPTER *ioc, - struct pci_dev *pdev) -{ - if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) { - if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) - return -ENODEV; - } - return 0; -} -/** - * _base_check_enable_msix - checks MSIX capabable. - * @ioc: per adapter object - * - * Check to see if card is capable of MSIX, and set number - * of available msix vectors - */ -static int -_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc) -{ - int base; - u16 message_control; - - - /* Check whether controller SAS2008 B0 controller, - if it is SAS2008 B0 controller use IO-APIC instead of MSIX */ - if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 && - ioc->pdev->revision == 0x01) { - return -EINVAL; - } - - base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); - if (!base) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not " - "supported\n", ioc->name)); - return -EINVAL; - } - - /* get msix vector count */ - /* NUMA_IO not supported for older controllers */ - if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 || - ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2) - ioc->msix_vector_count = 1; - else { - pci_read_config_word(ioc->pdev, base + 2, &message_control); - ioc->msix_vector_count = (message_control & 0x3FF) + 1; - } - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, " - "vector_count(%d)\n", ioc->name, ioc->msix_vector_count)); - - return 0; -} - -/** - * _base_free_irq - free irq - * @ioc: per adapter object - * - * Freeing respective reply_queue from the list. - */ -static void -_base_free_irq(struct MPT2SAS_ADAPTER *ioc) -{ - struct adapter_reply_queue *reply_q, *next; - - if (list_empty(&ioc->reply_queue_list)) - return; - - list_for_each_entry_safe(reply_q, next, &ioc->reply_queue_list, list) { - list_del(&reply_q->list); - irq_set_affinity_hint(reply_q->vector, NULL); - free_cpumask_var(reply_q->affinity_hint); - synchronize_irq(reply_q->vector); - free_irq(reply_q->vector, reply_q); - kfree(reply_q); - } -} - -/** - * _base_request_irq - request irq - * @ioc: per adapter object - * @index: msix index into vector table - * @vector: irq vector - * - * Inserting respective reply_queue into the list. - */ -static int -_base_request_irq(struct MPT2SAS_ADAPTER *ioc, u8 index, u32 vector) -{ - struct adapter_reply_queue *reply_q; - int r; - - reply_q = kzalloc(sizeof(struct adapter_reply_queue), GFP_KERNEL); - if (!reply_q) { - printk(MPT2SAS_ERR_FMT "unable to allocate memory %d!\n", - ioc->name, (int)sizeof(struct adapter_reply_queue)); - return -ENOMEM; - } - reply_q->ioc = ioc; - reply_q->msix_index = index; - reply_q->vector = vector; - - if (!alloc_cpumask_var(&reply_q->affinity_hint, GFP_KERNEL)) - return -ENOMEM; - cpumask_clear(reply_q->affinity_hint); - - atomic_set(&reply_q->busy, 0); - if (ioc->msix_enable) - snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", - MPT2SAS_DRIVER_NAME, ioc->id, index); - else - snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", - MPT2SAS_DRIVER_NAME, ioc->id); - r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, - reply_q); - if (r) { - printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n", - reply_q->name, vector); - kfree(reply_q); - return -EBUSY; - } - - INIT_LIST_HEAD(&reply_q->list); - list_add_tail(&reply_q->list, &ioc->reply_queue_list); - return 0; -} - -/** - * _base_assign_reply_queues - assigning msix index for each cpu - * @ioc: per adapter object - * - * The enduser would need to set the affinity via /proc/irq/#/smp_affinity - * - * It would nice if we could call irq_set_affinity, however it is not - * an exported symbol - */ -static void -_base_assign_reply_queues(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned int cpu, nr_cpus, nr_msix, index = 0; - struct adapter_reply_queue *reply_q; - - if (!_base_is_controller_msix_enabled(ioc)) - return; - - memset(ioc->cpu_msix_table, 0, ioc->cpu_msix_table_sz); - - nr_cpus = num_online_cpus(); - nr_msix = ioc->reply_queue_count = min(ioc->reply_queue_count, - ioc->facts.MaxMSIxVectors); - if (!nr_msix) - return; - - cpu = cpumask_first(cpu_online_mask); - - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - - unsigned int i, group = nr_cpus / nr_msix; - - if (cpu >= nr_cpus) - break; - - if (index < nr_cpus % nr_msix) - group++; - - for (i = 0 ; i < group ; i++) { - ioc->cpu_msix_table[cpu] = index; - cpumask_or(reply_q->affinity_hint, - reply_q->affinity_hint, get_cpu_mask(cpu)); - cpu = cpumask_next(cpu, cpu_online_mask); - } - - if (irq_set_affinity_hint(reply_q->vector, - reply_q->affinity_hint)) - dinitprintk(ioc, pr_info(MPT2SAS_FMT - "error setting affinity hint for irq vector %d\n", - ioc->name, reply_q->vector)); - index++; - } -} - -/** - * _base_disable_msix - disables msix - * @ioc: per adapter object - * - */ -static void -_base_disable_msix(struct MPT2SAS_ADAPTER *ioc) -{ - if (ioc->msix_enable) { - pci_disable_msix(ioc->pdev); - ioc->msix_enable = 0; - } -} - -/** - * _base_enable_msix - enables msix, failback to io_apic - * @ioc: per adapter object - * - */ -static int -_base_enable_msix(struct MPT2SAS_ADAPTER *ioc) -{ - struct msix_entry *entries, *a; - int r; - int i; - u8 try_msix = 0; - - if (msix_disable == -1 || msix_disable == 0) - try_msix = 1; - - if (!try_msix) - goto try_ioapic; - - if (_base_check_enable_msix(ioc) != 0) - goto try_ioapic; - - ioc->reply_queue_count = min_t(int, ioc->cpu_count, - ioc->msix_vector_count); - - if (!ioc->rdpq_array_enable && max_msix_vectors == -1) - max_msix_vectors = 8; - - if (max_msix_vectors > 0) { - ioc->reply_queue_count = min_t(int, max_msix_vectors, - ioc->reply_queue_count); - ioc->msix_vector_count = ioc->reply_queue_count; - } else if (max_msix_vectors == 0) - goto try_ioapic; - - printk(MPT2SAS_INFO_FMT - "MSI-X vectors supported: %d, no of cores: %d, max_msix_vectors: %d\n", - ioc->name, ioc->msix_vector_count, ioc->cpu_count, max_msix_vectors); - - entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry), - GFP_KERNEL); - if (!entries) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "kcalloc " - "failed @ at %s:%d/%s() !!!\n", ioc->name, __FILE__, - __LINE__, __func__)); - goto try_ioapic; - } - - for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) - a->entry = i; - - r = pci_enable_msix_exact(ioc->pdev, entries, ioc->reply_queue_count); - if (r) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT - "pci_enable_msix_exact failed (r=%d) !!!\n", ioc->name, r)); - kfree(entries); - goto try_ioapic; - } - - ioc->msix_enable = 1; - for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++) { - r = _base_request_irq(ioc, i, a->vector); - if (r) { - _base_free_irq(ioc); - _base_disable_msix(ioc); - kfree(entries); - goto try_ioapic; - } - } - - kfree(entries); - return 0; - -/* failback to io_apic interrupt routing */ - try_ioapic: - - ioc->reply_queue_count = 1; - r = _base_request_irq(ioc, 0, ioc->pdev->irq); - - return r; -} - -/** - * mpt2sas_base_map_resources - map in controller resources (io/irq/memap) - * @ioc: per adapter object - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) -{ - struct pci_dev *pdev = ioc->pdev; - u32 memap_sz; - u32 pio_sz; - int i, r = 0; - u64 pio_chip = 0; - u64 chip_phys = 0; - struct adapter_reply_queue *reply_q; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", - ioc->name, __func__)); - - ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); - if (pci_enable_device_mem(pdev)) { - printk(MPT2SAS_WARN_FMT "pci_enable_device_mem: " - "failed\n", ioc->name); - ioc->bars = 0; - return -ENODEV; - } - - - if (pci_request_selected_regions(pdev, ioc->bars, - MPT2SAS_DRIVER_NAME)) { - printk(MPT2SAS_WARN_FMT "pci_request_selected_regions: " - "failed\n", ioc->name); - ioc->bars = 0; - r = -ENODEV; - goto out_fail; - } - - /* AER (Advanced Error Reporting) hooks */ - pci_enable_pcie_error_reporting(pdev); - - pci_set_master(pdev); - - if (_base_config_dma_addressing(ioc, pdev) != 0) { - printk(MPT2SAS_WARN_FMT "no suitable DMA mask for %s\n", - ioc->name, pci_name(pdev)); - r = -ENODEV; - goto out_fail; - } - - for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { - if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { - if (pio_sz) - continue; - pio_chip = (u64)pci_resource_start(pdev, i); - pio_sz = pci_resource_len(pdev, i); - } else { - if (memap_sz) - continue; - /* verify memory resource is valid before using */ - if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { - ioc->chip_phys = pci_resource_start(pdev, i); - chip_phys = (u64)ioc->chip_phys; - memap_sz = pci_resource_len(pdev, i); - ioc->chip = ioremap(ioc->chip_phys, memap_sz); - if (ioc->chip == NULL) { - printk(MPT2SAS_ERR_FMT "unable to map " - "adapter memory!\n", ioc->name); - r = -EINVAL; - goto out_fail; - } - } - } - } - - _base_mask_interrupts(ioc); - - r = _base_get_ioc_facts(ioc, CAN_SLEEP); - if (r) - goto out_fail; - - if (!ioc->rdpq_array_enable_assigned) { - ioc->rdpq_array_enable = ioc->rdpq_array_capable; - ioc->rdpq_array_enable_assigned = 1; - } - - r = _base_enable_msix(ioc); - if (r) - goto out_fail; - - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) - printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n", - reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : - "IO-APIC enabled"), reply_q->vector); - - printk(MPT2SAS_INFO_FMT "iomem(0x%016llx), mapped(0x%p), size(%d)\n", - ioc->name, (unsigned long long)chip_phys, ioc->chip, memap_sz); - printk(MPT2SAS_INFO_FMT "ioport(0x%016llx), size(%d)\n", - ioc->name, (unsigned long long)pio_chip, pio_sz); - - /* Save PCI configuration state for recovery from PCI AER/EEH errors */ - pci_save_state(pdev); - - return 0; - - out_fail: - if (ioc->chip_phys) - iounmap(ioc->chip); - ioc->chip_phys = 0; - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); - return r; -} - -/** - * mpt2sas_base_get_msg_frame - obtain request mf pointer - * @ioc: per adapter object - * @smid: system request message index(smid zero is invalid) - * - * Returns virt pointer to message frame. - */ -void * -mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return (void *)(ioc->request + (smid * ioc->request_sz)); -} - -/** - * mpt2sas_base_get_sense_buffer - obtain a sense buffer assigned to a mf request - * @ioc: per adapter object - * @smid: system request message index - * - * Returns virt pointer to sense buffer. - */ -void * -mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); -} - -/** - * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer assigned to a mf request - * @ioc: per adapter object - * @smid: system request message index - * - * Returns phys pointer to the low 32bit address of the sense buffer. - */ -__le32 -mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return cpu_to_le32(ioc->sense_dma + - ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); -} - -/** - * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address - * @ioc: per adapter object - * @phys_addr: lower 32 physical addr of the reply - * - * Converts 32bit lower physical addr into a virt address. - */ -void * -mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr) -{ - if (!phys_addr) - return NULL; - return ioc->reply + (phys_addr - (u32)ioc->reply_dma); -} - -/** - * mpt2sas_base_get_smid - obtain a free smid from internal queue - * @ioc: per adapter object - * @cb_idx: callback index - * - * Returns smid (zero is invalid) - */ -u16 -mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) -{ - unsigned long flags; - struct request_tracker *request; - u16 smid; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (list_empty(&ioc->internal_free_list)) { - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: smid not available\n", - ioc->name, __func__); - return 0; - } - - request = list_entry(ioc->internal_free_list.next, - struct request_tracker, tracker_list); - request->cb_idx = cb_idx; - smid = request->smid; - list_del(&request->tracker_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return smid; -} - -/** - * mpt2sas_base_get_smid_scsiio - obtain a free smid from scsiio queue - * @ioc: per adapter object - * @cb_idx: callback index - * @scmd: pointer to scsi command object - * - * Returns smid (zero is invalid) - */ -u16 -mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, - struct scsi_cmnd *scmd) -{ - unsigned long flags; - struct scsiio_tracker *request; - u16 smid; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (list_empty(&ioc->free_list)) { - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - printk(MPT2SAS_ERR_FMT "%s: smid not available\n", - ioc->name, __func__); - return 0; - } - - request = list_entry(ioc->free_list.next, - struct scsiio_tracker, tracker_list); - request->scmd = scmd; - request->cb_idx = cb_idx; - smid = request->smid; - list_del(&request->tracker_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return smid; -} - -/** - * mpt2sas_base_get_smid_hpr - obtain a free smid from hi-priority queue - * @ioc: per adapter object - * @cb_idx: callback index - * - * Returns smid (zero is invalid) - */ -u16 -mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) -{ - unsigned long flags; - struct request_tracker *request; - u16 smid; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (list_empty(&ioc->hpr_free_list)) { - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return 0; - } - - request = list_entry(ioc->hpr_free_list.next, - struct request_tracker, tracker_list); - request->cb_idx = cb_idx; - smid = request->smid; - list_del(&request->tracker_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return smid; -} - - -/** - * mpt2sas_base_free_smid - put smid back on free_list - * @ioc: per adapter object - * @smid: system request message index - * - * Return nothing. - */ -void -mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - unsigned long flags; - int i; - struct chain_tracker *chain_req, *next; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (smid < ioc->hi_priority_smid) { - /* scsiio queue */ - i = smid - 1; - if (!list_empty(&ioc->scsi_lookup[i].chain_list)) { - list_for_each_entry_safe(chain_req, next, - &ioc->scsi_lookup[i].chain_list, tracker_list) { - list_del_init(&chain_req->tracker_list); - list_add(&chain_req->tracker_list, - &ioc->free_chain_list); - } - } - ioc->scsi_lookup[i].cb_idx = 0xFF; - ioc->scsi_lookup[i].scmd = NULL; - ioc->scsi_lookup[i].direct_io = 0; - list_add(&ioc->scsi_lookup[i].tracker_list, - &ioc->free_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - /* - * See _wait_for_commands_to_complete() call with regards - * to this code. - */ - if (ioc->shost_recovery && ioc->pending_io_count) { - if (ioc->pending_io_count == 1) - wake_up(&ioc->reset_wq); - ioc->pending_io_count--; - } - return; - } else if (smid < ioc->internal_smid) { - /* hi-priority */ - i = smid - ioc->hi_priority_smid; - ioc->hpr_lookup[i].cb_idx = 0xFF; - list_add(&ioc->hpr_lookup[i].tracker_list, - &ioc->hpr_free_list); - } else if (smid <= ioc->hba_queue_depth) { - /* internal queue */ - i = smid - ioc->internal_smid; - ioc->internal_lookup[i].cb_idx = 0xFF; - list_add(&ioc->internal_lookup[i].tracker_list, - &ioc->internal_free_list); - } - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); -} - -/** - * _base_writeq - 64 bit write to MMIO - * @ioc: per adapter object - * @b: data payload - * @addr: address in MMIO space - * @writeq_lock: spin lock - * - * Glue for handling an atomic 64 bit word to MMIO. This special handling takes - * care of 32 bit environment where its not quarenteed to send the entire word - * in one transfer. - */ -#ifndef writeq -static inline void _base_writeq(__u64 b, volatile void __iomem *addr, - spinlock_t *writeq_lock) -{ - unsigned long flags; - __u64 data_out = cpu_to_le64(b); - - spin_lock_irqsave(writeq_lock, flags); - writel((u32)(data_out), addr); - writel((u32)(data_out >> 32), (addr + 4)); - spin_unlock_irqrestore(writeq_lock, flags); -} -#else -static inline void _base_writeq(__u64 b, volatile void __iomem *addr, - spinlock_t *writeq_lock) -{ - writeq(cpu_to_le64(b), addr); -} -#endif - -static inline u8 -_base_get_msix_index(struct MPT2SAS_ADAPTER *ioc) -{ - return ioc->cpu_msix_table[raw_smp_processor_id()]; -} - -/** - * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware - * @ioc: per adapter object - * @smid: system request message index - * @handle: device handle - * - * Return nothing. - */ -void -mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u16 handle) -{ - Mpi2RequestDescriptorUnion_t descriptor; - u64 *request = (u64 *)&descriptor; - - - descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; - descriptor.SCSIIO.MSIxIndex = _base_get_msix_index(ioc); - descriptor.SCSIIO.SMID = cpu_to_le16(smid); - descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); - descriptor.SCSIIO.LMID = 0; - _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, - &ioc->scsi_lookup_lock); -} - - -/** - * mpt2sas_base_put_smid_hi_priority - send Task Management request to firmware - * @ioc: per adapter object - * @smid: system request message index - * - * Return nothing. - */ -void -mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - Mpi2RequestDescriptorUnion_t descriptor; - u64 *request = (u64 *)&descriptor; - - descriptor.HighPriority.RequestFlags = - MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; - descriptor.HighPriority.MSIxIndex = 0; - descriptor.HighPriority.SMID = cpu_to_le16(smid); - descriptor.HighPriority.LMID = 0; - descriptor.HighPriority.Reserved1 = 0; - _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, - &ioc->scsi_lookup_lock); -} - -/** - * mpt2sas_base_put_smid_default - Default, primarily used for config pages - * @ioc: per adapter object - * @smid: system request message index - * - * Return nothing. - */ -void -mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - Mpi2RequestDescriptorUnion_t descriptor; - u64 *request = (u64 *)&descriptor; - - descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; - descriptor.Default.MSIxIndex = _base_get_msix_index(ioc); - descriptor.Default.SMID = cpu_to_le16(smid); - descriptor.Default.LMID = 0; - descriptor.Default.DescriptorTypeDependent = 0; - _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, - &ioc->scsi_lookup_lock); -} - -/** - * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware - * @ioc: per adapter object - * @smid: system request message index - * @io_index: value used to track the IO - * - * Return nothing. - */ -void -mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u16 io_index) -{ - Mpi2RequestDescriptorUnion_t descriptor; - u64 *request = (u64 *)&descriptor; - - descriptor.SCSITarget.RequestFlags = - MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET; - descriptor.SCSITarget.MSIxIndex = _base_get_msix_index(ioc); - descriptor.SCSITarget.SMID = cpu_to_le16(smid); - descriptor.SCSITarget.LMID = 0; - descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index); - _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, - &ioc->scsi_lookup_lock); -} - -/** - * _base_display_dell_branding - Disply branding string - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_base_display_dell_branding(struct MPT2SAS_ADAPTER *ioc) -{ - char dell_branding[MPT2SAS_DELL_BRANDING_SIZE]; - - if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_DELL) - return; - - memset(dell_branding, 0, MPT2SAS_DELL_BRANDING_SIZE); - switch (ioc->pdev->subsystem_device) { - case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID: - strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID: - strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID: - strncpy(dell_branding, - MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID: - strncpy(dell_branding, - MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID: - strncpy(dell_branding, - MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_PERC_H200_SSDID: - strncpy(dell_branding, MPT2SAS_DELL_PERC_H200_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - case MPT2SAS_DELL_6GBPS_SAS_SSDID: - strncpy(dell_branding, MPT2SAS_DELL_6GBPS_SAS_BRANDING, - MPT2SAS_DELL_BRANDING_SIZE - 1); - break; - default: - sprintf(dell_branding, "0x%4X", ioc->pdev->subsystem_device); - break; - } - - printk(MPT2SAS_INFO_FMT "%s: Vendor(0x%04X), Device(0x%04X)," - " SSVID(0x%04X), SSDID(0x%04X)\n", ioc->name, dell_branding, - ioc->pdev->vendor, ioc->pdev->device, ioc->pdev->subsystem_vendor, - ioc->pdev->subsystem_device); -} - -/** - * _base_display_intel_branding - Display branding string - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc) -{ - if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL) - return; - - switch (ioc->pdev->device) { - case MPI2_MFGPAGE_DEVID_SAS2008: - switch (ioc->pdev->subsystem_device) { - case MPT2SAS_INTEL_RMS2LL080_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS2LL080_BRANDING); - break; - case MPT2SAS_INTEL_RMS2LL040_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS2LL040_BRANDING); - break; - case MPT2SAS_INTEL_SSD910_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_SSD910_BRANDING); - break; - default: - break; - } - case MPI2_MFGPAGE_DEVID_SAS2308_2: - switch (ioc->pdev->subsystem_device) { - case MPT2SAS_INTEL_RS25GB008_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RS25GB008_BRANDING); - break; - case MPT2SAS_INTEL_RMS25JB080_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25JB080_BRANDING); - break; - case MPT2SAS_INTEL_RMS25JB040_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25JB040_BRANDING); - break; - case MPT2SAS_INTEL_RMS25KB080_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25KB080_BRANDING); - break; - case MPT2SAS_INTEL_RMS25KB040_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25KB040_BRANDING); - break; - case MPT2SAS_INTEL_RMS25LB040_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25LB040_BRANDING); - break; - case MPT2SAS_INTEL_RMS25LB080_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_INTEL_RMS25LB080_BRANDING); - break; - default: - break; - } - default: - break; - } -} - -/** - * _base_display_hp_branding - Display branding string - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_base_display_hp_branding(struct MPT2SAS_ADAPTER *ioc) -{ - if (ioc->pdev->subsystem_vendor != MPT2SAS_HP_3PAR_SSVID) - return; - - switch (ioc->pdev->device) { - case MPI2_MFGPAGE_DEVID_SAS2004: - switch (ioc->pdev->subsystem_device) { - case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); - break; - default: - break; - } - case MPI2_MFGPAGE_DEVID_SAS2308_2: - switch (ioc->pdev->subsystem_device) { - case MPT2SAS_HP_2_4_INTERNAL_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_HP_2_4_INTERNAL_BRANDING); - break; - case MPT2SAS_HP_2_4_EXTERNAL_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_HP_2_4_EXTERNAL_BRANDING); - break; - case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); - break; - case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: - printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); - break; - default: - break; - } - default: - break; - } -} - -/** - * _base_display_ioc_capabilities - Disply IOC's capabilities. - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) -{ - int i = 0; - char desc[16]; - u32 iounit_pg1_flags; - u32 bios_version; - - bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); - strncpy(desc, ioc->manu_pg0.ChipName, 16); - printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), " - "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", - ioc->name, desc, - (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, - (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, - (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, - ioc->facts.FWVersion.Word & 0x000000FF, - ioc->pdev->revision, - (bios_version & 0xFF000000) >> 24, - (bios_version & 0x00FF0000) >> 16, - (bios_version & 0x0000FF00) >> 8, - bios_version & 0x000000FF); - - _base_display_dell_branding(ioc); - _base_display_intel_branding(ioc); - _base_display_hp_branding(ioc); - - printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name); - - if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) { - printk("Initiator"); - i++; - } - - if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) { - printk("%sTarget", i ? "," : ""); - i++; - } - - i = 0; - printk("), "); - printk("Capabilities=("); - - if (!ioc->hide_ir_msg) { - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { - printk("Raid"); - i++; - } - } - - if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { - printk("%sTLR", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) { - printk("%sMulticast", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) { - printk("%sBIDI Target", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) { - printk("%sEEDP", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) { - printk("%sSnapshot Buffer", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) { - printk("%sDiag Trace Buffer", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) { - printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : ""); - i++; - } - - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) { - printk("%sTask Set Full", i ? "," : ""); - i++; - } - - iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); - if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) { - printk("%sNCQ", i ? "," : ""); - i++; - } - - printk(")\n"); -} - -/** - * mpt2sas_base_update_missing_delay - change the missing delay timers - * @ioc: per adapter object - * @device_missing_delay: amount of time till device is reported missing - * @io_missing_delay: interval IO is returned when there is a missing device - * - * Return nothing. - * - * Passed on the command line, this function will modify the device missing - * delay, as well as the io missing delay. This should be called at driver - * load time. - */ -void -mpt2sas_base_update_missing_delay(struct MPT2SAS_ADAPTER *ioc, - u16 device_missing_delay, u8 io_missing_delay) -{ - u16 dmd, dmd_new, dmd_orignal; - u8 io_missing_delay_original; - u16 sz; - Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; - Mpi2ConfigReply_t mpi_reply; - u8 num_phys = 0; - u16 ioc_status; - - mpt2sas_config_get_number_hba_phys(ioc, &num_phys); - if (!num_phys) - return; - - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); - sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg1) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, - sas_iounit_pg1, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - - /* device missing delay */ - dmd = sas_iounit_pg1->ReportDeviceMissingDelay; - if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) - dmd = (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; - else - dmd = dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; - dmd_orignal = dmd; - if (device_missing_delay > 0x7F) { - dmd = (device_missing_delay > 0x7F0) ? 0x7F0 : - device_missing_delay; - dmd = dmd / 16; - dmd |= MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16; - } else - dmd = device_missing_delay; - sas_iounit_pg1->ReportDeviceMissingDelay = dmd; - - /* io missing delay */ - io_missing_delay_original = sas_iounit_pg1->IODeviceMissingDelay; - sas_iounit_pg1->IODeviceMissingDelay = io_missing_delay; - - if (!mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, - sz)) { - if (dmd & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) - dmd_new = (dmd & - MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; - else - dmd_new = - dmd & MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; - printk(MPT2SAS_INFO_FMT "device_missing_delay: old(%d), " - "new(%d)\n", ioc->name, dmd_orignal, dmd_new); - printk(MPT2SAS_INFO_FMT "ioc_missing_delay: old(%d), " - "new(%d)\n", ioc->name, io_missing_delay_original, - io_missing_delay); - ioc->device_missing_delay = dmd_new; - ioc->io_missing_delay = io_missing_delay; - } - -out: - kfree(sas_iounit_pg1); -} - -/** - * _base_static_config_pages - static start of day config pages - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_base_static_config_pages(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2ConfigReply_t mpi_reply; - u32 iounit_pg1_flags; - - mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); - if (ioc->ir_firmware) - mpt2sas_config_get_manufacturing_pg10(ioc, &mpi_reply, - &ioc->manu_pg10); - mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); - mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); - mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); - mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); - mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); - mpt2sas_config_get_iounit_pg8(ioc, &mpi_reply, &ioc->iounit_pg8); - _base_display_ioc_capabilities(ioc); - - /* - * Enable task_set_full handling in iounit_pg1 when the - * facts capabilities indicate that its supported. - */ - iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); - if ((ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING)) - iounit_pg1_flags &= - ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; - else - iounit_pg1_flags |= - MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; - ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); - mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); - - if (ioc->iounit_pg8.NumSensors) - ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors; -} - -/** - * _base_release_memory_pools - release memory - * @ioc: per adapter object - * - * Free memory allocated from _base_allocate_memory_pools. - * - * Return nothing. - */ -static void -_base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) -{ - int i = 0; - struct reply_post_struct *rps; - - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->request) { - pci_free_consistent(ioc->pdev, ioc->request_dma_sz, - ioc->request, ioc->request_dma); - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "request_pool(0x%p)" - ": free\n", ioc->name, ioc->request)); - ioc->request = NULL; - } - - if (ioc->sense) { - pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); - if (ioc->sense_dma_pool) - pci_pool_destroy(ioc->sense_dma_pool); - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_pool(0x%p)" - ": free\n", ioc->name, ioc->sense)); - ioc->sense = NULL; - } - - if (ioc->reply) { - pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma); - if (ioc->reply_dma_pool) - pci_pool_destroy(ioc->reply_dma_pool); - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_pool(0x%p)" - ": free\n", ioc->name, ioc->reply)); - ioc->reply = NULL; - } - - if (ioc->reply_free) { - pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free, - ioc->reply_free_dma); - if (ioc->reply_free_dma_pool) - pci_pool_destroy(ioc->reply_free_dma_pool); - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_pool" - "(0x%p): free\n", ioc->name, ioc->reply_free)); - ioc->reply_free = NULL; - } - - if (ioc->reply_post) { - do { - rps = &ioc->reply_post[i]; - if (rps->reply_post_free) { - pci_pool_free( - ioc->reply_post_free_dma_pool, - rps->reply_post_free, - rps->reply_post_free_dma); - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT - "reply_post_free_pool(0x%p): free\n", - ioc->name, rps->reply_post_free)); - rps->reply_post_free = NULL; - } - } while (ioc->rdpq_array_enable && - (++i < ioc->reply_queue_count)); - - if (ioc->reply_post_free_dma_pool) - pci_pool_destroy(ioc->reply_post_free_dma_pool); - kfree(ioc->reply_post); - } - - if (ioc->config_page) { - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT - "config_page(0x%p): free\n", ioc->name, - ioc->config_page)); - pci_free_consistent(ioc->pdev, ioc->config_page_sz, - ioc->config_page, ioc->config_page_dma); - } - - if (ioc->scsi_lookup) { - free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); - ioc->scsi_lookup = NULL; - } - kfree(ioc->hpr_lookup); - kfree(ioc->internal_lookup); - if (ioc->chain_lookup) { - for (i = 0; i < ioc->chain_depth; i++) { - if (ioc->chain_lookup[i].chain_buffer) - pci_pool_free(ioc->chain_dma_pool, - ioc->chain_lookup[i].chain_buffer, - ioc->chain_lookup[i].chain_buffer_dma); - } - if (ioc->chain_dma_pool) - pci_pool_destroy(ioc->chain_dma_pool); - free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); - ioc->chain_lookup = NULL; - } -} - - -/** - * _base_allocate_memory_pools - allocate start of day memory pools - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 success, anything else error - */ -static int -_base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - struct mpt2sas_facts *facts; - u16 max_sge_elements; - u16 chains_needed_per_io; - u32 sz, total_sz, reply_post_free_sz; - u32 retry_sz; - u16 max_request_credit; - int i; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - retry_sz = 0; - facts = &ioc->facts; - - /* command line tunables for max sgl entries */ - if (max_sgl_entries != -1) { - ioc->shost->sg_tablesize = min_t(unsigned short, - max_sgl_entries, SCSI_MAX_SG_CHAIN_SEGMENTS); - if (ioc->shost->sg_tablesize > MPT2SAS_SG_DEPTH) - printk(MPT2SAS_WARN_FMT - "sg_tablesize(%u) is bigger than kernel defined" - " SCSI_MAX_SG_SEGMENTS(%u)\n", ioc->name, - ioc->shost->sg_tablesize, MPT2SAS_SG_DEPTH); - } else { - ioc->shost->sg_tablesize = MPT2SAS_SG_DEPTH; - } - - /* command line tunables for max controller queue depth */ - if (max_queue_depth != -1 && max_queue_depth != 0) { - max_request_credit = min_t(u16, max_queue_depth + - ioc->hi_priority_depth + ioc->internal_depth, - facts->RequestCredit); - if (max_request_credit > MAX_HBA_QUEUE_DEPTH) - max_request_credit = MAX_HBA_QUEUE_DEPTH; - } else - max_request_credit = min_t(u16, facts->RequestCredit, - MAX_HBA_QUEUE_DEPTH); - - ioc->hba_queue_depth = max_request_credit; - ioc->hi_priority_depth = facts->HighPriorityCredit; - ioc->internal_depth = ioc->hi_priority_depth + 5; - - /* request frame size */ - ioc->request_sz = facts->IOCRequestFrameSize * 4; - - /* reply frame size */ - ioc->reply_sz = facts->ReplyFrameSize * 4; - - retry_allocation: - total_sz = 0; - /* calculate number of sg elements left over in the 1st frame */ - max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) - - sizeof(Mpi2SGEIOUnion_t)) + ioc->sge_size); - ioc->max_sges_in_main_message = max_sge_elements/ioc->sge_size; - - /* now do the same for a chain buffer */ - max_sge_elements = ioc->request_sz - ioc->sge_size; - ioc->max_sges_in_chain_message = max_sge_elements/ioc->sge_size; - - ioc->chain_offset_value_for_main_message = - ((sizeof(Mpi2SCSIIORequest_t) - sizeof(Mpi2SGEIOUnion_t)) + - (ioc->max_sges_in_chain_message * ioc->sge_size)) / 4; - - /* - * MPT2SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE - */ - chains_needed_per_io = ((ioc->shost->sg_tablesize - - ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message) - + 1; - if (chains_needed_per_io > facts->MaxChainDepth) { - chains_needed_per_io = facts->MaxChainDepth; - ioc->shost->sg_tablesize = min_t(u16, - ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message - * chains_needed_per_io), ioc->shost->sg_tablesize); - } - ioc->chains_needed_per_io = chains_needed_per_io; - - /* reply free queue sizing - taking into account for 64 FW events */ - ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; - - /* calculate reply descriptor post queue depth */ - ioc->reply_post_queue_depth = ioc->hba_queue_depth + - ioc->reply_free_queue_depth + 1; - /* align the reply post queue on the next 16 count boundary */ - if (ioc->reply_post_queue_depth % 16) - ioc->reply_post_queue_depth += 16 - - (ioc->reply_post_queue_depth % 16); - - - if (ioc->reply_post_queue_depth > - facts->MaxReplyDescriptorPostQueueDepth) { - ioc->reply_post_queue_depth = - facts->MaxReplyDescriptorPostQueueDepth - - (facts->MaxReplyDescriptorPostQueueDepth % 16); - ioc->hba_queue_depth = - ((ioc->reply_post_queue_depth - 64) / 2) - 1; - ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64; - } - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " - "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " - "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, - ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, - ioc->chains_needed_per_io)); - - /* reply post queue, 16 byte align */ - reply_post_free_sz = ioc->reply_post_queue_depth * - sizeof(Mpi2DefaultReplyDescriptor_t); - - sz = reply_post_free_sz; - if (_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable) - sz *= ioc->reply_queue_count; - - ioc->reply_post = kcalloc((ioc->rdpq_array_enable) ? - (ioc->reply_queue_count):1, - sizeof(struct reply_post_struct), GFP_KERNEL); - - if (!ioc->reply_post) { - printk(MPT2SAS_ERR_FMT "reply_post_free pool: kcalloc failed\n", - ioc->name); - goto out; - } - ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool", - ioc->pdev, sz, 16, 0); - if (!ioc->reply_post_free_dma_pool) { - printk(MPT2SAS_ERR_FMT - "reply_post_free pool: pci_pool_create failed\n", - ioc->name); - goto out; - } - i = 0; - do { - ioc->reply_post[i].reply_post_free = - pci_pool_alloc(ioc->reply_post_free_dma_pool, - GFP_KERNEL, - &ioc->reply_post[i].reply_post_free_dma); - if (!ioc->reply_post[i].reply_post_free) { - printk(MPT2SAS_ERR_FMT - "reply_post_free pool: pci_pool_alloc failed\n", - ioc->name); - goto out; - } - memset(ioc->reply_post[i].reply_post_free, 0, sz); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "reply post free pool (0x%p): depth(%d)," - "element_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->reply_post[i].reply_post_free, - ioc->reply_post_queue_depth, 8, sz/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "reply_post_free_dma = (0x%llx)\n", ioc->name, - (unsigned long long) - ioc->reply_post[i].reply_post_free_dma)); - total_sz += sz; - } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count)); - - if (ioc->dma_mask == 64) { - if (_base_change_consistent_dma_mask(ioc, ioc->pdev) != 0) { - printk(MPT2SAS_WARN_FMT - "no suitable consistent DMA mask for %s\n", - ioc->name, pci_name(ioc->pdev)); - goto out; - } - } - - ioc->scsiio_depth = ioc->hba_queue_depth - - ioc->hi_priority_depth - ioc->internal_depth; - - /* set the scsi host can_queue depth - * with some internal commands that could be outstanding - */ - ioc->shost->can_queue = ioc->scsiio_depth; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: " - "can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue)); - - /* contiguous pool for request and chains, 16 byte align, one extra " - * "frame for smid=0 - */ - ioc->chain_depth = ioc->chains_needed_per_io * ioc->scsiio_depth; - sz = ((ioc->scsiio_depth + 1) * ioc->request_sz); - - /* hi-priority queue */ - sz += (ioc->hi_priority_depth * ioc->request_sz); - - /* internal queue */ - sz += (ioc->internal_depth * ioc->request_sz); - - ioc->request_dma_sz = sz; - ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); - if (!ioc->request) { - printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "failed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kB)\n", ioc->name, ioc->hba_queue_depth, - ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - if (ioc->scsiio_depth < MPT2SAS_SAS_QUEUE_DEPTH) - goto out; - retry_sz += 64; - ioc->hba_queue_depth = max_request_credit - retry_sz; - goto retry_allocation; - } - - if (retry_sz) - printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " - "succeed: hba_depth(%d), chains_per_io(%d), frame_sz(%d), " - "total(%d kb)\n", ioc->name, ioc->hba_queue_depth, - ioc->chains_needed_per_io, ioc->request_sz, sz/1024); - - - /* hi-priority queue */ - ioc->hi_priority = ioc->request + ((ioc->scsiio_depth + 1) * - ioc->request_sz); - ioc->hi_priority_dma = ioc->request_dma + ((ioc->scsiio_depth + 1) * - ioc->request_sz); - - /* internal queue */ - ioc->internal = ioc->hi_priority + (ioc->hi_priority_depth * - ioc->request_sz); - ioc->internal_dma = ioc->hi_priority_dma + (ioc->hi_priority_depth * - ioc->request_sz); - - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): " - "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->request, ioc->hba_queue_depth, ioc->request_sz, - (ioc->hba_queue_depth * ioc->request_sz)/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n", - ioc->name, (unsigned long long) ioc->request_dma)); - total_sz += sz; - - sz = ioc->scsiio_depth * sizeof(struct scsiio_tracker); - ioc->scsi_lookup_pages = get_order(sz); - ioc->scsi_lookup = (struct scsiio_tracker *)__get_free_pages( - GFP_KERNEL, ioc->scsi_lookup_pages); - if (!ioc->scsi_lookup) { - printk(MPT2SAS_ERR_FMT "scsi_lookup: get_free_pages failed, " - "sz(%d)\n", ioc->name, (int)sz); - goto out; - } - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsiio(0x%p): " - "depth(%d)\n", ioc->name, ioc->request, - ioc->scsiio_depth)); - - ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); - sz = ioc->chain_depth * sizeof(struct chain_tracker); - ioc->chain_pages = get_order(sz); - - ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( - GFP_KERNEL, ioc->chain_pages); - if (!ioc->chain_lookup) { - printk(MPT2SAS_ERR_FMT "chain_lookup: get_free_pages failed, " - "sz(%d)\n", ioc->name, (int)sz); - goto out; - } - ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev, - ioc->request_sz, 16, 0); - if (!ioc->chain_dma_pool) { - printk(MPT2SAS_ERR_FMT "chain_dma_pool: pci_pool_create " - "failed\n", ioc->name); - goto out; - } - for (i = 0; i < ioc->chain_depth; i++) { - ioc->chain_lookup[i].chain_buffer = pci_pool_alloc( - ioc->chain_dma_pool , GFP_KERNEL, - &ioc->chain_lookup[i].chain_buffer_dma); - if (!ioc->chain_lookup[i].chain_buffer) { - ioc->chain_depth = i; - goto chain_done; - } - total_sz += ioc->request_sz; - } -chain_done: - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool depth" - "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * - ioc->request_sz))/1024)); - - /* initialize hi-priority queue smid's */ - ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, - sizeof(struct request_tracker), GFP_KERNEL); - if (!ioc->hpr_lookup) { - printk(MPT2SAS_ERR_FMT "hpr_lookup: kcalloc failed\n", - ioc->name); - goto out; - } - ioc->hi_priority_smid = ioc->scsiio_depth + 1; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hi_priority(0x%p): " - "depth(%d), start smid(%d)\n", ioc->name, ioc->hi_priority, - ioc->hi_priority_depth, ioc->hi_priority_smid)); - - /* initialize internal queue smid's */ - ioc->internal_lookup = kcalloc(ioc->internal_depth, - sizeof(struct request_tracker), GFP_KERNEL); - if (!ioc->internal_lookup) { - printk(MPT2SAS_ERR_FMT "internal_lookup: kcalloc failed\n", - ioc->name); - goto out; - } - ioc->internal_smid = ioc->hi_priority_smid + ioc->hi_priority_depth; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "internal(0x%p): " - "depth(%d), start smid(%d)\n", ioc->name, ioc->internal, - ioc->internal_depth, ioc->internal_smid)); - - /* sense buffers, 4 byte align */ - sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; - ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, - 0); - if (!ioc->sense_dma_pool) { - printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_create failed\n", - ioc->name); - goto out; - } - ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL, - &ioc->sense_dma); - if (!ioc->sense) { - printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_alloc failed\n", - ioc->name); - goto out; - } - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "sense pool(0x%p): depth(%d), element_size(%d), pool_size" - "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, - SCSI_SENSE_BUFFERSIZE, sz/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n", - ioc->name, (unsigned long long)ioc->sense_dma)); - total_sz += sz; - - /* reply pool, 4 byte align */ - sz = ioc->reply_free_queue_depth * ioc->reply_sz; - ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4, - 0); - if (!ioc->reply_dma_pool) { - printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_create failed\n", - ioc->name); - goto out; - } - ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL, - &ioc->reply_dma); - if (!ioc->reply) { - printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_alloc failed\n", - ioc->name); - goto out; - } - ioc->reply_dma_min_address = (u32)(ioc->reply_dma); - ioc->reply_dma_max_address = (u32)(ioc->reply_dma) + sz; - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply pool(0x%p): depth" - "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->reply, - ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_dma(0x%llx)\n", - ioc->name, (unsigned long long)ioc->reply_dma)); - total_sz += sz; - - /* reply free queue, 16 byte align */ - sz = ioc->reply_free_queue_depth * 4; - ioc->reply_free_dma_pool = pci_pool_create("reply_free pool", - ioc->pdev, sz, 16, 0); - if (!ioc->reply_free_dma_pool) { - printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_create " - "failed\n", ioc->name); - goto out; - } - ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL, - &ioc->reply_free_dma); - if (!ioc->reply_free) { - printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_alloc " - "failed\n", ioc->name); - goto out; - } - memset(ioc->reply_free, 0, sz); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free pool(0x%p): " - "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name, - ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_dma" - "(0x%llx)\n", ioc->name, (unsigned long long)ioc->reply_free_dma)); - total_sz += sz; - - ioc->config_page_sz = 512; - ioc->config_page = pci_alloc_consistent(ioc->pdev, - ioc->config_page_sz, &ioc->config_page_dma); - if (!ioc->config_page) { - printk(MPT2SAS_ERR_FMT "config page: pci_pool_alloc " - "failed\n", ioc->name); - goto out; - } - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config page(0x%p): size" - "(%d)\n", ioc->name, ioc->config_page, ioc->config_page_sz)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config_page_dma" - "(0x%llx)\n", ioc->name, (unsigned long long)ioc->config_page_dma)); - total_sz += ioc->config_page_sz; - - printk(MPT2SAS_INFO_FMT "Allocated physical memory: size(%d kB)\n", - ioc->name, total_sz/1024); - printk(MPT2SAS_INFO_FMT "Current Controller Queue Depth(%d), " - "Max Controller Queue Depth(%d)\n", - ioc->name, ioc->shost->can_queue, facts->RequestCredit); - printk(MPT2SAS_INFO_FMT "Scatter Gather Elements per IO(%d)\n", - ioc->name, ioc->shost->sg_tablesize); - return 0; - - out: - return -ENOMEM; -} - - -/** - * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter. - * @ioc: Pointer to MPT_ADAPTER structure - * @cooked: Request raw or cooked IOC state - * - * Returns all IOC Doorbell register bits if cooked==0, else just the - * Doorbell bits in MPI_IOC_STATE_MASK. - */ -u32 -mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked) -{ - u32 s, sc; - - s = readl(&ioc->chip->Doorbell); - sc = s & MPI2_IOC_STATE_MASK; - return cooked ? sc : s; -} - -/** - * _base_wait_on_iocstate - waiting on a particular ioc state - * @ioc_state: controller state { READY, OPERATIONAL, or RESET } - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_wait_on_iocstate(struct MPT2SAS_ADAPTER *ioc, u32 ioc_state, int timeout, - int sleep_flag) -{ - u32 count, cntdn; - u32 current_state; - - count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; - do { - current_state = mpt2sas_base_get_iocstate(ioc, 1); - if (current_state == ioc_state) - return 0; - if (count && current_state == MPI2_IOC_STATE_FAULT) - break; - if (sleep_flag == CAN_SLEEP) - msleep(1); - else - udelay(500); - count++; - } while (--cntdn); - - return current_state; -} - -/** - * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by - * a write to the doorbell) - * @ioc: per adapter object - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - * - * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. - */ -static int -_base_wait_for_doorbell_int(struct MPT2SAS_ADAPTER *ioc, int timeout, - int sleep_flag) -{ - u32 cntdn, count; - u32 int_status; - - count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; - do { - int_status = readl(&ioc->chip->HostInterruptStatus); - if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "successful count(%d), timeout(%d)\n", ioc->name, - __func__, count, timeout)); - return 0; - } - if (sleep_flag == CAN_SLEEP) - msleep(1); - else - udelay(500); - count++; - } while (--cntdn); - - printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " - "int_status(%x)!\n", ioc->name, __func__, count, int_status); - return -EFAULT; -} - -/** - * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. - * @ioc: per adapter object - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - * - * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to - * doorbell. - */ -static int -_base_wait_for_doorbell_ack(struct MPT2SAS_ADAPTER *ioc, int timeout, - int sleep_flag) -{ - u32 cntdn, count; - u32 int_status; - u32 doorbell; - - count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; - do { - int_status = readl(&ioc->chip->HostInterruptStatus); - if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "successful count(%d), timeout(%d)\n", ioc->name, - __func__, count, timeout)); - return 0; - } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { - doorbell = readl(&ioc->chip->Doorbell); - if ((doorbell & MPI2_IOC_STATE_MASK) == - MPI2_IOC_STATE_FAULT) { - mpt2sas_base_fault_info(ioc , doorbell); - return -EFAULT; - } - } else if (int_status == 0xFFFFFFFF) - goto out; - - if (sleep_flag == CAN_SLEEP) - msleep(1); - else - udelay(500); - count++; - } while (--cntdn); - - out: - printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " - "int_status(%x)!\n", ioc->name, __func__, count, int_status); - return -EFAULT; -} - -/** - * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use - * @ioc: per adapter object - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - * - */ -static int -_base_wait_for_doorbell_not_used(struct MPT2SAS_ADAPTER *ioc, int timeout, - int sleep_flag) -{ - u32 cntdn, count; - u32 doorbell_reg; - - count = 0; - cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; - do { - doorbell_reg = readl(&ioc->chip->Doorbell); - if (!(doorbell_reg & MPI2_DOORBELL_USED)) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "successful count(%d), timeout(%d)\n", ioc->name, - __func__, count, timeout)); - return 0; - } - if (sleep_flag == CAN_SLEEP) - msleep(1); - else - udelay(500); - count++; - } while (--cntdn); - - printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " - "doorbell_reg(%x)!\n", ioc->name, __func__, count, doorbell_reg); - return -EFAULT; -} - -/** - * _base_send_ioc_reset - send doorbell reset - * @ioc: per adapter object - * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_send_ioc_reset(struct MPT2SAS_ADAPTER *ioc, u8 reset_type, int timeout, - int sleep_flag) -{ - u32 ioc_state; - int r = 0; - - if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) { - printk(MPT2SAS_ERR_FMT "%s: unknown reset_type\n", - ioc->name, __func__); - return -EFAULT; - } - - if (!(ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY)) - return -EFAULT; - - printk(MPT2SAS_INFO_FMT "sending message unit reset !!\n", ioc->name); - - writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, - &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { - r = -EFAULT; - goto out; - } - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); - if (ioc_state) { - printk(MPT2SAS_ERR_FMT "%s: failed going to ready state " - " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); - r = -EFAULT; - goto out; - } - out: - printk(MPT2SAS_INFO_FMT "message unit reset: %s\n", - ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); - return r; -} - -/** - * _base_handshake_req_reply_wait - send request thru doorbell interface - * @ioc: per adapter object - * @request_bytes: request length - * @request: pointer having request payload - * @reply_bytes: reply length - * @reply: pointer to reply payload - * @timeout: timeout in second - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, - u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) -{ - MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; - int i; - u8 failed; - u16 dummy; - __le32 *mfp; - - /* make sure doorbell is not in use */ - if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { - printk(MPT2SAS_ERR_FMT "doorbell is in use " - " (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - - /* clear pending doorbell interrupts from previous state changes */ - if (readl(&ioc->chip->HostInterruptStatus) & - MPI2_HIS_IOC2SYS_DB_STATUS) - writel(0, &ioc->chip->HostInterruptStatus); - - /* send message to ioc */ - writel(((MPI2_FUNCTION_HANDSHAKE<chip->Doorbell); - - if ((_base_wait_for_doorbell_int(ioc, 5, NO_SLEEP))) { - printk(MPT2SAS_ERR_FMT "doorbell handshake " - "int failed (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - writel(0, &ioc->chip->HostInterruptStatus); - - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { - printk(MPT2SAS_ERR_FMT "doorbell handshake " - "ack failed (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - - /* send message 32-bits at a time */ - for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { - writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); - if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) - failed = 1; - } - - if (failed) { - printk(MPT2SAS_ERR_FMT "doorbell handshake " - "sending request failed (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - - /* now wait for the reply */ - if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { - printk(MPT2SAS_ERR_FMT "doorbell handshake " - "int failed (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - - /* read the first two 16-bits, it gives the total length of the reply */ - reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) - & MPI2_DOORBELL_DATA_MASK); - writel(0, &ioc->chip->HostInterruptStatus); - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { - printk(MPT2SAS_ERR_FMT "doorbell handshake " - "int failed (line=%d)\n", ioc->name, __LINE__); - return -EFAULT; - } - reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell) - & MPI2_DOORBELL_DATA_MASK); - writel(0, &ioc->chip->HostInterruptStatus); - - for (i = 2; i < default_reply->MsgLength * 2; i++) { - if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { - printk(MPT2SAS_ERR_FMT "doorbell " - "handshake int failed (line=%d)\n", ioc->name, - __LINE__); - return -EFAULT; - } - if (i >= reply_bytes/2) /* overflow case */ - dummy = readl(&ioc->chip->Doorbell); - else - reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) - & MPI2_DOORBELL_DATA_MASK); - writel(0, &ioc->chip->HostInterruptStatus); - } - - _base_wait_for_doorbell_int(ioc, 5, sleep_flag); - if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "doorbell is in use " - " (line=%d)\n", ioc->name, __LINE__)); - } - writel(0, &ioc->chip->HostInterruptStatus); - - if (ioc->logging_level & MPT_DEBUG_INIT) { - mfp = (__le32 *)reply; - printk(KERN_INFO "\toffset:data\n"); - for (i = 0; i < reply_bytes/4; i++) - printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4, - le32_to_cpu(mfp[i])); - } - return 0; -} - -/** - * mpt2sas_base_sas_iounit_control - send sas iounit control to FW - * @ioc: per adapter object - * @mpi_reply: the reply payload from FW - * @mpi_request: the request payload sent to FW - * - * The SAS IO Unit Control Request message allows the host to perform low-level - * operations, such as resets on the PHYs of the IO Unit, also allows the host - * to obtain the IOC assigned device handles for a device if it has other - * identifying information about the device, in addition allows the host to - * remove IOC resources associated with the device. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, - Mpi2SasIoUnitControlReply_t *mpi_reply, - Mpi2SasIoUnitControlRequest_t *mpi_request) -{ - u16 smid; - u32 ioc_state; - unsigned long timeleft; - bool issue_reset = false; - int rc; - void *request; - u16 wait_state_count; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - mutex_lock(&ioc->base_cmds.mutex); - - if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - rc = 0; - ioc->base_cmds.status = MPT2_CMD_PENDING; - request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->base_cmds.smid = smid; - memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)); - if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || - mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) - ioc->ioc_link_reset_in_progress = 1; - init_completion(&ioc->base_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, - msecs_to_jiffies(10000)); - if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || - mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && - ioc->ioc_link_reset_in_progress) - ioc->ioc_link_reset_in_progress = 0; - if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SasIoUnitControlRequest_t)/4); - if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) - issue_reset = true; - goto issue_host_reset; - } - if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) - memcpy(mpi_reply, ioc->base_cmds.reply, - sizeof(Mpi2SasIoUnitControlReply_t)); - else - memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t)); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - goto out; - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - rc = -EFAULT; - out: - mutex_unlock(&ioc->base_cmds.mutex); - return rc; -} - - -/** - * mpt2sas_base_scsi_enclosure_processor - sending request to sep device - * @ioc: per adapter object - * @mpi_reply: the reply payload from FW - * @mpi_request: the request payload sent to FW - * - * The SCSI Enclosure Processor request message causes the IOC to - * communicate with SES devices to control LED status signals. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, - Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request) -{ - u16 smid; - u32 ioc_state; - unsigned long timeleft; - bool issue_reset = false; - int rc; - void *request; - u16 wait_state_count; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - mutex_lock(&ioc->base_cmds.mutex); - - if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - rc = 0; - ioc->base_cmds.status = MPT2_CMD_PENDING; - request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->base_cmds.smid = smid; - memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); - init_completion(&ioc->base_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, - msecs_to_jiffies(10000)); - if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SepRequest_t)/4); - if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) - issue_reset = true; - goto issue_host_reset; - } - if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) - memcpy(mpi_reply, ioc->base_cmds.reply, - sizeof(Mpi2SepReply_t)); - else - memset(mpi_reply, 0, sizeof(Mpi2SepReply_t)); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - goto out; - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - rc = -EFAULT; - out: - mutex_unlock(&ioc->base_cmds.mutex); - return rc; -} - -/** - * _base_get_port_facts - obtain port facts reply and save in ioc - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag) -{ - Mpi2PortFactsRequest_t mpi_request; - Mpi2PortFactsReply_t mpi_reply; - struct mpt2sas_port_facts *pfacts; - int mpi_reply_sz, mpi_request_sz, r; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - mpi_reply_sz = sizeof(Mpi2PortFactsReply_t); - mpi_request_sz = sizeof(Mpi2PortFactsRequest_t); - memset(&mpi_request, 0, mpi_request_sz); - mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; - mpi_request.PortNumber = port; - r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); - - if (r != 0) { - printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", - ioc->name, __func__, r); - return r; - } - - pfacts = &ioc->pfacts[port]; - memset(pfacts, 0, sizeof(struct mpt2sas_port_facts)); - pfacts->PortNumber = mpi_reply.PortNumber; - pfacts->VP_ID = mpi_reply.VP_ID; - pfacts->VF_ID = mpi_reply.VF_ID; - pfacts->MaxPostedCmdBuffers = - le16_to_cpu(mpi_reply.MaxPostedCmdBuffers); - - return 0; -} - -/** - * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL - * @ioc: per adapter object - * @timeout: - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_wait_for_iocstate(struct MPT2SAS_ADAPTER *ioc, int timeout, - int sleep_flag) -{ - u32 ioc_state, doorbell; - int rc; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->pci_error_recovery) - return 0; - - doorbell = mpt2sas_base_get_iocstate(ioc, 0); - ioc_state = doorbell & MPI2_IOC_STATE_MASK; - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: ioc_state(0x%08x)\n", - ioc->name, __func__, ioc_state)); - - switch (ioc_state) { - case MPI2_IOC_STATE_READY: - case MPI2_IOC_STATE_OPERATIONAL: - return 0; - } - - if (doorbell & MPI2_DOORBELL_USED) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT - "unexpected doorbell activ!e\n", ioc->name)); - goto issue_diag_reset; - } - - if (ioc_state == MPI2_IOC_STATE_FAULT) { - mpt2sas_base_fault_info(ioc, doorbell & - MPI2_DOORBELL_DATA_MASK); - goto issue_diag_reset; - } - - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, - timeout, sleep_flag); - if (ioc_state) { - printk(MPT2SAS_ERR_FMT - "%s: failed going to ready state (ioc_state=0x%x)\n", - ioc->name, __func__, ioc_state); - return -EFAULT; - } - - issue_diag_reset: - rc = _base_diag_reset(ioc, sleep_flag); - return rc; -} - -/** - * _base_get_ioc_facts - obtain ioc facts reply and save in ioc - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - Mpi2IOCFactsRequest_t mpi_request; - Mpi2IOCFactsReply_t mpi_reply; - struct mpt2sas_facts *facts; - int mpi_reply_sz, mpi_request_sz, r; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - r = _base_wait_for_iocstate(ioc, 10, sleep_flag); - if (r) { - printk(MPT2SAS_ERR_FMT "%s: failed getting to correct state\n", - ioc->name, __func__); - return r; - } - - mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); - mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); - memset(&mpi_request, 0, mpi_request_sz); - mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; - r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, - (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); - - if (r != 0) { - printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", - ioc->name, __func__, r); - return r; - } - - facts = &ioc->facts; - memset(facts, 0, sizeof(struct mpt2sas_facts)); - facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion); - facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion); - facts->VP_ID = mpi_reply.VP_ID; - facts->VF_ID = mpi_reply.VF_ID; - facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions); - facts->MaxChainDepth = mpi_reply.MaxChainDepth; - facts->WhoInit = mpi_reply.WhoInit; - facts->NumberOfPorts = mpi_reply.NumberOfPorts; - facts->MaxMSIxVectors = mpi_reply.MaxMSIxVectors; - facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit); - facts->MaxReplyDescriptorPostQueueDepth = - le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth); - facts->ProductID = le16_to_cpu(mpi_reply.ProductID); - facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities); - if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)) - ioc->ir_firmware = 1; - if ((facts->IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE)) - ioc->rdpq_array_capable = 1; - facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word); - facts->IOCRequestFrameSize = - le16_to_cpu(mpi_reply.IOCRequestFrameSize); - facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators); - facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets); - ioc->shost->max_id = -1; - facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders); - facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures); - facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags); - facts->HighPriorityCredit = - le16_to_cpu(mpi_reply.HighPriorityCredit); - facts->ReplyFrameSize = mpi_reply.ReplyFrameSize; - facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle); - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hba queue depth(%d), " - "max chains per io(%d)\n", ioc->name, facts->RequestCredit, - facts->MaxChainDepth)); - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request frame size(%d), " - "reply frame size(%d)\n", ioc->name, - facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4)); - return 0; -} - -/** - * _base_send_ioc_init - send ioc_init to firmware - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - Mpi2IOCInitRequest_t mpi_request; - Mpi2IOCInitReply_t mpi_reply; - int i, r = 0; - struct timeval current_time; - u16 ioc_status; - u32 reply_post_free_array_sz = 0; - Mpi2IOCInitRDPQArrayEntry *reply_post_free_array = NULL; - dma_addr_t reply_post_free_array_dma; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); - mpi_request.Function = MPI2_FUNCTION_IOC_INIT; - mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; - mpi_request.VF_ID = 0; /* TODO */ - mpi_request.VP_ID = 0; - mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); - mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); - - if (_base_is_controller_msix_enabled(ioc)) - mpi_request.HostMSIxVectors = ioc->reply_queue_count; - mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4); - mpi_request.ReplyDescriptorPostQueueDepth = - cpu_to_le16(ioc->reply_post_queue_depth); - mpi_request.ReplyFreeQueueDepth = - cpu_to_le16(ioc->reply_free_queue_depth); - - mpi_request.SenseBufferAddressHigh = - cpu_to_le32((u64)ioc->sense_dma >> 32); - mpi_request.SystemReplyAddressHigh = - cpu_to_le32((u64)ioc->reply_dma >> 32); - mpi_request.SystemRequestFrameBaseAddress = - cpu_to_le64((u64)ioc->request_dma); - mpi_request.ReplyFreeQueueAddress = - cpu_to_le64((u64)ioc->reply_free_dma); - - if (ioc->rdpq_array_enable) { - reply_post_free_array_sz = ioc->reply_queue_count * - sizeof(Mpi2IOCInitRDPQArrayEntry); - reply_post_free_array = pci_alloc_consistent(ioc->pdev, - reply_post_free_array_sz, &reply_post_free_array_dma); - if (!reply_post_free_array) { - printk(MPT2SAS_ERR_FMT - "reply_post_free_array: pci_alloc_consistent failed\n", - ioc->name); - r = -ENOMEM; - goto out; - } - memset(reply_post_free_array, 0, reply_post_free_array_sz); - for (i = 0; i < ioc->reply_queue_count; i++) - reply_post_free_array[i].RDPQBaseAddress = - cpu_to_le64( - (u64)ioc->reply_post[i].reply_post_free_dma); - mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE; - mpi_request.ReplyDescriptorPostQueueAddress = - cpu_to_le64((u64)reply_post_free_array_dma); - } else { - mpi_request.ReplyDescriptorPostQueueAddress = - cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma); - } - - /* This time stamp specifies number of milliseconds - * since epoch ~ midnight January 1, 1970. - */ - do_gettimeofday(¤t_time); - mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 + - (current_time.tv_usec / 1000)); - - if (ioc->logging_level & MPT_DEBUG_INIT) { - __le32 *mfp; - int i; - - mfp = (__le32 *)&mpi_request; - printk(KERN_INFO "\toffset:data\n"); - for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) - printk(KERN_INFO "\t[0x%02x]:%08x\n", i*4, - le32_to_cpu(mfp[i])); - } - - r = _base_handshake_req_reply_wait(ioc, - sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, - sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, - sleep_flag); - - if (r != 0) { - printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", - ioc->name, __func__, r); - goto out; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS || - mpi_reply.IOCLogInfo) { - printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__); - r = -EIO; - } - -out: - if (reply_post_free_array) - pci_free_consistent(ioc->pdev, reply_post_free_array_sz, - reply_post_free_array, - reply_post_free_array_dma); - return r; -} - -/** - * mpt2sas_port_enable_done - command completion routine for port enable - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - u16 ioc_status; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) - return 1; - - if (ioc->port_enable_cmds.status == MPT2_CMD_NOT_USED) - return 1; - - ioc->port_enable_cmds.status |= MPT2_CMD_COMPLETE; - if (mpi_reply) { - ioc->port_enable_cmds.status |= MPT2_CMD_REPLY_VALID; - memcpy(ioc->port_enable_cmds.reply, mpi_reply, - mpi_reply->MsgLength*4); - } - ioc->port_enable_cmds.status &= ~MPT2_CMD_PENDING; - - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - ioc->port_enable_failed = 1; - - if (ioc->is_driver_loading) { - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - mpt2sas_port_enable_complete(ioc); - return 1; - } else { - ioc->start_scan_failed = ioc_status; - ioc->start_scan = 0; - return 1; - } - } - complete(&ioc->port_enable_cmds.done); - return 1; -} - - -/** - * _base_send_port_enable - send port_enable(discovery stuff) to firmware - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - Mpi2PortEnableRequest_t *mpi_request; - Mpi2PortEnableReply_t *mpi_reply; - unsigned long timeleft; - int r = 0; - u16 smid; - u16 ioc_status; - - printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name); - - if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { - printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", - ioc->name, __func__); - return -EAGAIN; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - return -EAGAIN; - } - - ioc->port_enable_cmds.status = MPT2_CMD_PENDING; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->port_enable_cmds.smid = smid; - memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); - mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - - init_completion(&ioc->port_enable_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->port_enable_cmds.done, - 300*HZ); - if (!(ioc->port_enable_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2PortEnableRequest_t)/4); - if (ioc->port_enable_cmds.status & MPT2_CMD_RESET) - r = -EFAULT; - else - r = -ETIME; - goto out; - } - mpi_reply = ioc->port_enable_cmds.reply; - - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "%s: failed with (ioc_status=0x%08x)\n", - ioc->name, __func__, ioc_status); - r = -EFAULT; - goto out; - } - out: - ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED; - printk(MPT2SAS_INFO_FMT "port enable: %s\n", ioc->name, ((r == 0) ? - "SUCCESS" : "FAILED")); - return r; -} - -/** - * mpt2sas_port_enable - initiate firmware discovery (don't wait for reply) - * @ioc: per adapter object - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2PortEnableRequest_t *mpi_request; - u16 smid; - - printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name); - - if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { - printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", - ioc->name, __func__); - return -EAGAIN; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->port_enable_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - return -EAGAIN; - } - - ioc->port_enable_cmds.status = MPT2_CMD_PENDING; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->port_enable_cmds.smid = smid; - memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); - mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; - - mpt2sas_base_put_smid_default(ioc, smid); - return 0; -} - -/** - * _base_determine_wait_on_discovery - desposition - * @ioc: per adapter object - * - * Decide whether to wait on discovery to complete. Used to either - * locate boot device, or report volumes ahead of physical devices. - * - * Returns 1 for wait, 0 for don't wait - */ -static int -_base_determine_wait_on_discovery(struct MPT2SAS_ADAPTER *ioc) -{ - /* We wait for discovery to complete if IR firmware is loaded. - * The sas topology events arrive before PD events, so we need time to - * turn on the bit in ioc->pd_handles to indicate PD - * Also, it maybe required to report Volumes ahead of physical - * devices when MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING is set. - */ - if (ioc->ir_firmware) - return 1; - - /* if no Bios, then we don't need to wait */ - if (!ioc->bios_pg3.BiosVersion) - return 0; - - /* Bios is present, then we drop down here. - * - * If there any entries in the Bios Page 2, then we wait - * for discovery to complete. - */ - - /* Current Boot Device */ - if ((ioc->bios_pg2.CurrentBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK) == - MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && - /* Request Boot Device */ - (ioc->bios_pg2.ReqBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK) == - MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED && - /* Alternate Request Boot Device */ - (ioc->bios_pg2.ReqAltBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK) == - MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED) - return 0; - - return 1; -} - - -/** - * _base_unmask_events - turn on notification for this event - * @ioc: per adapter object - * @event: firmware event - * - * The mask is stored in ioc->event_masks. - */ -static void -_base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event) -{ - u32 desired_event; - - if (event >= 128) - return; - - desired_event = (1 << (event % 32)); - - if (event < 32) - ioc->event_masks[0] &= ~desired_event; - else if (event < 64) - ioc->event_masks[1] &= ~desired_event; - else if (event < 96) - ioc->event_masks[2] &= ~desired_event; - else if (event < 128) - ioc->event_masks[3] &= ~desired_event; -} - -/** - * _base_event_notification - send event notification - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - Mpi2EventNotificationRequest_t *mpi_request; - unsigned long timeleft; - u16 smid; - int r = 0; - int i; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->base_cmds.status & MPT2_CMD_PENDING) { - printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", - ioc->name, __func__); - return -EAGAIN; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - return -EAGAIN; - } - ioc->base_cmds.status = MPT2_CMD_PENDING; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->base_cmds.smid = smid; - memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); - mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) - mpi_request->EventMasks[i] = - cpu_to_le32(ioc->event_masks[i]); - init_completion(&ioc->base_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); - if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2EventNotificationRequest_t)/4); - if (ioc->base_cmds.status & MPT2_CMD_RESET) - r = -EFAULT; - else - r = -ETIME; - } else - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: complete\n", - ioc->name, __func__)); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - return r; -} - -/** - * mpt2sas_base_validate_event_type - validating event types - * @ioc: per adapter object - * @event: firmware event - * - * This will turn on firmware event notification when application - * ask for that event. We don't mask events that are already enabled. - */ -void -mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type) -{ - int i, j; - u32 event_mask, desired_event; - u8 send_update_to_fw; - - for (i = 0, send_update_to_fw = 0; i < - MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) { - event_mask = ~event_type[i]; - desired_event = 1; - for (j = 0; j < 32; j++) { - if (!(event_mask & desired_event) && - (ioc->event_masks[i] & desired_event)) { - ioc->event_masks[i] &= ~desired_event; - send_update_to_fw = 1; - } - desired_event = (desired_event << 1); - } - } - - if (!send_update_to_fw) - return; - - mutex_lock(&ioc->base_cmds.mutex); - _base_event_notification(ioc, CAN_SLEEP); - mutex_unlock(&ioc->base_cmds.mutex); -} - -/** - * _base_diag_reset - the "big hammer" start of day reset - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - u32 host_diagnostic; - u32 ioc_state; - u32 count; - u32 hcb_size; - - printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name); - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "clear interrupts\n", - ioc->name)); - - count = 0; - do { - /* Write magic sequence to WriteSequence register - * Loop until in diagnostic mode - */ - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "write magic " - "sequence\n", ioc->name)); - writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence); - writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); - - /* wait 100 msec */ - if (sleep_flag == CAN_SLEEP) - msleep(100); - else - mdelay(100); - - if (count++ > 20) - goto out; - - host_diagnostic = readl(&ioc->chip->HostDiagnostic); - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "wrote magic " - "sequence: count(%d), host_diagnostic(0x%08x)\n", - ioc->name, count, host_diagnostic)); - - } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); - - hcb_size = readl(&ioc->chip->HCBSize); - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "diag reset: issued\n", - ioc->name)); - writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, - &ioc->chip->HostDiagnostic); - - /* This delay allows the chip PCIe hardware time to finish reset tasks*/ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); - else - mdelay(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000); - - /* Approximately 300 second max wait */ - for (count = 0; count < (300000000 / - MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { - - host_diagnostic = readl(&ioc->chip->HostDiagnostic); - - if (host_diagnostic == 0xFFFFFFFF) - goto out; - if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) - break; - - /* Wait to pass the second read delay window */ - if (sleep_flag == CAN_SLEEP) - msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - /1000); - else - mdelay(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC - /1000); - } - - if (host_diagnostic & MPI2_DIAG_HCB_MODE) { - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter " - "assuming the HCB Address points to good F/W\n", - ioc->name)); - host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; - host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; - writel(host_diagnostic, &ioc->chip->HostDiagnostic); - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT - "re-enable the HCDW\n", ioc->name)); - writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE, - &ioc->chip->HCBSize); - } - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "restart the adapter\n", - ioc->name)); - writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, - &ioc->chip->HostDiagnostic); - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "disable writes to the " - "diagnostic register\n", ioc->name)); - writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); - - drsprintk(ioc, printk(MPT2SAS_INFO_FMT "Wait for FW to go to the " - "READY state\n", ioc->name)); - ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, - sleep_flag); - if (ioc_state) { - printk(MPT2SAS_ERR_FMT "%s: failed going to ready state " - " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); - goto out; - } - - printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name); - return 0; - - out: - printk(MPT2SAS_ERR_FMT "diag reset: FAILED\n", ioc->name); - return -EFAULT; -} - -/** - * _base_make_ioc_ready - put controller in READY state - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * @type: FORCE_BIG_HAMMER or SOFT_RESET - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, - enum reset_type type) -{ - u32 ioc_state; - int rc; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->pci_error_recovery) - return 0; - - ioc_state = mpt2sas_base_get_iocstate(ioc, 0); - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: ioc_state(0x%08x)\n", - ioc->name, __func__, ioc_state)); - - if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) - return 0; - - if (ioc_state & MPI2_DOORBELL_USED) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell " - "active!\n", ioc->name)); - goto issue_diag_reset; - } - - if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt2sas_base_fault_info(ioc, ioc_state & - MPI2_DOORBELL_DATA_MASK); - goto issue_diag_reset; - } - - if (type == FORCE_BIG_HAMMER) - goto issue_diag_reset; - - if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) - if (!(_base_send_ioc_reset(ioc, - MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) { - ioc->ioc_reset_count++; - return 0; - } - - issue_diag_reset: - rc = _base_diag_reset(ioc, CAN_SLEEP); - ioc->ioc_reset_count++; - return rc; -} - -/** - * _base_make_ioc_operational - put controller in OPERATIONAL state - * @ioc: per adapter object - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * Returns 0 for success, non-zero for failure. - */ -static int -_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - int r, i; - unsigned long flags; - u32 reply_address; - u16 smid; - struct _tr_list *delayed_tr, *delayed_tr_next; - u8 hide_flag; - struct adapter_reply_queue *reply_q; - long reply_post_free; - u32 reply_post_free_sz, index = 0; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - /* clean the delayed target reset list */ - list_for_each_entry_safe(delayed_tr, delayed_tr_next, - &ioc->delayed_tr_list, list) { - list_del(&delayed_tr->list); - kfree(delayed_tr); - } - - list_for_each_entry_safe(delayed_tr, delayed_tr_next, - &ioc->delayed_tr_volume_list, list) { - list_del(&delayed_tr->list); - kfree(delayed_tr); - } - - /* initialize the scsi lookup free list */ - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - INIT_LIST_HEAD(&ioc->free_list); - smid = 1; - for (i = 0; i < ioc->scsiio_depth; i++, smid++) { - INIT_LIST_HEAD(&ioc->scsi_lookup[i].chain_list); - ioc->scsi_lookup[i].cb_idx = 0xFF; - ioc->scsi_lookup[i].smid = smid; - ioc->scsi_lookup[i].scmd = NULL; - ioc->scsi_lookup[i].direct_io = 0; - list_add_tail(&ioc->scsi_lookup[i].tracker_list, - &ioc->free_list); - } - - /* hi-priority queue */ - INIT_LIST_HEAD(&ioc->hpr_free_list); - smid = ioc->hi_priority_smid; - for (i = 0; i < ioc->hi_priority_depth; i++, smid++) { - ioc->hpr_lookup[i].cb_idx = 0xFF; - ioc->hpr_lookup[i].smid = smid; - list_add_tail(&ioc->hpr_lookup[i].tracker_list, - &ioc->hpr_free_list); - } - - /* internal queue */ - INIT_LIST_HEAD(&ioc->internal_free_list); - smid = ioc->internal_smid; - for (i = 0; i < ioc->internal_depth; i++, smid++) { - ioc->internal_lookup[i].cb_idx = 0xFF; - ioc->internal_lookup[i].smid = smid; - list_add_tail(&ioc->internal_lookup[i].tracker_list, - &ioc->internal_free_list); - } - - /* chain pool */ - INIT_LIST_HEAD(&ioc->free_chain_list); - for (i = 0; i < ioc->chain_depth; i++) - list_add_tail(&ioc->chain_lookup[i].tracker_list, - &ioc->free_chain_list); - - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - /* initialize Reply Free Queue */ - for (i = 0, reply_address = (u32)ioc->reply_dma ; - i < ioc->reply_free_queue_depth ; i++, reply_address += - ioc->reply_sz) - ioc->reply_free[i] = cpu_to_le32(reply_address); - - /* initialize reply queues */ - if (ioc->is_driver_loading) - _base_assign_reply_queues(ioc); - - /* initialize Reply Post Free Queue */ - reply_post_free_sz = ioc->reply_post_queue_depth * - sizeof(Mpi2DefaultReplyDescriptor_t); - reply_post_free = (long)ioc->reply_post[index].reply_post_free; - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - reply_q->reply_post_host_index = 0; - reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *) - reply_post_free; - for (i = 0; i < ioc->reply_post_queue_depth; i++) - reply_q->reply_post_free[i].Words = - cpu_to_le64(ULLONG_MAX); - if (!_base_is_controller_msix_enabled(ioc)) - goto skip_init_reply_post_free_queue; - /* - * If RDPQ is enabled, switch to the next allocation. - * Otherwise advance within the contiguous region. - */ - if (ioc->rdpq_array_enable) - reply_post_free = (long) - ioc->reply_post[++index].reply_post_free; - else - reply_post_free += reply_post_free_sz; - } - skip_init_reply_post_free_queue: - - r = _base_send_ioc_init(ioc, sleep_flag); - if (r) - return r; - - /* initialize reply free host index */ - ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; - writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); - - /* initialize reply post host index */ - list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT, - &ioc->chip->ReplyPostHostIndex); - if (!_base_is_controller_msix_enabled(ioc)) - goto skip_init_reply_post_host_index; - } - - skip_init_reply_post_host_index: - - _base_unmask_interrupts(ioc); - - r = _base_event_notification(ioc, sleep_flag); - if (r) - return r; - - if (sleep_flag == CAN_SLEEP) - _base_static_config_pages(ioc); - - - if (ioc->is_driver_loading) { - if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier - == 0x80) { - hide_flag = (u8) ( - le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) & - MFG_PAGE10_HIDE_SSDS_MASK); - if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK) - ioc->mfg_pg10_hide_flag = hide_flag; - } - ioc->wait_for_discovery_to_complete = - _base_determine_wait_on_discovery(ioc); - return r; /* scan_start and scan_finished support */ - } - r = _base_send_port_enable(ioc, sleep_flag); - if (r) - return r; - - return r; -} - -/** - * mpt2sas_base_free_resources - free resources controller resources (io/irq/memap) - * @ioc: per adapter object - * - * Return nothing. - */ -void -mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) -{ - struct pci_dev *pdev = ioc->pdev; - - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->chip_phys && ioc->chip) { - _base_mask_interrupts(ioc); - ioc->shost_recovery = 1; - _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); - ioc->shost_recovery = 0; - } - - _base_free_irq(ioc); - _base_disable_msix(ioc); - - if (ioc->chip_phys && ioc->chip) - iounmap(ioc->chip); - ioc->chip_phys = 0; - - if (pci_is_enabled(pdev)) { - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); - } - return; -} - -/** - * mpt2sas_base_attach - attach controller instance - * @ioc: per adapter object - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) -{ - int r, i; - int cpu_id, last_cpu_id = 0; - - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - /* setup cpu_msix_table */ - ioc->cpu_count = num_online_cpus(); - for_each_online_cpu(cpu_id) - last_cpu_id = cpu_id; - ioc->cpu_msix_table_sz = last_cpu_id + 1; - ioc->cpu_msix_table = kzalloc(ioc->cpu_msix_table_sz, GFP_KERNEL); - ioc->reply_queue_count = 1; - if (!ioc->cpu_msix_table) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for " - "cpu_msix_table failed!!!\n", ioc->name)); - r = -ENOMEM; - goto out_free_resources; - } - - if (ioc->is_warpdrive) { - ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, - sizeof(resource_size_t *), GFP_KERNEL); - if (!ioc->reply_post_host_index) { - dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation " - "for cpu_msix_table failed!!!\n", ioc->name)); - r = -ENOMEM; - goto out_free_resources; - } - } - - ioc->rdpq_array_enable_assigned = 0; - ioc->dma_mask = 0; - r = mpt2sas_base_map_resources(ioc); - if (r) - goto out_free_resources; - - if (ioc->is_warpdrive) { - ioc->reply_post_host_index[0] = (resource_size_t __iomem *) - &ioc->chip->ReplyPostHostIndex; - - for (i = 1; i < ioc->cpu_msix_table_sz; i++) - ioc->reply_post_host_index[i] = - (resource_size_t __iomem *) - ((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1) - * 4))); - } - - pci_set_drvdata(ioc->pdev, ioc->shost); - r = _base_get_ioc_facts(ioc, CAN_SLEEP); - if (r) - goto out_free_resources; - - r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); - if (r) - goto out_free_resources; - - ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, - sizeof(struct mpt2sas_port_facts), GFP_KERNEL); - if (!ioc->pfacts) { - r = -ENOMEM; - goto out_free_resources; - } - - for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { - r = _base_get_port_facts(ioc, i, CAN_SLEEP); - if (r) - goto out_free_resources; - } - - r = _base_allocate_memory_pools(ioc, CAN_SLEEP); - if (r) - goto out_free_resources; - - init_waitqueue_head(&ioc->reset_wq); - /* allocate memory pd handle bitmask list */ - ioc->pd_handles_sz = (ioc->facts.MaxDevHandle / 8); - if (ioc->facts.MaxDevHandle % 8) - ioc->pd_handles_sz++; - ioc->pd_handles = kzalloc(ioc->pd_handles_sz, - GFP_KERNEL); - if (!ioc->pd_handles) { - r = -ENOMEM; - goto out_free_resources; - } - ioc->blocking_handles = kzalloc(ioc->pd_handles_sz, - GFP_KERNEL); - if (!ioc->blocking_handles) { - r = -ENOMEM; - goto out_free_resources; - } - ioc->fwfault_debug = mpt2sas_fwfault_debug; - - /* base internal command bits */ - mutex_init(&ioc->base_cmds.mutex); - ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - - /* port_enable command bits */ - ioc->port_enable_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->port_enable_cmds.status = MPT2_CMD_NOT_USED; - - /* transport internal command bits */ - ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->transport_cmds.status = MPT2_CMD_NOT_USED; - mutex_init(&ioc->transport_cmds.mutex); - - /* scsih internal command bits */ - ioc->scsih_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->scsih_cmds.status = MPT2_CMD_NOT_USED; - mutex_init(&ioc->scsih_cmds.mutex); - - /* task management internal command bits */ - ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_init(&ioc->tm_cmds.mutex); - - /* config page internal command bits */ - ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - mutex_init(&ioc->config_cmds.mutex); - - /* ctl module internal command bits */ - ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); - ioc->ctl_cmds.sense = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); - ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - mutex_init(&ioc->ctl_cmds.mutex); - - if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || - !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || - !ioc->config_cmds.reply || !ioc->ctl_cmds.reply || - !ioc->ctl_cmds.sense) { - r = -ENOMEM; - goto out_free_resources; - } - - if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || - !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || - !ioc->config_cmds.reply || !ioc->ctl_cmds.reply) { - r = -ENOMEM; - goto out_free_resources; - } - - for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) - ioc->event_masks[i] = -1; - - /* here we enable the events we care about */ - _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY); - _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); - _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); - _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); - _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); - _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); - _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME); - _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK); - _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); - _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); - _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD); - r = _base_make_ioc_operational(ioc, CAN_SLEEP); - if (r) - goto out_free_resources; - - ioc->non_operational_loop = 0; - - return 0; - - out_free_resources: - - ioc->remove_host = 1; - mpt2sas_base_free_resources(ioc); - _base_release_memory_pools(ioc); - pci_set_drvdata(ioc->pdev, NULL); - kfree(ioc->cpu_msix_table); - if (ioc->is_warpdrive) - kfree(ioc->reply_post_host_index); - kfree(ioc->pd_handles); - kfree(ioc->blocking_handles); - kfree(ioc->tm_cmds.reply); - kfree(ioc->transport_cmds.reply); - kfree(ioc->scsih_cmds.reply); - kfree(ioc->config_cmds.reply); - kfree(ioc->base_cmds.reply); - kfree(ioc->port_enable_cmds.reply); - kfree(ioc->ctl_cmds.reply); - kfree(ioc->ctl_cmds.sense); - kfree(ioc->pfacts); - ioc->ctl_cmds.reply = NULL; - ioc->base_cmds.reply = NULL; - ioc->tm_cmds.reply = NULL; - ioc->scsih_cmds.reply = NULL; - ioc->transport_cmds.reply = NULL; - ioc->config_cmds.reply = NULL; - ioc->pfacts = NULL; - return r; -} - - -/** - * mpt2sas_base_detach - remove controller instance - * @ioc: per adapter object - * - * Return nothing. - */ -void -mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) -{ - - dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - mpt2sas_base_stop_watchdog(ioc); - mpt2sas_base_free_resources(ioc); - _base_release_memory_pools(ioc); - pci_set_drvdata(ioc->pdev, NULL); - kfree(ioc->cpu_msix_table); - if (ioc->is_warpdrive) - kfree(ioc->reply_post_host_index); - kfree(ioc->pd_handles); - kfree(ioc->blocking_handles); - kfree(ioc->pfacts); - kfree(ioc->ctl_cmds.reply); - kfree(ioc->ctl_cmds.sense); - kfree(ioc->base_cmds.reply); - kfree(ioc->port_enable_cmds.reply); - kfree(ioc->tm_cmds.reply); - kfree(ioc->transport_cmds.reply); - kfree(ioc->scsih_cmds.reply); - kfree(ioc->config_cmds.reply); -} - -/** - * _base_reset_handler - reset callback handler (for base) - * @ioc: per adapter object - * @reset_phase: phase - * - * The handler for doing any required cleanup or initialization. - * - * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, - * MPT2_IOC_DONE_RESET - * - * Return nothing. - */ -static void -_base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) -{ - mpt2sas_scsih_reset_handler(ioc, reset_phase); - mpt2sas_ctl_reset_handler(ioc, reset_phase); - switch (reset_phase) { - case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - break; - case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); - if (ioc->transport_cmds.status & MPT2_CMD_PENDING) { - ioc->transport_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid); - complete(&ioc->transport_cmds.done); - } - if (ioc->base_cmds.status & MPT2_CMD_PENDING) { - ioc->base_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid); - complete(&ioc->base_cmds.done); - } - if (ioc->port_enable_cmds.status & MPT2_CMD_PENDING) { - ioc->port_enable_failed = 1; - ioc->port_enable_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->port_enable_cmds.smid); - if (ioc->is_driver_loading) { - ioc->start_scan_failed = - MPI2_IOCSTATUS_INTERNAL_ERROR; - ioc->start_scan = 0; - ioc->port_enable_cmds.status = - MPT2_CMD_NOT_USED; - } else - complete(&ioc->port_enable_cmds.done); - - } - if (ioc->config_cmds.status & MPT2_CMD_PENDING) { - ioc->config_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid); - ioc->config_cmds.smid = USHRT_MAX; - complete(&ioc->config_cmds.done); - } - break; - case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); - break; - } -} - -/** - * _wait_for_commands_to_complete - reset controller - * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * - * This function waiting(3s) for all pending commands to complete - * prior to putting controller in reset. - */ -static void -_wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) -{ - u32 ioc_state; - unsigned long flags; - u16 i; - - ioc->pending_io_count = 0; - if (sleep_flag != CAN_SLEEP) - return; - - ioc_state = mpt2sas_base_get_iocstate(ioc, 0); - if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) - return; - - /* pending command count */ - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = 0; i < ioc->scsiio_depth; i++) - if (ioc->scsi_lookup[i].cb_idx != 0xFF) - ioc->pending_io_count++; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - if (!ioc->pending_io_count) - return; - - /* wait for pending commands to complete */ - wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 10 * HZ); -} - -/** - * mpt2sas_base_hard_reset_handler - reset controller - * @ioc: Pointer to MPT_ADAPTER structure - * @sleep_flag: CAN_SLEEP or NO_SLEEP - * @type: FORCE_BIG_HAMMER or SOFT_RESET - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, - enum reset_type type) -{ - int r; - unsigned long flags; - - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - if (ioc->pci_error_recovery) { - printk(MPT2SAS_ERR_FMT "%s: pci error recovery reset\n", - ioc->name, __func__); - r = 0; - goto out_unlocked; - } - - if (mpt2sas_fwfault_debug) - mpt2sas_halt_firmware(ioc); - - /* TODO - What we really should be doing is pulling - * out all the code associated with NO_SLEEP; its never used. - * That is legacy code from mpt fusion driver, ported over. - * I will leave this BUG_ON here for now till its been resolved. - */ - BUG_ON(sleep_flag == NO_SLEEP); - - /* wait for an active reset in progress to complete */ - if (!mutex_trylock(&ioc->reset_in_progress_mutex)) { - do { - ssleep(1); - } while (ioc->shost_recovery == 1); - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name, - __func__)); - return ioc->ioc_reset_in_progress_status; - } - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - ioc->shost_recovery = 1; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - - _base_reset_handler(ioc, MPT2_IOC_PRE_RESET); - _wait_for_commands_to_complete(ioc, sleep_flag); - _base_mask_interrupts(ioc); - r = _base_make_ioc_ready(ioc, sleep_flag, type); - if (r) - goto out; - _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); - - /* If this hard reset is called while port enable is active, then - * there is no reason to call make_ioc_operational - */ - if (ioc->is_driver_loading && ioc->port_enable_failed) { - ioc->remove_host = 1; - r = -EFAULT; - goto out; - } - - r = _base_get_ioc_facts(ioc, CAN_SLEEP); - if (r) - goto out; - - if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable) - panic("%s: Issue occurred with flashing controller firmware." - "Please reboot the system and ensure that the correct" - " firmware version is running\n", ioc->name); - - r = _base_make_ioc_operational(ioc, sleep_flag); - if (!r) - _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); - out: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: %s\n", - ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED"))); - - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - ioc->ioc_reset_in_progress_status = r; - ioc->shost_recovery = 0; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - mutex_unlock(&ioc->reset_in_progress_mutex); - - out_unlocked: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit\n", ioc->name, - __func__)); - return r; -} diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_base.h b/kernel/drivers/scsi/mpt2sas/mpt2sas_base.h deleted file mode 100644 index caff8d10c..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_base.h +++ /dev/null @@ -1,1198 +0,0 @@ -/* - * This is the Fusion MPT base driver providing common API layer interface - * for access to MPT (Message Passing Technology) firmware. - * - * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#ifndef MPT2SAS_BASE_H_INCLUDED -#define MPT2SAS_BASE_H_INCLUDED - -#include "mpi/mpi2_type.h" -#include "mpi/mpi2.h" -#include "mpi/mpi2_ioc.h" -#include "mpi/mpi2_cnfg.h" -#include "mpi/mpi2_init.h" -#include "mpi/mpi2_raid.h" -#include "mpi/mpi2_tool.h" -#include "mpi/mpi2_sas.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mpt2sas_debug.h" - -/* driver versioning info */ -#define MPT2SAS_DRIVER_NAME "mpt2sas" -#define MPT2SAS_AUTHOR "Avago Technologies " -#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "20.100.00.00" -#define MPT2SAS_MAJOR_VERSION 20 -#define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 00 -#define MPT2SAS_RELEASE_VERSION 00 - -/* - * Set MPT2SAS_SG_DEPTH value based on user input. - */ -#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE -#if CONFIG_SCSI_MPT2SAS_MAX_SGE < 16 -#define MPT2SAS_SG_DEPTH 16 -#elif CONFIG_SCSI_MPT2SAS_MAX_SGE > 128 -#define MPT2SAS_SG_DEPTH 128 -#else -#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE -#endif -#else -#define MPT2SAS_SG_DEPTH 128 /* MAX_HW_SEGMENTS */ -#endif - - -/* - * Generic Defines - */ -#define MPT2SAS_SATA_QUEUE_DEPTH 32 -#define MPT2SAS_SAS_QUEUE_DEPTH 254 -#define MPT2SAS_RAID_QUEUE_DEPTH 128 - -#define MPT_NAME_LENGTH 32 /* generic length of strings */ -#define MPT_STRING_LENGTH 64 - -#define MPT_MAX_CALLBACKS 16 - - -#define CAN_SLEEP 1 -#define NO_SLEEP 0 - -#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ - -#define MPI2_HIM_MASK 0xFFFFFFFF /* mask every bit*/ - -#define MPT2SAS_INVALID_DEVICE_HANDLE 0xFFFF - - -/* - * reset phases - */ -#define MPT2_IOC_PRE_RESET 1 /* prior to host reset */ -#define MPT2_IOC_AFTER_RESET 2 /* just after host reset */ -#define MPT2_IOC_DONE_RESET 3 /* links re-initialized */ - -/* - * logging format - */ -#define MPT2SAS_FMT "%s: " -#define MPT2SAS_INFO_FMT KERN_INFO MPT2SAS_FMT -#define MPT2SAS_NOTE_FMT KERN_NOTICE MPT2SAS_FMT -#define MPT2SAS_WARN_FMT KERN_WARNING MPT2SAS_FMT -#define MPT2SAS_ERR_FMT KERN_ERR MPT2SAS_FMT - -/* - * Dell HBA branding - */ -#define MPT2SAS_DELL_BRANDING_SIZE 32 - -#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA" -#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter" -#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated" -#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular" -#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded" -#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200" -#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS" - -/* - * Dell HBA SSDIDs - */ -#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C -#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D -#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E -#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F -#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20 -#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21 -#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22 - -/* - * Intel HBA branding - */ -#define MPT2SAS_INTEL_RMS25JB080_BRANDING \ - "Intel(R) Integrated RAID Module RMS25JB080" -#define MPT2SAS_INTEL_RMS25JB040_BRANDING \ - "Intel(R) Integrated RAID Module RMS25JB040" -#define MPT2SAS_INTEL_RMS25KB080_BRANDING \ - "Intel(R) Integrated RAID Module RMS25KB080" -#define MPT2SAS_INTEL_RMS25KB040_BRANDING \ - "Intel(R) Integrated RAID Module RMS25KB040" -#define MPT2SAS_INTEL_RMS25LB040_BRANDING \ - "Intel(R) Integrated RAID Module RMS25LB040" -#define MPT2SAS_INTEL_RMS25LB080_BRANDING \ - "Intel(R) Integrated RAID Module RMS25LB080" -#define MPT2SAS_INTEL_RMS2LL080_BRANDING \ - "Intel Integrated RAID Module RMS2LL080" -#define MPT2SAS_INTEL_RMS2LL040_BRANDING \ - "Intel Integrated RAID Module RMS2LL040" -#define MPT2SAS_INTEL_RS25GB008_BRANDING \ - "Intel(R) RAID Controller RS25GB008" -#define MPT2SAS_INTEL_SSD910_BRANDING \ - "Intel(R) SSD 910 Series" -/* - * Intel HBA SSDIDs - */ -#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516 -#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517 -#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518 -#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519 -#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A -#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B -#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E -#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F -#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000 -#define MPT2SAS_INTEL_SSD910_SSDID 0x3700 - -/* - * HP HBA branding - */ -#define MPT2SAS_HP_3PAR_SSVID 0x1590 -#define MPT2SAS_HP_2_4_INTERNAL_BRANDING "HP H220 Host Bus Adapter" -#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING "HP H221 Host Bus Adapter" -#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING "HP H222 Host Bus Adapter" -#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING "HP H220i Host Bus Adapter" -#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING "HP H210i Host Bus Adapter" - -/* - * HO HBA SSDIDs - */ -#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 -#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 -#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 -#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 -#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 - -/* - * WarpDrive Specific Log codes - */ - -#define MPT2_WARPDRIVE_LOGENTRY (0x8002) -#define MPT2_WARPDRIVE_LC_SSDT (0x41) -#define MPT2_WARPDRIVE_LC_SSDLW (0x43) -#define MPT2_WARPDRIVE_LC_SSDLF (0x44) -#define MPT2_WARPDRIVE_LC_BRMF (0x4D) - -/* - * per target private data - */ -#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01 -#define MPT_TARGET_FLAGS_VOLUME 0x02 -#define MPT_TARGET_FLAGS_DELETED 0x04 - -/** - * struct MPT2SAS_TARGET - starget private hostdata - * @starget: starget object - * @sas_address: target sas address - * @raid_device: raid_device pointer to access volume data - * @handle: device handle - * @num_luns: number luns - * @flags: MPT_TARGET_FLAGS_XXX flags - * @deleted: target flaged for deletion - * @tm_busy: target is busy with TM request. - */ -struct MPT2SAS_TARGET { - struct scsi_target *starget; - u64 sas_address; - struct _raid_device *raid_device; - u16 handle; - int num_luns; - u32 flags; - u8 deleted; - u8 tm_busy; -}; - - -/* - * per device private data - */ -#define MPT_DEVICE_FLAGS_INIT 0x01 -#define MPT_DEVICE_TLR_ON 0x02 - -/** - * struct MPT2SAS_DEVICE - sdev private hostdata - * @sas_target: starget private hostdata - * @lun: lun number - * @flags: MPT_DEVICE_XXX flags - * @configured_lun: lun is configured - * @block: device is in SDEV_BLOCK state - * @tlr_snoop_check: flag used in determining whether to disable TLR - */ - -/* OEM Identifiers */ -#define MFG10_OEM_ID_INVALID (0x00000000) -#define MFG10_OEM_ID_DELL (0x00000001) -#define MFG10_OEM_ID_FSC (0x00000002) -#define MFG10_OEM_ID_SUN (0x00000003) -#define MFG10_OEM_ID_IBM (0x00000004) - -/* GENERIC Flags 0*/ -#define MFG10_GF0_OCE_DISABLED (0x00000001) -#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002) -#define MFG10_GF0_R10_DISPLAY (0x00000004) -#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008) -#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010) - -/* OEM Specific Flags will come from OEM specific header files */ -typedef struct _MPI2_CONFIG_PAGE_MAN_10 { - MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ - U8 OEMIdentifier; /* 04h */ - U8 Reserved1; /* 05h */ - U16 Reserved2; /* 08h */ - U32 Reserved3; /* 0Ch */ - U32 GenericFlags0; /* 10h */ - U32 GenericFlags1; /* 14h */ - U32 Reserved4; /* 18h */ - U32 OEMSpecificFlags0; /* 1Ch */ - U32 OEMSpecificFlags1; /* 20h */ - U32 Reserved5[18]; /* 24h-60h*/ -} MPI2_CONFIG_PAGE_MAN_10, - MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_10, - Mpi2ManufacturingPage10_t, MPI2_POINTER pMpi2ManufacturingPage10_t; - -#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003) -#define MFG_PAGE10_HIDE_ALL_DISKS (0x00) -#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01) -#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02) - - -struct MPT2SAS_DEVICE { - struct MPT2SAS_TARGET *sas_target; - unsigned int lun; - u32 flags; - u8 configured_lun; - u8 block; - u8 tlr_snoop_check; -}; - -#define MPT2_CMD_NOT_USED 0x8000 /* free */ -#define MPT2_CMD_COMPLETE 0x0001 /* completed */ -#define MPT2_CMD_PENDING 0x0002 /* pending */ -#define MPT2_CMD_REPLY_VALID 0x0004 /* reply is valid */ -#define MPT2_CMD_RESET 0x0008 /* host reset dropped the command */ - -/** - * struct _internal_cmd - internal commands struct - * @mutex: mutex - * @done: completion - * @reply: reply message pointer - * @sense: sense data - * @status: MPT2_CMD_XXX status - * @smid: system message id - */ -struct _internal_cmd { - struct mutex mutex; - struct completion done; - void *reply; - void *sense; - u16 status; - u16 smid; -}; - - -/** - * struct _sas_device - attached device information - * @list: sas device list - * @starget: starget object - * @sas_address: device sas address - * @device_name: retrieved from the SAS IDENTIFY frame. - * @handle: device handle - * @sas_address_parent: sas address of parent expander or sas host - * @enclosure_handle: enclosure handle - * @enclosure_logical_id: enclosure logical identifier - * @volume_handle: volume handle (valid when hidden raid member) - * @volume_wwid: volume unique identifier - * @device_info: bitfield provides detailed info about the device - * @id: target id - * @channel: target channel - * @slot: number number - * @phy: phy identifier provided in sas device page 0 - * @responding: used in _scsih_sas_device_mark_responding - * @pfa_led_on: flag for PFA LED status - */ -struct _sas_device { - struct list_head list; - struct scsi_target *starget; - u64 sas_address; - u64 device_name; - u16 handle; - u64 sas_address_parent; - u16 enclosure_handle; - u64 enclosure_logical_id; - u16 volume_handle; - u64 volume_wwid; - u32 device_info; - int id; - int channel; - u16 slot; - u8 phy; - u8 responding; - u8 pfa_led_on; -}; - -/** - * struct _raid_device - raid volume link list - * @list: sas device list - * @starget: starget object - * @sdev: scsi device struct (volumes are single lun) - * @wwid: unique identifier for the volume - * @handle: device handle - * @block_size: Block size of the volume - * @id: target id - * @channel: target channel - * @volume_type: the raid level - * @device_info: bitfield provides detailed info about the hidden components - * @num_pds: number of hidden raid components - * @responding: used in _scsih_raid_device_mark_responding - * @percent_complete: resync percent complete - * @direct_io_enabled: Whether direct io to PDs are allowed or not - * @stripe_exponent: X where 2powX is the stripe sz in blocks - * @block_exponent: X where 2powX is the block sz in bytes - * @max_lba: Maximum number of LBA in the volume - * @stripe_sz: Stripe Size of the volume - * @device_info: Device info of the volume member disk - * @pd_handle: Array of handles of the physical drives for direct I/O in le16 - */ -#define MPT_MAX_WARPDRIVE_PDS 8 -struct _raid_device { - struct list_head list; - struct scsi_target *starget; - struct scsi_device *sdev; - u64 wwid; - u16 handle; - u16 block_sz; - int id; - int channel; - u8 volume_type; - u8 num_pds; - u8 responding; - u8 percent_complete; - u8 direct_io_enabled; - u8 stripe_exponent; - u8 block_exponent; - u64 max_lba; - u32 stripe_sz; - u32 device_info; - u16 pd_handle[MPT_MAX_WARPDRIVE_PDS]; -}; - -/** - * struct _boot_device - boot device info - * @is_raid: flag to indicate whether this is volume - * @device: holds pointer for either struct _sas_device or - * struct _raid_device - */ -struct _boot_device { - u8 is_raid; - void *device; -}; - -/** - * struct _sas_port - wide/narrow sas port information - * @port_list: list of ports belonging to expander - * @num_phys: number of phys belonging to this port - * @remote_identify: attached device identification - * @rphy: sas transport rphy object - * @port: sas transport wide/narrow port object - * @phy_list: _sas_phy list objects belonging to this port - */ -struct _sas_port { - struct list_head port_list; - u8 num_phys; - struct sas_identify remote_identify; - struct sas_rphy *rphy; - struct sas_port *port; - struct list_head phy_list; -}; - -/** - * struct _sas_phy - phy information - * @port_siblings: list of phys belonging to a port - * @identify: phy identification - * @remote_identify: attached device identification - * @phy: sas transport phy object - * @phy_id: unique phy id - * @handle: device handle for this phy - * @attached_handle: device handle for attached device - * @phy_belongs_to_port: port has been created for this phy - */ -struct _sas_phy { - struct list_head port_siblings; - struct sas_identify identify; - struct sas_identify remote_identify; - struct sas_phy *phy; - u8 phy_id; - u16 handle; - u16 attached_handle; - u8 phy_belongs_to_port; -}; - -/** - * struct _sas_node - sas_host/expander information - * @list: list of expanders - * @parent_dev: parent device class - * @num_phys: number phys belonging to this sas_host/expander - * @sas_address: sas address of this sas_host/expander - * @handle: handle for this sas_host/expander - * @sas_address_parent: sas address of parent expander or sas host - * @enclosure_handle: handle for this a member of an enclosure - * @device_info: bitwise defining capabilities of this sas_host/expander - * @responding: used in _scsih_expander_device_mark_responding - * @phy: a list of phys that make up this sas_host/expander - * @sas_port_list: list of ports attached to this sas_host/expander - */ -struct _sas_node { - struct list_head list; - struct device *parent_dev; - u8 num_phys; - u64 sas_address; - u16 handle; - u64 sas_address_parent; - u16 enclosure_handle; - u64 enclosure_logical_id; - u8 responding; - struct _sas_phy *phy; - struct list_head sas_port_list; -}; - -/** - * enum reset_type - reset state - * @FORCE_BIG_HAMMER: issue diagnostic reset - * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer - */ -enum reset_type { - FORCE_BIG_HAMMER, - SOFT_RESET, -}; - -/** - * struct chain_tracker - firmware chain tracker - * @chain_buffer: chain buffer - * @chain_buffer_dma: physical address - * @tracker_list: list of free request (ioc->free_chain_list) - */ -struct chain_tracker { - void *chain_buffer; - dma_addr_t chain_buffer_dma; - struct list_head tracker_list; -}; - -/** - * struct scsiio_tracker - scsi mf request tracker - * @smid: system message id - * @scmd: scsi request pointer - * @cb_idx: callback index - * @direct_io: To indicate whether I/O is direct (WARPDRIVE) - * @chain_list: list of chains associated to this IO - * @tracker_list: list of free request (ioc->free_list) - */ -struct scsiio_tracker { - u16 smid; - struct scsi_cmnd *scmd; - u8 cb_idx; - u8 direct_io; - struct list_head chain_list; - struct list_head tracker_list; -}; - -/** - * struct request_tracker - firmware request tracker - * @smid: system message id - * @cb_idx: callback index - * @tracker_list: list of free request (ioc->free_list) - */ -struct request_tracker { - u16 smid; - u8 cb_idx; - struct list_head tracker_list; -}; - -/** - * struct _tr_list - target reset list - * @handle: device handle - * @state: state machine - */ -struct _tr_list { - struct list_head list; - u16 handle; - u16 state; -}; - -typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); - -/** - * struct adapter_reply_queue - the reply queue struct - * @ioc: per adapter object - * @msix_index: msix index into vector table - * @vector: irq vector - * @reply_post_host_index: head index in the pool where FW completes IO - * @reply_post_free: reply post base virt address - * @name: the name registered to request_irq() - * @busy: isr is actively processing replies on another cpu - * @list: this list -*/ -struct adapter_reply_queue { - struct MPT2SAS_ADAPTER *ioc; - u8 msix_index; - unsigned int vector; - u32 reply_post_host_index; - Mpi2ReplyDescriptorsUnion_t *reply_post_free; - char name[MPT_NAME_LENGTH]; - atomic_t busy; - cpumask_var_t affinity_hint; - struct list_head list; -}; - -/* IOC Facts and Port Facts converted from little endian to cpu */ -union mpi2_version_union { - MPI2_VERSION_STRUCT Struct; - u32 Word; -}; - -struct mpt2sas_facts { - u16 MsgVersion; - u16 HeaderVersion; - u8 IOCNumber; - u8 VP_ID; - u8 VF_ID; - u16 IOCExceptions; - u16 IOCStatus; - u32 IOCLogInfo; - u8 MaxChainDepth; - u8 WhoInit; - u8 NumberOfPorts; - u8 MaxMSIxVectors; - u16 RequestCredit; - u16 ProductID; - u32 IOCCapabilities; - union mpi2_version_union FWVersion; - u16 IOCRequestFrameSize; - u16 Reserved3; - u16 MaxInitiators; - u16 MaxTargets; - u16 MaxSasExpanders; - u16 MaxEnclosures; - u16 ProtocolFlags; - u16 HighPriorityCredit; - u16 MaxReplyDescriptorPostQueueDepth; - u8 ReplyFrameSize; - u8 MaxVolumes; - u16 MaxDevHandle; - u16 MaxPersistentEntries; - u16 MinDevHandle; -}; - -struct mpt2sas_port_facts { - u8 PortNumber; - u8 VP_ID; - u8 VF_ID; - u8 PortType; - u16 MaxPostedCmdBuffers; -}; - -struct reply_post_struct { - Mpi2ReplyDescriptorsUnion_t *reply_post_free; - dma_addr_t reply_post_free_dma; -}; - -/** - * enum mutex_type - task management mutex type - * @TM_MUTEX_OFF: mutex is not required becuase calling function is acquiring it - * @TM_MUTEX_ON: mutex is required - */ -enum mutex_type { - TM_MUTEX_OFF = 0, - TM_MUTEX_ON = 1, -}; - -typedef void (*MPT2SAS_FLUSH_RUNNING_CMDS)(struct MPT2SAS_ADAPTER *ioc); -/** - * struct MPT2SAS_ADAPTER - per adapter struct - * @list: ioc_list - * @shost: shost object - * @id: unique adapter id - * @cpu_count: number online cpus - * @name: generic ioc string - * @tmp_string: tmp string used for logging - * @pdev: pci pdev object - * @chip: memory mapped register space - * @chip_phys: physical addrss prior to mapping - * @logging_level: see mpt2sas_debug.h - * @fwfault_debug: debuging FW timeouts - * @ir_firmware: IR firmware present - * @bars: bitmask of BAR's that must be configured - * @mask_interrupts: ignore interrupt - * @dma_mask: used to set the consistent dma mask - * @fault_reset_work_q_name: fw fault work queue - * @fault_reset_work_q: "" - * @fault_reset_work: "" - * @firmware_event_name: fw event work queue - * @firmware_event_thread: "" - * @fw_events_off: flag to turn off fw event handling - * @fw_event_lock: - * @fw_event_list: list of fw events - * @aen_event_read_flag: event log was read - * @broadcast_aen_busy: broadcast aen waiting to be serviced - * @shost_recovery: host reset in progress - * @ioc_reset_in_progress_lock: - * @ioc_link_reset_in_progress: phy/hard reset in progress - * @ignore_loginfos: ignore loginfos during task management - * @remove_host: flag for when driver unloads, to avoid sending dev resets - * @pci_error_recovery: flag to prevent ioc access until slot reset completes - * @wait_for_discovery_to_complete: flag set at driver load time when - * waiting on reporting devices - * @is_driver_loading: flag set at driver load time - * @port_enable_failed: flag set when port enable has failed - * @start_scan: flag set from scan_start callback, cleared from _mpt2sas_fw_work - * @start_scan_failed: means port enable failed, return's the ioc_status - * @msix_enable: flag indicating msix is enabled - * @msix_vector_count: number msix vectors - * @cpu_msix_table: table for mapping cpus to msix index - * @cpu_msix_table_sz: table size - * @schedule_dead_ioc_flush_running_cmds: callback to flush pending commands - * @scsi_io_cb_idx: shost generated commands - * @tm_cb_idx: task management commands - * @scsih_cb_idx: scsih internal commands - * @transport_cb_idx: transport internal commands - * @ctl_cb_idx: clt internal commands - * @base_cb_idx: base internal commands - * @config_cb_idx: base internal commands - * @tm_tr_cb_idx : device removal target reset handshake - * @tm_tr_volume_cb_idx : volume removal target reset - * @base_cmds: - * @transport_cmds: - * @scsih_cmds: - * @tm_cmds: - * @ctl_cmds: - * @config_cmds: - * @base_add_sg_single: handler for either 32/64 bit sgl's - * @event_type: bits indicating which events to log - * @event_context: unique id for each logged event - * @event_log: event log pointer - * @event_masks: events that are masked - * @facts: static facts data - * @pfacts: static port facts data - * @manu_pg0: static manufacturing page 0 - * @manu_pg10: static manufacturing page 10 - * @bios_pg2: static bios page 2 - * @bios_pg3: static bios page 3 - * @ioc_pg8: static ioc page 8 - * @iounit_pg0: static iounit page 0 - * @iounit_pg1: static iounit page 1 - * @iounit_pg8: static iounit page 8 - * @sas_hba: sas host object - * @sas_expander_list: expander object list - * @sas_node_lock: - * @sas_device_list: sas device object list - * @sas_device_init_list: sas device object list (used only at init time) - * @sas_device_lock: - * @io_missing_delay: time for IO completed by fw when PDR enabled - * @device_missing_delay: time for device missing by fw when PDR enabled - * @sas_id : used for setting volume target IDs - * @blocking_handles: bitmask used to identify which devices need blocking - * @pd_handles : bitmask for PD handles - * @pd_handles_sz : size of pd_handle bitmask - * @config_page_sz: config page size - * @config_page: reserve memory for config page payload - * @config_page_dma: - * @hba_queue_depth: hba request queue depth - * @sge_size: sg element size for either 32/64 bit - * @scsiio_depth: SCSI_IO queue depth - * @request_sz: per request frame size - * @request: pool of request frames - * @request_dma: - * @request_dma_sz: - * @scsi_lookup: firmware request tracker list - * @scsi_lookup_lock: - * @free_list: free list of request - * @chain: pool of chains - * @pending_io_count: - * @reset_wq: - * @chain_dma: - * @max_sges_in_main_message: number sg elements in main message - * @max_sges_in_chain_message: number sg elements per chain - * @chains_needed_per_io: max chains per io - * @chain_offset_value_for_main_message: location 1st sg in main - * @chain_depth: total chains allocated - * @hi_priority_smid: - * @hi_priority: - * @hi_priority_dma: - * @hi_priority_depth: - * @hpr_lookup: - * @hpr_free_list: - * @internal_smid: - * @internal: - * @internal_dma: - * @internal_depth: - * @internal_lookup: - * @internal_free_list: - * @sense: pool of sense - * @sense_dma: - * @sense_dma_pool: - * @reply_depth: hba reply queue depth: - * @reply_sz: per reply frame size: - * @reply: pool of replys: - * @reply_dma: - * @reply_dma_pool: - * @reply_free_queue_depth: reply free depth - * @reply_free: pool for reply free queue (32 bit addr) - * @reply_free_dma: - * @reply_free_dma_pool: - * @reply_free_host_index: tail index in pool to insert free replys - * @reply_post_queue_depth: reply post queue depth - * @reply_post_struct: struct for reply_post_free physical & virt address - * @rdpq_array_capable: FW supports multiple reply queue addresses in ioc_init - * @rdpq_array_enable: rdpq_array support is enabled in the driver - * @rdpq_array_enable_assigned: this ensures that rdpq_array_enable flag - * is assigned only ones - * @reply_queue_count: number of reply queue's - * @reply_queue_list: link list contaning the reply queue info - * @reply_post_host_index: head index in the pool where FW completes IO - * @delayed_tr_list: target reset link list - * @delayed_tr_volume_list: volume target reset link list - * @@temp_sensors_count: flag to carry the number of temperature sensors - */ -struct MPT2SAS_ADAPTER { - struct list_head list; - struct Scsi_Host *shost; - u8 id; - int cpu_count; - char name[MPT_NAME_LENGTH]; - char tmp_string[MPT_STRING_LENGTH]; - struct pci_dev *pdev; - Mpi2SystemInterfaceRegs_t __iomem *chip; - resource_size_t chip_phys; - int logging_level; - int fwfault_debug; - u8 ir_firmware; - int bars; - u8 mask_interrupts; - int dma_mask; - - /* fw fault handler */ - char fault_reset_work_q_name[20]; - struct workqueue_struct *fault_reset_work_q; - struct delayed_work fault_reset_work; - - /* fw event handler */ - char firmware_event_name[20]; - struct workqueue_struct *firmware_event_thread; - spinlock_t fw_event_lock; - struct list_head fw_event_list; - - /* misc flags */ - int aen_event_read_flag; - u8 broadcast_aen_busy; - u16 broadcast_aen_pending; - u8 shost_recovery; - - struct mutex reset_in_progress_mutex; - spinlock_t ioc_reset_in_progress_lock; - u8 ioc_link_reset_in_progress; - u8 ioc_reset_in_progress_status; - - u8 ignore_loginfos; - u8 remove_host; - u8 pci_error_recovery; - u8 wait_for_discovery_to_complete; - struct completion port_enable_done; - u8 is_driver_loading; - u8 port_enable_failed; - - u8 start_scan; - u16 start_scan_failed; - - u8 msix_enable; - u16 msix_vector_count; - u8 *cpu_msix_table; - resource_size_t __iomem **reply_post_host_index; - u16 cpu_msix_table_sz; - u32 ioc_reset_count; - MPT2SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; - u32 non_operational_loop; - - /* internal commands, callback index */ - u8 scsi_io_cb_idx; - u8 tm_cb_idx; - u8 transport_cb_idx; - u8 scsih_cb_idx; - u8 ctl_cb_idx; - u8 base_cb_idx; - u8 port_enable_cb_idx; - u8 config_cb_idx; - u8 tm_tr_cb_idx; - u8 tm_tr_volume_cb_idx; - u8 tm_sas_control_cb_idx; - struct _internal_cmd base_cmds; - struct _internal_cmd port_enable_cmds; - struct _internal_cmd transport_cmds; - struct _internal_cmd scsih_cmds; - struct _internal_cmd tm_cmds; - struct _internal_cmd ctl_cmds; - struct _internal_cmd config_cmds; - - MPT_ADD_SGE base_add_sg_single; - - /* event log */ - u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; - u32 event_context; - void *event_log; - u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; - - /* static config pages */ - struct mpt2sas_facts facts; - struct mpt2sas_port_facts *pfacts; - Mpi2ManufacturingPage0_t manu_pg0; - Mpi2BiosPage2_t bios_pg2; - Mpi2BiosPage3_t bios_pg3; - Mpi2IOCPage8_t ioc_pg8; - Mpi2IOUnitPage0_t iounit_pg0; - Mpi2IOUnitPage1_t iounit_pg1; - Mpi2IOUnitPage8_t iounit_pg8; - - struct _boot_device req_boot_device; - struct _boot_device req_alt_boot_device; - struct _boot_device current_boot_device; - - /* sas hba, expander, and device list */ - struct _sas_node sas_hba; - struct list_head sas_expander_list; - spinlock_t sas_node_lock; - struct list_head sas_device_list; - struct list_head sas_device_init_list; - spinlock_t sas_device_lock; - struct list_head raid_device_list; - spinlock_t raid_device_lock; - u8 io_missing_delay; - u16 device_missing_delay; - int sas_id; - void *blocking_handles; - void *pd_handles; - u16 pd_handles_sz; - - /* config page */ - u16 config_page_sz; - void *config_page; - dma_addr_t config_page_dma; - - /* scsiio request */ - u16 hba_queue_depth; - u16 sge_size; - u16 scsiio_depth; - u16 request_sz; - u8 *request; - dma_addr_t request_dma; - u32 request_dma_sz; - struct scsiio_tracker *scsi_lookup; - ulong scsi_lookup_pages; - spinlock_t scsi_lookup_lock; - struct list_head free_list; - int pending_io_count; - wait_queue_head_t reset_wq; - - /* chain */ - struct chain_tracker *chain_lookup; - struct list_head free_chain_list; - struct dma_pool *chain_dma_pool; - ulong chain_pages; - u16 max_sges_in_main_message; - u16 max_sges_in_chain_message; - u16 chains_needed_per_io; - u16 chain_offset_value_for_main_message; - u32 chain_depth; - - /* hi-priority queue */ - u16 hi_priority_smid; - u8 *hi_priority; - dma_addr_t hi_priority_dma; - u16 hi_priority_depth; - struct request_tracker *hpr_lookup; - struct list_head hpr_free_list; - - /* internal queue */ - u16 internal_smid; - u8 *internal; - dma_addr_t internal_dma; - u16 internal_depth; - struct request_tracker *internal_lookup; - struct list_head internal_free_list; - - /* sense */ - u8 *sense; - dma_addr_t sense_dma; - struct dma_pool *sense_dma_pool; - - /* reply */ - u16 reply_sz; - u8 *reply; - dma_addr_t reply_dma; - u32 reply_dma_max_address; - u32 reply_dma_min_address; - struct dma_pool *reply_dma_pool; - - /* reply free queue */ - u16 reply_free_queue_depth; - __le32 *reply_free; - dma_addr_t reply_free_dma; - struct dma_pool *reply_free_dma_pool; - u32 reply_free_host_index; - - /* reply post queue */ - u16 reply_post_queue_depth; - struct reply_post_struct *reply_post; - u8 rdpq_array_capable; - u8 rdpq_array_enable; - u8 rdpq_array_enable_assigned; - struct dma_pool *reply_post_free_dma_pool; - u8 reply_queue_count; - struct list_head reply_queue_list; - - struct list_head delayed_tr_list; - struct list_head delayed_tr_volume_list; - u8 temp_sensors_count; - - /* diag buffer support */ - u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; - u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; - dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT]; - u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT]; - u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT]; - Mpi2ManufacturingPage10_t manu_pg10; - u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23]; - u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; - u32 ring_buffer_offset; - u32 ring_buffer_sz; - u8 is_warpdrive; - u8 hide_ir_msg; - u8 mfg_pg10_hide_flag; - u8 hide_drives; - -}; - -typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply); - - -/* base shared API */ -extern struct list_head mpt2sas_ioc_list; -void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc); -void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc); - -int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc); -void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc); -int mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc); -void mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc); -int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, - enum reset_type type); - -void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); -__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, - u16 smid); -void mpt2sas_base_flush_reply_queues(struct MPT2SAS_ADAPTER *ioc); - -/* hi-priority queue */ -u16 mpt2sas_base_get_smid_hpr(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); -u16 mpt2sas_base_get_smid_scsiio(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx, - struct scsi_cmnd *scmd); - -u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); -void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u16 handle); -void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u16 io_index); -void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid); -void mpt2sas_base_initialize_callback_handler(void); -u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); -void mpt2sas_base_release_callback_handler(u8 cb_idx); - -u8 mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply); -u8 mpt2sas_port_enable_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 msix_index, u32 reply); -void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); - -u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked); - -void mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code); -int mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, - Mpi2SasIoUnitControlReply_t *mpi_reply, Mpi2SasIoUnitControlRequest_t - *mpi_request); -int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, - Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request); -void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type); - -void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); - -void mpt2sas_base_update_missing_delay(struct MPT2SAS_ADAPTER *ioc, - u16 device_missing_delay, u8 io_missing_delay); - -int mpt2sas_port_enable(struct MPT2SAS_ADAPTER *ioc); - -/* scsih shared API */ -void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, - u32 reply); -int mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, - uint channel, uint id, uint lun, u8 type, u16 smid_task, - ulong timeout, enum mutex_type m_type); -void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); -void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); -void mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address); -void mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address); -struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, - u16 handle); -struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER - *ioc, u64 sas_address); -struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( - struct MPT2SAS_ADAPTER *ioc, u64 sas_address); - -void mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc); - -void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); - -/* config shared API */ -u8 mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply); -int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys); -int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); -int mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page); -int mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2BiosPage2_t *config_page); -int mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2BiosPage3_t *config_page); -int mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2IOUnitPage0_t *config_page); -int mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle); -int mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle); -int mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz); -int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2IOUnitPage1_t *config_page); -int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2IOUnitPage1_t *config_page); -int mpt2sas_config_get_iounit_pg8(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page); -int mpt2sas_config_get_iounit_pg3(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); -int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); -int mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); -int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2IOCPage8_t *config_page); -int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle); -int mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, u16 handle); -int mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle); -int mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number); -int mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number); -int mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u32 handle); -int mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 *num_pds); -int mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, u32 handle, u16 sz); -int mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, - u32 form_specific); -int mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, - u16 *volume_handle); -int mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle, - u64 *wwid); -/* ctl shared API */ -extern struct device_attribute *mpt2sas_host_attrs[]; -extern struct device_attribute *mpt2sas_dev_attrs[]; -void mpt2sas_ctl_init(void); -void mpt2sas_ctl_exit(void); -u8 mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply); -void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); -void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, - u32 reply); -void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventNotificationReply_t *mpi_reply); - -void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, - u8 bits_to_regsiter); - -/* transport shared API */ -u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply); -struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, - u16 handle, u64 sas_address); -void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u64 sas_address_parent); -int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy - *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); -int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy - *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev); -void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address, u16 handle, u8 phy_number, u8 link_rate); -extern struct sas_function_template mpt2sas_transport_functions; -extern struct scsi_transport_template *mpt2sas_transport_template; -extern int scsi_internal_device_block(struct scsi_device *sdev); -extern u8 mpt2sas_stm_zero_smid_handler(struct MPT2SAS_ADAPTER *ioc, - u8 msix_index, u32 reply); -extern int scsi_internal_device_unblock(struct scsi_device *sdev, - enum scsi_device_state new_state); - -#endif /* MPT2SAS_BASE_H_INCLUDED */ diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_config.c b/kernel/drivers/scsi/mpt2sas/mpt2sas_config.c deleted file mode 100644 index c43815b1a..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_config.c +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * This module provides common API for accessing firmware configuration pages - * - * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mpt2sas_base.h" - -/* local definitions */ - -/* Timeout for config page request (in seconds) */ -#define MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT 15 - -/* Common sgl flags for READING a config page. */ -#define MPT2_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ - | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT) - -/* Common sgl flags for WRITING a config page. */ -#define MPT2_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ - | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \ - << MPI2_SGE_FLAGS_SHIFT) - -/** - * struct config_request - obtain dma memory via routine - * @sz: size - * @page: virt pointer - * @page_dma: phys pointer - * - */ -struct config_request{ - u16 sz; - void *page; - dma_addr_t page_dma; -}; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _config_display_some_debug - debug routine - * @ioc: per adapter object - * @smid: system request message index - * @calling_function_name: string pass from calling function - * @mpi_reply: reply message frame - * Context: none. - * - * Function for displaying debug info helpful when debugging issues - * in this module. - */ -static void -_config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, - char *calling_function_name, MPI2DefaultReply_t *mpi_reply) -{ - Mpi2ConfigRequest_t *mpi_request; - char *desc = NULL; - - if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) - return; - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) { - case MPI2_CONFIG_PAGETYPE_IO_UNIT: - desc = "io_unit"; - break; - case MPI2_CONFIG_PAGETYPE_IOC: - desc = "ioc"; - break; - case MPI2_CONFIG_PAGETYPE_BIOS: - desc = "bios"; - break; - case MPI2_CONFIG_PAGETYPE_RAID_VOLUME: - desc = "raid_volume"; - break; - case MPI2_CONFIG_PAGETYPE_MANUFACTURING: - desc = "manufaucturing"; - break; - case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK: - desc = "physdisk"; - break; - case MPI2_CONFIG_PAGETYPE_EXTENDED: - switch (mpi_request->ExtPageType) { - case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT: - desc = "sas_io_unit"; - break; - case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER: - desc = "sas_expander"; - break; - case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE: - desc = "sas_device"; - break; - case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY: - desc = "sas_phy"; - break; - case MPI2_CONFIG_EXTPAGETYPE_LOG: - desc = "log"; - break; - case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE: - desc = "enclosure"; - break; - case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG: - desc = "raid_config"; - break; - case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING: - desc = "driver_mapping"; - break; - } - break; - } - - if (!desc) - return; - - printk(MPT2SAS_INFO_FMT "%s: %s(%d), action(%d), form(0x%08x), " - "smid(%d)\n", ioc->name, calling_function_name, desc, - mpi_request->Header.PageNumber, mpi_request->Action, - le32_to_cpu(mpi_request->PageAddress), smid); - - if (!mpi_reply) - return; - - if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) - printk(MPT2SAS_INFO_FMT - "\tiocstatus(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo)); -} -#endif - -/** - * _config_alloc_config_dma_memory - obtain physical memory - * @ioc: per adapter object - * @mem: struct config_request - * - * A wrapper for obtaining dma-able memory for config page request. - * - * Returns 0 for success, non-zero for failure. - */ -static int -_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, - struct config_request *mem) -{ - int r = 0; - - if (mem->sz > ioc->config_page_sz) { - mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz, - &mem->page_dma, GFP_KERNEL); - if (!mem->page) { - printk(MPT2SAS_ERR_FMT "%s: dma_alloc_coherent" - " failed asking for (%d) bytes!!\n", - ioc->name, __func__, mem->sz); - r = -ENOMEM; - } - } else { /* use tmp buffer if less than 512 bytes */ - mem->page = ioc->config_page; - mem->page_dma = ioc->config_page_dma; - } - return r; -} - -/** - * _config_free_config_dma_memory - wrapper to free the memory - * @ioc: per adapter object - * @mem: struct config_request - * - * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory. - * - * Returns 0 for success, non-zero for failure. - */ -static void -_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, - struct config_request *mem) -{ - if (mem->sz > ioc->config_page_sz) - dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page, - mem->page_dma); -} - -/** - * mpt2sas_config_done - config page completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: none. - * - * The callback handler when using _config_request. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - if (ioc->config_cmds.status == MPT2_CMD_NOT_USED) - return 1; - if (ioc->config_cmds.smid != smid) - return 1; - ioc->config_cmds.status |= MPT2_CMD_COMPLETE; - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply) { - ioc->config_cmds.status |= MPT2_CMD_REPLY_VALID; - memcpy(ioc->config_cmds.reply, mpi_reply, - mpi_reply->MsgLength*4); - } - ioc->config_cmds.status &= ~MPT2_CMD_PENDING; -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _config_display_some_debug(ioc, smid, "config_done", mpi_reply); -#endif - ioc->config_cmds.smid = USHRT_MAX; - complete(&ioc->config_cmds.done); - return 1; -} - -/** - * _config_request - main routine for sending config page requests - * @ioc: per adapter object - * @mpi_request: request message frame - * @mpi_reply: reply mf payload returned from firmware - * @timeout: timeout in seconds - * @config_page: contents of the config page - * @config_page_sz: size of config page - * Context: sleep - * - * A generic API for config page requests to firmware. - * - * The ioc->config_cmds.status flag should be MPT2_CMD_NOT_USED before calling - * this API. - * - * The callback index is set inside `ioc->config_cb_idx. - * - * Returns 0 for success, non-zero for failure. - */ -static int -_config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t - *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout, - void *config_page, u16 config_page_sz) -{ - u16 smid; - u32 ioc_state; - unsigned long timeleft; - Mpi2ConfigRequest_t *config_request; - int r; - u8 retry_count, issue_host_reset = 0; - u16 wait_state_count; - struct config_request mem; - - mutex_lock(&ioc->config_cmds.mutex); - if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n", - ioc->name, __func__); - mutex_unlock(&ioc->config_cmds.mutex); - return -EAGAIN; - } - - retry_count = 0; - memset(&mem, 0, sizeof(struct config_request)); - - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - - if (config_page) { - mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion; - mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber; - mpi_request->Header.PageType = mpi_reply->Header.PageType; - mpi_request->Header.PageLength = mpi_reply->Header.PageLength; - mpi_request->ExtPageLength = mpi_reply->ExtPageLength; - mpi_request->ExtPageType = mpi_reply->ExtPageType; - if (mpi_request->Header.PageLength) - mem.sz = mpi_request->Header.PageLength * 4; - else - mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; - r = _config_alloc_config_dma_memory(ioc, &mem); - if (r != 0) - goto out; - if (mpi_request->Action == - MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT || - mpi_request->Action == - MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { - ioc->base_add_sg_single(&mpi_request->PageBufferSGE, - MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz, - mem.page_dma); - memcpy(mem.page, config_page, min_t(u16, mem.sz, - config_page_sz)); - } else { - memset(config_page, 0, config_page_sz); - ioc->base_add_sg_single(&mpi_request->PageBufferSGE, - MPT2_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma); - } - } - - retry_config: - if (retry_count) { - if (retry_count > 2) { /* attempt only 2 retries */ - r = -EFAULT; - goto free_mem; - } - printk(MPT2SAS_INFO_FMT "%s: attempting retry (%d)\n", - ioc->name, __func__, retry_count); - } - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - r = -EFAULT; - goto free_mem; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - r = -EAGAIN; - goto free_mem; - } - - r = 0; - memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t)); - ioc->config_cmds.status = MPT2_CMD_PENDING; - config_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->config_cmds.smid = smid; - memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _config_display_some_debug(ioc, smid, "config_request", NULL); -#endif - init_completion(&ioc->config_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, - timeout*HZ); - if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2ConfigRequest_t)/4); - retry_count++; - if (ioc->config_cmds.smid == smid) - mpt2sas_base_free_smid(ioc, smid); - if ((ioc->shost_recovery) || (ioc->config_cmds.status & - MPT2_CMD_RESET) || ioc->pci_error_recovery) - goto retry_config; - issue_host_reset = 1; - r = -EFAULT; - goto free_mem; - } - - if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID) - memcpy(mpi_reply, ioc->config_cmds.reply, - sizeof(Mpi2ConfigReply_t)); - if (retry_count) - printk(MPT2SAS_INFO_FMT "%s: retry (%d) completed!!\n", - ioc->name, __func__, retry_count); - if (config_page && mpi_request->Action == - MPI2_CONFIG_ACTION_PAGE_READ_CURRENT) - memcpy(config_page, mem.page, min_t(u16, mem.sz, - config_page_sz)); - free_mem: - if (config_page) - _config_free_config_dma_memory(ioc, &mem); - out: - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->config_cmds.mutex); - - if (issue_host_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - return r; -} - -/** - * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_manufacturing_pg10 - obtain manufacturing page 10 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; - mpi_request.Header.PageNumber = 10; - mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_bios_pg2 - obtain bios page 2 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; - mpi_request.Header.PageNumber = 2; - mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_bios_pg3 - obtain bios page 3 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2BiosPage3_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; - mpi_request.Header.PageNumber = 3; - mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_set_iounit_pg1 - set iounit page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_iounit_pg3 - obtain iounit page 3 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @sz: size of buffer passed in config_page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_iounit_pg3(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; - mpi_request.Header.PageNumber = 3; - mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - out: - return r; -} - -/** - * mpt2sas_config_get_iounit_pg8 - obtain iounit page 8 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_iounit_pg8(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; - mpi_request.Header.PageNumber = 8; - mpi_request.Header.PageVersion = MPI2_IOUNITPAGE8_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; - mpi_request.Header.PageNumber = 8; - mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: device handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; - mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; - mpi_request.Header.PageNumber = 0; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: device handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; - mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION; - mpi_request.Header.PageNumber = 1; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host - * @ioc: per adapter object - * @num_phys: pointer returned with the number of phys - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - u16 ioc_status; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasIOUnitPage0_t config_page; - - *num_phys = 0; - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, - sizeof(Mpi2SasIOUnitPage0_t)); - if (!r) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) - *num_phys = config_page.NumPhys; - } - out: - return r; -} - -/** - * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @sz: size of buffer passed in config_page - * Context: sleep. - * - * Calling function should call config_get_number_hba_phys prior to - * this function, so enough memory is allocated for config_page. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - out: - return r; -} - -/** - * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @sz: size of buffer passed in config_page - * Context: sleep. - * - * Calling function should call config_get_number_hba_phys prior to - * this function, so enough memory is allocated for config_page. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - out: - return r; -} - -/** - * mpt2sas_config_set_sas_iounit_pg1 - send sas iounit page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @sz: size of buffer passed in config_page - * Context: sleep. - * - * Calling function should call config_get_number_hba_phys prior to - * this function, so enough memory is allocated for config_page. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_set_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; - _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - out: - return r; -} - -/** - * mpt2sas_config_get_expander_pg0 - obtain expander page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: expander handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_expander_pg1 - obtain expander page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @phy_number: phy number - * @handle: expander handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, - u16 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = - cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | - (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: expander handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_phy_pg0 - obtain phy page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @phy_number: phy number - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = - cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_phy_pg1 - obtain phy page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @phy_number: phy number - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = - cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: volume handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, - u32 handle) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; - mpi_request.Header.PageNumber = 1; - mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume - * @ioc: per adapter object - * @handle: volume handle - * @num_pds: returns pds count - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, - u8 *num_pds) -{ - Mpi2ConfigRequest_t mpi_request; - Mpi2RaidVolPage0_t config_page; - Mpi2ConfigReply_t mpi_reply; - int r; - u16 ioc_status; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - *num_pds = 0; - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = - cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page, - sizeof(Mpi2RaidVolPage0_t)); - if (!r) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) - *num_pds = config_page.NumPhysDisks; - } - - out: - return r; -} - -/** - * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_HANDLE or HANDLE - * @handle: volume handle - * @sz: size of buffer passed in config_page - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, - Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, - u32 handle, u16 sz) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | handle); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); - out: - return r; -} - -/** - * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0 - * @ioc: per adapter object - * @mpi_reply: reply mf payload returned from firmware - * @config_page: contents of the config page - * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE - * @form_specific: specific to the form - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t - *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, - u32 form_specific) -{ - Mpi2ConfigRequest_t mpi_request; - int r; - - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK; - mpi_request.Header.PageNumber = 0; - mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.PageAddress = cpu_to_le32(form | form_specific); - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - r = _config_request(ioc, &mpi_request, mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - sizeof(*config_page)); - out: - return r; -} - -/** - * mpt2sas_config_get_volume_handle - returns volume handle for give hidden raid components - * @ioc: per adapter object - * @pd_handle: phys disk handle - * @volume_handle: volume handle - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, - u16 *volume_handle) -{ - Mpi2RaidConfigurationPage0_t *config_page = NULL; - Mpi2ConfigRequest_t mpi_request; - Mpi2ConfigReply_t mpi_reply; - int r, i, config_page_sz; - u16 ioc_status; - int config_num; - u16 element_type; - u16 phys_disk_dev_handle; - - *volume_handle = 0; - memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); - mpi_request.Function = MPI2_FUNCTION_CONFIG; - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; - mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; - mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG; - mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION; - mpi_request.Header.PageNumber = 0; - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); - if (r) - goto out; - - mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; - config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4); - config_page = kmalloc(config_page_sz, GFP_KERNEL); - if (!config_page) { - r = -1; - goto out; - } - config_num = 0xff; - while (1) { - mpi_request.PageAddress = cpu_to_le32(config_num + - MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM); - r = _config_request(ioc, &mpi_request, &mpi_reply, - MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, - config_page_sz); - if (r) - goto out; - r = -1; - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - goto out; - for (i = 0; i < config_page->NumElements; i++) { - element_type = le16_to_cpu(config_page-> - ConfigElement[i].ElementFlags) & - MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE; - if (element_type == - MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT || - element_type == - MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT) { - phys_disk_dev_handle = - le16_to_cpu(config_page->ConfigElement[i]. - PhysDiskDevHandle); - if (phys_disk_dev_handle == pd_handle) { - *volume_handle = - le16_to_cpu(config_page-> - ConfigElement[i].VolDevHandle); - r = 0; - goto out; - } - } else if (element_type == - MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT) { - *volume_handle = 0; - r = 0; - goto out; - } - } - config_num = config_page->ConfigNum; - } - out: - kfree(config_page); - return r; -} - -/** - * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle - * @ioc: per adapter object - * @volume_handle: volume handle - * @wwid: volume wwid - * Context: sleep. - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle, - u64 *wwid) -{ - Mpi2ConfigReply_t mpi_reply; - Mpi2RaidVolPage1_t raid_vol_pg1; - - *wwid = 0; - if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, - &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, - volume_handle))) { - *wwid = le64_to_cpu(raid_vol_pg1.WWID); - return 0; - } else - return -1; -} diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.c deleted file mode 100644 index 4e509604b..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ /dev/null @@ -1,3077 +0,0 @@ -/* - * Management Module Support for MPT (Message Passing Technology) based - * controllers - * - * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "mpt2sas_base.h" -#include "mpt2sas_ctl.h" - -static DEFINE_MUTEX(_ctl_mutex); -static struct fasync_struct *async_queue; -static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); - -static int _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, - u8 *issue_reset); - -/** - * enum block_state - blocking state - * @NON_BLOCKING: non blocking - * @BLOCKING: blocking - * - * These states are for ioctls that need to wait for a response - * from firmware, so they probably require sleep. - */ -enum block_state { - NON_BLOCKING, - BLOCKING, -}; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _ctl_sas_device_find_by_handle - sas device search - * @ioc: per adapter object - * @handle: sas device handle (assigned by firmware) - * Context: Calling function should acquire ioc->sas_device_lock - * - * This searches for sas_device based on sas_address, then return sas_device - * object. - */ -static struct _sas_device * -_ctl_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_device *sas_device, *r; - - r = NULL; - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - - out: - return r; -} - -/** - * _ctl_display_some_debug - debug routine - * @ioc: per adapter object - * @smid: system request message index - * @calling_function_name: string pass from calling function - * @mpi_reply: reply message frame - * Context: none. - * - * Function for displaying debug info helpful when debugging issues - * in this module. - */ -static void -_ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, - char *calling_function_name, MPI2DefaultReply_t *mpi_reply) -{ - Mpi2ConfigRequest_t *mpi_request; - char *desc = NULL; - - if (!(ioc->logging_level & MPT_DEBUG_IOCTL)) - return; - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - switch (mpi_request->Function) { - case MPI2_FUNCTION_SCSI_IO_REQUEST: - { - Mpi2SCSIIORequest_t *scsi_request = - (Mpi2SCSIIORequest_t *)mpi_request; - - snprintf(ioc->tmp_string, MPT_STRING_LENGTH, - "scsi_io, cmd(0x%02x), cdb_len(%d)", - scsi_request->CDB.CDB32[0], - le16_to_cpu(scsi_request->IoFlags) & 0xF); - desc = ioc->tmp_string; - break; - } - case MPI2_FUNCTION_SCSI_TASK_MGMT: - desc = "task_mgmt"; - break; - case MPI2_FUNCTION_IOC_INIT: - desc = "ioc_init"; - break; - case MPI2_FUNCTION_IOC_FACTS: - desc = "ioc_facts"; - break; - case MPI2_FUNCTION_CONFIG: - { - Mpi2ConfigRequest_t *config_request = - (Mpi2ConfigRequest_t *)mpi_request; - - snprintf(ioc->tmp_string, MPT_STRING_LENGTH, - "config, type(0x%02x), ext_type(0x%02x), number(%d)", - (config_request->Header.PageType & - MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType, - config_request->Header.PageNumber); - desc = ioc->tmp_string; - break; - } - case MPI2_FUNCTION_PORT_FACTS: - desc = "port_facts"; - break; - case MPI2_FUNCTION_PORT_ENABLE: - desc = "port_enable"; - break; - case MPI2_FUNCTION_EVENT_NOTIFICATION: - desc = "event_notification"; - break; - case MPI2_FUNCTION_FW_DOWNLOAD: - desc = "fw_download"; - break; - case MPI2_FUNCTION_FW_UPLOAD: - desc = "fw_upload"; - break; - case MPI2_FUNCTION_RAID_ACTION: - desc = "raid_action"; - break; - case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: - { - Mpi2SCSIIORequest_t *scsi_request = - (Mpi2SCSIIORequest_t *)mpi_request; - - snprintf(ioc->tmp_string, MPT_STRING_LENGTH, - "raid_pass, cmd(0x%02x), cdb_len(%d)", - scsi_request->CDB.CDB32[0], - le16_to_cpu(scsi_request->IoFlags) & 0xF); - desc = ioc->tmp_string; - break; - } - case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: - desc = "sas_iounit_cntl"; - break; - case MPI2_FUNCTION_SATA_PASSTHROUGH: - desc = "sata_pass"; - break; - case MPI2_FUNCTION_DIAG_BUFFER_POST: - desc = "diag_buffer_post"; - break; - case MPI2_FUNCTION_DIAG_RELEASE: - desc = "diag_release"; - break; - case MPI2_FUNCTION_SMP_PASSTHROUGH: - desc = "smp_passthrough"; - break; - } - - if (!desc) - return; - - printk(MPT2SAS_INFO_FMT "%s: %s, smid(%d)\n", - ioc->name, calling_function_name, desc, smid); - - if (!mpi_reply) - return; - - if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) - printk(MPT2SAS_INFO_FMT - "\tiocstatus(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo)); - - if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - mpi_request->Function == - MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - Mpi2SCSIIOReply_t *scsi_reply = - (Mpi2SCSIIOReply_t *)mpi_reply; - struct _sas_device *sas_device = NULL; - unsigned long flags; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _ctl_sas_device_find_by_handle(ioc, - le16_to_cpu(scsi_reply->DevHandle)); - if (sas_device) { - printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), " - "phy(%d)\n", ioc->name, (unsigned long long) - sas_device->sas_address, sas_device->phy); - printk(MPT2SAS_WARN_FMT - "\tenclosure_logical_id(0x%016llx), slot(%d)\n", - ioc->name, sas_device->enclosure_logical_id, - sas_device->slot); - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (scsi_reply->SCSIState || scsi_reply->SCSIStatus) - printk(MPT2SAS_INFO_FMT - "\tscsi_state(0x%02x), scsi_status" - "(0x%02x)\n", ioc->name, - scsi_reply->SCSIState, - scsi_reply->SCSIStatus); - } -} -#endif - -/** - * mpt2sas_ctl_done - ctl module completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: none. - * - * The callback handler when using ioc->ctl_cb_idx. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - Mpi2SCSIIOReply_t *scsiio_reply; - const void *sense_data; - u32 sz; - - if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED) - return 1; - if (ioc->ctl_cmds.smid != smid) - return 1; - ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE; - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply) { - memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); - ioc->ctl_cmds.status |= MPT2_CMD_REPLY_VALID; - /* get sense data */ - if (mpi_reply->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - mpi_reply->Function == - MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - scsiio_reply = (Mpi2SCSIIOReply_t *)mpi_reply; - if (scsiio_reply->SCSIState & - MPI2_SCSI_STATE_AUTOSENSE_VALID) { - sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, - le32_to_cpu(scsiio_reply->SenseCount)); - sense_data = mpt2sas_base_get_sense_buffer(ioc, - smid); - memcpy(ioc->ctl_cmds.sense, sense_data, sz); - } - } - } -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); -#endif - ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING; - complete(&ioc->ctl_cmds.done); - return 1; -} - -/** - * _ctl_check_event_type - determines when an event needs logging - * @ioc: per adapter object - * @event: firmware event - * - * The bitmask in ioc->event_type[] indicates which events should be - * be saved in the driver event_log. This bitmask is set by application. - * - * Returns 1 when event should be captured, or zero means no match. - */ -static int -_ctl_check_event_type(struct MPT2SAS_ADAPTER *ioc, u16 event) -{ - u16 i; - u32 desired_event; - - if (event >= 128 || !event || !ioc->event_log) - return 0; - - desired_event = (1 << (event % 32)); - if (!desired_event) - desired_event = 1; - i = event / 32; - return desired_event & ioc->event_type[i]; -} - -/** - * mpt2sas_ctl_add_to_event_log - add event - * @ioc: per adapter object - * @mpi_reply: reply message frame - * - * Return nothing. - */ -void -mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventNotificationReply_t *mpi_reply) -{ - struct MPT2_IOCTL_EVENTS *event_log; - u16 event; - int i; - u32 sz, event_data_sz; - u8 send_aen = 0; - - if (!ioc->event_log) - return; - - event = le16_to_cpu(mpi_reply->Event); - - if (_ctl_check_event_type(ioc, event)) { - - /* insert entry into circular event_log */ - i = ioc->event_context % MPT2SAS_CTL_EVENT_LOG_SIZE; - event_log = ioc->event_log; - event_log[i].event = event; - event_log[i].context = ioc->event_context++; - - event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4; - sz = min_t(u32, event_data_sz, MPT2_EVENT_DATA_SIZE); - memset(event_log[i].data, 0, MPT2_EVENT_DATA_SIZE); - memcpy(event_log[i].data, mpi_reply->EventData, sz); - send_aen = 1; - } - - /* This aen_event_read_flag flag is set until the - * application has read the event log. - * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify. - */ - if (event == MPI2_EVENT_LOG_ENTRY_ADDED || - (send_aen && !ioc->aen_event_read_flag)) { - ioc->aen_event_read_flag = 1; - wake_up_interruptible(&ctl_poll_wait); - if (async_queue) - kill_fasync(&async_queue, SIGIO, POLL_IN); - } -} - -/** - * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) - * @ioc: per adapter object - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: interrupt. - * - * This function merely adds a new work task into ioc->firmware_event_thread. - * The tasks are worked from _firmware_event_work in user context. - * - * Returns void. - */ -void -mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, - u32 reply) -{ - Mpi2EventNotificationReply_t *mpi_reply; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); - return; -} - -/** - * _ctl_verify_adapter - validates ioc_number passed from application - * @ioc: per adapter object - * @iocpp: The ioc pointer is returned in this. - * - * Return (-1) means error, else ioc_number. - */ -static int -_ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp) -{ - struct MPT2SAS_ADAPTER *ioc; - - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { - if (ioc->id != ioc_number) - continue; - *iocpp = ioc; - return ioc_number; - } - *iocpp = NULL; - return -1; -} - -/** - * mpt2sas_ctl_reset_handler - reset callback handler (for ctl) - * @ioc: per adapter object - * @reset_phase: phase - * - * The handler for doing any required cleanup or initialization. - * - * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, - * MPT2_IOC_DONE_RESET - */ -void -mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) -{ - int i; - u8 issue_reset; - - switch (reset_phase) { - case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { - if (!(ioc->diag_buffer_status[i] & - MPT2_DIAG_BUFFER_IS_REGISTERED)) - continue; - if ((ioc->diag_buffer_status[i] & - MPT2_DIAG_BUFFER_IS_RELEASED)) - continue; - _ctl_send_release(ioc, i, &issue_reset); - } - break; - case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); - if (ioc->ctl_cmds.status & MPT2_CMD_PENDING) { - ioc->ctl_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid); - complete(&ioc->ctl_cmds.done); - } - break; - case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); - - for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { - if (!(ioc->diag_buffer_status[i] & - MPT2_DIAG_BUFFER_IS_REGISTERED)) - continue; - if ((ioc->diag_buffer_status[i] & - MPT2_DIAG_BUFFER_IS_RELEASED)) - continue; - ioc->diag_buffer_status[i] |= - MPT2_DIAG_BUFFER_IS_DIAG_RESET; - } - break; - } -} - -/** - * _ctl_fasync - - * @fd - - * @filep - - * @mode - - * - * Called when application request fasyn callback handler. - */ -static int -_ctl_fasync(int fd, struct file *filep, int mode) -{ - return fasync_helper(fd, filep, mode, &async_queue); -} - -/** - * _ctl_poll - - * @file - - * @wait - - * - */ -static unsigned int -_ctl_poll(struct file *filep, poll_table *wait) -{ - struct MPT2SAS_ADAPTER *ioc; - - poll_wait(filep, &ctl_poll_wait, wait); - - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { - if (ioc->aen_event_read_flag) - return POLLIN | POLLRDNORM; - } - return 0; -} - -/** - * _ctl_set_task_mid - assign an active smid to tm request - * @ioc: per adapter object - * @karg - (struct mpt2_ioctl_command) - * @tm_request - pointer to mf from user space - * - * Returns 0 when an smid if found, else fail. - * during failure, the reply frame is filled. - */ -static int -_ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, - Mpi2SCSITaskManagementRequest_t *tm_request) -{ - u8 found = 0; - u16 i; - u16 handle; - struct scsi_cmnd *scmd; - struct MPT2SAS_DEVICE *priv_data; - unsigned long flags; - Mpi2SCSITaskManagementReply_t *tm_reply; - u32 sz; - u32 lun; - char *desc = NULL; - - if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) - desc = "abort_task"; - else if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) - desc = "query_task"; - else - return 0; - - lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN); - - handle = le16_to_cpu(tm_request->DevHandle); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - for (i = ioc->scsiio_depth; i && !found; i--) { - scmd = ioc->scsi_lookup[i - 1].scmd; - if (scmd == NULL || scmd->device == NULL || - scmd->device->hostdata == NULL) - continue; - if (lun != scmd->device->lun) - continue; - priv_data = scmd->device->hostdata; - if (priv_data->sas_target == NULL) - continue; - if (priv_data->sas_target->handle != handle) - continue; - tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid); - found = 1; - } - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - if (!found) { - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name, - desc, le16_to_cpu(tm_request->DevHandle), lun)); - tm_reply = ioc->ctl_cmds.reply; - tm_reply->DevHandle = tm_request->DevHandle; - tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; - tm_reply->TaskType = tm_request->TaskType; - tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4; - tm_reply->VP_ID = tm_request->VP_ID; - tm_reply->VF_ID = tm_request->VF_ID; - sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz); - if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply, - sz)) - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - return 1; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, - desc, le16_to_cpu(tm_request->DevHandle), lun, - le16_to_cpu(tm_request->TaskMID))); - return 0; -} - -/** - * _ctl_do_mpt_command - main handler for MPT2COMMAND opcode - * @ioc: per adapter object - * @karg - (struct mpt2_ioctl_command) - * @mf - pointer to mf in user space - */ -static long -_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command karg, - void __user *mf) -{ - MPI2RequestHeader_t *mpi_request = NULL, *request; - MPI2DefaultReply_t *mpi_reply; - u32 ioc_state; - u16 ioc_status; - u16 smid; - unsigned long timeout, timeleft; - u8 issue_reset; - u32 sz; - void *psge; - void *data_out = NULL; - dma_addr_t data_out_dma; - size_t data_out_sz = 0; - void *data_in = NULL; - dma_addr_t data_in_dma; - size_t data_in_sz = 0; - u32 sgl_flags; - long ret; - u16 wait_state_count; - - issue_reset = 0; - - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", - ioc->name, __func__); - ret = -EAGAIN; - goto out; - } - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - ret = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); - if (!mpi_request) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a memory for " - "mpi_request\n", ioc->name, __func__); - ret = -ENOMEM; - goto out; - } - - /* Check for overflow and wraparound */ - if (karg.data_sge_offset * 4 > ioc->request_sz || - karg.data_sge_offset > (UINT_MAX / 4)) { - ret = -EINVAL; - goto out; - } - - /* copy in request message frame from user */ - if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, - __func__); - ret = -EFAULT; - goto out; - } - - if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { - smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - ret = -EAGAIN; - goto out; - } - } else { - - smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - ret = -EAGAIN; - goto out; - } - } - - ret = 0; - ioc->ctl_cmds.status = MPT2_CMD_PENDING; - memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); - request = mpt2sas_base_get_msg_frame(ioc, smid); - memcpy(request, mpi_request, karg.data_sge_offset*4); - ioc->ctl_cmds.smid = smid; - data_out_sz = karg.data_out_size; - data_in_sz = karg.data_in_size; - - if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - if (!le16_to_cpu(mpi_request->FunctionDependent1) || - le16_to_cpu(mpi_request->FunctionDependent1) > - ioc->facts.MaxDevHandle) { - ret = -EINVAL; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - } - - /* obtain dma-able memory for data transfer */ - if (data_out_sz) /* WRITE */ { - data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, - &data_out_dma); - if (!data_out) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -ENOMEM; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - if (copy_from_user(data_out, karg.data_out_buf_ptr, - data_out_sz)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -EFAULT; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - } - - if (data_in_sz) /* READ */ { - data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, - &data_in_dma); - if (!data_in) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -ENOMEM; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - } - - /* add scatter gather elements */ - psge = (void *)request + (karg.data_sge_offset*4); - - if (!data_out_sz && !data_in_sz) { - mpt2sas_base_build_zero_len_sge(ioc, psge); - } else if (data_out_sz && data_in_sz) { - /* WRITE sgel first */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - data_out_sz, data_out_dma); - - /* incr sgel */ - psge += ioc->sge_size; - - /* READ sgel last */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - data_in_sz, data_in_dma); - } else if (data_out_sz) /* WRITE */ { - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - data_out_sz, data_out_dma); - } else if (data_in_sz) /* READ */ { - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - data_in_sz, data_in_dma); - } - - /* send command to firmware */ -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); -#endif - - init_completion(&ioc->ctl_cmds.done); - switch (mpi_request->Function) { - case MPI2_FUNCTION_SCSI_IO_REQUEST: - case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: - { - Mpi2SCSIIORequest_t *scsiio_request = - (Mpi2SCSIIORequest_t *)request; - scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; - scsiio_request->SenseBufferLowAddress = - mpt2sas_base_get_sense_buffer_dma(ioc, smid); - memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); - if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) - mpt2sas_base_put_smid_scsi_io(ioc, smid, - le16_to_cpu(mpi_request->FunctionDependent1)); - else - mpt2sas_base_put_smid_default(ioc, smid); - break; - } - case MPI2_FUNCTION_SCSI_TASK_MGMT: - { - Mpi2SCSITaskManagementRequest_t *tm_request = - (Mpi2SCSITaskManagementRequest_t *)request; - - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "TASK_MGMT: " - "handle(0x%04x), task_type(0x%02x)\n", ioc->name, - le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); - - if (tm_request->TaskType == - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || - tm_request->TaskType == - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { - if (_ctl_set_task_mid(ioc, &karg, tm_request)) { - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - } - - mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( - tm_request->DevHandle)); - mpt2sas_base_put_smid_hi_priority(ioc, smid); - break; - } - case MPI2_FUNCTION_SMP_PASSTHROUGH: - { - Mpi2SmpPassthroughRequest_t *smp_request = - (Mpi2SmpPassthroughRequest_t *)mpi_request; - u8 *data; - - /* ioc determines which port to use */ - smp_request->PhysicalPort = 0xFF; - if (smp_request->PassthroughFlags & - MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) - data = (u8 *)&smp_request->SGL; - else { - if (unlikely(data_out == NULL)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - mpt2sas_base_free_smid(ioc, smid); - ret = -EINVAL; - goto out; - } - data = data_out; - } - - if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { - ioc->ioc_link_reset_in_progress = 1; - ioc->ignore_loginfos = 1; - } - mpt2sas_base_put_smid_default(ioc, smid); - break; - } - case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: - { - Mpi2SasIoUnitControlRequest_t *sasiounit_request = - (Mpi2SasIoUnitControlRequest_t *)mpi_request; - - if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET - || sasiounit_request->Operation == - MPI2_SAS_OP_PHY_LINK_RESET) { - ioc->ioc_link_reset_in_progress = 1; - ioc->ignore_loginfos = 1; - } - mpt2sas_base_put_smid_default(ioc, smid); - break; - } - default: - mpt2sas_base_put_smid_default(ioc, smid); - break; - } - - if (karg.timeout < MPT2_IOCTL_DEFAULT_TIMEOUT) - timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; - else - timeout = karg.timeout; - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - timeout*HZ); - if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { - Mpi2SCSITaskManagementRequest_t *tm_request = - (Mpi2SCSITaskManagementRequest_t *)mpi_request; - mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( - tm_request->DevHandle)); - } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || - mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && - ioc->ioc_link_reset_in_progress) { - ioc->ioc_link_reset_in_progress = 0; - ioc->ignore_loginfos = 0; - } - if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, - __func__); - _debug_dump_mf(mpi_request, karg.data_sge_offset); - if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && - (ioc->logging_level & MPT_DEBUG_TM)) { - Mpi2SCSITaskManagementReply_t *tm_reply = - (Mpi2SCSITaskManagementReply_t *)mpi_reply; - - printk(MPT2SAS_INFO_FMT "TASK_MGMT: " - "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " - "TerminationCount(0x%08x)\n", ioc->name, - le16_to_cpu(tm_reply->IOCStatus), - le32_to_cpu(tm_reply->IOCLogInfo), - le32_to_cpu(tm_reply->TerminationCount)); - } -#endif - /* copy out xdata to user */ - if (data_in_sz) { - if (copy_to_user(karg.data_in_buf_ptr, data_in, - data_in_sz)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -ENODATA; - goto out; - } - } - - /* copy out reply message frame to user */ - if (karg.max_reply_bytes) { - sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz); - if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply, - sz)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -ENODATA; - goto out; - } - } - - /* copy out sense to user */ - if (karg.max_sense_bytes && (mpi_request->Function == - MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == - MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { - sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); - if (copy_to_user(karg.sense_data_ptr, - ioc->ctl_cmds.sense, sz)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - ret = -ENODATA; - goto out; - } - } - - issue_host_reset: - if (issue_reset) { - ret = -ENODATA; - if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || - mpi_request->Function == - MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || - mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) { - printk(MPT2SAS_INFO_FMT "issue target reset: handle " - "= (0x%04x)\n", ioc->name, - le16_to_cpu(mpi_request->FunctionDependent1)); - mpt2sas_halt_firmware(ioc); - mpt2sas_scsih_issue_tm(ioc, - le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, - 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10, - TM_MUTEX_ON); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - } else - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - } - - out: - - /* free memory associated with sg buffers */ - if (data_in) - pci_free_consistent(ioc->pdev, data_in_sz, data_in, - data_in_dma); - - if (data_out) - pci_free_consistent(ioc->pdev, data_out_sz, data_out, - data_out_dma); - - kfree(mpi_request); - ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - return ret; -} - -/** - * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_getiocinfo(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_iocinfo karg; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - memset(&karg, 0 , sizeof(karg)); - if (ioc->is_warpdrive) - karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; - else - karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; - if (ioc->pfacts) - karg.port_number = ioc->pfacts[0].PortNumber; - karg.hw_rev = ioc->pdev->revision; - karg.pci_id = ioc->pdev->device; - karg.subsystem_device = ioc->pdev->subsystem_device; - karg.subsystem_vendor = ioc->pdev->subsystem_vendor; - karg.pci_information.u.bits.bus = ioc->pdev->bus->number; - karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn); - karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); - karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); - karg.firmware_version = ioc->facts.FWVersion.Word; - strcpy(karg.driver_version, MPT2SAS_DRIVER_NAME); - strcat(karg.driver_version, "-"); - strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION); - karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); - - if (copy_to_user(arg, &karg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - return 0; -} - -/** - * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_eventquery(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_eventquery karg; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE; - memcpy(karg.event_types, ioc->event_type, - MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); - - if (copy_to_user(arg, &karg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - return 0; -} - -/** - * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_eventenable(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_eventenable karg; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - if (ioc->event_log) - return 0; - memcpy(ioc->event_type, karg.event_types, - MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); - mpt2sas_base_validate_event_type(ioc, ioc->event_type); - - /* initialize event_log */ - ioc->event_context = 0; - ioc->aen_event_read_flag = 0; - ioc->event_log = kcalloc(MPT2SAS_CTL_EVENT_LOG_SIZE, - sizeof(struct MPT2_IOCTL_EVENTS), GFP_KERNEL); - if (!ioc->event_log) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -ENOMEM; - } - return 0; -} - -/** - * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_eventreport(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_eventreport karg; - u32 number_bytes, max_events, max; - struct mpt2_ioctl_eventreport __user *uarg = arg; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - number_bytes = karg.hdr.max_data_size - - sizeof(struct mpt2_ioctl_header); - max_events = number_bytes/sizeof(struct MPT2_IOCTL_EVENTS); - max = min_t(u32, MPT2SAS_CTL_EVENT_LOG_SIZE, max_events); - - /* If fewer than 1 event is requested, there must have - * been some type of error. - */ - if (!max || !ioc->event_log) - return -ENODATA; - - number_bytes = max * sizeof(struct MPT2_IOCTL_EVENTS); - if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - /* reset flag so SIGIO can restart */ - ioc->aen_event_read_flag = 0; - return 0; -} - -/** - * _ctl_do_reset - main handler for MPT2HARDRESET opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_do_reset(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_diag_reset karg; - int retval; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - if (ioc->shost_recovery || ioc->pci_error_recovery || - ioc->is_driver_loading) - return -EAGAIN; - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, - __func__)); - - retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - printk(MPT2SAS_INFO_FMT "host reset: %s\n", - ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); - return 0; -} - -/** - * _ctl_btdh_search_sas_device - searching for sas device - * @ioc: per adapter object - * @btdh: btdh ioctl payload - */ -static int -_ctl_btdh_search_sas_device(struct MPT2SAS_ADAPTER *ioc, - struct mpt2_ioctl_btdh_mapping *btdh) -{ - struct _sas_device *sas_device; - unsigned long flags; - int rc = 0; - - if (list_empty(&ioc->sas_device_list)) - return rc; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && - btdh->handle == sas_device->handle) { - btdh->bus = sas_device->channel; - btdh->id = sas_device->id; - rc = 1; - goto out; - } else if (btdh->bus == sas_device->channel && btdh->id == - sas_device->id && btdh->handle == 0xFFFF) { - btdh->handle = sas_device->handle; - rc = 1; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return rc; -} - -/** - * _ctl_btdh_search_raid_device - searching for raid device - * @ioc: per adapter object - * @btdh: btdh ioctl payload - */ -static int -_ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc, - struct mpt2_ioctl_btdh_mapping *btdh) -{ - struct _raid_device *raid_device; - unsigned long flags; - int rc = 0; - - if (list_empty(&ioc->raid_device_list)) - return rc; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - list_for_each_entry(raid_device, &ioc->raid_device_list, list) { - if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && - btdh->handle == raid_device->handle) { - btdh->bus = raid_device->channel; - btdh->id = raid_device->id; - rc = 1; - goto out; - } else if (btdh->bus == raid_device->channel && btdh->id == - raid_device->id && btdh->handle == 0xFFFF) { - btdh->handle = raid_device->handle; - rc = 1; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - return rc; -} - -/** - * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_btdh_mapping(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_ioctl_btdh_mapping karg; - int rc; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - rc = _ctl_btdh_search_sas_device(ioc, &karg); - if (!rc) - _ctl_btdh_search_raid_device(ioc, &karg); - - if (copy_to_user(arg, &karg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - return 0; -} - -/** - * _ctl_diag_capability - return diag buffer capability - * @ioc: per adapter object - * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED - * - * returns 1 when diag buffer support is enabled in firmware - */ -static u8 -_ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type) -{ - u8 rc = 0; - - switch (buffer_type) { - case MPI2_DIAG_BUF_TYPE_TRACE: - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) - rc = 1; - break; - case MPI2_DIAG_BUF_TYPE_SNAPSHOT: - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) - rc = 1; - break; - case MPI2_DIAG_BUF_TYPE_EXTENDED: - if (ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) - rc = 1; - } - - return rc; -} - -/** - * _ctl_diag_register_2 - wrapper for registering diag buffer support - * @ioc: per adapter object - * @diag_register: the diag_register struct passed in from user space - * - */ -static long -_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, - struct mpt2_diag_register *diag_register) -{ - int rc, i; - void *request_data = NULL; - dma_addr_t request_data_dma; - u32 request_data_sz = 0; - Mpi2DiagBufferPostRequest_t *mpi_request; - Mpi2DiagBufferPostReply_t *mpi_reply; - u8 buffer_type; - unsigned long timeleft; - u16 smid; - u16 ioc_status; - u8 issue_reset = 0; - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - buffer_type = diag_register->buffer_type; - if (!_ctl_diag_capability(ioc, buffer_type)) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -EPERM; - } - - if (ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_REGISTERED) { - printk(MPT2SAS_ERR_FMT "%s: already has a registered " - "buffer for buffer_type(0x%02x)\n", ioc->name, __func__, - buffer_type); - return -EINVAL; - } - - if (diag_register->requested_buffer_size % 4) { - printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size " - "is not 4 byte aligned\n", ioc->name, __func__); - return -EINVAL; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - rc = 0; - ioc->ctl_cmds.status = MPT2_CMD_PENDING; - memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->ctl_cmds.smid = smid; - - request_data = ioc->diag_buffer[buffer_type]; - request_data_sz = diag_register->requested_buffer_size; - ioc->unique_id[buffer_type] = diag_register->unique_id; - ioc->diag_buffer_status[buffer_type] = 0; - memcpy(ioc->product_specific[buffer_type], - diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS); - ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; - - if (request_data) { - request_data_dma = ioc->diag_buffer_dma[buffer_type]; - if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) { - pci_free_consistent(ioc->pdev, - ioc->diag_buffer_sz[buffer_type], - request_data, request_data_dma); - request_data = NULL; - } - } - - if (request_data == NULL) { - ioc->diag_buffer_sz[buffer_type] = 0; - ioc->diag_buffer_dma[buffer_type] = 0; - request_data = pci_alloc_consistent( - ioc->pdev, request_data_sz, &request_data_dma); - if (request_data == NULL) { - printk(MPT2SAS_ERR_FMT "%s: failed allocating memory" - " for diag buffers, requested size(%d)\n", - ioc->name, __func__, request_data_sz); - mpt2sas_base_free_smid(ioc, smid); - return -ENOMEM; - } - ioc->diag_buffer[buffer_type] = request_data; - ioc->diag_buffer_sz[buffer_type] = request_data_sz; - ioc->diag_buffer_dma[buffer_type] = request_data_dma; - } - - mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; - mpi_request->BufferType = diag_register->buffer_type; - mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags); - mpi_request->BufferAddress = cpu_to_le64(request_data_dma); - mpi_request->BufferLength = cpu_to_le32(request_data_sz); - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(0x%p), " - "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, - (unsigned long long)request_data_dma, - le32_to_cpu(mpi_request->BufferLength))); - - for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) - mpi_request->ProductSpecific[i] = - cpu_to_le32(ioc->product_specific[buffer_type][i]); - - init_completion(&ioc->ctl_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); - - if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, - __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2DiagBufferPostRequest_t)/4); - if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - /* process the completed Reply Message Frame */ - if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { - printk(MPT2SAS_ERR_FMT "%s: no reply message\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - - mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - ioc->diag_buffer_status[buffer_type] |= - MPT2_DIAG_BUFFER_IS_REGISTERED; - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", - ioc->name, __func__)); - } else { - printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " - "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); - rc = -EFAULT; - } - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - - out: - - if (rc && request_data) - pci_free_consistent(ioc->pdev, request_data_sz, - request_data, request_data_dma); - - ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - return rc; -} - -/** - * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time - * @ioc: per adapter object - * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1 - * - * This is called when command line option diag_buffer_enable is enabled - * at driver load time. - */ -void -mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register) -{ - struct mpt2_diag_register diag_register; - - memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); - - if (bits_to_register & 1) { - printk(MPT2SAS_INFO_FMT "registering trace buffer support\n", - ioc->name); - diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; - /* register for 1MB buffers */ - diag_register.requested_buffer_size = (1024 * 1024); - diag_register.unique_id = 0x7075900; - _ctl_diag_register_2(ioc, &diag_register); - } - - if (bits_to_register & 2) { - printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n", - ioc->name); - diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT; - /* register for 2MB buffers */ - diag_register.requested_buffer_size = 2 * (1024 * 1024); - diag_register.unique_id = 0x7075901; - _ctl_diag_register_2(ioc, &diag_register); - } - - if (bits_to_register & 4) { - printk(MPT2SAS_INFO_FMT "registering extended buffer support\n", - ioc->name); - diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED; - /* register for 2MB buffers */ - diag_register.requested_buffer_size = 2 * (1024 * 1024); - diag_register.unique_id = 0x7075901; - _ctl_diag_register_2(ioc, &diag_register); - } -} - -/** - * _ctl_diag_register - application register with driver - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - * - * This will allow the driver to setup any required buffers that will be - * needed by firmware to communicate with the driver. - */ -static long -_ctl_diag_register(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_diag_register karg; - long rc; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - rc = _ctl_diag_register_2(ioc, &karg); - return rc; -} - -/** - * _ctl_diag_unregister - application unregister with driver - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - * - * This will allow the driver to cleanup any memory allocated for diag - * messages and to free up any resources. - */ -static long -_ctl_diag_unregister(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_diag_unregister karg; - void *request_data; - dma_addr_t request_data_dma; - u32 request_data_sz; - u8 buffer_type; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - buffer_type = karg.unique_id & 0x000000ff; - if (!_ctl_diag_capability(ioc, buffer_type)) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -EPERM; - } - - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " - "registered\n", ioc->name, __func__, buffer_type); - return -EINVAL; - } - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) has not been " - "released\n", ioc->name, __func__, buffer_type); - return -EINVAL; - } - - if (karg.unique_id != ioc->unique_id[buffer_type]) { - printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " - "registered\n", ioc->name, __func__, karg.unique_id); - return -EINVAL; - } - - request_data = ioc->diag_buffer[buffer_type]; - if (!request_data) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -ENOMEM; - } - - request_data_sz = ioc->diag_buffer_sz[buffer_type]; - request_data_dma = ioc->diag_buffer_dma[buffer_type]; - pci_free_consistent(ioc->pdev, request_data_sz, - request_data, request_data_dma); - ioc->diag_buffer[buffer_type] = NULL; - ioc->diag_buffer_status[buffer_type] = 0; - return 0; -} - -/** - * _ctl_diag_query - query relevant info associated with diag buffers - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - * - * The application will send only buffer_type and unique_id. Driver will - * inspect unique_id first, if valid, fill in all the info. If unique_id is - * 0x00, the driver will return info specified by Buffer Type. - */ -static long -_ctl_diag_query(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_diag_query karg; - void *request_data; - int i; - u8 buffer_type; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - karg.application_flags = 0; - buffer_type = karg.buffer_type; - - if (!_ctl_diag_capability(ioc, buffer_type)) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -EPERM; - } - - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " - "registered\n", ioc->name, __func__, buffer_type); - return -EINVAL; - } - - if (karg.unique_id & 0xffffff00) { - if (karg.unique_id != ioc->unique_id[buffer_type]) { - printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " - "registered\n", ioc->name, __func__, - karg.unique_id); - return -EINVAL; - } - } - - request_data = ioc->diag_buffer[buffer_type]; - if (!request_data) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -ENOMEM; - } - - if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) - karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | - MPT2_APP_FLAGS_BUFFER_VALID); - else - karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | - MPT2_APP_FLAGS_BUFFER_VALID | - MPT2_APP_FLAGS_FW_BUFFER_ACCESS); - - for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) - karg.product_specific[i] = - ioc->product_specific[buffer_type][i]; - - karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type]; - karg.driver_added_buffer_size = 0; - karg.unique_id = ioc->unique_id[buffer_type]; - karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type]; - - if (copy_to_user(arg, &karg, sizeof(struct mpt2_diag_query))) { - printk(MPT2SAS_ERR_FMT "%s: unable to write mpt2_diag_query " - "data @ %p\n", ioc->name, __func__, arg); - return -EFAULT; - } - return 0; -} - -/** - * _ctl_send_release - Diag Release Message - * @ioc: per adapter object - * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED - * @issue_reset - specifies whether host reset is required. - * - */ -static int -_ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) -{ - Mpi2DiagReleaseRequest_t *mpi_request; - Mpi2DiagReleaseReply_t *mpi_reply; - u16 smid; - u16 ioc_status; - u32 ioc_state; - int rc; - unsigned long timeleft; - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - rc = 0; - *issue_reset = 0; - - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "skipping due to FAULT state\n", ioc->name, - __func__)); - rc = -EAGAIN; - goto out; - } - - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - ioc->ctl_cmds.status = MPT2_CMD_PENDING; - memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->ctl_cmds.smid = smid; - - mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; - mpi_request->BufferType = buffer_type; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - - init_completion(&ioc->ctl_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); - - if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, - __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2DiagReleaseRequest_t)/4); - if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) - *issue_reset = 1; - rc = -EFAULT; - goto out; - } - - /* process the completed Reply Message Frame */ - if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { - printk(MPT2SAS_ERR_FMT "%s: no reply message\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - - mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - ioc->diag_buffer_status[buffer_type] |= - MPT2_DIAG_BUFFER_IS_RELEASED; - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", - ioc->name, __func__)); - } else { - printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " - "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); - rc = -EFAULT; - } - - out: - ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - return rc; -} - -/** - * _ctl_diag_release - request to send Diag Release Message to firmware - * @arg - user space buffer containing ioctl content - * - * This allows ownership of the specified buffer to returned to the driver, - * allowing an application to read the buffer without fear that firmware is - * overwritting information in the buffer. - */ -static long -_ctl_diag_release(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_diag_release karg; - void *request_data; - int rc; - u8 buffer_type; - u8 issue_reset = 0; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - buffer_type = karg.unique_id & 0x000000ff; - if (!_ctl_diag_capability(ioc, buffer_type)) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -EPERM; - } - - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " - "registered\n", ioc->name, __func__, buffer_type); - return -EINVAL; - } - - if (karg.unique_id != ioc->unique_id[buffer_type]) { - printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " - "registered\n", ioc->name, __func__, karg.unique_id); - return -EINVAL; - } - - if (ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_RELEASED) { - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) " - "is already released\n", ioc->name, __func__, - buffer_type); - return 0; - } - - request_data = ioc->diag_buffer[buffer_type]; - - if (!request_data) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -ENOMEM; - } - - /* buffers were released by due to host reset */ - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_DIAG_RESET)) { - ioc->diag_buffer_status[buffer_type] |= - MPT2_DIAG_BUFFER_IS_RELEASED; - ioc->diag_buffer_status[buffer_type] &= - ~MPT2_DIAG_BUFFER_IS_DIAG_RESET; - printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) " - "was released due to host reset\n", ioc->name, __func__, - buffer_type); - return 0; - } - - rc = _ctl_send_release(ioc, buffer_type, &issue_reset); - - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - - return rc; -} - -/** - * _ctl_diag_read_buffer - request for copy of the diag buffer - * @ioc: per adapter object - * @arg - user space buffer containing ioctl content - */ -static long -_ctl_diag_read_buffer(struct MPT2SAS_ADAPTER *ioc, void __user *arg) -{ - struct mpt2_diag_read_buffer karg; - struct mpt2_diag_read_buffer __user *uarg = arg; - void *request_data, *diag_data; - Mpi2DiagBufferPostRequest_t *mpi_request; - Mpi2DiagBufferPostReply_t *mpi_reply; - int rc, i; - u8 buffer_type; - unsigned long timeleft, request_size, copy_size; - u16 smid; - u16 ioc_status; - u8 issue_reset = 0; - - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, - __func__)); - - buffer_type = karg.unique_id & 0x000000ff; - if (!_ctl_diag_capability(ioc, buffer_type)) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -EPERM; - } - - if (karg.unique_id != ioc->unique_id[buffer_type]) { - printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " - "registered\n", ioc->name, __func__, karg.unique_id); - return -EINVAL; - } - - request_data = ioc->diag_buffer[buffer_type]; - if (!request_data) { - printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); - return -ENOMEM; - } - - request_size = ioc->diag_buffer_sz[buffer_type]; - - if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { - printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " - "or bytes_to_read are not 4 byte aligned\n", ioc->name, - __func__); - return -EINVAL; - } - - if (karg.starting_offset > request_size) - return -EINVAL; - - diag_data = (void *)(request_data + karg.starting_offset); - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), " - "offset(%d), sz(%d)\n", ioc->name, __func__, - diag_data, karg.starting_offset, karg.bytes_to_read)); - - /* Truncate data on requests that are too large */ - if ((diag_data + karg.bytes_to_read < diag_data) || - (diag_data + karg.bytes_to_read > request_data + request_size)) - copy_size = request_size - karg.starting_offset; - else - copy_size = karg.bytes_to_read; - - if (copy_to_user((void __user *)uarg->diagnostic_data, - diag_data, copy_size)) { - printk(MPT2SAS_ERR_FMT "%s: Unable to write " - "mpt_diag_read_buffer_t data @ %p\n", ioc->name, - __func__, diag_data); - return -EFAULT; - } - - if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0) - return 0; - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: Reregister " - "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type)); - if ((ioc->diag_buffer_status[buffer_type] & - MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "buffer_type(0x%02x) is still registered\n", ioc->name, - __func__, buffer_type)); - return 0; - } - /* Get a free request frame and save the message context. - */ - - if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - rc = 0; - ioc->ctl_cmds.status = MPT2_CMD_PENDING; - memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->ctl_cmds.smid = smid; - - mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; - mpi_request->BufferType = buffer_type; - mpi_request->BufferLength = - cpu_to_le32(ioc->diag_buffer_sz[buffer_type]); - mpi_request->BufferAddress = - cpu_to_le64(ioc->diag_buffer_dma[buffer_type]); - for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) - mpi_request->ProductSpecific[i] = - cpu_to_le32(ioc->product_specific[buffer_type][i]); - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - - init_completion(&ioc->ctl_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, - MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); - - if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, - __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2DiagBufferPostRequest_t)/4); - if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - /* process the completed Reply Message Frame */ - if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { - printk(MPT2SAS_ERR_FMT "%s: no reply message\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - - mpi_reply = ioc->ctl_cmds.reply; - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; - - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - ioc->diag_buffer_status[buffer_type] |= - MPT2_DIAG_BUFFER_IS_REGISTERED; - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", - ioc->name, __func__)); - } else { - printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " - "log_info(0x%08x)\n", ioc->name, __func__, - ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); - rc = -EFAULT; - } - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - - out: - - ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; - return rc; -} - - -#ifdef CONFIG_COMPAT -/** - * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. - * @ioc: per adapter object - * @cmd - ioctl opcode - * @arg - (struct mpt2_ioctl_command32) - * - * MPT2COMMAND32 - Handle 32bit applications running on 64bit os. - */ -static long -_ctl_compat_mpt_command(struct MPT2SAS_ADAPTER *ioc, unsigned cmd, - void __user *arg) -{ - struct mpt2_ioctl_command32 karg32; - struct mpt2_ioctl_command32 __user *uarg; - struct mpt2_ioctl_command karg; - - if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32)) - return -EINVAL; - - uarg = (struct mpt2_ioctl_command32 __user *) arg; - - if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); - karg.hdr.ioc_number = karg32.hdr.ioc_number; - karg.hdr.port_number = karg32.hdr.port_number; - karg.hdr.max_data_size = karg32.hdr.max_data_size; - karg.timeout = karg32.timeout; - karg.max_reply_bytes = karg32.max_reply_bytes; - karg.data_in_size = karg32.data_in_size; - karg.data_out_size = karg32.data_out_size; - karg.max_sense_bytes = karg32.max_sense_bytes; - karg.data_sge_offset = karg32.data_sge_offset; - karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr); - karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr); - karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr); - karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr); - return _ctl_do_mpt_command(ioc, karg, &uarg->mf); -} -#endif - -/** - * _ctl_ioctl_main - main ioctl entry point - * @file - (struct file) - * @cmd - ioctl opcode - * @arg - - * compat - handles 32 bit applications in 64bit os - */ -static long -_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, - u8 compat) -{ - struct MPT2SAS_ADAPTER *ioc; - struct mpt2_ioctl_header ioctl_header; - enum block_state state; - long ret = -EINVAL; - - /* get IOCTL header */ - if (copy_from_user(&ioctl_header, (char __user *)arg, - sizeof(struct mpt2_ioctl_header))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - return -EFAULT; - } - - if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) - return -ENODEV; - if (ioc->shost_recovery || ioc->pci_error_recovery || - ioc->is_driver_loading) - return -EAGAIN; - - state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; - if (state == NON_BLOCKING) { - if (!mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { - return -ERESTARTSYS; - } - - switch (cmd) { - case MPT2IOCINFO: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo)) - ret = _ctl_getiocinfo(ioc, arg); - break; -#ifdef CONFIG_COMPAT - case MPT2COMMAND32: -#endif - case MPT2COMMAND: - { - struct mpt2_ioctl_command __user *uarg; - struct mpt2_ioctl_command karg; -#ifdef CONFIG_COMPAT - if (compat) { - ret = _ctl_compat_mpt_command(ioc, cmd, arg); - break; - } -#endif - if (copy_from_user(&karg, arg, sizeof(karg))) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", - __FILE__, __LINE__, __func__); - ret = -EFAULT; - break; - } - - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) { - uarg = arg; - ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); - } - break; - } - case MPT2EVENTQUERY: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery)) - ret = _ctl_eventquery(ioc, arg); - break; - case MPT2EVENTENABLE: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable)) - ret = _ctl_eventenable(ioc, arg); - break; - case MPT2EVENTREPORT: - ret = _ctl_eventreport(ioc, arg); - break; - case MPT2HARDRESET: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset)) - ret = _ctl_do_reset(ioc, arg); - break; - case MPT2BTDHMAPPING: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping)) - ret = _ctl_btdh_mapping(ioc, arg); - break; - case MPT2DIAGREGISTER: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register)) - ret = _ctl_diag_register(ioc, arg); - break; - case MPT2DIAGUNREGISTER: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister)) - ret = _ctl_diag_unregister(ioc, arg); - break; - case MPT2DIAGQUERY: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query)) - ret = _ctl_diag_query(ioc, arg); - break; - case MPT2DIAGRELEASE: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release)) - ret = _ctl_diag_release(ioc, arg); - break; - case MPT2DIAGREADBUFFER: - if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer)) - ret = _ctl_diag_read_buffer(ioc, arg); - break; - default: - - dctlprintk(ioc, printk(MPT2SAS_INFO_FMT - "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); - break; - } - - mutex_unlock(&ioc->ctl_cmds.mutex); - return ret; -} - -/** - * _ctl_ioctl - main ioctl entry point (unlocked) - * @file - (struct file) - * @cmd - ioctl opcode - * @arg - - */ -static long -_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - long ret; - - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0); - return ret; -} -#ifdef CONFIG_COMPAT -/** - * _ctl_ioctl_compat - main ioctl entry point (compat) - * @file - - * @cmd - - * @arg - - * - * This routine handles 32 bit applications in 64bit os. - */ -static long -_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) -{ - long ret; - - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1); - return ret; -} -#endif - -/* scsi host attributes */ - -/** - * _ctl_version_fw_show - firmware version - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", - (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, - (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, - (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, - ioc->facts.FWVersion.Word & 0x000000FF); -} -static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); - -/** - * _ctl_version_bios_show - bios version - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion); - - return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", - (version & 0xFF000000) >> 24, - (version & 0x00FF0000) >> 16, - (version & 0x0000FF00) >> 8, - version & 0x000000FF); -} -static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); - -/** - * _ctl_version_mpi_show - MPI (message passing interface) version - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", - ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); -} -static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); - -/** - * _ctl_version_product_show - product name - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_product_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); -} -static DEVICE_ATTR(version_product, S_IRUGO, - _ctl_version_product_show, NULL); - -/** - * _ctl_version_nvdata_persistent_show - ndvata persistent version - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_nvdata_persistent_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%08xh\n", - le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); -} -static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, - _ctl_version_nvdata_persistent_show, NULL); - -/** - * _ctl_version_nvdata_default_show - nvdata default version - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_version_nvdata_default_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%08xh\n", - le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); -} -static DEVICE_ATTR(version_nvdata_default, S_IRUGO, - _ctl_version_nvdata_default_show, NULL); - -/** - * _ctl_board_name_show - board name - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_board_name_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); -} -static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); - -/** - * _ctl_board_assembly_show - board assembly name - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); -} -static DEVICE_ATTR(board_assembly, S_IRUGO, - _ctl_board_assembly_show, NULL); - -/** - * _ctl_board_tracer_show - board tracer number - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); -} -static DEVICE_ATTR(board_tracer, S_IRUGO, - _ctl_board_tracer_show, NULL); - -/** - * _ctl_io_delay_show - io missing delay - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is for firmware implemention for deboucing device - * removal events. - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); -} -static DEVICE_ATTR(io_delay, S_IRUGO, - _ctl_io_delay_show, NULL); - -/** - * _ctl_device_delay_show - device missing delay - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is for firmware implemention for deboucing device - * removal events. - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); -} -static DEVICE_ATTR(device_delay, S_IRUGO, - _ctl_device_delay_show, NULL); - -/** - * _ctl_fw_queue_depth_show - global credits - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is firmware queue depth limit - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); -} -static DEVICE_ATTR(fw_queue_depth, S_IRUGO, - _ctl_fw_queue_depth_show, NULL); - -/** - * _ctl_sas_address_show - sas address - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is the controller sas address - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "0x%016llx\n", - (unsigned long long)ioc->sas_hba.sas_address); -} -static DEVICE_ATTR(host_sas_address, S_IRUGO, - _ctl_host_sas_address_show, NULL); - -/** - * _ctl_logging_level_show - logging level - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read/write' shost attribute. - */ -static ssize_t -_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); -} -static ssize_t -_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - int val = 0; - - if (sscanf(buf, "%x", &val) != 1) - return -EINVAL; - - ioc->logging_level = val; - printk(MPT2SAS_INFO_FMT "logging_level=%08xh\n", ioc->name, - ioc->logging_level); - return strlen(buf); -} -static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, - _ctl_logging_level_show, _ctl_logging_level_store); - -/* device attributes */ -/* - * _ctl_fwfault_debug_show - show/store fwfault_debug - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * mpt2sas_fwfault_debug is command line option - * A sysfs 'read/write' shost attribute. - */ -static ssize_t -_ctl_fwfault_debug_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); -} -static ssize_t -_ctl_fwfault_debug_store(struct device *cdev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - int val = 0; - - if (sscanf(buf, "%d", &val) != 1) - return -EINVAL; - - ioc->fwfault_debug = val; - printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name, - ioc->fwfault_debug); - return strlen(buf); -} -static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, - _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); - - -/** - * _ctl_ioc_reset_count_show - ioc reset count - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is firmware queue depth limit - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - return snprintf(buf, PAGE_SIZE, "%08d\n", ioc->ioc_reset_count); -} -static DEVICE_ATTR(ioc_reset_count, S_IRUGO, - _ctl_ioc_reset_count_show, NULL); - -/** - * _ctl_ioc_reply_queue_count_show - number of reply queues - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is number of reply queues - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_ioc_reply_queue_count_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - u8 reply_queue_count; - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - if ((ioc->facts.IOCCapabilities & - MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable) - reply_queue_count = ioc->reply_queue_count; - else - reply_queue_count = 1; - return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); -} -static DEVICE_ATTR(reply_queue_count, S_IRUGO, - _ctl_ioc_reply_queue_count_show, NULL); - -/** - * _ctl_BRM_status_show - Backup Rail Monitor Status - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is number of reply queues - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; - Mpi2ConfigReply_t mpi_reply; - u16 backup_rail_monitor_status = 0; - u16 ioc_status; - int sz; - ssize_t rc = 0; - - if (!ioc->is_warpdrive) { - printk(MPT2SAS_ERR_FMT "%s: BRM attribute is only for"\ - "warpdrive\n", ioc->name, __func__); - goto out; - } - - /* allocate upto GPIOVal 36 entries */ - sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); - io_unit_pg3 = kzalloc(sz, GFP_KERNEL); - if (!io_unit_pg3) { - printk(MPT2SAS_ERR_FMT "%s: failed allocating memory"\ - "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); - goto out; - } - - if (mpt2sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != - 0) { - printk(MPT2SAS_ERR_FMT - "%s: failed reading iounit_pg3\n", ioc->name, - __func__); - goto out; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "%s: iounit_pg3 failed with"\ - "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); - goto out; - } - - if (io_unit_pg3->GPIOCount < 25) { - printk(MPT2SAS_ERR_FMT "%s: iounit_pg3->GPIOCount less than"\ - "25 entries, detected (%d) entries\n", ioc->name, __func__, - io_unit_pg3->GPIOCount); - goto out; - } - - /* BRM status is in bit zero of GPIOVal[24] */ - backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); - rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); - - out: - kfree(io_unit_pg3); - return rc; -} -static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); - -struct DIAG_BUFFER_START { - __le32 Size; - __le32 DiagVersion; - u8 BufferType; - u8 Reserved[3]; - __le32 Reserved1; - __le32 Reserved2; - __le32 Reserved3; -}; -/** - * _ctl_host_trace_buffer_size_show - host buffer size (trace only) - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_host_trace_buffer_size_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - u32 size = 0; - struct DIAG_BUFFER_START *request_data; - - if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { - printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " - "registered\n", ioc->name, __func__); - return 0; - } - - if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " - "registered\n", ioc->name, __func__); - return 0; - } - - request_data = (struct DIAG_BUFFER_START *) - ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]; - if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || - le32_to_cpu(request_data->DiagVersion) == 0x01000000) && - le32_to_cpu(request_data->Reserved3) == 0x4742444c) - size = le32_to_cpu(request_data->Size); - - ioc->ring_buffer_sz = size; - return snprintf(buf, PAGE_SIZE, "%d\n", size); -} -static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, - _ctl_host_trace_buffer_size_show, NULL); - -/** - * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read/write' shost attribute. - * - * You will only be able to read 4k bytes of ring buffer at a time. - * In order to read beyond 4k bytes, you will have to write out the - * offset to the same attribute, it will move the pointer. - */ -static ssize_t -_ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, - char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - void *request_data; - u32 size; - - if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { - printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " - "registered\n", ioc->name, __func__); - return 0; - } - - if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { - printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " - "registered\n", ioc->name, __func__); - return 0; - } - - if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) - return 0; - - size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; - size = (size > PAGE_SIZE) ? PAGE_SIZE : size; - request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset; - memcpy(buf, request_data, size); - return size; -} - -static ssize_t -_ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - int val = 0; - - if (sscanf(buf, "%d", &val) != 1) - return -EINVAL; - - ioc->ring_buffer_offset = val; - return strlen(buf); -} -static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, - _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); - -/*****************************************/ - -/** - * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * A sysfs 'read/write' shost attribute. - * - * This is a mechnism to post/release host_trace_buffers - */ -static ssize_t -_ctl_host_trace_buffer_enable_show(struct device *cdev, - struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) || - ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0)) - return snprintf(buf, PAGE_SIZE, "off\n"); - else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_RELEASED)) - return snprintf(buf, PAGE_SIZE, "release\n"); - else - return snprintf(buf, PAGE_SIZE, "post\n"); -} - -static ssize_t -_ctl_host_trace_buffer_enable_store(struct device *cdev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct Scsi_Host *shost = class_to_shost(cdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - char str[10] = ""; - struct mpt2_diag_register diag_register; - u8 issue_reset = 0; - - if (sscanf(buf, "%9s", str) != 1) - return -EINVAL; - - if (!strcmp(str, "post")) { - /* exit out if host buffers are already posted */ - if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) && - (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_REGISTERED) && - ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_RELEASED) == 0)) - goto out; - memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); - printk(MPT2SAS_INFO_FMT "posting host trace buffers\n", - ioc->name); - diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; - diag_register.requested_buffer_size = (1024 * 1024); - diag_register.unique_id = 0x7075900; - ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; - _ctl_diag_register_2(ioc, &diag_register); - } else if (!strcmp(str, "release")) { - /* exit out if host buffers are already released */ - if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) - goto out; - if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) - goto out; - if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & - MPT2_DIAG_BUFFER_IS_RELEASED)) - goto out; - printk(MPT2SAS_INFO_FMT "releasing host trace buffer\n", - ioc->name); - _ctl_send_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset); - } - - out: - return strlen(buf); -} -static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, - _ctl_host_trace_buffer_enable_show, _ctl_host_trace_buffer_enable_store); - -struct device_attribute *mpt2sas_host_attrs[] = { - &dev_attr_version_fw, - &dev_attr_version_bios, - &dev_attr_version_mpi, - &dev_attr_version_product, - &dev_attr_version_nvdata_persistent, - &dev_attr_version_nvdata_default, - &dev_attr_board_name, - &dev_attr_board_assembly, - &dev_attr_board_tracer, - &dev_attr_io_delay, - &dev_attr_device_delay, - &dev_attr_logging_level, - &dev_attr_fwfault_debug, - &dev_attr_fw_queue_depth, - &dev_attr_host_sas_address, - &dev_attr_ioc_reset_count, - &dev_attr_host_trace_buffer_size, - &dev_attr_host_trace_buffer, - &dev_attr_host_trace_buffer_enable, - &dev_attr_reply_queue_count, - &dev_attr_BRM_status, - NULL, -}; - -/** - * _ctl_device_sas_address_show - sas address - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is the sas address for the target - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; - - return snprintf(buf, PAGE_SIZE, "0x%016llx\n", - (unsigned long long)sas_device_priv_data->sas_target->sas_address); -} -static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); - -/** - * _ctl_device_handle_show - device handle - * @cdev - pointer to embedded class device - * @buf - the buffer returned - * - * This is the firmware assigned device handle - * - * A sysfs 'read-only' shost attribute. - */ -static ssize_t -_ctl_device_handle_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; - - return snprintf(buf, PAGE_SIZE, "0x%04x\n", - sas_device_priv_data->sas_target->handle); -} -static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); - -struct device_attribute *mpt2sas_dev_attrs[] = { - &dev_attr_sas_address, - &dev_attr_sas_device_handle, - NULL, -}; - -static const struct file_operations ctl_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = _ctl_ioctl, - .poll = _ctl_poll, - .fasync = _ctl_fasync, -#ifdef CONFIG_COMPAT - .compat_ioctl = _ctl_ioctl_compat, -#endif - .llseek = noop_llseek, -}; - -static struct miscdevice ctl_dev = { - .minor = MPT2SAS_MINOR, - .name = MPT2SAS_DEV_NAME, - .fops = &ctl_fops, -}; - -/** - * mpt2sas_ctl_init - main entry point for ctl. - * - */ -void -mpt2sas_ctl_init(void) -{ - async_queue = NULL; - if (misc_register(&ctl_dev) < 0) - printk(KERN_ERR "%s can't register misc device [minor=%d]\n", - MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); - - init_waitqueue_head(&ctl_poll_wait); -} - -/** - * mpt2sas_ctl_exit - exit point for ctl - * - */ -void -mpt2sas_ctl_exit(void) -{ - struct MPT2SAS_ADAPTER *ioc; - int i; - - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { - - /* free memory associated to diag buffers */ - for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { - if (!ioc->diag_buffer[i]) - continue; - pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i], - ioc->diag_buffer[i], ioc->diag_buffer_dma[i]); - ioc->diag_buffer[i] = NULL; - ioc->diag_buffer_status[i] = 0; - } - - kfree(ioc->event_log); - } - misc_deregister(&ctl_dev); -} - diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.h deleted file mode 100644 index 46b2fc5b7..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Management Module Support for MPT (Message Passing Technology) based - * controllers - * - * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#ifndef MPT2SAS_CTL_H_INCLUDED -#define MPT2SAS_CTL_H_INCLUDED - -#ifdef __KERNEL__ -#include -#endif - -#define MPT2SAS_DEV_NAME "mpt2ctl" -#define MPT2_MAGIC_NUMBER 'L' -#define MPT2_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ - -/** - * IOCTL opcodes - */ -#define MPT2IOCINFO _IOWR(MPT2_MAGIC_NUMBER, 17, \ - struct mpt2_ioctl_iocinfo) -#define MPT2COMMAND _IOWR(MPT2_MAGIC_NUMBER, 20, \ - struct mpt2_ioctl_command) -#ifdef CONFIG_COMPAT -#define MPT2COMMAND32 _IOWR(MPT2_MAGIC_NUMBER, 20, \ - struct mpt2_ioctl_command32) -#endif -#define MPT2EVENTQUERY _IOWR(MPT2_MAGIC_NUMBER, 21, \ - struct mpt2_ioctl_eventquery) -#define MPT2EVENTENABLE _IOWR(MPT2_MAGIC_NUMBER, 22, \ - struct mpt2_ioctl_eventenable) -#define MPT2EVENTREPORT _IOWR(MPT2_MAGIC_NUMBER, 23, \ - struct mpt2_ioctl_eventreport) -#define MPT2HARDRESET _IOWR(MPT2_MAGIC_NUMBER, 24, \ - struct mpt2_ioctl_diag_reset) -#define MPT2BTDHMAPPING _IOWR(MPT2_MAGIC_NUMBER, 31, \ - struct mpt2_ioctl_btdh_mapping) - -/* diag buffer support */ -#define MPT2DIAGREGISTER _IOWR(MPT2_MAGIC_NUMBER, 26, \ - struct mpt2_diag_register) -#define MPT2DIAGRELEASE _IOWR(MPT2_MAGIC_NUMBER, 27, \ - struct mpt2_diag_release) -#define MPT2DIAGUNREGISTER _IOWR(MPT2_MAGIC_NUMBER, 28, \ - struct mpt2_diag_unregister) -#define MPT2DIAGQUERY _IOWR(MPT2_MAGIC_NUMBER, 29, \ - struct mpt2_diag_query) -#define MPT2DIAGREADBUFFER _IOWR(MPT2_MAGIC_NUMBER, 30, \ - struct mpt2_diag_read_buffer) - -/** - * struct mpt2_ioctl_header - main header structure - * @ioc_number - IOC unit number - * @port_number - IOC port number - * @max_data_size - maximum number bytes to transfer on read - */ -struct mpt2_ioctl_header { - uint32_t ioc_number; - uint32_t port_number; - uint32_t max_data_size; -}; - -/** - * struct mpt2_ioctl_diag_reset - diagnostic reset - * @hdr - generic header - */ -struct mpt2_ioctl_diag_reset { - struct mpt2_ioctl_header hdr; -}; - - -/** - * struct mpt2_ioctl_pci_info - pci device info - * @device - pci device id - * @function - pci function id - * @bus - pci bus id - * @segment_id - pci segment id - */ -struct mpt2_ioctl_pci_info { - union { - struct { - uint32_t device:5; - uint32_t function:3; - uint32_t bus:24; - } bits; - uint32_t word; - } u; - uint32_t segment_id; -}; - - -#define MPT2_IOCTL_INTERFACE_SCSI (0x00) -#define MPT2_IOCTL_INTERFACE_FC (0x01) -#define MPT2_IOCTL_INTERFACE_FC_IP (0x02) -#define MPT2_IOCTL_INTERFACE_SAS (0x03) -#define MPT2_IOCTL_INTERFACE_SAS2 (0x04) -#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05) -#define MPT2_IOCTL_VERSION_LENGTH (32) - -/** - * struct mpt2_ioctl_iocinfo - generic controller info - * @hdr - generic header - * @adapter_type - type of adapter (spi, fc, sas) - * @port_number - port number - * @pci_id - PCI Id - * @hw_rev - hardware revision - * @sub_system_device - PCI subsystem Device ID - * @sub_system_vendor - PCI subsystem Vendor ID - * @rsvd0 - reserved - * @firmware_version - firmware version - * @bios_version - BIOS version - * @driver_version - driver version - 32 ASCII characters - * @rsvd1 - reserved - * @scsi_id - scsi id of adapter 0 - * @rsvd2 - reserved - * @pci_information - pci info (2nd revision) - */ -struct mpt2_ioctl_iocinfo { - struct mpt2_ioctl_header hdr; - uint32_t adapter_type; - uint32_t port_number; - uint32_t pci_id; - uint32_t hw_rev; - uint32_t subsystem_device; - uint32_t subsystem_vendor; - uint32_t rsvd0; - uint32_t firmware_version; - uint32_t bios_version; - uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH]; - uint8_t rsvd1; - uint8_t scsi_id; - uint16_t rsvd2; - struct mpt2_ioctl_pci_info pci_information; -}; - - -/* number of event log entries */ -#define MPT2SAS_CTL_EVENT_LOG_SIZE (50) - -/** - * struct mpt2_ioctl_eventquery - query event count and type - * @hdr - generic header - * @event_entries - number of events returned by get_event_report - * @rsvd - reserved - * @event_types - type of events currently being captured - */ -struct mpt2_ioctl_eventquery { - struct mpt2_ioctl_header hdr; - uint16_t event_entries; - uint16_t rsvd; - uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; -}; - -/** - * struct mpt2_ioctl_eventenable - enable/disable event capturing - * @hdr - generic header - * @event_types - toggle off/on type of events to be captured - */ -struct mpt2_ioctl_eventenable { - struct mpt2_ioctl_header hdr; - uint32_t event_types[4]; -}; - -#define MPT2_EVENT_DATA_SIZE (192) -/** - * struct MPT2_IOCTL_EVENTS - - * @event - the event that was reported - * @context - unique value for each event assigned by driver - * @data - event data returned in fw reply message - */ -struct MPT2_IOCTL_EVENTS { - uint32_t event; - uint32_t context; - uint8_t data[MPT2_EVENT_DATA_SIZE]; -}; - -/** - * struct mpt2_ioctl_eventreport - returing event log - * @hdr - generic header - * @event_data - (see struct MPT2_IOCTL_EVENTS) - */ -struct mpt2_ioctl_eventreport { - struct mpt2_ioctl_header hdr; - struct MPT2_IOCTL_EVENTS event_data[1]; -}; - -/** - * struct mpt2_ioctl_command - generic mpt firmware passthru ioctl - * @hdr - generic header - * @timeout - command timeout in seconds. (if zero then use driver default - * value). - * @reply_frame_buf_ptr - reply location - * @data_in_buf_ptr - destination for read - * @data_out_buf_ptr - data source for write - * @sense_data_ptr - sense data location - * @max_reply_bytes - maximum number of reply bytes to be sent to app. - * @data_in_size - number bytes for data transfer in (read) - * @data_out_size - number bytes for data transfer out (write) - * @max_sense_bytes - maximum number of bytes for auto sense buffers - * @data_sge_offset - offset in words from the start of the request message to - * the first SGL - * @mf[1]; - */ -struct mpt2_ioctl_command { - struct mpt2_ioctl_header hdr; - uint32_t timeout; - void __user *reply_frame_buf_ptr; - void __user *data_in_buf_ptr; - void __user *data_out_buf_ptr; - void __user *sense_data_ptr; - uint32_t max_reply_bytes; - uint32_t data_in_size; - uint32_t data_out_size; - uint32_t max_sense_bytes; - uint32_t data_sge_offset; - uint8_t mf[1]; -}; - -#ifdef CONFIG_COMPAT -struct mpt2_ioctl_command32 { - struct mpt2_ioctl_header hdr; - uint32_t timeout; - uint32_t reply_frame_buf_ptr; - uint32_t data_in_buf_ptr; - uint32_t data_out_buf_ptr; - uint32_t sense_data_ptr; - uint32_t max_reply_bytes; - uint32_t data_in_size; - uint32_t data_out_size; - uint32_t max_sense_bytes; - uint32_t data_sge_offset; - uint8_t mf[1]; -}; -#endif - -/** - * struct mpt2_ioctl_btdh_mapping - mapping info - * @hdr - generic header - * @id - target device identification number - * @bus - SCSI bus number that the target device exists on - * @handle - device handle for the target device - * @rsvd - reserved - * - * To obtain a bus/id the application sets - * handle to valid handle, and bus/id to 0xFFFF. - * - * To obtain the device handle the application sets - * bus/id valid value, and the handle to 0xFFFF. - */ -struct mpt2_ioctl_btdh_mapping { - struct mpt2_ioctl_header hdr; - uint32_t id; - uint32_t bus; - uint16_t handle; - uint16_t rsvd; -}; - - -/* status bits for ioc->diag_buffer_status */ -#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01) -#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02) -#define MPT2_DIAG_BUFFER_IS_DIAG_RESET (0x04) - -/* application flags for mpt2_diag_register, mpt2_diag_query */ -#define MPT2_APP_FLAGS_APP_OWNED (0x0001) -#define MPT2_APP_FLAGS_BUFFER_VALID (0x0002) -#define MPT2_APP_FLAGS_FW_BUFFER_ACCESS (0x0004) - -/* flags for mpt2_diag_read_buffer */ -#define MPT2_FLAGS_REREGISTER (0x0001) - -#define MPT2_PRODUCT_SPECIFIC_DWORDS 23 - -/** - * struct mpt2_diag_register - application register with driver - * @hdr - generic header - * @reserved - - * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED - * @application_flags - misc flags - * @diagnostic_flags - specifies flags affecting command processing - * @product_specific - product specific information - * @requested_buffer_size - buffers size in bytes - * @unique_id - tag specified by application that is used to signal ownership - * of the buffer. - * - * This will allow the driver to setup any required buffers that will be - * needed by firmware to communicate with the driver. - */ -struct mpt2_diag_register { - struct mpt2_ioctl_header hdr; - uint8_t reserved; - uint8_t buffer_type; - uint16_t application_flags; - uint32_t diagnostic_flags; - uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS]; - uint32_t requested_buffer_size; - uint32_t unique_id; -}; - -/** - * struct mpt2_diag_unregister - application unregister with driver - * @hdr - generic header - * @unique_id - tag uniquely identifies the buffer to be unregistered - * - * This will allow the driver to cleanup any memory allocated for diag - * messages and to free up any resources. - */ -struct mpt2_diag_unregister { - struct mpt2_ioctl_header hdr; - uint32_t unique_id; -}; - -/** - * struct mpt2_diag_query - query relevant info associated with diag buffers - * @hdr - generic header - * @reserved - - * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED - * @application_flags - misc flags - * @diagnostic_flags - specifies flags affecting command processing - * @product_specific - product specific information - * @total_buffer_size - diag buffer size in bytes - * @driver_added_buffer_size - size of extra space appended to end of buffer - * @unique_id - unique id associated with this buffer. - * - * The application will send only buffer_type and unique_id. Driver will - * inspect unique_id first, if valid, fill in all the info. If unique_id is - * 0x00, the driver will return info specified by Buffer Type. - */ -struct mpt2_diag_query { - struct mpt2_ioctl_header hdr; - uint8_t reserved; - uint8_t buffer_type; - uint16_t application_flags; - uint32_t diagnostic_flags; - uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS]; - uint32_t total_buffer_size; - uint32_t driver_added_buffer_size; - uint32_t unique_id; -}; - -/** - * struct mpt2_diag_release - request to send Diag Release Message to firmware - * @hdr - generic header - * @unique_id - tag uniquely identifies the buffer to be released - * - * This allows ownership of the specified buffer to returned to the driver, - * allowing an application to read the buffer without fear that firmware is - * overwritting information in the buffer. - */ -struct mpt2_diag_release { - struct mpt2_ioctl_header hdr; - uint32_t unique_id; -}; - -/** - * struct mpt2_diag_read_buffer - request for copy of the diag buffer - * @hdr - generic header - * @status - - * @reserved - - * @flags - misc flags - * @starting_offset - starting offset within drivers buffer where to start - * reading data at into the specified application buffer - * @bytes_to_read - number of bytes to copy from the drivers buffer into the - * application buffer starting at starting_offset. - * @unique_id - unique id associated with this buffer. - * @diagnostic_data - data payload - */ -struct mpt2_diag_read_buffer { - struct mpt2_ioctl_header hdr; - uint8_t status; - uint8_t reserved; - uint16_t flags; - uint32_t starting_offset; - uint32_t bytes_to_read; - uint32_t unique_id; - uint32_t diagnostic_data[1]; -}; - -#endif /* MPT2SAS_CTL_H_INCLUDED */ diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_debug.h b/kernel/drivers/scsi/mpt2sas/mpt2sas_debug.h deleted file mode 100644 index 277120d45..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Logging Support for MPT (Message Passing Technology) based controllers - * - * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#ifndef MPT2SAS_DEBUG_H_INCLUDED -#define MPT2SAS_DEBUG_H_INCLUDED - -#define MPT_DEBUG 0x00000001 -#define MPT_DEBUG_MSG_FRAME 0x00000002 -#define MPT_DEBUG_SG 0x00000004 -#define MPT_DEBUG_EVENTS 0x00000008 -#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010 -#define MPT_DEBUG_INIT 0x00000020 -#define MPT_DEBUG_EXIT 0x00000040 -#define MPT_DEBUG_FAIL 0x00000080 -#define MPT_DEBUG_TM 0x00000100 -#define MPT_DEBUG_REPLY 0x00000200 -#define MPT_DEBUG_HANDSHAKE 0x00000400 -#define MPT_DEBUG_CONFIG 0x00000800 -#define MPT_DEBUG_DL 0x00001000 -#define MPT_DEBUG_RESET 0x00002000 -#define MPT_DEBUG_SCSI 0x00004000 -#define MPT_DEBUG_IOCTL 0x00008000 -#define MPT_DEBUG_CSMISAS 0x00010000 -#define MPT_DEBUG_SAS 0x00020000 -#define MPT_DEBUG_TRANSPORT 0x00040000 -#define MPT_DEBUG_TASK_SET_FULL 0x00080000 - -#define MPT_DEBUG_TARGET_MODE 0x00100000 - - -/* - * CONFIG_SCSI_MPT2SAS_LOGGING - enabled in Kconfig - */ - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \ -{ \ - if (IOC->logging_level & BITS) \ - CMD; \ -} -#else -#define MPT_CHECK_LOGGING(IOC, CMD, BITS) -#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */ - - -/* - * debug macros - */ - -#define dprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG) - -#define dsgprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG) - -#define devtprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS) - -#define dewtprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK) - -#define dinitprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT) - -#define dexitprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT) - -#define dfailprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL) - -#define dtmprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM) - -#define dreplyprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY) - -#define dhsprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE) - -#define dcprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG) - -#define ddlprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL) - -#define drsprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET) - -#define dsprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI) - -#define dctlprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL) - -#define dcsmisasprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CSMISAS) - -#define dsasprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS) - -#define dsastransport(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE) - -#define dmfprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME) - -#define dtsfprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL) - -#define dtransportprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT) - -#define dTMprintk(IOC, CMD) \ - MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TARGET_MODE) - -/* inline functions for dumping debug data*/ -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _debug_dump_mf - print message frame contents - * @mpi_request: pointer to message frame - * @sz: number of dwords - */ -static inline void -_debug_dump_mf(void *mpi_request, int sz) -{ - int i; - __le32 *mfp = (__le32 *)mpi_request; - - printk(KERN_INFO "mf:\n\t"); - for (i = 0; i < sz; i++) { - if (i && ((i % 8) == 0)) - printk("\n\t"); - printk("%08x ", le32_to_cpu(mfp[i])); - } - printk("\n"); -} -#else -#define _debug_dump_mf(mpi_request, sz) -#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */ - -#endif /* MPT2SAS_DEBUG_H_INCLUDED */ diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/kernel/drivers/scsi/mpt2sas/mpt2sas_scsih.c deleted file mode 100644 index 3f26147bb..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ /dev/null @@ -1,8592 +0,0 @@ -/* - * Scsi Host Layer for MPT (Message Passing Technology) based controllers - * - * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mpt2sas_base.h" - -MODULE_AUTHOR(MPT2SAS_AUTHOR); -MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MPT2SAS_DRIVER_VERSION); - -#define RAID_CHANNEL 1 - -/* forward proto's */ -static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_expander); -static void _firmware_event_work(struct work_struct *work); - -static u8 _scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid); - -static void _scsih_scan_start(struct Scsi_Host *shost); -static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time); - -/* global parameters */ -LIST_HEAD(mpt2sas_ioc_list); - -/* local parameters */ -static u8 scsi_io_cb_idx = -1; -static u8 tm_cb_idx = -1; -static u8 ctl_cb_idx = -1; -static u8 base_cb_idx = -1; -static u8 port_enable_cb_idx = -1; -static u8 transport_cb_idx = -1; -static u8 scsih_cb_idx = -1; -static u8 config_cb_idx = -1; -static int mpt_ids; - -static u8 tm_tr_cb_idx = -1 ; -static u8 tm_tr_volume_cb_idx = -1 ; -static u8 tm_sas_control_cb_idx = -1; - -/* command line options */ -static u32 logging_level; -MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info " - "(default=0)"); - -static ushort max_sectors = 0xFFFF; -module_param(max_sectors, ushort, 0); -MODULE_PARM_DESC(max_sectors, "max sectors, range 64 to 32767 default=32767"); - -static int missing_delay[2] = {-1, -1}; -module_param_array(missing_delay, int, NULL, 0); -MODULE_PARM_DESC(missing_delay, " device missing delay , io missing delay"); - -/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ -#define MPT2SAS_MAX_LUN (16895) -static int max_lun = MPT2SAS_MAX_LUN; -module_param(max_lun, int, 0); -MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); - -/* diag_buffer_enable is bitwise - * bit 0 set = TRACE - * bit 1 set = SNAPSHOT - * bit 2 set = EXTENDED - * - * Either bit can be set, or both - */ -static int diag_buffer_enable = -1; -module_param(diag_buffer_enable, int, 0); -MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers " - "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); - -static int disable_discovery = -1; -module_param(disable_discovery, int, 0); -MODULE_PARM_DESC(disable_discovery, " disable discovery "); - -/* permit overriding the host protection capabilities mask (EEDP/T10 PI) */ -static int prot_mask = 0; -module_param(prot_mask, int, 0); -MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); - -/** - * struct sense_info - common structure for obtaining sense keys - * @skey: sense key - * @asc: additional sense code - * @ascq: additional sense code qualifier - */ -struct sense_info { - u8 skey; - u8 asc; - u8 ascq; -}; - - -#define MPT2SAS_TURN_ON_PFA_LED (0xFFFC) -#define MPT2SAS_PORT_ENABLE_COMPLETE (0xFFFD) -#define MPT2SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF) -/** - * struct fw_event_work - firmware event struct - * @list: link list framework - * @work: work object (ioc->fault_reset_work_q) - * @cancel_pending_work: flag set during reset handling - * @ioc: per adapter object - * @device_handle: device handle - * @VF_ID: virtual function id - * @VP_ID: virtual port id - * @ignore: flag meaning this event has been marked to ignore - * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h - * @event_data: reply event data payload follows - * - * This object stored on ioc->fw_event_list. - */ -struct fw_event_work { - struct list_head list; - u8 cancel_pending_work; - struct delayed_work delayed_work; - struct MPT2SAS_ADAPTER *ioc; - u16 device_handle; - u8 VF_ID; - u8 VP_ID; - u8 ignore; - u16 event; - char event_data[0] __aligned(4); -}; - -/* raid transport support */ -static struct raid_template *mpt2sas_raid_template; - -/** - * struct _scsi_io_transfer - scsi io transfer - * @handle: sas device handle (assigned by firmware) - * @is_raid: flag set for hidden raid components - * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE, - * @data_length: data transfer length - * @data_dma: dma pointer to data - * @sense: sense data - * @lun: lun number - * @cdb_length: cdb length - * @cdb: cdb contents - * @timeout: timeout for this command - * @VF_ID: virtual function id - * @VP_ID: virtual port id - * @valid_reply: flag set for reply message - * @sense_length: sense length - * @ioc_status: ioc status - * @scsi_state: scsi state - * @scsi_status: scsi staus - * @log_info: log information - * @transfer_length: data length transfer when there is a reply message - * - * Used for sending internal scsi commands to devices within this module. - * Refer to _scsi_send_scsi_io(). - */ -struct _scsi_io_transfer { - u16 handle; - u8 is_raid; - enum dma_data_direction dir; - u32 data_length; - dma_addr_t data_dma; - u8 sense[SCSI_SENSE_BUFFERSIZE]; - u32 lun; - u8 cdb_length; - u8 cdb[32]; - u8 timeout; - u8 VF_ID; - u8 VP_ID; - u8 valid_reply; - /* the following bits are only valid when 'valid_reply = 1' */ - u32 sense_length; - u16 ioc_status; - u8 scsi_state; - u8 scsi_status; - u32 log_info; - u32 transfer_length; -}; - -/* - * The pci device ids are defined in mpi/mpi2_cnfg.h. - */ -static struct pci_device_id scsih_pci_table[] = { - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, - PCI_ANY_ID, PCI_ANY_ID }, - /* Falcon ~ 2008*/ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, - PCI_ANY_ID, PCI_ANY_ID }, - /* Liberator ~ 2108 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, - PCI_ANY_ID, PCI_ANY_ID }, - /* Meteor ~ 2116 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, - PCI_ANY_ID, PCI_ANY_ID }, - /* Thunderbolt ~ 2208 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, - PCI_ANY_ID, PCI_ANY_ID }, - /* Mustang ~ 2308 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, - PCI_ANY_ID, PCI_ANY_ID }, - /* SSS6200 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, - PCI_ANY_ID, PCI_ANY_ID }, - {0} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(pci, scsih_pci_table); - -/** - * _scsih_set_debug_level - global setting of ioc->logging_level. - * - * Note: The logging levels are defined in mpt2sas_debug.h. - */ -static int -_scsih_set_debug_level(const char *val, struct kernel_param *kp) -{ - int ret = param_set_int(val, kp); - struct MPT2SAS_ADAPTER *ioc; - - if (ret) - return ret; - - printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level); - list_for_each_entry(ioc, &mpt2sas_ioc_list, list) - ioc->logging_level = logging_level; - return 0; -} -module_param_call(logging_level, _scsih_set_debug_level, param_get_int, - &logging_level, 0644); - -/** - * _scsih_srch_boot_sas_address - search based on sas_address - * @sas_address: sas address - * @boot_device: boot device object from bios page 2 - * - * Returns 1 when there's a match, 0 means no match. - */ -static inline int -_scsih_srch_boot_sas_address(u64 sas_address, - Mpi2BootDeviceSasWwid_t *boot_device) -{ - return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0; -} - -/** - * _scsih_srch_boot_device_name - search based on device name - * @device_name: device name specified in INDENTIFY fram - * @boot_device: boot device object from bios page 2 - * - * Returns 1 when there's a match, 0 means no match. - */ -static inline int -_scsih_srch_boot_device_name(u64 device_name, - Mpi2BootDeviceDeviceName_t *boot_device) -{ - return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0; -} - -/** - * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot - * @enclosure_logical_id: enclosure logical id - * @slot_number: slot number - * @boot_device: boot device object from bios page 2 - * - * Returns 1 when there's a match, 0 means no match. - */ -static inline int -_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number, - Mpi2BootDeviceEnclosureSlot_t *boot_device) -{ - return (enclosure_logical_id == le64_to_cpu(boot_device-> - EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device-> - SlotNumber)) ? 1 : 0; -} - -/** - * _scsih_is_boot_device - search for matching boot device. - * @sas_address: sas address - * @device_name: device name specified in INDENTIFY fram - * @enclosure_logical_id: enclosure logical id - * @slot_number: slot number - * @form: specifies boot device form - * @boot_device: boot device object from bios page 2 - * - * Returns 1 when there's a match, 0 means no match. - */ -static int -_scsih_is_boot_device(u64 sas_address, u64 device_name, - u64 enclosure_logical_id, u16 slot, u8 form, - Mpi2BiosPage2BootDevice_t *boot_device) -{ - int rc = 0; - - switch (form) { - case MPI2_BIOSPAGE2_FORM_SAS_WWID: - if (!sas_address) - break; - rc = _scsih_srch_boot_sas_address( - sas_address, &boot_device->SasWwid); - break; - case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT: - if (!enclosure_logical_id) - break; - rc = _scsih_srch_boot_encl_slot( - enclosure_logical_id, - slot, &boot_device->EnclosureSlot); - break; - case MPI2_BIOSPAGE2_FORM_DEVICE_NAME: - if (!device_name) - break; - rc = _scsih_srch_boot_device_name( - device_name, &boot_device->DeviceName); - break; - case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED: - break; - } - - return rc; -} - -/** - * _scsih_get_sas_address - set the sas_address for given device handle - * @handle: device handle - * @sas_address: sas address - * - * Returns 0 success, non-zero when failure - */ -static int -_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle, - u64 *sas_address) -{ - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2ConfigReply_t mpi_reply; - u32 ioc_status; - *sas_address = 0; - - if (handle <= ioc->sas_hba.num_phys) { - *sas_address = ioc->sas_hba.sas_address; - return 0; - } - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - return -ENXIO; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { - *sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - return 0; - } - - /* we hit this becuase the given parent handle doesn't exist */ - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) - return -ENXIO; - /* else error case */ - printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x), " - "failure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, - __FILE__, __LINE__, __func__); - return -EIO; -} - -/** - * _scsih_determine_boot_device - determine boot device. - * @ioc: per adapter object - * @device: either sas_device or raid_device object - * @is_raid: [flag] 1 = raid object, 0 = sas object - * - * Determines whether this device should be first reported device to - * to scsi-ml or sas transport, this purpose is for persistent boot device. - * There are primary, alternate, and current entries in bios page 2. The order - * priority is primary, alternate, then current. This routine saves - * the corresponding device object and is_raid flag in the ioc object. - * The saved data to be used later in _scsih_probe_boot_devices(). - */ -static void -_scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, - void *device, u8 is_raid) -{ - struct _sas_device *sas_device; - struct _raid_device *raid_device; - u64 sas_address; - u64 device_name; - u64 enclosure_logical_id; - u16 slot; - - /* only process this function when driver loads */ - if (!ioc->is_driver_loading) - return; - - /* no Bios, return immediately */ - if (!ioc->bios_pg3.BiosVersion) - return; - - if (!is_raid) { - sas_device = device; - sas_address = sas_device->sas_address; - device_name = sas_device->device_name; - enclosure_logical_id = sas_device->enclosure_logical_id; - slot = sas_device->slot; - } else { - raid_device = device; - sas_address = raid_device->wwid; - device_name = 0; - enclosure_logical_id = 0; - slot = 0; - } - - if (!ioc->req_boot_device.device) { - if (_scsih_is_boot_device(sas_address, device_name, - enclosure_logical_id, slot, - (ioc->bios_pg2.ReqBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK), - &ioc->bios_pg2.RequestedBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s: req_boot_device(0x%016llx)\n", - ioc->name, __func__, - (unsigned long long)sas_address)); - ioc->req_boot_device.device = device; - ioc->req_boot_device.is_raid = is_raid; - } - } - - if (!ioc->req_alt_boot_device.device) { - if (_scsih_is_boot_device(sas_address, device_name, - enclosure_logical_id, slot, - (ioc->bios_pg2.ReqAltBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK), - &ioc->bios_pg2.RequestedAltBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s: req_alt_boot_device(0x%016llx)\n", - ioc->name, __func__, - (unsigned long long)sas_address)); - ioc->req_alt_boot_device.device = device; - ioc->req_alt_boot_device.is_raid = is_raid; - } - } - - if (!ioc->current_boot_device.device) { - if (_scsih_is_boot_device(sas_address, device_name, - enclosure_logical_id, slot, - (ioc->bios_pg2.CurrentBootDeviceForm & - MPI2_BIOSPAGE2_FORM_MASK), - &ioc->bios_pg2.CurrentBootDevice)) { - dinitprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s: current_boot_device(0x%016llx)\n", - ioc->name, __func__, - (unsigned long long)sas_address)); - ioc->current_boot_device.device = device; - ioc->current_boot_device.is_raid = is_raid; - } - } -} - -/** - * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search - * @ioc: per adapter object - * @sas_address: sas address - * Context: Calling function should acquire ioc->sas_device_lock - * - * This searches for sas_device based on sas_address, then return sas_device - * object. - */ -struct _sas_device * -mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address) -{ - struct _sas_device *sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; - - return NULL; -} - -/** - * _scsih_sas_device_find_by_handle - sas device search - * @ioc: per adapter object - * @handle: sas device handle (assigned by firmware) - * Context: Calling function should acquire ioc->sas_device_lock - * - * This searches for sas_device based on sas_address, then return sas_device - * object. - */ -static struct _sas_device * -_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_device *sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->handle == handle) - return sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->handle == handle) - return sas_device; - - return NULL; -} - -/** - * _scsih_sas_device_remove - remove sas_device from list. - * @ioc: per adapter object - * @sas_device: the sas_device object - * Context: This function will acquire ioc->sas_device_lock. - * - * Removing object and freeing associated memory from the ioc->sas_device_list. - */ -static void -_scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device) -{ - unsigned long flags; - - if (!sas_device) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_del(&sas_device->list); - kfree(sas_device); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -} - - -/** - * _scsih_sas_device_add - insert sas_device to the list. - * @ioc: per adapter object - * @sas_device: the sas_device object - * Context: This function will acquire ioc->sas_device_lock. - * - * Adding new object to the ioc->sas_device_list. - */ -static void -_scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device) -{ - unsigned long flags; - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" - "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long)sas_device->sas_address)); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_add_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { - _scsih_sas_device_remove(ioc, sas_device); - } else if (!sas_device->starget) { - /* When asyn scanning is enabled, its not possible to remove - * devices while scanning is turned on due to an oops in - * scsi_sysfs_add_sdev()->add_device()->sysfs_addrm_start() - */ - if (!ioc->is_driver_loading) { - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); - _scsih_sas_device_remove(ioc, sas_device); - } - } -} - -/** - * _scsih_sas_device_init_add - insert sas_device to the list. - * @ioc: per adapter object - * @sas_device: the sas_device object - * Context: This function will acquire ioc->sas_device_lock. - * - * Adding new object at driver load time to the ioc->sas_device_init_list. - */ -static void -_scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device) -{ - unsigned long flags; - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" - "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long)sas_device->sas_address)); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_add_tail(&sas_device->list, &ioc->sas_device_init_list); - _scsih_determine_boot_device(ioc, sas_device, 0); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -} - -/** - * _scsih_raid_device_find_by_id - raid device search - * @ioc: per adapter object - * @id: sas device target id - * @channel: sas device channel - * Context: Calling function should acquire ioc->raid_device_lock - * - * This searches for raid_device based on target id, then return raid_device - * object. - */ -static struct _raid_device * -_scsih_raid_device_find_by_id(struct MPT2SAS_ADAPTER *ioc, int id, int channel) -{ - struct _raid_device *raid_device, *r; - - r = NULL; - list_for_each_entry(raid_device, &ioc->raid_device_list, list) { - if (raid_device->id == id && raid_device->channel == channel) { - r = raid_device; - goto out; - } - } - - out: - return r; -} - -/** - * _scsih_raid_device_find_by_handle - raid device search - * @ioc: per adapter object - * @handle: sas device handle (assigned by firmware) - * Context: Calling function should acquire ioc->raid_device_lock - * - * This searches for raid_device based on handle, then return raid_device - * object. - */ -static struct _raid_device * -_scsih_raid_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _raid_device *raid_device, *r; - - r = NULL; - list_for_each_entry(raid_device, &ioc->raid_device_list, list) { - if (raid_device->handle != handle) - continue; - r = raid_device; - goto out; - } - - out: - return r; -} - -/** - * _scsih_raid_device_find_by_wwid - raid device search - * @ioc: per adapter object - * @handle: sas device handle (assigned by firmware) - * Context: Calling function should acquire ioc->raid_device_lock - * - * This searches for raid_device based on wwid, then return raid_device - * object. - */ -static struct _raid_device * -_scsih_raid_device_find_by_wwid(struct MPT2SAS_ADAPTER *ioc, u64 wwid) -{ - struct _raid_device *raid_device, *r; - - r = NULL; - list_for_each_entry(raid_device, &ioc->raid_device_list, list) { - if (raid_device->wwid != wwid) - continue; - r = raid_device; - goto out; - } - - out: - return r; -} - -/** - * _scsih_raid_device_add - add raid_device object - * @ioc: per adapter object - * @raid_device: raid_device object - * - * This is added to the raid_device_list link list. - */ -static void -_scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc, - struct _raid_device *raid_device) -{ - unsigned long flags; - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle" - "(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, - raid_device->handle, (unsigned long long)raid_device->wwid)); - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - list_add_tail(&raid_device->list, &ioc->raid_device_list); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); -} - -/** - * _scsih_raid_device_remove - delete raid_device object - * @ioc: per adapter object - * @raid_device: raid_device object - * - */ -static void -_scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc, - struct _raid_device *raid_device) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - list_del(&raid_device->list); - kfree(raid_device); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); -} - -/** - * mpt2sas_scsih_expander_find_by_handle - expander device search - * @ioc: per adapter object - * @handle: expander handle (assigned by firmware) - * Context: Calling function should acquire ioc->sas_device_lock - * - * This searches for expander device based on handle, then returns the - * sas_node object. - */ -struct _sas_node * -mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_node *sas_expander, *r; - - r = NULL; - list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->handle != handle) - continue; - r = sas_expander; - goto out; - } - out: - return r; -} - -/** - * mpt2sas_scsih_expander_find_by_sas_address - expander device search - * @ioc: per adapter object - * @sas_address: sas address - * Context: Calling function should acquire ioc->sas_node_lock. - * - * This searches for expander device based on sas_address, then returns the - * sas_node object. - */ -struct _sas_node * -mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address) -{ - struct _sas_node *sas_expander, *r; - - r = NULL; - list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->sas_address != sas_address) - continue; - r = sas_expander; - goto out; - } - out: - return r; -} - -/** - * _scsih_expander_node_add - insert expander device to the list. - * @ioc: per adapter object - * @sas_expander: the sas_device object - * Context: This function will acquire ioc->sas_node_lock. - * - * Adding new object to the ioc->sas_expander_list. - * - * Return nothing. - */ -static void -_scsih_expander_node_add(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_expander) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - list_add_tail(&sas_expander->list, &ioc->sas_expander_list); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); -} - -/** - * _scsih_is_end_device - determines if device is an end device - * @device_info: bitfield providing information about the device. - * Context: none - * - * Returns 1 if end device. - */ -static int -_scsih_is_end_device(u32 device_info) -{ - if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE && - ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) | - (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) | - (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE))) - return 1; - else - return 0; -} - -/** - * _scsih_scsi_lookup_get - returns scmd entry - * @ioc: per adapter object - * @smid: system request message index - * - * Returns the smid stored scmd pointer. - */ -static struct scsi_cmnd * -_scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return ioc->scsi_lookup[smid - 1].scmd; -} - -/** - * _scsih_scsi_lookup_get_clear - returns scmd entry - * @ioc: per adapter object - * @smid: system request message index - * - * Returns the smid stored scmd pointer. - * Then will derefrence the stored scmd pointer. - */ -static inline struct scsi_cmnd * -_scsih_scsi_lookup_get_clear(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - unsigned long flags; - struct scsi_cmnd *scmd; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - scmd = ioc->scsi_lookup[smid - 1].scmd; - ioc->scsi_lookup[smid - 1].scmd = NULL; - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - - return scmd; -} - -/** - * _scsih_scsi_lookup_find_by_scmd - scmd lookup - * @ioc: per adapter object - * @smid: system request message index - * @scmd: pointer to scsi command object - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * This will search for a scmd pointer in the scsi_lookup array, - * returning the revelent smid. A returned value of zero means invalid. - */ -static u16 -_scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd - *scmd) -{ - u16 smid; - unsigned long flags; - int i; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - smid = 0; - for (i = 0; i < ioc->scsiio_depth; i++) { - if (ioc->scsi_lookup[i].scmd == scmd) { - smid = ioc->scsi_lookup[i].smid; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return smid; -} - -/** - * _scsih_scsi_lookup_find_by_target - search for matching channel:id - * @ioc: per adapter object - * @id: target id - * @channel: channel - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * This will search for a matching channel:id in the scsi_lookup array, - * returning 1 if found. - */ -static u8 -_scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id, - int channel) -{ - u8 found; - unsigned long flags; - int i; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - found = 0; - for (i = 0 ; i < ioc->scsiio_depth; i++) { - if (ioc->scsi_lookup[i].scmd && - (ioc->scsi_lookup[i].scmd->device->id == id && - ioc->scsi_lookup[i].scmd->device->channel == channel)) { - found = 1; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return found; -} - -/** - * _scsih_scsi_lookup_find_by_lun - search for matching channel:id:lun - * @ioc: per adapter object - * @id: target id - * @lun: lun number - * @channel: channel - * Context: This function will acquire ioc->scsi_lookup_lock. - * - * This will search for a matching channel:id:lun in the scsi_lookup array, - * returning 1 if found. - */ -static u8 -_scsih_scsi_lookup_find_by_lun(struct MPT2SAS_ADAPTER *ioc, int id, - unsigned int lun, int channel) -{ - u8 found; - unsigned long flags; - int i; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - found = 0; - for (i = 0 ; i < ioc->scsiio_depth; i++) { - if (ioc->scsi_lookup[i].scmd && - (ioc->scsi_lookup[i].scmd->device->id == id && - ioc->scsi_lookup[i].scmd->device->channel == channel && - ioc->scsi_lookup[i].scmd->device->lun == lun)) { - found = 1; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return found; -} - -/** - * _scsih_get_chain_buffer_tracker - obtain chain tracker - * @ioc: per adapter object - * @smid: smid associated to an IO request - * - * Returns chain tracker(from ioc->free_chain_list) - */ -static struct chain_tracker * -_scsih_get_chain_buffer_tracker(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - struct chain_tracker *chain_req; - unsigned long flags; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (list_empty(&ioc->free_chain_list)) { - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT "chain buffers not " - "available\n", ioc->name)); - return NULL; - } - chain_req = list_entry(ioc->free_chain_list.next, - struct chain_tracker, tracker_list); - list_del_init(&chain_req->tracker_list); - list_add_tail(&chain_req->tracker_list, - &ioc->scsi_lookup[smid - 1].chain_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - return chain_req; -} - -/** - * _scsih_build_scatter_gather - main sg creation routine - * @ioc: per adapter object - * @scmd: scsi command - * @smid: system request message index - * Context: none. - * - * The main routine that builds scatter gather table from a given - * scsi request sent via the .queuecommand main handler. - * - * Returns 0 success, anything else error - */ -static int -_scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, - struct scsi_cmnd *scmd, u16 smid) -{ - Mpi2SCSIIORequest_t *mpi_request; - dma_addr_t chain_dma; - struct scatterlist *sg_scmd; - void *sg_local, *chain; - u32 chain_offset; - u32 chain_length; - u32 chain_flags; - int sges_left; - u32 sges_in_segment; - u32 sgl_flags; - u32 sgl_flags_last_element; - u32 sgl_flags_end_buffer; - struct chain_tracker *chain_req; - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - - /* init scatter gather flags */ - sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT; - if (scmd->sc_data_direction == DMA_TO_DEVICE) - sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC; - sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT) - << MPI2_SGE_FLAGS_SHIFT; - sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) - << MPI2_SGE_FLAGS_SHIFT; - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - - sg_scmd = scsi_sglist(scmd); - sges_left = scsi_dma_map(scmd); - if (sges_left < 0) { - sdev_printk(KERN_ERR, scmd->device, "pci_map_sg" - " failed: request for %d bytes!\n", scsi_bufflen(scmd)); - return -ENOMEM; - } - - sg_local = &mpi_request->SGL; - sges_in_segment = ioc->max_sges_in_main_message; - if (sges_left <= sges_in_segment) - goto fill_in_last_segment; - - mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) + - (sges_in_segment * ioc->sge_size))/4; - - /* fill in main message segment when there is a chain following */ - while (sges_in_segment) { - if (sges_in_segment == 1) - ioc->base_add_sg_single(sg_local, - sgl_flags_last_element | sg_dma_len(sg_scmd), - sg_dma_address(sg_scmd)); - else - ioc->base_add_sg_single(sg_local, sgl_flags | - sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); - sg_scmd = sg_next(sg_scmd); - sg_local += ioc->sge_size; - sges_left--; - sges_in_segment--; - } - - /* initializing the chain flags and pointers */ - chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; - chain_req = _scsih_get_chain_buffer_tracker(ioc, smid); - if (!chain_req) - return -1; - chain = chain_req->chain_buffer; - chain_dma = chain_req->chain_buffer_dma; - do { - sges_in_segment = (sges_left <= - ioc->max_sges_in_chain_message) ? sges_left : - ioc->max_sges_in_chain_message; - chain_offset = (sges_left == sges_in_segment) ? - 0 : (sges_in_segment * ioc->sge_size)/4; - chain_length = sges_in_segment * ioc->sge_size; - if (chain_offset) { - chain_offset = chain_offset << - MPI2_SGE_CHAIN_OFFSET_SHIFT; - chain_length += ioc->sge_size; - } - ioc->base_add_sg_single(sg_local, chain_flags | chain_offset | - chain_length, chain_dma); - sg_local = chain; - if (!chain_offset) - goto fill_in_last_segment; - - /* fill in chain segments */ - while (sges_in_segment) { - if (sges_in_segment == 1) - ioc->base_add_sg_single(sg_local, - sgl_flags_last_element | - sg_dma_len(sg_scmd), - sg_dma_address(sg_scmd)); - else - ioc->base_add_sg_single(sg_local, sgl_flags | - sg_dma_len(sg_scmd), - sg_dma_address(sg_scmd)); - sg_scmd = sg_next(sg_scmd); - sg_local += ioc->sge_size; - sges_left--; - sges_in_segment--; - } - - chain_req = _scsih_get_chain_buffer_tracker(ioc, smid); - if (!chain_req) - return -1; - chain = chain_req->chain_buffer; - chain_dma = chain_req->chain_buffer_dma; - } while (1); - - - fill_in_last_segment: - - /* fill the last segment */ - while (sges_left) { - if (sges_left == 1) - ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer | - sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); - else - ioc->base_add_sg_single(sg_local, sgl_flags | - sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); - sg_scmd = sg_next(sg_scmd); - sg_local += ioc->sge_size; - sges_left--; - } - - return 0; -} - -/** - * _scsih_change_queue_depth - setting device queue depth - * @sdev: scsi device struct - * @qdepth: requested queue depth - * - * Returns queue depth. - */ -static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) -{ - struct Scsi_Host *shost = sdev->host; - int max_depth; - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _sas_device *sas_device; - unsigned long flags; - - max_depth = shost->can_queue; - - /* limit max device queue for SATA to 32 */ - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - goto not_sata; - sas_target_priv_data = sas_device_priv_data->sas_target; - if (!sas_target_priv_data) - goto not_sata; - if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) - goto not_sata; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_device_priv_data->sas_target->sas_address); - if (sas_device && sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - max_depth = MPT2SAS_SATA_QUEUE_DEPTH; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - not_sata: - if (!sdev->tagged_supported) - max_depth = 1; - if (qdepth > max_depth) - qdepth = max_depth; - return scsi_change_queue_depth(sdev, qdepth); -} - -/** - * _scsih_target_alloc - target add routine - * @starget: scsi target struct - * - * Returns 0 if ok. Any other return is assumed to be an error and - * the device is ignored. - */ -static int -_scsih_target_alloc(struct scsi_target *starget) -{ - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _sas_device *sas_device; - struct _raid_device *raid_device; - unsigned long flags; - struct sas_rphy *rphy; - - sas_target_priv_data = kzalloc(sizeof(*sas_target_priv_data), - GFP_KERNEL); - if (!sas_target_priv_data) - return -ENOMEM; - - starget->hostdata = sas_target_priv_data; - sas_target_priv_data->starget = starget; - sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; - - /* RAID volumes */ - if (starget->channel == RAID_CHANNEL) { - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, - starget->channel); - if (raid_device) { - sas_target_priv_data->handle = raid_device->handle; - sas_target_priv_data->sas_address = raid_device->wwid; - sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; - if (ioc->is_warpdrive) - sas_target_priv_data->raid_device = raid_device; - raid_device->starget = starget; - } - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - return 0; - } - - /* sas/sata devices */ - spin_lock_irqsave(&ioc->sas_device_lock, flags); - rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); - - if (sas_device) { - sas_target_priv_data->handle = sas_device->handle; - sas_target_priv_data->sas_address = sas_device->sas_address; - sas_device->starget = starget; - sas_device->id = starget->id; - sas_device->channel = starget->channel; - if (test_bit(sas_device->handle, ioc->pd_handles)) - sas_target_priv_data->flags |= - MPT_TARGET_FLAGS_RAID_COMPONENT; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - return 0; -} - -/** - * _scsih_target_destroy - target destroy routine - * @starget: scsi target struct - * - * Returns nothing. - */ -static void -_scsih_target_destroy(struct scsi_target *starget) -{ - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _sas_device *sas_device; - struct _raid_device *raid_device; - unsigned long flags; - struct sas_rphy *rphy; - - sas_target_priv_data = starget->hostdata; - if (!sas_target_priv_data) - return; - - if (starget->channel == RAID_CHANNEL) { - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, - starget->channel); - if (raid_device) { - raid_device->starget = NULL; - raid_device->sdev = NULL; - } - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - goto out; - } - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); - if (sas_device && (sas_device->starget == starget) && - (sas_device->id == starget->id) && - (sas_device->channel == starget->channel)) - sas_device->starget = NULL; - - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - out: - kfree(sas_target_priv_data); - starget->hostdata = NULL; -} - -/** - * _scsih_slave_alloc - device add routine - * @sdev: scsi device struct - * - * Returns 0 if ok. Any other return is assumed to be an error and - * the device is ignored. - */ -static int -_scsih_slave_alloc(struct scsi_device *sdev) -{ - struct Scsi_Host *shost; - struct MPT2SAS_ADAPTER *ioc; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_target *starget; - struct _raid_device *raid_device; - struct _sas_device *sas_device; - unsigned long flags; - - sas_device_priv_data = kzalloc(sizeof(*sas_device_priv_data), - GFP_KERNEL); - if (!sas_device_priv_data) - return -ENOMEM; - - sas_device_priv_data->lun = sdev->lun; - sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT; - - starget = scsi_target(sdev); - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->num_luns++; - sas_device_priv_data->sas_target = sas_target_priv_data; - sdev->hostdata = sas_device_priv_data; - if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT)) - sdev->no_uld_attach = 1; - - shost = dev_to_shost(&starget->dev); - ioc = shost_priv(shost); - if (starget->channel == RAID_CHANNEL) { - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_id(ioc, - starget->id, starget->channel); - if (raid_device) - raid_device->sdev = sdev; /* raid is single lun */ - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - } - - if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_target_priv_data->sas_address); - if (sas_device && (sas_device->starget == NULL)) { - sdev_printk(KERN_INFO, sdev, - "%s : sas_device->starget set to starget @ %d\n", - __func__, __LINE__); - sas_device->starget = starget; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } - - return 0; -} - -/** - * _scsih_slave_destroy - device destroy routine - * @sdev: scsi device struct - * - * Returns nothing. - */ -static void -_scsih_slave_destroy(struct scsi_device *sdev) -{ - struct MPT2SAS_TARGET *sas_target_priv_data; - struct scsi_target *starget; - struct Scsi_Host *shost; - struct MPT2SAS_ADAPTER *ioc; - struct _sas_device *sas_device; - unsigned long flags; - - if (!sdev->hostdata) - return; - - starget = scsi_target(sdev); - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->num_luns--; - - shost = dev_to_shost(&starget->dev); - ioc = shost_priv(shost); - - if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_target_priv_data->sas_address); - if (sas_device && !sas_target_priv_data->num_luns) - sas_device->starget = NULL; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } - - kfree(sdev->hostdata); - sdev->hostdata = NULL; -} - -/** - * _scsih_display_sata_capabilities - sata capabilities - * @ioc: per adapter object - * @handle: device handle - * @sdev: scsi device struct - */ -static void -_scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, - u16 handle, struct scsi_device *sdev) -{ - Mpi2ConfigReply_t mpi_reply; - Mpi2SasDevicePage0_t sas_device_pg0; - u32 ioc_status; - u16 flags; - u32 device_info; - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - flags = le16_to_cpu(sas_device_pg0.Flags); - device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); - - sdev_printk(KERN_INFO, sdev, - "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " - "sw_preserve(%s)\n", - (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n", - (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n", - (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" : - "n", - (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n", - (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n", - (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n"); -} - -/** - * _scsih_is_raid - return boolean indicating device is raid volume - * @dev the device struct object - */ -static int -_scsih_is_raid(struct device *dev) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); - - if (ioc->is_warpdrive) - return 0; - return (sdev->channel == RAID_CHANNEL) ? 1 : 0; -} - -/** - * _scsih_get_resync - get raid volume resync percent complete - * @dev the device struct object - */ -static void -_scsih_get_resync(struct device *dev) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); - static struct _raid_device *raid_device; - unsigned long flags; - Mpi2RaidVolPage0_t vol_pg0; - Mpi2ConfigReply_t mpi_reply; - u32 volume_status_flags; - u8 percent_complete; - u16 handle; - - percent_complete = 0; - handle = 0; - if (ioc->is_warpdrive) - goto out; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, - sdev->channel); - if (raid_device) { - handle = raid_device->handle; - percent_complete = raid_device->percent_complete; - } - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - - if (!handle) - goto out; - - if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, - MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, - sizeof(Mpi2RaidVolPage0_t))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - percent_complete = 0; - goto out; - } - - volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags); - if (!(volume_status_flags & - MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS)) - percent_complete = 0; - - out: - raid_set_resync(mpt2sas_raid_template, dev, percent_complete); -} - -/** - * _scsih_get_state - get raid volume level - * @dev the device struct object - */ -static void -_scsih_get_state(struct device *dev) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); - static struct _raid_device *raid_device; - unsigned long flags; - Mpi2RaidVolPage0_t vol_pg0; - Mpi2ConfigReply_t mpi_reply; - u32 volstate; - enum raid_state state = RAID_STATE_UNKNOWN; - u16 handle = 0; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, - sdev->channel); - if (raid_device) - handle = raid_device->handle; - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - - if (!raid_device) - goto out; - - if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, - MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, - sizeof(Mpi2RaidVolPage0_t))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - - volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); - if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) { - state = RAID_STATE_RESYNCING; - goto out; - } - - switch (vol_pg0.VolumeState) { - case MPI2_RAID_VOL_STATE_OPTIMAL: - case MPI2_RAID_VOL_STATE_ONLINE: - state = RAID_STATE_ACTIVE; - break; - case MPI2_RAID_VOL_STATE_DEGRADED: - state = RAID_STATE_DEGRADED; - break; - case MPI2_RAID_VOL_STATE_FAILED: - case MPI2_RAID_VOL_STATE_MISSING: - state = RAID_STATE_OFFLINE; - break; - } - out: - raid_set_state(mpt2sas_raid_template, dev, state); -} - -/** - * _scsih_set_level - set raid level - * @sdev: scsi device struct - * @volume_type: volume type - */ -static void -_scsih_set_level(struct scsi_device *sdev, u8 volume_type) -{ - enum raid_level level = RAID_LEVEL_UNKNOWN; - - switch (volume_type) { - case MPI2_RAID_VOL_TYPE_RAID0: - level = RAID_LEVEL_0; - break; - case MPI2_RAID_VOL_TYPE_RAID10: - level = RAID_LEVEL_10; - break; - case MPI2_RAID_VOL_TYPE_RAID1E: - level = RAID_LEVEL_1E; - break; - case MPI2_RAID_VOL_TYPE_RAID1: - level = RAID_LEVEL_1; - break; - } - - raid_set_level(mpt2sas_raid_template, &sdev->sdev_gendev, level); -} - -/** - * _scsih_get_volume_capabilities - volume capabilities - * @ioc: per adapter object - * @sas_device: the raid_device object - * - * Returns 0 for success, else 1 - */ -static int -_scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, - struct _raid_device *raid_device) -{ - Mpi2RaidVolPage0_t *vol_pg0; - Mpi2RaidPhysDiskPage0_t pd_pg0; - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 sz; - u8 num_pds; - - if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, - &num_pds)) || !num_pds) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, - __func__)); - return 1; - } - - raid_device->num_pds = num_pds; - sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * - sizeof(Mpi2RaidVol0PhysDisk_t)); - vol_pg0 = kzalloc(sz, GFP_KERNEL); - if (!vol_pg0) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, - __func__)); - return 1; - } - - if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, - MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, - __func__)); - kfree(vol_pg0); - return 1; - } - - raid_device->volume_type = vol_pg0->VolumeType; - - /* figure out what the underlying devices are by - * obtaining the device_info bits for the 1st device - */ - if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, - &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, - vol_pg0->PhysDisk[0].PhysDiskNum))) { - if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, - &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, - le16_to_cpu(pd_pg0.DevHandle)))) { - raid_device->device_info = - le32_to_cpu(sas_device_pg0.DeviceInfo); - } - } - - kfree(vol_pg0); - return 0; -} -/** - * _scsih_disable_ddio - Disable direct I/O for all the volumes - * @ioc: per adapter object - */ -static void -_scsih_disable_ddio(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2RaidVolPage1_t vol_pg1; - Mpi2ConfigReply_t mpi_reply; - struct _raid_device *raid_device; - u16 handle; - u16 ioc_status; - unsigned long flags; - - handle = 0xFFFF; - while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, - &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) - break; - handle = le16_to_cpu(vol_pg1.DevHandle); - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - if (raid_device) - raid_device->direct_io_enabled = 0; - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - } - return; -} - - -/** - * _scsih_get_num_volumes - Get number of volumes in the ioc - * @ioc: per adapter object - */ -static u8 -_scsih_get_num_volumes(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2RaidVolPage1_t vol_pg1; - Mpi2ConfigReply_t mpi_reply; - u16 handle; - u8 vol_cnt = 0; - u16 ioc_status; - - handle = 0xFFFF; - while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, - &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) - break; - vol_cnt++; - handle = le16_to_cpu(vol_pg1.DevHandle); - } - return vol_cnt; -} - - -/** - * _scsih_init_warpdrive_properties - Set properties for warpdrive direct I/O. - * @ioc: per adapter object - * @raid_device: the raid_device object - */ -static void -_scsih_init_warpdrive_properties(struct MPT2SAS_ADAPTER *ioc, - struct _raid_device *raid_device) -{ - Mpi2RaidVolPage0_t *vol_pg0; - Mpi2RaidPhysDiskPage0_t pd_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 sz; - u8 num_pds, count; - unsigned long stripe_sz, block_sz; - u8 stripe_exp, block_exp; - u64 dev_max_lba; - - if (!ioc->is_warpdrive) - return; - - if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "globally as drives are exposed\n", ioc->name); - return; - } - if (_scsih_get_num_volumes(ioc) > 1) { - _scsih_disable_ddio(ioc); - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "globally as number of drives > 1\n", ioc->name); - return; - } - if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, - &num_pds)) || !num_pds) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "Failure in computing number of drives\n", ioc->name); - return; - } - - sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * - sizeof(Mpi2RaidVol0PhysDisk_t)); - vol_pg0 = kzalloc(sz, GFP_KERNEL); - if (!vol_pg0) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "Memory allocation failure for RVPG0\n", ioc->name); - return; - } - - if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, - MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "Failure in retrieving RVPG0\n", ioc->name); - kfree(vol_pg0); - return; - } - - /* - * WARPDRIVE:If number of physical disks in a volume exceeds the max pds - * assumed for WARPDRIVE, disable direct I/O - */ - if (num_pds > MPT_MAX_WARPDRIVE_PDS) { - printk(MPT2SAS_WARN_FMT "WarpDrive : Direct IO is disabled " - "for the drive with handle(0x%04x): num_mem=%d, " - "max_mem_allowed=%d\n", ioc->name, raid_device->handle, - num_pds, MPT_MAX_WARPDRIVE_PDS); - kfree(vol_pg0); - return; - } - for (count = 0; count < num_pds; count++) { - if (mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, - &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, - vol_pg0->PhysDisk[count].PhysDiskNum) || - le16_to_cpu(pd_pg0.DevHandle) == - MPT2SAS_INVALID_DEVICE_HANDLE) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is " - "disabled for the drive with handle(0x%04x) member" - "handle retrieval failed for member number=%d\n", - ioc->name, raid_device->handle, - vol_pg0->PhysDisk[count].PhysDiskNum); - goto out_error; - } - /* Disable direct I/O if member drive lba exceeds 4 bytes */ - dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA); - if (dev_max_lba >> 32) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is " - "disabled for the drive with handle(0x%04x) member" - "handle (0x%04x) unsupported max lba 0x%016llx\n", - ioc->name, raid_device->handle, - le16_to_cpu(pd_pg0.DevHandle), - (unsigned long long)dev_max_lba); - goto out_error; - } - - raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle); - } - - /* - * Assumption for WD: Direct I/O is not supported if the volume is - * not RAID0 - */ - if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "for the drive with handle(0x%04x): type=%d, " - "s_sz=%uK, blk_size=%u\n", ioc->name, - raid_device->handle, raid_device->volume_type, - (le32_to_cpu(vol_pg0->StripeSize) * - le16_to_cpu(vol_pg0->BlockSize)) / 1024, - le16_to_cpu(vol_pg0->BlockSize)); - goto out_error; - } - - stripe_sz = le32_to_cpu(vol_pg0->StripeSize); - stripe_exp = find_first_bit(&stripe_sz, 32); - if (stripe_exp == 32) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "for the drive with handle(0x%04x) invalid stripe sz %uK\n", - ioc->name, raid_device->handle, - (le32_to_cpu(vol_pg0->StripeSize) * - le16_to_cpu(vol_pg0->BlockSize)) / 1024); - goto out_error; - } - raid_device->stripe_exponent = stripe_exp; - block_sz = le16_to_cpu(vol_pg0->BlockSize); - block_exp = find_first_bit(&block_sz, 16); - if (block_exp == 16) { - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is disabled " - "for the drive with handle(0x%04x) invalid block sz %u\n", - ioc->name, raid_device->handle, - le16_to_cpu(vol_pg0->BlockSize)); - goto out_error; - } - raid_device->block_exponent = block_exp; - raid_device->direct_io_enabled = 1; - - printk(MPT2SAS_INFO_FMT "WarpDrive : Direct IO is Enabled for the drive" - " with handle(0x%04x)\n", ioc->name, raid_device->handle); - /* - * WARPDRIVE: Though the following fields are not used for direct IO, - * stored for future purpose: - */ - raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA); - raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize); - raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize); - - - kfree(vol_pg0); - return; - -out_error: - raid_device->direct_io_enabled = 0; - for (count = 0; count < num_pds; count++) - raid_device->pd_handle[count] = 0; - kfree(vol_pg0); - return; -} - -/** - * _scsih_enable_tlr - setting TLR flags - * @ioc: per adapter object - * @sdev: scsi device struct - * - * Enabling Transaction Layer Retries for tape devices when - * vpd page 0x90 is present - * - */ -static void -_scsih_enable_tlr(struct MPT2SAS_ADAPTER *ioc, struct scsi_device *sdev) -{ - /* only for TAPE */ - if (sdev->type != TYPE_TAPE) - return; - - if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)) - return; - - sas_enable_tlr(sdev); - sdev_printk(KERN_INFO, sdev, "TLR %s\n", - sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled"); - return; - -} - -/** - * _scsih_slave_configure - device configure routine. - * @sdev: scsi device struct - * - * Returns 0 if ok. Any other return is assumed to be an error and - * the device is ignored. - */ -static int -_scsih_slave_configure(struct scsi_device *sdev) -{ - struct Scsi_Host *shost = sdev->host; - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _sas_device *sas_device; - struct _raid_device *raid_device; - unsigned long flags; - int qdepth; - u8 ssp_target = 0; - char *ds = ""; - char *r_level = ""; - u16 handle, volume_handle = 0; - u64 volume_wwid = 0; - - qdepth = 1; - sas_device_priv_data = sdev->hostdata; - sas_device_priv_data->configured_lun = 1; - sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT; - sas_target_priv_data = sas_device_priv_data->sas_target; - handle = sas_target_priv_data->handle; - - /* raid volume handling */ - if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - if (!raid_device) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, - __LINE__, __func__)); - return 1; - } - - if (_scsih_get_volume_capabilities(ioc, raid_device)) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, - __LINE__, __func__)); - return 1; - } - /* - * WARPDRIVE: Initialize the required data for Direct IO - */ - _scsih_init_warpdrive_properties(ioc, raid_device); - - /* RAID Queue Depth Support - * IS volume = underlying qdepth of drive type, either - * MPT2SAS_SAS_QUEUE_DEPTH or MPT2SAS_SATA_QUEUE_DEPTH - * IM/IME/R10 = 128 (MPT2SAS_RAID_QUEUE_DEPTH) - */ - if (raid_device->device_info & - MPI2_SAS_DEVICE_INFO_SSP_TARGET) { - qdepth = MPT2SAS_SAS_QUEUE_DEPTH; - ds = "SSP"; - } else { - qdepth = MPT2SAS_SATA_QUEUE_DEPTH; - if (raid_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - ds = "SATA"; - else - ds = "STP"; - } - - switch (raid_device->volume_type) { - case MPI2_RAID_VOL_TYPE_RAID0: - r_level = "RAID0"; - break; - case MPI2_RAID_VOL_TYPE_RAID1E: - qdepth = MPT2SAS_RAID_QUEUE_DEPTH; - if (ioc->manu_pg10.OEMIdentifier && - (le32_to_cpu(ioc->manu_pg10.GenericFlags0) & - MFG10_GF0_R10_DISPLAY) && - !(raid_device->num_pds % 2)) - r_level = "RAID10"; - else - r_level = "RAID1E"; - break; - case MPI2_RAID_VOL_TYPE_RAID1: - qdepth = MPT2SAS_RAID_QUEUE_DEPTH; - r_level = "RAID1"; - break; - case MPI2_RAID_VOL_TYPE_RAID10: - qdepth = MPT2SAS_RAID_QUEUE_DEPTH; - r_level = "RAID10"; - break; - case MPI2_RAID_VOL_TYPE_UNKNOWN: - default: - qdepth = MPT2SAS_RAID_QUEUE_DEPTH; - r_level = "RAIDX"; - break; - } - - if (!ioc->hide_ir_msg) - sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " - "wwid(0x%016llx), pd_count(%d), type(%s)\n", - r_level, raid_device->handle, - (unsigned long long)raid_device->wwid, - raid_device->num_pds, ds); - _scsih_change_queue_depth(sdev, qdepth); - /* raid transport support */ - if (!ioc->is_warpdrive) - _scsih_set_level(sdev, raid_device->volume_type); - return 0; - } - - /* non-raid handling */ - if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { - if (mpt2sas_config_get_volume_handle(ioc, handle, - &volume_handle)) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__)); - return 1; - } - if (volume_handle && mpt2sas_config_get_volume_wwid(ioc, - volume_handle, &volume_wwid)) { - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__)); - return 1; - } - } - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_device_priv_data->sas_target->sas_address); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - dfailprintk(ioc, printk(MPT2SAS_WARN_FMT - "failure at %s:%d/%s()!\n", ioc->name, __FILE__, - __LINE__, __func__)); - return 1; - } - sas_device->volume_handle = volume_handle; - sas_device->volume_wwid = volume_wwid; - if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { - qdepth = MPT2SAS_SAS_QUEUE_DEPTH; - ssp_target = 1; - ds = "SSP"; - } else { - qdepth = MPT2SAS_SATA_QUEUE_DEPTH; - if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) - ds = "STP"; - else if (sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - ds = "SATA"; - } - sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " - "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", - ds, sas_device->handle, - (unsigned long long)sas_device->sas_address, - sas_device->phy, - (unsigned long long)sas_device->device_name); - sdev_printk(KERN_INFO, sdev, "%s: " - "enclosure_logical_id(0x%016llx), slot(%d)\n", ds, - (unsigned long long) sas_device->enclosure_logical_id, - sas_device->slot); - - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!ssp_target) - _scsih_display_sata_capabilities(ioc, handle, sdev); - - - _scsih_change_queue_depth(sdev, qdepth); - - if (ssp_target) { - sas_read_port_mode_page(sdev); - _scsih_enable_tlr(ioc, sdev); - } - return 0; -} - -/** - * _scsih_bios_param - fetch head, sector, cylinder info for a disk - * @sdev: scsi device struct - * @bdev: pointer to block device context - * @capacity: device size (in 512 byte sectors) - * @params: three element array to place output: - * params[0] number of heads (max 255) - * params[1] number of sectors (max 63) - * params[2] number of cylinders - * - * Return nothing. - */ -static int -_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int params[]) -{ - int heads; - int sectors; - sector_t cylinders; - ulong dummy; - - heads = 64; - sectors = 32; - - dummy = heads * sectors; - cylinders = capacity; - sector_div(cylinders, dummy); - - /* - * Handle extended translation size for logical drives - * > 1Gb - */ - if ((ulong)capacity >= 0x200000) { - heads = 255; - sectors = 63; - dummy = heads * sectors; - cylinders = capacity; - sector_div(cylinders, dummy); - } - - /* return result */ - params[0] = heads; - params[1] = sectors; - params[2] = cylinders; - - return 0; -} - -/** - * _scsih_response_code - translation of device response code - * @ioc: per adapter object - * @response_code: response code returned by the device - * - * Return nothing. - */ -static void -_scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) -{ - char *desc; - - switch (response_code) { - case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE: - desc = "task management request completed"; - break; - case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME: - desc = "invalid frame"; - break; - case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED: - desc = "task management request not supported"; - break; - case MPI2_SCSITASKMGMT_RSP_TM_FAILED: - desc = "task management request failed"; - break; - case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED: - desc = "task management request succeeded"; - break; - case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN: - desc = "invalid lun"; - break; - case 0xA: - desc = "overlapped tag attempted"; - break; - case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC: - desc = "task queued, however not sent to target"; - break; - default: - desc = "unknown"; - break; - } - printk(MPT2SAS_WARN_FMT "response_code(0x%01x): %s\n", - ioc->name, response_code, desc); -} - -/** - * _scsih_tm_done - tm completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: none. - * - * The callback handler when using scsih_issue_tm. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED) - return 1; - if (ioc->tm_cmds.smid != smid) - return 1; - mpt2sas_base_flush_reply_queues(ioc); - ioc->tm_cmds.status |= MPT2_CMD_COMPLETE; - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (mpi_reply) { - memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); - ioc->tm_cmds.status |= MPT2_CMD_REPLY_VALID; - } - ioc->tm_cmds.status &= ~MPT2_CMD_PENDING; - complete(&ioc->tm_cmds.done); - return 1; -} - -/** - * mpt2sas_scsih_set_tm_flag - set per target tm_busy - * @ioc: per adapter object - * @handle: device handle - * - * During taskmangement request, we need to freeze the device queue. - */ -void -mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - u8 skip = 0; - - shost_for_each_device(sdev, ioc->shost) { - if (skip) - continue; - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (sas_device_priv_data->sas_target->handle == handle) { - sas_device_priv_data->sas_target->tm_busy = 1; - skip = 1; - ioc->ignore_loginfos = 1; - } - } -} - -/** - * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy - * @ioc: per adapter object - * @handle: device handle - * - * During taskmangement request, we need to freeze the device queue. - */ -void -mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - u8 skip = 0; - - shost_for_each_device(sdev, ioc->shost) { - if (skip) - continue; - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (sas_device_priv_data->sas_target->handle == handle) { - sas_device_priv_data->sas_target->tm_busy = 0; - skip = 1; - ioc->ignore_loginfos = 0; - } - } -} - - -/** - * mpt2sas_scsih_issue_tm - main routine for sending tm requests - * @ioc: per adapter struct - * @device_handle: device handle - * @channel: the channel assigned by the OS - * @id: the id assigned by the OS - * @lun: lun number - * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) - * @smid_task: smid assigned to the task - * @timeout: timeout in seconds - * @m_type: TM_MUTEX_ON or TM_MUTEX_OFF - * Context: user - * - * A generic API for sending task management requests to firmware. - * - * The callback index is set inside `ioc->tm_cb_idx`. - * - * Return SUCCESS or FAILED. - */ -int -mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel, - uint id, uint lun, u8 type, u16 smid_task, ulong timeout, - enum mutex_type m_type) -{ - Mpi2SCSITaskManagementRequest_t *mpi_request; - Mpi2SCSITaskManagementReply_t *mpi_reply; - u16 smid = 0; - u32 ioc_state; - unsigned long timeleft; - struct scsiio_tracker *scsi_lookup = NULL; - int rc; - - if (m_type == TM_MUTEX_ON) - mutex_lock(&ioc->tm_cmds.mutex); - if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", - __func__, ioc->name); - rc = FAILED; - goto err_out; - } - - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - rc = FAILED; - goto err_out; - } - - ioc_state = mpt2sas_base_get_iocstate(ioc, 0); - if (ioc_state & MPI2_DOORBELL_USED) { - dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "unexpected doorbell " - "active!\n", ioc->name)); - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; - } - - if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { - mpt2sas_base_fault_info(ioc, ioc_state & - MPI2_DOORBELL_DATA_MASK); - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - goto err_out; - } - - smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = FAILED; - goto err_out; - } - - if (type == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) - scsi_lookup = &ioc->scsi_lookup[smid_task - 1]; - - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," - " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, - smid_task)); - ioc->tm_cmds.status = MPT2_CMD_PENDING; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->tm_cmds.smid = smid; - memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); - memset(ioc->tm_cmds.reply, 0, sizeof(Mpi2SCSITaskManagementReply_t)); - mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; - mpi_request->DevHandle = cpu_to_le16(handle); - mpi_request->TaskType = type; - mpi_request->TaskMID = cpu_to_le16(smid_task); - int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); - mpt2sas_scsih_set_tm_flag(ioc, handle); - init_completion(&ioc->tm_cmds.done); - mpt2sas_base_put_smid_hi_priority(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); - if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SCSITaskManagementRequest_t)/4); - if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) { - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = (!rc) ? SUCCESS : FAILED; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mpt2sas_scsih_clear_tm_flag(ioc, handle); - goto err_out; - } - } - - if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { - mpi_reply = ioc->tm_cmds.reply; - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "complete tm: " - "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - if (ioc->logging_level & MPT_DEBUG_TM) { - _scsih_response_code(ioc, mpi_reply->ResponseCode); - if (mpi_reply->IOCStatus) - _debug_dump_mf(mpi_request, - sizeof(Mpi2SCSITaskManagementRequest_t)/4); - } - } - - switch (type) { - case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: - rc = SUCCESS; - if (scsi_lookup->scmd == NULL) - break; - rc = FAILED; - break; - - case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: - if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) - rc = FAILED; - else - rc = SUCCESS; - break; - - case MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET: - case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: - if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) - rc = FAILED; - else - rc = SUCCESS; - break; - case MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK: - rc = SUCCESS; - break; - default: - rc = FAILED; - break; - } - - mpt2sas_scsih_clear_tm_flag(ioc, handle); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - - return rc; - - err_out: - if (m_type == TM_MUTEX_ON) - mutex_unlock(&ioc->tm_cmds.mutex); - return rc; -} - -/** - * _scsih_tm_display_info - displays info about the device - * @ioc: per adapter struct - * @scmd: pointer to scsi command object - * - * Called by task management callback handlers. - */ -static void -_scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) -{ - struct scsi_target *starget = scmd->device->sdev_target; - struct MPT2SAS_TARGET *priv_target = starget->hostdata; - struct _sas_device *sas_device = NULL; - unsigned long flags; - char *device_str = NULL; - - if (!priv_target) - return; - if (ioc->hide_ir_msg) - device_str = "WarpDrive"; - else - device_str = "volume"; - - scsi_print_command(scmd); - if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { - starget_printk(KERN_INFO, starget, "%s handle(0x%04x), " - "%s wwid(0x%016llx)\n", device_str, priv_target->handle, - device_str, (unsigned long long)priv_target->sas_address); - } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); - if (sas_device) { - if (priv_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) { - starget_printk(KERN_INFO, starget, - "volume handle(0x%04x), " - "volume wwid(0x%016llx)\n", - sas_device->volume_handle, - (unsigned long long)sas_device->volume_wwid); - } - starget_printk(KERN_INFO, starget, - "handle(0x%04x), sas_address(0x%016llx), phy(%d)\n", - sas_device->handle, - (unsigned long long)sas_device->sas_address, - sas_device->phy); - starget_printk(KERN_INFO, starget, - "enclosure_logical_id(0x%016llx), slot(%d)\n", - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot); - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } -} - -/** - * _scsih_abort - eh threads main abort routine - * @scmd: pointer to scsi command object - * - * Returns SUCCESS if command aborted else FAILED - */ -static int -_scsih_abort(struct scsi_cmnd *scmd) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); - struct MPT2SAS_DEVICE *sas_device_priv_data; - u16 smid; - u16 handle; - int r; - - sdev_printk(KERN_INFO, scmd->device, "attempting task abort! " - "scmd(%p)\n", scmd); - _scsih_tm_display_info(ioc, scmd); - - sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - sdev_printk(KERN_INFO, scmd->device, "device been deleted! " - "scmd(%p)\n", scmd); - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - r = SUCCESS; - goto out; - } - - /* search for the command */ - smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd); - if (!smid) { - scmd->result = DID_RESET << 16; - r = SUCCESS; - goto out; - } - - /* for hidden raid components and volumes this is not supported */ - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT || - sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) { - scmd->result = DID_RESET << 16; - r = FAILED; - goto out; - } - - mpt2sas_halt_firmware(ioc); - - handle = sas_device_priv_data->sas_target->handle; - r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, - scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, TM_MUTEX_ON); - - out: - sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", - ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); - return r; -} - -/** - * _scsih_dev_reset - eh threads main device reset routine - * @scmd: pointer to scsi command object - * - * Returns SUCCESS if command aborted else FAILED - */ -static int -_scsih_dev_reset(struct scsi_cmnd *scmd) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; - u16 handle; - int r; - - struct scsi_target *starget = scmd->device->sdev_target; - - starget_printk(KERN_INFO, starget, "attempting device reset! " - "scmd(%p)\n", scmd); - _scsih_tm_display_info(ioc, scmd); - - sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - starget_printk(KERN_INFO, starget, "device been deleted! " - "scmd(%p)\n", scmd); - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - r = SUCCESS; - goto out; - } - - /* for hidden raid components obtain the volume_handle */ - handle = 0; - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); - if (sas_device) - handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } else - handle = sas_device_priv_data->sas_target->handle; - - if (!handle) { - scmd->result = DID_RESET << 16; - r = FAILED; - goto out; - } - - r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, - scmd->device->id, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, TM_MUTEX_ON); - - out: - sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", - ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); - return r; -} - -/** - * _scsih_target_reset - eh threads main target reset routine - * @scmd: pointer to scsi command object - * - * Returns SUCCESS if command aborted else FAILED - */ -static int -_scsih_target_reset(struct scsi_cmnd *scmd) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; - u16 handle; - int r; - struct scsi_target *starget = scmd->device->sdev_target; - - starget_printk(KERN_INFO, starget, "attempting target reset! " - "scmd(%p)\n", scmd); - _scsih_tm_display_info(ioc, scmd); - - sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - starget_printk(KERN_INFO, starget, "target been deleted! " - "scmd(%p)\n", scmd); - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - r = SUCCESS; - goto out; - } - - /* for hidden raid components obtain the volume_handle */ - handle = 0; - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); - if (sas_device) - handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } else - handle = sas_device_priv_data->sas_target->handle; - - if (!handle) { - scmd->result = DID_RESET << 16; - r = FAILED; - goto out; - } - - r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, - scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, - 30, TM_MUTEX_ON); - - out: - starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", - ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); - return r; -} - -/** - * _scsih_host_reset - eh threads main host reset routine - * @scmd: pointer to scsi command object - * - * Returns SUCCESS if command aborted else FAILED - */ -static int -_scsih_host_reset(struct scsi_cmnd *scmd) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); - int r, retval; - - printk(MPT2SAS_INFO_FMT "attempting host reset! scmd(%p)\n", - ioc->name, scmd); - scsi_print_command(scmd); - - if (ioc->is_driver_loading) { - printk(MPT2SAS_INFO_FMT "Blocking the host reset\n", - ioc->name); - r = FAILED; - goto out; - } - - retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - r = (retval < 0) ? FAILED : SUCCESS; - - out: - printk(MPT2SAS_INFO_FMT "host reset: %s scmd(%p)\n", - ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); - - return r; -} - -/** - * _scsih_fw_event_add - insert and queue up fw_event - * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock. - * - * This adds the firmware event object into link list, then queues it up to - * be processed from user context. - * - * Return nothing. - */ -static void -_scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) -{ - unsigned long flags; - - if (ioc->firmware_event_thread == NULL) - return; - - spin_lock_irqsave(&ioc->fw_event_lock, flags); - list_add_tail(&fw_event->list, &ioc->fw_event_list); - INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); - queue_delayed_work(ioc->firmware_event_thread, - &fw_event->delayed_work, 0); - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); -} - -/** - * _scsih_fw_event_free - delete fw_event - * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock. - * - * This removes firmware event object from link list, frees associated memory. - * - * Return nothing. - */ -static void -_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work - *fw_event) -{ - unsigned long flags; - - spin_lock_irqsave(&ioc->fw_event_lock, flags); - list_del(&fw_event->list); - kfree(fw_event); - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); -} - - -/** - * _scsih_error_recovery_delete_devices - remove devices not responding - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc) -{ - struct fw_event_work *fw_event; - - if (ioc->is_driver_loading) - return; - - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); - if (!fw_event) - return; - - fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES; - fw_event->ioc = ioc; - _scsih_fw_event_add(ioc, fw_event); -} - -/** - * mpt2sas_port_enable_complete - port enable completed (fake event) - * @ioc: per adapter object - * - * Return nothing. - */ -void -mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc) -{ - struct fw_event_work *fw_event; - - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); - if (!fw_event) - return; - fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE; - fw_event->ioc = ioc; - _scsih_fw_event_add(ioc, fw_event); -} - -/** - * _scsih_fw_event_cleanup_queue - cleanup event queue - * @ioc: per adapter object - * - * Walk the firmware event queue, either killing timers, or waiting - * for outstanding events to complete - * - * Return nothing. - */ -static void -_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc) -{ - struct fw_event_work *fw_event, *next; - - if (list_empty(&ioc->fw_event_list) || - !ioc->firmware_event_thread || in_interrupt()) - return; - - list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { - if (cancel_delayed_work_sync(&fw_event->delayed_work)) { - _scsih_fw_event_free(ioc, fw_event); - continue; - } - } -} - -/** - * _scsih_ublock_io_all_device - unblock every device - * @ioc: per adapter object - * - * change the device state from block to running - */ -static void -_scsih_ublock_io_all_device(struct MPT2SAS_ADAPTER *ioc) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (!sas_device_priv_data->block) - continue; - sas_device_priv_data->block = 0; - dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, "device_running, " - "handle(0x%04x)\n", - sas_device_priv_data->sas_target->handle)); - scsi_internal_device_unblock(sdev, SDEV_RUNNING); - } -} -/** - * _scsih_ublock_io_device - set the device state to SDEV_RUNNING - * @ioc: per adapter object - * @handle: device handle - * - * During device pull we need to appropiately set the sdev state. - */ -static void -_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (!sas_device_priv_data->block) - continue; - if (sas_device_priv_data->sas_target->sas_address == - sas_address) { - dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, - MPT2SAS_INFO_FMT "SDEV_RUNNING: " - "sas address(0x%016llx)\n", ioc->name, - (unsigned long long)sas_address)); - sas_device_priv_data->block = 0; - scsi_internal_device_unblock(sdev, SDEV_RUNNING); - } - } -} - -/** - * _scsih_block_io_all_device - set the device state to SDEV_BLOCK - * @ioc: per adapter object - * @handle: device handle - * - * During device pull we need to appropiately set the sdev state. - */ -static void -_scsih_block_io_all_device(struct MPT2SAS_ADAPTER *ioc) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (sas_device_priv_data->block) - continue; - sas_device_priv_data->block = 1; - dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, "device_blocked, " - "handle(0x%04x)\n", - sas_device_priv_data->sas_target->handle)); - scsi_internal_device_block(sdev); - } -} - - -/** - * _scsih_block_io_device - set the device state to SDEV_BLOCK - * @ioc: per adapter object - * @handle: device handle - * - * During device pull we need to appropiately set the sdev state. - */ -static void -_scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (sas_device_priv_data->block) - continue; - if (sas_device_priv_data->sas_target->handle == handle) { - dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, - MPT2SAS_INFO_FMT "SDEV_BLOCK: " - "handle(0x%04x)\n", ioc->name, handle)); - sas_device_priv_data->block = 1; - scsi_internal_device_block(sdev); - } - } -} - -/** - * _scsih_block_io_to_children_attached_to_ex - * @ioc: per adapter object - * @sas_expander: the sas_device object - * - * This routine set sdev state to SDEV_BLOCK for all devices - * attached to this expander. This function called when expander is - * pulled. - */ -static void -_scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_expander) -{ - struct _sas_port *mpt2sas_port; - struct _sas_device *sas_device; - struct _sas_node *expander_sibling; - unsigned long flags; - - if (!sas_expander) - return; - - list_for_each_entry(mpt2sas_port, - &sas_expander->sas_port_list, port_list) { - if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = - mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - if (sas_device) - set_bit(sas_device->handle, - ioc->blocking_handles); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } - } - - list_for_each_entry(mpt2sas_port, - &sas_expander->sas_port_list, port_list) { - - if (mpt2sas_port->remote_identify.device_type == - SAS_EDGE_EXPANDER_DEVICE || - mpt2sas_port->remote_identify.device_type == - SAS_FANOUT_EXPANDER_DEVICE) { - expander_sibling = - mpt2sas_scsih_expander_find_by_sas_address( - ioc, mpt2sas_port->remote_identify.sas_address); - _scsih_block_io_to_children_attached_to_ex(ioc, - expander_sibling); - } - } -} - -/** - * _scsih_block_io_to_children_attached_directly - * @ioc: per adapter object - * @event_data: topology change event data - * - * This routine set sdev state to SDEV_BLOCK for all devices - * direct attached during device pull. - */ -static void -_scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataSasTopologyChangeList_t *event_data) -{ - int i; - u16 handle; - u16 reason_code; - u8 phy_number; - - for (i = 0; i < event_data->NumEntries; i++) { - handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); - if (!handle) - continue; - phy_number = event_data->StartPhyNum + i; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; - if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING) - _scsih_block_io_device(ioc, handle); - } -} - -/** - * _scsih_tm_tr_send - send task management request - * @ioc: per adapter object - * @handle: device handle - * Context: interrupt time. - * - * This code is to initiate the device removal handshake protocol - * with controller firmware. This function will issue target reset - * using high priority request queue. It will send a sas iounit - * control request (MPI2_SAS_OP_REMOVE_DEVICE) from this completion. - * - * This is designed to send muliple task management request at the same - * time to the fifo. If the fifo is full, we will append the request, - * and process it in a future completion. - */ -static void -_scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - Mpi2SCSITaskManagementRequest_t *mpi_request; - u16 smid; - struct _sas_device *sas_device; - struct MPT2SAS_TARGET *sas_target_priv_data = NULL; - u64 sas_address = 0; - unsigned long flags; - struct _tr_list *delayed_tr; - u32 ioc_state; - - if (ioc->remove_host) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " - "removed: handle(0x%04x)\n", __func__, ioc->name, handle)); - return; - } else if (ioc->pci_error_recovery) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " - "error recovery: handle(0x%04x)\n", __func__, ioc->name, - handle)); - return; - } - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " - "operational: handle(0x%04x)\n", __func__, ioc->name, - handle)); - return; - } - - /* if PD, then return */ - if (test_bit(handle, ioc->pd_handles)) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device && sas_device->starget && - sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - sas_address = sas_device->sas_address; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_target_priv_data) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "setting delete flag: " - "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, - (unsigned long long)sas_address)); - _scsih_ublock_io_device(ioc, sas_address); - sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; - } - - smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); - if (!smid) { - delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); - if (!delayed_tr) - return; - INIT_LIST_HEAD(&delayed_tr->list); - delayed_tr->handle = handle; - list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", - ioc->name, handle)); - return; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " - "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, - ioc->tm_tr_cb_idx)); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; - mpi_request->DevHandle = cpu_to_le16(handle); - mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; - mpt2sas_base_put_smid_hi_priority(ioc, smid); -} - - - -/** - * _scsih_sas_control_complete - completion routine - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: interrupt time. - * - * This is the sas iounit control completion routine. - * This code is part of the code to initiate the device removal - * handshake protocol with controller firmware. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 msix_index, u32 reply) -{ - Mpi2SasIoUnitControlReply_t *mpi_reply = - mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (likely(mpi_reply)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "sc_complete:handle(0x%04x), (open) " - "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, - le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } else { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - } - return 1; -} - -/** - * _scsih_tm_tr_volume_send - send target reset request for volumes - * @ioc: per adapter object - * @handle: device handle - * Context: interrupt time. - * - * This is designed to send muliple task management request at the same - * time to the fifo. If the fifo is full, we will append the request, - * and process it in a future completion. - */ -static void -_scsih_tm_tr_volume_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - Mpi2SCSITaskManagementRequest_t *mpi_request; - u16 smid; - struct _tr_list *delayed_tr; - - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " - "progress!\n", __func__, ioc->name)); - return; - } - - smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_volume_cb_idx); - if (!smid) { - delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); - if (!delayed_tr) - return; - INIT_LIST_HEAD(&delayed_tr->list); - delayed_tr->handle = handle; - list_add_tail(&delayed_tr->list, &ioc->delayed_tr_volume_list); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", - ioc->name, handle)); - return; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " - "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, - ioc->tm_tr_volume_cb_idx)); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; - mpi_request->DevHandle = cpu_to_le16(handle); - mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; - mpt2sas_base_put_smid_hi_priority(ioc, smid); -} - -/** - * _scsih_tm_volume_tr_complete - target reset completion - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: interrupt time. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_tm_volume_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, - u8 msix_index, u32 reply) -{ - u16 handle; - Mpi2SCSITaskManagementRequest_t *mpi_request_tm; - Mpi2SCSITaskManagementReply_t *mpi_reply = - mpt2sas_base_get_reply_virt_addr(ioc, reply); - - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " - "progress!\n", __func__, ioc->name)); - return 1; - } - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return 1; - } - mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); - handle = le16_to_cpu(mpi_request_tm->DevHandle); - if (handle != le16_to_cpu(mpi_reply->DevHandle)) { - dewtprintk(ioc, printk("spurious interrupt: " - "handle(0x%04x:0x%04x), smid(%d)!!!\n", handle, - le16_to_cpu(mpi_reply->DevHandle), smid)); - return 0; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", ioc->name, - handle, smid, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - - return _scsih_check_for_pending_tm(ioc, smid); -} - -/** - * _scsih_tm_tr_complete - - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: interrupt time. - * - * This is the target reset completion routine. - * This code is part of the code to initiate the device removal - * handshake protocol with controller firmware. - * It will send a sas iounit control request (MPI2_SAS_OP_REMOVE_DEVICE) - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - u16 handle; - Mpi2SCSITaskManagementRequest_t *mpi_request_tm; - Mpi2SCSITaskManagementReply_t *mpi_reply = - mpt2sas_base_get_reply_virt_addr(ioc, reply); - Mpi2SasIoUnitControlRequest_t *mpi_request; - u16 smid_sas_ctrl; - u32 ioc_state; - - if (ioc->remove_host) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " - "removed\n", __func__, ioc->name)); - return 1; - } else if (ioc->pci_error_recovery) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " - "error recovery\n", __func__, ioc->name)); - return 1; - } - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " - "operational\n", __func__, ioc->name)); - return 1; - } - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return 1; - } - mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); - handle = le16_to_cpu(mpi_request_tm->DevHandle); - if (handle != le16_to_cpu(mpi_reply->DevHandle)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: " - "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle, - le16_to_cpu(mpi_reply->DevHandle), smid)); - return 0; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", ioc->name, - handle, smid, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - - smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); - if (!smid_sas_ctrl) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - return 1; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), " - "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl, - ioc->tm_sas_control_cb_idx)); - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); - memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; - mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request->DevHandle = mpi_request_tm->DevHandle; - mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); - - return _scsih_check_for_pending_tm(ioc, smid); -} - -/** - * _scsih_check_for_pending_tm - check for pending task management - * @ioc: per adapter object - * @smid: system request message index - * - * This will check delayed target reset list, and feed the - * next reqeust. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - struct _tr_list *delayed_tr; - - if (!list_empty(&ioc->delayed_tr_volume_list)) { - delayed_tr = list_entry(ioc->delayed_tr_volume_list.next, - struct _tr_list, list); - mpt2sas_base_free_smid(ioc, smid); - _scsih_tm_tr_volume_send(ioc, delayed_tr->handle); - list_del(&delayed_tr->list); - kfree(delayed_tr); - return 0; - } - - if (!list_empty(&ioc->delayed_tr_list)) { - delayed_tr = list_entry(ioc->delayed_tr_list.next, - struct _tr_list, list); - mpt2sas_base_free_smid(ioc, smid); - _scsih_tm_tr_send(ioc, delayed_tr->handle); - list_del(&delayed_tr->list); - kfree(delayed_tr); - return 0; - } - - return 1; -} - -/** - * _scsih_check_topo_delete_events - sanity check on topo events - * @ioc: per adapter object - * @event_data: the event data payload - * - * This routine added to better handle cable breaker. - * - * This handles the case where driver receives multiple expander - * add and delete events in a single shot. When there is a delete event - * the routine will void any pending add events waiting in the event queue. - * - * Return nothing. - */ -static void -_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataSasTopologyChangeList_t *event_data) -{ - struct fw_event_work *fw_event; - Mpi2EventDataSasTopologyChangeList_t *local_event_data; - u16 expander_handle; - struct _sas_node *sas_expander; - unsigned long flags; - int i, reason_code; - u16 handle; - - for (i = 0 ; i < event_data->NumEntries; i++) { - handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); - if (!handle) - continue; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; - if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING) - _scsih_tm_tr_send(ioc, handle); - } - - expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); - if (expander_handle < ioc->sas_hba.num_phys) { - _scsih_block_io_to_children_attached_directly(ioc, event_data); - return; - } - if (event_data->ExpStatus == - MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING) { - /* put expander attached devices into blocking state */ - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, - expander_handle); - _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - do { - handle = find_first_bit(ioc->blocking_handles, - ioc->facts.MaxDevHandle); - if (handle < ioc->facts.MaxDevHandle) - _scsih_block_io_device(ioc, handle); - } while (test_and_clear_bit(handle, ioc->blocking_handles)); - } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING) - _scsih_block_io_to_children_attached_directly(ioc, event_data); - - if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) - return; - - /* mark ignore flag for pending events */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - list_for_each_entry(fw_event, &ioc->fw_event_list, list) { - if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || - fw_event->ignore) - continue; - local_event_data = (Mpi2EventDataSasTopologyChangeList_t *) - fw_event->event_data; - if (local_event_data->ExpStatus == - MPI2_EVENT_SAS_TOPO_ES_ADDED || - local_event_data->ExpStatus == - MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { - if (le16_to_cpu(local_event_data->ExpanderDevHandle) == - expander_handle) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "setting ignoring flag\n", ioc->name)); - fw_event->ignore = 1; - } - } - } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); -} - -/** - * _scsih_set_volume_delete_flag - setting volume delete flag - * @ioc: per adapter object - * @handle: device handle - * - * This - * Return nothing. - */ -static void -_scsih_set_volume_delete_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _raid_device *raid_device; - struct MPT2SAS_TARGET *sas_target_priv_data; - unsigned long flags; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - if (raid_device && raid_device->starget && - raid_device->starget->hostdata) { - sas_target_priv_data = - raid_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "setting delete flag: handle(0x%04x), " - "wwid(0x%016llx)\n", ioc->name, handle, - (unsigned long long) raid_device->wwid)); - } - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); -} - -/** - * _scsih_set_volume_handle_for_tr - set handle for target reset to volume - * @handle: input handle - * @a: handle for volume a - * @b: handle for volume b - * - * IR firmware only supports two raid volumes. The purpose of this - * routine is to set the volume handle in either a or b. When the given - * input handle is non-zero, or when a and b have not been set before. - */ -static void -_scsih_set_volume_handle_for_tr(u16 handle, u16 *a, u16 *b) -{ - if (!handle || handle == *a || handle == *b) - return; - if (!*a) - *a = handle; - else if (!*b) - *b = handle; -} - -/** - * _scsih_check_ir_config_unhide_events - check for UNHIDE events - * @ioc: per adapter object - * @event_data: the event data payload - * Context: interrupt time. - * - * This routine will send target reset to volume, followed by target - * resets to the PDs. This is called when a PD has been removed, or - * volume has been deleted or removed. When the target reset is sent - * to volume, the PD target resets need to be queued to start upon - * completion of the volume target reset. - * - * Return nothing. - */ -static void -_scsih_check_ir_config_unhide_events(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataIrConfigChangeList_t *event_data) -{ - Mpi2EventIrConfigElement_t *element; - int i; - u16 handle, volume_handle, a, b; - struct _tr_list *delayed_tr; - - a = 0; - b = 0; - - if (ioc->is_warpdrive) - return; - - /* Volume Resets for Deleted or Removed */ - element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - for (i = 0; i < event_data->NumElements; i++, element++) { - if (element->ReasonCode == - MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED || - element->ReasonCode == - MPI2_EVENT_IR_CHANGE_RC_REMOVED) { - volume_handle = le16_to_cpu(element->VolDevHandle); - _scsih_set_volume_delete_flag(ioc, volume_handle); - _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); - } - } - - /* Volume Resets for UNHIDE events */ - element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - for (i = 0; i < event_data->NumElements; i++, element++) { - if (le32_to_cpu(event_data->Flags) & - MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) - continue; - if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_UNHIDE) { - volume_handle = le16_to_cpu(element->VolDevHandle); - _scsih_set_volume_handle_for_tr(volume_handle, &a, &b); - } - } - - if (a) - _scsih_tm_tr_volume_send(ioc, a); - if (b) - _scsih_tm_tr_volume_send(ioc, b); - - /* PD target resets */ - element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - for (i = 0; i < event_data->NumElements; i++, element++) { - if (element->ReasonCode != MPI2_EVENT_IR_CHANGE_RC_UNHIDE) - continue; - handle = le16_to_cpu(element->PhysDiskDevHandle); - volume_handle = le16_to_cpu(element->VolDevHandle); - clear_bit(handle, ioc->pd_handles); - if (!volume_handle) - _scsih_tm_tr_send(ioc, handle); - else if (volume_handle == a || volume_handle == b) { - delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); - BUG_ON(!delayed_tr); - INIT_LIST_HEAD(&delayed_tr->list); - delayed_tr->handle = handle; - list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, - handle)); - } else - _scsih_tm_tr_send(ioc, handle); - } -} - - -/** - * _scsih_check_volume_delete_events - set delete flag for volumes - * @ioc: per adapter object - * @event_data: the event data payload - * Context: interrupt time. - * - * This will handle the case when the cable connected to entire volume is - * pulled. We will take care of setting the deleted flag so normal IO will - * not be sent. - * - * Return nothing. - */ -static void -_scsih_check_volume_delete_events(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataIrVolume_t *event_data) -{ - u32 state; - - if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) - return; - state = le32_to_cpu(event_data->NewValue); - if (state == MPI2_RAID_VOL_STATE_MISSING || state == - MPI2_RAID_VOL_STATE_FAILED) - _scsih_set_volume_delete_flag(ioc, - le16_to_cpu(event_data->VolDevHandle)); -} - -/** - * _scsih_temp_threshold_events - display temperature threshold exceeded events - * @ioc: per adapter object - * @event_data: the temp threshold event data - * Context: interrupt time. - * - * Return nothing. - */ -static void -_scsih_temp_threshold_events(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataTemperature_t *event_data) -{ - if (ioc->temp_sensors_count >= event_data->SensorNum) { - printk(MPT2SAS_ERR_FMT "Temperature Threshold flags %s%s%s%s" - " exceeded for Sensor: %d !!!\n", ioc->name, - ((le16_to_cpu(event_data->Status) & 0x1) == 1) ? "0 " : " ", - ((le16_to_cpu(event_data->Status) & 0x2) == 2) ? "1 " : " ", - ((le16_to_cpu(event_data->Status) & 0x4) == 4) ? "2 " : " ", - ((le16_to_cpu(event_data->Status) & 0x8) == 8) ? "3 " : " ", - event_data->SensorNum); - printk(MPT2SAS_ERR_FMT "Current Temp In Celsius: %d\n", - ioc->name, event_data->CurrentTemperature); - } -} - -/** - * _scsih_flush_running_cmds - completing outstanding commands. - * @ioc: per adapter object - * - * The flushing out of all pending scmd commands following host reset, - * where all IO is dropped to the floor. - * - * Return nothing. - */ -static void -_scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) -{ - struct scsi_cmnd *scmd; - u16 smid; - u16 count = 0; - - for (smid = 1; smid <= ioc->scsiio_depth; smid++) { - scmd = _scsih_scsi_lookup_get_clear(ioc, smid); - if (!scmd) - continue; - count++; - mpt2sas_base_free_smid(ioc, smid); - scsi_dma_unmap(scmd); - if (ioc->pci_error_recovery) - scmd->result = DID_NO_CONNECT << 16; - else - scmd->result = DID_RESET << 16; - scmd->scsi_done(scmd); - } - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "completing %d cmds\n", - ioc->name, count)); -} - -/** - * _scsih_setup_eedp - setup MPI request for EEDP transfer - * @scmd: pointer to scsi command object - * @mpi_request: pointer to the SCSI_IO reqest message frame - * - * Supporting protection 1 and 3. - * - * Returns nothing - */ -static void -_scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request) -{ - u16 eedp_flags; - unsigned char prot_op = scsi_get_prot_op(scmd); - unsigned char prot_type = scsi_get_prot_type(scmd); - - if (prot_type == SCSI_PROT_DIF_TYPE0 || prot_op == SCSI_PROT_NORMAL) - return; - - if (prot_op == SCSI_PROT_READ_STRIP) - eedp_flags = MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP; - else if (prot_op == SCSI_PROT_WRITE_INSERT) - eedp_flags = MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; - else - return; - - switch (prot_type) { - case SCSI_PROT_DIF_TYPE1: - case SCSI_PROT_DIF_TYPE2: - - /* - * enable ref/guard checking - * auto increment ref tag - */ - eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | - MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - mpi_request->CDB.EEDP32.PrimaryReferenceTag = - cpu_to_be32(scsi_get_lba(scmd)); - break; - - case SCSI_PROT_DIF_TYPE3: - - /* - * enable guard checking - */ - eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; - break; - } - mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size); - mpi_request->EEDPFlags = cpu_to_le16(eedp_flags); -} - -/** - * _scsih_eedp_error_handling - return sense code for EEDP errors - * @scmd: pointer to scsi command object - * @ioc_status: ioc status - * - * Returns nothing - */ -static void -_scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) -{ - u8 ascq; - - switch (ioc_status) { - case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: - ascq = 0x01; - break; - case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: - ascq = 0x02; - break; - case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: - ascq = 0x03; - break; - default: - ascq = 0x00; - break; - } - - scsi_build_sense_buffer(0, scmd->sense_buffer, ILLEGAL_REQUEST, 0x10, ascq); - scmd->result = DRIVER_SENSE << 24 | (DID_ABORT << 16) | - SAM_STAT_CHECK_CONDITION; -} - -/** - * _scsih_scsi_direct_io_get - returns direct io flag - * @ioc: per adapter object - * @smid: system request message index - * - * Returns the smid stored scmd pointer. - */ -static inline u8 -_scsih_scsi_direct_io_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) -{ - return ioc->scsi_lookup[smid - 1].direct_io; -} - -/** - * _scsih_scsi_direct_io_set - sets direct io flag - * @ioc: per adapter object - * @smid: system request message index - * @direct_io: Zero or non-zero value to set in the direct_io flag - * - * Returns Nothing. - */ -static inline void -_scsih_scsi_direct_io_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 direct_io) -{ - ioc->scsi_lookup[smid - 1].direct_io = direct_io; -} - - -/** - * _scsih_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O - * @ioc: per adapter object - * @scmd: pointer to scsi command object - * @raid_device: pointer to raid device data structure - * @mpi_request: pointer to the SCSI_IO reqest message frame - * @smid: system request message index - * - * Returns nothing - */ -static void -_scsih_setup_direct_io(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, - struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, - u16 smid) -{ - sector_t v_lba, p_lba, stripe_off, column, io_size; - u32 stripe_sz, stripe_exp; - u8 num_pds, cmd = scmd->cmnd[0]; - - if (cmd != READ_10 && cmd != WRITE_10 && - cmd != READ_16 && cmd != WRITE_16) - return; - - if (cmd == READ_10 || cmd == WRITE_10) - v_lba = get_unaligned_be32(&mpi_request->CDB.CDB32[2]); - else - v_lba = get_unaligned_be64(&mpi_request->CDB.CDB32[2]); - - io_size = scsi_bufflen(scmd) >> raid_device->block_exponent; - - if (v_lba + io_size - 1 > raid_device->max_lba) - return; - - stripe_sz = raid_device->stripe_sz; - stripe_exp = raid_device->stripe_exponent; - stripe_off = v_lba & (stripe_sz - 1); - - /* Return unless IO falls within a stripe */ - if (stripe_off + io_size > stripe_sz) - return; - - num_pds = raid_device->num_pds; - p_lba = v_lba >> stripe_exp; - column = sector_div(p_lba, num_pds); - p_lba = (p_lba << stripe_exp) + stripe_off; - - mpi_request->DevHandle = cpu_to_le16(raid_device->pd_handle[column]); - - if (cmd == READ_10 || cmd == WRITE_10) - put_unaligned_be32(lower_32_bits(p_lba), - &mpi_request->CDB.CDB32[2]); - else - put_unaligned_be64(p_lba, &mpi_request->CDB.CDB32[2]); - - _scsih_scsi_direct_io_set(ioc, smid, 1); -} - -/** - * _scsih_qcmd - main scsi request entry point - * @scmd: pointer to scsi command object - * @done: function pointer to be invoked on completion - * - * The callback index is set inside `ioc->scsi_io_cb_idx`. - * - * Returns 0 on success. If there's a failure, return either: - * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or - * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full - */ -static int -_scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct _raid_device *raid_device; - Mpi2SCSIIORequest_t *mpi_request; - u32 mpi_control; - u16 smid; - - sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; - } - - if (ioc->pci_error_recovery || ioc->remove_host) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; - } - - sas_target_priv_data = sas_device_priv_data->sas_target; - /* invalid device handle */ - if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; - } - - /* host recovery or link resets sent via IOCTLs */ - if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) - return SCSI_MLQUEUE_HOST_BUSY; - /* device busy with task management */ - else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) - return SCSI_MLQUEUE_DEVICE_BUSY; - /* device has been deleted */ - else if (sas_target_priv_data->deleted) { - scmd->result = DID_NO_CONNECT << 16; - scmd->scsi_done(scmd); - return 0; - } - - if (scmd->sc_data_direction == DMA_FROM_DEVICE) - mpi_control = MPI2_SCSIIO_CONTROL_READ; - else if (scmd->sc_data_direction == DMA_TO_DEVICE) - mpi_control = MPI2_SCSIIO_CONTROL_WRITE; - else - mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; - - /* set tags */ - mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - - /* Make sure Device is not raid volume. - * We do not expose raid functionality to upper layer for warpdrive. - */ - if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) && - sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) - mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; - - smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - goto out; - } - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); - _scsih_setup_eedp(scmd, mpi_request); - if (scmd->cmd_len == 32) - mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; - mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) - mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; - else - mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; - mpi_request->DevHandle = - cpu_to_le16(sas_device_priv_data->sas_target->handle); - mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); - mpi_request->Control = cpu_to_le32(mpi_control); - mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len); - mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR; - mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; - mpi_request->SenseBufferLowAddress = - mpt2sas_base_get_sense_buffer_dma(ioc, smid); - mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; - mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI + - MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR); - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) - mpi_request->LUN); - memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); - - if (!mpi_request->DataLength) { - mpt2sas_base_build_zero_len_sge(ioc, &mpi_request->SGL); - } else { - if (_scsih_build_scatter_gather(ioc, scmd, smid)) { - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - } - - raid_device = sas_target_priv_data->raid_device; - if (raid_device && raid_device->direct_io_enabled) - _scsih_setup_direct_io(ioc, scmd, raid_device, mpi_request, - smid); - - if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) - mpt2sas_base_put_smid_scsi_io(ioc, smid, - le16_to_cpu(mpi_request->DevHandle)); - else - mpt2sas_base_put_smid_default(ioc, smid); - return 0; - - out: - return SCSI_MLQUEUE_HOST_BUSY; -} - -/** - * _scsih_normalize_sense - normalize descriptor and fixed format sense data - * @sense_buffer: sense data returned by target - * @data: normalized skey/asc/ascq - * - * Return nothing. - */ -static void -_scsih_normalize_sense(char *sense_buffer, struct sense_info *data) -{ - if ((sense_buffer[0] & 0x7F) >= 0x72) { - /* descriptor format */ - data->skey = sense_buffer[1] & 0x0F; - data->asc = sense_buffer[2]; - data->ascq = sense_buffer[3]; - } else { - /* fixed format */ - data->skey = sense_buffer[2] & 0x0F; - data->asc = sense_buffer[12]; - data->ascq = sense_buffer[13]; - } -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request - * @ioc: per adapter object - * @scmd: pointer to scsi command object - * @mpi_reply: reply mf payload returned from firmware - * - * scsi_status - SCSI Status code returned from target device - * scsi_state - state info associated with SCSI_IO determined by ioc - * ioc_status - ioc supplied status info - * - * Return nothing. - */ -static void -_scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, - Mpi2SCSIIOReply_t *mpi_reply, u16 smid) -{ - u32 response_info; - u8 *response_bytes; - u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & - MPI2_IOCSTATUS_MASK; - u8 scsi_state = mpi_reply->SCSIState; - u8 scsi_status = mpi_reply->SCSIStatus; - char *desc_ioc_state = NULL; - char *desc_scsi_status = NULL; - char *desc_scsi_state = ioc->tmp_string; - u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); - struct _sas_device *sas_device = NULL; - unsigned long flags; - struct scsi_target *starget = scmd->device->sdev_target; - struct MPT2SAS_TARGET *priv_target = starget->hostdata; - char *device_str = NULL; - - if (!priv_target) - return; - - if (ioc->hide_ir_msg) - device_str = "WarpDrive"; - else - device_str = "volume"; - - if (log_info == 0x31170000) - return; - - switch (ioc_status) { - case MPI2_IOCSTATUS_SUCCESS: - desc_ioc_state = "success"; - break; - case MPI2_IOCSTATUS_INVALID_FUNCTION: - desc_ioc_state = "invalid function"; - break; - case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: - desc_ioc_state = "scsi recovered error"; - break; - case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: - desc_ioc_state = "scsi invalid dev handle"; - break; - case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: - desc_ioc_state = "scsi device not there"; - break; - case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: - desc_ioc_state = "scsi data overrun"; - break; - case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: - desc_ioc_state = "scsi data underrun"; - break; - case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: - desc_ioc_state = "scsi io data error"; - break; - case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: - desc_ioc_state = "scsi protocol error"; - break; - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: - desc_ioc_state = "scsi task terminated"; - break; - case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: - desc_ioc_state = "scsi residual mismatch"; - break; - case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: - desc_ioc_state = "scsi task mgmt failed"; - break; - case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: - desc_ioc_state = "scsi ioc terminated"; - break; - case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: - desc_ioc_state = "scsi ext terminated"; - break; - case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: - desc_ioc_state = "eedp guard error"; - break; - case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: - desc_ioc_state = "eedp ref tag error"; - break; - case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: - desc_ioc_state = "eedp app tag error"; - break; - default: - desc_ioc_state = "unknown"; - break; - } - - switch (scsi_status) { - case MPI2_SCSI_STATUS_GOOD: - desc_scsi_status = "good"; - break; - case MPI2_SCSI_STATUS_CHECK_CONDITION: - desc_scsi_status = "check condition"; - break; - case MPI2_SCSI_STATUS_CONDITION_MET: - desc_scsi_status = "condition met"; - break; - case MPI2_SCSI_STATUS_BUSY: - desc_scsi_status = "busy"; - break; - case MPI2_SCSI_STATUS_INTERMEDIATE: - desc_scsi_status = "intermediate"; - break; - case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET: - desc_scsi_status = "intermediate condmet"; - break; - case MPI2_SCSI_STATUS_RESERVATION_CONFLICT: - desc_scsi_status = "reservation conflict"; - break; - case MPI2_SCSI_STATUS_COMMAND_TERMINATED: - desc_scsi_status = "command terminated"; - break; - case MPI2_SCSI_STATUS_TASK_SET_FULL: - desc_scsi_status = "task set full"; - break; - case MPI2_SCSI_STATUS_ACA_ACTIVE: - desc_scsi_status = "aca active"; - break; - case MPI2_SCSI_STATUS_TASK_ABORTED: - desc_scsi_status = "task aborted"; - break; - default: - desc_scsi_status = "unknown"; - break; - } - - desc_scsi_state[0] = '\0'; - if (!scsi_state) - desc_scsi_state = " "; - if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) - strcat(desc_scsi_state, "response info "); - if (scsi_state & MPI2_SCSI_STATE_TERMINATED) - strcat(desc_scsi_state, "state terminated "); - if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS) - strcat(desc_scsi_state, "no status "); - if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED) - strcat(desc_scsi_state, "autosense failed "); - if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) - strcat(desc_scsi_state, "autosense valid "); - - scsi_print_command(scmd); - - if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { - printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name, - device_str, (unsigned long long)priv_target->sas_address); - } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); - if (sas_device) { - printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), " - "phy(%d)\n", ioc->name, sas_device->sas_address, - sas_device->phy); - printk(MPT2SAS_WARN_FMT - "\tenclosure_logical_id(0x%016llx), slot(%d)\n", - ioc->name, sas_device->enclosure_logical_id, - sas_device->slot); - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } - - printk(MPT2SAS_WARN_FMT "\thandle(0x%04x), ioc_status(%s)(0x%04x), " - "smid(%d)\n", ioc->name, le16_to_cpu(mpi_reply->DevHandle), - desc_ioc_state, ioc_status, smid); - printk(MPT2SAS_WARN_FMT "\trequest_len(%d), underflow(%d), " - "resid(%d)\n", ioc->name, scsi_bufflen(scmd), scmd->underflow, - scsi_get_resid(scmd)); - printk(MPT2SAS_WARN_FMT "\ttag(%d), transfer_count(%d), " - "sc->result(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->TaskTag), - le32_to_cpu(mpi_reply->TransferCount), scmd->result); - printk(MPT2SAS_WARN_FMT "\tscsi_status(%s)(0x%02x), " - "scsi_state(%s)(0x%02x)\n", ioc->name, desc_scsi_status, - scsi_status, desc_scsi_state, scsi_state); - - if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { - struct sense_info data; - _scsih_normalize_sense(scmd->sense_buffer, &data); - printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " - "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey, - data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); - } - - if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { - response_info = le32_to_cpu(mpi_reply->ResponseInfo); - response_bytes = (u8 *)&response_info; - _scsih_response_code(ioc, response_bytes[0]); - } -} -#endif - -/** - * _scsih_turn_on_pfa_led - illuminate PFA LED - * @ioc: per adapter object - * @handle: device handle - * Context: process - * - * Return nothing. - */ -static void -_scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - Mpi2SepReply_t mpi_reply; - Mpi2SepRequest_t mpi_request; - struct _sas_device *sas_device; - - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) - return; - - memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; - mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; - mpi_request.SlotStatus = - cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); - mpi_request.DevHandle = cpu_to_le16(handle); - mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; - if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - return; - } - sas_device->pfa_led_on = 1; - - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); - return; - } -} - -/** - * _scsih_turn_off_pfa_led - turn off PFA LED - * @ioc: per adapter object - * @sas_device: sas device whose PFA LED has to turned off - * Context: process - * - * Return nothing. - */ -static void -_scsih_turn_off_pfa_led(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device) -{ - Mpi2SepReply_t mpi_reply; - Mpi2SepRequest_t mpi_request; - - memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; - mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; - mpi_request.SlotStatus = 0; - mpi_request.Slot = cpu_to_le16(sas_device->slot); - mpi_request.DevHandle = 0; - mpi_request.EnclosureHandle = cpu_to_le16(sas_device->enclosure_handle); - mpi_request.Flags = MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS; - if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - return; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "enclosure_processor: " - "ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, - le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); - return; - } -} - -/** - * _scsih_send_event_to_turn_on_pfa_led - fire delayed event - * @ioc: per adapter object - * @handle: device handle - * Context: interrupt. - * - * Return nothing. - */ -static void -_scsih_send_event_to_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct fw_event_work *fw_event; - - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); - if (!fw_event) - return; - fw_event->event = MPT2SAS_TURN_ON_PFA_LED; - fw_event->device_handle = handle; - fw_event->ioc = ioc; - _scsih_fw_event_add(ioc, fw_event); -} - -/** - * _scsih_smart_predicted_fault - process smart errors - * @ioc: per adapter object - * @handle: device handle - * Context: interrupt. - * - * Return nothing. - */ -static void -_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct scsi_target *starget; - struct MPT2SAS_TARGET *sas_target_priv_data; - Mpi2EventNotificationReply_t *event_reply; - Mpi2EventDataSasDeviceStatusChange_t *event_data; - struct _sas_device *sas_device; - ssize_t sz; - unsigned long flags; - - /* only handle non-raid devices */ - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - - if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || - ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - starget_printk(KERN_WARNING, starget, "predicted fault\n"); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) - _scsih_send_event_to_turn_on_pfa_led(ioc, handle); - - /* insert into event log */ - sz = offsetof(Mpi2EventNotificationReply_t, EventData) + - sizeof(Mpi2EventDataSasDeviceStatusChange_t); - event_reply = kzalloc(sz, GFP_ATOMIC); - if (!event_reply) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; - event_reply->Event = - cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); - event_reply->MsgLength = sz/4; - event_reply->EventDataLength = - cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4); - event_data = (Mpi2EventDataSasDeviceStatusChange_t *) - event_reply->EventData; - event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA; - event_data->ASC = 0x5D; - event_data->DevHandle = cpu_to_le16(handle); - event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); - mpt2sas_ctl_add_to_event_log(ioc, event_reply); - kfree(event_reply); -} - -/** - * _scsih_io_done - scsi request callback - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Callback handler when using _scsih_qcmd. - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) -{ - Mpi2SCSIIORequest_t *mpi_request; - Mpi2SCSIIOReply_t *mpi_reply; - struct scsi_cmnd *scmd; - u16 ioc_status; - u32 xfer_cnt; - u8 scsi_state; - u8 scsi_status; - u32 log_info; - struct MPT2SAS_DEVICE *sas_device_priv_data; - u32 response_code = 0; - unsigned long flags; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - scmd = _scsih_scsi_lookup_get_clear(ioc, smid); - if (scmd == NULL) - return 1; - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - - if (mpi_reply == NULL) { - scmd->result = DID_OK << 16; - goto out; - } - - sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target || - sas_device_priv_data->sas_target->deleted) { - scmd->result = DID_NO_CONNECT << 16; - goto out; - } - ioc_status = le16_to_cpu(mpi_reply->IOCStatus); - /* - * WARPDRIVE: If direct_io is set then it is directIO, - * the failed direct I/O should be redirected to volume - */ - if (_scsih_scsi_direct_io_get(ioc, smid) && - ((ioc_status & MPI2_IOCSTATUS_MASK) - != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - ioc->scsi_lookup[smid - 1].scmd = scmd; - _scsih_scsi_direct_io_set(ioc, smid, 0); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); - mpi_request->DevHandle = - cpu_to_le16(sas_device_priv_data->sas_target->handle); - mpt2sas_base_put_smid_scsi_io(ioc, smid, - sas_device_priv_data->sas_target->handle); - return 0; - } - - - /* turning off TLR */ - scsi_state = mpi_reply->SCSIState; - if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) - response_code = - le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; - if (!sas_device_priv_data->tlr_snoop_check) { - sas_device_priv_data->tlr_snoop_check++; - /* Make sure Device is not raid volume. - * We do not expose raid functionality to upper layer for warpdrive. - */ - if (!ioc->is_warpdrive && !_scsih_is_raid(&scmd->device->sdev_gendev) && - sas_is_tlr_enabled(scmd->device) && - response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { - sas_disable_tlr(scmd->device); - sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); - } - } - - xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); - scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); - if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) - log_info = le32_to_cpu(mpi_reply->IOCLogInfo); - else - log_info = 0; - ioc_status &= MPI2_IOCSTATUS_MASK; - scsi_status = mpi_reply->SCSIStatus; - - if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && - (scsi_status == MPI2_SCSI_STATUS_BUSY || - scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT || - scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) { - ioc_status = MPI2_IOCSTATUS_SUCCESS; - } - - if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { - struct sense_info data; - const void *sense_data = mpt2sas_base_get_sense_buffer(ioc, - smid); - u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, - le32_to_cpu(mpi_reply->SenseCount)); - memcpy(scmd->sense_buffer, sense_data, sz); - _scsih_normalize_sense(scmd->sense_buffer, &data); - /* failure prediction threshold exceeded */ - if (data.asc == 0x5D) - _scsih_smart_predicted_fault(ioc, - le16_to_cpu(mpi_reply->DevHandle)); - } - - switch (ioc_status) { - case MPI2_IOCSTATUS_BUSY: - case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: - scmd->result = SAM_STAT_BUSY; - break; - - case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: - scmd->result = DID_NO_CONNECT << 16; - break; - - case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: - if (sas_device_priv_data->block) { - scmd->result = DID_TRANSPORT_DISRUPTED << 16; - goto out; - } - if (log_info == 0x32010081) { - scmd->result = DID_RESET << 16; - break; - } - scmd->result = DID_SOFT_ERROR << 16; - break; - case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: - case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: - scmd->result = DID_RESET << 16; - break; - - case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: - if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt)) - scmd->result = DID_SOFT_ERROR << 16; - else - scmd->result = (DID_OK << 16) | scsi_status; - break; - - case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: - scmd->result = (DID_OK << 16) | scsi_status; - - if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)) - break; - - if (xfer_cnt < scmd->underflow) { - if (scsi_status == SAM_STAT_BUSY) - scmd->result = SAM_STAT_BUSY; - else - scmd->result = DID_SOFT_ERROR << 16; - } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | - MPI2_SCSI_STATE_NO_SCSI_STATUS)) - scmd->result = DID_SOFT_ERROR << 16; - else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) - scmd->result = DID_RESET << 16; - else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { - mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID; - mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION; - scmd->result = (DRIVER_SENSE << 24) | - SAM_STAT_CHECK_CONDITION; - scmd->sense_buffer[0] = 0x70; - scmd->sense_buffer[2] = ILLEGAL_REQUEST; - scmd->sense_buffer[12] = 0x20; - scmd->sense_buffer[13] = 0; - } - break; - - case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: - scsi_set_resid(scmd, 0); - case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: - case MPI2_IOCSTATUS_SUCCESS: - scmd->result = (DID_OK << 16) | scsi_status; - if (response_code == - MPI2_SCSITASKMGMT_RSP_INVALID_FRAME || - (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | - MPI2_SCSI_STATE_NO_SCSI_STATUS))) - scmd->result = DID_SOFT_ERROR << 16; - else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) - scmd->result = DID_RESET << 16; - break; - - case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: - case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: - case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: - _scsih_eedp_error_handling(scmd, ioc_status); - break; - case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: - case MPI2_IOCSTATUS_INVALID_FUNCTION: - case MPI2_IOCSTATUS_INVALID_SGL: - case MPI2_IOCSTATUS_INTERNAL_ERROR: - case MPI2_IOCSTATUS_INVALID_FIELD: - case MPI2_IOCSTATUS_INVALID_STATE: - case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: - case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: - default: - scmd->result = DID_SOFT_ERROR << 16; - break; - - } - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY)) - _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid); -#endif - - out: - scsi_dma_unmap(scmd); - scmd->scsi_done(scmd); - return 1; -} - -/** - * _scsih_sas_host_refresh - refreshing sas host object contents - * @ioc: per adapter object - * Context: user - * - * During port enable, fw will send topology events for every device. Its - * possible that the handles may change from the previous setting, so this - * code keeping handles updating if changed. - * - * Return nothing. - */ -static void -_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc) -{ - u16 sz; - u16 ioc_status; - int i; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; - u16 attached_handle; - u8 link_rate; - - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT - "updating handles for sas_host(0x%016llx)\n", - ioc->name, (unsigned long long)ioc->sas_hba.sas_address)); - - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys - * sizeof(Mpi2SasIOUnit0PhyData_t)); - sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, - sas_iounit_pg0, sz)) != 0) - goto out; - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - goto out; - for (i = 0; i < ioc->sas_hba.num_phys ; i++) { - link_rate = sas_iounit_pg0->PhyData[i].NegotiatedLinkRate >> 4; - if (i == 0) - ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> - PhyData[0].ControllerDevHandle); - ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; - attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i]. - AttachedDevHandle); - if (attached_handle && link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) - link_rate = MPI2_SAS_NEG_LINK_RATE_1_5; - mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address, - attached_handle, i, link_rate); - } - out: - kfree(sas_iounit_pg0); -} - -/** - * _scsih_sas_host_add - create sas host object - * @ioc: per adapter object - * - * Creating host side data object, stored in ioc->sas_hba - * - * Return nothing. - */ -static void -_scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc) -{ - int i; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; - Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; - Mpi2SasPhyPage0_t phy_pg0; - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2SasEnclosurePage0_t enclosure_pg0; - u16 ioc_status; - u16 sz; - u16 device_missing_delay; - - mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys); - if (!ioc->sas_hba.num_phys) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - /* sas_iounit page 0 */ - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit0PhyData_t)); - sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, - sas_iounit_pg0, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - - /* sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); - sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg1) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, - sas_iounit_pg1, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - - ioc->io_missing_delay = - le16_to_cpu(sas_iounit_pg1->IODeviceMissingDelay); - device_missing_delay = - le16_to_cpu(sas_iounit_pg1->ReportDeviceMissingDelay); - if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) - ioc->device_missing_delay = (device_missing_delay & - MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; - else - ioc->device_missing_delay = device_missing_delay & - MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; - - ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev; - ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys, - sizeof(struct _sas_phy), GFP_KERNEL); - if (!ioc->sas_hba.phy) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - for (i = 0; i < ioc->sas_hba.num_phys ; i++) { - if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, - i))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - - if (i == 0) - ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0-> - PhyData[0].ControllerDevHandle); - ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle; - ioc->sas_hba.phy[i].phy_id = i; - mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], - phy_pg0, ioc->sas_hba.parent_dev); - } - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out; - } - ioc->sas_hba.enclosure_handle = - le16_to_cpu(sas_device_pg0.EnclosureHandle); - ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - printk(MPT2SAS_INFO_FMT "host_add: handle(0x%04x), " - "sas_addr(0x%016llx), phys(%d)\n", ioc->name, ioc->sas_hba.handle, - (unsigned long long) ioc->sas_hba.sas_address, - ioc->sas_hba.num_phys) ; - - if (ioc->sas_hba.enclosure_handle) { - if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, - MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - ioc->sas_hba.enclosure_handle))) { - ioc->sas_hba.enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - } - } - - out: - kfree(sas_iounit_pg1); - kfree(sas_iounit_pg0); -} - -/** - * _scsih_expander_add - creating expander object - * @ioc: per adapter object - * @handle: expander handle - * - * Creating expander object, stored in ioc->sas_expander_list. - * - * Return 0 for success, else error. - */ -static int -_scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_node *sas_expander; - Mpi2ConfigReply_t mpi_reply; - Mpi2ExpanderPage0_t expander_pg0; - Mpi2ExpanderPage1_t expander_pg1; - Mpi2SasEnclosurePage0_t enclosure_pg0; - u32 ioc_status; - u16 parent_handle; - u64 sas_address, sas_address_parent = 0; - int i; - unsigned long flags; - struct _sas_port *mpt2sas_port = NULL; - int rc = 0; - - if (!handle) - return -1; - - if (ioc->shost_recovery || ioc->pci_error_recovery) - return -1; - - if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, - MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - /* handle out of order topology events */ - parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle); - if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent) - != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - if (sas_address_parent != ioc->sas_hba.sas_address) { - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, - sas_address_parent); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (!sas_expander) { - rc = _scsih_expander_add(ioc, parent_handle); - if (rc != 0) - return rc; - } - } - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_address = le64_to_cpu(expander_pg0.SASAddress); - sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, - sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - if (sas_expander) - return 0; - - sas_expander = kzalloc(sizeof(struct _sas_node), - GFP_KERNEL); - if (!sas_expander) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - sas_expander->handle = handle; - sas_expander->num_phys = expander_pg0.NumPhys; - sas_expander->sas_address_parent = sas_address_parent; - sas_expander->sas_address = sas_address; - - printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x)," - " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name, - handle, parent_handle, (unsigned long long) - sas_expander->sas_address, sas_expander->num_phys); - - if (!sas_expander->num_phys) - goto out_fail; - sas_expander->phy = kcalloc(sas_expander->num_phys, - sizeof(struct _sas_phy), GFP_KERNEL); - if (!sas_expander->phy) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -1; - goto out_fail; - } - - INIT_LIST_HEAD(&sas_expander->sas_port_list); - mpt2sas_port = mpt2sas_transport_port_add(ioc, handle, - sas_address_parent); - if (!mpt2sas_port) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -1; - goto out_fail; - } - sas_expander->parent_dev = &mpt2sas_port->rphy->dev; - - for (i = 0 ; i < sas_expander->num_phys ; i++) { - if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, - &expander_pg1, i, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -1; - goto out_fail; - } - sas_expander->phy[i].handle = handle; - sas_expander->phy[i].phy_id = i; - - if ((mpt2sas_transport_add_expander_phy(ioc, - &sas_expander->phy[i], expander_pg1, - sas_expander->parent_dev))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -1; - goto out_fail; - } - } - - if (sas_expander->enclosure_handle) { - if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_expander->enclosure_handle))) { - sas_expander->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - } - } - - _scsih_expander_node_add(ioc, sas_expander); - return 0; - - out_fail: - - if (mpt2sas_port) - mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_address_parent); - kfree(sas_expander); - return rc; -} - -/** - * _scsih_done - scsih callback handler. - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Callback handler when sending internal generated message frames. - * The callback index passed is `ioc->scsih_cb_idx` - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -static u8 -_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED) - return 1; - if (ioc->scsih_cmds.smid != smid) - return 1; - ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE; - if (mpi_reply) { - memcpy(ioc->scsih_cmds.reply, mpi_reply, - mpi_reply->MsgLength*4); - ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID; - } - ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING; - complete(&ioc->scsih_cmds.done); - return 1; -} - -/** - * mpt2sas_expander_remove - removing expander object - * @ioc: per adapter object - * @sas_address: expander sas_address - * - * Return nothing. - */ -void -mpt2sas_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) -{ - struct _sas_node *sas_expander; - unsigned long flags; - - if (ioc->shost_recovery) - return; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, - sas_address); - if (sas_expander) - list_del(&sas_expander->list); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (sas_expander) - _scsih_expander_node_remove(ioc, sas_expander); -} - -/** - * _scsih_check_access_status - check access flags - * @ioc: per adapter object - * @sas_address: sas address - * @handle: sas device handle - * @access_flags: errors returned during discovery of the device - * - * Return 0 for success, else failure - */ -static u8 -_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u16 handle, u8 access_status) -{ - u8 rc = 1; - char *desc = NULL; - - switch (access_status) { - case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: - case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: - rc = 0; - break; - case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: - desc = "sata capability failed"; - break; - case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: - desc = "sata affiliation conflict"; - break; - case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: - desc = "route not addressable"; - break; - case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: - desc = "smp error not addressable"; - break; - case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: - desc = "device blocked"; - break; - case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: - case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: - desc = "sata initialization failed"; - break; - default: - desc = "unknown"; - break; - } - - if (!rc) - return 0; - - printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), " - "handle(0x%04x)\n", ioc->name, desc, - (unsigned long long)sas_address, handle); - return rc; -} - -static void -_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - Mpi2ConfigReply_t mpi_reply; - Mpi2SasDevicePage0_t sas_device_pg0; - struct _sas_device *sas_device; - u32 ioc_status; - unsigned long flags; - u64 sas_address; - struct scsi_target *starget; - struct MPT2SAS_TARGET *sas_target_priv_data; - u32 device_info; - - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) - return; - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - return; - - /* check if this is end device */ - device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); - if (!(_scsih_is_end_device(device_info))) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - - if (!sas_device) { - printk(MPT2SAS_ERR_FMT "device is not present " - "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - - if (unlikely(sas_device->handle != handle)) { - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)" - " to (0x%04x)!!!\n", sas_device->handle, handle); - sas_target_priv_data->handle = handle; - sas_device->handle = handle; - } - - /* check if device is present */ - if (!(le16_to_cpu(sas_device_pg0.Flags) & - MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { - printk(MPT2SAS_ERR_FMT "device is not present " - "handle(0x%04x), flags!!!\n", ioc->name, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - - /* check if there were any issues with discovery */ - if (_scsih_check_access_status(ioc, sas_address, handle, - sas_device_pg0.AccessStatus)) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - _scsih_ublock_io_device(ioc, sas_address); - -} - -/** - * _scsih_add_device - creating sas device object - * @ioc: per adapter object - * @handle: sas device handle - * @phy_num: phy number end device attached to - * @is_pd: is this hidden raid component - * - * Creating end device object, stored in ioc->sas_device_list. - * - * Returns 0 for success, non-zero for failure. - */ -static int -_scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) -{ - Mpi2ConfigReply_t mpi_reply; - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2SasEnclosurePage0_t enclosure_pg0; - struct _sas_device *sas_device; - u32 ioc_status; - __le64 sas_address; - u32 device_info; - unsigned long flags; - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - - /* check if device is present */ - if (!(le16_to_cpu(sas_device_pg0.Flags) & - MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - printk(MPT2SAS_ERR_FMT "Flags = 0x%04x\n", - ioc->name, le16_to_cpu(sas_device_pg0.Flags)); - return -1; - } - - /* check if there were any issues with discovery */ - if (_scsih_check_access_status(ioc, sas_address, handle, - sas_device_pg0.AccessStatus)) - return -1; - - /* check if this is end device */ - device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); - if (!(_scsih_is_end_device(device_info))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) - return 0; - - sas_device = kzalloc(sizeof(struct _sas_device), - GFP_KERNEL); - if (!sas_device) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - - sas_device->handle = handle; - if (_scsih_get_sas_address(ioc, le16_to_cpu - (sas_device_pg0.ParentDevHandle), - &sas_device->sas_address_parent) != 0) - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - sas_device->enclosure_handle = - le16_to_cpu(sas_device_pg0.EnclosureHandle); - sas_device->slot = - le16_to_cpu(sas_device_pg0.Slot); - sas_device->device_info = device_info; - sas_device->sas_address = sas_address; - sas_device->phy = sas_device_pg0.PhyNum; - - /* get enclosure_logical_id */ - if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0( - ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_device->enclosure_handle))) - sas_device->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - - /* get device name */ - sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); - - if (ioc->wait_for_discovery_to_complete) - _scsih_sas_device_init_add(ioc, sas_device); - else - _scsih_sas_device_add(ioc, sas_device); - - return 0; -} - -/** - * _scsih_remove_device - removing sas device object - * @ioc: per adapter object - * @sas_device_delete: the sas_device object - * - * Return nothing. - */ -static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, - struct _sas_device *sas_device) -{ - struct MPT2SAS_TARGET *sas_target_priv_data; - - if ((ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) && - (sas_device->pfa_led_on)) { - _scsih_turn_off_pfa_led(ioc, sas_device); - sas_device->pfa_led_on = 0; - } - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: " - "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long) - sas_device->sas_address)); - - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - _scsih_ublock_io_device(ioc, sas_device->sas_address); - sas_target_priv_data->handle = - MPT2SAS_INVALID_DEVICE_HANDLE; - } - - if (!ioc->hide_drives) - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); - - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" - "(0x%016llx)\n", ioc->name, sas_device->handle, - (unsigned long long) sas_device->sas_address); - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: " - "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long) - sas_device->sas_address)); - kfree(sas_device); -} -/** - * _scsih_device_remove_by_handle - removing device object by handle - * @ioc: per adapter object - * @handle: device handle - * - * Return nothing. - */ -static void -_scsih_device_remove_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _sas_device *sas_device; - unsigned long flags; - - if (ioc->shost_recovery) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device) - list_del(&sas_device->list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) - _scsih_remove_device(ioc, sas_device); -} - -/** - * mpt2sas_device_remove_by_sas_address - removing device object by sas address - * @ioc: per adapter object - * @sas_address: device sas_address - * - * Return nothing. - */ -void -mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address) -{ - struct _sas_device *sas_device; - unsigned long flags; - - if (ioc->shost_recovery) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - if (sas_device) - list_del(&sas_device->list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) - _scsih_remove_device(ioc, sas_device); -} -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_sas_topology_change_event_debug - debug for topology event - * @ioc: per adapter object - * @event_data: event data payload - * Context: user. - */ -static void -_scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataSasTopologyChangeList_t *event_data) -{ - int i; - u16 handle; - u16 reason_code; - u8 phy_number; - char *status_str = NULL; - u8 link_rate, prev_link_rate; - - switch (event_data->ExpStatus) { - case MPI2_EVENT_SAS_TOPO_ES_ADDED: - status_str = "add"; - break; - case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING: - status_str = "remove"; - break; - case MPI2_EVENT_SAS_TOPO_ES_RESPONDING: - case 0: - status_str = "responding"; - break; - case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: - status_str = "remove delay"; - break; - default: - status_str = "unknown status"; - break; - } - printk(MPT2SAS_INFO_FMT "sas topology change: (%s)\n", - ioc->name, status_str); - printk(KERN_INFO "\thandle(0x%04x), enclosure_handle(0x%04x) " - "start_phy(%02d), count(%d)\n", - le16_to_cpu(event_data->ExpanderDevHandle), - le16_to_cpu(event_data->EnclosureHandle), - event_data->StartPhyNum, event_data->NumEntries); - for (i = 0; i < event_data->NumEntries; i++) { - handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); - if (!handle) - continue; - phy_number = event_data->StartPhyNum + i; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; - switch (reason_code) { - case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: - status_str = "target add"; - break; - case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: - status_str = "target remove"; - break; - case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING: - status_str = "delay target remove"; - break; - case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: - status_str = "link rate change"; - break; - case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE: - status_str = "target responding"; - break; - default: - status_str = "unknown"; - break; - } - link_rate = event_data->PHY[i].LinkRate >> 4; - prev_link_rate = event_data->PHY[i].LinkRate & 0xF; - printk(KERN_INFO "\tphy(%02d), attached_handle(0x%04x): %s:" - " link rate: new(0x%02x), old(0x%02x)\n", phy_number, - handle, status_str, link_rate, prev_link_rate); - - } -} -#endif - -/** - * _scsih_sas_topology_change_event - handle topology changes - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - */ -static void -_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - int i; - u16 parent_handle, handle; - u16 reason_code; - u8 phy_number, max_phys; - struct _sas_node *sas_expander; - u64 sas_address; - unsigned long flags; - u8 link_rate, prev_link_rate; - Mpi2EventDataSasTopologyChangeList_t *event_data = - (Mpi2EventDataSasTopologyChangeList_t *) - fw_event->event_data; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_topology_change_event_debug(ioc, event_data); -#endif - - if (ioc->remove_host || ioc->pci_error_recovery) - return; - - if (!ioc->sas_hba.num_phys) - _scsih_sas_host_add(ioc); - else - _scsih_sas_host_refresh(ioc); - - if (fw_event->ignore) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring expander " - "event\n", ioc->name)); - return; - } - - parent_handle = le16_to_cpu(event_data->ExpanderDevHandle); - - /* handle expander add */ - if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) - if (_scsih_expander_add(ioc, parent_handle) != 0) - return; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, - parent_handle); - if (sas_expander) { - sas_address = sas_expander->sas_address; - max_phys = sas_expander->num_phys; - } else if (parent_handle < ioc->sas_hba.num_phys) { - sas_address = ioc->sas_hba.sas_address; - max_phys = ioc->sas_hba.num_phys; - } else { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - /* handle siblings events */ - for (i = 0; i < event_data->NumEntries; i++) { - if (fw_event->ignore) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "ignoring " - "expander event\n", ioc->name)); - return; - } - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) - return; - phy_number = event_data->StartPhyNum + i; - if (phy_number >= max_phys) - continue; - reason_code = event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_RC_MASK; - if ((event_data->PHY[i].PhyStatus & - MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) && (reason_code != - MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING)) - continue; - handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); - if (!handle) - continue; - link_rate = event_data->PHY[i].LinkRate >> 4; - prev_link_rate = event_data->PHY[i].LinkRate & 0xF; - switch (reason_code) { - case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: - - if (ioc->shost_recovery) - break; - - if (link_rate == prev_link_rate) - break; - - mpt2sas_transport_update_links(ioc, sas_address, - handle, phy_number, link_rate); - - if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) - break; - - _scsih_check_device(ioc, handle); - break; - case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: - - if (ioc->shost_recovery) - break; - - mpt2sas_transport_update_links(ioc, sas_address, - handle, phy_number, link_rate); - - _scsih_add_device(ioc, handle, phy_number, 0); - break; - case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: - - _scsih_device_remove_by_handle(ioc, handle); - break; - } - } - - /* handle expander removal */ - if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING && - sas_expander) - mpt2sas_expander_remove(ioc, sas_address); - -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_sas_device_status_change_event_debug - debug for device event - * @event_data: event data payload - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataSasDeviceStatusChange_t *event_data) -{ - char *reason_str = NULL; - - switch (event_data->ReasonCode) { - case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA: - reason_str = "smart data"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED: - reason_str = "unsupported device discovered"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET: - reason_str = "internal device reset"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL: - reason_str = "internal task abort"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL: - reason_str = "internal task abort set"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL: - reason_str = "internal clear task set"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL: - reason_str = "internal query task"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE: - reason_str = "sata init failure"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: - reason_str = "internal device reset complete"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL: - reason_str = "internal task abort complete"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: - reason_str = "internal async notification"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY: - reason_str = "expander reduced functionality"; - break; - case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY: - reason_str = "expander reduced functionality complete"; - break; - default: - reason_str = "unknown reason"; - break; - } - printk(MPT2SAS_INFO_FMT "device status change: (%s)\n" - "\thandle(0x%04x), sas address(0x%016llx), tag(%d)", - ioc->name, reason_str, le16_to_cpu(event_data->DevHandle), - (unsigned long long)le64_to_cpu(event_data->SASAddress), - le16_to_cpu(event_data->TaskTag)); - if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA) - printk(MPT2SAS_INFO_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, - event_data->ASC, event_data->ASCQ); - printk(KERN_INFO "\n"); -} -#endif - -/** - * _scsih_sas_device_status_change_event - handle device status change - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - struct MPT2SAS_TARGET *target_priv_data; - struct _sas_device *sas_device; - u64 sas_address; - unsigned long flags; - Mpi2EventDataSasDeviceStatusChange_t *event_data = - (Mpi2EventDataSasDeviceStatusChange_t *) - fw_event->event_data; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_device_status_change_event_debug(ioc, - event_data); -#endif - - /* In MPI Revision K (0xC), the internal device reset complete was - * implemented, so avoid setting tm_busy flag for older firmware. - */ - if ((ioc->facts.HeaderVersion >> 8) < 0xC) - return; - - if (event_data->ReasonCode != - MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && - event_data->ReasonCode != - MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) - return; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_address = le64_to_cpu(event_data->SASAddress); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - - if (!sas_device || !sas_device->starget) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - - target_priv_data = sas_device->starget->hostdata; - if (!target_priv_data) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - - if (event_data->ReasonCode == - MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) - target_priv_data->tm_busy = 1; - else - target_priv_data->tm_busy = 0; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure event - * @ioc: per adapter object - * @event_data: event data payload - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataSasEnclDevStatusChange_t *event_data) -{ - char *reason_str = NULL; - - switch (event_data->ReasonCode) { - case MPI2_EVENT_SAS_ENCL_RC_ADDED: - reason_str = "enclosure add"; - break; - case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING: - reason_str = "enclosure remove"; - break; - default: - reason_str = "unknown reason"; - break; - } - - printk(MPT2SAS_INFO_FMT "enclosure status change: (%s)\n" - "\thandle(0x%04x), enclosure logical id(0x%016llx)" - " number slots(%d)\n", ioc->name, reason_str, - le16_to_cpu(event_data->EnclosureHandle), - (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID), - le16_to_cpu(event_data->StartSlot)); -} -#endif - -/** - * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - _scsih_sas_enclosure_dev_status_change_event_debug(ioc, - (Mpi2EventDataSasEnclDevStatusChange_t *) - fw_event->event_data); -#endif -} - -/** - * _scsih_sas_broadcast_primitive_event - handle broadcast events - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_broadcast_primitive_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - struct scsi_cmnd *scmd; - struct scsi_device *sdev; - u16 smid, handle; - u32 lun; - struct MPT2SAS_DEVICE *sas_device_priv_data; - u32 termination_count; - u32 query_count; - Mpi2SCSITaskManagementReply_t *mpi_reply; - Mpi2EventDataSasBroadcastPrimitive_t *event_data = - (Mpi2EventDataSasBroadcastPrimitive_t *) - fw_event->event_data; - u16 ioc_status; - unsigned long flags; - int r; - u8 max_retries = 0; - u8 task_abort_retries; - - mutex_lock(&ioc->tm_cmds.mutex); - pr_info(MPT2SAS_FMT - "%s: enter: phy number(%d), width(%d)\n", - ioc->name, __func__, event_data->PhyNum, - event_data->PortWidth); - - _scsih_block_io_all_device(ioc); - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - mpi_reply = ioc->tm_cmds.reply; -broadcast_aen_retry: - - /* sanity checks for retrying this loop */ - if (max_retries++ == 5) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: giving up\n", - ioc->name, __func__)); - goto out; - } else if (max_retries > 1) - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: %d retry\n", - ioc->name, __func__, max_retries - 1)); - - termination_count = 0; - query_count = 0; - for (smid = 1; smid <= ioc->scsiio_depth; smid++) { - if (ioc->shost_recovery) - goto out; - scmd = _scsih_scsi_lookup_get(ioc, smid); - if (!scmd) - continue; - sdev = scmd->device; - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data || !sas_device_priv_data->sas_target) - continue; - /* skip hidden raid components */ - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_RAID_COMPONENT) - continue; - /* skip volumes */ - if (sas_device_priv_data->sas_target->flags & - MPT_TARGET_FLAGS_VOLUME) - continue; - - handle = sas_device_priv_data->sas_target->handle; - lun = sas_device_priv_data->lun; - query_count++; - - if (ioc->shost_recovery) - goto out; - - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - r = mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, - TM_MUTEX_OFF); - if (r == FAILED) { - sdev_printk(KERN_WARNING, sdev, - "mpt2sas_scsih_issue_tm: FAILED when sending " - "QUERY_TASK: scmd(%p)\n", scmd); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - goto broadcast_aen_retry; - } - ioc_status = le16_to_cpu(mpi_reply->IOCStatus) - & MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - sdev_printk(KERN_WARNING, sdev, "query task: FAILED " - "with IOCSTATUS(0x%04x), scmd(%p)\n", ioc_status, - scmd); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - goto broadcast_aen_retry; - } - - /* see if IO is still owned by IOC and target */ - if (mpi_reply->ResponseCode == - MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || - mpi_reply->ResponseCode == - MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC) { - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - continue; - } - task_abort_retries = 0; - tm_retry: - if (task_abort_retries++ == 60) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s: ABORT_TASK: giving up\n", ioc->name, - __func__)); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - goto broadcast_aen_retry; - } - - if (ioc->shost_recovery) - goto out_no_lock; - - r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, - sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, - TM_MUTEX_OFF); - if (r == FAILED) { - sdev_printk(KERN_WARNING, sdev, - "mpt2sas_scsih_issue_tm: ABORT_TASK: FAILED : " - "scmd(%p)\n", scmd); - goto tm_retry; - } - - if (task_abort_retries > 1) - sdev_printk(KERN_WARNING, sdev, - "mpt2sas_scsih_issue_tm: ABORT_TASK: RETRIES (%d):" - " scmd(%p)\n", - task_abort_retries - 1, scmd); - - termination_count += le32_to_cpu(mpi_reply->TerminationCount); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - } - - if (ioc->broadcast_aen_pending) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: loop back due to" - " pending AEN\n", ioc->name, __func__)); - ioc->broadcast_aen_pending = 0; - goto broadcast_aen_retry; - } - - out: - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - out_no_lock: - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s - exit, query_count = %d termination_count = %d\n", - ioc->name, __func__, query_count, termination_count)); - - ioc->broadcast_aen_busy = 0; - if (!ioc->shost_recovery) - _scsih_ublock_io_all_device(ioc); - mutex_unlock(&ioc->tm_cmds.mutex); -} - -/** - * _scsih_sas_discovery_event - handle discovery events - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - Mpi2EventDataSasDiscovery_t *event_data = - (Mpi2EventDataSasDiscovery_t *) - fw_event->event_data; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { - printk(MPT2SAS_INFO_FMT "discovery event: (%s)", ioc->name, - (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? - "start" : "stop"); - if (event_data->DiscoveryStatus) - printk("discovery_status(0x%08x)", - le32_to_cpu(event_data->DiscoveryStatus)); - printk("\n"); - } -#endif - - if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && - !ioc->sas_hba.num_phys) { - if (disable_discovery > 0 && ioc->shost_recovery) { - /* Wait for the reset to complete */ - while (ioc->shost_recovery) - ssleep(1); - } - _scsih_sas_host_add(ioc); - } -} - -/** - * _scsih_reprobe_lun - reprobing lun - * @sdev: scsi device struct - * @no_uld_attach: sdev->no_uld_attach flag setting - * - **/ -static void -_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) -{ - int rc; - - sdev->no_uld_attach = no_uld_attach ? 1 : 0; - sdev_printk(KERN_INFO, sdev, "%s raid component\n", - sdev->no_uld_attach ? "hidding" : "exposing"); - rc = scsi_device_reprobe(sdev); -} - -/** - * _scsih_sas_volume_add - add new volume - * @ioc: per adapter object - * @element: IR config element data - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) -{ - struct _raid_device *raid_device; - unsigned long flags; - u64 wwid; - u16 handle = le16_to_cpu(element->VolDevHandle); - int rc; - - mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); - if (!wwid) { - printk(MPT2SAS_ERR_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - return; - } - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - - if (raid_device) - return; - - raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); - if (!raid_device) { - printk(MPT2SAS_ERR_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - return; - } - - raid_device->id = ioc->sas_id++; - raid_device->channel = RAID_CHANNEL; - raid_device->handle = handle; - raid_device->wwid = wwid; - _scsih_raid_device_add(ioc, raid_device); - if (!ioc->wait_for_discovery_to_complete) { - rc = scsi_add_device(ioc->shost, RAID_CHANNEL, - raid_device->id, 0); - if (rc) - _scsih_raid_device_remove(ioc, raid_device); - } else { - spin_lock_irqsave(&ioc->raid_device_lock, flags); - _scsih_determine_boot_device(ioc, raid_device, 1); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - } -} - -/** - * _scsih_sas_volume_delete - delete volume - * @ioc: per adapter object - * @handle: volume device handle - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, u16 handle) -{ - struct _raid_device *raid_device; - unsigned long flags; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct scsi_target *starget = NULL; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - if (raid_device) { - if (raid_device->starget) { - starget = raid_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->deleted = 1; - } - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid" - "(0x%016llx)\n", ioc->name, raid_device->handle, - (unsigned long long) raid_device->wwid); - list_del(&raid_device->list); - kfree(raid_device); - } - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - if (starget) - scsi_remove_target(&starget->dev); -} - -/** - * _scsih_sas_pd_expose - expose pd component to /dev/sdX - * @ioc: per adapter object - * @element: IR config element data - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) -{ - struct _sas_device *sas_device; - struct scsi_target *starget = NULL; - struct MPT2SAS_TARGET *sas_target_priv_data; - unsigned long flags; - u16 handle = le16_to_cpu(element->PhysDiskDevHandle); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device) { - sas_device->volume_handle = 0; - sas_device->volume_wwid = 0; - clear_bit(handle, ioc->pd_handles); - if (sas_device->starget && sas_device->starget->hostdata) { - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->flags &= - ~MPT_TARGET_FLAGS_RAID_COMPONENT; - } - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device) - return; - - /* exposing raid component */ - if (starget) - starget_for_each_device(starget, NULL, _scsih_reprobe_lun); -} - -/** - * _scsih_sas_pd_hide - hide pd component from /dev/sdX - * @ioc: per adapter object - * @element: IR config element data - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) -{ - struct _sas_device *sas_device; - struct scsi_target *starget = NULL; - struct MPT2SAS_TARGET *sas_target_priv_data; - unsigned long flags; - u16 handle = le16_to_cpu(element->PhysDiskDevHandle); - u16 volume_handle = 0; - u64 volume_wwid = 0; - - mpt2sas_config_get_volume_handle(ioc, handle, &volume_handle); - if (volume_handle) - mpt2sas_config_get_volume_wwid(ioc, volume_handle, - &volume_wwid); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device) { - set_bit(handle, ioc->pd_handles); - if (sas_device->starget && sas_device->starget->hostdata) { - starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->flags |= - MPT_TARGET_FLAGS_RAID_COMPONENT; - sas_device->volume_handle = volume_handle; - sas_device->volume_wwid = volume_wwid; - } - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (!sas_device) - return; - - /* hiding raid component */ - if (starget) - starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); -} - -/** - * _scsih_sas_pd_delete - delete pd component - * @ioc: per adapter object - * @element: IR config element data - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) -{ - u16 handle = le16_to_cpu(element->PhysDiskDevHandle); - - _scsih_device_remove_by_handle(ioc, handle); -} - -/** - * _scsih_sas_pd_add - remove pd component - * @ioc: per adapter object - * @element: IR config element data - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventIrConfigElement_t *element) -{ - struct _sas_device *sas_device; - unsigned long flags; - u16 handle = le16_to_cpu(element->PhysDiskDevHandle); - Mpi2ConfigReply_t mpi_reply; - Mpi2SasDevicePage0_t sas_device_pg0; - u32 ioc_status; - u64 sas_address; - u16 parent_handle; - - set_bit(handle, ioc->pd_handles); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) - return; - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); - if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) - mpt2sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); - - _scsih_add_device(ioc, handle, 0, 1); -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events - * @ioc: per adapter object - * @event_data: event data payload - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataIrConfigChangeList_t *event_data) -{ - Mpi2EventIrConfigElement_t *element; - u8 element_type; - int i; - char *reason_str = NULL, *element_str = NULL; - - element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - - printk(MPT2SAS_INFO_FMT "raid config change: (%s), elements(%d)\n", - ioc->name, (le32_to_cpu(event_data->Flags) & - MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? - "foreign" : "native", event_data->NumElements); - for (i = 0; i < event_data->NumElements; i++, element++) { - switch (element->ReasonCode) { - case MPI2_EVENT_IR_CHANGE_RC_ADDED: - reason_str = "add"; - break; - case MPI2_EVENT_IR_CHANGE_RC_REMOVED: - reason_str = "remove"; - break; - case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE: - reason_str = "no change"; - break; - case MPI2_EVENT_IR_CHANGE_RC_HIDE: - reason_str = "hide"; - break; - case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: - reason_str = "unhide"; - break; - case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: - reason_str = "volume_created"; - break; - case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: - reason_str = "volume_deleted"; - break; - case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: - reason_str = "pd_created"; - break; - case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: - reason_str = "pd_deleted"; - break; - default: - reason_str = "unknown reason"; - break; - } - element_type = le16_to_cpu(element->ElementFlags) & - MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK; - switch (element_type) { - case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT: - element_str = "volume"; - break; - case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT: - element_str = "phys disk"; - break; - case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT: - element_str = "hot spare"; - break; - default: - element_str = "unknown element"; - break; - } - printk(KERN_INFO "\t(%s:%s), vol handle(0x%04x), " - "pd handle(0x%04x), pd num(0x%02x)\n", element_str, - reason_str, le16_to_cpu(element->VolDevHandle), - le16_to_cpu(element->PhysDiskDevHandle), - element->PhysDiskNum); - } -} -#endif - -/** - * _scsih_sas_ir_config_change_event - handle ir configuration change events - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - Mpi2EventIrConfigElement_t *element; - int i; - u8 foreign_config; - Mpi2EventDataIrConfigChangeList_t *event_data = - (Mpi2EventDataIrConfigChangeList_t *) - fw_event->event_data; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - && !ioc->hide_ir_msg) - _scsih_sas_ir_config_change_event_debug(ioc, event_data); - -#endif - - if (ioc->shost_recovery) - return; - - foreign_config = (le32_to_cpu(event_data->Flags) & - MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; - - element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - for (i = 0; i < event_data->NumElements; i++, element++) { - - switch (element->ReasonCode) { - case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: - case MPI2_EVENT_IR_CHANGE_RC_ADDED: - if (!foreign_config) - _scsih_sas_volume_add(ioc, element); - break; - case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: - case MPI2_EVENT_IR_CHANGE_RC_REMOVED: - if (!foreign_config) - _scsih_sas_volume_delete(ioc, - le16_to_cpu(element->VolDevHandle)); - break; - case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: - if (!ioc->is_warpdrive) - _scsih_sas_pd_hide(ioc, element); - break; - case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: - if (!ioc->is_warpdrive) - _scsih_sas_pd_expose(ioc, element); - break; - case MPI2_EVENT_IR_CHANGE_RC_HIDE: - if (!ioc->is_warpdrive) - _scsih_sas_pd_add(ioc, element); - break; - case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: - if (!ioc->is_warpdrive) - _scsih_sas_pd_delete(ioc, element); - break; - } - } -} - -/** - * _scsih_sas_ir_volume_event - IR volume event - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - u64 wwid; - unsigned long flags; - struct _raid_device *raid_device; - u16 handle; - u32 state; - int rc; - Mpi2EventDataIrVolume_t *event_data = - (Mpi2EventDataIrVolume_t *) - fw_event->event_data; - - if (ioc->shost_recovery) - return; - - if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) - return; - - handle = le16_to_cpu(event_data->VolDevHandle); - state = le32_to_cpu(event_data->NewValue); - if (!ioc->hide_ir_msg) - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), " - "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, - le32_to_cpu(event_data->PreviousValue), state)); - - switch (state) { - case MPI2_RAID_VOL_STATE_MISSING: - case MPI2_RAID_VOL_STATE_FAILED: - _scsih_sas_volume_delete(ioc, handle); - break; - - case MPI2_RAID_VOL_STATE_ONLINE: - case MPI2_RAID_VOL_STATE_DEGRADED: - case MPI2_RAID_VOL_STATE_OPTIMAL: - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - - if (raid_device) - break; - - mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); - if (!wwid) { - printk(MPT2SAS_ERR_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - break; - } - - raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); - if (!raid_device) { - printk(MPT2SAS_ERR_FMT - "failure at %s:%d/%s()!\n", ioc->name, - __FILE__, __LINE__, __func__); - break; - } - - raid_device->id = ioc->sas_id++; - raid_device->channel = RAID_CHANNEL; - raid_device->handle = handle; - raid_device->wwid = wwid; - _scsih_raid_device_add(ioc, raid_device); - rc = scsi_add_device(ioc->shost, RAID_CHANNEL, - raid_device->id, 0); - if (rc) - _scsih_raid_device_remove(ioc, raid_device); - break; - - case MPI2_RAID_VOL_STATE_INITIALIZING: - default: - break; - } -} - -/** - * _scsih_sas_ir_physical_disk_event - PD event - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - u16 handle, parent_handle; - u32 state; - struct _sas_device *sas_device; - unsigned long flags; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasDevicePage0_t sas_device_pg0; - u32 ioc_status; - Mpi2EventDataIrPhysicalDisk_t *event_data = - (Mpi2EventDataIrPhysicalDisk_t *) - fw_event->event_data; - u64 sas_address; - - if (ioc->shost_recovery) - return; - - if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) - return; - - handle = le16_to_cpu(event_data->PhysDiskDevHandle); - state = le32_to_cpu(event_data->NewValue); - - if (!ioc->hide_ir_msg) - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: handle(0x%04x), " - "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, - le32_to_cpu(event_data->PreviousValue), state)); - - switch (state) { - case MPI2_RAID_PD_STATE_ONLINE: - case MPI2_RAID_PD_STATE_DEGRADED: - case MPI2_RAID_PD_STATE_REBUILDING: - case MPI2_RAID_PD_STATE_OPTIMAL: - case MPI2_RAID_PD_STATE_HOT_SPARE: - - if (!ioc->is_warpdrive) - set_bit(handle, ioc->pd_handles); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) - return; - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, - &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, - handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); - if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) - mpt2sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); - - _scsih_add_device(ioc, handle, 0, 1); - - break; - - case MPI2_RAID_PD_STATE_OFFLINE: - case MPI2_RAID_PD_STATE_NOT_CONFIGURED: - case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: - default: - break; - } -} - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING -/** - * _scsih_sas_ir_operation_status_event_debug - debug for IR op event - * @ioc: per adapter object - * @event_data: event data payload - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc, - Mpi2EventDataIrOperationStatus_t *event_data) -{ - char *reason_str = NULL; - - switch (event_data->RAIDOperation) { - case MPI2_EVENT_IR_RAIDOP_RESYNC: - reason_str = "resync"; - break; - case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION: - reason_str = "online capacity expansion"; - break; - case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK: - reason_str = "consistency check"; - break; - case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT: - reason_str = "background init"; - break; - case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT: - reason_str = "make data consistent"; - break; - } - - if (!reason_str) - return; - - printk(MPT2SAS_INFO_FMT "raid operational status: (%s)" - "\thandle(0x%04x), percent complete(%d)\n", - ioc->name, reason_str, - le16_to_cpu(event_data->VolDevHandle), - event_data->PercentComplete); -} -#endif - -/** - * _scsih_sas_ir_operation_status_event - handle RAID operation events - * @ioc: per adapter object - * @fw_event: The fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, - struct fw_event_work *fw_event) -{ - Mpi2EventDataIrOperationStatus_t *event_data = - (Mpi2EventDataIrOperationStatus_t *) - fw_event->event_data; - static struct _raid_device *raid_device; - unsigned long flags; - u16 handle; - -#ifdef CONFIG_SCSI_MPT2SAS_LOGGING - if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) - && !ioc->hide_ir_msg) - _scsih_sas_ir_operation_status_event_debug(ioc, - event_data); -#endif - - /* code added for raid transport support */ - if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - handle = le16_to_cpu(event_data->VolDevHandle); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); - if (raid_device) - raid_device->percent_complete = - event_data->PercentComplete; - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - } -} - -/** - * _scsih_prep_device_scan - initialize parameters prior to device scan - * @ioc: per adapter object - * - * Set the deleted flag prior to device scan. If the device is found during - * the scan, then we clear the deleted flag. - */ -static void -_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc) -{ - struct MPT2SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; - - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (sas_device_priv_data && sas_device_priv_data->sas_target) - sas_device_priv_data->sas_target->deleted = 1; - } -} - -/** - * _scsih_mark_responding_sas_device - mark a sas_devices as responding - * @ioc: per adapter object - * @sas_address: sas address - * @slot: enclosure slot id - * @handle: device handle - * - * After host reset, find out whether devices are still responding. - * Used in _scsi_remove_unresponsive_sas_devices. - * - * Return nothing. - */ -static void -_scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u16 slot, u16 handle) -{ - struct MPT2SAS_TARGET *sas_target_priv_data = NULL; - struct scsi_target *starget; - struct _sas_device *sas_device; - unsigned long flags; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address == sas_address && - sas_device->slot == slot) { - sas_device->responding = 1; - starget = sas_device->starget; - if (starget && starget->hostdata) { - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->tm_busy = 0; - sas_target_priv_data->deleted = 0; - } else - sas_target_priv_data = NULL; - if (starget) - starget_printk(KERN_INFO, starget, - "handle(0x%04x), sas_addr(0x%016llx), " - "enclosure logical id(0x%016llx), " - "slot(%d)\n", handle, - (unsigned long long)sas_device->sas_address, - (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->handle == handle) - goto out; - printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", - sas_device->handle); - sas_device->handle = handle; - if (sas_target_priv_data) - sas_target_priv_data->handle = handle; - goto out; - } - } - out: - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -} - -/** - * _scsih_search_responding_sas_devices - - * @ioc: per adapter object - * - * After host reset, find out whether devices are still responding. - * If not remove. - * - * Return nothing. - */ -static void -_scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 ioc_status; - __le64 sas_address; - u16 handle; - u32 device_info; - u16 slot; - - printk(MPT2SAS_INFO_FMT "search for end-devices: start\n", ioc->name); - - if (list_empty(&ioc->sas_device_list)) - goto out; - - handle = 0xFFFF; - while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, - &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, - handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - break; - handle = le16_to_cpu(sas_device_pg0.DevHandle); - device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); - if (!(_scsih_is_end_device(device_info))) - continue; - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - slot = le16_to_cpu(sas_device_pg0.Slot); - _scsih_mark_responding_sas_device(ioc, sas_address, slot, - handle); - } -out: - printk(MPT2SAS_INFO_FMT "search for end-devices: complete\n", - ioc->name); -} - -/** - * _scsih_mark_responding_raid_device - mark a raid_device as responding - * @ioc: per adapter object - * @wwid: world wide identifier for raid volume - * @handle: device handle - * - * After host reset, find out whether devices are still responding. - * Used in _scsi_remove_unresponsive_raid_devices. - * - * Return nothing. - */ -static void -_scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, - u16 handle) -{ - struct MPT2SAS_TARGET *sas_target_priv_data; - struct scsi_target *starget; - struct _raid_device *raid_device; - unsigned long flags; - - spin_lock_irqsave(&ioc->raid_device_lock, flags); - list_for_each_entry(raid_device, &ioc->raid_device_list, list) { - if (raid_device->wwid == wwid && raid_device->starget) { - starget = raid_device->starget; - if (starget && starget->hostdata) { - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->deleted = 0; - } else - sas_target_priv_data = NULL; - raid_device->responding = 1; - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - starget_printk(KERN_INFO, raid_device->starget, - "handle(0x%04x), wwid(0x%016llx)\n", handle, - (unsigned long long)raid_device->wwid); - /* - * WARPDRIVE: The handles of the PDs might have changed - * across the host reset so re-initialize the - * required data for Direct IO - */ - _scsih_init_warpdrive_properties(ioc, raid_device); - spin_lock_irqsave(&ioc->raid_device_lock, flags); - if (raid_device->handle == handle) { - spin_unlock_irqrestore(&ioc->raid_device_lock, - flags); - return; - } - printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", - raid_device->handle); - raid_device->handle = handle; - if (sas_target_priv_data) - sas_target_priv_data->handle = handle; - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - return; - } - } - - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); -} - -/** - * _scsih_search_responding_raid_devices - - * @ioc: per adapter object - * - * After host reset, find out whether devices are still responding. - * If not remove. - * - * Return nothing. - */ -static void -_scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2RaidVolPage1_t volume_pg1; - Mpi2RaidVolPage0_t volume_pg0; - Mpi2RaidPhysDiskPage0_t pd_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 ioc_status; - u16 handle; - u8 phys_disk_num; - - if (!ioc->ir_firmware) - return; - - printk(MPT2SAS_INFO_FMT "search for raid volumes: start\n", - ioc->name); - - if (list_empty(&ioc->raid_device_list)) - goto out; - - handle = 0xFFFF; - while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, - &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - break; - handle = le16_to_cpu(volume_pg1.DevHandle); - - if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, - &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, - sizeof(Mpi2RaidVolPage0_t))) - continue; - - if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || - volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || - volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) - _scsih_mark_responding_raid_device(ioc, - le64_to_cpu(volume_pg1.WWID), handle); - } - - /* refresh the pd_handles */ - if (!ioc->is_warpdrive) { - phys_disk_num = 0xFF; - memset(ioc->pd_handles, 0, ioc->pd_handles_sz); - while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, - &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, - phys_disk_num))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - break; - phys_disk_num = pd_pg0.PhysDiskNum; - handle = le16_to_cpu(pd_pg0.DevHandle); - set_bit(handle, ioc->pd_handles); - } - } -out: - printk(MPT2SAS_INFO_FMT "search for responding raid volumes: " - "complete\n", ioc->name); -} - -/** - * _scsih_mark_responding_expander - mark a expander as responding - * @ioc: per adapter object - * @sas_address: sas address - * @handle: - * - * After host reset, find out whether devices are still responding. - * Used in _scsi_remove_unresponsive_expanders. - * - * Return nothing. - */ -static void -_scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u16 handle) -{ - struct _sas_node *sas_expander; - unsigned long flags; - int i; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { - if (sas_expander->sas_address != sas_address) - continue; - sas_expander->responding = 1; - if (sas_expander->handle == handle) - goto out; - printk(KERN_INFO "\texpander(0x%016llx): handle changed" - " from(0x%04x) to (0x%04x)!!!\n", - (unsigned long long)sas_expander->sas_address, - sas_expander->handle, handle); - sas_expander->handle = handle; - for (i = 0 ; i < sas_expander->num_phys ; i++) - sas_expander->phy[i].handle = handle; - goto out; - } - out: - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); -} - -/** - * _scsih_search_responding_expanders - - * @ioc: per adapter object - * - * After host reset, find out whether devices are still responding. - * If not remove. - * - * Return nothing. - */ -static void -_scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2ExpanderPage0_t expander_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 ioc_status; - u64 sas_address; - u16 handle; - - printk(MPT2SAS_INFO_FMT "search for expanders: start\n", ioc->name); - - if (list_empty(&ioc->sas_expander_list)) - goto out; - - handle = 0xFFFF; - while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, - MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) - break; - - handle = le16_to_cpu(expander_pg0.DevHandle); - sas_address = le64_to_cpu(expander_pg0.SASAddress); - printk(KERN_INFO "\texpander present: handle(0x%04x), " - "sas_addr(0x%016llx)\n", handle, - (unsigned long long)sas_address); - _scsih_mark_responding_expander(ioc, sas_address, handle); - } - - out: - printk(MPT2SAS_INFO_FMT "search for expanders: complete\n", ioc->name); -} - -/** - * _scsih_remove_unresponding_sas_devices - removing unresponding devices - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) -{ - struct _sas_device *sas_device, *sas_device_next; - struct _sas_node *sas_expander, *sas_expander_next; - struct _raid_device *raid_device, *raid_device_next; - struct list_head tmp_list; - unsigned long flags; - - printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n", - ioc->name); - - /* removing unresponding end devices */ - printk(MPT2SAS_INFO_FMT "removing unresponding devices: end-devices\n", - ioc->name); - list_for_each_entry_safe(sas_device, sas_device_next, - &ioc->sas_device_list, list) { - if (!sas_device->responding) - mpt2sas_device_remove_by_sas_address(ioc, - sas_device->sas_address); - else - sas_device->responding = 0; - } - - /* removing unresponding volumes */ - if (ioc->ir_firmware) { - printk(MPT2SAS_INFO_FMT "removing unresponding devices: " - "volumes\n", ioc->name); - list_for_each_entry_safe(raid_device, raid_device_next, - &ioc->raid_device_list, list) { - if (!raid_device->responding) - _scsih_sas_volume_delete(ioc, - raid_device->handle); - else - raid_device->responding = 0; - } - } - /* removing unresponding expanders */ - printk(MPT2SAS_INFO_FMT "removing unresponding devices: expanders\n", - ioc->name); - spin_lock_irqsave(&ioc->sas_node_lock, flags); - INIT_LIST_HEAD(&tmp_list); - list_for_each_entry_safe(sas_expander, sas_expander_next, - &ioc->sas_expander_list, list) { - if (!sas_expander->responding) - list_move_tail(&sas_expander->list, &tmp_list); - else - sas_expander->responding = 0; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - list_for_each_entry_safe(sas_expander, sas_expander_next, &tmp_list, - list) { - list_del(&sas_expander->list); - _scsih_expander_node_remove(ioc, sas_expander); - } - printk(MPT2SAS_INFO_FMT "removing unresponding devices: complete\n", - ioc->name); - /* unblock devices */ - _scsih_ublock_io_all_device(ioc); -} - -static void -_scsih_refresh_expander_links(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_expander, u16 handle) -{ - Mpi2ExpanderPage1_t expander_pg1; - Mpi2ConfigReply_t mpi_reply; - int i; - - for (i = 0 ; i < sas_expander->num_phys ; i++) { - if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, - &expander_pg1, i, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - mpt2sas_transport_update_links(ioc, sas_expander->sas_address, - le16_to_cpu(expander_pg1.AttachedDevHandle), i, - expander_pg1.NegotiatedLinkRate >> 4); - } -} - -/** - * _scsih_scan_for_devices_after_reset - scan for devices after host reset - * @ioc: per adapter object - * - * Return nothing. - */ -static void -_scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2ExpanderPage0_t expander_pg0; - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2RaidVolPage1_t volume_pg1; - Mpi2RaidVolPage0_t volume_pg0; - Mpi2RaidPhysDiskPage0_t pd_pg0; - Mpi2EventIrConfigElement_t element; - Mpi2ConfigReply_t mpi_reply; - u8 phys_disk_num; - u16 ioc_status; - u16 handle, parent_handle; - u64 sas_address; - struct _sas_device *sas_device; - struct _sas_node *expander_device; - static struct _raid_device *raid_device; - u8 retry_count; - unsigned long flags; - - printk(MPT2SAS_INFO_FMT "scan devices: start\n", ioc->name); - - _scsih_sas_host_refresh(ioc); - - printk(MPT2SAS_INFO_FMT "\tscan devices: expanders start\n", - ioc->name); - /* expanders */ - handle = 0xFFFF; - while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, - MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from expander scan: " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - handle = le16_to_cpu(expander_pg0.DevHandle); - spin_lock_irqsave(&ioc->sas_node_lock, flags); - expander_device = mpt2sas_scsih_expander_find_by_sas_address( - ioc, le64_to_cpu(expander_pg0.SASAddress)); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (expander_device) - _scsih_refresh_expander_links(ioc, expander_device, - handle); - else { - printk(MPT2SAS_INFO_FMT "\tBEFORE adding expander: " - "handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(expander_pg0.SASAddress)); - _scsih_expander_add(ioc, handle); - printk(MPT2SAS_INFO_FMT "\tAFTER adding expander: " - "handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(expander_pg0.SASAddress)); - } - } - - printk(MPT2SAS_INFO_FMT "\tscan devices: expanders complete\n", - ioc->name); - - if (!ioc->ir_firmware) - goto skip_to_sas; - - printk(MPT2SAS_INFO_FMT "\tscan devices phys disk start\n", ioc->name); - /* phys disk */ - phys_disk_num = 0xFF; - while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, - &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, - phys_disk_num))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from phys disk scan:" - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - phys_disk_num = pd_pg0.PhysDiskNum; - handle = le16_to_cpu(pd_pg0.DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) - continue; - if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, - &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, - handle) != 0) - continue; - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from phys disk scan " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); - if (!_scsih_get_sas_address(ioc, parent_handle, - &sas_address)) { - printk(MPT2SAS_INFO_FMT "\tBEFORE adding phys disk: " - " handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(sas_device_pg0.SASAddress)); - mpt2sas_transport_update_links(ioc, sas_address, - handle, sas_device_pg0.PhyNum, - MPI2_SAS_NEG_LINK_RATE_1_5); - set_bit(handle, ioc->pd_handles); - retry_count = 0; - /* This will retry adding the end device. - * _scsih_add_device() will decide on retries and - * return "1" when it should be retried - */ - while (_scsih_add_device(ioc, handle, retry_count++, - 1)) { - ssleep(1); - } - printk(MPT2SAS_INFO_FMT "\tAFTER adding phys disk: " - " handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(sas_device_pg0.SASAddress)); - } - } - - printk(MPT2SAS_INFO_FMT "\tscan devices: phys disk complete\n", - ioc->name); - - printk(MPT2SAS_INFO_FMT "\tscan devices: volumes start\n", ioc->name); - /* volumes */ - handle = 0xFFFF; - while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, - &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from volume scan: " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - handle = le16_to_cpu(volume_pg1.DevHandle); - spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_wwid(ioc, - le64_to_cpu(volume_pg1.WWID)); - spin_unlock_irqrestore(&ioc->raid_device_lock, flags); - if (raid_device) - continue; - if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, - &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, - sizeof(Mpi2RaidVolPage0_t))) - continue; - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from volume scan: " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || - volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || - volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) { - memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t)); - element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED; - element.VolDevHandle = volume_pg1.DevHandle; - printk(MPT2SAS_INFO_FMT "\tBEFORE adding volume: " - " handle (0x%04x)\n", ioc->name, - volume_pg1.DevHandle); - _scsih_sas_volume_add(ioc, &element); - printk(MPT2SAS_INFO_FMT "\tAFTER adding volume: " - " handle (0x%04x)\n", ioc->name, - volume_pg1.DevHandle); - } - } - - printk(MPT2SAS_INFO_FMT "\tscan devices: volumes complete\n", - ioc->name); - - skip_to_sas: - - printk(MPT2SAS_INFO_FMT "\tscan devices: end devices start\n", - ioc->name); - /* sas devices */ - handle = 0xFFFF; - while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, - &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, - handle))) { - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_INFO_FMT "\tbreak from end device scan:" - " ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, ioc_status, - le32_to_cpu(mpi_reply.IOCLogInfo)); - break; - } - handle = le16_to_cpu(sas_device_pg0.DevHandle); - if (!(_scsih_is_end_device( - le32_to_cpu(sas_device_pg0.DeviceInfo)))) - continue; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - le64_to_cpu(sas_device_pg0.SASAddress)); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) - continue; - parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); - if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { - printk(MPT2SAS_INFO_FMT "\tBEFORE adding end device: " - "handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(sas_device_pg0.SASAddress)); - mpt2sas_transport_update_links(ioc, sas_address, handle, - sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); - retry_count = 0; - /* This will retry adding the end device. - * _scsih_add_device() will decide on retries and - * return "1" when it should be retried - */ - while (_scsih_add_device(ioc, handle, retry_count++, - 0)) { - ssleep(1); - } - printk(MPT2SAS_INFO_FMT "\tAFTER adding end device: " - "handle (0x%04x), sas_addr(0x%016llx)\n", - ioc->name, handle, (unsigned long long) - le64_to_cpu(sas_device_pg0.SASAddress)); - } - } - - printk(MPT2SAS_INFO_FMT "\tscan devices: end devices complete\n", - ioc->name); - - printk(MPT2SAS_INFO_FMT "scan devices: complete\n", ioc->name); -} - - -/** - * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) - * @ioc: per adapter object - * @reset_phase: phase - * - * The handler for doing any required cleanup or initialization. - * - * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, - * MPT2_IOC_DONE_RESET - * - * Return nothing. - */ -void -mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) -{ - switch (reset_phase) { - case MPT2_IOC_PRE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - break; - case MPT2_IOC_AFTER_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); - if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) { - ioc->scsih_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); - complete(&ioc->scsih_cmds.done); - } - if (ioc->tm_cmds.status & MPT2_CMD_PENDING) { - ioc->tm_cmds.status |= MPT2_CMD_RESET; - mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); - complete(&ioc->tm_cmds.done); - } - _scsih_fw_event_cleanup_queue(ioc); - _scsih_flush_running_cmds(ioc); - break; - case MPT2_IOC_DONE_RESET: - dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " - "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); - _scsih_sas_host_refresh(ioc); - _scsih_prep_device_scan(ioc); - _scsih_search_responding_sas_devices(ioc); - _scsih_search_responding_raid_devices(ioc); - _scsih_search_responding_expanders(ioc); - if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && - !ioc->sas_hba.num_phys)) { - _scsih_prep_device_scan(ioc); - _scsih_search_responding_sas_devices(ioc); - _scsih_search_responding_raid_devices(ioc); - _scsih_search_responding_expanders(ioc); - _scsih_error_recovery_delete_devices(ioc); - } - break; - } -} - -/** - * _firmware_event_work - delayed task for processing firmware events - * @ioc: per adapter object - * @work: equal to the fw_event_work object - * Context: user. - * - * Return nothing. - */ -static void -_firmware_event_work(struct work_struct *work) -{ - struct fw_event_work *fw_event = container_of(work, - struct fw_event_work, delayed_work.work); - struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; - - /* the queue is being flushed so ignore this event */ - if (ioc->remove_host || - ioc->pci_error_recovery) { - _scsih_fw_event_free(ioc, fw_event); - return; - } - - switch (fw_event->event) { - case MPT2SAS_REMOVE_UNRESPONDING_DEVICES: - while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) - ssleep(1); - _scsih_remove_unresponding_sas_devices(ioc); - _scsih_scan_for_devices_after_reset(ioc); - break; - case MPT2SAS_PORT_ENABLE_COMPLETE: - ioc->start_scan = 0; - - if (missing_delay[0] != -1 && missing_delay[1] != -1) - mpt2sas_base_update_missing_delay(ioc, missing_delay[0], - missing_delay[1]); - - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "port enable: complete " - "from worker thread\n", ioc->name)); - break; - case MPT2SAS_TURN_ON_PFA_LED: - _scsih_turn_on_pfa_led(ioc, fw_event->device_handle); - break; - case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - _scsih_sas_topology_change_event(ioc, fw_event); - break; - case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - _scsih_sas_device_status_change_event(ioc, - fw_event); - break; - case MPI2_EVENT_SAS_DISCOVERY: - _scsih_sas_discovery_event(ioc, - fw_event); - break; - case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - _scsih_sas_broadcast_primitive_event(ioc, - fw_event); - break; - case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - _scsih_sas_enclosure_dev_status_change_event(ioc, - fw_event); - break; - case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - _scsih_sas_ir_config_change_event(ioc, fw_event); - break; - case MPI2_EVENT_IR_VOLUME: - _scsih_sas_ir_volume_event(ioc, fw_event); - break; - case MPI2_EVENT_IR_PHYSICAL_DISK: - _scsih_sas_ir_physical_disk_event(ioc, fw_event); - break; - case MPI2_EVENT_IR_OPERATION_STATUS: - _scsih_sas_ir_operation_status_event(ioc, fw_event); - break; - } - _scsih_fw_event_free(ioc, fw_event); -} - -/** - * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) - * @ioc: per adapter object - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * Context: interrupt. - * - * This function merely adds a new work task into ioc->firmware_event_thread. - * The tasks are worked from _firmware_event_work in user context. - * - * Returns void. - */ -void -mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, - u32 reply) -{ - struct fw_event_work *fw_event; - Mpi2EventNotificationReply_t *mpi_reply; - u16 event; - u16 sz; - - /* events turned off due to host reset or driver unloading */ - if (ioc->remove_host || ioc->pci_error_recovery) - return; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - - if (unlikely(!mpi_reply)) { - printk(MPT2SAS_ERR_FMT "mpi_reply not valid at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - event = le16_to_cpu(mpi_reply->Event); - - switch (event) { - /* handle these */ - case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: - { - Mpi2EventDataSasBroadcastPrimitive_t *baen_data = - (Mpi2EventDataSasBroadcastPrimitive_t *) - mpi_reply->EventData; - - if (baen_data->Primitive != - MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT) - return; - - if (ioc->broadcast_aen_busy) { - ioc->broadcast_aen_pending++; - return; - } else - ioc->broadcast_aen_busy = 1; - break; - } - - case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: - _scsih_check_topo_delete_events(ioc, - (Mpi2EventDataSasTopologyChangeList_t *) - mpi_reply->EventData); - break; - case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - _scsih_check_ir_config_unhide_events(ioc, - (Mpi2EventDataIrConfigChangeList_t *) - mpi_reply->EventData); - break; - case MPI2_EVENT_IR_VOLUME: - _scsih_check_volume_delete_events(ioc, - (Mpi2EventDataIrVolume_t *) - mpi_reply->EventData); - break; - case MPI2_EVENT_LOG_ENTRY_ADDED: - { - Mpi2EventDataLogEntryAdded_t *log_entry; - __le32 *log_code; - - if (!ioc->is_warpdrive) - break; - - log_entry = (Mpi2EventDataLogEntryAdded_t *) - mpi_reply->EventData; - log_code = (__le32 *)log_entry->LogData; - - if (le16_to_cpu(log_entry->LogEntryQualifier) - != MPT2_WARPDRIVE_LOGENTRY) - break; - - switch (le32_to_cpu(*log_code)) { - case MPT2_WARPDRIVE_LC_SSDT: - printk(MPT2SAS_WARN_FMT "WarpDrive Warning: " - "IO Throttling has occurred in the WarpDrive " - "subsystem. Check WarpDrive documentation for " - "additional details.\n", ioc->name); - break; - case MPT2_WARPDRIVE_LC_SSDLW: - printk(MPT2SAS_WARN_FMT "WarpDrive Warning: " - "Program/Erase Cycles for the WarpDrive subsystem " - "in degraded range. Check WarpDrive documentation " - "for additional details.\n", ioc->name); - break; - case MPT2_WARPDRIVE_LC_SSDLF: - printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: " - "There are no Program/Erase Cycles for the " - "WarpDrive subsystem. The storage device will be " - "in read-only mode. Check WarpDrive documentation " - "for additional details.\n", ioc->name); - break; - case MPT2_WARPDRIVE_LC_BRMF: - printk(MPT2SAS_ERR_FMT "WarpDrive Fatal Error: " - "The Backup Rail Monitor has failed on the " - "WarpDrive subsystem. Check WarpDrive " - "documentation for additional details.\n", - ioc->name); - break; - } - - break; - } - case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: - case MPI2_EVENT_IR_OPERATION_STATUS: - case MPI2_EVENT_SAS_DISCOVERY: - case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: - case MPI2_EVENT_IR_PHYSICAL_DISK: - break; - - case MPI2_EVENT_TEMP_THRESHOLD: - _scsih_temp_threshold_events(ioc, - (Mpi2EventDataTemperature_t *) - mpi_reply->EventData); - break; - - default: /* ignore the rest */ - return; - } - - sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; - fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC); - if (!fw_event) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - memcpy(fw_event->event_data, mpi_reply->EventData, sz); - fw_event->ioc = ioc; - fw_event->VF_ID = mpi_reply->VF_ID; - fw_event->VP_ID = mpi_reply->VP_ID; - fw_event->event = event; - _scsih_fw_event_add(ioc, fw_event); - return; -} - -/* shost template */ -static struct scsi_host_template scsih_driver_template = { - .module = THIS_MODULE, - .name = "Fusion MPT SAS Host", - .proc_name = MPT2SAS_DRIVER_NAME, - .queuecommand = _scsih_qcmd, - .target_alloc = _scsih_target_alloc, - .slave_alloc = _scsih_slave_alloc, - .slave_configure = _scsih_slave_configure, - .target_destroy = _scsih_target_destroy, - .slave_destroy = _scsih_slave_destroy, - .scan_finished = _scsih_scan_finished, - .scan_start = _scsih_scan_start, - .change_queue_depth = _scsih_change_queue_depth, - .eh_abort_handler = _scsih_abort, - .eh_device_reset_handler = _scsih_dev_reset, - .eh_target_reset_handler = _scsih_target_reset, - .eh_host_reset_handler = _scsih_host_reset, - .bios_param = _scsih_bios_param, - .can_queue = 1, - .this_id = -1, - .sg_tablesize = MPT2SAS_SG_DEPTH, - .max_sectors = 32767, - .cmd_per_lun = 7, - .use_clustering = ENABLE_CLUSTERING, - .shost_attrs = mpt2sas_host_attrs, - .sdev_attrs = mpt2sas_dev_attrs, - .track_queue_depth = 1, -}; - -/** - * _scsih_expander_node_remove - removing expander device from list. - * @ioc: per adapter object - * @sas_expander: the sas_device object - * Context: Calling function should acquire ioc->sas_node_lock. - * - * Removing object and freeing associated memory from the - * ioc->sas_expander_list. - * - * Return nothing. - */ -static void -_scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_expander) -{ - struct _sas_port *mpt2sas_port, *next; - - /* remove sibling ports attached to this expander */ - list_for_each_entry_safe(mpt2sas_port, next, - &sas_expander->sas_port_list, port_list) { - if (ioc->shost_recovery) - return; - if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) - mpt2sas_device_remove_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - else if (mpt2sas_port->remote_identify.device_type == - SAS_EDGE_EXPANDER_DEVICE || - mpt2sas_port->remote_identify.device_type == - SAS_FANOUT_EXPANDER_DEVICE) - mpt2sas_expander_remove(ioc, - mpt2sas_port->remote_identify.sas_address); - } - - mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, - sas_expander->sas_address_parent); - - printk(MPT2SAS_INFO_FMT "expander_remove: handle" - "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, - sas_expander->handle, (unsigned long long) - sas_expander->sas_address); - - kfree(sas_expander->phy); - kfree(sas_expander); -} - -/** - * _scsih_ir_shutdown - IR shutdown notification - * @ioc: per adapter object - * - * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that - * the host system is shutting down. - * - * Return nothing. - */ -static void -_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc) -{ - Mpi2RaidActionRequest_t *mpi_request; - Mpi2RaidActionReply_t *mpi_reply; - u16 smid; - - /* is IR firmware build loaded ? */ - if (!ioc->ir_firmware) - return; - - mutex_lock(&ioc->scsih_cmds.mutex); - - if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n", - ioc->name, __func__); - goto out; - } - ioc->scsih_cmds.status = MPT2_CMD_PENDING; - - smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - ioc->scsih_cmds.status = MPT2_CMD_NOT_USED; - goto out; - } - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->scsih_cmds.smid = smid; - memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t)); - - mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; - mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; - - if (!ioc->hide_ir_msg) - printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name); - init_completion(&ioc->scsih_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); - - if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - goto out; - } - - if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) { - mpi_reply = ioc->scsih_cmds.reply; - - if (!ioc->hide_ir_msg) - printk(MPT2SAS_INFO_FMT "IR shutdown (complete): " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo)); - } - - out: - ioc->scsih_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->scsih_cmds.mutex); -} - -/** - * _scsih_shutdown - routine call during system shutdown - * @pdev: PCI device struct - * - * Return nothing. - */ -static void -_scsih_shutdown(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct workqueue_struct *wq; - unsigned long flags; - - ioc->remove_host = 1; - _scsih_fw_event_cleanup_queue(ioc); - - spin_lock_irqsave(&ioc->fw_event_lock, flags); - wq = ioc->firmware_event_thread; - ioc->firmware_event_thread = NULL; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - if (wq) - destroy_workqueue(wq); - - _scsih_ir_shutdown(ioc); - mpt2sas_base_detach(ioc); -} - -/** - * _scsih_remove - detach and remove add host - * @pdev: PCI device struct - * - * Routine called when unloading the driver. - * Return nothing. - */ -static void -_scsih_remove(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - struct _sas_port *mpt2sas_port, *next_port; - struct _raid_device *raid_device, *next; - struct MPT2SAS_TARGET *sas_target_priv_data; - struct workqueue_struct *wq; - unsigned long flags; - - ioc->remove_host = 1; - _scsih_fw_event_cleanup_queue(ioc); - - spin_lock_irqsave(&ioc->fw_event_lock, flags); - wq = ioc->firmware_event_thread; - ioc->firmware_event_thread = NULL; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - if (wq) - destroy_workqueue(wq); - - /* release all the volumes */ - _scsih_ir_shutdown(ioc); - list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, - list) { - if (raid_device->starget) { - sas_target_priv_data = - raid_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - scsi_remove_target(&raid_device->starget->dev); - } - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid" - "(0x%016llx)\n", ioc->name, raid_device->handle, - (unsigned long long) raid_device->wwid); - _scsih_raid_device_remove(ioc, raid_device); - } - - /* free ports attached to the sas_host */ - list_for_each_entry_safe(mpt2sas_port, next_port, - &ioc->sas_hba.sas_port_list, port_list) { - if (mpt2sas_port->remote_identify.device_type == - SAS_END_DEVICE) - mpt2sas_device_remove_by_sas_address(ioc, - mpt2sas_port->remote_identify.sas_address); - else if (mpt2sas_port->remote_identify.device_type == - SAS_EDGE_EXPANDER_DEVICE || - mpt2sas_port->remote_identify.device_type == - SAS_FANOUT_EXPANDER_DEVICE) - mpt2sas_expander_remove(ioc, - mpt2sas_port->remote_identify.sas_address); - } - - /* free phys attached to the sas_host */ - if (ioc->sas_hba.num_phys) { - kfree(ioc->sas_hba.phy); - ioc->sas_hba.phy = NULL; - ioc->sas_hba.num_phys = 0; - } - - sas_remove_host(shost); - scsi_remove_host(shost); - mpt2sas_base_detach(ioc); - list_del(&ioc->list); - scsi_host_put(shost); -} - -/** - * _scsih_probe_boot_devices - reports 1st device - * @ioc: per adapter object - * - * If specified in bios page 2, this routine reports the 1st - * device scsi-ml or sas transport for persistent boot device - * purposes. Please refer to function _scsih_determine_boot_device() - */ -static void -_scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) -{ - u8 is_raid; - void *device; - struct _sas_device *sas_device; - struct _raid_device *raid_device; - u16 handle; - u64 sas_address_parent; - u64 sas_address; - unsigned long flags; - int rc; - - /* no Bios, return immediately */ - if (!ioc->bios_pg3.BiosVersion) - return; - - device = NULL; - is_raid = 0; - if (ioc->req_boot_device.device) { - device = ioc->req_boot_device.device; - is_raid = ioc->req_boot_device.is_raid; - } else if (ioc->req_alt_boot_device.device) { - device = ioc->req_alt_boot_device.device; - is_raid = ioc->req_alt_boot_device.is_raid; - } else if (ioc->current_boot_device.device) { - device = ioc->current_boot_device.device; - is_raid = ioc->current_boot_device.is_raid; - } - - if (!device) - return; - - if (is_raid) { - raid_device = device; - rc = scsi_add_device(ioc->shost, RAID_CHANNEL, - raid_device->id, 0); - if (rc) - _scsih_raid_device_remove(ioc, raid_device); - } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = device; - handle = sas_device->handle; - sas_address_parent = sas_device->sas_address_parent; - sas_address = sas_device->sas_address; - list_move_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (ioc->hide_drives) - return; - if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { - _scsih_sas_device_remove(ioc, sas_device); - } else if (!sas_device->starget) { - if (!ioc->is_driver_loading) { - mpt2sas_transport_port_remove(ioc, - sas_address, - sas_address_parent); - _scsih_sas_device_remove(ioc, sas_device); - } - } - } -} - -/** - * _scsih_probe_raid - reporting raid volumes to scsi-ml - * @ioc: per adapter object - * - * Called during initial loading of the driver. - */ -static void -_scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) -{ - struct _raid_device *raid_device, *raid_next; - int rc; - - list_for_each_entry_safe(raid_device, raid_next, - &ioc->raid_device_list, list) { - if (raid_device->starget) - continue; - rc = scsi_add_device(ioc->shost, RAID_CHANNEL, - raid_device->id, 0); - if (rc) - _scsih_raid_device_remove(ioc, raid_device); - } -} - -/** - * _scsih_probe_sas - reporting sas devices to sas transport - * @ioc: per adapter object - * - * Called during initial loading of the driver. - */ -static void -_scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) -{ - struct _sas_device *sas_device, *next; - unsigned long flags; - - /* SAS Device List */ - list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, - list) { - - if (ioc->hide_drives) - continue; - - if (!mpt2sas_transport_port_add(ioc, sas_device->handle, - sas_device->sas_address_parent)) { - list_del(&sas_device->list); - kfree(sas_device); - continue; - } else if (!sas_device->starget) { - if (!ioc->is_driver_loading) { - mpt2sas_transport_port_remove(ioc, - sas_device->sas_address, - sas_device->sas_address_parent); - list_del(&sas_device->list); - kfree(sas_device); - continue; - } - } - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_move_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - } -} - -/** - * _scsih_probe_devices - probing for devices - * @ioc: per adapter object - * - * Called during initial loading of the driver. - */ -static void -_scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc) -{ - u16 volume_mapping_flags; - - if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) - return; /* return when IOC doesn't support initiator mode */ - - _scsih_probe_boot_devices(ioc); - - if (ioc->ir_firmware) { - volume_mapping_flags = - le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & - MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; - if (volume_mapping_flags == - MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) { - _scsih_probe_raid(ioc); - _scsih_probe_sas(ioc); - } else { - _scsih_probe_sas(ioc); - _scsih_probe_raid(ioc); - } - } else - _scsih_probe_sas(ioc); -} - - -/** - * _scsih_scan_start - scsi lld callback for .scan_start - * @shost: SCSI host pointer - * - * The shost has the ability to discover targets on its own instead - * of scanning the entire bus. In our implemention, we will kick off - * firmware discovery. - */ -static void -_scsih_scan_start(struct Scsi_Host *shost) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - int rc; - - if (diag_buffer_enable != -1 && diag_buffer_enable != 0) - mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); - - if (disable_discovery > 0) - return; - - ioc->start_scan = 1; - rc = mpt2sas_port_enable(ioc); - - if (rc != 0) - printk(MPT2SAS_INFO_FMT "port enable: FAILED\n", ioc->name); -} - -/** - * _scsih_scan_finished - scsi lld callback for .scan_finished - * @shost: SCSI host pointer - * @time: elapsed time of the scan in jiffies - * - * This function will be called periodically until it returns 1 with the - * scsi_host and the elapsed time of the scan in jiffies. In our implemention, - * we wait for firmware discovery to complete, then return 1. - */ -static int -_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - if (disable_discovery > 0) { - ioc->is_driver_loading = 0; - ioc->wait_for_discovery_to_complete = 0; - return 1; - } - - if (time >= (300 * HZ)) { - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - printk(MPT2SAS_INFO_FMT "port enable: FAILED with timeout " - "(timeout=300s)\n", ioc->name); - ioc->is_driver_loading = 0; - return 1; - } - - if (ioc->start_scan) - return 0; - - if (ioc->start_scan_failed) { - printk(MPT2SAS_INFO_FMT "port enable: FAILED with " - "(ioc_status=0x%08x)\n", ioc->name, ioc->start_scan_failed); - ioc->is_driver_loading = 0; - ioc->wait_for_discovery_to_complete = 0; - ioc->remove_host = 1; - return 1; - } - - printk(MPT2SAS_INFO_FMT "port enable: SUCCESS\n", ioc->name); - ioc->base_cmds.status = MPT2_CMD_NOT_USED; - - if (ioc->wait_for_discovery_to_complete) { - ioc->wait_for_discovery_to_complete = 0; - _scsih_probe_devices(ioc); - } - mpt2sas_base_start_watchdog(ioc); - ioc->is_driver_loading = 0; - return 1; -} - - -/** - * _scsih_probe - attach and add scsi host - * @pdev: PCI device struct - * @id: pci device id - * - * Returns 0 success, anything else error. - */ -static int -_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct MPT2SAS_ADAPTER *ioc; - struct Scsi_Host *shost; - int rv; - - shost = scsi_host_alloc(&scsih_driver_template, - sizeof(struct MPT2SAS_ADAPTER)); - if (!shost) - return -ENODEV; - - /* init local params */ - ioc = shost_priv(shost); - memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER)); - INIT_LIST_HEAD(&ioc->list); - list_add_tail(&ioc->list, &mpt2sas_ioc_list); - ioc->shost = shost; - ioc->id = mpt_ids++; - sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id); - ioc->pdev = pdev; - if (id->device == MPI2_MFGPAGE_DEVID_SSS6200) { - ioc->is_warpdrive = 1; - ioc->hide_ir_msg = 1; - } else - ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; - ioc->scsi_io_cb_idx = scsi_io_cb_idx; - ioc->tm_cb_idx = tm_cb_idx; - ioc->ctl_cb_idx = ctl_cb_idx; - ioc->base_cb_idx = base_cb_idx; - ioc->port_enable_cb_idx = port_enable_cb_idx; - ioc->transport_cb_idx = transport_cb_idx; - ioc->scsih_cb_idx = scsih_cb_idx; - ioc->config_cb_idx = config_cb_idx; - ioc->tm_tr_cb_idx = tm_tr_cb_idx; - ioc->tm_tr_volume_cb_idx = tm_tr_volume_cb_idx; - ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx; - ioc->logging_level = logging_level; - ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; - /* misc semaphores and spin locks */ - mutex_init(&ioc->reset_in_progress_mutex); - spin_lock_init(&ioc->ioc_reset_in_progress_lock); - spin_lock_init(&ioc->scsi_lookup_lock); - spin_lock_init(&ioc->sas_device_lock); - spin_lock_init(&ioc->sas_node_lock); - spin_lock_init(&ioc->fw_event_lock); - spin_lock_init(&ioc->raid_device_lock); - - INIT_LIST_HEAD(&ioc->sas_device_list); - INIT_LIST_HEAD(&ioc->sas_device_init_list); - INIT_LIST_HEAD(&ioc->sas_expander_list); - INIT_LIST_HEAD(&ioc->fw_event_list); - INIT_LIST_HEAD(&ioc->raid_device_list); - INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); - INIT_LIST_HEAD(&ioc->delayed_tr_list); - INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); - INIT_LIST_HEAD(&ioc->reply_queue_list); - - /* init shost parameters */ - shost->max_cmd_len = 32; - shost->max_lun = max_lun; - shost->transportt = mpt2sas_transport_template; - shost->unique_id = ioc->id; - - if (max_sectors != 0xFFFF) { - if (max_sectors < 64) { - shost->max_sectors = 64; - printk(MPT2SAS_WARN_FMT "Invalid value %d passed " - "for max_sectors, range is 64 to 32767. Assigning " - "value of 64.\n", ioc->name, max_sectors); - } else if (max_sectors > 32767) { - shost->max_sectors = 32767; - printk(MPT2SAS_WARN_FMT "Invalid value %d passed " - "for max_sectors, range is 64 to 8192. Assigning " - "default value of 32767.\n", ioc->name, - max_sectors); - } else { - shost->max_sectors = max_sectors & 0xFFFE; - printk(MPT2SAS_INFO_FMT "The max_sectors value is " - "set to %d\n", ioc->name, shost->max_sectors); - } - } - - /* register EEDP capabilities with SCSI layer */ - if (prot_mask) - scsi_host_set_prot(shost, prot_mask); - else - scsi_host_set_prot(shost, SHOST_DIF_TYPE1_PROTECTION - | SHOST_DIF_TYPE2_PROTECTION - | SHOST_DIF_TYPE3_PROTECTION); - - scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); - - /* event thread */ - snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), - "fw_event%d", ioc->id); - ioc->firmware_event_thread = create_singlethread_workqueue( - ioc->firmware_event_name); - if (!ioc->firmware_event_thread) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rv = -ENODEV; - goto out_thread_fail; - } - - ioc->is_driver_loading = 1; - if ((mpt2sas_base_attach(ioc))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rv = -ENODEV; - goto out_attach_fail; - } - - if (ioc->is_warpdrive) { - if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) - ioc->hide_drives = 0; - else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS) - ioc->hide_drives = 1; - else { - if (_scsih_get_num_volumes(ioc)) - ioc->hide_drives = 1; - else - ioc->hide_drives = 0; - } - } else - ioc->hide_drives = 0; - - rv = scsi_add_host(shost, &pdev->dev); - if (rv) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out_add_shost_fail; - } - - scsi_scan_host(shost); - - return 0; - - out_add_shost_fail: - mpt2sas_base_detach(ioc); - out_attach_fail: - destroy_workqueue(ioc->firmware_event_thread); - out_thread_fail: - list_del(&ioc->list); - scsi_host_put(shost); - return rv; -} - -#ifdef CONFIG_PM -/** - * _scsih_suspend - power management suspend main entry point - * @pdev: PCI device struct - * @state: PM state change to (usually PCI_D3) - * - * Returns 0 success, anything else error. - */ -static int -_scsih_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - pci_power_t device_state; - - mpt2sas_base_stop_watchdog(ioc); - scsi_block_requests(shost); - _scsih_ir_shutdown(ioc); - device_state = pci_choose_state(pdev, state); - printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, entering " - "operating state [D%d]\n", ioc->name, pdev, - pci_name(pdev), device_state); - - mpt2sas_base_free_resources(ioc); - pci_save_state(pdev); - pci_set_power_state(pdev, device_state); - return 0; -} - -/** - * _scsih_resume - power management resume main entry point - * @pdev: PCI device struct - * - * Returns 0 success, anything else error. - */ -static int -_scsih_resume(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - pci_power_t device_state = pdev->current_state; - int r; - - printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, previous " - "operating state [D%d]\n", ioc->name, pdev, - pci_name(pdev), device_state); - - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, PCI_D0, 0); - pci_restore_state(pdev); - ioc->pdev = pdev; - r = mpt2sas_base_map_resources(ioc); - if (r) - return r; - - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); - scsi_unblock_requests(shost); - mpt2sas_base_start_watchdog(ioc); - return 0; -} -#endif /* CONFIG_PM */ - -/** - * _scsih_pci_error_detected - Called when a PCI error is detected. - * @pdev: PCI device struct - * @state: PCI channel state - * - * Description: Called when a PCI error is detected. - * - * Return value: - * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT - */ -static pci_ers_result_t -_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n", - ioc->name, state); - - switch (state) { - case pci_channel_io_normal: - return PCI_ERS_RESULT_CAN_RECOVER; - case pci_channel_io_frozen: - /* Fatal error, prepare for slot reset */ - ioc->pci_error_recovery = 1; - scsi_block_requests(ioc->shost); - mpt2sas_base_stop_watchdog(ioc); - mpt2sas_base_free_resources(ioc); - return PCI_ERS_RESULT_NEED_RESET; - case pci_channel_io_perm_failure: - /* Permanent error, prepare for device removal */ - ioc->pci_error_recovery = 1; - mpt2sas_base_stop_watchdog(ioc); - _scsih_flush_running_cmds(ioc); - return PCI_ERS_RESULT_DISCONNECT; - } - return PCI_ERS_RESULT_NEED_RESET; -} - -/** - * _scsih_pci_slot_reset - Called when PCI slot has been reset. - * @pdev: PCI device struct - * - * Description: This routine is called by the pci error recovery - * code after the PCI slot has been reset, just before we - * should resume normal operations. - */ -static pci_ers_result_t -_scsih_pci_slot_reset(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - int rc; - - printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n", - ioc->name); - - ioc->pci_error_recovery = 0; - ioc->pdev = pdev; - pci_restore_state(pdev); - rc = mpt2sas_base_map_resources(ioc); - if (rc) - return PCI_ERS_RESULT_DISCONNECT; - - - rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - - printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name, - (rc == 0) ? "success" : "failed"); - - if (!rc) - return PCI_ERS_RESULT_RECOVERED; - else - return PCI_ERS_RESULT_DISCONNECT; -} - -/** - * _scsih_pci_resume() - resume normal ops after PCI reset - * @pdev: pointer to PCI device - * - * Called when the error recovery driver tells us that its - * OK to resume normal operation. Use completion to allow - * halted scsi ops to resume. - */ -static void -_scsih_pci_resume(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name); - - pci_cleanup_aer_uncorrect_error_status(pdev); - mpt2sas_base_start_watchdog(ioc); - scsi_unblock_requests(ioc->shost); -} - -/** - * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers - * @pdev: pointer to PCI device - */ -static pci_ers_result_t -_scsih_pci_mmio_enabled(struct pci_dev *pdev) -{ - struct Scsi_Host *shost = pci_get_drvdata(pdev); - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - - printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n", - ioc->name); - - /* TODO - dump whatever for debugging purposes */ - - /* Request a slot reset. */ - return PCI_ERS_RESULT_NEED_RESET; -} - -static const struct pci_error_handlers _scsih_err_handler = { - .error_detected = _scsih_pci_error_detected, - .mmio_enabled = _scsih_pci_mmio_enabled, - .slot_reset = _scsih_pci_slot_reset, - .resume = _scsih_pci_resume, -}; - -static struct pci_driver scsih_driver = { - .name = MPT2SAS_DRIVER_NAME, - .id_table = scsih_pci_table, - .probe = _scsih_probe, - .remove = _scsih_remove, - .shutdown = _scsih_shutdown, - .err_handler = &_scsih_err_handler, -#ifdef CONFIG_PM - .suspend = _scsih_suspend, - .resume = _scsih_resume, -#endif -}; - -/* raid transport support */ -static struct raid_function_template mpt2sas_raid_functions = { - .cookie = &scsih_driver_template, - .is_raid = _scsih_is_raid, - .get_resync = _scsih_get_resync, - .get_state = _scsih_get_state, -}; - -/** - * _scsih_init - main entry point for this driver. - * - * Returns 0 success, anything else error. - */ -static int __init -_scsih_init(void) -{ - int error; - - mpt_ids = 0; - printk(KERN_INFO "%s version %s loaded\n", MPT2SAS_DRIVER_NAME, - MPT2SAS_DRIVER_VERSION); - - mpt2sas_transport_template = - sas_attach_transport(&mpt2sas_transport_functions); - if (!mpt2sas_transport_template) - return -ENODEV; - /* raid transport support */ - mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions); - if (!mpt2sas_raid_template) { - sas_release_transport(mpt2sas_transport_template); - return -ENODEV; - } - - mpt2sas_base_initialize_callback_handler(); - - /* queuecommand callback hander */ - scsi_io_cb_idx = mpt2sas_base_register_callback_handler(_scsih_io_done); - - /* task management callback handler */ - tm_cb_idx = mpt2sas_base_register_callback_handler(_scsih_tm_done); - - /* base internal commands callback handler */ - base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); - port_enable_cb_idx = mpt2sas_base_register_callback_handler( - mpt2sas_port_enable_done); - - /* transport internal commands callback handler */ - transport_cb_idx = mpt2sas_base_register_callback_handler( - mpt2sas_transport_done); - - /* scsih internal commands callback handler */ - scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done); - - /* configuration page API internal commands callback handler */ - config_cb_idx = mpt2sas_base_register_callback_handler( - mpt2sas_config_done); - - /* ctl module callback handler */ - ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); - - tm_tr_cb_idx = mpt2sas_base_register_callback_handler( - _scsih_tm_tr_complete); - - tm_tr_volume_cb_idx = mpt2sas_base_register_callback_handler( - _scsih_tm_volume_tr_complete); - - tm_sas_control_cb_idx = mpt2sas_base_register_callback_handler( - _scsih_sas_control_complete); - - mpt2sas_ctl_init(); - - error = pci_register_driver(&scsih_driver); - if (error) { - /* raid transport support */ - raid_class_release(mpt2sas_raid_template); - sas_release_transport(mpt2sas_transport_template); - } - - return error; -} - -/** - * _scsih_exit - exit point for this driver (when it is a module). - * - * Returns 0 success, anything else error. - */ -static void __exit -_scsih_exit(void) -{ - printk(KERN_INFO "mpt2sas version %s unloading\n", - MPT2SAS_DRIVER_VERSION); - - pci_unregister_driver(&scsih_driver); - - mpt2sas_ctl_exit(); - - mpt2sas_base_release_callback_handler(scsi_io_cb_idx); - mpt2sas_base_release_callback_handler(tm_cb_idx); - mpt2sas_base_release_callback_handler(base_cb_idx); - mpt2sas_base_release_callback_handler(port_enable_cb_idx); - mpt2sas_base_release_callback_handler(transport_cb_idx); - mpt2sas_base_release_callback_handler(scsih_cb_idx); - mpt2sas_base_release_callback_handler(config_cb_idx); - mpt2sas_base_release_callback_handler(ctl_cb_idx); - - mpt2sas_base_release_callback_handler(tm_tr_cb_idx); - mpt2sas_base_release_callback_handler(tm_tr_volume_cb_idx); - mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); - - /* raid transport support */ - raid_class_release(mpt2sas_raid_template); - sas_release_transport(mpt2sas_transport_template); - -} - -module_init(_scsih_init); -module_exit(_scsih_exit); diff --git a/kernel/drivers/scsi/mpt2sas/mpt2sas_transport.c b/kernel/drivers/scsi/mpt2sas/mpt2sas_transport.c deleted file mode 100644 index ff2500ab9..000000000 --- a/kernel/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ /dev/null @@ -1,2169 +0,0 @@ -/* - * SAS Transport Layer for MPT (Message Passing Technology) based controllers - * - * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2014 LSI Corporation - * Copyright (C) 20013-2014 Avago Technologies - * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 - * of the License, or (at your option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mpt2sas_base.h" -/** - * _transport_sas_node_find_by_sas_address - sas node search - * @ioc: per adapter object - * @sas_address: sas address of expander or sas host - * Context: Calling function should acquire ioc->sas_node_lock. - * - * Search for either hba phys or expander device based on handle, then returns - * the sas_node object. - */ -static struct _sas_node * -_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address) -{ - if (ioc->sas_hba.sas_address == sas_address) - return &ioc->sas_hba; - else - return mpt2sas_scsih_expander_find_by_sas_address(ioc, - sas_address); -} - -/** - * _transport_convert_phy_link_rate - - * @link_rate: link rate returned from mpt firmware - * - * Convert link_rate from mpi fusion into sas_transport form. - */ -static enum sas_linkrate -_transport_convert_phy_link_rate(u8 link_rate) -{ - enum sas_linkrate rc; - - switch (link_rate) { - case MPI2_SAS_NEG_LINK_RATE_1_5: - rc = SAS_LINK_RATE_1_5_GBPS; - break; - case MPI2_SAS_NEG_LINK_RATE_3_0: - rc = SAS_LINK_RATE_3_0_GBPS; - break; - case MPI2_SAS_NEG_LINK_RATE_6_0: - rc = SAS_LINK_RATE_6_0_GBPS; - break; - case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: - rc = SAS_PHY_DISABLED; - break; - case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED: - rc = SAS_LINK_RATE_FAILED; - break; - case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR: - rc = SAS_SATA_PORT_SELECTOR; - break; - case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS: - rc = SAS_PHY_RESET_IN_PROGRESS; - break; - default: - case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE: - case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE: - rc = SAS_LINK_RATE_UNKNOWN; - break; - } - return rc; -} - -/** - * _transport_set_identify - set identify for phys and end devices - * @ioc: per adapter object - * @handle: device handle - * @identify: sas identify info - * - * Populates sas identify info. - * - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, - struct sas_identify *identify) -{ - Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2ConfigReply_t mpi_reply; - u32 device_info; - u32 ioc_status; - - if (ioc->shost_recovery || ioc->pci_error_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return -EFAULT; - } - - if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, - MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - - ioc->name, __FILE__, __LINE__, __func__); - return -ENXIO; - } - - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)" - "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, - __FILE__, __LINE__, __func__); - return -EIO; - } - - memset(identify, 0, sizeof(struct sas_identify)); - device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); - - /* sas_address */ - identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - - /* phy number of the parent device this device is linked to */ - identify->phy_identifier = sas_device_pg0.PhyNum; - - /* device_type */ - switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { - case MPI2_SAS_DEVICE_INFO_NO_DEVICE: - identify->device_type = SAS_PHY_UNUSED; - break; - case MPI2_SAS_DEVICE_INFO_END_DEVICE: - identify->device_type = SAS_END_DEVICE; - break; - case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER: - identify->device_type = SAS_EDGE_EXPANDER_DEVICE; - break; - case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER: - identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; - break; - } - - /* initiator_port_protocols */ - if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR) - identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; - if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR) - identify->initiator_port_protocols |= SAS_PROTOCOL_STP; - if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR) - identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; - if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST) - identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; - - /* target_port_protocols */ - if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) - identify->target_port_protocols |= SAS_PROTOCOL_SSP; - if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) - identify->target_port_protocols |= SAS_PROTOCOL_STP; - if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) - identify->target_port_protocols |= SAS_PROTOCOL_SMP; - if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - identify->target_port_protocols |= SAS_PROTOCOL_SATA; - - return 0; -} - -/** - * mpt2sas_transport_done - internal transport layer callback handler. - * @ioc: per adapter object - * @smid: system request message index - * @msix_index: MSIX table index supplied by the OS - * @reply: reply message frame(lower 32bit addr) - * - * Callback handler when sending internal generated transport cmds. - * The callback index passed is `ioc->transport_cb_idx` - * - * Return 1 meaning mf should be freed from _base_interrupt - * 0 means the mf is freed from this function. - */ -u8 -mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, - u32 reply) -{ - MPI2DefaultReply_t *mpi_reply; - - mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED) - return 1; - if (ioc->transport_cmds.smid != smid) - return 1; - ioc->transport_cmds.status |= MPT2_CMD_COMPLETE; - if (mpi_reply) { - memcpy(ioc->transport_cmds.reply, mpi_reply, - mpi_reply->MsgLength*4); - ioc->transport_cmds.status |= MPT2_CMD_REPLY_VALID; - } - ioc->transport_cmds.status &= ~MPT2_CMD_PENDING; - complete(&ioc->transport_cmds.done); - return 1; -} - -/* report manufacture request structure */ -struct rep_manu_request{ - u8 smp_frame_type; - u8 function; - u8 reserved; - u8 request_length; -}; - -/* report manufacture reply structure */ -struct rep_manu_reply{ - u8 smp_frame_type; /* 0x41 */ - u8 function; /* 0x01 */ - u8 function_result; - u8 response_length; - u16 expander_change_count; - u8 reserved0[2]; - u8 sas_format; - u8 reserved2[3]; - u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; - u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; - u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; - u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; - u16 component_id; - u8 component_revision_id; - u8 reserved3; - u8 vendor_specific[8]; -}; - -/** - * _transport_expander_report_manufacture - obtain SMP report_manufacture - * @ioc: per adapter object - * @sas_address: expander sas address - * @edev: the sas_expander_device object - * - * Fills in the sas_expander_device object when SMP port is created. - * - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address, struct sas_expander_device *edev) -{ - Mpi2SmpPassthroughRequest_t *mpi_request; - Mpi2SmpPassthroughReply_t *mpi_reply; - struct rep_manu_reply *manufacture_reply; - struct rep_manu_request *manufacture_request; - int rc; - u16 smid; - u32 ioc_state; - unsigned long timeleft; - void *psge; - u32 sgl_flags; - u8 issue_reset = 0; - void *data_out = NULL; - dma_addr_t data_out_dma; - u32 sz; - u16 wait_state_count; - - if (ioc->shost_recovery || ioc->pci_error_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return -EFAULT; - } - - mutex_lock(&ioc->transport_cmds.mutex); - - if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - ioc->transport_cmds.status = MPT2_CMD_PENDING; - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - rc = 0; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->transport_cmds.smid = smid; - - sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply); - data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); - - if (!data_out) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - rc = -ENOMEM; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - - manufacture_request = data_out; - manufacture_request->smp_frame_type = 0x40; - manufacture_request->function = 1; - manufacture_request->reserved = 0; - manufacture_request->request_length = 0; - - memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - mpi_request->SASAddress = cpu_to_le64(sas_address); - mpi_request->RequestDataLength = - cpu_to_le16(sizeof(struct rep_manu_request)); - psge = &mpi_request->SGL; - - /* WRITE sgel first */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct rep_manu_request), data_out_dma); - - /* incr sgel */ - psge += ioc->sge_size; - - /* READ sgel last */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct rep_manu_reply), data_out_dma + - sizeof(struct rep_manu_request)); - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - " - "send to sas_addr(0x%016llx)\n", ioc->name, - (unsigned long long)sas_address)); - init_completion(&ioc->transport_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); - - if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SmpPassthroughRequest_t)/4); - if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "report_manufacture - " - "complete\n", ioc->name)); - - if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { - u8 *tmp; - - mpi_reply = ioc->transport_cmds.reply; - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "report_manufacture - reply data transfer size(%d)\n", - ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); - - if (le16_to_cpu(mpi_reply->ResponseDataLength) != - sizeof(struct rep_manu_reply)) - goto out; - - manufacture_reply = data_out + sizeof(struct rep_manu_request); - strncpy(edev->vendor_id, manufacture_reply->vendor_id, - SAS_EXPANDER_VENDOR_ID_LEN); - strncpy(edev->product_id, manufacture_reply->product_id, - SAS_EXPANDER_PRODUCT_ID_LEN); - strncpy(edev->product_rev, manufacture_reply->product_rev, - SAS_EXPANDER_PRODUCT_REV_LEN); - edev->level = manufacture_reply->sas_format & 1; - if (edev->level) { - strncpy(edev->component_vendor_id, - manufacture_reply->component_vendor_id, - SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); - tmp = (u8 *)&manufacture_reply->component_id; - edev->component_id = tmp[0] << 8 | tmp[1]; - edev->component_revision_id = - manufacture_reply->component_revision_id; - } - } else - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "report_manufacture - no reply\n", ioc->name)); - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - out: - ioc->transport_cmds.status = MPT2_CMD_NOT_USED; - if (data_out) - pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); - - mutex_unlock(&ioc->transport_cmds.mutex); - return rc; -} - -/** - * _transport_delete_port - helper function to removing a port - * @ioc: per adapter object - * @mpt2sas_port: mpt2sas per port object - * - * Returns nothing. - */ -static void -_transport_delete_port(struct MPT2SAS_ADAPTER *ioc, - struct _sas_port *mpt2sas_port) -{ - u64 sas_address = mpt2sas_port->remote_identify.sas_address; - enum sas_device_type device_type = - mpt2sas_port->remote_identify.device_type; - - dev_printk(KERN_INFO, &mpt2sas_port->port->dev, - "remove: sas_addr(0x%016llx)\n", - (unsigned long long) sas_address); - - ioc->logging_level |= MPT_DEBUG_TRANSPORT; - if (device_type == SAS_END_DEVICE) - mpt2sas_device_remove_by_sas_address(ioc, sas_address); - else if (device_type == SAS_EDGE_EXPANDER_DEVICE || - device_type == SAS_FANOUT_EXPANDER_DEVICE) - mpt2sas_expander_remove(ioc, sas_address); - ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; -} - -/** - * _transport_delete_phy - helper function to removing single phy from port - * @ioc: per adapter object - * @mpt2sas_port: mpt2sas per port object - * @mpt2sas_phy: mpt2sas per phy object - * - * Returns nothing. - */ -static void -_transport_delete_phy(struct MPT2SAS_ADAPTER *ioc, - struct _sas_port *mpt2sas_port, struct _sas_phy *mpt2sas_phy) -{ - u64 sas_address = mpt2sas_port->remote_identify.sas_address; - - dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, - "remove: sas_addr(0x%016llx), phy(%d)\n", - (unsigned long long) sas_address, mpt2sas_phy->phy_id); - - list_del(&mpt2sas_phy->port_siblings); - mpt2sas_port->num_phys--; - sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); - mpt2sas_phy->phy_belongs_to_port = 0; -} - -/** - * _transport_add_phy - helper function to adding single phy to port - * @ioc: per adapter object - * @mpt2sas_port: mpt2sas per port object - * @mpt2sas_phy: mpt2sas per phy object - * - * Returns nothing. - */ -static void -_transport_add_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_port *mpt2sas_port, - struct _sas_phy *mpt2sas_phy) -{ - u64 sas_address = mpt2sas_port->remote_identify.sas_address; - - dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, - "add: sas_addr(0x%016llx), phy(%d)\n", (unsigned long long) - sas_address, mpt2sas_phy->phy_id); - - list_add_tail(&mpt2sas_phy->port_siblings, &mpt2sas_port->phy_list); - mpt2sas_port->num_phys++; - sas_port_add_phy(mpt2sas_port->port, mpt2sas_phy->phy); - mpt2sas_phy->phy_belongs_to_port = 1; -} - -/** - * _transport_add_phy_to_an_existing_port - adding new phy to existing port - * @ioc: per adapter object - * @sas_node: sas node object (either expander or sas host) - * @mpt2sas_phy: mpt2sas per phy object - * @sas_address: sas address of device/expander were phy needs to be added to - * - * Returns nothing. - */ -static void -_transport_add_phy_to_an_existing_port(struct MPT2SAS_ADAPTER *ioc, -struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy, u64 sas_address) -{ - struct _sas_port *mpt2sas_port; - struct _sas_phy *phy_srch; - - if (mpt2sas_phy->phy_belongs_to_port == 1) - return; - - list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, - port_list) { - if (mpt2sas_port->remote_identify.sas_address != - sas_address) - continue; - list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, - port_siblings) { - if (phy_srch == mpt2sas_phy) - return; - } - _transport_add_phy(ioc, mpt2sas_port, mpt2sas_phy); - return; - } - -} - -/** - * _transport_del_phy_from_an_existing_port - delete phy from existing port - * @ioc: per adapter object - * @sas_node: sas node object (either expander or sas host) - * @mpt2sas_phy: mpt2sas per phy object - * - * Returns nothing. - */ -static void -_transport_del_phy_from_an_existing_port(struct MPT2SAS_ADAPTER *ioc, - struct _sas_node *sas_node, struct _sas_phy *mpt2sas_phy) -{ - struct _sas_port *mpt2sas_port, *next; - struct _sas_phy *phy_srch; - - if (mpt2sas_phy->phy_belongs_to_port == 0) - return; - - list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, - port_list) { - list_for_each_entry(phy_srch, &mpt2sas_port->phy_list, - port_siblings) { - if (phy_srch != mpt2sas_phy) - continue; - if (mpt2sas_port->num_phys == 1) - _transport_delete_port(ioc, mpt2sas_port); - else - _transport_delete_phy(ioc, mpt2sas_port, - mpt2sas_phy); - return; - } - } -} - -/** - * _transport_sanity_check - sanity check when adding a new port - * @ioc: per adapter object - * @sas_node: sas node object (either expander or sas host) - * @sas_address: sas address of device being added - * - * See the explanation above from _transport_delete_duplicate_port - */ -static void -_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node, - u64 sas_address) -{ - int i; - - for (i = 0; i < sas_node->num_phys; i++) { - if (sas_node->phy[i].remote_identify.sas_address != sas_address) - continue; - if (sas_node->phy[i].phy_belongs_to_port == 1) - _transport_del_phy_from_an_existing_port(ioc, sas_node, - &sas_node->phy[i]); - } -} - -/** - * mpt2sas_transport_port_add - insert port to the list - * @ioc: per adapter object - * @handle: handle of attached device - * @sas_address: sas address of parent expander or sas host - * Context: This function will acquire ioc->sas_node_lock. - * - * Adding new port object to the sas_node->sas_port_list. - * - * Returns mpt2sas_port. - */ -struct _sas_port * -mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, - u64 sas_address) -{ - struct _sas_phy *mpt2sas_phy, *next; - struct _sas_port *mpt2sas_port; - unsigned long flags; - struct _sas_node *sas_node; - struct sas_rphy *rphy; - int i; - struct sas_port *port; - - mpt2sas_port = kzalloc(sizeof(struct _sas_port), - GFP_KERNEL); - if (!mpt2sas_port) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return NULL; - } - - INIT_LIST_HEAD(&mpt2sas_port->port_list); - INIT_LIST_HEAD(&mpt2sas_port->phy_list); - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - if (!sas_node) { - printk(MPT2SAS_ERR_FMT "%s: Could not find " - "parent sas_address(0x%016llx)!\n", ioc->name, - __func__, (unsigned long long)sas_address); - goto out_fail; - } - - if ((_transport_set_identify(ioc, handle, - &mpt2sas_port->remote_identify))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out_fail; - } - - if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out_fail; - } - - _transport_sanity_check(ioc, sas_node, - mpt2sas_port->remote_identify.sas_address); - - for (i = 0; i < sas_node->num_phys; i++) { - if (sas_node->phy[i].remote_identify.sas_address != - mpt2sas_port->remote_identify.sas_address) - continue; - list_add_tail(&sas_node->phy[i].port_siblings, - &mpt2sas_port->phy_list); - mpt2sas_port->num_phys++; - } - - if (!mpt2sas_port->num_phys) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out_fail; - } - - port = sas_port_alloc_num(sas_node->parent_dev); - if ((sas_port_add(port))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - goto out_fail; - } - - list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, - port_siblings) { - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &port->dev, "add: handle(0x%04x)" - ", sas_addr(0x%016llx), phy(%d)\n", handle, - (unsigned long long) - mpt2sas_port->remote_identify.sas_address, - mpt2sas_phy->phy_id); - sas_port_add_phy(port, mpt2sas_phy->phy); - mpt2sas_phy->phy_belongs_to_port = 1; - } - - mpt2sas_port->port = port; - if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) - rphy = sas_end_device_alloc(port); - else - rphy = sas_expander_alloc(port, - mpt2sas_port->remote_identify.device_type); - - rphy->identify = mpt2sas_port->remote_identify; - if ((sas_rphy_add(rphy))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - } - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), " - "sas_addr(0x%016llx)\n", handle, - (unsigned long long) - mpt2sas_port->remote_identify.sas_address); - mpt2sas_port->rphy = rphy; - spin_lock_irqsave(&ioc->sas_node_lock, flags); - list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list); - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - /* fill in report manufacture */ - if (mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || - mpt2sas_port->remote_identify.device_type == - MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) - _transport_expander_report_manufacture(ioc, - mpt2sas_port->remote_identify.sas_address, - rphy_to_expander_device(rphy)); - - return mpt2sas_port; - - out_fail: - list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list, - port_siblings) - list_del(&mpt2sas_phy->port_siblings); - kfree(mpt2sas_port); - return NULL; -} - -/** - * mpt2sas_transport_port_remove - remove port from the list - * @ioc: per adapter object - * @sas_address: sas address of attached device - * @sas_address_parent: sas address of parent expander or sas host - * Context: This function will acquire ioc->sas_node_lock. - * - * Removing object and freeing associated memory from the - * ioc->sas_port_list. - * - * Return nothing. - */ -void -mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, - u64 sas_address_parent) -{ - int i; - unsigned long flags; - struct _sas_port *mpt2sas_port, *next; - struct _sas_node *sas_node; - u8 found = 0; - struct _sas_phy *mpt2sas_phy, *next_phy; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_sas_address(ioc, - sas_address_parent); - if (!sas_node) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return; - } - list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, - port_list) { - if (mpt2sas_port->remote_identify.sas_address != sas_address) - continue; - found = 1; - list_del(&mpt2sas_port->port_list); - goto out; - } - out: - if (!found) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return; - } - - for (i = 0; i < sas_node->num_phys; i++) { - if (sas_node->phy[i].remote_identify.sas_address == sas_address) - memset(&sas_node->phy[i].remote_identify, 0 , - sizeof(struct sas_identify)); - } - - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - list_for_each_entry_safe(mpt2sas_phy, next_phy, - &mpt2sas_port->phy_list, port_siblings) { - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &mpt2sas_port->port->dev, - "remove: sas_addr(0x%016llx), phy(%d)\n", - (unsigned long long) - mpt2sas_port->remote_identify.sas_address, - mpt2sas_phy->phy_id); - mpt2sas_phy->phy_belongs_to_port = 0; - sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); - list_del(&mpt2sas_phy->port_siblings); - } - sas_port_delete(mpt2sas_port->port); - kfree(mpt2sas_port); -} - -/** - * mpt2sas_transport_add_host_phy - report sas_host phy to transport - * @ioc: per adapter object - * @mpt2sas_phy: mpt2sas per phy object - * @phy_pg0: sas phy page 0 - * @parent_dev: parent device class object - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy - *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev) -{ - struct sas_phy *phy; - int phy_index = mpt2sas_phy->phy_id; - - - INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); - phy = sas_phy_alloc(parent_dev, phy_index); - if (!phy) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - if ((_transport_set_identify(ioc, mpt2sas_phy->handle, - &mpt2sas_phy->identify))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - phy->identify = mpt2sas_phy->identify; - mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle); - if (mpt2sas_phy->attached_handle) - _transport_set_identify(ioc, mpt2sas_phy->attached_handle, - &mpt2sas_phy->remote_identify); - phy->identify.phy_identifier = mpt2sas_phy->phy_id; - phy->negotiated_linkrate = _transport_convert_phy_link_rate( - phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); - phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( - phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); - phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( - phy_pg0.HwLinkRate >> 4); - phy->minimum_linkrate = _transport_convert_phy_link_rate( - phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); - phy->maximum_linkrate = _transport_convert_phy_link_rate( - phy_pg0.ProgrammedLinkRate >> 4); - - if ((sas_phy_add(phy))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - sas_phy_free(phy); - return -1; - } - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &phy->dev, - "add: handle(0x%04x), sas_addr(0x%016llx)\n" - "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", - mpt2sas_phy->handle, (unsigned long long) - mpt2sas_phy->identify.sas_address, - mpt2sas_phy->attached_handle, - (unsigned long long) - mpt2sas_phy->remote_identify.sas_address); - mpt2sas_phy->phy = phy; - return 0; -} - - -/** - * mpt2sas_transport_add_expander_phy - report expander phy to transport - * @ioc: per adapter object - * @mpt2sas_phy: mpt2sas per phy object - * @expander_pg1: expander page 1 - * @parent_dev: parent device class object - * - * Returns 0 for success, non-zero for failure. - */ -int -mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy - *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev) -{ - struct sas_phy *phy; - int phy_index = mpt2sas_phy->phy_id; - - INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); - phy = sas_phy_alloc(parent_dev, phy_index); - if (!phy) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - if ((_transport_set_identify(ioc, mpt2sas_phy->handle, - &mpt2sas_phy->identify))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -1; - } - phy->identify = mpt2sas_phy->identify; - mpt2sas_phy->attached_handle = - le16_to_cpu(expander_pg1.AttachedDevHandle); - if (mpt2sas_phy->attached_handle) - _transport_set_identify(ioc, mpt2sas_phy->attached_handle, - &mpt2sas_phy->remote_identify); - phy->identify.phy_identifier = mpt2sas_phy->phy_id; - phy->negotiated_linkrate = _transport_convert_phy_link_rate( - expander_pg1.NegotiatedLinkRate & - MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); - phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( - expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); - phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( - expander_pg1.HwLinkRate >> 4); - phy->minimum_linkrate = _transport_convert_phy_link_rate( - expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); - phy->maximum_linkrate = _transport_convert_phy_link_rate( - expander_pg1.ProgrammedLinkRate >> 4); - - if ((sas_phy_add(phy))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - sas_phy_free(phy); - return -1; - } - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &phy->dev, - "add: handle(0x%04x), sas_addr(0x%016llx)\n" - "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", - mpt2sas_phy->handle, (unsigned long long) - mpt2sas_phy->identify.sas_address, - mpt2sas_phy->attached_handle, - (unsigned long long) - mpt2sas_phy->remote_identify.sas_address); - mpt2sas_phy->phy = phy; - return 0; -} - -/** - * mpt2sas_transport_update_links - refreshing phy link changes - * @ioc: per adapter object - * @sas_address: sas address of parent expander or sas host - * @handle: attached device handle - * @phy_numberv: phy number - * @link_rate: new link rate - * - * Returns nothing. - */ -void -mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, - u64 sas_address, u16 handle, u8 phy_number, u8 link_rate) -{ - unsigned long flags; - struct _sas_node *sas_node; - struct _sas_phy *mpt2sas_phy; - - if (ioc->shost_recovery || ioc->pci_error_recovery) - return; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address); - if (!sas_node) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return; - } - - mpt2sas_phy = &sas_node->phy[phy_number]; - mpt2sas_phy->attached_handle = handle; - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) { - _transport_set_identify(ioc, handle, - &mpt2sas_phy->remote_identify); - _transport_add_phy_to_an_existing_port(ioc, sas_node, - mpt2sas_phy, mpt2sas_phy->remote_identify.sas_address); - } else - memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct - sas_identify)); - - if (mpt2sas_phy->phy) - mpt2sas_phy->phy->negotiated_linkrate = - _transport_convert_phy_link_rate(link_rate); - - if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) - dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, - "refresh: parent sas_addr(0x%016llx),\n" - "\tlink_rate(0x%02x), phy(%d)\n" - "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", - (unsigned long long)sas_address, - link_rate, phy_number, handle, (unsigned long long) - mpt2sas_phy->remote_identify.sas_address); -} - -static inline void * -phy_to_ioc(struct sas_phy *phy) -{ - struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); - return shost_priv(shost); -} - -static inline void * -rphy_to_ioc(struct sas_rphy *rphy) -{ - struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); - return shost_priv(shost); -} - - -/* report phy error log structure */ -struct phy_error_log_request{ - u8 smp_frame_type; /* 0x40 */ - u8 function; /* 0x11 */ - u8 allocated_response_length; - u8 request_length; /* 02 */ - u8 reserved_1[5]; - u8 phy_identifier; - u8 reserved_2[2]; -}; - -/* report phy error log reply structure */ -struct phy_error_log_reply{ - u8 smp_frame_type; /* 0x41 */ - u8 function; /* 0x11 */ - u8 function_result; - u8 response_length; - __be16 expander_change_count; - u8 reserved_1[3]; - u8 phy_identifier; - u8 reserved_2[2]; - __be32 invalid_dword; - __be32 running_disparity_error; - __be32 loss_of_dword_sync; - __be32 phy_reset_problem; -}; - -/** - * _transport_get_expander_phy_error_log - return expander counters - * @ioc: per adapter object - * @phy: The sas phy object - * - * Returns 0 for success, non-zero for failure. - * - */ -static int -_transport_get_expander_phy_error_log(struct MPT2SAS_ADAPTER *ioc, - struct sas_phy *phy) -{ - Mpi2SmpPassthroughRequest_t *mpi_request; - Mpi2SmpPassthroughReply_t *mpi_reply; - struct phy_error_log_request *phy_error_log_request; - struct phy_error_log_reply *phy_error_log_reply; - int rc; - u16 smid; - u32 ioc_state; - unsigned long timeleft; - void *psge; - u32 sgl_flags; - u8 issue_reset = 0; - void *data_out = NULL; - dma_addr_t data_out_dma; - u32 sz; - u16 wait_state_count; - - if (ioc->shost_recovery || ioc->pci_error_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return -EFAULT; - } - - mutex_lock(&ioc->transport_cmds.mutex); - - if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - ioc->transport_cmds.status = MPT2_CMD_PENDING; - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->transport_cmds.smid = smid; - - sz = sizeof(struct phy_error_log_request) + - sizeof(struct phy_error_log_reply); - data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); - if (!data_out) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - rc = -ENOMEM; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - - rc = -EINVAL; - memset(data_out, 0, sz); - phy_error_log_request = data_out; - phy_error_log_request->smp_frame_type = 0x40; - phy_error_log_request->function = 0x11; - phy_error_log_request->request_length = 2; - phy_error_log_request->allocated_response_length = 0; - phy_error_log_request->phy_identifier = phy->number; - - memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); - mpi_request->RequestDataLength = - cpu_to_le16(sizeof(struct phy_error_log_request)); - psge = &mpi_request->SGL; - - /* WRITE sgel first */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct phy_error_log_request), data_out_dma); - - /* incr sgel */ - psge += ioc->sge_size; - - /* READ sgel last */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct phy_error_log_reply), data_out_dma + - sizeof(struct phy_error_log_request)); - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - " - "send to sas_addr(0x%016llx), phy(%d)\n", ioc->name, - (unsigned long long)phy->identify.sas_address, phy->number)); - init_completion(&ioc->transport_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); - - if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SmpPassthroughRequest_t)/4); - if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_error_log - " - "complete\n", ioc->name)); - - if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { - - mpi_reply = ioc->transport_cmds.reply; - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_error_log - reply data transfer size(%d)\n", - ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); - - if (le16_to_cpu(mpi_reply->ResponseDataLength) != - sizeof(struct phy_error_log_reply)) - goto out; - - phy_error_log_reply = data_out + - sizeof(struct phy_error_log_request); - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_error_log - function_result(%d)\n", - ioc->name, phy_error_log_reply->function_result)); - - phy->invalid_dword_count = - be32_to_cpu(phy_error_log_reply->invalid_dword); - phy->running_disparity_error_count = - be32_to_cpu(phy_error_log_reply->running_disparity_error); - phy->loss_of_dword_sync_count = - be32_to_cpu(phy_error_log_reply->loss_of_dword_sync); - phy->phy_reset_problem_count = - be32_to_cpu(phy_error_log_reply->phy_reset_problem); - rc = 0; - } else - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_error_log - no reply\n", ioc->name)); - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - out: - ioc->transport_cmds.status = MPT2_CMD_NOT_USED; - if (data_out) - pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); - - mutex_unlock(&ioc->transport_cmds.mutex); - return rc; -} - -/** - * _transport_get_linkerrors - return phy counters for both hba and expanders - * @phy: The sas phy object - * - * Returns 0 for success, non-zero for failure. - * - */ -static int -_transport_get_linkerrors(struct sas_phy *phy) -{ - struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - unsigned long flags; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasPhyPage1_t phy_pg1; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return -EINVAL; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - if (phy->identify.sas_address != ioc->sas_hba.sas_address) - return _transport_get_expander_phy_error_log(ioc, phy); - - /* get hba phy error logs */ - if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, - phy->number))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -ENXIO; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) - printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" - "(0x%04x), loginfo(0x%08x)\n", ioc->name, - phy->number, le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo)); - - phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); - phy->running_disparity_error_count = - le32_to_cpu(phy_pg1.RunningDisparityErrorCount); - phy->loss_of_dword_sync_count = - le32_to_cpu(phy_pg1.LossDwordSynchCount); - phy->phy_reset_problem_count = - le32_to_cpu(phy_pg1.PhyResetProblemCount); - return 0; -} - -/** - * _transport_get_enclosure_identifier - - * @phy: The sas phy object - * - * Obtain the enclosure logical id for an expander. - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) -{ - struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); - struct _sas_device *sas_device; - unsigned long flags; - int rc; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); - if (sas_device) { - *identifier = sas_device->enclosure_logical_id; - rc = 0; - } else { - *identifier = 0; - rc = -ENXIO; - } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return rc; -} - -/** - * _transport_get_bay_identifier - - * @phy: The sas phy object - * - * Returns the slot id for a device that resides inside an enclosure. - */ -static int -_transport_get_bay_identifier(struct sas_rphy *rphy) -{ - struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); - struct _sas_device *sas_device; - unsigned long flags; - int rc; - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); - if (sas_device) - rc = sas_device->slot; - else - rc = -ENXIO; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return rc; -} - -/* phy control request structure */ -struct phy_control_request{ - u8 smp_frame_type; /* 0x40 */ - u8 function; /* 0x91 */ - u8 allocated_response_length; - u8 request_length; /* 0x09 */ - u16 expander_change_count; - u8 reserved_1[3]; - u8 phy_identifier; - u8 phy_operation; - u8 reserved_2[13]; - u64 attached_device_name; - u8 programmed_min_physical_link_rate; - u8 programmed_max_physical_link_rate; - u8 reserved_3[6]; -}; - -/* phy control reply structure */ -struct phy_control_reply{ - u8 smp_frame_type; /* 0x41 */ - u8 function; /* 0x11 */ - u8 function_result; - u8 response_length; -}; - -#define SMP_PHY_CONTROL_LINK_RESET (0x01) -#define SMP_PHY_CONTROL_HARD_RESET (0x02) -#define SMP_PHY_CONTROL_DISABLE (0x03) - -/** - * _transport_expander_phy_control - expander phy control - * @ioc: per adapter object - * @phy: The sas phy object - * - * Returns 0 for success, non-zero for failure. - * - */ -static int -_transport_expander_phy_control(struct MPT2SAS_ADAPTER *ioc, - struct sas_phy *phy, u8 phy_operation) -{ - Mpi2SmpPassthroughRequest_t *mpi_request; - Mpi2SmpPassthroughReply_t *mpi_reply; - struct phy_control_request *phy_control_request; - struct phy_control_reply *phy_control_reply; - int rc; - u16 smid; - u32 ioc_state; - unsigned long timeleft; - void *psge; - u32 sgl_flags; - u8 issue_reset = 0; - void *data_out = NULL; - dma_addr_t data_out_dma; - u32 sz; - u16 wait_state_count; - - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return -EFAULT; - } - - mutex_lock(&ioc->transport_cmds.mutex); - - if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - ioc->transport_cmds.status = MPT2_CMD_PENDING; - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto out; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto out; - } - - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->transport_cmds.smid = smid; - - sz = sizeof(struct phy_control_request) + - sizeof(struct phy_control_reply); - data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); - if (!data_out) { - printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, - __LINE__, __func__); - rc = -ENOMEM; - mpt2sas_base_free_smid(ioc, smid); - goto out; - } - - rc = -EINVAL; - memset(data_out, 0, sz); - phy_control_request = data_out; - phy_control_request->smp_frame_type = 0x40; - phy_control_request->function = 0x91; - phy_control_request->request_length = 9; - phy_control_request->allocated_response_length = 0; - phy_control_request->phy_identifier = phy->number; - phy_control_request->phy_operation = phy_operation; - phy_control_request->programmed_min_physical_link_rate = - phy->minimum_linkrate << 4; - phy_control_request->programmed_max_physical_link_rate = - phy->maximum_linkrate << 4; - - memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - mpi_request->SASAddress = cpu_to_le64(phy->identify.sas_address); - mpi_request->RequestDataLength = - cpu_to_le16(sizeof(struct phy_error_log_request)); - psge = &mpi_request->SGL; - - /* WRITE sgel first */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct phy_control_request), data_out_dma); - - /* incr sgel */ - psge += ioc->sge_size; - - /* READ sgel last */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - ioc->base_add_sg_single(psge, sgl_flags | - sizeof(struct phy_control_reply), data_out_dma + - sizeof(struct phy_control_request)); - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - " - "send to sas_addr(0x%016llx), phy(%d), opcode(%d)\n", ioc->name, - (unsigned long long)phy->identify.sas_address, phy->number, - phy_operation)); - - init_completion(&ioc->transport_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); - - if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s: timeout\n", - ioc->name, __func__); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SmpPassthroughRequest_t)/4); - if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "phy_control - " - "complete\n", ioc->name)); - - if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { - - mpi_reply = ioc->transport_cmds.reply; - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_control - reply data transfer size(%d)\n", - ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); - - if (le16_to_cpu(mpi_reply->ResponseDataLength) != - sizeof(struct phy_control_reply)) - goto out; - - phy_control_reply = data_out + - sizeof(struct phy_control_request); - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_control - function_result(%d)\n", - ioc->name, phy_control_reply->function_result)); - - rc = 0; - } else - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "phy_control - no reply\n", ioc->name)); - - issue_host_reset: - if (issue_reset) - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - out: - ioc->transport_cmds.status = MPT2_CMD_NOT_USED; - if (data_out) - pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); - - mutex_unlock(&ioc->transport_cmds.mutex); - return rc; -} - -/** - * _transport_phy_reset - - * @phy: The sas phy object - * @hard_reset: - * - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_phy_reset(struct sas_phy *phy, int hard_reset) -{ - struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - Mpi2SasIoUnitControlReply_t mpi_reply; - Mpi2SasIoUnitControlRequest_t mpi_request; - unsigned long flags; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return -EINVAL; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - /* handle expander phys */ - if (phy->identify.sas_address != ioc->sas_hba.sas_address) - return _transport_expander_phy_control(ioc, phy, - (hard_reset == 1) ? SMP_PHY_CONTROL_HARD_RESET : - SMP_PHY_CONTROL_LINK_RESET); - - /* handle hba phys */ - memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t)); - mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; - mpi_request.Operation = hard_reset ? - MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; - mpi_request.PhyNum = phy->number; - - if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return -ENXIO; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) - printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" - "(0x%04x), loginfo(0x%08x)\n", ioc->name, - phy->number, le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo)); - - return 0; -} - -/** - * _transport_phy_enable - enable/disable phys - * @phy: The sas phy object - * @enable: enable phy when true - * - * Only support sas_host direct attached phys. - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_phy_enable(struct sas_phy *phy, int enable) -{ - struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; - Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; - Mpi2ConfigReply_t mpi_reply; - u16 ioc_status; - u16 sz; - int rc = 0; - unsigned long flags; - int i, discovery_active; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return -EINVAL; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - /* handle expander phys */ - if (phy->identify.sas_address != ioc->sas_hba.sas_address) - return _transport_expander_phy_control(ioc, phy, - (enable == 1) ? SMP_PHY_CONTROL_LINK_RESET : - SMP_PHY_CONTROL_DISABLE); - - /* handle hba phys */ - - /* read sas_iounit page 0 */ - sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit0PhyData_t)); - sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENOMEM; - goto out; - } - if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, - sas_iounit_pg0, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENXIO; - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -EIO; - goto out; - } - - /* unable to enable/disable phys when when discovery is active */ - for (i = 0, discovery_active = 0; i < ioc->sas_hba.num_phys ; i++) { - if (sas_iounit_pg0->PhyData[i].PortFlags & - MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) { - printk(MPT2SAS_ERR_FMT "discovery is active on " - "port = %d, phy = %d: unable to enable/disable " - "phys, try again later!\n", ioc->name, - sas_iounit_pg0->PhyData[i].Port, i); - discovery_active = 1; - } - } - - if (discovery_active) { - rc = -EAGAIN; - goto out; - } - - /* read sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); - sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg1) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENOMEM; - goto out; - } - if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, - sas_iounit_pg1, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENXIO; - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -EIO; - goto out; - } - /* copy Port/PortFlags/PhyFlags from page 0 */ - for (i = 0; i < ioc->sas_hba.num_phys ; i++) { - sas_iounit_pg1->PhyData[i].Port = - sas_iounit_pg0->PhyData[i].Port; - sas_iounit_pg1->PhyData[i].PortFlags = - (sas_iounit_pg0->PhyData[i].PortFlags & - MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG); - sas_iounit_pg1->PhyData[i].PhyFlags = - (sas_iounit_pg0->PhyData[i].PhyFlags & - (MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED + - MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED)); - } - if (enable) - sas_iounit_pg1->PhyData[phy->number].PhyFlags - &= ~MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; - else - sas_iounit_pg1->PhyData[phy->number].PhyFlags - |= MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE; - - mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, sz); - - /* link reset */ - if (enable) - _transport_phy_reset(phy, 0); - - out: - kfree(sas_iounit_pg1); - kfree(sas_iounit_pg0); - return rc; -} - -/** - * _transport_phy_speed - set phy min/max link rates - * @phy: The sas phy object - * @rates: rates defined in sas_phy_linkrates - * - * Only support sas_host direct attached phys. - * Returns 0 for success, non-zero for failure. - */ -static int -_transport_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates) -{ - struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); - Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; - Mpi2SasPhyPage0_t phy_pg0; - Mpi2ConfigReply_t mpi_reply; - u16 ioc_status; - u16 sz; - int i; - int rc = 0; - unsigned long flags; - - spin_lock_irqsave(&ioc->sas_node_lock, flags); - if (_transport_sas_node_find_by_sas_address(ioc, - phy->identify.sas_address) == NULL) { - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - return -EINVAL; - } - spin_unlock_irqrestore(&ioc->sas_node_lock, flags); - - if (!rates->minimum_linkrate) - rates->minimum_linkrate = phy->minimum_linkrate; - else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) - rates->minimum_linkrate = phy->minimum_linkrate_hw; - - if (!rates->maximum_linkrate) - rates->maximum_linkrate = phy->maximum_linkrate; - else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) - rates->maximum_linkrate = phy->maximum_linkrate_hw; - - /* handle expander phys */ - if (phy->identify.sas_address != ioc->sas_hba.sas_address) { - phy->minimum_linkrate = rates->minimum_linkrate; - phy->maximum_linkrate = rates->maximum_linkrate; - return _transport_expander_phy_control(ioc, phy, - SMP_PHY_CONTROL_LINK_RESET); - } - - /* handle hba phys */ - - /* sas_iounit page 1 */ - sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * - sizeof(Mpi2SasIOUnit1PhyData_t)); - sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); - if (!sas_iounit_pg1) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENOMEM; - goto out; - } - if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, - sas_iounit_pg1, sz))) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENXIO; - goto out; - } - ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & - MPI2_IOCSTATUS_MASK; - if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -EIO; - goto out; - } - - for (i = 0; i < ioc->sas_hba.num_phys; i++) { - if (phy->number != i) { - sas_iounit_pg1->PhyData[i].MaxMinLinkRate = - (ioc->sas_hba.phy[i].phy->minimum_linkrate + - (ioc->sas_hba.phy[i].phy->maximum_linkrate << 4)); - } else { - sas_iounit_pg1->PhyData[i].MaxMinLinkRate = - (rates->minimum_linkrate + - (rates->maximum_linkrate << 4)); - } - } - - if (mpt2sas_config_set_sas_iounit_pg1(ioc, &mpi_reply, sas_iounit_pg1, - sz)) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - rc = -ENXIO; - goto out; - } - - /* link reset */ - _transport_phy_reset(phy, 0); - - /* read phy page 0, then update the rates in the sas transport phy */ - if (!mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, - phy->number)) { - phy->minimum_linkrate = _transport_convert_phy_link_rate( - phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); - phy->maximum_linkrate = _transport_convert_phy_link_rate( - phy_pg0.ProgrammedLinkRate >> 4); - phy->negotiated_linkrate = _transport_convert_phy_link_rate( - phy_pg0.NegotiatedLinkRate & - MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); - } - - out: - kfree(sas_iounit_pg1); - return rc; -} - - -/** - * _transport_smp_handler - transport portal for smp passthru - * @shost: shost object - * @rphy: sas transport rphy object - * @req: - * - * This used primarily for smp_utils. - * Example: - * smp_rep_general /sys/class/bsg/expander-5:0 - */ -static int -_transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, - struct request *req) -{ - struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - Mpi2SmpPassthroughRequest_t *mpi_request; - Mpi2SmpPassthroughReply_t *mpi_reply; - int rc; - u16 smid; - u32 ioc_state; - unsigned long timeleft; - void *psge; - u32 sgl_flags; - u8 issue_reset = 0; - dma_addr_t dma_addr_in = 0; - dma_addr_t dma_addr_out = 0; - dma_addr_t pci_dma_in = 0; - dma_addr_t pci_dma_out = 0; - void *pci_addr_in = NULL; - void *pci_addr_out = NULL; - u16 wait_state_count; - struct request *rsp = req->next_rq; - struct bio_vec bvec; - struct bvec_iter iter; - - if (!rsp) { - printk(MPT2SAS_ERR_FMT "%s: the smp response space is " - "missing\n", ioc->name, __func__); - return -EINVAL; - } - if (ioc->shost_recovery || ioc->pci_error_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return -EFAULT; - } - - rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); - if (rc) - return rc; - - if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { - printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", ioc->name, - __func__); - rc = -EAGAIN; - goto out; - } - ioc->transport_cmds.status = MPT2_CMD_PENDING; - - /* Check if the request is split across multiple segments */ - if (bio_multiple_segments(req->bio)) { - u32 offset = 0; - - /* Allocate memory and copy the request */ - pci_addr_out = pci_alloc_consistent(ioc->pdev, - blk_rq_bytes(req), &pci_dma_out); - if (!pci_addr_out) { - printk(MPT2SAS_INFO_FMT "%s(): PCI Addr out = NULL\n", - ioc->name, __func__); - rc = -ENOMEM; - goto out; - } - - bio_for_each_segment(bvec, req->bio, iter) { - memcpy(pci_addr_out + offset, - page_address(bvec.bv_page) + bvec.bv_offset, - bvec.bv_len); - offset += bvec.bv_len; - } - } else { - dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), - blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_out) { - printk(MPT2SAS_INFO_FMT "%s(): DMA Addr out = NULL\n", - ioc->name, __func__); - rc = -ENOMEM; - goto free_pci; - } - } - - /* Check if the response needs to be populated across - * multiple segments */ - if (bio_multiple_segments(rsp->bio)) { - pci_addr_in = pci_alloc_consistent(ioc->pdev, blk_rq_bytes(rsp), - &pci_dma_in); - if (!pci_addr_in) { - printk(MPT2SAS_INFO_FMT "%s(): PCI Addr in = NULL\n", - ioc->name, __func__); - rc = -ENOMEM; - goto unmap; - } - } else { - dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), - blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_in) { - printk(MPT2SAS_INFO_FMT "%s(): DMA Addr in = NULL\n", - ioc->name, __func__); - rc = -ENOMEM; - goto unmap; - } - } - - wait_state_count = 0; - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { - if (wait_state_count++ == 10) { - printk(MPT2SAS_ERR_FMT - "%s: failed due to ioc not operational\n", - ioc->name, __func__); - rc = -EFAULT; - goto unmap; - } - ssleep(1); - ioc_state = mpt2sas_base_get_iocstate(ioc, 1); - printk(MPT2SAS_INFO_FMT "%s: waiting for " - "operational state(count=%d)\n", ioc->name, - __func__, wait_state_count); - } - if (wait_state_count) - printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", - ioc->name, __func__); - - smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); - if (!smid) { - printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", - ioc->name, __func__); - rc = -EAGAIN; - goto unmap; - } - - rc = 0; - mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); - ioc->transport_cmds.smid = smid; - - memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); - mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; - mpi_request->PhysicalPort = 0xFF; - mpi_request->VF_ID = 0; /* TODO */ - mpi_request->VP_ID = 0; - mpi_request->SASAddress = (rphy) ? - cpu_to_le64(rphy->identify.sas_address) : - cpu_to_le64(ioc->sas_hba.sas_address); - mpi_request->RequestDataLength = cpu_to_le16(blk_rq_bytes(req) - 4); - psge = &mpi_request->SGL; - - /* WRITE sgel first */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - if (bio_multiple_segments(req->bio)) { - ioc->base_add_sg_single(psge, sgl_flags | - (blk_rq_bytes(req) - 4), pci_dma_out); - } else { - ioc->base_add_sg_single(psge, sgl_flags | - (blk_rq_bytes(req) - 4), dma_addr_out); - } - - /* incr sgel */ - psge += ioc->sge_size; - - /* READ sgel last */ - sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | - MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | - MPI2_SGE_FLAGS_END_OF_LIST); - sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; - if (bio_multiple_segments(rsp->bio)) { - ioc->base_add_sg_single(psge, sgl_flags | - (blk_rq_bytes(rsp) + 4), pci_dma_in); - } else { - ioc->base_add_sg_single(psge, sgl_flags | - (blk_rq_bytes(rsp) + 4), dma_addr_in); - } - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - " - "sending smp request\n", ioc->name, __func__)); - - init_completion(&ioc->transport_cmds.done); - mpt2sas_base_put_smid_default(ioc, smid); - timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, - 10*HZ); - - if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { - printk(MPT2SAS_ERR_FMT "%s : timeout\n", - __func__, ioc->name); - _debug_dump_mf(mpi_request, - sizeof(Mpi2SmpPassthroughRequest_t)/4); - if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; - } - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT "%s - " - "complete\n", ioc->name, __func__)); - - if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { - - mpi_reply = ioc->transport_cmds.reply; - - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s - reply data transfer size(%d)\n", - ioc->name, __func__, - le16_to_cpu(mpi_reply->ResponseDataLength))); - - memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); - req->sense_len = sizeof(*mpi_reply); - req->resid_len = 0; - rsp->resid_len -= - le16_to_cpu(mpi_reply->ResponseDataLength); - /* check if the resp needs to be copied from the allocated - * pci mem */ - if (bio_multiple_segments(rsp->bio)) { - u32 offset = 0; - u32 bytes_to_copy = - le16_to_cpu(mpi_reply->ResponseDataLength); - bio_for_each_segment(bvec, rsp->bio, iter) { - if (bytes_to_copy <= bvec.bv_len) { - memcpy(page_address(bvec.bv_page) + - bvec.bv_offset, pci_addr_in + - offset, bytes_to_copy); - break; - } else { - memcpy(page_address(bvec.bv_page) + - bvec.bv_offset, pci_addr_in + - offset, bvec.bv_len); - bytes_to_copy -= bvec.bv_len; - } - offset += bvec.bv_len; - } - } - } else { - dtransportprintk(ioc, printk(MPT2SAS_INFO_FMT - "%s - no reply\n", ioc->name, __func__)); - rc = -ENXIO; - } - - issue_host_reset: - if (issue_reset) { - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, - FORCE_BIG_HAMMER); - rc = -ETIMEDOUT; - } - - unmap: - if (dma_addr_out) - pci_unmap_single(ioc->pdev, dma_addr_out, blk_rq_bytes(req), - PCI_DMA_BIDIRECTIONAL); - if (dma_addr_in) - pci_unmap_single(ioc->pdev, dma_addr_in, blk_rq_bytes(rsp), - PCI_DMA_BIDIRECTIONAL); - - free_pci: - if (pci_addr_out) - pci_free_consistent(ioc->pdev, blk_rq_bytes(req), pci_addr_out, - pci_dma_out); - - if (pci_addr_in) - pci_free_consistent(ioc->pdev, blk_rq_bytes(rsp), pci_addr_in, - pci_dma_in); - - out: - ioc->transport_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->transport_cmds.mutex); - return rc; -} - -struct sas_function_template mpt2sas_transport_functions = { - .get_linkerrors = _transport_get_linkerrors, - .get_enclosure_identifier = _transport_get_enclosure_identifier, - .get_bay_identifier = _transport_get_bay_identifier, - .phy_reset = _transport_phy_reset, - .phy_enable = _transport_phy_enable, - .set_phy_speed = _transport_phy_speed, - .smp_handler = _transport_smp_handler, -}; - -struct scsi_transport_template *mpt2sas_transport_template; diff --git a/kernel/drivers/scsi/mpt3sas/Kconfig b/kernel/drivers/scsi/mpt3sas/Kconfig index 4d235dd74..b736dbc80 100644 --- a/kernel/drivers/scsi/mpt3sas/Kconfig +++ b/kernel/drivers/scsi/mpt3sas/Kconfig @@ -41,15 +41,15 @@ # USA. config SCSI_MPT3SAS - tristate "LSI MPT Fusion SAS 3.0 Device Driver" + tristate "LSI MPT Fusion SAS 3.0 & SAS 2.0 Device Driver" depends on PCI && SCSI select SCSI_SAS_ATTRS select RAID_ATTRS ---help--- This driver supports PCI-Express SAS 12Gb/s Host Adapters. -config SCSI_MPT3SAS_MAX_SGE - int "LSI MPT Fusion Max number of SG Entries (16 - 256)" +config SCSI_MPT2SAS_MAX_SGE + int "LSI MPT Fusion SAS 2.0 Max number of SG Entries (16 - 256)" depends on PCI && SCSI && SCSI_MPT3SAS default "128" range 16 256 @@ -60,8 +60,23 @@ config SCSI_MPT3SAS_MAX_SGE can be 256. However, it may decreased down to 16. Decreasing this parameter will reduce memory requirements on a per controller instance. -config SCSI_MPT3SAS_LOGGING - bool "LSI MPT Fusion logging facility" +config SCSI_MPT3SAS_MAX_SGE + int "LSI MPT Fusion SAS 3.0 Max number of SG Entries (16 - 256)" depends on PCI && SCSI && SCSI_MPT3SAS + default "128" + range 16 256 + ---help--- + This option allows you to specify the maximum number of scatter- + gather entries per I/O. The driver default is 128, which matches + MAX_PHYS_SEGMENTS in most kernels. However in SuSE kernels this + can be 256. However, it may decreased down to 16. Decreasing this + parameter will reduce memory requirements on a per controller instance. + +config SCSI_MPT2SAS + tristate "Legacy MPT2SAS config option" + default n + select SCSI_MPT3SAS + depends on PCI && SCSI ---help--- - This turns on a logging facility. + Dummy config option for backwards compatiblity: configure the MPT3SAS + driver instead. diff --git a/kernel/drivers/scsi/mpt3sas/Makefile b/kernel/drivers/scsi/mpt3sas/Makefile index efb0c4c2e..b7643f596 100644 --- a/kernel/drivers/scsi/mpt3sas/Makefile +++ b/kernel/drivers/scsi/mpt3sas/Makefile @@ -5,4 +5,5 @@ mpt3sas-y += mpt3sas_base.o \ mpt3sas_scsih.o \ mpt3sas_transport.o \ mpt3sas_ctl.o \ - mpt3sas_trigger_diag.o + mpt3sas_trigger_diag.o \ + mpt3sas_warpdrive.o diff --git a/kernel/drivers/scsi/mpt3sas/mpi/mpi2.h b/kernel/drivers/scsi/mpt3sas/mpi/mpi2.h index c34c11579..ec27ad2d1 100644 --- a/kernel/drivers/scsi/mpt3sas/mpi/mpi2.h +++ b/kernel/drivers/scsi/mpt3sas/mpi/mpi2.h @@ -8,7 +8,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.31 + * mpi2.h Version: 02.00.35 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -88,6 +88,10 @@ * Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET. * 04-09-13 02.00.30 Bumped MPI2_HEADER_VERSION_UNIT. * 04-17-13 02.00.31 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-19-13 02.00.32 Bumped MPI2_HEADER_VERSION_UNIT. + * 12-05-13 02.00.33 Bumped MPI2_HEADER_VERSION_UNIT. + * 01-08-14 02.00.34 Bumped MPI2_HEADER_VERSION_UNIT + * 06-13-14 02.00.35 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -121,7 +125,7 @@ #define MPI2_VERSION_02_05 (0x0205) /*Unit and Dev versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x1F) +#define MPI2_HEADER_VERSION_UNIT (0x23) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) diff --git a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index e261a3153..581fdb375 100644 --- a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -6,7 +6,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.26 + * mpi2_cnfg.h Version: 02.00.29 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -165,6 +165,20 @@ * match the specification. * 08-19-13 02.00.26 Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for * future use. + * 12-05-13 02.00.27 Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for + * MPI2_CONFIG_PAGE_MAN_7. + * Added EnclosureLevel and ConnectorName fields to + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_DEV_0. + * Added EnclosureLevel field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 01-08-14 02.00.28 Added more defines for the BiosOptions field of + * MPI2_CONFIG_PAGE_BIOS_1. + * 06-13-14 02.00.29 Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and + * more defines for the BiosOptions field.. * -------------------------------------------------------------------------- */ @@ -724,6 +738,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 { #define MPI2_MANUFACTURING7_PAGEVERSION (0x01) /*defines for the Flags field */ +#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL (0x00000008) #define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER (0x00000002) #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) @@ -1311,7 +1326,9 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { MPI2_CONFIG_PAGE_HEADER Header; /*0x00 */ U32 BiosOptions; /*0x04 */ U32 IOCSettings; /*0x08 */ - U32 Reserved1; /*0x0C */ + U8 SSUTimeout; /*0x0C */ + U8 Reserved1; /*0x0D */ + U16 Reserved2; /*0x0E */ U32 DeviceSettings; /*0x10 */ U16 NumberOfDevices; /*0x14 */ U16 UEFIVersion; /*0x16 */ @@ -1323,9 +1340,24 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 { *PTR_MPI2_CONFIG_PAGE_BIOS_1, Mpi2BiosPage1_t, *pMpi2BiosPage1_t; -#define MPI2_BIOSPAGE1_PAGEVERSION (0x05) +#define MPI2_BIOSPAGE1_PAGEVERSION (0x07) /*values for BIOS Page 1 BiosOptions field */ +#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK (0x00003800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE (0x00000800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID (0x00001000) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS (0x00001800) +#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY (0x00002000) + +#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS (0x00000400) + +#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD (0x00000300) +#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD (0x00000000) +#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD (0x00000100) +#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD (0x00000200) +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD (0x00000300) + #define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID (0x000000F0) #define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID (0x00000000) @@ -2633,9 +2665,9 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { U8 ControlGroup; /*0x2E */ U8 - Reserved1; /*0x2F */ + EnclosureLevel; /*0x2F */ U32 - Reserved2; /*0x30 */ + ConnectorName[4]; /*0x30 */ U32 Reserved3; /*0x34 */ } MPI2_CONFIG_PAGE_SAS_DEV_0, @@ -2643,7 +2675,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { Mpi2SasDevicePage0_t, *pMpi2SasDevicePage0_t; -#define MPI2_SASDEVICE0_PAGEVERSION (0x08) +#define MPI2_SASDEVICE0_PAGEVERSION (0x09) /*values for SAS Device Page 0 AccessStatus field */ #define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) @@ -2683,6 +2715,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 { #define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) #define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) #define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) +#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID (0x0002) #define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) @@ -3019,8 +3052,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { NumSlots; /*0x18 */ U16 StartSlot; /*0x1A */ - U16 + U8 Reserved2; /*0x1C */ + U8 + EnclosureLevel; /*0x1D */ U16 SEPDevHandle; /*0x1E */ U32 @@ -3031,9 +3066,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 { *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t; -#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03) +#define MPI2_SASENCLOSURE0_PAGEVERSION (0x04) /*values for SAS Enclosure Page 0 Flags field */ +#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID (0x0010) #define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) #define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) #define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) diff --git a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h index 490830957..d7598cc4b 100644 --- a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h +++ b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h @@ -6,7 +6,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.23 + * mpi2_ioc.h Version: 02.00.24 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -132,6 +132,7 @@ * Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE. * Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY. * Added Encrypted Hash Extended Image. + * 12-05-13 02.00.24 Added MPI25_HASH_IMAGE_TYPE_BIOS. * -------------------------------------------------------------------------- */ @@ -1598,6 +1599,7 @@ Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t; /* values for HashImageType */ #define MPI25_HASH_IMAGE_TYPE_UNUSED (0x00) #define MPI25_HASH_IMAGE_TYPE_FIRMWARE (0x01) +#define MPI25_HASH_IMAGE_TYPE_BIOS (0x02) /* values for HashAlgorithm */ #define MPI25_HASH_ALGORITHM_UNUSED (0x00) diff --git a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_tool.h b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_tool.h index 904910d8a..1629e5bce 100644 --- a/kernel/drivers/scsi/mpt3sas/mpi/mpi2_tool.h +++ b/kernel/drivers/scsi/mpt3sas/mpi/mpi2_tool.h @@ -6,7 +6,7 @@ * Title: MPI diagnostic tool structures and definitions * Creation Date: March 26, 2007 * - * mpi2_tool.h Version: 02.00.11 + * mpi2_tool.h Version: 02.00.12 * * Version History * --------------- @@ -33,6 +33,7 @@ * 07-26-12 02.00.10 Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that * it uses MPI Chain SGE as well as MPI Simple SGE. * 08-19-13 02.00.11 Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info. + * 01-08-14 02.00.12 Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC. * -------------------------------------------------------------------------- */ @@ -100,6 +101,7 @@ typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST { #define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) #define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) #define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) +#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC (0x04000000) #define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) #define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) #define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_base.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_base.c index 14a781b6b..11393ebf1 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -83,10 +83,10 @@ static int msix_disable = -1; module_param(msix_disable, int, 0); MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); -static int max_msix_vectors = 8; +static int max_msix_vectors = -1; module_param(max_msix_vectors, int, 0); MODULE_PARM_DESC(max_msix_vectors, - " max msix vectors - (default=8)"); + " max msix vectors"); static int mpt3sas_fwfault_debug; MODULE_PARM_DESC(mpt3sas_fwfault_debug, @@ -108,9 +108,12 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) if (ret) return ret; + /* global ioc spinlock to protect controller list on list operations */ pr_info("setting fwfault_debug(%d)\n", mpt3sas_fwfault_debug); + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt3sas_ioc_list, list) ioc->fwfault_debug = mpt3sas_fwfault_debug; + spin_unlock(&gioc_lock); return 0; } module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug, @@ -157,7 +160,7 @@ _base_fault_reset_work(struct work_struct *work) spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->pci_error_recovery) goto rearm_timer; spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); @@ -166,6 +169,20 @@ _base_fault_reset_work(struct work_struct *work) pr_err(MPT3SAS_FMT "SAS host is non-operational !!!!\n", ioc->name); + /* It may be possible that EEH recovery can resolve some of + * pci bus failure issues rather removing the dead ioc function + * by considering controller is in a non-operational state. So + * here priority is given to the EEH recovery. If it doesn't + * not resolve this issue, mpt3sas driver will consider this + * controller to non-operational state and remove the dead ioc + * function. + */ + if (ioc->non_operational_loop++ < 5) { + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, + flags); + goto rearm_timer; + } + /* * Call _scsih_flush_pending_cmds callback so that we flush all * pending commands back to OS. This call is required to aovid @@ -181,7 +198,7 @@ _base_fault_reset_work(struct work_struct *work) ioc->remove_host = 1; /*Remove the Dead Host */ p = kthread_run(mpt3sas_remove_dead_ioc_func, ioc, - "mpt3sas_dead_ioc_%d", ioc->id); + "%s_dead_ioc_%d", ioc->driver_name, ioc->id); if (IS_ERR(p)) pr_err(MPT3SAS_FMT "%s: Running mpt3sas_dead_ioc thread failed !!!!\n", @@ -193,6 +210,8 @@ _base_fault_reset_work(struct work_struct *work) return; /* don't rearm timer */ } + ioc->non_operational_loop = 0; + if ((doorbell & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) { rc = mpt3sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); @@ -235,7 +254,8 @@ mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc) INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); snprintf(ioc->fault_reset_work_q_name, - sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); + sizeof(ioc->fault_reset_work_q_name), "poll_%s%d_status", + ioc->driver_name, ioc->id); ioc->fault_reset_work_q = create_singlethread_workqueue(ioc->fault_reset_work_q_name); if (!ioc->fault_reset_work_q) { @@ -324,7 +344,6 @@ mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc) panic("panic in %s\n", __func__); } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _base_sas_ioc_info - verbose translation of the ioc status * @ioc: per adapter object @@ -578,7 +597,8 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, desc = "Device Status Change"; break; case MPI2_EVENT_IR_OPERATION_STATUS: - desc = "IR Operation Status"; + if (!ioc->hide_ir_msg) + desc = "IR Operation Status"; break; case MPI2_EVENT_SAS_DISCOVERY: { @@ -609,16 +629,20 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, desc = "SAS Enclosure Device Status Change"; break; case MPI2_EVENT_IR_VOLUME: - desc = "IR Volume"; + if (!ioc->hide_ir_msg) + desc = "IR Volume"; break; case MPI2_EVENT_IR_PHYSICAL_DISK: - desc = "IR Physical Disk"; + if (!ioc->hide_ir_msg) + desc = "IR Physical Disk"; break; case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: - desc = "IR Configuration Change List"; + if (!ioc->hide_ir_msg) + desc = "IR Configuration Change List"; break; case MPI2_EVENT_LOG_ENTRY_ADDED: - desc = "Log Entry Added"; + if (!ioc->hide_ir_msg) + desc = "Log Entry Added"; break; case MPI2_EVENT_TEMP_THRESHOLD: desc = "Temperature Threshold"; @@ -630,7 +654,6 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, desc); } -#endif /** * _base_sas_log_info - verbose translation of firmware log info @@ -675,7 +698,10 @@ _base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) originator_str = "PL"; break; case 2: - originator_str = "IR"; + if (!ioc->hide_ir_msg) + originator_str = "IR"; + else + originator_str = "WarpDrive"; break; } @@ -710,13 +736,13 @@ _base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, return; } ioc_status = le16_to_cpu(mpi_reply->IOCStatus); -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING + if ((ioc_status & MPI2_IOCSTATUS_MASK) && (ioc->logging_level & MPT_DEBUG_REPLY)) { _base_sas_ioc_info(ioc , mpi_reply, mpt3sas_base_get_msg_frame(ioc, smid)); } -#endif + if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { loginfo = le32_to_cpu(mpi_reply->IOCLogInfo); _base_sas_log_info(ioc, loginfo); @@ -783,9 +809,9 @@ _base_async_event(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply) return 1; if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) return 1; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING + _base_display_event_data(ioc, mpi_reply); -#endif + if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) goto out; smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx); @@ -1009,8 +1035,36 @@ _base_interrupt(int irq, void *bus_id) } wmb(); - writel(reply_q->reply_post_host_index | (msix_index << - MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex); + if (ioc->is_warpdrive) { + writel(reply_q->reply_post_host_index, + ioc->reply_post_host_index[msix_index]); + atomic_dec(&reply_q->busy); + return IRQ_HANDLED; + } + + /* Update Reply Post Host Index. + * For those HBA's which support combined reply queue feature + * 1. Get the correct Supplemental Reply Post Host Index Register. + * i.e. (msix_index / 8)th entry from Supplemental Reply Post Host + * Index Register address bank i.e replyPostRegisterIndex[], + * 2. Then update this register with new reply host index value + * in ReplyPostIndex field and the MSIxIndex field with + * msix_index value reduced to a value between 0 and 7, + * using a modulo 8 operation. Since each Supplemental Reply Post + * Host Index Register supports 8 MSI-X vectors. + * + * For other HBA's just update the Reply Post Host Index register with + * new reply host index value in ReplyPostIndex Field and msix_index + * value in MSIxIndex field. + */ + if (ioc->msix96_vector) + writel(reply_q->reply_post_host_index | ((msix_index & 7) << + MPI2_RPHI_MSIX_INDEX_SHIFT), + ioc->replyPostRegisterIndex[msix_index/8]); + else + writel(reply_q->reply_post_host_index | (msix_index << + MPI2_RPHI_MSIX_INDEX_SHIFT), + &ioc->chip->ReplyPostHostIndex); atomic_dec(&reply_q->busy); return IRQ_HANDLED; } @@ -1297,6 +1351,149 @@ _base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr) _base_add_sg_single_ieee(paddr, sgl_flags, 0, 0, -1); } +/** + * _base_build_sg_scmd - main sg creation routine + * @ioc: per adapter object + * @scmd: scsi command + * @smid: system request message index + * Context: none. + * + * The main routine that builds scatter gather table from a given + * scsi request sent via the .queuecommand main handler. + * + * Returns 0 success, anything else error + */ +static int +_base_build_sg_scmd(struct MPT3SAS_ADAPTER *ioc, + struct scsi_cmnd *scmd, u16 smid) +{ + Mpi2SCSIIORequest_t *mpi_request; + dma_addr_t chain_dma; + struct scatterlist *sg_scmd; + void *sg_local, *chain; + u32 chain_offset; + u32 chain_length; + u32 chain_flags; + int sges_left; + u32 sges_in_segment; + u32 sgl_flags; + u32 sgl_flags_last_element; + u32 sgl_flags_end_buffer; + struct chain_tracker *chain_req; + + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + + /* init scatter gather flags */ + sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT; + if (scmd->sc_data_direction == DMA_TO_DEVICE) + sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC; + sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT) + << MPI2_SGE_FLAGS_SHIFT; + sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) + << MPI2_SGE_FLAGS_SHIFT; + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + + sg_scmd = scsi_sglist(scmd); + sges_left = scsi_dma_map(scmd); + if (sges_left < 0) { + sdev_printk(KERN_ERR, scmd->device, + "pci_map_sg failed: request for %d bytes!\n", + scsi_bufflen(scmd)); + return -ENOMEM; + } + + sg_local = &mpi_request->SGL; + sges_in_segment = ioc->max_sges_in_main_message; + if (sges_left <= sges_in_segment) + goto fill_in_last_segment; + + mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) + + (sges_in_segment * ioc->sge_size))/4; + + /* fill in main message segment when there is a chain following */ + while (sges_in_segment) { + if (sges_in_segment == 1) + ioc->base_add_sg_single(sg_local, + sgl_flags_last_element | sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + sges_in_segment--; + } + + /* initializing the chain flags and pointers */ + chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; + chain_req = _base_get_chain_buffer_tracker(ioc, smid); + if (!chain_req) + return -1; + chain = chain_req->chain_buffer; + chain_dma = chain_req->chain_buffer_dma; + do { + sges_in_segment = (sges_left <= + ioc->max_sges_in_chain_message) ? sges_left : + ioc->max_sges_in_chain_message; + chain_offset = (sges_left == sges_in_segment) ? + 0 : (sges_in_segment * ioc->sge_size)/4; + chain_length = sges_in_segment * ioc->sge_size; + if (chain_offset) { + chain_offset = chain_offset << + MPI2_SGE_CHAIN_OFFSET_SHIFT; + chain_length += ioc->sge_size; + } + ioc->base_add_sg_single(sg_local, chain_flags | chain_offset | + chain_length, chain_dma); + sg_local = chain; + if (!chain_offset) + goto fill_in_last_segment; + + /* fill in chain segments */ + while (sges_in_segment) { + if (sges_in_segment == 1) + ioc->base_add_sg_single(sg_local, + sgl_flags_last_element | + sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + sges_in_segment--; + } + + chain_req = _base_get_chain_buffer_tracker(ioc, smid); + if (!chain_req) + return -1; + chain = chain_req->chain_buffer; + chain_dma = chain_req->chain_buffer_dma; + } while (1); + + + fill_in_last_segment: + + /* fill the last segment */ + while (sges_left) { + if (sges_left == 1) + ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + } + + return 0; +} + /** * _base_build_sg_scmd_ieee - main sg creation routine for IEEE format * @ioc: per adapter object @@ -1338,7 +1535,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, sg_scmd = scsi_sglist(scmd); sges_left = scsi_dma_map(scmd); - if (!sges_left) { + if (sges_left < 0) { sdev_printk(KERN_ERR, scmd->device, "pci_map_sg failed: request for %d bytes!\n", scsi_bufflen(scmd)); @@ -1407,7 +1604,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc, fill_in_last_segment: /* fill the last segment */ - while (sges_left) { + while (sges_left > 0) { if (sges_left == 1) _base_add_sg_single_ieee(sg_local, simple_sgl_flags_last, 0, sg_dma_len(sg_scmd), @@ -1549,6 +1746,14 @@ _base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) int base; u16 message_control; + /* Check whether controller SAS2008 B0 controller, + * if it is SAS2008 B0 controller use IO-APIC instead of MSIX + */ + if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 && + ioc->pdev->revision == SAS2_PCI_DEVICE_B0_REVISION) { + return -EINVAL; + } + base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); if (!base) { dfailprintk(ioc, pr_info(MPT3SAS_FMT "msix not supported\n", @@ -1557,11 +1762,19 @@ _base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc) } /* get msix vector count */ - - pci_read_config_word(ioc->pdev, base + 2, &message_control); - ioc->msix_vector_count = (message_control & 0x3FF) + 1; - if (ioc->msix_vector_count > 8) - ioc->msix_vector_count = 8; + /* NUMA_IO not supported for older controllers */ + if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2004 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_1 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_2 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2108_3 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_1 || + ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2116_2) + ioc->msix_vector_count = 1; + else { + pci_read_config_word(ioc->pdev, base + 2, &message_control); + ioc->msix_vector_count = (message_control & 0x3FF) + 1; + } dinitprintk(ioc, pr_info(MPT3SAS_FMT "msix is supported, vector_count(%d)\n", ioc->name, ioc->msix_vector_count)); @@ -1623,10 +1836,10 @@ _base_request_irq(struct MPT3SAS_ADAPTER *ioc, u8 index, u32 vector) atomic_set(&reply_q->busy, 0); if (ioc->msix_enable) snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d-msix%d", - MPT3SAS_DRIVER_NAME, ioc->id, index); + ioc->driver_name, ioc->id, index); else snprintf(reply_q->name, MPT_NAME_LENGTH, "%s%d", - MPT3SAS_DRIVER_NAME, ioc->id); + ioc->driver_name, ioc->id); r = request_irq(vector, _base_interrupt, IRQF_SHARED, reply_q->name, reply_q); if (r) { @@ -1792,6 +2005,36 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc) return r; } +/** + * mpt3sas_base_unmap_resources - free controller resources + * @ioc: per adapter object + */ +void +mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc) +{ + struct pci_dev *pdev = ioc->pdev; + + dexitprintk(ioc, printk(MPT3SAS_FMT "%s\n", + ioc->name, __func__)); + + _base_free_irq(ioc); + _base_disable_msix(ioc); + + if (ioc->msix96_vector) + kfree(ioc->replyPostRegisterIndex); + + if (ioc->chip_phys) { + iounmap(ioc->chip); + ioc->chip_phys = 0; + } + + if (pci_is_enabled(pdev)) { + pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + } +} + /** * mpt3sas_base_map_resources - map in controller resources (io/irq/memap) * @ioc: per adapter object @@ -1822,7 +2065,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) if (pci_request_selected_regions(pdev, ioc->bars, - MPT3SAS_DRIVER_NAME)) { + ioc->driver_name)) { pr_warn(MPT3SAS_FMT "pci_request_selected_regions: failed\n", ioc->name); ioc->bars = 0; @@ -1843,7 +2086,8 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) goto out_fail; } - for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { + for (i = 0, memap_sz = 0, pio_sz = 0; (i < DEVICE_COUNT_RESOURCE) && + (!memap_sz || !pio_sz); i++) { if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { if (pio_sz) continue; @@ -1856,15 +2100,16 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) chip_phys = (u64)ioc->chip_phys; memap_sz = pci_resource_len(pdev, i); ioc->chip = ioremap(ioc->chip_phys, memap_sz); - if (ioc->chip == NULL) { - pr_err(MPT3SAS_FMT "unable to map adapter memory!\n", - ioc->name); - r = -EINVAL; - goto out_fail; - } } } + if (ioc->chip == NULL) { + pr_err(MPT3SAS_FMT "unable to map adapter memory! " + " or resource not found\n", ioc->name); + r = -EINVAL; + goto out_fail; + } + _base_mask_interrupts(ioc); r = _base_get_ioc_facts(ioc, CAN_SLEEP); @@ -1880,6 +2125,36 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) if (r) goto out_fail; + /* Use the Combined reply queue feature only for SAS3 C0 & higher + * revision HBAs and also only when reply queue count is greater than 8 + */ + if (ioc->msix96_vector && ioc->reply_queue_count > 8) { + /* Determine the Supplemental Reply Post Host Index Registers + * Addresse. Supplemental Reply Post Host Index Registers + * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and + * each register is at offset bytes of + * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one. + */ + ioc->replyPostRegisterIndex = kcalloc( + MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT, + sizeof(resource_size_t *), GFP_KERNEL); + if (!ioc->replyPostRegisterIndex) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "allocation for reply Post Register Index failed!!!\n", + ioc->name)); + r = -ENOMEM; + goto out_fail; + } + + for (i = 0; i < MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT; i++) { + ioc->replyPostRegisterIndex[i] = (resource_size_t *) + ((u8 *)&ioc->chip->Doorbell + + MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + + (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); + } + } else + ioc->msix96_vector = 0; + list_for_each_entry(reply_q, &ioc->reply_queue_list, list) pr_info(MPT3SAS_FMT "%s: IRQ %d\n", reply_q->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : @@ -1895,12 +2170,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) return 0; out_fail: - if (ioc->chip_phys) - iounmap(ioc->chip); - ioc->chip_phys = 0; - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); + mpt3sas_base_unmap_resources(ioc); return r; } @@ -2081,6 +2351,7 @@ mpt3sas_base_free_smid(struct MPT3SAS_ADAPTER *ioc, u16 smid) } ioc->scsi_lookup[i].cb_idx = 0xFF; ioc->scsi_lookup[i].scmd = NULL; + ioc->scsi_lookup[i].direct_io = 0; list_add(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -2241,54 +2512,265 @@ mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) } /** - * _base_display_intel_branding - Display branding string + * _base_display_OEMs_branding - Display branding string * @ioc: per adapter object * * Return nothing. */ static void -_base_display_intel_branding(struct MPT3SAS_ADAPTER *ioc) +_base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) { if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL) return; - switch (ioc->pdev->device) { - case MPI25_MFGPAGE_DEVID_SAS3008: - switch (ioc->pdev->subsystem_device) { - case MPT3SAS_INTEL_RMS3JC080_SSDID: - pr_info(MPT3SAS_FMT "%s\n", ioc->name, - MPT3SAS_INTEL_RMS3JC080_BRANDING); + switch (ioc->pdev->subsystem_vendor) { + case PCI_VENDOR_ID_INTEL: + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2008: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_INTEL_RMS2LL080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS2LL080_BRANDING); + break; + case MPT2SAS_INTEL_RMS2LL040_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS2LL040_BRANDING); + break; + case MPT2SAS_INTEL_SSD910_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_SSD910_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Intel(R) Controller: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + case MPI2_MFGPAGE_DEVID_SAS2308_2: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_INTEL_RS25GB008_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RS25GB008_BRANDING); + break; + case MPT2SAS_INTEL_RMS25JB080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25JB080_BRANDING); + break; + case MPT2SAS_INTEL_RMS25JB040_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25JB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25KB080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25KB080_BRANDING); + break; + case MPT2SAS_INTEL_RMS25KB040_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25KB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25LB040_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25LB040_BRANDING); + break; + case MPT2SAS_INTEL_RMS25LB080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_INTEL_RMS25LB080_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Intel(R) Controller: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + case MPI25_MFGPAGE_DEVID_SAS3008: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_INTEL_RMS3JC080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_INTEL_RMS3JC080_BRANDING); + break; + + case MPT3SAS_INTEL_RS3GC008_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_INTEL_RS3GC008_BRANDING); + break; + case MPT3SAS_INTEL_RS3FC044_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_INTEL_RS3FC044_BRANDING); + break; + case MPT3SAS_INTEL_RS3UC080_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_INTEL_RS3UC080_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Intel(R) Controller: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } break; - - case MPT3SAS_INTEL_RS3GC008_SSDID: - pr_info(MPT3SAS_FMT "%s\n", ioc->name, - MPT3SAS_INTEL_RS3GC008_BRANDING); + default: + pr_info(MPT3SAS_FMT + "Intel(R) Controller: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); break; - case MPT3SAS_INTEL_RS3FC044_SSDID: - pr_info(MPT3SAS_FMT "%s\n", ioc->name, - MPT3SAS_INTEL_RS3FC044_BRANDING); + } + break; + case PCI_VENDOR_ID_DELL: + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2008: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING); + break; + case MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING); + break; + case MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING); + break; + case MPT2SAS_DELL_PERC_H200_MODULAR_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING); + break; + case MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING); + break; + case MPT2SAS_DELL_PERC_H200_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_PERC_H200_BRANDING); + break; + case MPT2SAS_DELL_6GBPS_SAS_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_DELL_6GBPS_SAS_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Dell 6Gbps HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } break; - case MPT3SAS_INTEL_RS3UC080_SSDID: - pr_info(MPT3SAS_FMT "%s\n", ioc->name, - MPT3SAS_INTEL_RS3UC080_BRANDING); + case MPI25_MFGPAGE_DEVID_SAS3008: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_DELL_12G_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_DELL_12G_HBA_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } break; default: pr_info(MPT3SAS_FMT - "Intel(R) Controller: Subsystem ID: 0x%X\n", - ioc->name, ioc->pdev->subsystem_device); + "Dell HBA: Subsystem ID: 0x%X\n", ioc->name, + ioc->pdev->subsystem_device); break; } break; + case PCI_VENDOR_ID_CISCO: + switch (ioc->pdev->device) { + case MPI25_MFGPAGE_DEVID_SAS3008: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_CISCO_12G_8E_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_8E_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_8I_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_8I_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + break; + case MPI25_MFGPAGE_DEVID_SAS3108_1: + switch (ioc->pdev->subsystem_device) { + case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING); + break; + case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING + ); + break; + default: + pr_info(MPT3SAS_FMT + "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + break; + default: + pr_info(MPT3SAS_FMT + "Cisco SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + break; + case MPT2SAS_HP_3PAR_SSVID: + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2004: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + case MPI2_MFGPAGE_DEVID_SAS2308_2: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_2_4_INTERNAL_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_INTERNAL_BRANDING); + break; + case MPT2SAS_HP_2_4_EXTERNAL_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: + pr_info(MPT3SAS_FMT "%s\n", ioc->name, + MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); + break; + default: + pr_info(MPT3SAS_FMT + "HP 6Gbps SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } + default: + pr_info(MPT3SAS_FMT + "HP SAS HBA: Subsystem ID: 0x%X\n", + ioc->name, ioc->pdev->subsystem_device); + break; + } default: - pr_info(MPT3SAS_FMT - "Intel(R) Controller: Subsystem ID: 0x%X\n", - ioc->name, ioc->pdev->subsystem_device); break; } } - - /** * _base_display_ioc_capabilities - Disply IOC's capabilities. * @ioc: per adapter object @@ -2318,7 +2800,7 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) (bios_version & 0x0000FF00) >> 8, bios_version & 0x000000FF); - _base_display_intel_branding(ioc); + _base_display_OEMs_branding(ioc); pr_info(MPT3SAS_FMT "Protocol=(", ioc->name); @@ -2336,10 +2818,12 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc) pr_info("), "); pr_info("Capabilities=("); - if (ioc->facts.IOCCapabilities & + if (!ioc->hide_ir_msg) { + if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { pr_info("Raid"); i++; + } } if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { @@ -2680,18 +3164,22 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /* command line tunables for max sgl entries */ if (max_sgl_entries != -1) sg_tablesize = max_sgl_entries; - else - sg_tablesize = MPT3SAS_SG_DEPTH; + else { + if (ioc->hba_mpi_version_belonged == MPI2_VERSION) + sg_tablesize = MPT2SAS_SG_DEPTH; + else + sg_tablesize = MPT3SAS_SG_DEPTH; + } - if (sg_tablesize < MPT3SAS_MIN_PHYS_SEGMENTS) - sg_tablesize = MPT3SAS_MIN_PHYS_SEGMENTS; - else if (sg_tablesize > MPT3SAS_MAX_PHYS_SEGMENTS) { + if (sg_tablesize < MPT_MIN_PHYS_SEGMENTS) + sg_tablesize = MPT_MIN_PHYS_SEGMENTS; + else if (sg_tablesize > MPT_MAX_PHYS_SEGMENTS) { sg_tablesize = min_t(unsigned short, sg_tablesize, SCSI_MAX_SG_CHAIN_SEGMENTS); pr_warn(MPT3SAS_FMT "sg_tablesize(%u) is bigger than kernel" " defined SCSI_MAX_SG_SEGMENTS(%u)\n", ioc->name, - sg_tablesize, MPT3SAS_MAX_PHYS_SEGMENTS); + sg_tablesize, MPT_MAX_PHYS_SEGMENTS); } ioc->shost->sg_tablesize = sg_tablesize; @@ -3136,6 +3624,9 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout, * * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. */ +static int +_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag); + static int _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout, int sleep_flag) @@ -3678,6 +4169,64 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag) return 0; } +/** + * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL + * @ioc: per adapter object + * @timeout: + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout, + int sleep_flag) +{ + u32 ioc_state; + int rc; + + dinitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ioc->name, + __func__)); + + if (ioc->pci_error_recovery) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: host in pci error recovery\n", ioc->name, __func__)); + return -EFAULT; + } + + ioc_state = mpt3sas_base_get_iocstate(ioc, 0); + dhsprintk(ioc, printk(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n", + ioc->name, __func__, ioc_state)); + + if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) || + (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) + return 0; + + if (ioc_state & MPI2_DOORBELL_USED) { + dhsprintk(ioc, printk(MPT3SAS_FMT + "unexpected doorbell active!\n", ioc->name)); + goto issue_diag_reset; + } + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt3sas_base_fault_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + goto issue_diag_reset; + } + + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, + timeout, sleep_flag); + if (ioc_state) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: failed going to ready state (ioc_state=0x%x)\n", + ioc->name, __func__, ioc_state)); + return -EFAULT; + } + + issue_diag_reset: + rc = _base_diag_reset(ioc, sleep_flag); + return rc; +} + /** * _base_get_ioc_facts - obtain ioc facts reply and save in ioc * @ioc: per adapter object @@ -3696,6 +4245,13 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); + r = _base_wait_for_iocstate(ioc, 10, sleep_flag); + if (r) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "%s: failed getting to correct state\n", + ioc->name, __func__)); + return r; + } mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); memset(&mpi_request, 0, mpi_request_sz); @@ -3781,7 +4337,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; mpi_request.VF_ID = 0; /* TODO */ mpi_request.VP_ID = 0; - mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); + mpi_request.MsgVersion = cpu_to_le16(ioc->hba_mpi_version_belonged); mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); if (_base_is_controller_msix_enabled(ioc)) @@ -4415,6 +4971,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) u32 reply_address; u16 smid; struct _tr_list *delayed_tr, *delayed_tr_next; + u8 hide_flag; struct adapter_reply_queue *reply_q; long reply_post_free; u32 reply_post_free_sz, index = 0; @@ -4445,6 +5002,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) ioc->scsi_lookup[i].cb_idx = 0xFF; ioc->scsi_lookup[i].smid = smid; ioc->scsi_lookup[i].scmd = NULL; + ioc->scsi_lookup[i].direct_io = 0; list_add_tail(&ioc->scsi_lookup[i].tracker_list, &ioc->free_list); } @@ -4522,8 +5080,15 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) /* initialize reply post host index */ list_for_each_entry(reply_q, &ioc->reply_queue_list, list) { - writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT, - &ioc->chip->ReplyPostHostIndex); + if (ioc->msix96_vector) + writel((reply_q->msix_index & 7)<< + MPI2_RPHI_MSIX_INDEX_SHIFT, + ioc->replyPostRegisterIndex[reply_q->msix_index/8]); + else + writel(reply_q->msix_index << + MPI2_RPHI_MSIX_INDEX_SHIFT, + &ioc->chip->ReplyPostHostIndex); + if (!_base_is_controller_msix_enabled(ioc)) goto skip_init_reply_post_host_index; } @@ -4540,6 +5105,16 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) if (ioc->is_driver_loading) { + + if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier + == 0x80) { + hide_flag = (u8) ( + le32_to_cpu(ioc->manu_pg10.OEMSpecificFlags0) & + MFG_PAGE10_HIDE_SSDS_MASK); + if (hide_flag != MFG_PAGE10_HIDE_SSDS_MASK) + ioc->mfg_pg10_hide_flag = hide_flag; + } + ioc->wait_for_discovery_to_complete = _base_determine_wait_on_discovery(ioc); @@ -4562,11 +5137,11 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag) void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) { - struct pci_dev *pdev = ioc->pdev; - dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); + /* synchronizing freeing resource with pci_access_mutex lock */ + mutex_lock(&ioc->pci_access_mutex); if (ioc->chip_phys && ioc->chip) { _base_mask_interrupts(ioc); ioc->shost_recovery = 1; @@ -4574,18 +5149,8 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc) ioc->shost_recovery = 0; } - _base_free_irq(ioc); - _base_disable_msix(ioc); - - if (ioc->chip_phys && ioc->chip) - iounmap(ioc->chip); - ioc->chip_phys = 0; - - if (pci_is_enabled(pdev)) { - pci_release_selected_regions(ioc->pdev, ioc->bars); - pci_disable_pcie_error_reporting(pdev); - pci_disable_device(pdev); - } + mpt3sas_base_unmap_resources(ioc); + mutex_unlock(&ioc->pci_access_mutex); return; } @@ -4619,30 +5184,58 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) goto out_free_resources; } + if (ioc->is_warpdrive) { + ioc->reply_post_host_index = kcalloc(ioc->cpu_msix_table_sz, + sizeof(resource_size_t *), GFP_KERNEL); + if (!ioc->reply_post_host_index) { + dfailprintk(ioc, pr_info(MPT3SAS_FMT "allocation " + "for cpu_msix_table failed!!!\n", ioc->name)); + r = -ENOMEM; + goto out_free_resources; + } + } + ioc->rdpq_array_enable_assigned = 0; ioc->dma_mask = 0; r = mpt3sas_base_map_resources(ioc); if (r) goto out_free_resources; + if (ioc->is_warpdrive) { + ioc->reply_post_host_index[0] = (resource_size_t __iomem *) + &ioc->chip->ReplyPostHostIndex; + + for (i = 1; i < ioc->cpu_msix_table_sz; i++) + ioc->reply_post_host_index[i] = + (resource_size_t __iomem *) + ((u8 __iomem *)&ioc->chip->Doorbell + (0x4000 + ((i - 1) + * 4))); + } pci_set_drvdata(ioc->pdev, ioc->shost); r = _base_get_ioc_facts(ioc, CAN_SLEEP); if (r) goto out_free_resources; - /* - * In SAS3.0, - * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and - * Target Status - all require the IEEE formated scatter gather - * elements. - */ - - ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; - ioc->build_sg = &_base_build_sg_ieee; - ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; - ioc->mpi25 = 1; - ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); + switch (ioc->hba_mpi_version_belonged) { + case MPI2_VERSION: + ioc->build_sg_scmd = &_base_build_sg_scmd; + ioc->build_sg = &_base_build_sg; + ioc->build_zero_len_sge = &_base_build_zero_len_sge; + break; + case MPI25_VERSION: + /* + * In SAS3.0, + * SCSI_IO, SMP_PASSTHRU, SATA_PASSTHRU, Target Assist, and + * Target Status - all require the IEEE formated scatter gather + * elements. + */ + ioc->build_sg_scmd = &_base_build_sg_scmd_ieee; + ioc->build_sg = &_base_build_sg_ieee; + ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee; + ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t); + break; + } /* * These function pointers for other requests that don't @@ -4758,6 +5351,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) if (r) goto out_free_resources; + ioc->non_operational_loop = 0; return 0; out_free_resources: @@ -4768,6 +5362,8 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) _base_release_memory_pools(ioc); pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->cpu_msix_table); + if (ioc->is_warpdrive) + kfree(ioc->reply_post_host_index); kfree(ioc->pd_handles); kfree(ioc->blocking_handles); kfree(ioc->tm_cmds.reply); @@ -4807,6 +5403,8 @@ mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc) _base_release_memory_pools(ioc); pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->cpu_msix_table); + if (ioc->is_warpdrive) + kfree(ioc->reply_post_host_index); kfree(ioc->pd_handles); kfree(ioc->blocking_handles); kfree(ioc->pfacts); diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_base.h b/kernel/drivers/scsi/mpt3sas/mpt3sas_base.h index afa881682..5ad271efb 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -63,6 +63,8 @@ #include #include #include +#include +#include #include "mpt3sas_debug.h" #include "mpt3sas_trigger_diag.h" @@ -71,23 +73,37 @@ #define MPT3SAS_DRIVER_NAME "mpt3sas" #define MPT3SAS_AUTHOR "Avago Technologies " #define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" -#define MPT3SAS_DRIVER_VERSION "04.100.00.00" -#define MPT3SAS_MAJOR_VERSION 4 -#define MPT3SAS_MINOR_VERSION 100 +#define MPT3SAS_DRIVER_VERSION "09.102.00.00" +#define MPT3SAS_MAJOR_VERSION 9 +#define MPT3SAS_MINOR_VERSION 102 #define MPT3SAS_BUILD_VERSION 0 #define MPT3SAS_RELEASE_VERSION 00 +#define MPT2SAS_DRIVER_NAME "mpt2sas" +#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" +#define MPT2SAS_DRIVER_VERSION "20.102.00.00" +#define MPT2SAS_MAJOR_VERSION 20 +#define MPT2SAS_MINOR_VERSION 102 +#define MPT2SAS_BUILD_VERSION 0 +#define MPT2SAS_RELEASE_VERSION 00 + /* * Set MPT3SAS_SG_DEPTH value based on user input. */ -#define MPT3SAS_MAX_PHYS_SEGMENTS SCSI_MAX_SG_SEGMENTS -#define MPT3SAS_MIN_PHYS_SEGMENTS 16 +#define MPT_MAX_PHYS_SEGMENTS SCSI_MAX_SG_SEGMENTS +#define MPT_MIN_PHYS_SEGMENTS 16 + #ifdef CONFIG_SCSI_MPT3SAS_MAX_SGE #define MPT3SAS_SG_DEPTH CONFIG_SCSI_MPT3SAS_MAX_SGE #else -#define MPT3SAS_SG_DEPTH MPT3SAS_MAX_PHYS_SEGMENTS +#define MPT3SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS #endif +#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE +#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE +#else +#define MPT2SAS_SG_DEPTH MPT_MAX_PHYS_SEGMENTS +#endif /* * Generic Defines @@ -123,6 +139,16 @@ */ #define MPT3SAS_FMT "%s: " +/* + * WarpDrive Specific Log codes + */ + +#define MPT2_WARPDRIVE_LOGENTRY (0x8002) +#define MPT2_WARPDRIVE_LC_SSDT (0x41) +#define MPT2_WARPDRIVE_LC_SSDLW (0x43) +#define MPT2_WARPDRIVE_LC_SSDLF (0x44) +#define MPT2_WARPDRIVE_LC_BRMF (0x4D) + /* * per target private data */ @@ -131,9 +157,33 @@ #define MPT_TARGET_FLAGS_DELETED 0x04 #define MPT_TARGET_FASTPATH_IO 0x08 +#define SAS2_PCI_DEVICE_B0_REVISION (0x01) +#define SAS3_PCI_DEVICE_C0_REVISION (0x02) + /* * Intel HBA branding */ +#define MPT2SAS_INTEL_RMS25JB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25JB080" +#define MPT2SAS_INTEL_RMS25JB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25JB040" +#define MPT2SAS_INTEL_RMS25KB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25KB080" +#define MPT2SAS_INTEL_RMS25KB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25KB040" +#define MPT2SAS_INTEL_RMS25LB040_BRANDING \ + "Intel(R) Integrated RAID Module RMS25LB040" +#define MPT2SAS_INTEL_RMS25LB080_BRANDING \ + "Intel(R) Integrated RAID Module RMS25LB080" +#define MPT2SAS_INTEL_RMS2LL080_BRANDING \ + "Intel Integrated RAID Module RMS2LL080" +#define MPT2SAS_INTEL_RMS2LL040_BRANDING \ + "Intel Integrated RAID Module RMS2LL040" +#define MPT2SAS_INTEL_RS25GB008_BRANDING \ + "Intel(R) RAID Controller RS25GB008" +#define MPT2SAS_INTEL_SSD910_BRANDING \ + "Intel(R) SSD 910 Series" + #define MPT3SAS_INTEL_RMS3JC080_BRANDING \ "Intel(R) Integrated RAID Module RMS3JC080" #define MPT3SAS_INTEL_RS3GC008_BRANDING \ @@ -146,10 +196,69 @@ /* * Intel HBA SSDIDs */ -#define MPT3SAS_INTEL_RMS3JC080_SSDID 0x3521 -#define MPT3SAS_INTEL_RS3GC008_SSDID 0x3522 -#define MPT3SAS_INTEL_RS3FC044_SSDID 0x3523 -#define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524 +#define MPT2SAS_INTEL_RMS25JB080_SSDID 0x3516 +#define MPT2SAS_INTEL_RMS25JB040_SSDID 0x3517 +#define MPT2SAS_INTEL_RMS25KB080_SSDID 0x3518 +#define MPT2SAS_INTEL_RMS25KB040_SSDID 0x3519 +#define MPT2SAS_INTEL_RMS25LB040_SSDID 0x351A +#define MPT2SAS_INTEL_RMS25LB080_SSDID 0x351B +#define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E +#define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F +#define MPT2SAS_INTEL_RS25GB008_SSDID 0x3000 +#define MPT2SAS_INTEL_SSD910_SSDID 0x3700 + +#define MPT3SAS_INTEL_RMS3JC080_SSDID 0x3521 +#define MPT3SAS_INTEL_RS3GC008_SSDID 0x3522 +#define MPT3SAS_INTEL_RS3FC044_SSDID 0x3523 +#define MPT3SAS_INTEL_RS3UC080_SSDID 0x3524 + +/* + * Dell HBA branding + */ +#define MPT2SAS_DELL_BRANDING_SIZE 32 + +#define MPT2SAS_DELL_6GBPS_SAS_HBA_BRANDING "Dell 6Gbps SAS HBA" +#define MPT2SAS_DELL_PERC_H200_ADAPTER_BRANDING "Dell PERC H200 Adapter" +#define MPT2SAS_DELL_PERC_H200_INTEGRATED_BRANDING "Dell PERC H200 Integrated" +#define MPT2SAS_DELL_PERC_H200_MODULAR_BRANDING "Dell PERC H200 Modular" +#define MPT2SAS_DELL_PERC_H200_EMBEDDED_BRANDING "Dell PERC H200 Embedded" +#define MPT2SAS_DELL_PERC_H200_BRANDING "Dell PERC H200" +#define MPT2SAS_DELL_6GBPS_SAS_BRANDING "Dell 6Gbps SAS" + +#define MPT3SAS_DELL_12G_HBA_BRANDING \ + "Dell 12Gbps HBA" + +/* + * Dell HBA SSDIDs + */ +#define MPT2SAS_DELL_6GBPS_SAS_HBA_SSDID 0x1F1C +#define MPT2SAS_DELL_PERC_H200_ADAPTER_SSDID 0x1F1D +#define MPT2SAS_DELL_PERC_H200_INTEGRATED_SSDID 0x1F1E +#define MPT2SAS_DELL_PERC_H200_MODULAR_SSDID 0x1F1F +#define MPT2SAS_DELL_PERC_H200_EMBEDDED_SSDID 0x1F20 +#define MPT2SAS_DELL_PERC_H200_SSDID 0x1F21 +#define MPT2SAS_DELL_6GBPS_SAS_SSDID 0x1F22 + +#define MPT3SAS_DELL_12G_HBA_SSDID 0x1F46 + +/* + * Cisco HBA branding + */ +#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING \ + "Cisco 9300-8E 12G SAS HBA" +#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING \ + "Cisco 9300-8i 12G SAS HBA" +#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING \ + "Cisco 12G Modular SAS Pass through Controller" +#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING \ + "UCS C3X60 12G SAS Pass through Controller" +/* + * Cisco HBA SSSDIDs + */ +#define MPT3SAS_CISCO_12G_8E_HBA_SSDID 0x14C +#define MPT3SAS_CISCO_12G_8I_HBA_SSDID 0x154 +#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID 0x155 +#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID 0x156 /* * status bits for ioc->diag_buffer_status @@ -158,6 +267,38 @@ #define MPT3_DIAG_BUFFER_IS_RELEASED (0x02) #define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04) +/* + * HP HBA branding + */ +#define MPT2SAS_HP_3PAR_SSVID 0x1590 + +#define MPT2SAS_HP_2_4_INTERNAL_BRANDING \ + "HP H220 Host Bus Adapter" +#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING \ + "HP H221 Host Bus Adapter" +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING \ + "HP H222 Host Bus Adapter" +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING \ + "HP H220i Host Bus Adapter" +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING \ + "HP H210i Host Bus Adapter" + +/* + * HO HBA SSDIDs + */ +#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 +#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 + +/* + * Combined Reply Queue constants, + * There are twelve Supplemental Reply Post Host Index Registers + * and each register is at offset 0x10 bytes from the previous one. + */ +#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT 12 +#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10) /* OEM Identifiers */ #define MFG10_OEM_ID_INVALID (0x00000000) @@ -173,6 +314,8 @@ #define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008) #define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010) +#define VIRTUAL_IO_FAILED_RETRY (0x32010081) + /* OEM Specific Flags will come from OEM specific header files */ struct Mpi2ManufacturingPage10_t { MPI2_CONFIG_PAGE_HEADER Header; /* 00h */ @@ -204,20 +347,24 @@ struct Mpi2ManufacturingPage11_t { * struct MPT3SAS_TARGET - starget private hostdata * @starget: starget object * @sas_address: target sas address + * @raid_device: raid_device pointer to access volume data * @handle: device handle * @num_luns: number luns * @flags: MPT_TARGET_FLAGS_XXX flags * @deleted: target flaged for deletion * @tm_busy: target is busy with TM request. + * @sdev: The sas_device associated with this target */ struct MPT3SAS_TARGET { struct scsi_target *starget; u64 sas_address; + struct _raid_device *raid_device; u16 handle; int num_luns; u32 flags; u8 deleted; u8 tm_busy; + struct _sas_device *sdev; }; @@ -227,6 +374,11 @@ struct MPT3SAS_TARGET { #define MPT_DEVICE_FLAGS_INIT 0x01 #define MPT_DEVICE_TLR_ON 0x02 +#define MFG_PAGE10_HIDE_SSDS_MASK (0x00000003) +#define MFG_PAGE10_HIDE_ALL_DISKS (0x00) +#define MFG_PAGE10_EXPOSE_ALL_DISKS (0x01) +#define MFG_PAGE10_HIDE_IF_VOL_PRESENT (0x02) + /** * struct MPT3SAS_DEVICE - sdev private hostdata * @sas_target: starget private hostdata @@ -294,7 +446,8 @@ struct _internal_cmd { * @responding: used in _scsih_sas_device_mark_responding * @fast_path: fast path feature enable bit * @pfa_led_on: flag for PFA LED status - * + * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add() + * addition routine. */ struct _sas_device { struct list_head list; @@ -315,8 +468,27 @@ struct _sas_device { u8 responding; u8 fast_path; u8 pfa_led_on; + u8 pend_sas_rphy_add; + u8 enclosure_level; + u8 connector_name[4]; + struct kref refcount; }; +static inline void sas_device_get(struct _sas_device *s) +{ + kref_get(&s->refcount); +} + +static inline void sas_device_free(struct kref *r) +{ + kfree(container_of(r, struct _sas_device, refcount)); +} + +static inline void sas_device_put(struct _sas_device *s) +{ + kref_put(&s->refcount, sas_device_free); +} + /** * struct _raid_device - raid volume link list * @list: sas device list @@ -324,6 +496,7 @@ struct _sas_device { * @sdev: scsi device struct (volumes are single lun) * @wwid: unique identifier for the volume * @handle: device handle + * @block_size: Block size of the volume * @id: target id * @channel: target channel * @volume_type: the raid level @@ -331,6 +504,13 @@ struct _sas_device { * @num_pds: number of hidden raid components * @responding: used in _scsih_raid_device_mark_responding * @percent_complete: resync percent complete + * @direct_io_enabled: Whether direct io to PDs are allowed or not + * @stripe_exponent: X where 2powX is the stripe sz in blocks + * @block_exponent: X where 2powX is the block sz in bytes + * @max_lba: Maximum number of LBA in the volume + * @stripe_sz: Stripe Size of the volume + * @device_info: Device info of the volume member disk + * @pd_handle: Array of handles of the physical drives for direct I/O in le16 */ #define MPT_MAX_WARPDRIVE_PDS 8 struct _raid_device { @@ -339,13 +519,20 @@ struct _raid_device { struct scsi_device *sdev; u64 wwid; u16 handle; + u16 block_sz; int id; int channel; u8 volume_type; u8 num_pds; u8 responding; u8 percent_complete; + u8 direct_io_enabled; + u8 stripe_exponent; + u8 block_exponent; + u64 max_lba; + u32 stripe_sz; u32 device_info; + u16 pd_handle[MPT_MAX_WARPDRIVE_PDS]; }; /** @@ -454,12 +641,14 @@ struct chain_tracker { * @smid: system message id * @scmd: scsi request pointer * @cb_idx: callback index + * @direct_io: To indicate whether I/O is direct (WARPDRIVE) * @tracker_list: list of free request (ioc->free_list) */ struct scsiio_tracker { u16 smid; struct scsi_cmnd *scmd; u8 cb_idx; + u8 direct_io; struct list_head chain_list; struct list_head tracker_list; }; @@ -728,10 +917,17 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * is assigned only ones * @reply_queue_count: number of reply queue's * @reply_queue_list: link list contaning the reply queue info - * @reply_post_host_index: head index in the pool where FW completes IO + * @msix96_vector: 96 MSI-X vector support + * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue * @delayed_tr_list: target reset link list * @delayed_tr_volume_list: volume target reset link list - * @@temp_sensors_count: flag to carry the number of temperature sensors + * @temp_sensors_count: flag to carry the number of temperature sensors + * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and + * pci resource handling. PCI resource freeing will lead to free + * vital hardware/memory resource, which might be in use by cli/sysfs + * path functions resulting in Null pointer reference followed by kernel + * crash. To avoid the above race condition we use mutex syncrhonization + * which ensures the syncrhonization between cli/sysfs_show path. */ struct MPT3SAS_ADAPTER { struct list_head list; @@ -739,6 +935,7 @@ struct MPT3SAS_ADAPTER { u8 id; int cpu_count; char name[MPT_NAME_LENGTH]; + char driver_name[MPT_NAME_LENGTH]; char tmp_string[MPT_STRING_LENGTH]; struct pci_dev *pdev; Mpi2SystemInterfaceRegs_t __iomem *chip; @@ -785,8 +982,10 @@ struct MPT3SAS_ADAPTER { u16 msix_vector_count; u8 *cpu_msix_table; u16 cpu_msix_table_sz; + resource_size_t __iomem **reply_post_host_index; u32 ioc_reset_count; MPT3SAS_FLUSH_RUNNING_CMDS schedule_dead_ioc_flush_running_cmds; + u32 non_operational_loop; /* internal commands, callback index */ u8 scsi_io_cb_idx; @@ -814,8 +1013,8 @@ struct MPT3SAS_ADAPTER { MPT_BUILD_SG_SCMD build_sg_scmd; MPT_BUILD_SG build_sg; MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge; - u8 mpi25; u16 sge_size_ieee; + u16 hba_mpi_version_belonged; /* function ptr for MPI sg elements only */ MPT_BUILD_SG build_sg_mpi; @@ -937,9 +1136,14 @@ struct MPT3SAS_ADAPTER { u8 reply_queue_count; struct list_head reply_queue_list; + u8 msix96_vector; + /* reply post register index */ + resource_size_t **replyPostRegisterIndex; + struct list_head delayed_tr_list; struct list_head delayed_tr_volume_list; u8 temp_sensors_count; + struct mutex pci_access_mutex; /* diag buffer support */ u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; @@ -951,6 +1155,10 @@ struct MPT3SAS_ADAPTER { u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; u32 ring_buffer_offset; u32 ring_buffer_sz; + u8 is_warpdrive; + u8 hide_ir_msg; + u8 mfg_pg10_hide_flag; + u8 hide_drives; spinlock_t diag_trigger_lock; u8 diag_trigger_active; struct SL_WH_MASTER_TRIGGER_T diag_trigger_master; @@ -965,6 +1173,19 @@ typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, /* base shared API */ extern struct list_head mpt3sas_ioc_list; +extern char driver_name[MPT_NAME_LENGTH]; +/* spinlock on list operations over IOCs + * Case: when multiple warpdrive cards(IOCs) are in use + * Each IOC will added to the ioc list structure on initialization. + * Watchdog threads run at regular intervals to check IOC for any + * fault conditions which will trigger the dead_ioc thread to + * deallocate pci resource, resulting deleting the IOC netry from list, + * this deletion need to protected by spinlock to enusre that + * ioc removal is syncrhonized, if not synchronized it might lead to + * list_del corruption as the ioc list is traversed in cli path. + */ +extern spinlock_t gioc_lock; + void mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc); @@ -1043,10 +1264,14 @@ struct _sas_node *mpt3sas_scsih_expander_find_by_handle( struct MPT3SAS_ADAPTER *ioc, u16 handle); struct _sas_node *mpt3sas_scsih_expander_find_by_sas_address( struct MPT3SAS_ADAPTER *ioc, u64 sas_address); -struct _sas_device *mpt3sas_scsih_sas_device_find_by_sas_address( - struct MPT3SAS_ADAPTER *ioc, u64 sas_address); +struct _sas_device *mpt3sas_get_sdev_by_addr( + struct MPT3SAS_ADAPTER *ioc, u64 sas_address); +struct _sas_device *__mpt3sas_get_sdev_by_addr( + struct MPT3SAS_ADAPTER *ioc, u64 sas_address); void mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc); +struct _raid_device * +mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle); /* config shared API */ u8 mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, @@ -1086,6 +1311,8 @@ int mpt3sas_config_get_sas_iounit_pg0(struct MPT3SAS_ADAPTER *ioc, u16 sz); int mpt3sas_config_get_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page); +int mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); int mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page); int mpt3sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t @@ -1130,8 +1357,8 @@ int mpt3sas_config_get_volume_wwid(struct MPT3SAS_ADAPTER *ioc, /* ctl shared API */ extern struct device_attribute *mpt3sas_host_attrs[]; extern struct device_attribute *mpt3sas_dev_attrs[]; -void mpt3sas_ctl_init(void); -void mpt3sas_ctl_exit(void); +void mpt3sas_ctl_init(ushort hbas_to_enumerate); +void mpt3sas_ctl_exit(ushort hbas_to_enumerate); u8 mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); void mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); @@ -1146,6 +1373,7 @@ int mpt3sas_send_diag_release(struct MPT3SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset); /* transport shared API */ +extern struct scsi_transport_template *mpt3sas_transport_template; u8 mpt3sas_transport_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply); struct _sas_port *mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, @@ -1177,4 +1405,18 @@ void mpt3sas_trigger_scsi(struct MPT3SAS_ADAPTER *ioc, u8 sense_key, u8 asc, u8 ascq); void mpt3sas_trigger_mpi(struct MPT3SAS_ADAPTER *ioc, u16 ioc_status, u32 loginfo); + +/* warpdrive APIs */ +u8 mpt3sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, + struct _raid_device *raid_device); +u8 +mpt3sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid); +void +mpt3sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io); +void +mpt3sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, + struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, + u16 smid); + #endif /* MPT3SAS_BASE_H_INCLUDED */ diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_config.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_config.c index e45c4613e..a6914ec99 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -83,7 +83,6 @@ struct config_request { dma_addr_t page_dma; }; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _config_display_some_debug - debug routine * @ioc: per adapter object @@ -173,7 +172,6 @@ _config_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo)); } -#endif /** * _config_alloc_config_dma_memory - obtain physical memory @@ -255,9 +253,7 @@ mpt3sas_config_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, mpi_reply->MsgLength*4); } ioc->config_cmds.status &= ~MPT3_CMD_PENDING; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING _config_display_some_debug(ioc, smid, "config_done", mpi_reply); -#endif ioc->config_cmds.smid = USHRT_MAX; complete(&ioc->config_cmds.done); return 1; @@ -387,9 +383,7 @@ _config_request(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigRequest_t config_request = mpt3sas_base_get_msg_frame(ioc, smid); ioc->config_cmds.smid = smid; memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING _config_display_some_debug(ioc, smid, "config_request", NULL); -#endif init_completion(&ioc->config_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, @@ -871,6 +865,42 @@ mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, return r; } +/** + * mpt3sas_config_get_iounit_pg3 - obtain iounit page 3 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 3; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE3_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz); + out: + return r; +} + /** * mpt3sas_config_get_iounit_pg8 - obtain iounit page 8 * @ioc: per adapter object diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.c index 080c8a76d..d8366b056 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -78,7 +78,6 @@ enum block_state { BLOCKING, }; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _ctl_sas_device_find_by_handle - sas device search * @ioc: per adapter object @@ -254,8 +253,6 @@ _ctl_display_some_debug(struct MPT3SAS_ADAPTER *ioc, u16 smid, } } -#endif - /** * mpt3sas_ctl_done - ctl module completion routine * @ioc: per adapter object @@ -302,9 +299,7 @@ mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, } } } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); -#endif ioc->ctl_cmds.status &= ~MPT3_CMD_PENDING; complete(&ioc->ctl_cmds.done); return 1; @@ -414,20 +409,31 @@ mpt3sas_ctl_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, * _ctl_verify_adapter - validates ioc_number passed from application * @ioc: per adapter object * @iocpp: The ioc pointer is returned in this. + * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & + * MPI25_VERSION for mpt3ctl ioctl device. * * Return (-1) means error, else ioc_number. */ static int -_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp) +_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp, + int mpi_version) { struct MPT3SAS_ADAPTER *ioc; - + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt3sas_ioc_list, list) { if (ioc->id != ioc_number) continue; + /* Check whether this ioctl command is from right + * ioctl device or not, if not continue the search. + */ + if (ioc->hba_mpi_version_belonged != mpi_version) + continue; + spin_unlock(&gioc_lock); *iocpp = ioc; return ioc_number; } + spin_unlock(&gioc_lock); *iocpp = NULL; return -1; } @@ -497,7 +503,7 @@ mpt3sas_ctl_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) * * Called when application request fasyn callback handler. */ -static int +int _ctl_fasync(int fd, struct file *filep, int mode) { return fasync_helper(fd, filep, mode, &async_queue); @@ -509,17 +515,22 @@ _ctl_fasync(int fd, struct file *filep, int mode) * @wait - * */ -static unsigned int +unsigned int _ctl_poll(struct file *filep, poll_table *wait) { struct MPT3SAS_ADAPTER *ioc; poll_wait(filep, &ctl_poll_wait, wait); + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt3sas_ioc_list, list) { - if (ioc->aen_event_read_flag) + if (ioc->aen_event_read_flag) { + spin_unlock(&gioc_lock); return POLLIN | POLLRDNORM; + } } + spin_unlock(&gioc_lock); return 0; } @@ -759,9 +770,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, psge = (void *)request + (karg.data_sge_offset*4); /* send command to firmware */ -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); -#endif init_completion(&ioc->ctl_cmds.done); switch (mpi_request->Function) { @@ -916,7 +925,6 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && (ioc->logging_level & MPT_DEBUG_TM)) { Mpi2SCSITaskManagementReply_t *tm_reply = @@ -929,7 +937,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, le32_to_cpu(tm_reply->IOCLogInfo), le32_to_cpu(tm_reply->TerminationCount)); } -#endif + /* copy out xdata to user */ if (data_in_sz) { if (copy_to_user(karg.data_in_buf_ptr, data_in, @@ -1023,7 +1031,6 @@ _ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) __func__)); memset(&karg, 0 , sizeof(karg)); - karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3; if (ioc->pfacts) karg.port_number = ioc->pfacts[0].PortNumber; karg.hw_rev = ioc->pdev->revision; @@ -1035,9 +1042,21 @@ _ctl_getiocinfo(struct MPT3SAS_ADAPTER *ioc, void __user *arg) karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); karg.firmware_version = ioc->facts.FWVersion.Word; - strcpy(karg.driver_version, MPT3SAS_DRIVER_NAME); + strcpy(karg.driver_version, ioc->driver_name); strcat(karg.driver_version, "-"); - strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION); + switch (ioc->hba_mpi_version_belonged) { + case MPI2_VERSION: + if (ioc->is_warpdrive) + karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; + else + karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; + strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION); + break; + case MPI25_VERSION: + karg.adapter_type = MPT3_IOCTL_INTERFACE_SAS3; + strcat(karg.driver_version, MPT3SAS_DRIVER_VERSION); + break; + } karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); if (copy_to_user(arg, &karg, sizeof(karg))) { @@ -2181,12 +2200,14 @@ _ctl_compat_mpt_command(struct MPT3SAS_ADAPTER *ioc, unsigned cmd, * _ctl_ioctl_main - main ioctl entry point * @file - (struct file) * @cmd - ioctl opcode - * @arg - - * compat - handles 32 bit applications in 64bit os + * @arg - user space data buffer + * @compat - handles 32 bit applications in 64bit os + * @mpi_version: will be MPI2_VERSION for mpt2ctl ioctl device & + * MPI25_VERSION for mpt3ctl ioctl device. */ static long _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, - u8 compat) + u8 compat, u16 mpi_version) { struct MPT3SAS_ADAPTER *ioc; struct mpt3_ioctl_header ioctl_header; @@ -2201,19 +2222,29 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, return -EFAULT; } - if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) + if (_ctl_verify_adapter(ioctl_header.ioc_number, + &ioc, mpi_version) == -1 || !ioc) return -ENODEV; + /* pci_access_mutex lock acquired by ioctl path */ + mutex_lock(&ioc->pci_access_mutex); + if (ioc->shost_recovery || ioc->pci_error_recovery || - ioc->is_driver_loading) - return -EAGAIN; + ioc->is_driver_loading || ioc->remove_host) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; if (state == NON_BLOCKING) { - if (!mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; - } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) - return -ERESTARTSYS; + if (!mutex_trylock(&ioc->ctl_cmds.mutex)) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } + } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { + ret = -ERESTARTSYS; + goto out_unlock_pciaccess; + } switch (cmd) { @@ -2294,39 +2325,78 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, } mutex_unlock(&ioc->ctl_cmds.mutex); +out_unlock_pciaccess: + mutex_unlock(&ioc->pci_access_mutex); return ret; } /** - * _ctl_ioctl - main ioctl entry point (unlocked) + * _ctl_ioctl - mpt3ctl main ioctl entry point (unlocked) * @file - (struct file) * @cmd - ioctl opcode * @arg - */ -static long +long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0); + /* pass MPI25_VERSION value, to indicate that this ioctl cmd + * came from mpt3ctl ioctl device. + */ + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, MPI25_VERSION); return ret; } +/** + * _ctl_mpt2_ioctl - mpt2ctl main ioctl entry point (unlocked) + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - + */ +long +_ctl_mpt2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret; + + /* pass MPI2_VERSION value, to indicate that this ioctl cmd + * came from mpt2ctl ioctl device. + */ + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0, MPI2_VERSION); + return ret; +} #ifdef CONFIG_COMPAT /** - * _ctl_ioctl_compat - main ioctl entry point (compat) + *_ ctl_ioctl_compat - main ioctl entry point (compat) * @file - * @cmd - * @arg - * * This routine handles 32 bit applications in 64bit os. */ -static long +long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; - ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1); + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, MPI25_VERSION); + return ret; +} + +/** + *_ ctl_mpt2_ioctl_compat - main ioctl entry point (compat) + * @file - + * @cmd - + * @arg - + * + * This routine handles 32 bit applications in 64bit os. + */ +long +_ctl_mpt2_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) +{ + long ret; + + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1, MPI2_VERSION); return ret; } #endif @@ -2713,6 +2783,82 @@ _ctl_ioc_reply_queue_count_show(struct device *cdev, static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, NULL); +/** + * _ctl_BRM_status_show - Backup Rail Monitor Status + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is number of reply queues + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); + Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; + Mpi2ConfigReply_t mpi_reply; + u16 backup_rail_monitor_status = 0; + u16 ioc_status; + int sz; + ssize_t rc = 0; + + if (!ioc->is_warpdrive) { + pr_err(MPT3SAS_FMT "%s: BRM attribute is only for" + " warpdrive\n", ioc->name, __func__); + goto out; + } + /* pci_access_mutex lock acquired by sysfs show path */ + mutex_lock(&ioc->pci_access_mutex); + if (ioc->pci_error_recovery || ioc->remove_host) { + mutex_unlock(&ioc->pci_access_mutex); + return 0; + } + + /* allocate upto GPIOVal 36 entries */ + sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); + io_unit_pg3 = kzalloc(sz, GFP_KERNEL); + if (!io_unit_pg3) { + pr_err(MPT3SAS_FMT "%s: failed allocating memory " + "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); + goto out; + } + + if (mpt3sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != + 0) { + pr_err(MPT3SAS_FMT + "%s: failed reading iounit_pg3\n", ioc->name, + __func__); + goto out; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + pr_err(MPT3SAS_FMT "%s: iounit_pg3 failed with " + "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); + goto out; + } + + if (io_unit_pg3->GPIOCount < 25) { + pr_err(MPT3SAS_FMT "%s: iounit_pg3->GPIOCount less than " + "25 entries, detected (%d) entries\n", ioc->name, __func__, + io_unit_pg3->GPIOCount); + goto out; + } + + /* BRM status is in bit zero of GPIOVal[24] */ + backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); + rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); + + out: + kfree(io_unit_pg3); + mutex_unlock(&ioc->pci_access_mutex); + return rc; +} +static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); + struct DIAG_BUFFER_START { __le32 Size; __le32 DiagVersion; @@ -3165,6 +3311,7 @@ struct device_attribute *mpt3sas_host_attrs[] = { &dev_attr_diag_trigger_event, &dev_attr_diag_trigger_scsi, &dev_attr_diag_trigger_mpi, + &dev_attr_BRM_status, NULL, }; @@ -3218,6 +3365,7 @@ struct device_attribute *mpt3sas_dev_attrs[] = { NULL, }; +/* file operations table for mpt3ctl device */ static const struct file_operations ctl_fops = { .owner = THIS_MODULE, .unlocked_ioctl = _ctl_ioctl, @@ -3228,23 +3376,53 @@ static const struct file_operations ctl_fops = { #endif }; +/* file operations table for mpt2ctl device */ +static const struct file_operations ctl_gen2_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = _ctl_mpt2_ioctl, + .poll = _ctl_poll, + .fasync = _ctl_fasync, +#ifdef CONFIG_COMPAT + .compat_ioctl = _ctl_mpt2_ioctl_compat, +#endif +}; + static struct miscdevice ctl_dev = { .minor = MPT3SAS_MINOR, .name = MPT3SAS_DEV_NAME, .fops = &ctl_fops, }; +static struct miscdevice gen2_ctl_dev = { + .minor = MPT2SAS_MINOR, + .name = MPT2SAS_DEV_NAME, + .fops = &ctl_gen2_fops, +}; + /** * mpt3sas_ctl_init - main entry point for ctl. * */ void -mpt3sas_ctl_init(void) +mpt3sas_ctl_init(ushort hbas_to_enumerate) { async_queue = NULL; - if (misc_register(&ctl_dev) < 0) - pr_err("%s can't register misc device [minor=%d]\n", - MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR); + + /* Don't register mpt3ctl ioctl device if + * hbas_to_enumarate is one. + */ + if (hbas_to_enumerate != 1) + if (misc_register(&ctl_dev) < 0) + pr_err("%s can't register misc device [minor=%d]\n", + MPT3SAS_DRIVER_NAME, MPT3SAS_MINOR); + + /* Don't register mpt3ctl ioctl device if + * hbas_to_enumarate is two. + */ + if (hbas_to_enumerate != 2) + if (misc_register(&gen2_ctl_dev) < 0) + pr_err("%s can't register misc device [minor=%d]\n", + MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); init_waitqueue_head(&ctl_poll_wait); } @@ -3254,7 +3432,7 @@ mpt3sas_ctl_init(void) * */ void -mpt3sas_ctl_exit(void) +mpt3sas_ctl_exit(ushort hbas_to_enumerate) { struct MPT3SAS_ADAPTER *ioc; int i; @@ -3279,5 +3457,8 @@ mpt3sas_ctl_exit(void) kfree(ioc->event_log); } - misc_deregister(&ctl_dev); + if (hbas_to_enumerate != 1) + misc_deregister(&ctl_dev); + if (hbas_to_enumerate != 2) + misc_deregister(&gen2_ctl_dev); } diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.h index aee99ce67..89408356d 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.h +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_ctl.h @@ -50,10 +50,13 @@ #include #endif - +#ifndef MPT2SAS_MINOR +#define MPT2SAS_MINOR (MPT_MINOR + 1) +#endif #ifndef MPT3SAS_MINOR #define MPT3SAS_MINOR (MPT_MINOR + 2) #endif +#define MPT2SAS_DEV_NAME "mpt2ctl" #define MPT3SAS_DEV_NAME "mpt3ctl" #define MPT3_MAGIC_NUMBER 'L' #define MPT3_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ @@ -138,6 +141,7 @@ struct mpt3_ioctl_pci_info { #define MPT2_IOCTL_INTERFACE_FC_IP (0x02) #define MPT2_IOCTL_INTERFACE_SAS (0x03) #define MPT2_IOCTL_INTERFACE_SAS2 (0x04) +#define MPT2_IOCTL_INTERFACE_SAS2_SSS6200 (0x05) #define MPT3_IOCTL_INTERFACE_SAS3 (0x06) #define MPT2_IOCTL_VERSION_LENGTH (32) diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_debug.h b/kernel/drivers/scsi/mpt3sas/mpt3sas_debug.h index 4e8a63fdb..cceeb2c16 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_debug.h +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_debug.h @@ -68,20 +68,11 @@ #define MPT_DEBUG_TRIGGER_DIAG 0x00200000 -/* - * CONFIG_SCSI_MPT3SAS_LOGGING - enabled in Kconfig - */ - -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING #define MPT_CHECK_LOGGING(IOC, CMD, BITS) \ { \ if (IOC->logging_level & BITS) \ CMD; \ } -#else -#define MPT_CHECK_LOGGING(IOC, CMD, BITS) -#endif /* CONFIG_SCSI_MPT3SAS_LOGGING */ - /* * debug macros @@ -153,7 +144,7 @@ /* inline functions for dumping debug data*/ -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING + /** * _debug_dump_mf - print message frame contents * @mpi_request: pointer to message frame @@ -211,10 +202,5 @@ _debug_dump_config(void *mpi_request, int sz) } pr_info("\n"); } -#else -#define _debug_dump_mf(mpi_request, sz) -#define _debug_dump_reply(mpi_request, sz) -#define _debug_dump_config(mpi_request, sz) -#endif /* CONFIG_SCSI_MPT3SAS_LOGGING */ #endif /* MPT3SAS_DEBUG_H_INCLUDED */ diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 5a97e3286..9ab77b064 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -54,14 +54,10 @@ #include #include #include +#include #include "mpt3sas_base.h" -MODULE_AUTHOR(MPT3SAS_AUTHOR); -MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION); -MODULE_LICENSE("GPL"); -MODULE_VERSION(MPT3SAS_DRIVER_VERSION); - #define RAID_CHANNEL 1 /* forward proto's */ static void _scsih_expander_node_remove(struct MPT3SAS_ADAPTER *ioc, @@ -75,11 +71,16 @@ static int _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, static u8 _scsih_check_for_pending_tm(struct MPT3SAS_ADAPTER *ioc, u16 smid); -static void _scsih_scan_start(struct Scsi_Host *shost); -static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time); - /* global parameters */ LIST_HEAD(mpt3sas_ioc_list); +/* global ioc lock for list operations */ +DEFINE_SPINLOCK(gioc_lock); + +MODULE_AUTHOR(MPT3SAS_AUTHOR); +MODULE_DESCRIPTION(MPT3SAS_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MPT3SAS_DRIVER_VERSION); +MODULE_ALIAS("mpt2sas"); /* local parameters */ static u8 scsi_io_cb_idx = -1; @@ -90,7 +91,8 @@ static u8 port_enable_cb_idx = -1; static u8 transport_cb_idx = -1; static u8 scsih_cb_idx = -1; static u8 config_cb_idx = -1; -static int mpt_ids; +static int mpt2_ids; +static int mpt3_ids; static u8 tm_tr_cb_idx = -1 ; static u8 tm_tr_volume_cb_idx = -1 ; @@ -117,8 +119,12 @@ static u64 max_lun = MPT3SAS_MAX_LUN; module_param(max_lun, ullong, 0); MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); - - +static ushort hbas_to_enumerate; +module_param(hbas_to_enumerate, ushort, 0); +MODULE_PARM_DESC(hbas_to_enumerate, + " 0 - enumerates both SAS 2.0 & SAS 3.0 generation HBAs\n \ + 1 - enumerates only SAS 2.0 generation HBAs\n \ + 2 - enumerates only SAS 3.0 generation HBAs (default=0)"); /* diag_buffer_enable is bitwise * bit 0 set = TRACE @@ -143,8 +149,8 @@ MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); /* raid transport support */ - -static struct raid_template *mpt3sas_raid_template; +struct raid_template *mpt3sas_raid_template; +struct raid_template *mpt2sas_raid_template; /** @@ -191,11 +197,36 @@ struct fw_event_work { u8 VP_ID; u8 ignore; u16 event; + struct kref refcount; char event_data[0] __aligned(4); }; -/* raid transport support */ -static struct raid_template *mpt3sas_raid_template; +static void fw_event_work_free(struct kref *r) +{ + kfree(container_of(r, struct fw_event_work, refcount)); +} + +static void fw_event_work_get(struct fw_event_work *fw_work) +{ + kref_get(&fw_work->refcount); +} + +static void fw_event_work_put(struct fw_event_work *fw_work) +{ + kref_put(&fw_work->refcount, fw_event_work_free); +} + +static struct fw_event_work *alloc_fw_event_work(int len) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC); + if (!fw_event) + return NULL; + + kref_init(&fw_event->refcount); + return fw_event; +} /** * struct _scsi_io_transfer - scsi io transfer @@ -245,28 +276,6 @@ struct _scsi_io_transfer { u32 transfer_length; }; -/* - * The pci device ids are defined in mpi/mpi2_cnfg.h. - */ -static const struct pci_device_id scsih_pci_table[] = { - /* Fury ~ 3004 and 3008 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008, - PCI_ANY_ID, PCI_ANY_ID }, - /* Invader ~ 3108 */ - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5, - PCI_ANY_ID, PCI_ANY_ID }, - { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6, - PCI_ANY_ID, PCI_ANY_ID }, - {0} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(pci, scsih_pci_table); - /** * _scsih_set_debug_level - global setting of ioc->logging_level. * @@ -282,8 +291,10 @@ _scsih_set_debug_level(const char *val, struct kernel_param *kp) return ret; pr_info("setting logging_level(0x%08x)\n", logging_level); + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt3sas_ioc_list, list) ioc->logging_level = logging_level; + spin_unlock(&gioc_lock); return 0; } module_param_call(logging_level, _scsih_set_debug_level, param_get_int, @@ -518,8 +529,61 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, } } +static struct _sas_device * +__mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, + struct MPT3SAS_TARGET *tgt_priv) +{ + struct _sas_device *ret; + + assert_spin_locked(&ioc->sas_device_lock); + + ret = tgt_priv->sdev; + if (ret) + sas_device_get(ret); + + return ret; +} + +static struct _sas_device * +mpt3sas_get_sdev_from_target(struct MPT3SAS_ADAPTER *ioc, + struct MPT3SAS_TARGET *tgt_priv) +{ + struct _sas_device *ret; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + ret = __mpt3sas_get_sdev_from_target(ioc, tgt_priv); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return ret; +} + + +struct _sas_device * +__mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, + u64 sas_address) +{ + struct _sas_device *sas_device; + + assert_spin_locked(&ioc->sas_device_lock); + + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->sas_address == sas_address) + goto found_device; + + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->sas_address == sas_address) + goto found_device; + + return NULL; + +found_device: + sas_device_get(sas_device); + return sas_device; +} + /** - * mpt3sas_scsih_sas_device_find_by_sas_address - sas device search + * mpt3sas_get_sdev_by_addr - sas device search * @ioc: per adapter object * @sas_address: sas address * Context: Calling function should acquire ioc->sas_device_lock @@ -528,24 +592,44 @@ _scsih_determine_boot_device(struct MPT3SAS_ADAPTER *ioc, * object. */ struct _sas_device * -mpt3sas_scsih_sas_device_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, +mpt3sas_get_sdev_by_addr(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) +{ + struct _sas_device *sas_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = __mpt3sas_get_sdev_by_addr(ioc, + sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return sas_device; +} + +static struct _sas_device * +__mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct _sas_device *sas_device; + assert_spin_locked(&ioc->sas_device_lock); + list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; + if (sas_device->handle == handle) + goto found_device; list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->sas_address == sas_address) - return sas_device; + if (sas_device->handle == handle) + goto found_device; return NULL; + +found_device: + sas_device_get(sas_device); + return sas_device; } /** - * _scsih_sas_device_find_by_handle - sas device search + * mpt3sas_get_sdev_by_handle - sas device search * @ioc: per adapter object * @handle: sas device handle (assigned by firmware) * Context: Calling function should acquire ioc->sas_device_lock @@ -554,19 +638,16 @@ mpt3sas_scsih_sas_device_find_by_sas_address(struct MPT3SAS_ADAPTER *ioc, * object. */ static struct _sas_device * -_scsih_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +mpt3sas_get_sdev_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct _sas_device *sas_device; + unsigned long flags; - list_for_each_entry(sas_device, &ioc->sas_device_list, list) - if (sas_device->handle == handle) - return sas_device; - - list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) - if (sas_device->handle == handle) - return sas_device; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return NULL; + return sas_device; } /** @@ -575,7 +656,7 @@ _scsih_sas_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) * @sas_device: the sas_device object * Context: This function will acquire ioc->sas_device_lock. * - * Removing object and freeing associated memory from the ioc->sas_device_list. + * If sas_device is on the list, remove it and decrement its reference count. */ static void _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, @@ -585,10 +666,32 @@ _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc, if (!sas_device) return; + pr_info(MPT3SAS_FMT + "removing handle(0x%04x), sas_addr(0x%016llx)\n", + ioc->name, sas_device->handle, + (unsigned long long) sas_device->sas_address); + + if (sas_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot); + + if (sas_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing enclosure level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name); + /* + * The lock serializes access to the list, but we still need to verify + * that nobody removed the entry while we were waiting on the lock. + */ spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_del(&sas_device->list); - kfree(sas_device); + if (!list_empty(&sas_device->list)) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -609,12 +712,16 @@ _scsih_device_remove_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (sas_device) - list_del(&sas_device->list); + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { _scsih_remove_device(ioc, sas_device); + sas_device_put(sas_device); + } } /** @@ -635,13 +742,16 @@ mpt3sas_device_remove_by_sas_address(struct MPT3SAS_ADAPTER *ioc, return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - if (sas_device) - list_del(&sas_device->list); + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address); + if (sas_device) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { _scsih_remove_device(ioc, sas_device); + sas_device_put(sas_device); + } } /** @@ -663,7 +773,20 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot)); + + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, + sas_device->enclosure_level, sas_device->connector_name)); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device_get(sas_device); list_add_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -704,7 +827,20 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc, __func__, sas_device->handle, (unsigned long long)sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure logical id(0x%016llx), slot( %d)\n", + ioc->name, __func__, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot)); + + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, sas_device->enclosure_level, + sas_device->connector_name)); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device_get(sas_device); list_add_tail(&sas_device->list, &ioc->sas_device_init_list); _scsih_determine_boot_device(ioc, sas_device, 0); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -738,7 +874,7 @@ _scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel) } /** - * _scsih_raid_device_find_by_handle - raid device search + * mpt3sas_raid_device_find_by_handle - raid device search * @ioc: per adapter object * @handle: sas device handle (assigned by firmware) * Context: Calling function should acquire ioc->raid_device_lock @@ -746,8 +882,8 @@ _scsih_raid_device_find_by_id(struct MPT3SAS_ADAPTER *ioc, int id, int channel) * This searches for raid_device based on handle, then return raid_device * object. */ -static struct _raid_device * -_scsih_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +struct _raid_device * +mpt3sas_raid_device_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct _raid_device *raid_device, *r; @@ -1055,14 +1191,14 @@ _scsih_scsi_lookup_find_by_lun(struct MPT3SAS_ADAPTER *ioc, int id, } /** - * _scsih_change_queue_depth - setting device queue depth + * scsih_change_queue_depth - setting device queue depth * @sdev: scsi device struct * @qdepth: requested queue depth * * Returns queue depth. */ -static int -_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) +int +scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) { struct Scsi_Host *shost = sdev->host; int max_depth; @@ -1083,12 +1219,15 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) goto not_sata; if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) goto not_sata; + spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - sas_device_priv_data->sas_target->sas_address); - if (sas_device && sas_device->device_info & - MPI2_SAS_DEVICE_INFO_SATA_DEVICE) - max_depth = MPT3SAS_SATA_QUEUE_DEPTH; + sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); + if (sas_device) { + if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + max_depth = MPT3SAS_SATA_QUEUE_DEPTH; + + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); not_sata: @@ -1101,14 +1240,14 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) } /** - * _scsih_target_alloc - target add routine + * scsih_target_alloc - target add routine * @starget: scsi target struct * * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -static int -_scsih_target_alloc(struct scsi_target *starget) +int +scsih_target_alloc(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -1136,7 +1275,9 @@ _scsih_target_alloc(struct scsi_target *starget) sas_target_priv_data->handle = raid_device->handle; sas_target_priv_data->sas_address = raid_device->wwid; sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; - raid_device->starget = starget; + sas_target_priv_data->raid_device = raid_device; + if (ioc->is_warpdrive) + raid_device->starget = starget; } spin_unlock_irqrestore(&ioc->raid_device_lock, flags); return 0; @@ -1145,12 +1286,13 @@ _scsih_target_alloc(struct scsi_target *starget) /* sas/sata devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); if (sas_device) { sas_target_priv_data->handle = sas_device->handle; sas_target_priv_data->sas_address = sas_device->sas_address; + sas_target_priv_data->sdev = sas_device; sas_device->starget = starget; sas_device->id = starget->id; sas_device->channel = starget->channel; @@ -1166,13 +1308,13 @@ _scsih_target_alloc(struct scsi_target *starget) } /** - * _scsih_target_destroy - target destroy routine + * scsih_target_destroy - target destroy routine * @starget: scsi target struct * * Returns nothing. */ -static void -_scsih_target_destroy(struct scsi_target *starget) +void +scsih_target_destroy(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(&starget->dev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -1200,13 +1342,21 @@ _scsih_target_destroy(struct scsi_target *starget) spin_lock_irqsave(&ioc->sas_device_lock, flags); rphy = dev_to_rphy(starget->dev.parent); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - rphy->identify.sas_address); + sas_device = __mpt3sas_get_sdev_from_target(ioc, sas_target_priv_data); if (sas_device && (sas_device->starget == starget) && (sas_device->id == starget->id) && (sas_device->channel == starget->channel)) sas_device->starget = NULL; + if (sas_device) { + /* + * Corresponding get() is in _scsih_target_alloc() + */ + sas_target_priv_data->sdev = NULL; + sas_device_put(sas_device); + + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); out: @@ -1215,14 +1365,14 @@ _scsih_target_destroy(struct scsi_target *starget) } /** - * _scsih_slave_alloc - device add routine + * scsih_slave_alloc - device add routine * @sdev: scsi device struct * * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -static int -_scsih_slave_alloc(struct scsi_device *sdev) +int +scsih_slave_alloc(struct scsi_device *sdev) { struct Scsi_Host *shost; struct MPT3SAS_ADAPTER *ioc; @@ -1262,14 +1412,18 @@ _scsih_slave_alloc(struct scsi_device *sdev) if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_target_priv_data->sas_address); if (sas_device && (sas_device->starget == NULL)) { sdev_printk(KERN_INFO, sdev, "%s : sas_device->starget set to starget @ %d\n", - __func__, __LINE__); + __func__, __LINE__); sas_device->starget = starget; } + + if (sas_device) + sas_device_put(sas_device); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -1277,13 +1431,13 @@ _scsih_slave_alloc(struct scsi_device *sdev) } /** - * _scsih_slave_destroy - device destroy routine + * scsih_slave_destroy - device destroy routine * @sdev: scsi device struct * * Returns nothing. */ -static void -_scsih_slave_destroy(struct scsi_device *sdev) +void +scsih_slave_destroy(struct scsi_device *sdev) { struct MPT3SAS_TARGET *sas_target_priv_data; struct scsi_target *starget; @@ -1304,10 +1458,13 @@ _scsih_slave_destroy(struct scsi_device *sdev) if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - sas_target_priv_data->sas_address); + sas_device = __mpt3sas_get_sdev_from_target(ioc, + sas_target_priv_data); if (sas_device && !sas_target_priv_data->num_luns) sas_device->starget = NULL; + + if (sas_device) + sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -1369,23 +1526,26 @@ _scsih_display_sata_capabilities(struct MPT3SAS_ADAPTER *ioc, */ /** - * _scsih_is_raid - return boolean indicating device is raid volume + * scsih_is_raid - return boolean indicating device is raid volume * @dev the device struct object */ -static int -_scsih_is_raid(struct device *dev) +int +scsih_is_raid(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); + struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); + if (ioc->is_warpdrive) + return 0; return (sdev->channel == RAID_CHANNEL) ? 1 : 0; } /** - * _scsih_get_resync - get raid volume resync percent complete + * scsih_get_resync - get raid volume resync percent complete * @dev the device struct object */ -static void -_scsih_get_resync(struct device *dev) +void +scsih_get_resync(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); @@ -1399,6 +1559,9 @@ _scsih_get_resync(struct device *dev) percent_complete = 0; handle = 0; + if (ioc->is_warpdrive) + goto out; + spin_lock_irqsave(&ioc->raid_device_lock, flags); raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, sdev->channel); @@ -1426,15 +1589,18 @@ _scsih_get_resync(struct device *dev) percent_complete = 0; out: - raid_set_resync(mpt3sas_raid_template, dev, percent_complete); + if (ioc->hba_mpi_version_belonged == MPI2_VERSION) + raid_set_resync(mpt2sas_raid_template, dev, percent_complete); + if (ioc->hba_mpi_version_belonged == MPI25_VERSION) + raid_set_resync(mpt3sas_raid_template, dev, percent_complete); } /** - * _scsih_get_state - get raid volume level + * scsih_get_state - get raid volume level * @dev the device struct object */ -static void -_scsih_get_state(struct device *dev) +void +scsih_get_state(struct device *dev) { struct scsi_device *sdev = to_scsi_device(dev); struct MPT3SAS_ADAPTER *ioc = shost_priv(sdev->host); @@ -1484,7 +1650,10 @@ _scsih_get_state(struct device *dev) break; } out: - raid_set_state(mpt3sas_raid_template, dev, state); + if (ioc->hba_mpi_version_belonged == MPI2_VERSION) + raid_set_state(mpt2sas_raid_template, dev, state); + if (ioc->hba_mpi_version_belonged == MPI25_VERSION) + raid_set_state(mpt3sas_raid_template, dev, state); } /** @@ -1493,7 +1662,8 @@ _scsih_get_state(struct device *dev) * @volume_type: volume type */ static void -_scsih_set_level(struct scsi_device *sdev, u8 volume_type) +_scsih_set_level(struct MPT3SAS_ADAPTER *ioc, + struct scsi_device *sdev, u8 volume_type) { enum raid_level level = RAID_LEVEL_UNKNOWN; @@ -1512,7 +1682,12 @@ _scsih_set_level(struct scsi_device *sdev, u8 volume_type) break; } - raid_set_level(mpt3sas_raid_template, &sdev->sdev_gendev, level); + if (ioc->hba_mpi_version_belonged == MPI2_VERSION) + raid_set_level(mpt2sas_raid_template, + &sdev->sdev_gendev, level); + if (ioc->hba_mpi_version_belonged == MPI25_VERSION) + raid_set_level(mpt3sas_raid_template, + &sdev->sdev_gendev, level); } @@ -1582,8 +1757,6 @@ _scsih_get_volume_capabilities(struct MPT3SAS_ADAPTER *ioc, return 0; } - - /** * _scsih_enable_tlr - setting TLR flags * @ioc: per adapter object @@ -1612,14 +1785,14 @@ _scsih_enable_tlr(struct MPT3SAS_ADAPTER *ioc, struct scsi_device *sdev) } /** - * _scsih_slave_configure - device configure routine. + * scsih_slave_configure - device configure routine. * @sdev: scsi device struct * * Returns 0 if ok. Any other return is assumed to be an error and * the device is ignored. */ -static int -_scsih_slave_configure(struct scsi_device *sdev) +int +scsih_slave_configure(struct scsi_device *sdev) { struct Scsi_Host *shost = sdev->host; struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -1646,7 +1819,7 @@ _scsih_slave_configure(struct scsi_device *sdev) if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->raid_device_lock, flags); if (!raid_device) { dfailprintk(ioc, pr_warn(MPT3SAS_FMT @@ -1662,6 +1835,10 @@ _scsih_slave_configure(struct scsi_device *sdev) return 1; } + /* + * WARPDRIVE: Initialize the required data for Direct IO + */ + mpt3sas_init_warpdrive_properties(ioc, raid_device); /* RAID Queue Depth Support * IS volume = underlying qdepth of drive type, either @@ -1710,17 +1887,19 @@ _scsih_slave_configure(struct scsi_device *sdev) break; } - sdev_printk(KERN_INFO, sdev, - "%s: handle(0x%04x), wwid(0x%016llx), pd_count(%d), type(%s)\n", - r_level, raid_device->handle, - (unsigned long long)raid_device->wwid, - raid_device->num_pds, ds); - + if (!ioc->hide_ir_msg) + sdev_printk(KERN_INFO, sdev, + "%s: handle(0x%04x), wwid(0x%016llx)," + " pd_count(%d), type(%s)\n", + r_level, raid_device->handle, + (unsigned long long)raid_device->wwid, + raid_device->num_pds, ds); - _scsih_change_queue_depth(sdev, qdepth); + scsih_change_queue_depth(sdev, qdepth); -/* raid transport support */ - _scsih_set_level(sdev, raid_device->volume_type); + /* raid transport support */ + if (!ioc->is_warpdrive) + _scsih_set_level(ioc, sdev, raid_device->volume_type); return 0; } @@ -1743,7 +1922,7 @@ _scsih_slave_configure(struct scsi_device *sdev) } spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_device_priv_data->sas_target->sas_address); if (!sas_device) { spin_unlock_irqrestore(&ioc->sas_device_lock, flags); @@ -1772,18 +1951,25 @@ _scsih_slave_configure(struct scsi_device *sdev) "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n", ds, handle, (unsigned long long)sas_device->sas_address, sas_device->phy, (unsigned long long)sas_device->device_name); - sdev_printk(KERN_INFO, sdev, - "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", - ds, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->enclosure_handle != 0) + sdev_printk(KERN_INFO, sdev, + "%s: enclosure_logical_id(0x%016llx), slot(%d)\n", + ds, (unsigned long long) + sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->connector_name[0] != '\0') + sdev_printk(KERN_INFO, sdev, + "%s: enclosure level(0x%04x), connector name( %s)\n", + ds, sas_device->enclosure_level, + sas_device->connector_name); + sas_device_put(sas_device); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (!ssp_target) _scsih_display_sata_capabilities(ioc, handle, sdev); - _scsih_change_queue_depth(sdev, qdepth); + scsih_change_queue_depth(sdev, qdepth); if (ssp_target) { sas_read_port_mode_page(sdev); @@ -1794,7 +1980,7 @@ _scsih_slave_configure(struct scsi_device *sdev) } /** - * _scsih_bios_param - fetch head, sector, cylinder info for a disk + * scsih_bios_param - fetch head, sector, cylinder info for a disk * @sdev: scsi device struct * @bdev: pointer to block device context * @capacity: device size (in 512 byte sectors) @@ -1805,8 +1991,8 @@ _scsih_slave_configure(struct scsi_device *sdev) * * Return nothing. */ -static int -_scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, +int +scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int params[]) { int heads; @@ -2163,7 +2349,10 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) if (!priv_target) return; - device_str = "volume"; + if (ioc->hide_ir_msg) + device_str = "WarpDrive"; + else + device_str = "volume"; scsi_print_command(scmd); if (priv_target->flags & MPT_TARGET_FLAGS_VOLUME) { @@ -2173,8 +2362,7 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) device_str, (unsigned long long)priv_target->sas_address); } else { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); + sas_device = __mpt3sas_get_sdev_from_target(ioc, priv_target); if (sas_device) { if (priv_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { @@ -2189,23 +2377,32 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd) sas_device->handle, (unsigned long long)sas_device->sas_address, sas_device->phy); - starget_printk(KERN_INFO, starget, - "enclosure_logical_id(0x%016llx), slot(%d)\n", - (unsigned long long)sas_device->enclosure_logical_id, - sas_device->slot); + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure_logical_id(0x%016llx), slot(%d)\n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name) + starget_printk(KERN_INFO, starget, + "enclosure level(0x%04x),connector name(%s)\n", + sas_device->enclosure_level, + sas_device->connector_name); + + sas_device_put(sas_device); } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } } /** - * _scsih_abort - eh threads main abort routine + * scsih_abort - eh threads main abort routine * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ -static int -_scsih_abort(struct scsi_cmnd *scmd) +int +scsih_abort(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT3SAS_DEVICE *sas_device_priv_data; @@ -2258,21 +2455,23 @@ _scsih_abort(struct scsi_cmnd *scmd) } /** - * _scsih_dev_reset - eh threads main device reset routine + * scsih_dev_reset - eh threads main device reset routine * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ -static int -_scsih_dev_reset(struct scsi_cmnd *scmd) +int +scsih_dev_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT3SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; + struct _sas_device *sas_device = NULL; u16 handle; int r; + struct scsi_target *starget = scmd->device->sdev_target; + struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; + sdev_printk(KERN_INFO, scmd->device, "attempting device reset! scmd(%p)\n", scmd); _scsih_tm_display_info(ioc, scmd); @@ -2291,12 +2490,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) handle = 0; if (sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); + sas_device = mpt3sas_get_sdev_from_target(ioc, + target_priv_data); if (sas_device) handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } else handle = sas_device_priv_data->sas_target->handle; @@ -2313,25 +2510,29 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) out: sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + if (sas_device) + sas_device_put(sas_device); + return r; } /** - * _scsih_target_reset - eh threads main target reset routine + * scsih_target_reset - eh threads main target reset routine * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ -static int -_scsih_target_reset(struct scsi_cmnd *scmd) +int +scsih_target_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT3SAS_DEVICE *sas_device_priv_data; - struct _sas_device *sas_device; - unsigned long flags; + struct _sas_device *sas_device = NULL; u16 handle; int r; struct scsi_target *starget = scmd->device->sdev_target; + struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; starget_printk(KERN_INFO, starget, "attempting target reset! scmd(%p)\n", scmd); @@ -2351,12 +2552,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd) handle = 0; if (sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, - sas_device_priv_data->sas_target->handle); + sas_device = mpt3sas_get_sdev_from_target(ioc, + target_priv_data); if (sas_device) handle = sas_device->volume_handle; - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } else handle = sas_device_priv_data->sas_target->handle; @@ -2373,18 +2572,22 @@ _scsih_target_reset(struct scsi_cmnd *scmd) out: starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + if (sas_device) + sas_device_put(sas_device); + return r; } /** - * _scsih_host_reset - eh threads main host reset routine + * scsih_host_reset - eh threads main host reset routine * @scmd: pointer to scsi command object * * Returns SUCCESS if command aborted else FAILED */ -static int -_scsih_host_reset(struct scsi_cmnd *scmd) +int +scsih_host_reset(struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); int r, retval; @@ -2430,32 +2633,36 @@ _scsih_fw_event_add(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) return; spin_lock_irqsave(&ioc->fw_event_lock, flags); + fw_event_work_get(fw_event); INIT_LIST_HEAD(&fw_event->list); list_add_tail(&fw_event->list, &ioc->fw_event_list); INIT_WORK(&fw_event->work, _firmware_event_work); + fw_event_work_get(fw_event); queue_work(ioc->firmware_event_thread, &fw_event->work); spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } /** - * _scsih_fw_event_free - delete fw_event + * _scsih_fw_event_del_from_list - delete fw_event from the list * @ioc: per adapter object * @fw_event: object describing the event * Context: This function will acquire ioc->fw_event_lock. * - * This removes firmware event object from link list, frees associated memory. + * If the fw_event is on the fw_event_list, remove it and do a put. * * Return nothing. */ static void -_scsih_fw_event_free(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work +_scsih_fw_event_del_from_list(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { unsigned long flags; spin_lock_irqsave(&ioc->fw_event_lock, flags); - list_del(&fw_event->list); - kfree(fw_event); + if (!list_empty(&fw_event->list)) { + list_del_init(&fw_event->list); + fw_event_work_put(fw_event); + } spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } @@ -2472,17 +2679,19 @@ mpt3sas_send_trigger_data_event(struct MPT3SAS_ADAPTER *ioc, struct SL_WH_TRIGGERS_EVENT_DATA_T *event_data) { struct fw_event_work *fw_event; + u16 sz; if (ioc->is_driver_loading) return; - fw_event = kzalloc(sizeof(*fw_event) + sizeof(*event_data), - GFP_ATOMIC); + sz = sizeof(*event_data); + fw_event = alloc_fw_event_work(sz); if (!fw_event) return; fw_event->event = MPT3SAS_PROCESS_TRIGGER_DIAG; fw_event->ioc = ioc; memcpy(fw_event->event_data, event_data, sizeof(*event_data)); _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); } /** @@ -2498,12 +2707,13 @@ _scsih_error_recovery_delete_devices(struct MPT3SAS_ADAPTER *ioc) if (ioc->is_driver_loading) return; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT3SAS_REMOVE_UNRESPONDING_DEVICES; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); } /** @@ -2517,12 +2727,29 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc) { struct fw_event_work *fw_event; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT3SAS_PORT_ENABLE_COMPLETE; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); +} + +static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc) +{ + unsigned long flags; + struct fw_event_work *fw_event = NULL; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + if (!list_empty(&ioc->fw_event_list)) { + fw_event = list_first_entry(&ioc->fw_event_list, + struct fw_event_work, list); + list_del_init(&fw_event->list); + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + + return fw_event; } /** @@ -2537,48 +2764,124 @@ mpt3sas_port_enable_complete(struct MPT3SAS_ADAPTER *ioc) static void _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc) { - struct fw_event_work *fw_event, *next; + struct fw_event_work *fw_event; if (list_empty(&ioc->fw_event_list) || !ioc->firmware_event_thread || in_interrupt()) return; - list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { - if (cancel_delayed_work_sync(&fw_event->delayed_work)) { - _scsih_fw_event_free(ioc, fw_event); - continue; - } + while ((fw_event = dequeue_next_fw_event(ioc))) { + /* + * Wait on the fw_event to complete. If this returns 1, then + * the event was never executed, and we need a put for the + * reference the delayed_work had on the fw_event. + * + * If it did execute, we wait for it to finish, and the put will + * happen from _firmware_event_work() + */ + if (cancel_delayed_work_sync(&fw_event->delayed_work)) + fw_event_work_put(fw_event); + + fw_event_work_put(fw_event); } } /** - * _scsih_ublock_io_all_device - unblock every device - * @ioc: per adapter object + * _scsih_internal_device_block - block the sdev device + * @sdev: per device object + * @sas_device_priv_data : per device driver private data * - * change the device state from block to running + * make sure device is blocked without error, if not + * print an error */ static void -_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) +_scsih_internal_device_block(struct scsi_device *sdev, + struct MPT3SAS_DEVICE *sas_device_priv_data) { - struct MPT3SAS_DEVICE *sas_device_priv_data; - struct scsi_device *sdev; + int r = 0; - shost_for_each_device(sdev, ioc->shost) { - sas_device_priv_data = sdev->hostdata; - if (!sas_device_priv_data) - continue; - if (!sas_device_priv_data->block) - continue; + sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle); + sas_device_priv_data->block = 1; - sas_device_priv_data->block = 0; - dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, - "device_running, handle(0x%04x)\n", - sas_device_priv_data->sas_target->handle)); - scsi_internal_device_unblock(sdev, SDEV_RUNNING); - } + r = scsi_internal_device_block(sdev); + if (r == -EINVAL) + sdev_printk(KERN_WARNING, sdev, + "device_block failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); } - +/** + * _scsih_internal_device_unblock - unblock the sdev device + * @sdev: per device object + * @sas_device_priv_data : per device driver private data + * make sure device is unblocked without error, if not retry + * by blocking and then unblocking + */ + +static void +_scsih_internal_device_unblock(struct scsi_device *sdev, + struct MPT3SAS_DEVICE *sas_device_priv_data) +{ + int r = 0; + + sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, " + "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle); + sas_device_priv_data->block = 0; + r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); + if (r == -EINVAL) { + /* The device has been set to SDEV_RUNNING by SD layer during + * device addition but the request queue is still stopped by + * our earlier block call. We need to perform a block again + * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */ + + sdev_printk(KERN_WARNING, sdev, + "device_unblock failed with return(%d) for handle(0x%04x) " + "performing a block followed by an unblock\n", + sas_device_priv_data->sas_target->handle, r); + sas_device_priv_data->block = 1; + r = scsi_internal_device_block(sdev); + if (r) + sdev_printk(KERN_WARNING, sdev, "retried device_block " + "failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); + + sas_device_priv_data->block = 0; + r = scsi_internal_device_unblock(sdev, SDEV_RUNNING); + if (r) + sdev_printk(KERN_WARNING, sdev, "retried device_unblock" + " failed with return(%d) for handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle, r); + } +} + +/** + * _scsih_ublock_io_all_device - unblock every device + * @ioc: per adapter object + * + * change the device state from block to running + */ +static void +_scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc) +{ + struct MPT3SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (!sas_device_priv_data->block) + continue; + + dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, + "device_running, handle(0x%04x)\n", + sas_device_priv_data->sas_target->handle)); + _scsih_internal_device_unblock(sdev, sas_device_priv_data); + } +} + + /** * _scsih_ublock_io_device - prepare device to be deleted * @ioc: per adapter object @@ -2599,10 +2902,9 @@ _scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address) if (sas_device_priv_data->sas_target->sas_address != sas_address) continue; - if (sas_device_priv_data->block) { - sas_device_priv_data->block = 0; - scsi_internal_device_unblock(sdev, SDEV_RUNNING); - } + if (sas_device_priv_data->block) + _scsih_internal_device_unblock(sdev, + sas_device_priv_data); } } @@ -2625,10 +2927,7 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc) continue; if (sas_device_priv_data->block) continue; - sas_device_priv_data->block = 1; - scsi_internal_device_block(sdev); - sdev_printk(KERN_INFO, sdev, "device_blocked, handle(0x%04x)\n", - sas_device_priv_data->sas_target->handle); + _scsih_internal_device_block(sdev, sas_device_priv_data); } } @@ -2644,6 +2943,11 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct MPT3SAS_DEVICE *sas_device_priv_data; struct scsi_device *sdev; + struct _sas_device *sas_device; + + sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); + if (!sas_device) + return; shost_for_each_device(sdev, ioc->shost) { sas_device_priv_data = sdev->hostdata; @@ -2653,11 +2957,12 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) continue; if (sas_device_priv_data->block) continue; - sas_device_priv_data->block = 1; - scsi_internal_device_block(sdev); - sdev_printk(KERN_INFO, sdev, - "device_blocked, handle(0x%04x)\n", handle); + if (sas_device->pend_sas_rphy_add) + continue; + _scsih_internal_device_block(sdev, sas_device_priv_data); } + + sas_device_put(sas_device); } /** @@ -2686,12 +2991,13 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT3SAS_ADAPTER *ioc, if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = - mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - mpt3sas_port->remote_identify.sas_address); - if (sas_device) + sas_device = __mpt3sas_get_sdev_by_addr(ioc, + mpt3sas_port->remote_identify.sas_address); + if (sas_device) { set_bit(sas_device->handle, - ioc->blocking_handles); + ioc->blocking_handles); + sas_device_put(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } } @@ -2759,7 +3065,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) { Mpi2SCSITaskManagementRequest_t *mpi_request; u16 smid; - struct _sas_device *sas_device; + struct _sas_device *sas_device = NULL; struct MPT3SAS_TARGET *sas_target_priv_data = NULL; u64 sas_address = 0; unsigned long flags; @@ -2792,7 +3098,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); if (sas_device && sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; @@ -2806,6 +3112,18 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, (unsigned long long)sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag:enclosure logical id(0x%016llx)," + " slot(%d)\n", ioc->name, (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "setting delete flag: enclosure level(0x%04x)," + " connector name( %s)\n", ioc->name, + sas_device->enclosure_level, + sas_device->connector_name)); _scsih_ublock_io_device(ioc, sas_address); sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } @@ -2814,14 +3132,14 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) if (!smid) { delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); if (!delayed_tr) - return; + goto out; INIT_LIST_HEAD(&delayed_tr->list); delayed_tr->handle = handle; list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); dewtprintk(ioc, pr_info(MPT3SAS_FMT "DELAYED:tr:handle(0x%04x), (open)\n", ioc->name, handle)); - return; + goto out; } dewtprintk(ioc, pr_info(MPT3SAS_FMT @@ -2835,6 +3153,10 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; mpt3sas_base_put_smid_hi_priority(ioc, smid); mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL); + +out: + if (sas_device) + sas_device_put(sas_device); } /** @@ -3204,7 +3526,7 @@ _scsih_set_volume_delete_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) unsigned long flags; spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); if (raid_device && raid_device->starget && raid_device->starget->hostdata) { sas_target_priv_data = @@ -3265,6 +3587,9 @@ _scsih_check_ir_config_unhide_events(struct MPT3SAS_ADAPTER *ioc, a = 0; b = 0; + if (ioc->is_warpdrive) + return; + /* Volume Resets for Deleted or Removed */ element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; for (i = 0; i < event_data->NumElements; i++, element++) { @@ -3501,8 +3826,9 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) } + /** - * _scsih_qcmd - main scsi request entry point + * scsih_qcmd - main scsi request entry point * @scmd: pointer to scsi command object * @done: function pointer to be invoked on completion * @@ -3512,21 +3838,20 @@ _scsih_eedp_error_handling(struct scsi_cmnd *scmd, u16 ioc_status) * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full */ -static int -_scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) +int +scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); struct MPT3SAS_DEVICE *sas_device_priv_data; struct MPT3SAS_TARGET *sas_target_priv_data; + struct _raid_device *raid_device; Mpi2SCSIIORequest_t *mpi_request; u32 mpi_control; u16 smid; u16 handle; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_SCSI) scsi_print_command(scmd); -#endif sas_device_priv_data = scmd->device->hostdata; if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { @@ -3576,8 +3901,11 @@ _scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) /* set tags */ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && - scmd->cmd_len != 32) + /* Make sure Device is not raid volume. + * We do not expose raid functionality to upper layer for warpdrive. + */ + if (!ioc->is_warpdrive && !scsih_is_raid(&scmd->device->sdev_gendev) + && sas_is_tlr_enabled(scmd->device) && scmd->cmd_len != 32) mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); @@ -3619,13 +3947,19 @@ _scsih_qcmd(struct Scsi_Host *shost, struct scsi_cmnd *scmd) } else ioc->build_zero_len_sge(ioc, &mpi_request->SGL); + raid_device = sas_target_priv_data->raid_device; + if (raid_device && raid_device->direct_io_enabled) + mpt3sas_setup_direct_io(ioc, scmd, raid_device, mpi_request, + smid); + if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) { if (sas_target_priv_data->flags & MPT_TARGET_FASTPATH_IO) { mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len | MPI25_SCSIIO_IOFLAGS_FAST_PATH); mpt3sas_base_put_smid_fast_path(ioc, smid, handle); } else - mpt3sas_base_put_smid_scsi_io(ioc, smid, handle); + mpt3sas_base_put_smid_scsi_io(ioc, smid, + le16_to_cpu(mpi_request->DevHandle)); } else mpt3sas_base_put_smid_default(ioc, smid); return 0; @@ -3657,7 +3991,6 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data) } } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request * @ioc: per adapter object @@ -3685,14 +4018,16 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, char *desc_scsi_state = ioc->tmp_string; u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); struct _sas_device *sas_device = NULL; - unsigned long flags; struct scsi_target *starget = scmd->device->sdev_target; struct MPT3SAS_TARGET *priv_target = starget->hostdata; char *device_str = NULL; if (!priv_target) return; - device_str = "volume"; + if (ioc->hide_ir_msg) + device_str = "WarpDrive"; + else + device_str = "volume"; if (log_info == 0x31170000) return; @@ -3813,20 +4148,28 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, pr_warn(MPT3SAS_FMT "\t%s wwid(0x%016llx)\n", ioc->name, device_str, (unsigned long long)priv_target->sas_address); } else { - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - priv_target->sas_address); + sas_device = mpt3sas_get_sdev_from_target(ioc, priv_target); if (sas_device) { pr_warn(MPT3SAS_FMT "\tsas_address(0x%016llx), phy(%d)\n", ioc->name, (unsigned long long) sas_device->sas_address, sas_device->phy); - pr_warn(MPT3SAS_FMT - "\tenclosure_logical_id(0x%016llx), slot(%d)\n", - ioc->name, (unsigned long long) - sas_device->enclosure_logical_id, sas_device->slot); + if (sas_device->enclosure_handle != 0) + pr_warn(MPT3SAS_FMT + "\tenclosure_logical_id(0x%016llx)," + "slot(%d)\n", ioc->name, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0]) + pr_warn(MPT3SAS_FMT + "\tenclosure level(0x%04x)," + " connector name( %s)\n", ioc->name, + sas_device->enclosure_level, + sas_device->connector_name); + + sas_device_put(sas_device); } - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } pr_warn(MPT3SAS_FMT @@ -3861,7 +4204,6 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, _scsih_response_code(ioc, response_bytes[0]); } } -#endif /** * _scsih_turn_on_pfa_led - illuminate PFA LED @@ -3878,7 +4220,7 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) Mpi2SepRequest_t mpi_request; struct _sas_device *sas_device; - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); if (!sas_device) return; @@ -3893,7 +4235,7 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) &mpi_request)) != 0) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + goto out; } sas_device->pfa_led_on = 1; @@ -3902,9 +4244,12 @@ _scsih_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo))); - return; + goto out; } +out: + sas_device_put(sas_device); } + /** * _scsih_turn_off_pfa_led - turn off Fault LED * @ioc: per adapter object @@ -3943,6 +4288,7 @@ _scsih_turn_off_pfa_led(struct MPT3SAS_ADAPTER *ioc, return; } } + /** * _scsih_send_event_to_turn_on_pfa_led - fire delayed event * @ioc: per adapter object @@ -3956,13 +4302,14 @@ _scsih_send_event_to_turn_on_pfa_led(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct fw_event_work *fw_event; - fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + fw_event = alloc_fw_event_work(0); if (!fw_event) return; fw_event->event = MPT3SAS_TURN_ON_PFA_LED; fw_event->device_handle = handle; fw_event->ioc = ioc; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); } /** @@ -3986,20 +4333,27 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) /* only handle non-raid devices */ spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); + if (!sas_device) + goto out_unlock; + starget = sas_device->starget; sas_target_priv_data = starget->hostdata; if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || - ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } - starget_printk(KERN_WARNING, starget, "predicted fault\n"); + ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) + goto out_unlock; + + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, "predicted fault, " + "enclosure logical id(0x%016llx), slot(%d)\n", + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + starget_printk(KERN_WARNING, starget, "predicted fault, " + "enclosure level(0x%04x), connector name( %s)\n", + sas_device->enclosure_level, + sas_device->connector_name); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) @@ -4012,7 +4366,7 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) if (!event_reply) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - return; + goto out; } event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; @@ -4029,6 +4383,14 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle) event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); mpt3sas_ctl_add_to_event_log(ioc, event_reply); kfree(event_reply); +out: + if (sas_device) + sas_device_put(sas_device); + return; + +out_unlock: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + goto out; } /** @@ -4056,6 +4418,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) u32 log_info; struct MPT3SAS_DEVICE *sas_device_priv_data; u32 response_code = 0; + unsigned long flags; mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); scmd = _scsih_scsi_lookup_get_clear(ioc, smid); @@ -4077,6 +4440,24 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } ioc_status = le16_to_cpu(mpi_reply->IOCStatus); + /* + * WARPDRIVE: If direct_io is set then it is directIO, + * the failed direct I/O should be redirected to volume + */ + if (mpt3sas_scsi_direct_io_get(ioc, smid) && + ((ioc_status & MPI2_IOCSTATUS_MASK) + != MPI2_IOCSTATUS_SCSI_TASK_TERMINATED)) { + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + ioc->scsi_lookup[smid - 1].scmd = scmd; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + mpt3sas_scsi_direct_io_set(ioc, smid, 0); + memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); + mpi_request->DevHandle = + cpu_to_le16(sas_device_priv_data->sas_target->handle); + mpt3sas_base_put_smid_scsi_io(ioc, smid, + sas_device_priv_data->sas_target->handle); + return 0; + } /* turning off TLR */ scsi_state = mpi_reply->SCSIState; if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) @@ -4084,10 +4465,13 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF; if (!sas_device_priv_data->tlr_snoop_check) { sas_device_priv_data->tlr_snoop_check++; - if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && - response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) - sas_device_priv_data->flags &= - ~MPT_DEVICE_TLR_ON; + if (!ioc->is_warpdrive && + !scsih_is_raid(&scmd->device->sdev_gendev) && + sas_is_tlr_enabled(scmd->device) && + response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { + sas_disable_tlr(scmd->device); + sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); + } } xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); @@ -4119,8 +4503,13 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) _scsih_smart_predicted_fault(ioc, le16_to_cpu(mpi_reply->DevHandle)); mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq); - } + if (!(ioc->logging_level & MPT_DEBUG_REPLY) && + ((scmd->sense_buffer[2] == UNIT_ATTENTION) || + (scmd->sense_buffer[2] == MEDIUM_ERROR) || + (scmd->sense_buffer[2] == HARDWARE_ERROR))) + _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid); + } switch (ioc_status) { case MPI2_IOCSTATUS_BUSY: case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: @@ -4146,6 +4535,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) scmd->device->expecting_cc_ua = 1; } break; + } else if (log_info == VIRTUAL_IO_FAILED_RETRY) { + scmd->result = DID_RESET << 16; + break; } scmd->result = DID_SOFT_ERROR << 16; break; @@ -4223,10 +4615,8 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY)) _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid); -#endif out: @@ -4772,13 +5162,11 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(sas_device_pg0.SASAddress); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address); - if (!sas_device) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + if (!sas_device) + goto out_unlock; if (unlikely(sas_device->handle != handle)) { starget = sas_device->starget; @@ -4788,6 +5176,16 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, sas_device->handle, handle); sas_target_priv_data->handle = handle; sas_device->handle = handle; + if (sas_device_pg0.Flags & + MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0.EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0.ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } } /* check if device is present */ @@ -4796,20 +5194,25 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, pr_err(MPT3SAS_FMT "device is not present handle(0x%04x), flags!!!\n", ioc->name, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; + goto out_unlock; } /* check if there were any issues with discovery */ if (_scsih_check_access_status(ioc, sas_address, handle, - sas_device_pg0.AccessStatus)) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + sas_device_pg0.AccessStatus)) + goto out_unlock; spin_unlock_irqrestore(&ioc->sas_device_lock, flags); _scsih_ublock_io_device(ioc, sas_address); + if (sas_device) + sas_device_put(sas_device); + return; + +out_unlock: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + sas_device_put(sas_device); } /** @@ -4834,7 +5237,6 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u32 ioc_status; u64 sas_address; u32 device_info; - unsigned long flags; if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -4870,13 +5272,12 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, sas_device_pg0.AccessStatus)) return -1; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, - sas_address); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) + sas_device = mpt3sas_get_sdev_by_addr(ioc, + sas_address); + if (sas_device) { + sas_device_put(sas_device); return -1; + } sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); @@ -4886,6 +5287,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, return 0; } + kref_init(&sas_device->refcount); sas_device->handle = handle; if (_scsih_get_sas_address(ioc, le16_to_cpu(sas_device_pg0.ParentDevHandle), @@ -4894,14 +5296,24 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, ioc->name, __FILE__, __LINE__, __func__); sas_device->enclosure_handle = le16_to_cpu(sas_device_pg0.EnclosureHandle); - sas_device->slot = - le16_to_cpu(sas_device_pg0.Slot); + if (sas_device->enclosure_handle != 0) + sas_device->slot = + le16_to_cpu(sas_device_pg0.Slot); sas_device->device_info = device_info; sas_device->sas_address = sas_address; sas_device->phy = sas_device_pg0.PhyNum; sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) & MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0; + if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0.EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0.ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } /* get enclosure_logical_id */ if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0( ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, @@ -4917,6 +5329,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, else _scsih_sas_device_add(ioc, sas_device); + sas_device_put(sas_device); return 0; } @@ -4943,6 +5356,18 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, ioc->name, __func__, sas_device->handle, (unsigned long long) sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: enter: enclosure level(0x%04x), connector name( %s)\n", + ioc->name, __func__, + sas_device->enclosure_level, + sas_device->connector_name)); if (sas_device->starget && sas_device->starget->hostdata) { sas_target_priv_data = sas_device->starget->hostdata; @@ -4951,7 +5376,9 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE; } - mpt3sas_transport_port_remove(ioc, + + if (!ioc->hide_drives) + mpt3sas_transport_port_remove(ioc, sas_device->sas_address, sas_device->sas_address_parent); @@ -4959,17 +5386,36 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc, "removing handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, sas_device->handle, (unsigned long long) sas_device->sas_address); + if (sas_device->enclosure_handle != 0) + pr_info(MPT3SAS_FMT + "removing : enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->connector_name[0] != '\0') + pr_info(MPT3SAS_FMT + "removing enclosure level(0x%04x), connector name( %s)\n", + ioc->name, sas_device->enclosure_level, + sas_device->connector_name); dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, - sas_device->handle, (unsigned long long) - sas_device->sas_address)); - - kfree(sas_device); + sas_device->handle, (unsigned long long) + sas_device->sas_address)); + if (sas_device->enclosure_handle != 0) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n", + ioc->name, __func__, + (unsigned long long)sas_device->enclosure_logical_id, + sas_device->slot)); + if (sas_device->connector_name[0] != '\0') + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: exit: enclosure level(0x%04x), connector name(%s)\n", + ioc->name, __func__, sas_device->enclosure_level, + sas_device->connector_name)); } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_sas_topology_change_event_debug - debug for topology event * @ioc: per adapter object @@ -5047,7 +5493,6 @@ _scsih_sas_topology_change_event_debug(struct MPT3SAS_ADAPTER *ioc, } } -#endif /** * _scsih_sas_topology_change_event - handle topology changes @@ -5072,10 +5517,8 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, (Mpi2EventDataSasTopologyChangeList_t *) fw_event->event_data; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_topology_change_event_debug(ioc, event_data); -#endif if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) return 0; @@ -5181,7 +5624,6 @@ _scsih_sas_topology_change_event(struct MPT3SAS_ADAPTER *ioc, return 0; } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_sas_device_status_change_event_debug - debug for device event * @event_data: event data payload @@ -5249,7 +5691,6 @@ _scsih_sas_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, event_data->ASC, event_data->ASCQ); pr_info("\n"); } -#endif /** * _scsih_sas_device_status_change_event - handle device status change @@ -5271,11 +5712,9 @@ _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, (Mpi2EventDataSasDeviceStatusChange_t *) fw_event->event_data; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_device_status_change_event_debug(ioc, event_data); -#endif /* In MPI Revision K (0xC), the internal device reset complete was * implemented, so avoid setting tm_busy flag for older firmware. @@ -5291,29 +5730,30 @@ _scsih_sas_device_status_change_event(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_address = le64_to_cpu(event_data->SASAddress); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, sas_address); - if (!sas_device || !sas_device->starget) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + if (!sas_device || !sas_device->starget) + goto out; target_priv_data = sas_device->starget->hostdata; - if (!target_priv_data) { - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - return; - } + if (!target_priv_data) + goto out; if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET) target_priv_data->tm_busy = 1; else target_priv_data->tm_busy = 0; + +out: + if (sas_device) + sas_device_put(sas_device); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure * event @@ -5348,7 +5788,6 @@ _scsih_sas_enclosure_dev_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID), le16_to_cpu(event_data->StartSlot)); } -#endif /** * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events @@ -5362,12 +5801,10 @@ static void _scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_enclosure_dev_status_change_event_debug(ioc, (Mpi2EventDataSasEnclDevStatusChange_t *) fw_event->event_data); -#endif } /** @@ -5547,17 +5984,15 @@ _scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc, Mpi2EventDataSasDiscovery_t *event_data = (Mpi2EventDataSasDiscovery_t *) fw_event->event_data; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { pr_info(MPT3SAS_FMT "discovery event: (%s)", ioc->name, (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? "start" : "stop"); - if (event_data->DiscoveryStatus) - pr_info("discovery_status(0x%08x)", - le32_to_cpu(event_data->DiscoveryStatus)); - pr_info("\n"); + if (event_data->DiscoveryStatus) + pr_info("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); + pr_info("\n"); } -#endif if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && !ioc->sas_hba.num_phys) { @@ -5589,6 +6024,8 @@ _scsih_ir_fastpath(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phys_disk_num) u16 ioc_status; u32 log_info; + if (ioc->hba_mpi_version_belonged == MPI2_VERSION) + return rc; mutex_lock(&ioc->scsih_cmds.mutex); @@ -5756,7 +6193,7 @@ _scsih_sas_volume_delete(struct MPT3SAS_ADAPTER *ioc, u16 handle) struct scsi_target *starget = NULL; spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); if (raid_device) { if (raid_device->starget) { starget = raid_device->starget; @@ -5793,7 +6230,7 @@ _scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc, u16 handle = le16_to_cpu(element->PhysDiskDevHandle); spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); if (sas_device) { sas_device->volume_handle = 0; sas_device->volume_wwid = 0; @@ -5812,6 +6249,8 @@ _scsih_sas_pd_expose(struct MPT3SAS_ADAPTER *ioc, /* exposing raid component */ if (starget) starget_for_each_device(starget, NULL, _scsih_reprobe_lun); + + sas_device_put(sas_device); } /** @@ -5840,7 +6279,7 @@ _scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc, &volume_wwid); spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + sas_device = __mpt3sas_get_sdev_by_handle(ioc, handle); if (sas_device) { set_bit(handle, ioc->pd_handles); if (sas_device->starget && sas_device->starget->hostdata) { @@ -5858,8 +6297,11 @@ _scsih_sas_pd_hide(struct MPT3SAS_ADAPTER *ioc, /* hiding raid component */ _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); + if (starget) starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun); + + sas_device_put(sas_device); } /** @@ -5892,7 +6334,6 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, Mpi2EventIrConfigElement_t *element) { struct _sas_device *sas_device; - unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; @@ -5902,11 +6343,10 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, set_bit(handle, ioc->pd_handles); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); if (sas_device) { _scsih_ir_fastpath(ioc, handle, element->PhysDiskNum); + sas_device_put(sas_device); return; } @@ -5934,7 +6374,6 @@ _scsih_sas_pd_add(struct MPT3SAS_ADAPTER *ioc, _scsih_add_device(ioc, handle, 0, 1); } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events * @ioc: per adapter object @@ -6014,7 +6453,6 @@ _scsih_sas_ir_config_change_event_debug(struct MPT3SAS_ADAPTER *ioc, element->PhysDiskNum); } } -#endif /** * _scsih_sas_ir_config_change_event - handle ir configuration change events @@ -6035,18 +6473,16 @@ _scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc, (Mpi2EventDataIrConfigChangeList_t *) fw_event->event_data; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && + (!ioc->hide_ir_msg)) _scsih_sas_ir_config_change_event_debug(ioc, event_data); -#endif - foreign_config = (le32_to_cpu(event_data->Flags) & MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; - if (ioc->shost_recovery) { - + if (ioc->shost_recovery && + ioc->hba_mpi_version_belonged != MPI2_VERSION) { for (i = 0; i < event_data->NumElements; i++, element++) { if (element->ReasonCode == MPI2_EVENT_IR_CHANGE_RC_HIDE) _scsih_ir_fastpath(ioc, @@ -6055,6 +6491,7 @@ _scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc, } return; } + for (i = 0; i < event_data->NumElements; i++, element++) { switch (element->ReasonCode) { @@ -6070,16 +6507,20 @@ _scsih_sas_ir_config_change_event(struct MPT3SAS_ADAPTER *ioc, le16_to_cpu(element->VolDevHandle)); break; case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: - _scsih_sas_pd_hide(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_hide(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: - _scsih_sas_pd_expose(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_expose(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_HIDE: - _scsih_sas_pd_add(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_add(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: - _scsih_sas_pd_delete(ioc, element); + if (!ioc->is_warpdrive) + _scsih_sas_pd_delete(ioc, element); break; } } @@ -6114,10 +6555,11 @@ _scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc, handle = le16_to_cpu(event_data->VolDevHandle); state = le32_to_cpu(event_data->NewValue); - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", - ioc->name, __func__, handle, - le32_to_cpu(event_data->PreviousValue), state)); + if (!ioc->hide_ir_msg) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", + ioc->name, __func__, handle, + le32_to_cpu(event_data->PreviousValue), state)); switch (state) { case MPI2_RAID_VOL_STATE_MISSING: case MPI2_RAID_VOL_STATE_FAILED: @@ -6129,7 +6571,7 @@ _scsih_sas_ir_volume_event(struct MPT3SAS_ADAPTER *ioc, case MPI2_RAID_VOL_STATE_OPTIMAL: spin_lock_irqsave(&ioc->raid_device_lock, flags); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->raid_device_lock, flags); if (raid_device) @@ -6183,7 +6625,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, u16 handle, parent_handle; u32 state; struct _sas_device *sas_device; - unsigned long flags; Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; u32 ioc_status; @@ -6200,10 +6641,12 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, handle = le16_to_cpu(event_data->PhysDiskDevHandle); state = le32_to_cpu(event_data->NewValue); - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", - ioc->name, __func__, handle, + if (!ioc->hide_ir_msg) + dewtprintk(ioc, pr_info(MPT3SAS_FMT + "%s: handle(0x%04x), old(0x%08x), new(0x%08x)\n", + ioc->name, __func__, handle, le32_to_cpu(event_data->PreviousValue), state)); + switch (state) { case MPI2_RAID_PD_STATE_ONLINE: case MPI2_RAID_PD_STATE_DEGRADED: @@ -6211,13 +6654,14 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, case MPI2_RAID_PD_STATE_OPTIMAL: case MPI2_RAID_PD_STATE_HOT_SPARE: - set_bit(handle, ioc->pd_handles); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!ioc->is_warpdrive) + set_bit(handle, ioc->pd_handles); - if (sas_device) + sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + sas_device_put(sas_device); return; + } if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, @@ -6252,7 +6696,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT3SAS_ADAPTER *ioc, } } -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING /** * _scsih_sas_ir_operation_status_event_debug - debug for IR op event * @ioc: per adapter object @@ -6294,7 +6737,6 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT3SAS_ADAPTER *ioc, le16_to_cpu(event_data->VolDevHandle), event_data->PercentComplete); } -#endif /** * _scsih_sas_ir_operation_status_event - handle RAID operation events @@ -6315,18 +6757,17 @@ _scsih_sas_ir_operation_status_event(struct MPT3SAS_ADAPTER *ioc, unsigned long flags; u16 handle; -#ifdef CONFIG_SCSI_MPT3SAS_LOGGING - if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + if ((ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) && + (!ioc->hide_ir_msg)) _scsih_sas_ir_operation_status_event_debug(ioc, event_data); -#endif /* code added for raid transport support */ if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { spin_lock_irqsave(&ioc->raid_device_lock, flags); handle = le16_to_cpu(event_data->VolDevHandle); - raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); if (raid_device) raid_device->percent_complete = event_data->PercentComplete; @@ -6357,9 +6798,7 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) /** * _scsih_mark_responding_sas_device - mark a sas_devices as responding * @ioc: per adapter object - * @sas_address: sas address - * @slot: enclosure slot id - * @handle: device handle + * @sas_device_pg0: SAS Device page 0 * * After host reset, find out whether devices are still responding. * Used in _scsih_remove_unresponsive_sas_devices. @@ -6367,8 +6806,8 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc) * Return nothing. */ static void -_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, - u16 slot, u16 handle) +_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, +Mpi2SasDevicePage0_t *sas_device_pg0) { struct MPT3SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; @@ -6377,8 +6816,8 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address == sas_address && - sas_device->slot == slot) { + if ((sas_device->sas_address == sas_device_pg0->SASAddress) && + (sas_device->slot == sas_device_pg0->Slot)) { sas_device->responding = 1; starget = sas_device->starget; if (starget && starget->hostdata) { @@ -6387,22 +6826,40 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, sas_target_priv_data->deleted = 0; } else sas_target_priv_data = NULL; - if (starget) + if (starget) { starget_printk(KERN_INFO, starget, - "handle(0x%04x), sas_addr(0x%016llx), " - "enclosure logical id(0x%016llx), " - "slot(%d)\n", handle, - (unsigned long long)sas_device->sas_address, + "handle(0x%04x), sas_addr(0x%016llx)\n", + sas_device_pg0->DevHandle, (unsigned long long) - sas_device->enclosure_logical_id, - sas_device->slot); - if (sas_device->handle == handle) + sas_device->sas_address); + + if (sas_device->enclosure_handle != 0) + starget_printk(KERN_INFO, starget, + "enclosure logical id(0x%016llx)," + " slot(%d)\n", + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + } + if (sas_device_pg0->Flags & + MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { + sas_device->enclosure_level = + le16_to_cpu(sas_device_pg0->EnclosureLevel); + memcpy(&sas_device->connector_name[0], + &sas_device_pg0->ConnectorName[0], 4); + } else { + sas_device->enclosure_level = 0; + sas_device->connector_name[0] = '\0'; + } + + if (sas_device->handle == sas_device_pg0->DevHandle) goto out; pr_info("\thandle changed from(0x%04x)!!!\n", sas_device->handle); - sas_device->handle = handle; + sas_device->handle = sas_device_pg0->DevHandle; if (sas_target_priv_data) - sas_target_priv_data->handle = handle; + sas_target_priv_data->handle = + sas_device_pg0->DevHandle; goto out; } } @@ -6441,13 +6898,15 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; - handle = le16_to_cpu(sas_device_pg0.DevHandle); + handle = sas_device_pg0.DevHandle = + le16_to_cpu(sas_device_pg0.DevHandle); device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); if (!(_scsih_is_end_device(device_info))) continue; - _scsih_mark_responding_sas_device(ioc, - le64_to_cpu(sas_device_pg0.SASAddress), - le16_to_cpu(sas_device_pg0.Slot), handle); + sas_device_pg0.SASAddress = + le64_to_cpu(sas_device_pg0.SASAddress); + sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot); + _scsih_mark_responding_sas_device(ioc, &sas_device_pg0); } out: @@ -6470,7 +6929,7 @@ static void _scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid, u16 handle) { - struct MPT3SAS_TARGET *sas_target_priv_data; + struct MPT3SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; struct _raid_device *raid_device; unsigned long flags; @@ -6489,6 +6948,13 @@ _scsih_mark_responding_raid_device(struct MPT3SAS_ADAPTER *ioc, u64 wwid, starget_printk(KERN_INFO, raid_device->starget, "handle(0x%04x), wwid(0x%016llx)\n", handle, (unsigned long long)raid_device->wwid); + + /* + * WARPDRIVE: The handles of the PDs might have changed + * across the host reset so re-initialize the + * required data for Direct IO + */ + mpt3sas_init_warpdrive_properties(ioc, raid_device); spin_lock_irqsave(&ioc->raid_device_lock, flags); if (raid_device->handle == handle) { spin_unlock_irqrestore(&ioc->raid_device_lock, @@ -6558,6 +7024,7 @@ _scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) } /* refresh the pd_handles */ + if (!ioc->is_warpdrive) { phys_disk_num = 0xFF; memset(ioc->pd_handles, 0, ioc->pd_handles_sz); while (!(mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply, @@ -6571,6 +7038,7 @@ _scsih_search_responding_raid_devices(struct MPT3SAS_ADAPTER *ioc) handle = le16_to_cpu(pd_pg0.DevHandle); set_bit(handle, ioc->pd_handles); } + } out: pr_info(MPT3SAS_FMT "search for responding raid volumes: complete\n", ioc->name); @@ -6673,6 +7141,7 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) struct _raid_device *raid_device, *raid_device_next; struct list_head tmp_list; unsigned long flags; + LIST_HEAD(head); pr_info(MPT3SAS_FMT "removing unresponding devices: start\n", ioc->name); @@ -6680,14 +7149,28 @@ _scsih_remove_unresponding_sas_devices(struct MPT3SAS_ADAPTER *ioc) /* removing unresponding end devices */ pr_info(MPT3SAS_FMT "removing unresponding devices: end-devices\n", ioc->name); + /* + * Iterate, pulling off devices marked as non-responding. We become the + * owner for the reference the list had on any object we prune. + */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry_safe(sas_device, sas_device_next, &ioc->sas_device_list, list) { if (!sas_device->responding) - mpt3sas_device_remove_by_sas_address(ioc, - sas_device->sas_address); + list_move_tail(&sas_device->list, &head); else sas_device->responding = 0; } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* + * Now, uninitialize and remove the unresponding devices we pruned. + */ + list_for_each_entry_safe(sas_device, sas_device_next, &head, list) { + _scsih_remove_device(ioc, sas_device); + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } /* removing unresponding volumes */ if (ioc->ir_firmware) { @@ -6841,11 +7324,11 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) } phys_disk_num = pd_pg0.PhysDiskNum; handle = le16_to_cpu(pd_pg0.DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + sas_device = mpt3sas_get_sdev_by_handle(ioc, handle); + if (sas_device) { + sas_device_put(sas_device); continue; + } if (mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle) != 0) @@ -6966,12 +7449,12 @@ _scsih_scan_for_devices_after_reset(struct MPT3SAS_ADAPTER *ioc) if (!(_scsih_is_end_device( le32_to_cpu(sas_device_pg0.DeviceInfo)))) continue; - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = mpt3sas_get_sdev_by_addr(ioc, le64_to_cpu(sas_device_pg0.SASAddress)); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { + sas_device_put(sas_device); continue; + } parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { pr_info(MPT3SAS_FMT "\tBEFORE adding end device: " \ @@ -7063,10 +7546,11 @@ mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) static void _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { + _scsih_fw_event_del_from_list(ioc, fw_event); + /* the queue is being flushed so ignore this event */ - if (ioc->remove_host || - ioc->pci_error_recovery) { - _scsih_fw_event_free(ioc, fw_event); + if (ioc->remove_host || ioc->pci_error_recovery) { + fw_event_work_put(fw_event); return; } @@ -7077,8 +7561,16 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) fw_event->event_data); break; case MPT3SAS_REMOVE_UNRESPONDING_DEVICES: - while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery) + while (scsi_host_in_recovery(ioc->shost) || + ioc->shost_recovery) { + /* + * If we're unloading, bail. Otherwise, this can become + * an infinite loop. + */ + if (ioc->remove_host) + goto out; ssleep(1); + } _scsih_remove_unresponding_sas_devices(ioc); _scsih_scan_for_devices_after_reset(ioc); break; @@ -7123,7 +7615,8 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) _scsih_sas_ir_operation_status_event(ioc, fw_event); break; } - _scsih_fw_event_free(ioc, fw_event); +out: + fw_event_work_put(fw_event); } /** @@ -7220,7 +7713,53 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, (Mpi2EventDataIrVolume_t *) mpi_reply->EventData); break; + case MPI2_EVENT_LOG_ENTRY_ADDED: + { + Mpi2EventDataLogEntryAdded_t *log_entry; + u32 *log_code; + + if (!ioc->is_warpdrive) + break; + + log_entry = (Mpi2EventDataLogEntryAdded_t *) + mpi_reply->EventData; + log_code = (u32 *)log_entry->LogData; + + if (le16_to_cpu(log_entry->LogEntryQualifier) + != MPT2_WARPDRIVE_LOGENTRY) + break; + + switch (le32_to_cpu(*log_code)) { + case MPT2_WARPDRIVE_LC_SSDT: + pr_warn(MPT3SAS_FMT "WarpDrive Warning: " + "IO Throttling has occurred in the WarpDrive " + "subsystem. Check WarpDrive documentation for " + "additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_SSDLW: + pr_warn(MPT3SAS_FMT "WarpDrive Warning: " + "Program/Erase Cycles for the WarpDrive subsystem " + "in degraded range. Check WarpDrive documentation " + "for additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_SSDLF: + pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " + "There are no Program/Erase Cycles for the " + "WarpDrive subsystem. The storage device will be " + "in read-only mode. Check WarpDrive documentation " + "for additional details.\n", ioc->name); + break; + case MPT2_WARPDRIVE_LC_BRMF: + pr_err(MPT3SAS_FMT "WarpDrive Fatal Error: " + "The Backup Rail Monitor has failed on the " + "WarpDrive subsystem. Check WarpDrive " + "documentation for additional details.\n", + ioc->name); + break; + } + break; + } case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: case MPI2_EVENT_IR_OPERATION_STATUS: case MPI2_EVENT_SAS_DISCOVERY: @@ -7239,7 +7778,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, } sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; - fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC); + fw_event = alloc_fw_event_work(sz); if (!fw_event) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -7252,39 +7791,10 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, fw_event->VP_ID = mpi_reply->VP_ID; fw_event->event = event; _scsih_fw_event_add(ioc, fw_event); + fw_event_work_put(fw_event); return 1; } -/* shost template */ -static struct scsi_host_template scsih_driver_template = { - .module = THIS_MODULE, - .name = "Fusion MPT SAS Host", - .proc_name = MPT3SAS_DRIVER_NAME, - .queuecommand = _scsih_qcmd, - .target_alloc = _scsih_target_alloc, - .slave_alloc = _scsih_slave_alloc, - .slave_configure = _scsih_slave_configure, - .target_destroy = _scsih_target_destroy, - .slave_destroy = _scsih_slave_destroy, - .scan_finished = _scsih_scan_finished, - .scan_start = _scsih_scan_start, - .change_queue_depth = _scsih_change_queue_depth, - .eh_abort_handler = _scsih_abort, - .eh_device_reset_handler = _scsih_dev_reset, - .eh_target_reset_handler = _scsih_target_reset, - .eh_host_reset_handler = _scsih_host_reset, - .bios_param = _scsih_bios_param, - .can_queue = 1, - .this_id = -1, - .sg_tablesize = MPT3SAS_SG_DEPTH, - .max_sectors = 32767, - .cmd_per_lun = 7, - .use_clustering = ENABLE_CLUSTERING, - .shost_attrs = mpt3sas_host_attrs, - .sdev_attrs = mpt3sas_dev_attrs, - .track_queue_depth = 1, -}; - /** * _scsih_expander_node_remove - removing expander device from list. * @ioc: per adapter object @@ -7380,7 +7890,8 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) mpi_request->Function = MPI2_FUNCTION_RAID_ACTION; mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED; - pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name); + if (!ioc->hide_ir_msg) + pr_info(MPT3SAS_FMT "IR shutdown (sending)\n", ioc->name); init_completion(&ioc->scsih_cmds.done); mpt3sas_base_put_smid_default(ioc, smid); wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ); @@ -7393,10 +7904,11 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) if (ioc->scsih_cmds.status & MPT3_CMD_REPLY_VALID) { mpi_reply = ioc->scsih_cmds.reply; - pr_info(MPT3SAS_FMT - "IR shutdown (complete): ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo)); + if (!ioc->hide_ir_msg) + pr_info(MPT3SAS_FMT "IR shutdown " + "(complete): ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); } out: @@ -7405,13 +7917,13 @@ _scsih_ir_shutdown(struct MPT3SAS_ADAPTER *ioc) } /** - * _scsih_remove - detach and remove add host + * scsih_remove - detach and remove add host * @pdev: PCI device struct * * Routine called when unloading the driver. * Return nothing. */ -static void _scsih_remove(struct pci_dev *pdev) +void scsih_remove(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7472,18 +7984,20 @@ static void _scsih_remove(struct pci_dev *pdev) sas_remove_host(shost); scsi_remove_host(shost); mpt3sas_base_detach(ioc); + spin_lock(&gioc_lock); list_del(&ioc->list); + spin_unlock(&gioc_lock); scsi_host_put(shost); } /** - * _scsih_shutdown - routine call during system shutdown + * scsih_shutdown - routine call during system shutdown * @pdev: PCI device struct * * Return nothing. */ -static void -_scsih_shutdown(struct pci_dev *pdev) +void +scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7561,6 +8075,8 @@ _scsih_probe_boot_devices(struct MPT3SAS_ADAPTER *ioc) list_move_tail(&sas_device->list, &ioc->sas_device_list); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (ioc->hide_drives) + return; if (!mpt3sas_transport_port_add(ioc, handle, sas_address_parent)) { _scsih_sas_device_remove(ioc, sas_device); @@ -7598,6 +8114,48 @@ _scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc) } } +static struct _sas_device *get_next_sas_device(struct MPT3SAS_ADAPTER *ioc) +{ + struct _sas_device *sas_device = NULL; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + if (!list_empty(&ioc->sas_device_init_list)) { + sas_device = list_first_entry(&ioc->sas_device_init_list, + struct _sas_device, list); + sas_device_get(sas_device); + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return sas_device; +} + +static void sas_device_make_active(struct MPT3SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + + /* + * Since we dropped the lock during the call to port_add(), we need to + * be careful here that somebody else didn't move or delete this item + * while we were busy with other things. + * + * If it was on the list, we need a put() for the reference the list + * had. Either way, we need a get() for the destination list. + */ + if (!list_empty(&sas_device->list)) { + list_del_init(&sas_device->list); + sas_device_put(sas_device); + } + + sas_device_get(sas_device); + list_add_tail(&sas_device->list, &ioc->sas_device_list); + + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +} + /** * _scsih_probe_sas - reporting sas devices to sas transport * @ioc: per adapter object @@ -7607,17 +8165,16 @@ _scsih_probe_raid(struct MPT3SAS_ADAPTER *ioc) static void _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) { - struct _sas_device *sas_device, *next; - unsigned long flags; + struct _sas_device *sas_device; - /* SAS Device List */ - list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, - list) { + if (ioc->hide_drives) + return; + while ((sas_device = get_next_sas_device(ioc))) { if (!mpt3sas_transport_port_add(ioc, sas_device->handle, sas_device->sas_address_parent)) { - list_del(&sas_device->list); - kfree(sas_device); + _scsih_sas_device_remove(ioc, sas_device); + sas_device_put(sas_device); continue; } else if (!sas_device->starget) { /* @@ -7630,15 +8187,13 @@ _scsih_probe_sas(struct MPT3SAS_ADAPTER *ioc) mpt3sas_transport_port_remove(ioc, sas_device->sas_address, sas_device->sas_address_parent); - list_del(&sas_device->list); - kfree(sas_device); + _scsih_sas_device_remove(ioc, sas_device); + sas_device_put(sas_device); continue; } } - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_move_tail(&sas_device->list, &ioc->sas_device_list); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + sas_device_make_active(ioc, sas_device); + sas_device_put(sas_device); } } @@ -7675,15 +8230,15 @@ _scsih_probe_devices(struct MPT3SAS_ADAPTER *ioc) } /** - * _scsih_scan_start - scsi lld callback for .scan_start + * scsih_scan_start - scsi lld callback for .scan_start * @shost: SCSI host pointer * * The shost has the ability to discover targets on its own instead * of scanning the entire bus. In our implemention, we will kick off * firmware discovery. */ -static void -_scsih_scan_start(struct Scsi_Host *shost) +void +scsih_scan_start(struct Scsi_Host *shost) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); int rc; @@ -7701,7 +8256,7 @@ _scsih_scan_start(struct Scsi_Host *shost) } /** - * _scsih_scan_finished - scsi lld callback for .scan_finished + * scsih_scan_finished - scsi lld callback for .scan_finished * @shost: SCSI host pointer * @time: elapsed time of the scan in jiffies * @@ -7709,8 +8264,8 @@ _scsih_scan_start(struct Scsi_Host *shost) * scsi_host and the elapsed time of the scan in jiffies. In our implemention, * we wait for firmware discovery to complete, then return 1. */ -static int -_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) +int +scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7754,6 +8309,124 @@ _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) return 1; } +/* shost template for SAS 2.0 HBA devices */ +static struct scsi_host_template mpt2sas_driver_template = { + .module = THIS_MODULE, + .name = "Fusion MPT SAS Host", + .proc_name = MPT2SAS_DRIVER_NAME, + .queuecommand = scsih_qcmd, + .target_alloc = scsih_target_alloc, + .slave_alloc = scsih_slave_alloc, + .slave_configure = scsih_slave_configure, + .target_destroy = scsih_target_destroy, + .slave_destroy = scsih_slave_destroy, + .scan_finished = scsih_scan_finished, + .scan_start = scsih_scan_start, + .change_queue_depth = scsih_change_queue_depth, + .eh_abort_handler = scsih_abort, + .eh_device_reset_handler = scsih_dev_reset, + .eh_target_reset_handler = scsih_target_reset, + .eh_host_reset_handler = scsih_host_reset, + .bios_param = scsih_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = MPT2SAS_SG_DEPTH, + .max_sectors = 32767, + .cmd_per_lun = 7, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mpt3sas_host_attrs, + .sdev_attrs = mpt3sas_dev_attrs, + .track_queue_depth = 1, +}; + +/* raid transport support for SAS 2.0 HBA devices */ +static struct raid_function_template mpt2sas_raid_functions = { + .cookie = &mpt2sas_driver_template, + .is_raid = scsih_is_raid, + .get_resync = scsih_get_resync, + .get_state = scsih_get_state, +}; + +/* shost template for SAS 3.0 HBA devices */ +static struct scsi_host_template mpt3sas_driver_template = { + .module = THIS_MODULE, + .name = "Fusion MPT SAS Host", + .proc_name = MPT3SAS_DRIVER_NAME, + .queuecommand = scsih_qcmd, + .target_alloc = scsih_target_alloc, + .slave_alloc = scsih_slave_alloc, + .slave_configure = scsih_slave_configure, + .target_destroy = scsih_target_destroy, + .slave_destroy = scsih_slave_destroy, + .scan_finished = scsih_scan_finished, + .scan_start = scsih_scan_start, + .change_queue_depth = scsih_change_queue_depth, + .eh_abort_handler = scsih_abort, + .eh_device_reset_handler = scsih_dev_reset, + .eh_target_reset_handler = scsih_target_reset, + .eh_host_reset_handler = scsih_host_reset, + .bios_param = scsih_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = MPT3SAS_SG_DEPTH, + .max_sectors = 32767, + .cmd_per_lun = 7, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mpt3sas_host_attrs, + .sdev_attrs = mpt3sas_dev_attrs, + .track_queue_depth = 1, +}; + +/* raid transport support for SAS 3.0 HBA devices */ +static struct raid_function_template mpt3sas_raid_functions = { + .cookie = &mpt3sas_driver_template, + .is_raid = scsih_is_raid, + .get_resync = scsih_get_resync, + .get_state = scsih_get_state, +}; + +/** + * _scsih_determine_hba_mpi_version - determine in which MPI version class + * this device belongs to. + * @pdev: PCI device struct + * + * return MPI2_VERSION for SAS 2.0 HBA devices, + * MPI25_VERSION for SAS 3.0 HBA devices. + */ +u16 +_scsih_determine_hba_mpi_version(struct pci_dev *pdev) +{ + + switch (pdev->device) { + case MPI2_MFGPAGE_DEVID_SSS6200: + case MPI2_MFGPAGE_DEVID_SAS2004: + case MPI2_MFGPAGE_DEVID_SAS2008: + case MPI2_MFGPAGE_DEVID_SAS2108_1: + case MPI2_MFGPAGE_DEVID_SAS2108_2: + case MPI2_MFGPAGE_DEVID_SAS2108_3: + case MPI2_MFGPAGE_DEVID_SAS2116_1: + case MPI2_MFGPAGE_DEVID_SAS2116_2: + case MPI2_MFGPAGE_DEVID_SAS2208_1: + case MPI2_MFGPAGE_DEVID_SAS2208_2: + case MPI2_MFGPAGE_DEVID_SAS2208_3: + case MPI2_MFGPAGE_DEVID_SAS2208_4: + case MPI2_MFGPAGE_DEVID_SAS2208_5: + case MPI2_MFGPAGE_DEVID_SAS2208_6: + case MPI2_MFGPAGE_DEVID_SAS2308_1: + case MPI2_MFGPAGE_DEVID_SAS2308_2: + case MPI2_MFGPAGE_DEVID_SAS2308_3: + return MPI2_VERSION; + case MPI25_MFGPAGE_DEVID_SAS3004: + case MPI25_MFGPAGE_DEVID_SAS3008: + case MPI25_MFGPAGE_DEVID_SAS3108_1: + case MPI25_MFGPAGE_DEVID_SAS3108_2: + case MPI25_MFGPAGE_DEVID_SAS3108_5: + case MPI25_MFGPAGE_DEVID_SAS3108_6: + return MPI25_VERSION; + } + return 0; +} + /** * _scsih_probe - attach and add scsi host * @pdev: PCI device struct @@ -7761,26 +8434,72 @@ _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) * * Returns 0 success, anything else error. */ -static int +int _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct MPT3SAS_ADAPTER *ioc; - struct Scsi_Host *shost; + struct Scsi_Host *shost = NULL; int rv; + u16 hba_mpi_version; - shost = scsi_host_alloc(&scsih_driver_template, - sizeof(struct MPT3SAS_ADAPTER)); - if (!shost) + /* Determine in which MPI version class this pci device belongs */ + hba_mpi_version = _scsih_determine_hba_mpi_version(pdev); + if (hba_mpi_version == 0) return -ENODEV; - /* init local params */ - ioc = shost_priv(shost); - memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); + /* Enumerate only SAS 2.0 HBA's if hbas_to_enumerate is one, + * for other generation HBA's return with -ENODEV + */ + if ((hbas_to_enumerate == 1) && (hba_mpi_version != MPI2_VERSION)) + return -ENODEV; + + /* Enumerate only SAS 3.0 HBA's if hbas_to_enumerate is two, + * for other generation HBA's return with -ENODEV + */ + if ((hbas_to_enumerate == 2) && (hba_mpi_version != MPI25_VERSION)) + return -ENODEV; + + switch (hba_mpi_version) { + case MPI2_VERSION: + /* Use mpt2sas driver host template for SAS 2.0 HBA's */ + shost = scsi_host_alloc(&mpt2sas_driver_template, + sizeof(struct MPT3SAS_ADAPTER)); + if (!shost) + return -ENODEV; + ioc = shost_priv(shost); + memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); + ioc->hba_mpi_version_belonged = hba_mpi_version; + ioc->id = mpt2_ids++; + sprintf(ioc->driver_name, "%s", MPT2SAS_DRIVER_NAME); + if (pdev->device == MPI2_MFGPAGE_DEVID_SSS6200) { + ioc->is_warpdrive = 1; + ioc->hide_ir_msg = 1; + } else + ioc->mfg_pg10_hide_flag = MFG_PAGE10_EXPOSE_ALL_DISKS; + break; + case MPI25_VERSION: + /* Use mpt3sas driver host template for SAS 3.0 HBA's */ + shost = scsi_host_alloc(&mpt3sas_driver_template, + sizeof(struct MPT3SAS_ADAPTER)); + if (!shost) + return -ENODEV; + ioc = shost_priv(shost); + memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER)); + ioc->hba_mpi_version_belonged = hba_mpi_version; + ioc->id = mpt3_ids++; + sprintf(ioc->driver_name, "%s", MPT3SAS_DRIVER_NAME); + if (pdev->revision >= SAS3_PCI_DEVICE_C0_REVISION) + ioc->msix96_vector = 1; + break; + default: + return -ENODEV; + } + INIT_LIST_HEAD(&ioc->list); + spin_lock(&gioc_lock); list_add_tail(&ioc->list, &mpt3sas_ioc_list); + spin_unlock(&gioc_lock); ioc->shost = shost; - ioc->id = mpt_ids++; - sprintf(ioc->name, "%s%d", MPT3SAS_DRIVER_NAME, ioc->id); ioc->pdev = pdev; ioc->scsi_io_cb_idx = scsi_io_cb_idx; ioc->tm_cb_idx = tm_cb_idx; @@ -7797,6 +8516,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds; /* misc semaphores and spin locks */ mutex_init(&ioc->reset_in_progress_mutex); + /* initializing pci_access_mutex lock */ + mutex_init(&ioc->pci_access_mutex); spin_lock_init(&ioc->ioc_reset_in_progress_lock); spin_lock_init(&ioc->scsi_lookup_lock); spin_lock_init(&ioc->sas_device_lock); @@ -7815,6 +8536,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->delayed_tr_volume_list); INIT_LIST_HEAD(&ioc->reply_queue_list); + sprintf(ioc->name, "%s_cm%d", ioc->driver_name, ioc->id); + /* init shost parameters */ shost->max_cmd_len = 32; shost->max_lun = max_lun; @@ -7853,9 +8576,9 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* event thread */ snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), - "fw_event%d", ioc->id); - ioc->firmware_event_thread = create_singlethread_workqueue( - ioc->firmware_event_name); + "fw_event_%s%d", ioc->driver_name, ioc->id); + ioc->firmware_event_thread = alloc_ordered_workqueue( + ioc->firmware_event_name, WQ_MEM_RECLAIM); if (!ioc->firmware_event_thread) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -7870,6 +8593,21 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) rv = -ENODEV; goto out_attach_fail; } + + if (ioc->is_warpdrive) { + if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) + ioc->hide_drives = 0; + else if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_HIDE_ALL_DISKS) + ioc->hide_drives = 1; + else { + if (mpt3sas_get_num_volumes(ioc)) + ioc->hide_drives = 1; + else + ioc->hide_drives = 0; + } + } else + ioc->hide_drives = 0; + rv = scsi_add_host(shost, &pdev->dev); if (rv) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", @@ -7884,21 +8622,23 @@ out_add_shost_fail: out_attach_fail: destroy_workqueue(ioc->firmware_event_thread); out_thread_fail: + spin_lock(&gioc_lock); list_del(&ioc->list); + spin_unlock(&gioc_lock); scsi_host_put(shost); return rv; } #ifdef CONFIG_PM /** - * _scsih_suspend - power management suspend main entry point + * scsih_suspend - power management suspend main entry point * @pdev: PCI device struct * @state: PM state change to (usually PCI_D3) * * Returns 0 success, anything else error. */ -static int -_scsih_suspend(struct pci_dev *pdev, pm_message_t state) +int +scsih_suspend(struct pci_dev *pdev, pm_message_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7919,13 +8659,13 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state) } /** - * _scsih_resume - power management resume main entry point + * scsih_resume - power management resume main entry point * @pdev: PCI device struct * * Returns 0 success, anything else error. */ -static int -_scsih_resume(struct pci_dev *pdev) +int +scsih_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7952,7 +8692,7 @@ _scsih_resume(struct pci_dev *pdev) #endif /* CONFIG_PM */ /** - * _scsih_pci_error_detected - Called when a PCI error is detected. + * scsih_pci_error_detected - Called when a PCI error is detected. * @pdev: PCI device struct * @state: PCI channel state * @@ -7961,8 +8701,8 @@ _scsih_resume(struct pci_dev *pdev) * Return value: * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT */ -static pci_ers_result_t -_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +pci_ers_result_t +scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -7991,15 +8731,15 @@ _scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) } /** - * _scsih_pci_slot_reset - Called when PCI slot has been reset. + * scsih_pci_slot_reset - Called when PCI slot has been reset. * @pdev: PCI device struct * * Description: This routine is called by the pci error recovery * code after the PCI slot has been reset, just before we * should resume normal operations. */ -static pci_ers_result_t -_scsih_pci_slot_reset(struct pci_dev *pdev) +pci_ers_result_t +scsih_pci_slot_reset(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8028,15 +8768,15 @@ _scsih_pci_slot_reset(struct pci_dev *pdev) } /** - * _scsih_pci_resume() - resume normal ops after PCI reset + * scsih_pci_resume() - resume normal ops after PCI reset * @pdev: pointer to PCI device * * Called when the error recovery driver tells us that its * OK to resume normal operation. Use completion to allow * halted scsi ops to resume. */ -static void -_scsih_pci_resume(struct pci_dev *pdev) +void +scsih_pci_resume(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8049,11 +8789,11 @@ _scsih_pci_resume(struct pci_dev *pdev) } /** - * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers + * scsih_pci_mmio_enabled - Enable MMIO and dump debug registers * @pdev: pointer to PCI device */ -static pci_ers_result_t -_scsih_pci_mmio_enabled(struct pci_dev *pdev) +pci_ers_result_t +scsih_pci_mmio_enabled(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT3SAS_ADAPTER *ioc = shost_priv(shost); @@ -8067,61 +8807,99 @@ _scsih_pci_mmio_enabled(struct pci_dev *pdev) return PCI_ERS_RESULT_NEED_RESET; } -/* raid transport support */ -static struct raid_function_template mpt3sas_raid_functions = { - .cookie = &scsih_driver_template, - .is_raid = _scsih_is_raid, - .get_resync = _scsih_get_resync, - .get_state = _scsih_get_state, +/* + * The pci device ids are defined in mpi/mpi2_cnfg.h. + */ +static const struct pci_device_id mpt3sas_pci_table[] = { + /* Spitfire ~ 2004 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, + PCI_ANY_ID, PCI_ANY_ID }, + /* Falcon ~ 2008 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, + PCI_ANY_ID, PCI_ANY_ID }, + /* Liberator ~ 2108 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, + PCI_ANY_ID, PCI_ANY_ID }, + /* Meteor ~ 2116 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, + PCI_ANY_ID, PCI_ANY_ID }, + /* Thunderbolt ~ 2208 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, + PCI_ANY_ID, PCI_ANY_ID }, + /* Mustang ~ 2308 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, + PCI_ANY_ID, PCI_ANY_ID }, + /* SSS6200 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, + PCI_ANY_ID, PCI_ANY_ID }, + /* Fury ~ 3004 and 3008 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3004, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3008, + PCI_ANY_ID, PCI_ANY_ID }, + /* Invader ~ 3108 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_5, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI25_MFGPAGE_DEVID_SAS3108_6, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ }; +MODULE_DEVICE_TABLE(pci, mpt3sas_pci_table); -static struct pci_error_handlers _scsih_err_handler = { - .error_detected = _scsih_pci_error_detected, - .mmio_enabled = _scsih_pci_mmio_enabled, - .slot_reset = _scsih_pci_slot_reset, - .resume = _scsih_pci_resume, +static struct pci_error_handlers _mpt3sas_err_handler = { + .error_detected = scsih_pci_error_detected, + .mmio_enabled = scsih_pci_mmio_enabled, + .slot_reset = scsih_pci_slot_reset, + .resume = scsih_pci_resume, }; -static struct pci_driver scsih_driver = { +static struct pci_driver mpt3sas_driver = { .name = MPT3SAS_DRIVER_NAME, - .id_table = scsih_pci_table, + .id_table = mpt3sas_pci_table, .probe = _scsih_probe, - .remove = _scsih_remove, - .shutdown = _scsih_shutdown, - .err_handler = &_scsih_err_handler, + .remove = scsih_remove, + .shutdown = scsih_shutdown, + .err_handler = &_mpt3sas_err_handler, #ifdef CONFIG_PM - .suspend = _scsih_suspend, - .resume = _scsih_resume, + .suspend = scsih_suspend, + .resume = scsih_resume, #endif }; - /** - * _scsih_init - main entry point for this driver. + * scsih_init - main entry point for this driver. * * Returns 0 success, anything else error. */ -static int __init -_scsih_init(void) +int +scsih_init(void) { - int error; - - mpt_ids = 0; - - pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME, - MPT3SAS_DRIVER_VERSION); - - mpt3sas_transport_template = - sas_attach_transport(&mpt3sas_transport_functions); - if (!mpt3sas_transport_template) - return -ENODEV; - -/* raid transport support */ - mpt3sas_raid_template = raid_class_attach(&mpt3sas_raid_functions); - if (!mpt3sas_raid_template) { - sas_release_transport(mpt3sas_transport_template); - return -ENODEV; - } + mpt2_ids = 0; + mpt3_ids = 0; mpt3sas_base_initialize_callback_handler(); @@ -8159,33 +8937,17 @@ _scsih_init(void) tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler( _scsih_sas_control_complete); - mpt3sas_ctl_init(); - - error = pci_register_driver(&scsih_driver); - if (error) { - /* raid transport support */ - raid_class_release(mpt3sas_raid_template); - sas_release_transport(mpt3sas_transport_template); - } - - return error; + return 0; } /** - * _scsih_exit - exit point for this driver (when it is a module). + * scsih_exit - exit point for this driver (when it is a module). * * Returns 0 success, anything else error. */ -static void __exit -_scsih_exit(void) +void +scsih_exit(void) { - pr_info("mpt3sas version %s unloading\n", - MPT3SAS_DRIVER_VERSION); - - mpt3sas_ctl_exit(); - - pci_unregister_driver(&scsih_driver); - mpt3sas_base_release_callback_handler(scsi_io_cb_idx); mpt3sas_base_release_callback_handler(tm_cb_idx); @@ -8201,9 +8963,86 @@ _scsih_exit(void) mpt3sas_base_release_callback_handler(tm_sas_control_cb_idx); /* raid transport support */ - raid_class_release(mpt3sas_raid_template); + if (hbas_to_enumerate != 1) + raid_class_release(mpt3sas_raid_template); + if (hbas_to_enumerate != 2) + raid_class_release(mpt2sas_raid_template); sas_release_transport(mpt3sas_transport_template); } -module_init(_scsih_init); -module_exit(_scsih_exit); +/** + * _mpt3sas_init - main entry point for this driver. + * + * Returns 0 success, anything else error. + */ +static int __init +_mpt3sas_init(void) +{ + int error; + + pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME, + MPT3SAS_DRIVER_VERSION); + + mpt3sas_transport_template = + sas_attach_transport(&mpt3sas_transport_functions); + if (!mpt3sas_transport_template) + return -ENODEV; + + /* No need attach mpt3sas raid functions template + * if hbas_to_enumarate value is one. + */ + if (hbas_to_enumerate != 1) { + mpt3sas_raid_template = + raid_class_attach(&mpt3sas_raid_functions); + if (!mpt3sas_raid_template) { + sas_release_transport(mpt3sas_transport_template); + return -ENODEV; + } + } + + /* No need to attach mpt2sas raid functions template + * if hbas_to_enumarate value is two + */ + if (hbas_to_enumerate != 2) { + mpt2sas_raid_template = + raid_class_attach(&mpt2sas_raid_functions); + if (!mpt2sas_raid_template) { + sas_release_transport(mpt3sas_transport_template); + return -ENODEV; + } + } + + error = scsih_init(); + if (error) { + scsih_exit(); + return error; + } + + mpt3sas_ctl_init(hbas_to_enumerate); + + error = pci_register_driver(&mpt3sas_driver); + if (error) + scsih_exit(); + + return error; +} + +/** + * _mpt3sas_exit - exit point for this driver (when it is a module). + * + */ +static void __exit +_mpt3sas_exit(void) +{ + pr_info("mpt3sas version %s unloading\n", + MPT3SAS_DRIVER_VERSION); + + pci_unregister_driver(&mpt3sas_driver); + + mpt3sas_ctl_exit(hbas_to_enumerate); + + scsih_exit(); +} + +module_init(_mpt3sas_init); +module_exit(_mpt3sas_exit); diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_transport.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_transport.c index efb98afc4..ca36d7ea0 100644 --- a/kernel/drivers/scsi/mpt3sas/mpt3sas_transport.c +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_transport.c @@ -649,6 +649,7 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, unsigned long flags; struct _sas_node *sas_node; struct sas_rphy *rphy; + struct _sas_device *sas_device = NULL; int i; struct sas_port *port; @@ -731,10 +732,29 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle, mpt3sas_port->remote_identify.device_type); rphy->identify = mpt3sas_port->remote_identify; + + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_device = mpt3sas_get_sdev_by_addr(ioc, + mpt3sas_port->remote_identify.sas_address); + if (!sas_device) { + dfailprintk(ioc, printk(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__)); + goto out_fail; + } + sas_device->pend_sas_rphy_add = 1; + } + if ((sas_rphy_add(rphy))) { pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); } + + if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_device->pend_sas_rphy_add = 0; + sas_device_put(sas_device); + } + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), sas_addr(0x%016llx)\n", @@ -1306,15 +1326,17 @@ _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); if (sas_device) { *identifier = sas_device->enclosure_logical_id; rc = 0; + sas_device_put(sas_device); } else { *identifier = 0; rc = -ENXIO; } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return rc; } @@ -1334,12 +1356,14 @@ _transport_get_bay_identifier(struct sas_rphy *rphy) int rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device = __mpt3sas_get_sdev_by_addr(ioc, rphy->identify.sas_address); - if (sas_device) + if (sas_device) { rc = sas_device->slot; - else + sas_device_put(sas_device); + } else { rc = -ENXIO; + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return rc; } @@ -1946,7 +1970,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } else { dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_out) { + if (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) { pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n", ioc->name, __func__); rc = -ENOMEM; @@ -1968,7 +1992,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } else { dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL); - if (!dma_addr_in) { + if (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) { pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n", ioc->name, __func__); rc = -ENOMEM; diff --git a/kernel/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c b/kernel/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c new file mode 100644 index 000000000..540bd5005 --- /dev/null +++ b/kernel/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c @@ -0,0 +1,344 @@ +/* + * Scsi Host Layer for MPT (Message Passing Technology) based controllers + * + * Copyright (C) 2012-2014 LSI Corporation + * Copyright (C) 2013-2015 Avago Technologies + * (mailto: MPT-FusionLinux.pdl@avagotech.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; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ +#include +#include +#include +#include +#include + +#include "mpt3sas_base.h" + +/** + * _warpdrive_disable_ddio - Disable direct I/O for all the volumes + * @ioc: per adapter object + */ +static void +_warpdrive_disable_ddio(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi2RaidVolPage1_t vol_pg1; + Mpi2ConfigReply_t mpi_reply; + struct _raid_device *raid_device; + u16 handle; + u16 ioc_status; + unsigned long flags; + + handle = 0xFFFF; + while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(vol_pg1.DevHandle); + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = mpt3sas_raid_device_find_by_handle(ioc, handle); + if (raid_device) + raid_device->direct_io_enabled = 0; + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + } + return; +} + + +/** + * mpt3sas_get_num_volumes - Get number of volumes in the ioc + * @ioc: per adapter object + */ +u8 +mpt3sas_get_num_volumes(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi2RaidVolPage1_t vol_pg1; + Mpi2ConfigReply_t mpi_reply; + u16 handle; + u8 vol_cnt = 0; + u16 ioc_status; + + handle = 0xFFFF; + while (!(mpt3sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + vol_cnt++; + handle = le16_to_cpu(vol_pg1.DevHandle); + } + return vol_cnt; +} + + +/** + * mpt3sas_init_warpdrive_properties - Set properties for warpdrive direct I/O. + * @ioc: per adapter object + * @raid_device: the raid_device object + */ +void +mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, + struct _raid_device *raid_device) +{ + Mpi2RaidVolPage0_t *vol_pg0; + Mpi2RaidPhysDiskPage0_t pd_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 sz; + u8 num_pds, count; + unsigned long stripe_sz, block_sz; + u8 stripe_exp, block_exp; + u64 dev_max_lba; + + if (!ioc->is_warpdrive) + return; + + if (ioc->mfg_pg10_hide_flag == MFG_PAGE10_EXPOSE_ALL_DISKS) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "globally as drives are exposed\n", ioc->name); + return; + } + if (mpt3sas_get_num_volumes(ioc) > 1) { + _warpdrive_disable_ddio(ioc); + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "globally as number of drives > 1\n", ioc->name); + return; + } + if ((mpt3sas_config_get_number_pds(ioc, raid_device->handle, + &num_pds)) || !num_pds) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "Failure in computing number of drives\n", ioc->name); + return; + } + + sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * + sizeof(Mpi2RaidVol0PhysDisk_t)); + vol_pg0 = kzalloc(sz, GFP_KERNEL); + if (!vol_pg0) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "Memory allocation failure for RVPG0\n", ioc->name); + return; + } + + if ((mpt3sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, + MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "Failure in retrieving RVPG0\n", ioc->name); + kfree(vol_pg0); + return; + } + + /* + * WARPDRIVE:If number of physical disks in a volume exceeds the max pds + * assumed for WARPDRIVE, disable direct I/O + */ + if (num_pds > MPT_MAX_WARPDRIVE_PDS) { + pr_warn(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x): num_mem=%d, " + "max_mem_allowed=%d\n", ioc->name, raid_device->handle, + num_pds, MPT_MAX_WARPDRIVE_PDS); + kfree(vol_pg0); + return; + } + for (count = 0; count < num_pds; count++) { + if (mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply, + &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, + vol_pg0->PhysDisk[count].PhysDiskNum) || + pd_pg0.DevHandle == MPT3SAS_INVALID_DEVICE_HANDLE) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " + "disabled for the drive with handle(0x%04x) member" + "handle retrieval failed for member number=%d\n", + ioc->name, raid_device->handle, + vol_pg0->PhysDisk[count].PhysDiskNum); + goto out_error; + } + /* Disable direct I/O if member drive lba exceeds 4 bytes */ + dev_max_lba = le64_to_cpu(pd_pg0.DeviceMaxLBA); + if (dev_max_lba >> 32) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " + "disabled for the drive with handle(0x%04x) member" + " handle (0x%04x) unsupported max lba 0x%016llx\n", + ioc->name, raid_device->handle, + le16_to_cpu(pd_pg0.DevHandle), + (unsigned long long)dev_max_lba); + goto out_error; + } + + raid_device->pd_handle[count] = le16_to_cpu(pd_pg0.DevHandle); + } + + /* + * Assumption for WD: Direct I/O is not supported if the volume is + * not RAID0 + */ + if (raid_device->volume_type != MPI2_RAID_VOL_TYPE_RAID0) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x): type=%d, " + "s_sz=%uK, blk_size=%u\n", ioc->name, + raid_device->handle, raid_device->volume_type, + (le32_to_cpu(vol_pg0->StripeSize) * + le16_to_cpu(vol_pg0->BlockSize)) / 1024, + le16_to_cpu(vol_pg0->BlockSize)); + goto out_error; + } + + stripe_sz = le32_to_cpu(vol_pg0->StripeSize); + stripe_exp = find_first_bit(&stripe_sz, 32); + if (stripe_exp == 32) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x) invalid stripe sz %uK\n", + ioc->name, raid_device->handle, + (le32_to_cpu(vol_pg0->StripeSize) * + le16_to_cpu(vol_pg0->BlockSize)) / 1024); + goto out_error; + } + raid_device->stripe_exponent = stripe_exp; + block_sz = le16_to_cpu(vol_pg0->BlockSize); + block_exp = find_first_bit(&block_sz, 16); + if (block_exp == 16) { + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is disabled " + "for the drive with handle(0x%04x) invalid block sz %u\n", + ioc->name, raid_device->handle, + le16_to_cpu(vol_pg0->BlockSize)); + goto out_error; + } + raid_device->block_exponent = block_exp; + raid_device->direct_io_enabled = 1; + + pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is Enabled for the drive" + " with handle(0x%04x)\n", ioc->name, raid_device->handle); + /* + * WARPDRIVE: Though the following fields are not used for direct IO, + * stored for future purpose: + */ + raid_device->max_lba = le64_to_cpu(vol_pg0->MaxLBA); + raid_device->stripe_sz = le32_to_cpu(vol_pg0->StripeSize); + raid_device->block_sz = le16_to_cpu(vol_pg0->BlockSize); + + + kfree(vol_pg0); + return; + +out_error: + raid_device->direct_io_enabled = 0; + for (count = 0; count < num_pds; count++) + raid_device->pd_handle[count] = 0; + kfree(vol_pg0); + return; +} + +/** + * mpt3sas_scsi_direct_io_get - returns direct io flag + * @ioc: per adapter object + * @smid: system request message index + * + * Returns the smid stored scmd pointer. + */ +inline u8 +mpt3sas_scsi_direct_io_get(struct MPT3SAS_ADAPTER *ioc, u16 smid) +{ + return ioc->scsi_lookup[smid - 1].direct_io; +} + +/** + * mpt3sas_scsi_direct_io_set - sets direct io flag + * @ioc: per adapter object + * @smid: system request message index + * @direct_io: Zero or non-zero value to set in the direct_io flag + * + * Returns Nothing. + */ +inline void +mpt3sas_scsi_direct_io_set(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 direct_io) +{ + ioc->scsi_lookup[smid - 1].direct_io = direct_io; +} + +/** + * mpt3sas_setup_direct_io - setup MPI request for WARPDRIVE Direct I/O + * @ioc: per adapter object + * @scmd: pointer to scsi command object + * @raid_device: pointer to raid device data structure + * @mpi_request: pointer to the SCSI_IO reqest message frame + * @smid: system request message index + * + * Returns nothing + */ +void +mpt3sas_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, + struct _raid_device *raid_device, Mpi2SCSIIORequest_t *mpi_request, + u16 smid) +{ + sector_t v_lba, p_lba, stripe_off, column, io_size; + u32 stripe_sz, stripe_exp; + u8 num_pds, cmd = scmd->cmnd[0]; + + if (cmd != READ_10 && cmd != WRITE_10 && + cmd != READ_16 && cmd != WRITE_16) + return; + + if (cmd == READ_10 || cmd == WRITE_10) + v_lba = get_unaligned_be32(&mpi_request->CDB.CDB32[2]); + else + v_lba = get_unaligned_be64(&mpi_request->CDB.CDB32[2]); + + io_size = scsi_bufflen(scmd) >> raid_device->block_exponent; + + if (v_lba + io_size - 1 > raid_device->max_lba) + return; + + stripe_sz = raid_device->stripe_sz; + stripe_exp = raid_device->stripe_exponent; + stripe_off = v_lba & (stripe_sz - 1); + + /* Return unless IO falls within a stripe */ + if (stripe_off + io_size > stripe_sz) + return; + + num_pds = raid_device->num_pds; + p_lba = v_lba >> stripe_exp; + column = sector_div(p_lba, num_pds); + p_lba = (p_lba << stripe_exp) + stripe_off; + mpi_request->DevHandle = cpu_to_le16(raid_device->pd_handle[column]); + + if (cmd == READ_10 || cmd == WRITE_10) + put_unaligned_be32(lower_32_bits(p_lba), + &mpi_request->CDB.CDB32[2]); + else + put_unaligned_be64(p_lba, &mpi_request->CDB.CDB32[2]); + + mpt3sas_scsi_direct_io_set(ioc, smid, 1); +} diff --git a/kernel/drivers/scsi/mvsas/mv_init.c b/kernel/drivers/scsi/mvsas/mv_init.c index 53030b0e8..675e7fab0 100644 --- a/kernel/drivers/scsi/mvsas/mv_init.c +++ b/kernel/drivers/scsi/mvsas/mv_init.c @@ -56,7 +56,6 @@ static struct scsi_host_template mvs_sht = { .change_queue_depth = sas_change_queue_depth, .bios_param = sas_bios_param, .can_queue = 1, - .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, @@ -66,7 +65,6 @@ static struct scsi_host_template mvs_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = mvst_host_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -325,13 +323,9 @@ int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex) goto err_out; res_flag_ex = pci_resource_flags(pdev, bar_ex); - if (res_flag_ex & IORESOURCE_MEM) { - if (res_flag_ex & IORESOURCE_CACHEABLE) - mvi->regs_ex = ioremap(res_start, res_len); - else - mvi->regs_ex = ioremap_nocache(res_start, - res_len); - } else + if (res_flag_ex & IORESOURCE_MEM) + mvi->regs_ex = ioremap(res_start, res_len); + else mvi->regs_ex = (void *)res_start; if (!mvi->regs_ex) goto err_out; @@ -339,14 +333,14 @@ int mvs_ioremap(struct mvs_info *mvi, int bar, int bar_ex) res_start = pci_resource_start(pdev, bar); res_len = pci_resource_len(pdev, bar); - if (!res_start || !res_len) + if (!res_start || !res_len) { + iounmap(mvi->regs_ex); + mvi->regs_ex = NULL; goto err_out; + } res_flag = pci_resource_flags(pdev, bar); - if (res_flag & IORESOURCE_CACHEABLE) - mvi->regs = ioremap(res_start, res_len); - else - mvi->regs = ioremap_nocache(res_start, res_len); + mvi->regs = ioremap(res_start, res_len); if (!mvi->regs) { if (mvi->regs_ex && (res_flag_ex & IORESOURCE_MEM)) @@ -646,9 +640,9 @@ static void mvs_pci_remove(struct pci_dev *pdev) tasklet_kill(&((struct mvs_prv_info *)sha->lldd_ha)->mv_tasklet); #endif + scsi_remove_host(mvi->shost); sas_unregister_ha(sha); sas_remove_host(mvi->shost); - scsi_remove_host(mvi->shost); MVS_CHIP_DISP->interrupt_disable(mvi); free_irq(mvi->pdev->irq, sha); @@ -764,7 +758,7 @@ mvs_store_interrupt_coalescing(struct device *cdev, struct device_attribute *attr, const char *buffer, size_t size) { - int val = 0; + unsigned int val = 0; struct mvs_info *mvi = NULL; struct Scsi_Host *shost = class_to_shost(cdev); struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); @@ -772,7 +766,7 @@ mvs_store_interrupt_coalescing(struct device *cdev, if (buffer == NULL) return size; - if (sscanf(buffer, "%d", &val) != 1) + if (sscanf(buffer, "%u", &val) != 1) return -EINVAL; if (val >= 0x10000) { diff --git a/kernel/drivers/scsi/mvsas/mv_sas.c b/kernel/drivers/scsi/mvsas/mv_sas.c index 454536c49..9c780740f 100644 --- a/kernel/drivers/scsi/mvsas/mv_sas.c +++ b/kernel/drivers/scsi/mvsas/mv_sas.c @@ -887,6 +887,8 @@ static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc) static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, struct mvs_slot_info *slot, u32 slot_idx) { + if (!slot) + return; if (!slot->task) return; if (!sas_protocol_ata(task->task_proto)) diff --git a/kernel/drivers/scsi/mvumi.c b/kernel/drivers/scsi/mvumi.c index 3e6b86675..02360de6b 100644 --- a/kernel/drivers/scsi/mvumi.c +++ b/kernel/drivers/scsi/mvumi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -858,8 +859,8 @@ static void mvumi_hs_build_page(struct mvumi_hba *mhba, struct mvumi_hs_page2 *hs_page2; struct mvumi_hs_page4 *hs_page4; struct mvumi_hs_page3 *hs_page3; - struct timeval time; - unsigned int local_time; + u64 time; + u64 local_time; switch (hs_header->page_code) { case HS_PAGE_HOST_INFO: @@ -877,9 +878,8 @@ static void mvumi_hs_build_page(struct mvumi_hba *mhba, hs_page2->slot_number = 0; hs_page2->intr_level = 0; hs_page2->intr_vector = 0; - do_gettimeofday(&time); - local_time = (unsigned int) (time.tv_sec - - (sys_tz.tz_minuteswest * 60)); + time = ktime_get_real_seconds(); + local_time = (time - (sys_tz.tz_minuteswest * 60)); hs_page2->seconds_since1970 = local_time; hs_header->checksum = mvumi_calculate_checksum(hs_header, hs_header->frame_length); diff --git a/kernel/drivers/scsi/nsp32.c b/kernel/drivers/scsi/nsp32.c index c6077cefb..53c84771f 100644 --- a/kernel/drivers/scsi/nsp32.c +++ b/kernel/drivers/scsi/nsp32.c @@ -274,7 +274,6 @@ static struct scsi_host_template nsp32_template = { .can_queue = 1, .sg_tablesize = NSP32_SG_SIZE, .max_sectors = 128, - .cmd_per_lun = 1, .this_id = NSP32_HOST_SCSIID, .use_clustering = DISABLE_CLUSTERING, .eh_abort_handler = nsp32_eh_abort, diff --git a/kernel/drivers/scsi/pcmcia/nsp_cs.c b/kernel/drivers/scsi/pcmcia/nsp_cs.c index 1b6c8833a..5fb6eefc6 100644 --- a/kernel/drivers/scsi/pcmcia/nsp_cs.c +++ b/kernel/drivers/scsi/pcmcia/nsp_cs.c @@ -86,7 +86,6 @@ static struct scsi_host_template nsp_driver_template = { .can_queue = 1, .this_id = NSP_INITIATOR_ID, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/pcmcia/qlogic_stub.c b/kernel/drivers/scsi/pcmcia/qlogic_stub.c index bcaf89fe0..c670dc704 100644 --- a/kernel/drivers/scsi/pcmcia/qlogic_stub.c +++ b/kernel/drivers/scsi/pcmcia/qlogic_stub.c @@ -72,7 +72,6 @@ static struct scsi_host_template qlogicfas_driver_template = { .can_queue = 1, .this_id = -1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/pcmcia/sym53c500_cs.c b/kernel/drivers/scsi/pcmcia/sym53c500_cs.c index 155f95730..20011c8af 100644 --- a/kernel/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/kernel/drivers/scsi/pcmcia/sym53c500_cs.c @@ -680,7 +680,6 @@ static struct scsi_host_template sym53c500_driver_template = { .can_queue = 1, .this_id = 7, .sg_tablesize = 32, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = SYM53C500_shost_attrs }; diff --git a/kernel/drivers/scsi/pm8001/pm8001_defs.h b/kernel/drivers/scsi/pm8001/pm8001_defs.h index 74a4bb9af..199527dba 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_defs.h +++ b/kernel/drivers/scsi/pm8001/pm8001_defs.h @@ -49,13 +49,17 @@ enum chip_flavors { chip_8019, chip_8074, chip_8076, - chip_8077 + chip_8077, + chip_8006, + chip_8070, + chip_8072 }; enum phy_speed { PHY_SPEED_15 = 0x01, PHY_SPEED_30 = 0x02, PHY_SPEED_60 = 0x04, + PHY_SPEED_120 = 0x08, }; enum data_direction { diff --git a/kernel/drivers/scsi/pm8001/pm8001_hwi.c b/kernel/drivers/scsi/pm8001/pm8001_hwi.c index 96dcc097a..04e67a190 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_hwi.c +++ b/kernel/drivers/scsi/pm8001/pm8001_hwi.c @@ -2642,6 +2642,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) ts->resp = SAS_TASK_COMPLETE; ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; default: PM8001_IO_DBG(pm8001_ha, pm8001_printk("Unknown status 0x%x\n", status)); @@ -3263,6 +3264,10 @@ void pm8001_get_lrate_mode(struct pm8001_phy *phy, u8 link_rate) struct sas_phy *sas_phy = phy->sas_phy.phy; switch (link_rate) { + case PHY_SPEED_120: + phy->sas_phy.linkrate = SAS_LINK_RATE_12_0_GBPS; + phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_12_0_GBPS; + break; case PHY_SPEED_60: phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; diff --git a/kernel/drivers/scsi/pm8001/pm8001_init.c b/kernel/drivers/scsi/pm8001/pm8001_init.c index 65555916d..062ab34b8 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_init.c +++ b/kernel/drivers/scsi/pm8001/pm8001_init.c @@ -57,6 +57,9 @@ static const struct pm8001_chip_info pm8001_chips[] = { [chip_8074] = {0, 8, &pm8001_80xx_dispatch,}, [chip_8076] = {0, 16, &pm8001_80xx_dispatch,}, [chip_8077] = {0, 16, &pm8001_80xx_dispatch,}, + [chip_8006] = {0, 16, &pm8001_80xx_dispatch,}, + [chip_8070] = {0, 8, &pm8001_80xx_dispatch,}, + [chip_8072] = {0, 16, &pm8001_80xx_dispatch,}, }; static int pm8001_id; @@ -78,7 +81,6 @@ static struct scsi_host_template pm8001_sht = { .change_queue_depth = sas_change_queue_depth, .bios_param = sas_bios_param, .can_queue = 1, - .cmd_per_lun = 1, .this_id = -1, .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, @@ -88,7 +90,6 @@ static struct scsi_host_template pm8001_sht = { .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -480,7 +481,8 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, #ifdef PM8001_USE_TASKLET /* Tasklet for non msi-x interrupt handler */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) + || (pm8001_ha->chip_id == chip_8001)) tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet, (unsigned long)&(pm8001_ha->irq_vector[0])); else @@ -634,6 +636,11 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) payload.minor_function = 0; payload.length = 128; } + } else if ((pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) && + pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) { + payload.minor_function = 4; + payload.length = 4096; } else { payload.minor_function = 1; payload.length = 4096; @@ -660,6 +667,11 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) else if (deviceid == 0x0042) pm8001_ha->sas_addr[j] = payload.func_specific[0x010 + i]; + } else if ((pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) && + pm8001_ha->pdev->subsystem_vendor == PCI_VENDOR_ID_ATTO) { + pm8001_ha->sas_addr[j] = + payload.func_specific[0x010 + i]; } else pm8001_ha->sas_addr[j] = payload.func_specific[0x804 + i]; @@ -720,6 +732,153 @@ static int pm8001_get_phy_settings_info(struct pm8001_hba_info *pm8001_ha) return 0; } +struct pm8001_mpi3_phy_pg_trx_config { + u32 LaneLosCfg; + u32 LanePgaCfg1; + u32 LanePisoCfg1; + u32 LanePisoCfg2; + u32 LanePisoCfg3; + u32 LanePisoCfg4; + u32 LanePisoCfg5; + u32 LanePisoCfg6; + u32 LaneBctCtrl; +}; + +/** + * pm8001_get_internal_phy_settings : Retrieves the internal PHY settings + * @pm8001_ha : our adapter + * @phycfg : PHY config page to populate + */ +static +void pm8001_get_internal_phy_settings(struct pm8001_hba_info *pm8001_ha, + struct pm8001_mpi3_phy_pg_trx_config *phycfg) +{ + phycfg->LaneLosCfg = 0x00000132; + phycfg->LanePgaCfg1 = 0x00203949; + phycfg->LanePisoCfg1 = 0x000000FF; + phycfg->LanePisoCfg2 = 0xFF000001; + phycfg->LanePisoCfg3 = 0xE7011300; + phycfg->LanePisoCfg4 = 0x631C40C0; + phycfg->LanePisoCfg5 = 0xF8102036; + phycfg->LanePisoCfg6 = 0xF74A1000; + phycfg->LaneBctCtrl = 0x00FB33F8; +} + +/** + * pm8001_get_external_phy_settings : Retrieves the external PHY settings + * @pm8001_ha : our adapter + * @phycfg : PHY config page to populate + */ +static +void pm8001_get_external_phy_settings(struct pm8001_hba_info *pm8001_ha, + struct pm8001_mpi3_phy_pg_trx_config *phycfg) +{ + phycfg->LaneLosCfg = 0x00000132; + phycfg->LanePgaCfg1 = 0x00203949; + phycfg->LanePisoCfg1 = 0x000000FF; + phycfg->LanePisoCfg2 = 0xFF000001; + phycfg->LanePisoCfg3 = 0xE7011300; + phycfg->LanePisoCfg4 = 0x63349140; + phycfg->LanePisoCfg5 = 0xF8102036; + phycfg->LanePisoCfg6 = 0xF80D9300; + phycfg->LaneBctCtrl = 0x00FB33F8; +} + +/** + * pm8001_get_phy_mask : Retrieves the mask that denotes if a PHY is int/ext + * @pm8001_ha : our adapter + * @phymask : The PHY mask + */ +static +void pm8001_get_phy_mask(struct pm8001_hba_info *pm8001_ha, int *phymask) +{ + switch (pm8001_ha->pdev->subsystem_device) { + case 0x0070: /* H1280 - 8 external 0 internal */ + case 0x0072: /* H12F0 - 16 external 0 internal */ + *phymask = 0x0000; + break; + + case 0x0071: /* H1208 - 0 external 8 internal */ + case 0x0073: /* H120F - 0 external 16 internal */ + *phymask = 0xFFFF; + break; + + case 0x0080: /* H1244 - 4 external 4 internal */ + *phymask = 0x00F0; + break; + + case 0x0081: /* H1248 - 4 external 8 internal */ + *phymask = 0x0FF0; + break; + + case 0x0082: /* H1288 - 8 external 8 internal */ + *phymask = 0xFF00; + break; + + default: + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("Unknown subsystem device=0x%.04x", + pm8001_ha->pdev->subsystem_device)); + } +} + +/** + * pm8001_set_phy_settings_ven_117c_12Gb : Configure ATTO 12Gb PHY settings + * @pm8001_ha : our adapter + */ +static +int pm8001_set_phy_settings_ven_117c_12G(struct pm8001_hba_info *pm8001_ha) +{ + struct pm8001_mpi3_phy_pg_trx_config phycfg_int; + struct pm8001_mpi3_phy_pg_trx_config phycfg_ext; + int phymask = 0; + int i = 0; + + memset(&phycfg_int, 0, sizeof(phycfg_int)); + memset(&phycfg_ext, 0, sizeof(phycfg_ext)); + + pm8001_get_internal_phy_settings(pm8001_ha, &phycfg_int); + pm8001_get_external_phy_settings(pm8001_ha, &phycfg_ext); + pm8001_get_phy_mask(pm8001_ha, &phymask); + + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + if (phymask & (1 << i)) {/* Internal PHY */ + pm8001_set_phy_profile_single(pm8001_ha, i, + sizeof(phycfg_int) / sizeof(u32), + (u32 *)&phycfg_int); + + } else { /* External PHY */ + pm8001_set_phy_profile_single(pm8001_ha, i, + sizeof(phycfg_ext) / sizeof(u32), + (u32 *)&phycfg_ext); + } + } + + return 0; +} + +/** + * pm8001_configure_phy_settings : Configures PHY settings based on vendor ID. + * @pm8001_ha : our hba. + */ +static int pm8001_configure_phy_settings(struct pm8001_hba_info *pm8001_ha) +{ + switch (pm8001_ha->pdev->subsystem_vendor) { + case PCI_VENDOR_ID_ATTO: + if (pm8001_ha->pdev->device == 0x0042) /* 6Gb */ + return 0; + else + return pm8001_set_phy_settings_ven_117c_12G(pm8001_ha); + + case PCI_VENDOR_ID_ADAPTEC2: + case 0: + return 0; + + default: + return pm8001_get_phy_settings_info(pm8001_ha); + } +} + #ifdef PM8001_USE_MSIX /** * pm8001_setup_msix - enable MSI-X interrupt @@ -792,7 +951,7 @@ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) pdev = pm8001_ha->pdev; #ifdef PM8001_USE_MSIX - if (pdev->msix_cap) + if (pdev->msix_cap && pci_msi_enabled()) return pm8001_setup_msix(pm8001_ha); else { PM8001_INIT_DBG(pm8001_ha, @@ -803,6 +962,8 @@ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) intx: /* initialize the INT-X interrupt */ + pm8001_ha->irq_vector[0].irq_id = 0; + pm8001_ha->irq_vector[0].drv_inst = pm8001_ha; rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED, DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost)); return rc; @@ -902,12 +1063,9 @@ static int pm8001_pci_probe(struct pci_dev *pdev, pm8001_init_sas_add(pm8001_ha); /* phy setting support for motherboard controller */ - if (pdev->subsystem_vendor != PCI_VENDOR_ID_ADAPTEC2 && - pdev->subsystem_vendor != 0) { - rc = pm8001_get_phy_settings_info(pm8001_ha); - if (rc) - goto err_out_shost; - } + if (pm8001_configure_phy_settings(pm8001_ha)) + goto err_out_shost; + pm8001_post_sas_ha_init(shost, chip); rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); if (rc) @@ -937,10 +1095,10 @@ static void pm8001_pci_remove(struct pci_dev *pdev) struct pm8001_hba_info *pm8001_ha; int i, j; pm8001_ha = sha->lldd_ha; + scsi_remove_host(pm8001_ha->shost); sas_unregister_ha(sha); sas_remove_host(pm8001_ha->shost); list_del(&pm8001_ha->list); - scsi_remove_host(pm8001_ha->shost); PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF); PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha); @@ -956,7 +1114,8 @@ static void pm8001_pci_remove(struct pci_dev *pdev) #endif #ifdef PM8001_USE_TASKLET /* For non-msix and msix interrupts */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_kill(&pm8001_ha->tasklet[0]); else for (j = 0; j < PM8001_MAX_MSIX_VEC; j++) @@ -1005,7 +1164,8 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) #endif #ifdef PM8001_USE_TASKLET /* For non-msix and msix interrupts */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_kill(&pm8001_ha->tasklet[0]); else for (j = 0; j < PM8001_MAX_MSIX_VEC; j++) @@ -1074,7 +1234,8 @@ static int pm8001_pci_resume(struct pci_dev *pdev) goto err_out_disable; #ifdef PM8001_USE_TASKLET /* Tasklet for non msi-x interrupt handler */ - if ((!pdev->msix_cap) || (pm8001_ha->chip_id == chip_8001)) + if ((!pdev->msix_cap || !pci_msi_enabled()) || + (pm8001_ha->chip_id == chip_8001)) tasklet_init(&pm8001_ha->tasklet[0], pm8001_tasklet, (unsigned long)&(pm8001_ha->irq_vector[0])); else @@ -1087,6 +1248,19 @@ static int pm8001_pci_resume(struct pci_dev *pdev) for (i = 1; i < pm8001_ha->number_of_intr; i++) PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i); } + + /* Chip documentation for the 8070 and 8072 SPCv */ + /* states that a 500ms minimum delay is required */ + /* before issuing commands. Otherwise, the firmare */ + /* will enter an unrecoverable state. */ + + if (pm8001_ha->chip_id == chip_8070 || + pm8001_ha->chip_id == chip_8072) { + mdelay(500); + } + + /* Spin up the PHYs */ + pm8001_ha->flags = PM8001F_RUN_TIME; for (i = 0; i < pm8001_ha->chip->n_phy; i++) { pm8001_ha->phy[i].enable_completion = &completion; @@ -1108,6 +1282,8 @@ err_out_enable: */ static struct pci_device_id pm8001_pci_table[] = { { PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001 }, + { PCI_VDEVICE(PMC_Sierra, 0x8006), chip_8006 }, + { PCI_VDEVICE(ADAPTEC2, 0x8006), chip_8006 }, { PCI_VDEVICE(ATTO, 0x0042), chip_8001 }, /* Support for SPC/SPCv/SPCve controllers */ { PCI_VDEVICE(ADAPTEC2, 0x8001), chip_8001 }, @@ -1163,6 +1339,20 @@ static struct pci_device_id pm8001_pci_table[] = { PCI_VENDOR_ID_ADAPTEC2, 0x0808, 0, 0, chip_8077 }, { PCI_VENDOR_ID_ADAPTEC2, 0x8074, PCI_VENDOR_ID_ADAPTEC2, 0x0404, 0, 0, chip_8074 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0070, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0071, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0072, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0073, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8070, + PCI_VENDOR_ID_ATTO, 0x0080, 0, 0, chip_8070 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0081, 0, 0, chip_8072 }, + { PCI_VENDOR_ID_ATTO, 0x8072, + PCI_VENDOR_ID_ATTO, 0x0082, 0, 0, chip_8072 }, {} /* terminate list */ }; @@ -1218,7 +1408,7 @@ MODULE_AUTHOR("Anand Kumar Santhanam "); MODULE_AUTHOR("Sangeetha Gnanasekaran "); MODULE_AUTHOR("Nikith Ganigarakoppal "); MODULE_DESCRIPTION( - "PMC-Sierra PM8001/8081/8088/8089/8074/8076/8077 " + "PMC-Sierra PM8001/8006/8081/8088/8089/8074/8076/8077/8070/8072 " "SAS/SATA controller driver"); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/scsi/pm8001/pm8001_sas.c b/kernel/drivers/scsi/pm8001/pm8001_sas.c index b93f289b4..949198c01 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_sas.c +++ b/kernel/drivers/scsi/pm8001/pm8001_sas.c @@ -790,6 +790,7 @@ pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha, ccb->device = pm8001_dev; ccb->ccb_tag = ccb_tag; ccb->task = task; + ccb->n_elem = 0; res = PM8001_CHIP_DISP->task_abort(pm8001_ha, pm8001_dev, flag, task_tag, ccb_tag); @@ -975,19 +976,27 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) phy = sas_get_local_phy(dev); if (dev_is_sata(dev)) { - DECLARE_COMPLETION_ONSTACK(completion_setstate); if (scsi_is_sas_phy_local(phy)) { rc = 0; goto out; } rc = sas_phy_reset(phy, 1); + if (rc) { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("phy reset failed for device %x\n" + "with rc %d\n", pm8001_dev->device_id, rc)); + rc = TMF_RESP_FUNC_FAILED; + goto out; + } msleep(2000); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , dev, 1, 0); - pm8001_dev->setds_completion = &completion_setstate; - rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, - pm8001_dev, 0x01); - wait_for_completion(&completion_setstate); + if (rc) { + PM8001_EH_DBG(pm8001_ha, + pm8001_printk("task abort failed %x\n" + "with rc %d\n", pm8001_dev->device_id, rc)); + rc = TMF_RESP_FUNC_FAILED; + } } else { rc = sas_phy_reset(phy, 1); msleep(2000); diff --git a/kernel/drivers/scsi/pm8001/pm8001_sas.h b/kernel/drivers/scsi/pm8001/pm8001_sas.h index 8dd8b7840..6628cc383 100644 --- a/kernel/drivers/scsi/pm8001/pm8001_sas.h +++ b/kernel/drivers/scsi/pm8001/pm8001_sas.h @@ -58,7 +58,7 @@ #include "pm8001_defs.h" #define DRV_NAME "pm80xx" -#define DRV_VERSION "0.1.37" +#define DRV_VERSION "0.1.38" #define PM8001_FAIL_LOGGING 0x01 /* Error message logging */ #define PM8001_INIT_LOGGING 0x02 /* driver init logging */ #define PM8001_DISC_LOGGING 0x04 /* discovery layer logging */ @@ -106,7 +106,9 @@ do { \ #define DEV_IS_EXPANDER(type) ((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE)) #define IS_SPCV_12G(dev) ((dev->device == 0X8074) \ || (dev->device == 0X8076) \ - || (dev->device == 0X8077)) + || (dev->device == 0X8077) \ + || (dev->device == 0X8070) \ + || (dev->device == 0X8072)) #define PM8001_NAME_LENGTH 32/* generic length of strings */ extern struct list_head hba_list; @@ -241,7 +243,7 @@ struct pm8001_chip_info { struct pm8001_port { struct asd_sas_port sas_port; u8 port_attached; - u8 wide_port_phymap; + u16 wide_port_phymap; u8 port_state; struct list_head list; }; @@ -569,6 +571,14 @@ struct pm8001_fw_image_header { #define NCQ_READ_LOG_FLAG 0x80000000 #define NCQ_ABORT_ALL_FLAG 0x40000000 #define NCQ_2ND_RLE_FLAG 0x20000000 + +/* Device states */ +#define DS_OPERATIONAL 0x01 +#define DS_PORT_IN_RESET 0x02 +#define DS_IN_RECOVERY 0x03 +#define DS_IN_ERROR 0x04 +#define DS_NON_OPERATIONAL 0x07 + /** * brief param structure for firmware flash update. */ @@ -700,6 +710,8 @@ int pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha); int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, u32 length, u8 *buf); +void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, + u32 phy, u32 length, u32 *buf); int pm80xx_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue); ssize_t pm80xx_get_fatal_dump(struct device *cdev, struct device_attribute *attr, char *buf); diff --git a/kernel/drivers/scsi/pm8001/pm80xx_hwi.c b/kernel/drivers/scsi/pm8001/pm80xx_hwi.c index 05cce463a..eb4fee61d 100644 --- a/kernel/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/kernel/drivers/scsi/pm8001/pm80xx_hwi.c @@ -309,6 +309,9 @@ static void read_main_config_table(struct pm8001_hba_info *pm8001_ha) pm8001_mr32(address, MAIN_INT_VECTOR_TABLE_OFFSET); pm8001_ha->main_cfg_tbl.pm80xx_tbl.phy_attr_table_offset = pm8001_mr32(address, MAIN_SAS_PHY_ATTR_TABLE_OFFSET); + /* read port recover and reset timeout */ + pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer = + pm8001_mr32(address, MAIN_PORT_RECOVERY_TIMER); } /** @@ -585,6 +588,12 @@ static void update_main_config_table(struct pm8001_hba_info *pm8001_ha) pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer); pm8001_mw32(address, MAIN_INT_REASSERTION_DELAY, pm8001_ha->main_cfg_tbl.pm80xx_tbl.interrupt_reassertion_delay); + + pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer &= 0xffff0000; + pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer |= + PORT_RECOVERY_TIMEOUT; + pm8001_mw32(address, MAIN_PORT_RECOVERY_TIMER, + pm8001_ha->main_cfg_tbl.pm80xx_tbl.port_recovery_timer); } /** @@ -843,6 +852,7 @@ pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha) int rc; u32 tag; u32 opc = OPC_INB_SET_CONTROLLER_CONFIG; + u32 page_code; memset(&payload, 0, sizeof(struct set_ctrl_cfg_req)); rc = pm8001_tag_alloc(pm8001_ha, &tag); @@ -851,8 +861,14 @@ pm80xx_set_thermal_config(struct pm8001_hba_info *pm8001_ha) circularQ = &pm8001_ha->inbnd_q_tbl[0]; payload.tag = cpu_to_le32(tag); + + if (IS_SPCV_12G(pm8001_ha->pdev)) + page_code = THERMAL_PAGE_CODE_7H; + else + page_code = THERMAL_PAGE_CODE_8H; + payload.cfg_pg[0] = (THERMAL_LOG_ENABLE << 9) | - (THERMAL_ENABLE << 8) | THERMAL_OP_CODE; + (THERMAL_ENABLE << 8) | page_code; payload.cfg_pg[1] = (LTEMPHIL << 24) | (RTEMPHIL << 8); rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0); @@ -1251,6 +1267,8 @@ pm80xx_chip_soft_rst(struct pm8001_hba_info *pm8001_ha) /* check iButton feature support for motherboard controller */ if (pm8001_ha->pdev->subsystem_vendor != PCI_VENDOR_ID_ADAPTEC2 && + pm8001_ha->pdev->subsystem_vendor != + PCI_VENDOR_ID_ATTO && pm8001_ha->pdev->subsystem_vendor != 0) { ibutton0 = pm8001_cr32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_6); @@ -1593,6 +1611,13 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb) ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; break; + case IO_XFER_ERROR_INVALID_SSP_RSP_FRAME: + PM8001_IO_DBG(pm8001_ha, + pm8001_printk("IO_XFER_ERROR_INVALID_SSP_RSP_FRAME\n")); + ts->resp = SAS_TASK_COMPLETE; + ts->stat = SAS_OPEN_REJECT; + ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED: PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n")); @@ -2314,6 +2339,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb) ts->resp = SAS_TASK_COMPLETE; ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; + break; default: PM8001_IO_DBG(pm8001_ha, pm8001_printk("Unknown status 0x%x\n", status)); @@ -2829,6 +2855,32 @@ static void pm80xx_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha, static int pm80xx_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha, u32 phyId, u32 phy_op); +static void hw_event_port_recover(struct pm8001_hba_info *pm8001_ha, + void *piomb) +{ + struct hw_event_resp *pPayload = (struct hw_event_resp *)(piomb + 4); + u32 phyid_npip_portstate = le32_to_cpu(pPayload->phyid_npip_portstate); + u8 phy_id = (u8)((phyid_npip_portstate & 0xFF0000) >> 16); + u32 lr_status_evt_portid = + le32_to_cpu(pPayload->lr_status_evt_portid); + u8 deviceType = pPayload->sas_identify.dev_type; + u8 link_rate = (u8)((lr_status_evt_portid & 0xF0000000) >> 28); + struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + u8 port_id = (u8)(lr_status_evt_portid & 0x000000FF); + struct pm8001_port *port = &pm8001_ha->port[port_id]; + + if (deviceType == SAS_END_DEVICE) { + pm80xx_chip_phy_ctl_req(pm8001_ha, phy_id, + PHY_NOTIFY_ENABLE_SPINUP); + } + + port->wide_port_phymap |= (1U << phy_id); + pm8001_get_lrate_mode(phy, link_rate); + phy->sas_phy.oob_mode = SAS_OOB_MODE; + phy->phy_state = PHY_STATE_LINK_UP_SPCV; + phy->phy_attached = 1; +} + /** * hw_event_sas_phy_up -FW tells me a SAS phy up event. * @pm8001_ha: our hba card information @@ -2856,6 +2908,7 @@ hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb) unsigned long flags; u8 deviceType = pPayload->sas_identify.dev_type; port->port_state = portstate; + port->wide_port_phymap |= (1U << phy_id); phy->phy_state = PHY_STATE_LINK_UP_SPCV; PM8001_MSG_DBG(pm8001_ha, pm8001_printk( "portid:%d; phyid:%d; linkrate:%d; " @@ -2981,7 +3034,6 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb) struct pm8001_port *port = &pm8001_ha->port[port_id]; struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; port->port_state = portstate; - phy->phy_type = 0; phy->identify.device_type = 0; phy->phy_attached = 0; memset(&phy->dev_sas_addr, 0, SAS_ADDR_SIZE); @@ -2993,9 +3045,13 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb) pm8001_printk(" PortInvalid portID %d\n", port_id)); PM8001_MSG_DBG(pm8001_ha, pm8001_printk(" Last phy Down and port invalid\n")); - port->port_attached = 0; - pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, - port_id, phy_id, 0, 0); + if (phy->phy_type & PORT_TYPE_SATA) { + phy->phy_type = 0; + port->port_attached = 0; + pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + } + sas_phy_disconnected(&phy->sas_phy); break; case PORT_IN_RESET: PM8001_MSG_DBG(pm8001_ha, @@ -3003,22 +3059,26 @@ hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb) break; case PORT_NOT_ESTABLISHED: PM8001_MSG_DBG(pm8001_ha, - pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n")); + pm8001_printk(" Phy Down and PORT_NOT_ESTABLISHED\n")); port->port_attached = 0; break; case PORT_LOSTCOMM: PM8001_MSG_DBG(pm8001_ha, - pm8001_printk(" phy Down and PORT_LOSTCOMM\n")); + pm8001_printk(" Phy Down and PORT_LOSTCOMM\n")); PM8001_MSG_DBG(pm8001_ha, pm8001_printk(" Last phy Down and port invalid\n")); - port->port_attached = 0; - pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, - port_id, phy_id, 0, 0); + if (phy->phy_type & PORT_TYPE_SATA) { + port->port_attached = 0; + phy->phy_type = 0; + pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN, + port_id, phy_id, 0, 0); + } + sas_phy_disconnected(&phy->sas_phy); break; default: port->port_attached = 0; PM8001_MSG_DBG(pm8001_ha, - pm8001_printk(" phy Down and(default) = 0x%x\n", + pm8001_printk(" Phy Down and(default) = 0x%x\n", portstate)); break; @@ -3084,7 +3144,7 @@ static int mpi_thermal_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) */ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) { - unsigned long flags; + unsigned long flags, i; struct hw_event_resp *pPayload = (struct hw_event_resp *)(piomb + 4); u32 lr_status_evt_portid = @@ -3097,9 +3157,9 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) (u16)((lr_status_evt_portid & 0x00FFFF00) >> 8); u8 status = (u8)((lr_status_evt_portid & 0x0F000000) >> 24); - struct sas_ha_struct *sas_ha = pm8001_ha->sas; struct pm8001_phy *phy = &pm8001_ha->phy[phy_id]; + struct pm8001_port *port = &pm8001_ha->port[port_id]; struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; PM8001_MSG_DBG(pm8001_ha, pm8001_printk("portid:%d phyid:%d event:0x%x status:0x%x\n", @@ -3125,7 +3185,9 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) case HW_EVENT_PHY_DOWN: PM8001_MSG_DBG(pm8001_ha, pm8001_printk("HW_EVENT_PHY_DOWN\n")); - sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL); + if (phy->phy_type & PORT_TYPE_SATA) + sas_ha->notify_phy_event(&phy->sas_phy, + PHYE_LOSS_OF_SIGNAL); phy->phy_attached = 0; phy->phy_state = 0; hw_event_phy_down(pm8001_ha, piomb); @@ -3169,9 +3231,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n")); pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0); - sas_phy_disconnected(sas_phy); - phy->phy_attached = 0; - sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); break; case HW_EVENT_LINK_ERR_DISPARITY_ERROR: PM8001_MSG_DBG(pm8001_ha, @@ -3179,9 +3238,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_LINK_ERR_DISPARITY_ERROR, port_id, phy_id, 0, 0); - sas_phy_disconnected(sas_phy); - phy->phy_attached = 0; - sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); break; case HW_EVENT_LINK_ERR_CODE_VIOLATION: PM8001_MSG_DBG(pm8001_ha, @@ -3189,9 +3245,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_LINK_ERR_CODE_VIOLATION, port_id, phy_id, 0, 0); - sas_phy_disconnected(sas_phy); - phy->phy_attached = 0; - sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); break; case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH: PM8001_MSG_DBG(pm8001_ha, pm8001_printk( @@ -3199,9 +3252,6 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH, port_id, phy_id, 0, 0); - sas_phy_disconnected(sas_phy); - phy->phy_attached = 0; - sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); break; case HW_EVENT_MALFUNCTION: PM8001_MSG_DBG(pm8001_ha, @@ -3257,13 +3307,19 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void *piomb) pm80xx_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PORT_RECOVERY_TIMER_TMO, port_id, phy_id, 0, 0); - sas_phy_disconnected(sas_phy); - phy->phy_attached = 0; - sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); + for (i = 0; i < pm8001_ha->chip->n_phy; i++) { + if (port->wide_port_phymap & (1 << i)) { + phy = &pm8001_ha->phy[i]; + sas_ha->notify_phy_event(&phy->sas_phy, + PHYE_LOSS_OF_SIGNAL); + port->wide_port_phymap &= ~(1 << i); + } + } break; case HW_EVENT_PORT_RECOVER: PM8001_MSG_DBG(pm8001_ha, pm8001_printk("HW_EVENT_PORT_RECOVER\n")); + hw_event_port_recover(pm8001_ha, piomb); break; case HW_EVENT_PORT_RESET_COMPLETE: PM8001_MSG_DBG(pm8001_ha, @@ -4522,6 +4578,38 @@ void pm8001_set_phy_profile(struct pm8001_hba_info *pm8001_ha, } PM8001_INIT_DBG(pm8001_ha, pm8001_printk("phy settings completed\n")); } + +void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, + u32 phy, u32 length, u32 *buf) +{ + u32 tag, opc; + int rc, i; + struct set_phy_profile_req payload; + struct inbound_queue_table *circularQ; + + memset(&payload, 0, sizeof(payload)); + + rc = pm8001_tag_alloc(pm8001_ha, &tag); + if (rc) + PM8001_INIT_DBG(pm8001_ha, pm8001_printk("Invalid tag")); + + circularQ = &pm8001_ha->inbnd_q_tbl[0]; + opc = OPC_INB_SET_PHY_PROFILE; + + payload.tag = cpu_to_le32(tag); + payload.ppc_phyid = (((SAS_PHY_ANALOG_SETTINGS_PAGE & 0xF) << 8) + | (phy & 0xFF)); + + for (i = 0; i < length; i++) + payload.reserved[i] = cpu_to_le32(*(buf + i)); + + rc = pm8001_mpi_build_cmd(pm8001_ha, circularQ, opc, &payload, 0); + if (rc) + pm8001_tag_free(pm8001_ha, tag); + + PM8001_INIT_DBG(pm8001_ha, + pm8001_printk("PHY %d settings applied", phy)); +} const struct pm8001_dispatch pm8001_80xx_dispatch = { .name = "pmc80xx", .chip_init = pm80xx_chip_init, diff --git a/kernel/drivers/scsi/pm8001/pm80xx_hwi.h b/kernel/drivers/scsi/pm8001/pm80xx_hwi.h index 9970a3857..7a443bad6 100644 --- a/kernel/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/kernel/drivers/scsi/pm8001/pm80xx_hwi.h @@ -177,7 +177,8 @@ /* Thermal related */ #define THERMAL_ENABLE 0x1 #define THERMAL_LOG_ENABLE 0x1 -#define THERMAL_OP_CODE 0x6 +#define THERMAL_PAGE_CODE_7H 0x6 +#define THERMAL_PAGE_CODE_8H 0x7 #define LTEMPHIL 70 #define RTEMPHIL 100 @@ -1174,7 +1175,7 @@ typedef struct SASProtocolTimerConfig SASProtocolTimerConfig_t; #define IO_XFER_ERROR_INTERNAL_CRC_ERROR 0x54 #define MPI_IO_RQE_BUSY_FULL 0x55 #define IO_XFER_ERR_EOB_DATA_OVERRUN 0x56 -#define IO_XFR_ERROR_INVALID_SSP_RSP_FRAME 0x57 +#define IO_XFER_ERROR_INVALID_SSP_RSP_FRAME 0x57 #define IO_OPEN_CNX_ERROR_OPEN_PREEMPTED 0x58 #define MPI_ERR_IO_RESOURCE_UNAVAILABLE 0x1004 diff --git a/kernel/drivers/scsi/pmcraid.c b/kernel/drivers/scsi/pmcraid.c index ed31d8cc6..b2a88200f 100644 --- a/kernel/drivers/scsi/pmcraid.c +++ b/kernel/drivers/scsi/pmcraid.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -4254,7 +4255,6 @@ static struct scsi_host_template pmcraid_host_template = { .use_clustering = ENABLE_CLUSTERING, .shost_attrs = pmcraid_host_attrs, .proc_name = PMCRAID_DRIVER_NAME, - .use_blk_tags = 1, }; /* @@ -5563,11 +5563,9 @@ static void pmcraid_set_timestamp(struct pmcraid_cmd *cmd) __be32 time_stamp_len = cpu_to_be32(PMCRAID_TIMESTAMP_LEN); struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl; - struct timeval tv; __le64 timestamp; - do_gettimeofday(&tv); - timestamp = tv.tv_sec * 1000; + timestamp = ktime_get_real_seconds() * 1000; pinstance->timestamp_data->timestamp[0] = (__u8)(timestamp); pinstance->timestamp_data->timestamp[1] = (__u8)((timestamp) >> 8); diff --git a/kernel/drivers/scsi/ppa.c b/kernel/drivers/scsi/ppa.c index 1db8b2606..ee00e27ba 100644 --- a/kernel/drivers/scsi/ppa.c +++ b/kernel/drivers/scsi/ppa.c @@ -974,7 +974,6 @@ static struct scsi_host_template ppa_template = { .bios_param = ppa_biosparam, .this_id = -1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, .can_queue = 1, .slave_alloc = ppa_adjust_queue, diff --git a/kernel/drivers/scsi/ps3rom.c b/kernel/drivers/scsi/ps3rom.c index 5298def33..4924424d2 100644 --- a/kernel/drivers/scsi/ps3rom.c +++ b/kernel/drivers/scsi/ps3rom.c @@ -347,7 +347,6 @@ static struct scsi_host_template ps3rom_host_template = { .can_queue = 1, .this_id = 7, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .emulated = 1, /* only sg driver uses this */ .max_sectors = PS3ROM_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, diff --git a/kernel/drivers/scsi/qla1280.c b/kernel/drivers/scsi/qla1280.c index c68a66e8c..5d0ec42a9 100644 --- a/kernel/drivers/scsi/qla1280.c +++ b/kernel/drivers/scsi/qla1280.c @@ -4217,7 +4217,6 @@ static struct scsi_host_template qla1280_driver_template = { .can_queue = 0xfffff, .this_id = -1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/qla2xxx/Kconfig b/kernel/drivers/scsi/qla2xxx/Kconfig index 33f60c92e..a0f732b13 100644 --- a/kernel/drivers/scsi/qla2xxx/Kconfig +++ b/kernel/drivers/scsi/qla2xxx/Kconfig @@ -32,10 +32,10 @@ config SCSI_QLA_FC They are also included in the linux-firmware tree as well. config TCM_QLA2XXX - tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs" + tristate "TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs" depends on SCSI_QLA_FC && TARGET_CORE depends on LIBFC select BTREE default n ---help--- - Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs + Say Y here to enable the TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs diff --git a/kernel/drivers/scsi/qla2xxx/qla_attr.c b/kernel/drivers/scsi/qla2xxx/qla_attr.c index 82b92c414..6b942d9e5 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_attr.c +++ b/kernel/drivers/scsi/qla2xxx/qla_attr.c @@ -738,7 +738,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, ql_log(ql_log_info, vha, 0x706f, "Issuing MPI reset.\n"); - if (IS_QLA83XX(ha)) { + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { uint32_t idc_control; qla83xx_idc_lock(vha, 0); @@ -884,7 +884,6 @@ qla2x00_sysfs_read_dcbx_tlv(struct file *filp, struct kobject *kobj, struct device, kobj))); struct qla_hw_data *ha = vha->hw; int rval; - uint16_t actual_size; if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE) return 0; @@ -901,7 +900,6 @@ qla2x00_sysfs_read_dcbx_tlv(struct file *filp, struct kobject *kobj, } do_read: - actual_size = 0; memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE); rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma, @@ -1079,8 +1077,7 @@ qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - return scnprintf(buf, PAGE_SIZE, "%s\n", - vha->hw->model_desc ? vha->hw->model_desc : ""); + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_desc); } static ssize_t @@ -1348,7 +1345,8 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) && + !IS_QLA27XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", @@ -1537,6 +1535,20 @@ qla2x00_allow_cna_fw_dump_store(struct device *dev, return strlen(buf); } +static ssize_t +qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA27XX(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", + ha->pep_version[0], ha->pep_version[1], ha->pep_version[2]); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -1581,6 +1593,7 @@ static DEVICE_ATTR(fw_dump_size, S_IRUGO, qla2x00_fw_dump_size_show, NULL); static DEVICE_ATTR(allow_cna_fw_dump, S_IRUGO | S_IWUSR, qla2x00_allow_cna_fw_dump_show, qla2x00_allow_cna_fw_dump_store); +static DEVICE_ATTR(pep_version, S_IRUGO, qla2x00_pep_version_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1614,6 +1627,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_diag_megabytes, &dev_attr_fw_dump_size, &dev_attr_allow_cna_fw_dump, + &dev_attr_pep_version, NULL, }; diff --git a/kernel/drivers/scsi/qla2xxx/qla_bsg.c b/kernel/drivers/scsi/qla2xxx/qla_bsg.c index 2e2bb6f45..c26acde79 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_bsg.c +++ b/kernel/drivers/scsi/qla2xxx/qla_bsg.c @@ -405,7 +405,7 @@ done: return rval; } -inline uint16_t +static inline uint16_t qla24xx_calc_ct_iocbs(uint16_t dsds) { uint16_t iocbs; @@ -1733,7 +1733,6 @@ qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job) struct Scsi_Host *host = bsg_job->shost; scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; - uint16_t thread_id; uint32_t rval = EXT_STATUS_OK; uint16_t req_sg_cnt = 0; uint16_t rsp_sg_cnt = 0; @@ -1790,8 +1789,6 @@ qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job) goto done; } - thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; - mutex_lock(&ha->selflogin_lock); if (vha->self_login_loop_id == 0) { /* Initialize all required fields of fcport */ @@ -2174,7 +2171,6 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job) { int ret = -EINVAL; struct fc_rport *rport; - fc_port_t *fcport = NULL; struct Scsi_Host *host; scsi_qla_host_t *vha; @@ -2183,7 +2179,6 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job) if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { rport = bsg_job->rport; - fcport = *(fc_port_t **) rport->dd_data; host = rport_to_shost(rport); vha = shost_priv(host); } else { diff --git a/kernel/drivers/scsi/qla2xxx/qla_dbg.c b/kernel/drivers/scsi/qla2xxx/qla_dbg.c index e9ae6b924..34dc9a356 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_dbg.c +++ b/kernel/drivers/scsi/qla2xxx/qla_dbg.c @@ -19,14 +19,14 @@ * | Device Discovery | 0x2016 | 0x2020-0x2022, | * | | | 0x2011-0x2012, | * | | | 0x2099-0x20a4 | - * | Queue Command and IO tracing | 0x3059 | 0x300b | + * | Queue Command and IO tracing | 0x3075 | 0x300b | * | | | 0x3027-0x3028 | * | | | 0x303d-0x3041 | * | | | 0x302d,0x3033 | * | | | 0x3036,0x3038 | * | | | 0x303a | * | DPC Thread | 0x4023 | 0x4002,0x4013 | - * | Async Events | 0x5087 | 0x502b-0x502f | + * | Async Events | 0x508a | 0x502b-0x502f | * | | | 0x5047 | * | | | 0x5084,0x5075 | * | | | 0x503d,0x5044 | @@ -67,10 +67,10 @@ * | | | 0xd031-0xd0ff | * | | | 0xd101-0xd1fe | * | | | 0xd214-0xd2fe | - * | Target Mode | 0xe079 | | - * | Target Mode Management | 0xf080 | 0xf002 | + * | Target Mode | 0xe080 | | + * | Target Mode Management | 0xf096 | 0xf002 | * | | | 0xf046-0xf049 | - * | Target Mode Task Management | 0x1000b | | + * | Target Mode Task Management | 0x1000d | | * ---------------------------------------------------------------------- */ @@ -117,7 +117,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, { int rval; uint32_t cnt, stat, timer, dwords, idx; - uint16_t mb0, mb1; + uint16_t mb0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; dma_addr_t dump_dma = ha->gid_list_dma; uint32_t *dump = (uint32_t *)ha->gid_list; @@ -161,7 +161,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, &ha->mbx_cmd_flags); mb0 = RD_REG_WORD(®->mailbox0); - mb1 = RD_REG_WORD(®->mailbox1); + RD_REG_WORD(®->mailbox1); WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); @@ -486,7 +486,7 @@ qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) return ptr; *last_chain = &fcec->type; - fcec->type = __constant_htonl(DUMP_CHAIN_FCE); + fcec->type = htonl(DUMP_CHAIN_FCE); fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) + fce_calc_size(ha->fce_bufs)); fcec->size = htonl(fce_calc_size(ha->fce_bufs)); @@ -527,7 +527,7 @@ qla2xxx_copy_atioqueues(struct qla_hw_data *ha, void *ptr, /* aqp = ha->atio_q_map[que]; */ q = ptr; *last_chain = &q->type; - q->type = __constant_htonl(DUMP_CHAIN_QUEUE); + q->type = htonl(DUMP_CHAIN_QUEUE); q->chain_size = htonl( sizeof(struct qla2xxx_mqueue_chain) + sizeof(struct qla2xxx_mqueue_header) + @@ -536,7 +536,7 @@ qla2xxx_copy_atioqueues(struct qla_hw_data *ha, void *ptr, /* Add header. */ qh = ptr; - qh->queue = __constant_htonl(TYPE_ATIO_QUEUE); + qh->queue = htonl(TYPE_ATIO_QUEUE); qh->number = htonl(que); qh->size = htonl(aqp->length * sizeof(request_t)); ptr += sizeof(struct qla2xxx_mqueue_header); @@ -571,7 +571,7 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) /* Add chain. */ q = ptr; *last_chain = &q->type; - q->type = __constant_htonl(DUMP_CHAIN_QUEUE); + q->type = htonl(DUMP_CHAIN_QUEUE); q->chain_size = htonl( sizeof(struct qla2xxx_mqueue_chain) + sizeof(struct qla2xxx_mqueue_header) + @@ -580,7 +580,7 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) /* Add header. */ qh = ptr; - qh->queue = __constant_htonl(TYPE_REQUEST_QUEUE); + qh->queue = htonl(TYPE_REQUEST_QUEUE); qh->number = htonl(que); qh->size = htonl(req->length * sizeof(request_t)); ptr += sizeof(struct qla2xxx_mqueue_header); @@ -599,7 +599,7 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) /* Add chain. */ q = ptr; *last_chain = &q->type; - q->type = __constant_htonl(DUMP_CHAIN_QUEUE); + q->type = htonl(DUMP_CHAIN_QUEUE); q->chain_size = htonl( sizeof(struct qla2xxx_mqueue_chain) + sizeof(struct qla2xxx_mqueue_header) + @@ -608,7 +608,7 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) /* Add header. */ qh = ptr; - qh->queue = __constant_htonl(TYPE_RESPONSE_QUEUE); + qh->queue = htonl(TYPE_RESPONSE_QUEUE); qh->number = htonl(que); qh->size = htonl(rsp->length * sizeof(response_t)); ptr += sizeof(struct qla2xxx_mqueue_header); @@ -627,15 +627,15 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) uint32_t cnt, que_idx; uint8_t que_cnt; struct qla2xxx_mq_chain *mq = ptr; - device_reg_t __iomem *reg; + device_reg_t *reg; if (!ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) return ptr; mq = ptr; *last_chain = &mq->type; - mq->type = __constant_htonl(DUMP_CHAIN_MQ); - mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain)); + mq->type = htonl(DUMP_CHAIN_MQ); + mq->chain_size = htonl(sizeof(struct qla2xxx_mq_chain)); que_cnt = ha->max_req_queues > ha->max_rsp_queues ? ha->max_req_queues : ha->max_rsp_queues; @@ -695,8 +695,10 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked) flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd002, @@ -832,8 +834,12 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked) qla2xxx_dump_post_process(base_vha, rval); qla2300_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } /** @@ -859,8 +865,10 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked) mb0 = mb2 = 0; flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd004, @@ -1030,8 +1038,12 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked) qla2xxx_dump_post_process(base_vha, rval); qla2100_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } void @@ -1039,7 +1051,6 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; - uint32_t risc_address; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; @@ -1047,7 +1058,6 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) uint16_t __iomem *mbx_reg; unsigned long flags; struct qla24xx_fw_dump *fw; - uint32_t ext_mem_cnt; void *nxt; void *nxt_chain; uint32_t *last_chain = NULL; @@ -1056,12 +1066,13 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) if (IS_P3P_TYPE(ha)) return; - risc_address = ext_mem_cnt = 0; flags = 0; ha->fw_dump_cap_flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd006, @@ -1274,8 +1285,8 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt_chain = (void *)ha->fw_dump + ha->chain_offset; nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain); if (last_chain) { - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); - *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + ha->fw_dump->version |= htonl(DUMP_CHAIN_VARIANT); + *last_chain |= htonl(DUMP_CHAIN_LAST); } /* Adjust valid length. */ @@ -1285,8 +1296,12 @@ qla24xx_fw_dump_failed_0: qla2xxx_dump_post_process(base_vha, rval); qla24xx_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } void @@ -1294,7 +1309,6 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; - uint32_t risc_address; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; @@ -1302,17 +1316,17 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) uint16_t __iomem *mbx_reg; unsigned long flags; struct qla25xx_fw_dump *fw; - uint32_t ext_mem_cnt; void *nxt, *nxt_chain; uint32_t *last_chain = NULL; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - risc_address = ext_mem_cnt = 0; flags = 0; ha->fw_dump_cap_flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd008, @@ -1329,7 +1343,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) } fw = &ha->fw_dump->isp.isp25; qla2xxx_prep_dump(ha, ha->fw_dump); - ha->fw_dump->version = __constant_htonl(2); + ha->fw_dump->version = htonl(2); fw->host_status = htonl(RD_REG_DWORD(®->host_status)); @@ -1593,8 +1607,8 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain); if (last_chain) { - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); - *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + ha->fw_dump->version |= htonl(DUMP_CHAIN_VARIANT); + *last_chain |= htonl(DUMP_CHAIN_LAST); } /* Adjust valid length. */ @@ -1604,8 +1618,12 @@ qla25xx_fw_dump_failed_0: qla2xxx_dump_post_process(base_vha, rval); qla25xx_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } void @@ -1613,7 +1631,6 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; uint32_t cnt; - uint32_t risc_address; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; @@ -1621,17 +1638,17 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) uint16_t __iomem *mbx_reg; unsigned long flags; struct qla81xx_fw_dump *fw; - uint32_t ext_mem_cnt; void *nxt, *nxt_chain; uint32_t *last_chain = NULL; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - risc_address = ext_mem_cnt = 0; flags = 0; ha->fw_dump_cap_flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd00a, @@ -1914,8 +1931,8 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain); if (last_chain) { - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); - *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + ha->fw_dump->version |= htonl(DUMP_CHAIN_VARIANT); + *last_chain |= htonl(DUMP_CHAIN_LAST); } /* Adjust valid length. */ @@ -1925,16 +1942,19 @@ qla81xx_fw_dump_failed_0: qla2xxx_dump_post_process(base_vha, rval); qla81xx_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } void qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) { int rval; - uint32_t cnt, reg_data; - uint32_t risc_address; + uint32_t cnt; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; uint32_t __iomem *dmp_reg; @@ -1942,17 +1962,17 @@ qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) uint16_t __iomem *mbx_reg; unsigned long flags; struct qla83xx_fw_dump *fw; - uint32_t ext_mem_cnt; void *nxt, *nxt_chain; uint32_t *last_chain = NULL; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - risc_address = ext_mem_cnt = 0; flags = 0; ha->fw_dump_cap_flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif if (!ha->fw_dump) { ql_log(ql_log_warn, vha, 0xd00c, @@ -1979,16 +1999,16 @@ qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) WRT_REG_DWORD(®->iobase_addr, 0x6000); dmp_reg = ®->iobase_window; - reg_data = RD_REG_DWORD(dmp_reg); + RD_REG_DWORD(dmp_reg); WRT_REG_DWORD(dmp_reg, 0); dmp_reg = ®->unused_4_1[0]; - reg_data = RD_REG_DWORD(dmp_reg); + RD_REG_DWORD(dmp_reg); WRT_REG_DWORD(dmp_reg, 0); WRT_REG_DWORD(®->iobase_addr, 0x6010); dmp_reg = ®->unused_4_1[2]; - reg_data = RD_REG_DWORD(dmp_reg); + RD_REG_DWORD(dmp_reg); WRT_REG_DWORD(dmp_reg, 0); /* select PCR and disable ecc checking and correction */ @@ -2420,8 +2440,8 @@ copy_queue: nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); nxt_chain = qla2xxx_copy_atioqueues(ha, nxt_chain, &last_chain); if (last_chain) { - ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT); - *last_chain |= __constant_htonl(DUMP_CHAIN_LAST); + ha->fw_dump->version |= htonl(DUMP_CHAIN_VARIANT); + *last_chain |= htonl(DUMP_CHAIN_LAST); } /* Adjust valid length. */ @@ -2431,8 +2451,12 @@ qla83xx_fw_dump_failed_0: qla2xxx_dump_post_process(base_vha, rval); qla83xx_fw_dump_failed: +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#else + ; +#endif } /****************************************************************************/ diff --git a/kernel/drivers/scsi/qla2xxx/qla_def.h b/kernel/drivers/scsi/qla2xxx/qla_def.h index e86201d3b..388d79088 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_def.h +++ b/kernel/drivers/scsi/qla2xxx/qla_def.h @@ -274,6 +274,7 @@ #define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/ struct req_que; +struct qla_tgt_sess; /* * (sd.h is not exported, hence local inclusion) @@ -2026,6 +2027,7 @@ typedef struct fc_port { uint16_t port_id; unsigned long retry_delay_timestamp; + struct qla_tgt_sess *tgt_session; } fc_port_t; #include "qla_mr.h" @@ -3059,6 +3061,7 @@ struct qla_hw_data { #define PCI_DEVICE_ID_QLOGIC_ISP2031 0x2031 #define PCI_DEVICE_ID_QLOGIC_ISP2071 0x2071 #define PCI_DEVICE_ID_QLOGIC_ISP2271 0x2271 +#define PCI_DEVICE_ID_QLOGIC_ISP2261 0x2261 uint32_t device_type; #define DT_ISP2100 BIT_0 @@ -3082,7 +3085,8 @@ struct qla_hw_data { #define DT_ISP8044 BIT_18 #define DT_ISP2071 BIT_19 #define DT_ISP2271 BIT_20 -#define DT_ISP_LAST (DT_ISP2271 << 1) +#define DT_ISP2261 BIT_21 +#define DT_ISP_LAST (DT_ISP2261 << 1) #define DT_T10_PI BIT_25 #define DT_IIDMA BIT_26 @@ -3114,6 +3118,7 @@ struct qla_hw_data { #define IS_QLAFX00(ha) (DT_MASK(ha) & DT_ISPFX00) #define IS_QLA2071(ha) (DT_MASK(ha) & DT_ISP2071) #define IS_QLA2271(ha) (DT_MASK(ha) & DT_ISP2271) +#define IS_QLA2261(ha) (DT_MASK(ha) & DT_ISP2261) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) @@ -3122,7 +3127,7 @@ struct qla_hw_data { #define IS_QLA25XX(ha) (IS_QLA2532(ha)) #define IS_QLA83XX(ha) (IS_QLA2031(ha) || IS_QLA8031(ha)) #define IS_QLA84XX(ha) (IS_QLA8432(ha)) -#define IS_QLA27XX(ha) (IS_QLA2071(ha) || IS_QLA2271(ha)) +#define IS_QLA27XX(ha) (IS_QLA2071(ha) || IS_QLA2271(ha) || IS_QLA2261(ha)) #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) #define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \ @@ -3154,16 +3159,17 @@ struct qla_hw_data { /* Bit 21 of fw_attributes decides the MCTP capabilities */ #define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ ((ha)->fw_attributes_ext[0] & BIT_0)) -#define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha)) -#define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) +#define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) -#define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha)) +#define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \ (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22)) -#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) #define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length) #define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha)) #define IS_DPORT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) +#define IS_FAWWN_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha)) /* HBA serial number */ uint8_t serial0; @@ -3286,6 +3292,7 @@ struct qla_hw_data { uint8_t mpi_version[3]; uint32_t mpi_capabilities; uint8_t phy_version[3]; + uint8_t pep_version[3]; /* Firmware dump template */ void *fw_dump_template; @@ -3418,9 +3425,9 @@ struct qla_hw_data { mempool_t *ctx_mempool; #define FCP_CMND_DMA_POOL_SIZE 512 - unsigned long nx_pcibase; /* Base I/O address */ - uint8_t *nxdb_rd_ptr; /* Doorbell read pointer */ - unsigned long nxdb_wr_ptr; /* Door bell write pointer */ + void __iomem *nx_pcibase; /* Base I/O address */ + void __iomem *nxdb_rd_ptr; /* Doorbell read pointer */ + void __iomem *nxdb_wr_ptr; /* Door bell write pointer */ uint32_t crb_win; uint32_t curr_window; @@ -3579,6 +3586,16 @@ typedef struct scsi_qla_host { uint16_t fcoe_fcf_idx; uint8_t fcoe_vn_port_mac[6]; + /* list of commands waiting on workqueue */ + struct list_head qla_cmd_list; + struct list_head qla_sess_op_cmd_list; + spinlock_t cmd_list_lock; + + /* Counter to detect races between ELS and RSCN events */ + atomic_t generation_tick; + /* Time when global fcport update has been scheduled */ + int total_fcport_update_gen; + uint32_t vp_abort_cnt; struct fc_vport *fc_vport; /* holds fc_vport * for each vport */ diff --git a/kernel/drivers/scsi/qla2xxx/qla_gs.c b/kernel/drivers/scsi/qla2xxx/qla_gs.c index dccc4dcc3..94e8a8592 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_gs.c +++ b/kernel/drivers/scsi/qla2xxx/qla_gs.c @@ -35,10 +35,10 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ms_pkt->entry_type = MS_IOCB_TYPE; ms_pkt->entry_count = 1; SET_TARGET_ID(ha, ms_pkt->loop_id, SIMPLE_NAME_SERVER); - ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); + ms_pkt->control_flags = cpu_to_le16(CF_READ | CF_HEAD_TAG); ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); - ms_pkt->total_dsd_count = __constant_cpu_to_le16(2); + ms_pkt->cmd_dsd_count = cpu_to_le16(1); + ms_pkt->total_dsd_count = cpu_to_le16(2); ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); ms_pkt->req_bytecount = cpu_to_le32(req_size); @@ -74,10 +74,10 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ct_pkt->entry_type = CT_IOCB_TYPE; ct_pkt->entry_count = 1; - ct_pkt->nport_handle = __constant_cpu_to_le16(NPH_SNS); + ct_pkt->nport_handle = cpu_to_le16(NPH_SNS); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); - ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); ct_pkt->cmd_byte_count = cpu_to_le32(req_size); @@ -142,7 +142,7 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, case CS_DATA_UNDERRUN: case CS_DATA_OVERRUN: /* Overrun? */ if (ct_rsp->header.response != - __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) { + cpu_to_be16(CT_ACCEPT_RESPONSE)) { ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2077, "%s failed rejected request on port_id: %02x%02x%02x Compeltion status 0x%x, response 0x%x\n", routine, vha->d_id.b.domain, @@ -1153,10 +1153,10 @@ qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, ms_pkt->entry_type = MS_IOCB_TYPE; ms_pkt->entry_count = 1; SET_TARGET_ID(ha, ms_pkt->loop_id, vha->mgmt_svr_loop_id); - ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); + ms_pkt->control_flags = cpu_to_le16(CF_READ | CF_HEAD_TAG); ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); - ms_pkt->total_dsd_count = __constant_cpu_to_le16(2); + ms_pkt->cmd_dsd_count = cpu_to_le16(1); + ms_pkt->total_dsd_count = cpu_to_le16(2); ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); ms_pkt->req_bytecount = cpu_to_le32(req_size); @@ -1193,8 +1193,8 @@ qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, ct_pkt->entry_count = 1; ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); - ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); ct_pkt->cmd_byte_count = cpu_to_le32(req_size); @@ -1281,19 +1281,19 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Prepare FDMI command arguments -- attribute block, attributes. */ memcpy(ct_req->req.rhba.hba_identifier, vha->port_name, WWN_SIZE); - ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1); + ct_req->req.rhba.entry_count = cpu_to_be32(1); memcpy(ct_req->req.rhba.port_name, vha->port_name, WWN_SIZE); size = 2 * WWN_SIZE + 4 + 4; /* Attributes */ ct_req->req.rhba.attrs.count = - __constant_cpu_to_be32(FDMI_HBA_ATTR_COUNT); + cpu_to_be32(FDMI_HBA_ATTR_COUNT); entries = ct_req->req.rhba.hba_identifier; /* Nodename. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME); - eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE); + eiter->type = cpu_to_be16(FDMI_HBA_NODE_NAME); + eiter->len = cpu_to_be16(4 + WWN_SIZE); memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE); size += 4 + WWN_SIZE; @@ -1302,7 +1302,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Manufacturer. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_MANUFACTURER); + eiter->type = cpu_to_be16(FDMI_HBA_MANUFACTURER); alen = strlen(QLA2XXX_MANUFACTURER); snprintf(eiter->a.manufacturer, sizeof(eiter->a.manufacturer), "%s", "QLogic Corporation"); @@ -1315,7 +1315,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Serial number. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_SERIAL_NUMBER); + eiter->type = cpu_to_be16(FDMI_HBA_SERIAL_NUMBER); if (IS_FWI2_CAPABLE(ha)) qla2xxx_get_vpd_field(vha, "SN", eiter->a.serial_num, sizeof(eiter->a.serial_num)); @@ -1335,7 +1335,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Model name. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL); + eiter->type = cpu_to_be16(FDMI_HBA_MODEL); snprintf(eiter->a.model, sizeof(eiter->a.model), "%s", ha->model_number); alen = strlen(eiter->a.model); @@ -1348,7 +1348,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Model description. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION); + eiter->type = cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION); snprintf(eiter->a.model_desc, sizeof(eiter->a.model_desc), "%s", ha->model_desc); alen = strlen(eiter->a.model_desc); @@ -1361,7 +1361,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Hardware version. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_HARDWARE_VERSION); + eiter->type = cpu_to_be16(FDMI_HBA_HARDWARE_VERSION); if (!IS_FWI2_CAPABLE(ha)) { snprintf(eiter->a.hw_version, sizeof(eiter->a.hw_version), "HW:%s", ha->adapter_id); @@ -1385,7 +1385,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Driver version. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_DRIVER_VERSION); + eiter->type = cpu_to_be16(FDMI_HBA_DRIVER_VERSION); snprintf(eiter->a.driver_version, sizeof(eiter->a.driver_version), "%s", qla2x00_version_str); alen = strlen(eiter->a.driver_version); @@ -1398,7 +1398,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Option ROM version. */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_OPTION_ROM_VERSION); + eiter->type = cpu_to_be16(FDMI_HBA_OPTION_ROM_VERSION); snprintf(eiter->a.orom_version, sizeof(eiter->a.orom_version), "%d.%02d", ha->bios_revision[1], ha->bios_revision[0]); alen = strlen(eiter->a.orom_version); @@ -1411,7 +1411,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) /* Firmware version */ eiter = entries + size; - eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION); + eiter->type = cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION); ha->isp_ops->fw_version_str(vha, eiter->a.fw_version, sizeof(eiter->a.fw_version)); alen = strlen(eiter->a.fw_version); @@ -2484,8 +2484,8 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size, ct_pkt->entry_count = 1; ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); - ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); ct_pkt->cmd_byte_count = cpu_to_le32(req_size); diff --git a/kernel/drivers/scsi/qla2xxx/qla_init.c b/kernel/drivers/scsi/qla2xxx/qla_init.c index 998498e23..e197c6f39 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_init.c +++ b/kernel/drivers/scsi/qla2xxx/qla_init.c @@ -115,6 +115,8 @@ qla2x00_async_iocb_timeout(void *data) QLA_LOGIO_LOGIN_RETRIED : 0; qla2x00_post_async_login_done_work(fcport->vha, fcport, lio->u.logio.data); + } else if (sp->type == SRB_LOGOUT_CMD) { + qlt_logo_completion_handler(fcport, QLA_FUNCTION_TIMEOUT); } } @@ -497,7 +499,10 @@ void qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { - qla2x00_mark_device_lost(vha, fcport, 1, 0); + /* Don't re-login in target mode */ + if (!fcport->tgt_session) + qla2x00_mark_device_lost(vha, fcport, 1, 0); + qlt_logo_completion_handler(fcport, data[0]); return; } @@ -708,7 +713,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x00d4, "Unable to initialize ISP84XX.\n"); - qla84xx_put_chip(vha); + qla84xx_put_chip(vha); } } @@ -1127,7 +1132,7 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) unsigned long flags = 0; struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - uint32_t cnt, d2; + uint32_t cnt; uint16_t wd; static int abts_cnt; /* ISP abort retry counts */ int rval = QLA_SUCCESS; @@ -1159,7 +1164,7 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) udelay(100); /* Wait for firmware to complete NVRAM accesses. */ - d2 = (uint32_t) RD_REG_WORD(®->mailbox0); + RD_REG_WORD(®->mailbox0); for (cnt = 10000; RD_REG_WORD(®->mailbox0) != 0 && rval == QLA_SUCCESS; cnt--) { barrier(); @@ -1178,7 +1183,7 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) RD_REG_DWORD(®->mailbox0)); /* Wait for soft-reset to complete. */ - d2 = RD_REG_DWORD(®->ctrl_status); + RD_REG_DWORD(®->ctrl_status); for (cnt = 0; cnt < 6000000; cnt++) { barrier(); if ((RD_REG_DWORD(®->ctrl_status) & @@ -1221,7 +1226,7 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); RD_REG_DWORD(®->hccr); - d2 = (uint32_t) RD_REG_WORD(®->mailbox0); + RD_REG_WORD(®->mailbox0); for (cnt = 6000000; RD_REG_WORD(®->mailbox0) != 0 && rval == QLA_SUCCESS; cnt--) { barrier(); @@ -1272,16 +1277,19 @@ qla25xx_write_risc_sema_reg(scsi_qla_host_t *vha, uint32_t data) static void qla25xx_manipulate_risc_semaphore(scsi_qla_host_t *vha) { - struct qla_hw_data *ha = vha->hw; uint32_t wd32 = 0; uint delta_msec = 100; uint elapsed_msec = 0; uint timeout_msec; ulong n; - if (!IS_QLA25XX(ha) && !IS_QLA2031(ha)) + if (vha->hw->pdev->subsystem_device != 0x0175 && + vha->hw->pdev->subsystem_device != 0x0240) return; + WRT_REG_DWORD(&vha->hw->iobase->isp24.hccr, HCCRX_SET_RISC_PAUSE); + udelay(100); + attempt: timeout_msec = TIMEOUT_SEMAPHORE; n = timeout_msec / delta_msec; @@ -1538,7 +1546,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) mem_size = (ha->fw_memory_size - 0x11000 + 1) * sizeof(uint16_t); } else if (IS_FWI2_CAPABLE(ha)) { - if (IS_QLA83XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem); else if (IS_QLA81XX(ha)) fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem); @@ -1550,7 +1558,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) mem_size = (ha->fw_memory_size - 0x100000 + 1) * sizeof(uint32_t); if (ha->mqenable) { - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) mq_size = sizeof(struct qla2xxx_mq_chain); /* * Allocate maximum buffer size for all queues. @@ -1685,7 +1693,7 @@ allocate: ha->fw_dump->signature[1] = 'L'; ha->fw_dump->signature[2] = 'G'; ha->fw_dump->signature[3] = 'C'; - ha->fw_dump->version = __constant_htonl(1); + ha->fw_dump->version = htonl(1); ha->fw_dump->fixed_size = htonl(fixed_size); ha->fw_dump->mem_size = htonl(mem_size); @@ -2065,8 +2073,8 @@ qla2x00_config_rings(struct scsi_qla_host *vha) struct rsp_que *rsp = ha->rsp_q_map[0]; /* Setup ring parameters in initialization control block. */ - ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0); - ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0); + ha->init_cb->request_q_outpointer = cpu_to_le16(0); + ha->init_cb->response_q_inpointer = cpu_to_le16(0); ha->init_cb->request_q_length = cpu_to_le16(req->length); ha->init_cb->response_q_length = cpu_to_le16(rsp->length); ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); @@ -2085,7 +2093,7 @@ void qla24xx_config_rings(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; - device_reg_t __iomem *reg = ISP_QUE_REG(ha, 0); + device_reg_t *reg = ISP_QUE_REG(ha, 0); struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp; struct qla_msix_entry *msix; struct init_cb_24xx *icb; @@ -2095,8 +2103,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha) /* Setup ring parameters in initialization control block. */ icb = (struct init_cb_24xx *)ha->init_cb; - icb->request_q_outpointer = __constant_cpu_to_le16(0); - icb->response_q_inpointer = __constant_cpu_to_le16(0); + icb->request_q_outpointer = cpu_to_le16(0); + icb->response_q_inpointer = cpu_to_le16(0); icb->request_q_length = cpu_to_le16(req->length); icb->response_q_length = cpu_to_le16(rsp->length); icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); @@ -2105,18 +2113,17 @@ qla24xx_config_rings(struct scsi_qla_host *vha) icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); /* Setup ATIO queue dma pointers for target mode */ - icb->atio_q_inpointer = __constant_cpu_to_le16(0); + icb->atio_q_inpointer = cpu_to_le16(0); icb->atio_q_length = cpu_to_le16(ha->tgt.atio_q_length); icb->atio_q_address[0] = cpu_to_le32(LSD(ha->tgt.atio_dma)); icb->atio_q_address[1] = cpu_to_le32(MSD(ha->tgt.atio_dma)); if (IS_SHADOW_REG_CAPABLE(ha)) - icb->firmware_options_2 |= - __constant_cpu_to_le32(BIT_30|BIT_29); + icb->firmware_options_2 |= cpu_to_le32(BIT_30|BIT_29); if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) { - icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS); - icb->rid = __constant_cpu_to_le16(rid); + icb->qos = cpu_to_le16(QLA_DEFAULT_QUE_QOS); + icb->rid = cpu_to_le16(rid); if (ha->flags.msix_enabled) { msix = &ha->msix_entries[1]; ql_dbg(ql_dbg_init, vha, 0x00fd, @@ -2126,26 +2133,22 @@ qla24xx_config_rings(struct scsi_qla_host *vha) } /* Use alternate PCI bus number */ if (MSB(rid)) - icb->firmware_options_2 |= - __constant_cpu_to_le32(BIT_19); + icb->firmware_options_2 |= cpu_to_le32(BIT_19); /* Use alternate PCI devfn */ if (LSB(rid)) - icb->firmware_options_2 |= - __constant_cpu_to_le32(BIT_18); + icb->firmware_options_2 |= cpu_to_le32(BIT_18); /* Use Disable MSIX Handshake mode for capable adapters */ if ((ha->fw_attributes & BIT_6) && (IS_MSIX_NACK_CAPABLE(ha)) && (ha->flags.msix_enabled)) { - icb->firmware_options_2 &= - __constant_cpu_to_le32(~BIT_22); + icb->firmware_options_2 &= cpu_to_le32(~BIT_22); ha->flags.disable_msix_handshake = 1; ql_dbg(ql_dbg_init, vha, 0x00fe, "MSIX Handshake Disable Mode turned on.\n"); } else { - icb->firmware_options_2 |= - __constant_cpu_to_le32(BIT_22); + icb->firmware_options_2 |= cpu_to_le32(BIT_22); } - icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23); + icb->firmware_options_2 |= cpu_to_le32(BIT_23); WRT_REG_DWORD(®->isp25mq.req_q_in, 0); WRT_REG_DWORD(®->isp25mq.req_q_out, 0); @@ -2189,7 +2192,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) /* Clear outstanding commands array. */ for (que = 0; que < ha->max_req_queues; que++) { req = ha->req_q_map[que]; - if (!req) + if (!req || !test_bit(que, ha->req_qid_map)) continue; req->out_ptr = (void *)(req->ring + req->length); *req->out_ptr = 0; @@ -2206,7 +2209,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) for (que = 0; que < ha->max_rsp_queues; que++) { rsp = ha->rsp_q_map[que]; - if (!rsp) + if (!rsp || !test_bit(que, ha->rsp_qid_map)) continue; rsp->in_ptr = (void *)(rsp->ring + rsp->length); *rsp->in_ptr = 0; @@ -2243,7 +2246,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) } if (IS_FWI2_CAPABLE(ha)) { - mid_init_cb->options = __constant_cpu_to_le16(BIT_1); + mid_init_cb->options = cpu_to_le16(BIT_1); mid_init_cb->init_cb.execution_throttle = cpu_to_le16(ha->fw_xcb_count); /* D-Port Status */ @@ -2672,8 +2675,8 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) nv->frame_payload_size = 1024; } - nv->max_iocb_allocation = __constant_cpu_to_le16(256); - nv->execution_throttle = __constant_cpu_to_le16(16); + nv->max_iocb_allocation = cpu_to_le16(256); + nv->execution_throttle = cpu_to_le16(16); nv->retry_count = 8; nv->retry_delay = 1; @@ -2691,7 +2694,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) nv->host_p[1] = BIT_2; nv->reset_delay = 5; nv->port_down_retry_count = 8; - nv->max_luns_per_target = __constant_cpu_to_le16(8); + nv->max_luns_per_target = cpu_to_le16(8); nv->link_down_timeout = 60; rval = 1; @@ -2819,7 +2822,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) memcpy(vha->node_name, icb->node_name, WWN_SIZE); memcpy(vha->port_name, icb->port_name, WWN_SIZE); - icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); + icb->execution_throttle = cpu_to_le16(0xFFFF); ha->retry_count = nv->retry_count; @@ -2871,10 +2874,10 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) if (ql2xloginretrycount) ha->login_retry_count = ql2xloginretrycount; - icb->lun_enables = __constant_cpu_to_le16(0); + icb->lun_enables = cpu_to_le16(0); icb->command_resource_count = 0; icb->immediate_notify_resource_count = 0; - icb->timeout = __constant_cpu_to_le16(0); + icb->timeout = cpu_to_le16(0); if (IS_QLA2100(ha) || IS_QLA2200(ha)) { /* Enable RIO */ @@ -2922,24 +2925,14 @@ qla2x00_rport_del(void *data) { fc_port_t *fcport = data; struct fc_rport *rport; - scsi_qla_host_t *vha = fcport->vha; unsigned long flags; - unsigned long vha_flags; spin_lock_irqsave(fcport->vha->host->host_lock, flags); rport = fcport->drport ? fcport->drport: fcport->rport; fcport->drport = NULL; spin_unlock_irqrestore(fcport->vha->host->host_lock, flags); - if (rport) { + if (rport) fc_remote_port_delete(rport); - /* - * Release the target mode FC NEXUS in qla_target.c code - * if target mod is enabled. - */ - spin_lock_irqsave(&vha->hw->hardware_lock, vha_flags); - qlt_fc_port_deleted(vha, fcport); - spin_unlock_irqrestore(&vha->hw->hardware_lock, vha_flags); - } } /** @@ -3345,8 +3338,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) if (IS_QLAFX00(vha->hw)) { qla2x00_set_fcport_state(fcport, FCS_ONLINE); - qla2x00_reg_remote_port(vha, fcport); - return; + goto reg_port; } fcport->login_retry = 0; fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); @@ -3354,7 +3346,16 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_iidma_fcport(vha, fcport); qla24xx_update_fcport_fcp_prio(vha, fcport); - qla2x00_reg_remote_port(vha, fcport); + +reg_port: + if (qla_ini_mode_enabled(vha)) + qla2x00_reg_remote_port(vha, fcport); + else { + /* + * Create target mode FC NEXUS in qla_target.c + */ + qlt_fc_port_added(vha, fcport); + } } /* @@ -3379,6 +3380,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) LIST_HEAD(new_fcports); struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + int discovery_gen; /* If FL port exists, then SNS is present */ if (IS_FWI2_CAPABLE(ha)) @@ -3449,6 +3451,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) fcport->scan_state = QLA_FCPORT_SCAN; } + /* Mark the time right before querying FW for connected ports. + * This process is long, asynchronous and by the time it's done, + * collected information might not be accurate anymore. E.g. + * disconnected port might have re-connected and a brand new + * session has been created. In this case session's generation + * will be newer than discovery_gen. */ + qlt_do_generation_tick(vha, &discovery_gen); + rval = qla2x00_find_all_fabric_devs(vha, &new_fcports); if (rval != QLA_SUCCESS) break; @@ -3500,7 +3510,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) atomic_read(&fcport->state), fcport->flags, fcport->fc4_type, fcport->scan_state); - qlt_fc_port_deleted(vha, fcport); + qlt_fc_port_deleted(vha, fcport, + discovery_gen); } } } @@ -3945,12 +3956,10 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t *next_loopid) { int rval; - int retry; uint8_t opts; struct qla_hw_data *ha = vha->hw; rval = QLA_SUCCESS; - retry = 0; if (IS_ALOGIO_CAPABLE(ha)) { if (fcport->flags & FCF_ASYNC_SENT) @@ -4277,6 +4286,14 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) atomic_read(&fcport->state) != FCS_UNCONFIGURED) { spin_unlock_irqrestore(&ha->vport_slock, flags); qla2x00_rport_del(fcport); + + /* + * Release the target mode FC NEXUS in + * qla_target.c, if target mod is enabled. + */ + qlt_fc_port_deleted(vha, fcport, + base_vha->total_fcport_update_gen); + spin_lock_irqsave(&ha->vport_slock, flags); } } @@ -4944,7 +4961,7 @@ qla25xx_init_queues(struct qla_hw_data *ha) for (i = 1; i < ha->max_rsp_queues; i++) { rsp = ha->rsp_q_map[i]; - if (rsp) { + if (rsp && test_bit(i, ha->rsp_qid_map)) { rsp->options &= ~BIT_0; ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) @@ -4959,8 +4976,8 @@ qla25xx_init_queues(struct qla_hw_data *ha) } for (i = 1; i < ha->max_req_queues; i++) { req = ha->req_q_map[i]; - if (req) { - /* Clear outstanding commands array. */ + if (req && test_bit(i, ha->req_qid_map)) { + /* Clear outstanding commands array. */ req->options &= ~BIT_0; ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) @@ -5096,7 +5113,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) /* Bad NVRAM data, set defaults parameters. */ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || - nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) { + nv->nvram_version < cpu_to_le16(ICB_VERSION)) { /* Reset NVRAM data. */ ql_log(ql_log_warn, vha, 0x006b, "Inconsistent NVRAM detected: checksum=0x%x id=%c " @@ -5109,12 +5126,12 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) * Set default initialization control block. */ memset(nv, 0, ha->nvram_size); - nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION); - nv->version = __constant_cpu_to_le16(ICB_VERSION); + nv->nvram_version = cpu_to_le16(ICB_VERSION); + nv->version = cpu_to_le16(ICB_VERSION); nv->frame_payload_size = 2048; - nv->execution_throttle = __constant_cpu_to_le16(0xFFFF); - nv->exchange_count = __constant_cpu_to_le16(0); - nv->hard_address = __constant_cpu_to_le16(124); + nv->execution_throttle = cpu_to_le16(0xFFFF); + nv->exchange_count = cpu_to_le16(0); + nv->hard_address = cpu_to_le16(124); nv->port_name[0] = 0x21; nv->port_name[1] = 0x00 + ha->port_no + 1; nv->port_name[2] = 0x00; @@ -5132,29 +5149,29 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) nv->node_name[6] = 0x55; nv->node_name[7] = 0x86; qla24xx_nvram_wwn_from_ofw(vha, nv); - nv->login_retry_count = __constant_cpu_to_le16(8); - nv->interrupt_delay_timer = __constant_cpu_to_le16(0); - nv->login_timeout = __constant_cpu_to_le16(0); + nv->login_retry_count = cpu_to_le16(8); + nv->interrupt_delay_timer = cpu_to_le16(0); + nv->login_timeout = cpu_to_le16(0); nv->firmware_options_1 = - __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); - nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4); - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); - nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13); - nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10); - nv->efi_parameters = __constant_cpu_to_le32(0); + cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); + nv->firmware_options_2 = cpu_to_le32(2 << 4); + nv->firmware_options_2 |= cpu_to_le32(BIT_12); + nv->firmware_options_3 = cpu_to_le32(2 << 13); + nv->host_p = cpu_to_le32(BIT_11|BIT_10); + nv->efi_parameters = cpu_to_le32(0); nv->reset_delay = 5; - nv->max_luns_per_target = __constant_cpu_to_le16(128); - nv->port_down_retry_count = __constant_cpu_to_le16(30); - nv->link_down_timeout = __constant_cpu_to_le16(30); + nv->max_luns_per_target = cpu_to_le16(128); + nv->port_down_retry_count = cpu_to_le16(30); + nv->link_down_timeout = cpu_to_le16(30); rval = 1; } if (!qla_ini_mode_enabled(vha)) { /* Don't enable full login after initial LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + nv->firmware_options_1 &= cpu_to_le32(~BIT_13); /* Don't enable LIP full login for initiator */ - nv->host_p &= __constant_cpu_to_le32(~BIT_10); + nv->host_p &= cpu_to_le32(~BIT_10); } qlt_24xx_config_nvram_stage1(vha, nv); @@ -5188,14 +5205,14 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) qlt_24xx_config_nvram_stage2(vha, icb); - if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + if (nv->host_p & cpu_to_le32(BIT_15)) { /* Use alternate WWN? */ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); } /* Prepare nodename */ - if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { + if ((icb->firmware_options_1 & cpu_to_le32(BIT_14)) == 0) { /* * Firmware will apply the following mask if the nodename was * not provided. @@ -5227,7 +5244,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) memcpy(vha->node_name, icb->node_name, WWN_SIZE); memcpy(vha->port_name, icb->port_name, WWN_SIZE); - icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); + icb->execution_throttle = cpu_to_le16(0xFFFF); ha->retry_count = le16_to_cpu(nv->login_retry_count); @@ -5235,7 +5252,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout) nv->login_timeout = cpu_to_le16(ql2xlogintimeout); if (le16_to_cpu(nv->login_timeout) < 4) - nv->login_timeout = __constant_cpu_to_le16(4); + nv->login_timeout = cpu_to_le16(4); ha->login_timeout = le16_to_cpu(nv->login_timeout); icb->login_timeout = nv->login_timeout; @@ -5286,7 +5303,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ? le16_to_cpu(icb->interrupt_delay_timer): 2; } - icb->firmware_options_2 &= __constant_cpu_to_le32( + icb->firmware_options_2 &= cpu_to_le32( ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); vha->flags.process_response_queue = 0; if (ha->zio_mode != QLA_ZIO_DISABLED) { @@ -6042,7 +6059,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) /* Bad NVRAM data, set defaults parameters. */ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || - nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) { + nv->nvram_version < cpu_to_le16(ICB_VERSION)) { /* Reset NVRAM data. */ ql_log(ql_log_info, vha, 0x0073, "Inconsistent NVRAM detected: checksum=0x%x id=%c " @@ -6056,11 +6073,11 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) * Set default initialization control block. */ memset(nv, 0, ha->nvram_size); - nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION); - nv->version = __constant_cpu_to_le16(ICB_VERSION); + nv->nvram_version = cpu_to_le16(ICB_VERSION); + nv->version = cpu_to_le16(ICB_VERSION); nv->frame_payload_size = 2048; - nv->execution_throttle = __constant_cpu_to_le16(0xFFFF); - nv->exchange_count = __constant_cpu_to_le16(0); + nv->execution_throttle = cpu_to_le16(0xFFFF); + nv->exchange_count = cpu_to_le16(0); nv->port_name[0] = 0x21; nv->port_name[1] = 0x00 + ha->port_no + 1; nv->port_name[2] = 0x00; @@ -6077,20 +6094,20 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) nv->node_name[5] = 0x1c; nv->node_name[6] = 0x55; nv->node_name[7] = 0x86; - nv->login_retry_count = __constant_cpu_to_le16(8); - nv->interrupt_delay_timer = __constant_cpu_to_le16(0); - nv->login_timeout = __constant_cpu_to_le16(0); + nv->login_retry_count = cpu_to_le16(8); + nv->interrupt_delay_timer = cpu_to_le16(0); + nv->login_timeout = cpu_to_le16(0); nv->firmware_options_1 = - __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); - nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4); - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); - nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13); - nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10); - nv->efi_parameters = __constant_cpu_to_le32(0); + cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); + nv->firmware_options_2 = cpu_to_le32(2 << 4); + nv->firmware_options_2 |= cpu_to_le32(BIT_12); + nv->firmware_options_3 = cpu_to_le32(2 << 13); + nv->host_p = cpu_to_le32(BIT_11|BIT_10); + nv->efi_parameters = cpu_to_le32(0); nv->reset_delay = 5; - nv->max_luns_per_target = __constant_cpu_to_le16(128); - nv->port_down_retry_count = __constant_cpu_to_le16(30); - nv->link_down_timeout = __constant_cpu_to_le16(180); + nv->max_luns_per_target = cpu_to_le16(128); + nv->port_down_retry_count = cpu_to_le16(30); + nv->link_down_timeout = cpu_to_le16(180); nv->enode_mac[0] = 0x00; nv->enode_mac[1] = 0xC0; nv->enode_mac[2] = 0xDD; @@ -6149,13 +6166,13 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) qlt_81xx_config_nvram_stage2(vha, icb); /* Use alternate WWN? */ - if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + if (nv->host_p & cpu_to_le32(BIT_15)) { memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); } /* Prepare nodename */ - if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) { + if ((icb->firmware_options_1 & cpu_to_le32(BIT_14)) == 0) { /* * Firmware will apply the following mask if the nodename was * not provided. @@ -6184,7 +6201,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) memcpy(vha->node_name, icb->node_name, WWN_SIZE); memcpy(vha->port_name, icb->port_name, WWN_SIZE); - icb->execution_throttle = __constant_cpu_to_le16(0xFFFF); + icb->execution_throttle = cpu_to_le16(0xFFFF); ha->retry_count = le16_to_cpu(nv->login_retry_count); @@ -6192,7 +6209,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout) nv->login_timeout = cpu_to_le16(ql2xlogintimeout); if (le16_to_cpu(nv->login_timeout) < 4) - nv->login_timeout = __constant_cpu_to_le16(4); + nv->login_timeout = cpu_to_le16(4); ha->login_timeout = le16_to_cpu(nv->login_timeout); icb->login_timeout = nv->login_timeout; @@ -6238,7 +6255,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) /* if not running MSI-X we need handshaking on interrupts */ if (!vha->hw->flags.msix_enabled && (IS_QLA83XX(ha) || IS_QLA27XX(ha))) - icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22); + icb->firmware_options_2 |= cpu_to_le32(BIT_22); /* Enable ZIO. */ if (!vha->flags.init_done) { @@ -6247,7 +6264,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ? le16_to_cpu(icb->interrupt_delay_timer): 2; } - icb->firmware_options_2 &= __constant_cpu_to_le32( + icb->firmware_options_2 &= cpu_to_le32( ~(BIT_3 | BIT_2 | BIT_1 | BIT_0)); vha->flags.process_response_queue = 0; if (ha->zio_mode != QLA_ZIO_DISABLED) { diff --git a/kernel/drivers/scsi/qla2xxx/qla_iocb.c b/kernel/drivers/scsi/qla2xxx/qla_iocb.c index a1ab25fca..c49df34e9 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_iocb.c +++ b/kernel/drivers/scsi/qla2xxx/qla_iocb.c @@ -108,8 +108,7 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha) cont_pkt = (cont_entry_t *)req->ring_ptr; /* Load packet defaults. */ - *((uint32_t *)(&cont_pkt->entry_type)) = - __constant_cpu_to_le32(CONTINUE_TYPE); + *((uint32_t *)(&cont_pkt->entry_type)) = cpu_to_le32(CONTINUE_TYPE); return (cont_pkt); } @@ -138,8 +137,8 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req) /* Load packet defaults. */ *((uint32_t *)(&cont_pkt->entry_type)) = IS_QLAFX00(vha->hw) ? - __constant_cpu_to_le32(CONTINUE_A64_TYPE_FX00) : - __constant_cpu_to_le32(CONTINUE_A64_TYPE); + cpu_to_le32(CONTINUE_A64_TYPE_FX00) : + cpu_to_le32(CONTINUE_A64_TYPE); return (cont_pkt); } @@ -204,11 +203,11 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, /* Update entry type to indicate Command Type 2 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_TYPE); + cpu_to_le32(COMMAND_TYPE); /* No data transfer */ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return; } @@ -261,12 +260,11 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ - *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_A64_TYPE); + *((uint32_t *)(&cmd_pkt->entry_type)) = cpu_to_le32(COMMAND_A64_TYPE); /* No data transfer */ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return; } @@ -310,7 +308,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, int qla2x00_start_scsi(srb_t *sp) { - int ret, nseg; + int nseg; unsigned long flags; scsi_qla_host_t *vha; struct scsi_cmnd *cmd; @@ -327,7 +325,6 @@ qla2x00_start_scsi(srb_t *sp) struct rsp_que *rsp; /* Setup device pointers. */ - ret = 0; vha = sp->fcport->vha; ha = vha->hw; reg = &ha->iobase->isp; @@ -403,7 +400,7 @@ qla2x00_start_scsi(srb_t *sp) /* Set target ID and LUN number*/ SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id); cmd_pkt->lun = cpu_to_le16(cmd->device->lun); - cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); + cmd_pkt->control_flags = cpu_to_le16(CF_SIMPLE_TAG); /* Load SCSI command packet. */ memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len); @@ -454,7 +451,7 @@ void qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) { struct qla_hw_data *ha = vha->hw; - device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); + device_reg_t *reg = ISP_QUE_REG(ha, req->id); if (IS_P3P_TYPE(ha)) { qla82xx_start_iocbs(vha); @@ -597,12 +594,11 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ - *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_TYPE_6); + *((uint32_t *)(&cmd_pkt->entry_type)) = cpu_to_le32(COMMAND_TYPE_6); /* No data transfer */ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return 0; } @@ -611,13 +607,11 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { - cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_WRITE_DATA); + cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA); vha->qla_stats.output_bytes += scsi_bufflen(cmd); vha->qla_stats.output_requests++; } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { - cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_READ_DATA); + cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA); vha->qla_stats.input_bytes += scsi_bufflen(cmd); vha->qla_stats.input_requests++; } @@ -680,7 +674,7 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, * * Returns the number of dsd list needed to store @dsds. */ -inline uint16_t +static inline uint16_t qla24xx_calc_dsd_lists(uint16_t dsds) { uint16_t dsd_lists = 0; @@ -700,7 +694,7 @@ qla24xx_calc_dsd_lists(uint16_t dsds) * @cmd_pkt: Command type 3 IOCB * @tot_dsds: Total number of segments to transfer */ -inline void +static inline void qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, uint16_t tot_dsds) { @@ -710,32 +704,27 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, struct scsi_cmnd *cmd; struct scatterlist *sg; int i; - struct req_que *req; cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ - *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_TYPE_7); + *((uint32_t *)(&cmd_pkt->entry_type)) = cpu_to_le32(COMMAND_TYPE_7); /* No data transfer */ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return; } vha = sp->fcport->vha; - req = vha->req; /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { - cmd_pkt->task_mgmt_flags = - __constant_cpu_to_le16(TMF_WRITE_DATA); + cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA); vha->qla_stats.output_bytes += scsi_bufflen(cmd); vha->qla_stats.output_requests++; } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { - cmd_pkt->task_mgmt_flags = - __constant_cpu_to_le16(TMF_READ_DATA); + cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA); vha->qla_stats.input_bytes += scsi_bufflen(cmd); vha->qla_stats.input_requests++; } @@ -809,7 +798,7 @@ qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, * match LBA in CDB + N */ case SCSI_PROT_DIF_TYPE2: - pkt->app_tag = __constant_cpu_to_le16(0); + pkt->app_tag = cpu_to_le16(0); pkt->app_tag_mask[0] = 0x0; pkt->app_tag_mask[1] = 0x0; @@ -840,7 +829,7 @@ qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, case SCSI_PROT_DIF_TYPE1: pkt->ref_tag = cpu_to_le32((uint32_t) (0xffffffff & scsi_get_lba(cmd))); - pkt->app_tag = __constant_cpu_to_le16(0); + pkt->app_tag = cpu_to_le16(0); pkt->app_tag_mask[0] = 0x0; pkt->app_tag_mask[1] = 0x0; @@ -933,11 +922,9 @@ qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp, dma_addr_t sle_dma; uint32_t sle_dma_len, tot_prot_dma_len = 0; struct scsi_cmnd *cmd; - struct scsi_qla_host *vha; memset(&sgx, 0, sizeof(struct qla2_sgx)); if (sp) { - vha = sp->fcport->vha; cmd = GET_CMD_SP(sp); prot_int = cmd->device->sector_size; @@ -947,7 +934,6 @@ qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp, sg_prot = scsi_prot_sglist(cmd); } else if (tc) { - vha = tc->vha; prot_int = tc->blk_sz; sgx.tot_bytes = tc->bufflen; sgx.cur_sg = tc->sg; @@ -1047,15 +1033,12 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, int i; uint16_t used_dsds = tot_dsds; struct scsi_cmnd *cmd; - struct scsi_qla_host *vha; if (sp) { cmd = GET_CMD_SP(sp); sgl = scsi_sglist(cmd); - vha = sp->fcport->vha; } else if (tc) { sgl = tc->sg; - vha = tc->vha; } else { BUG(); return 1; @@ -1231,7 +1214,6 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, uint32_t *cur_dsd, *fcp_dl; scsi_qla_host_t *vha; struct scsi_cmnd *cmd; - int sgc; uint32_t total_bytes = 0; uint32_t data_bytes; uint32_t dif_bytes; @@ -1247,10 +1229,8 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, cmd = GET_CMD_SP(sp); - sgc = 0; /* Update entry type to indicate Command Type CRC_2 IOCB */ - *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_TYPE_CRC_2); + *((uint32_t *)(&cmd_pkt->entry_type)) = cpu_to_le32(COMMAND_TYPE_CRC_2); vha = sp->fcport->vha; ha = vha->hw; @@ -1258,7 +1238,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, /* No data transfer */ data_bytes = scsi_bufflen(cmd); if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return QLA_SUCCESS; } @@ -1267,10 +1247,10 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_WRITE_DATA); + cpu_to_le16(CF_WRITE_DATA); } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { cmd_pkt->control_flags = - __constant_cpu_to_le16(CF_READ_DATA); + cpu_to_le16(CF_READ_DATA); } if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) || @@ -1392,7 +1372,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, crc_ctx_pkt->blk_size = cpu_to_le16(blk_size); crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts); crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes); - crc_ctx_pkt->guard_seed = __constant_cpu_to_le16(0); + crc_ctx_pkt->guard_seed = cpu_to_le16(0); /* Fibre channel byte count */ cmd_pkt->byte_count = cpu_to_le32(total_bytes); fcp_dl = (uint32_t *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 + @@ -1400,13 +1380,12 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, *fcp_dl = htonl(total_bytes); if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { - cmd_pkt->byte_count = __constant_cpu_to_le32(0); + cmd_pkt->byte_count = cpu_to_le32(0); return QLA_SUCCESS; } /* Walks data segments */ - cmd_pkt->control_flags |= - __constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); + cmd_pkt->control_flags |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); if (!bundling && tot_prot_dsds) { if (qla24xx_walk_and_build_sglist_no_difb(ha, sp, @@ -1418,8 +1397,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, if (bundling && tot_prot_dsds) { /* Walks dif segments */ - cmd_pkt->control_flags |= - __constant_cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE); + cmd_pkt->control_flags |= cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE); cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address; if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd, tot_prot_dsds, NULL)) @@ -1442,7 +1420,7 @@ crc_queuing_error: int qla24xx_start_scsi(srb_t *sp) { - int ret, nseg; + int nseg; unsigned long flags; uint32_t *clr_ptr; uint32_t index; @@ -1458,8 +1436,6 @@ qla24xx_start_scsi(srb_t *sp) struct qla_hw_data *ha = vha->hw; /* Setup device pointers. */ - ret = 0; - qla25xx_set_que(sp, &rsp); req = vha->req; @@ -1753,7 +1729,7 @@ qla24xx_dif_start_scsi(srb_t *sp) cmd_pkt->entry_count = (uint8_t)req_cnt; /* Specify response queue number where completion should happen */ cmd_pkt->entry_status = (uint8_t) rsp->id; - cmd_pkt->timeout = __constant_cpu_to_le16(0); + cmd_pkt->timeout = cpu_to_le16(0); wmb(); /* Adjust ring index. */ @@ -1819,7 +1795,7 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp) { struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; - device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); + device_reg_t *reg = ISP_QUE_REG(ha, req->id); uint32_t index, handle; request_t *pkt; uint16_t cnt, req_cnt; @@ -1943,6 +1919,9 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); + if (!sp->fcport->tgt_session || + !sp->fcport->tgt_session->keep_nport_handle) + logio->control_flags |= cpu_to_le16(LCF_FREE_NPORT); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; @@ -2041,10 +2020,10 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->entry_status = 0; els_iocb->handle = sp->handle; els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); - els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + els_iocb->tx_dsd_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); els_iocb->vp_index = sp->fcport->vha->vp_idx; els_iocb->sof_type = EST_SOFI3; - els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt); + els_iocb->rx_dsd_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); els_iocb->opcode = sp->type == SRB_ELS_CMD_RPT ? @@ -2088,7 +2067,6 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) struct qla_hw_data *ha = vha->hw; struct fc_bsg_job *bsg_job = sp->u.bsg_job; int loop_iterartion = 0; - int cont_iocb_prsnt = 0; int entry_count = 1; memset(ct_iocb, 0, sizeof(ms_iocb_entry_t)); @@ -2096,13 +2074,13 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) ct_iocb->entry_status = 0; ct_iocb->handle1 = sp->handle; SET_TARGET_ID(ha, ct_iocb->loop_id, sp->fcport->loop_id); - ct_iocb->status = __constant_cpu_to_le16(0); - ct_iocb->control_flags = __constant_cpu_to_le16(0); + ct_iocb->status = cpu_to_le16(0); + ct_iocb->control_flags = cpu_to_le16(0); ct_iocb->timeout = 0; ct_iocb->cmd_dsd_count = - __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + cpu_to_le16(bsg_job->request_payload.sg_cnt); ct_iocb->total_dsd_count = - __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt + 1); + cpu_to_le16(bsg_job->request_payload.sg_cnt + 1); ct_iocb->req_bytecount = cpu_to_le32(bsg_job->request_payload.payload_len); ct_iocb->rsp_bytecount = @@ -2139,7 +2117,6 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) vha->hw->req_q_map[0]); cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; avail_dsds = 5; - cont_iocb_prsnt = 1; entry_count++; } @@ -2167,7 +2144,6 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) struct qla_hw_data *ha = vha->hw; struct fc_bsg_job *bsg_job = sp->u.bsg_job; int loop_iterartion = 0; - int cont_iocb_prsnt = 0; int entry_count = 1; ct_iocb->entry_type = CT_IOCB_TYPE; @@ -2177,13 +2153,13 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); ct_iocb->vp_index = sp->fcport->vha->vp_idx; - ct_iocb->comp_status = __constant_cpu_to_le16(0); + ct_iocb->comp_status = cpu_to_le16(0); ct_iocb->cmd_dsd_count = - __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt); + cpu_to_le16(bsg_job->request_payload.sg_cnt); ct_iocb->timeout = 0; ct_iocb->rsp_dsd_count = - __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt); + cpu_to_le16(bsg_job->reply_payload.sg_cnt); ct_iocb->rsp_byte_count = cpu_to_le32(bsg_job->reply_payload.payload_len); ct_iocb->cmd_byte_count = @@ -2214,7 +2190,6 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) ha->req_q_map[0]); cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; avail_dsds = 5; - cont_iocb_prsnt = 1; entry_count++; } @@ -2237,7 +2212,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) int qla82xx_start_scsi(srb_t *sp) { - int ret, nseg; + int nseg; unsigned long flags; struct scsi_cmnd *cmd; uint32_t *clr_ptr; @@ -2257,7 +2232,6 @@ qla82xx_start_scsi(srb_t *sp) struct rsp_que *rsp = NULL; /* Setup device pointers. */ - ret = 0; reg = &ha->iobase->isp82; cmd = GET_CMD_SP(sp); req = vha->req; @@ -2536,16 +2510,12 @@ sufficient_dsds: /* write, read and verify logic */ dbval = dbval | (req->id << 8) | (req->ring_index << 16); if (ql2xdbwr) - qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + qla82xx_wr_32(ha, (uintptr_t __force)ha->nxdb_wr_ptr, dbval); else { - WRT_REG_DWORD( - (unsigned long __iomem *)ha->nxdb_wr_ptr, - dbval); + WRT_REG_DWORD(ha->nxdb_wr_ptr, dbval); wmb(); - while (RD_REG_DWORD((void __iomem *)ha->nxdb_rd_ptr) != dbval) { - WRT_REG_DWORD( - (unsigned long __iomem *)ha->nxdb_wr_ptr, - dbval); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD(ha->nxdb_wr_ptr, dbval); wmb(); } } @@ -2679,7 +2649,7 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, /*Update entry type to indicate bidir command */ *((uint32_t *)(&cmd_pkt->entry_type)) = - __constant_cpu_to_le32(COMMAND_BIDIRECTIONAL); + cpu_to_le32(COMMAND_BIDIRECTIONAL); /* Set the transfer direction, in this set both flags * Also set the BD_WRAP_BACK flag, firmware will take care @@ -2687,8 +2657,7 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, */ cmd_pkt->wr_dseg_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); cmd_pkt->rd_dseg_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); - cmd_pkt->control_flags = - __constant_cpu_to_le16(BD_WRITE_DATA | BD_READ_DATA | + cmd_pkt->control_flags = cpu_to_le16(BD_WRITE_DATA | BD_READ_DATA | BD_WRAP_BACK); req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; @@ -2797,10 +2766,10 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) handle = req->current_outstanding_cmd; for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; - if (handle == req->num_outstanding_cmds) - handle = 1; - if (!req->outstanding_cmds[handle]) - break; + if (handle == req->num_outstanding_cmds) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; } if (index == req->num_outstanding_cmds) { diff --git a/kernel/drivers/scsi/qla2xxx/qla_isr.c b/kernel/drivers/scsi/qla2xxx/qla_isr.c index 6dc14cd78..0e59731f9 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_isr.c +++ b/kernel/drivers/scsi/qla2xxx/qla_isr.c @@ -116,7 +116,7 @@ bool qla2x00_check_reg32_for_disconnect(scsi_qla_host_t *vha, uint32_t reg) { /* Check for PCI disconnection */ - if (reg == 0xffffffff) { + if (reg == 0xffffffff && !pci_channel_offline(vha->hw->pdev)) { if (!test_and_set_bit(PFLG_DISCONNECTED, &vha->pci_flags) && !test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags) && !test_bit(PFLG_DRIVER_PROBING, &vha->pci_flags)) { @@ -560,6 +560,17 @@ qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry) return ret; } +static inline fc_port_t * +qla2x00_find_fcport_by_loopid(scsi_qla_host_t *vha, uint16_t loop_id) +{ + fc_port_t *fcport; + + list_for_each_entry(fcport, &vha->vp_fcports, list) + if (fcport->loop_id == loop_id) + return fcport; + return NULL; +} + /** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context @@ -575,7 +586,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82; - uint32_t rscn_entry, host_pid, tmp_pid; + uint32_t rscn_entry, host_pid; unsigned long flags; fc_port_t *fcport = NULL; @@ -897,11 +908,29 @@ skip_rio: (mb[1] != 0xffff)) && vha->vp_idx != (mb[3] & 0xff)) break; - /* Global event -- port logout or port unavailable. */ - if (mb[1] == 0xffff && mb[2] == 0x7) { + if (mb[2] == 0x7) { ql_dbg(ql_dbg_async, vha, 0x5010, - "Port unavailable %04x %04x %04x.\n", + "Port %s %04x %04x %04x.\n", + mb[1] == 0xffff ? "unavailable" : "logout", mb[1], mb[2], mb[3]); + + if (mb[1] == 0xffff) + goto global_port_update; + + /* Port logout */ + fcport = qla2x00_find_fcport_by_loopid(vha, mb[1]); + if (!fcport) + break; + if (atomic_read(&fcport->state) != FCS_ONLINE) + break; + ql_dbg(ql_dbg_async, vha, 0x508a, + "Marking port lost loopid=%04x portid=%06x.\n", + fcport->loop_id, fcport->d_id.b24); + qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); + break; + +global_port_update: + /* Port unavailable. */ ql_log(ql_log_warn, vha, 0x505e, "Link is offline.\n"); @@ -998,7 +1027,6 @@ skip_rio: list_for_each_entry(fcport, &vha->vp_fcports, list) { if (atomic_read(&fcport->state) != FCS_ONLINE) continue; - tmp_pid = fcport->d_id.b24; if (fcport->d_id.b24 == rscn_entry) { qla2x00_mark_device_lost(vha, fcport, 0, 0); break; @@ -1565,7 +1593,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) "Async-%s error - hdl=%x entry-status(%x).\n", type, sp->handle, sts->entry_status); iocb->u.tmf.data = QLA_FUNCTION_FAILED; - } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (sts->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_log(ql_log_warn, fcport->vha, 0x5039, "Async-%s error - hdl=%x completion status(%x).\n", type, sp->handle, sts->comp_status); @@ -1580,7 +1608,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) ql_log(ql_log_warn, fcport->vha, 0x503c, "Async-%s error - hdl=%x response(%x).\n", type, sp->handle, sts->data[3]); - iocb->u.tmf.data = QLA_FUNCTION_FAILED; + iocb->u.tmf.data = QLA_FUNCTION_FAILED; } } @@ -1979,7 +2007,7 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt, rval = EXT_STATUS_ERR; break; } - bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->reply_payload_rcv_len = 0; done: /* Return the vendor specific reply to API */ @@ -2045,14 +2073,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) } /* Validate handle. */ - if (handle < req->num_outstanding_cmds) + if (handle < req->num_outstanding_cmds) { sp = req->outstanding_cmds[handle]; - else - sp = NULL; - - if (sp == NULL) { + if (!sp) { + ql_dbg(ql_dbg_io, vha, 0x3075, + "%s(%ld): Already returned command for status handle (0x%x).\n", + __func__, vha->host_no, sts->handle); + return; + } + } else { ql_dbg(ql_dbg_io, vha, 0x3017, - "Invalid status handle (0x%x).\n", sts->handle); + "Invalid status handle, out of range (0x%x).\n", + sts->handle); if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { if (IS_P3P_TYPE(ha)) @@ -2339,12 +2371,12 @@ out: ql_dbg(ql_dbg_io, fcport->vha, 0x3022, "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu " "portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x " - "rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n", + "rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n", comp_status, scsi_status, res, vha->host_no, cp->device->id, cp->device->lun, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len, - resid_len, fw_resid_len); + resid_len, fw_resid_len, sp, cp); if (rsp->status_srb == NULL) sp->done(ha, sp, res); @@ -2441,13 +2473,7 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) } fatal: ql_log(ql_log_warn, vha, 0x5030, - "Error entry - invalid handle/queue.\n"); - - if (IS_P3P_TYPE(ha)) - set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); - else - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); + "Error entry - invalid handle/queue (%04x).\n", que); } /** @@ -2992,9 +3018,9 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) "MSI-X: Failed to enable support " "-- %d/%d\n Retry with %d vectors.\n", ha->msix_count, ret, ret); + ha->msix_count = ret; + ha->max_rsp_queues = ha->msix_count - 1; } - ha->msix_count = ret; - ha->max_rsp_queues = ha->msix_count - 1; ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) * ha->msix_count, GFP_KERNEL); if (!ha->msix_entries) { diff --git a/kernel/drivers/scsi/qla2xxx/qla_mbx.c b/kernel/drivers/scsi/qla2xxx/qla_mbx.c index 02b1c1c53..cb11e04be 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_mbx.c +++ b/kernel/drivers/scsi/qla2xxx/qla_mbx.c @@ -555,7 +555,9 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) if (IS_FWI2_CAPABLE(ha)) mcp->in_mb |= MBX_17|MBX_16|MBX_15; if (IS_QLA27XX(ha)) - mcp->in_mb |= MBX_21|MBX_20|MBX_19|MBX_18; + mcp->in_mb |= MBX_23 | MBX_22 | MBX_21 | MBX_20 | MBX_19 | + MBX_18 | MBX_14 | MBX_13 | MBX_11 | MBX_10 | MBX_9 | MBX_8; + mcp->flags = 0; mcp->tov = MBX_TOV_SECONDS; rval = qla2x00_mailbox_command(vha, mcp); @@ -571,6 +573,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */ else ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4]; + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw) || IS_QLA8044(ha)) { ha->mpi_version[0] = mcp->mb[10] & 0xff; ha->mpi_version[1] = mcp->mb[11] >> 8; @@ -580,6 +583,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) ha->phy_version[1] = mcp->mb[9] >> 8; ha->phy_version[2] = mcp->mb[9] & 0xff; } + if (IS_FWI2_CAPABLE(ha)) { ha->fw_attributes_h = mcp->mb[15]; ha->fw_attributes_ext[0] = mcp->mb[16]; @@ -591,7 +595,14 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) "%s: Ext_FwAttributes Upper: 0x%x, Lower: 0x%x.\n", __func__, mcp->mb[17], mcp->mb[16]); } + if (IS_QLA27XX(ha)) { + ha->mpi_version[0] = mcp->mb[10] & 0xff; + ha->mpi_version[1] = mcp->mb[11] >> 8; + ha->mpi_version[2] = mcp->mb[11] & 0xff; + ha->pep_version[0] = mcp->mb[13] & 0xff; + ha->pep_version[1] = mcp->mb[14] >> 8; + ha->pep_version[2] = mcp->mb[14] & 0xff; ha->fw_shared_ram_start = (mcp->mb[19] << 16) | mcp->mb[18]; ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20]; } @@ -1135,20 +1146,22 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, vha->fcoe_vn_port_mac[0] = mcp->mb[13] & 0xff; } /* If FA-WWN supported */ - if (mcp->mb[7] & BIT_14) { - vha->port_name[0] = MSB(mcp->mb[16]); - vha->port_name[1] = LSB(mcp->mb[16]); - vha->port_name[2] = MSB(mcp->mb[17]); - vha->port_name[3] = LSB(mcp->mb[17]); - vha->port_name[4] = MSB(mcp->mb[18]); - vha->port_name[5] = LSB(mcp->mb[18]); - vha->port_name[6] = MSB(mcp->mb[19]); - vha->port_name[7] = LSB(mcp->mb[19]); - fc_host_port_name(vha->host) = - wwn_to_u64(vha->port_name); - ql_dbg(ql_dbg_mbx, vha, 0x10ca, - "FA-WWN acquired %016llx\n", - wwn_to_u64(vha->port_name)); + if (IS_FAWWN_CAPABLE(vha->hw)) { + if (mcp->mb[7] & BIT_14) { + vha->port_name[0] = MSB(mcp->mb[16]); + vha->port_name[1] = LSB(mcp->mb[16]); + vha->port_name[2] = MSB(mcp->mb[17]); + vha->port_name[3] = LSB(mcp->mb[17]); + vha->port_name[4] = MSB(mcp->mb[18]); + vha->port_name[5] = LSB(mcp->mb[18]); + vha->port_name[6] = MSB(mcp->mb[19]); + vha->port_name[7] = LSB(mcp->mb[19]); + fc_host_port_name(vha->host) = + wwn_to_u64(vha->port_name); + ql_dbg(ql_dbg_mbx, vha, 0x10ca, + "FA-WWN acquired %016llx\n", + wwn_to_u64(vha->port_name)); + } } } @@ -1239,7 +1252,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) "Entered %s.\n", __func__); if (IS_P3P_TYPE(ha) && ql2xdbwr) - qla82xx_wr_32(ha, ha->nxdb_wr_ptr, + qla82xx_wr_32(ha, (uintptr_t __force)ha->nxdb_wr_ptr, (0x04 | (ha->portnum << 5) | (0 << 8) | (0 << 16))); if (ha->flags.npiv_supported) @@ -1865,7 +1878,6 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint32_t iop[2]; struct qla_hw_data *ha = vha->hw; struct req_que *req; - struct rsp_que *rsp; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1061, "Entered %s.\n", __func__); @@ -1874,7 +1886,6 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, req = ha->req_q_map[0]; else req = vha->req; - rsp = req->rsp; lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); if (lg == NULL) { @@ -1888,11 +1899,11 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg->entry_count = 1; lg->handle = MAKE_HANDLE(req->id, lg->handle); lg->nport_handle = cpu_to_le16(loop_id); - lg->control_flags = __constant_cpu_to_le16(LCF_COMMAND_PLOGI); + lg->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI); if (opt & BIT_0) - lg->control_flags |= __constant_cpu_to_le16(LCF_COND_PLOGI); + lg->control_flags |= cpu_to_le16(LCF_COND_PLOGI); if (opt & BIT_1) - lg->control_flags |= __constant_cpu_to_le16(LCF_SKIP_PRLI); + lg->control_flags |= cpu_to_le16(LCF_SKIP_PRLI); lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; @@ -1907,7 +1918,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, "Failed to complete IOCB -- error status (%x).\n", lg->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (lg->comp_status != cpu_to_le16(CS_COMPLETE)) { iop[0] = le32_to_cpu(lg->io_parameter[0]); iop[1] = le32_to_cpu(lg->io_parameter[1]); @@ -1961,7 +1972,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, mb[10] |= BIT_0; /* Class 2. */ if (lg->io_parameter[9] || lg->io_parameter[10]) mb[10] |= BIT_1; /* Class 3. */ - if (lg->io_parameter[0] & __constant_cpu_to_le32(BIT_7)) + if (lg->io_parameter[0] & cpu_to_le32(BIT_7)) mb[10] |= BIT_7; /* Confirmed Completion * Allowed */ @@ -2142,7 +2153,6 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, dma_addr_t lg_dma; struct qla_hw_data *ha = vha->hw; struct req_que *req; - struct rsp_que *rsp; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d, "Entered %s.\n", __func__); @@ -2159,13 +2169,12 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, req = ha->req_q_map[0]; else req = vha->req; - rsp = req->rsp; lg->entry_type = LOGINOUT_PORT_IOCB_TYPE; lg->entry_count = 1; lg->handle = MAKE_HANDLE(req->id, lg->handle); lg->nport_handle = cpu_to_le16(loop_id); lg->control_flags = - __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO| + cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO| LCF_FREE_NPORT); lg->port_id[0] = al_pa; lg->port_id[1] = area; @@ -2181,7 +2190,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, "Failed to complete IOCB -- error status (%x).\n", lg->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (lg->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x1071, "Failed to complete IOCB -- completion status (%x) " "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status), @@ -2415,7 +2424,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, *orig_iocb_cnt = mcp->mb[10]; if (vha->hw->flags.npiv_supported && max_npiv_vports) *max_npiv_vports = mcp->mb[11]; - if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs) + if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw) || + IS_QLA27XX(vha->hw)) && max_fcfs) *max_fcfs = mcp->mb[12]; } @@ -2672,7 +2682,7 @@ qla24xx_abort_command(srb_t *sp) "Failed to complete IOCB -- error status (%x).\n", abt->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (abt->nport_handle != __constant_cpu_to_le16(0)) { + } else if (abt->nport_handle != cpu_to_le16(0)) { ql_dbg(ql_dbg_mbx, vha, 0x1090, "Failed to complete IOCB -- completion status (%x).\n", le16_to_cpu(abt->nport_handle)); @@ -2755,8 +2765,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, "Failed to complete IOCB -- error status (%x).\n", sts->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (sts->comp_status != - __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (sts->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x1096, "Failed to complete IOCB -- completion status (%x).\n", le16_to_cpu(sts->comp_status)); @@ -2852,7 +2861,8 @@ qla2x00_write_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t data) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA2031(vha->hw) && !IS_QLA27XX(vha->hw)) + if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1182, @@ -2890,7 +2900,8 @@ qla2x00_read_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t *data) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA2031(vha->hw) && !IS_QLA27XX(vha->hw)) + if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1185, @@ -3482,7 +3493,7 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) "Failed to complete IOCB -- error status (%x).\n", vpmod->comp_status); rval = QLA_FUNCTION_FAILED; - } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x10bf, "Failed to complete IOCB -- completion status (%x).\n", le16_to_cpu(vpmod->comp_status)); @@ -3541,7 +3552,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) vce->entry_type = VP_CTRL_IOCB_TYPE; vce->entry_count = 1; vce->command = cpu_to_le16(cmd); - vce->vp_count = __constant_cpu_to_le16(1); + vce->vp_count = cpu_to_le16(1); /* index map in firmware starts with 1; decrement index * this is ok as we never use index 0 @@ -3561,7 +3572,7 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) "Failed to complete IOCB -- error status (%x).\n", vce->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + } else if (vce->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x10c5, "Failed to complet IOCB -- completion status (%x).\n", le16_to_cpu(vce->comp_status)); @@ -3898,7 +3909,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) spin_lock_irqsave(&ha->hardware_lock, flags); if (!(rsp->options & BIT_0)) { WRT_REG_DWORD(rsp->rsp_q_out, 0); - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) WRT_REG_DWORD(rsp->rsp_q_in, 0); } @@ -5345,7 +5356,7 @@ qla83xx_restart_nic_firmware(scsi_qla_host_t *vha) mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__); diff --git a/kernel/drivers/scsi/qla2xxx/qla_mid.c b/kernel/drivers/scsi/qla2xxx/qla_mid.c index cc9419251..cf7ba52ba 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_mid.c +++ b/kernel/drivers/scsi/qla2xxx/qla_mid.c @@ -371,7 +371,6 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha) void qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha) { - int ret; struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *vp; unsigned long flags = 0; @@ -392,7 +391,7 @@ qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha) atomic_inc(&vp->vref_count); spin_unlock_irqrestore(&ha->vport_slock, flags); - ret = qla2x00_do_dpc_vp(vp); + qla2x00_do_dpc_vp(vp); spin_lock_irqsave(&ha->vport_slock, flags); atomic_dec(&vp->vref_count); @@ -601,7 +600,7 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) /* Delete request queues */ for (cnt = 1; cnt < ha->max_req_queues; cnt++) { req = ha->req_q_map[cnt]; - if (req) { + if (req && test_bit(cnt, ha->req_qid_map)) { ret = qla25xx_delete_req_que(vha, req); if (ret != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x00ea, @@ -615,7 +614,7 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) /* Delete response queues */ for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) { rsp = ha->rsp_q_map[cnt]; - if (rsp) { + if (rsp && test_bit(cnt, ha->rsp_qid_map)) { ret = qla25xx_delete_rsp_que(vha, rsp); if (ret != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x00eb, diff --git a/kernel/drivers/scsi/qla2xxx/qla_mr.c b/kernel/drivers/scsi/qla2xxx/qla_mr.c index 6d190b4b8..b5029e543 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_mr.c +++ b/kernel/drivers/scsi/qla2xxx/qla_mr.c @@ -862,7 +862,7 @@ qlafx00_config_queues(struct scsi_qla_host *vha) dma_addr_t bar2_hdl = pci_resource_start(ha->pdev, 2); req->length = ha->req_que_len; - req->ring = (void *)ha->iobase + ha->req_que_off; + req->ring = (void __force *)ha->iobase + ha->req_que_off; req->dma = bar2_hdl + ha->req_que_off; if ((!req->ring) || (req->length == 0)) { ql_log_pci(ql_log_info, ha->pdev, 0x012f, @@ -877,7 +877,7 @@ qlafx00_config_queues(struct scsi_qla_host *vha) ha->req_que_off, (u64)req->dma); rsp->length = ha->rsp_que_len; - rsp->ring = (void *)ha->iobase + ha->rsp_que_off; + rsp->ring = (void __force *)ha->iobase + ha->rsp_que_off; rsp->dma = bar2_hdl + ha->rsp_que_off; if ((!rsp->ring) || (rsp->length == 0)) { ql_log_pci(ql_log_info, ha->pdev, 0x0131, @@ -1317,10 +1317,10 @@ int qlafx00_configure_devices(scsi_qla_host_t *vha) { int rval; - unsigned long flags, save_flags; + unsigned long flags; rval = QLA_SUCCESS; - save_flags = flags = vha->dpc_flags; + flags = vha->dpc_flags; ql_dbg(ql_dbg_disc, vha, 0x2090, "Configure devices -- dpc flags =0x%lx\n", flags); @@ -1425,7 +1425,7 @@ qlafx00_init_response_q_entries(struct rsp_que *rsp) pkt = rsp->ring_ptr; for (cnt = 0; cnt < rsp->length; cnt++) { pkt->signature = RESPONSE_PROCESSED; - WRT_REG_DWORD((void __iomem *)&pkt->signature, + WRT_REG_DWORD((void __force __iomem *)&pkt->signature, RESPONSE_PROCESSED); pkt++; } @@ -2279,7 +2279,6 @@ qlafx00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) struct sts_entry_fx00 *sts; __le16 comp_status; __le16 scsi_status; - uint16_t ox_id; __le16 lscsi_status; int32_t resid; uint32_t sense_len, par_sense_len, rsp_info_len, resid_len, @@ -2344,7 +2343,6 @@ qlafx00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) fcport = sp->fcport; - ox_id = 0; sense_len = par_sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (scsi_status & cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID)) @@ -2528,12 +2526,12 @@ check_scsi_status: ql_dbg(ql_dbg_io, fcport->vha, 0x3058, "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu " "tgt_id: 0x%x lscsi_status: 0x%x cdb=%10phN len=0x%x " - "rsp_info=0x%x resid=0x%x fw_resid=0x%x sense_len=0x%x, " + "rsp_info=%p resid=0x%x fw_resid=0x%x sense_len=0x%x, " "par_sense_len=0x%x, rsp_info_len=0x%x\n", comp_status, scsi_status, res, vha->host_no, cp->device->id, cp->device->lun, fcport->tgt_id, lscsi_status, cp->cmnd, scsi_bufflen(cp), - rsp_info_len, resid_len, fw_resid_len, sense_len, + rsp_info, resid_len, fw_resid_len, sense_len, par_sense_len, rsp_info_len); if (rsp->status_srb == NULL) @@ -3009,7 +3007,7 @@ qlafx00_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_fx00 *cmd_pkt, /* No data transfer */ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { - lcmd_pkt->byte_count = __constant_cpu_to_le32(0); + lcmd_pkt->byte_count = cpu_to_le32(0); return; } @@ -3071,7 +3069,7 @@ qlafx00_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_fx00 *cmd_pkt, int qlafx00_start_scsi(srb_t *sp) { - int ret, nseg; + int nseg; unsigned long flags; uint32_t index; uint32_t handle; @@ -3088,8 +3086,6 @@ qlafx00_start_scsi(srb_t *sp) struct scsi_lun llun; /* Setup device pointers. */ - ret = 0; - rsp = ha->rsp_q_map[0]; req = vha->req; diff --git a/kernel/drivers/scsi/qla2xxx/qla_nx.c b/kernel/drivers/scsi/qla2xxx/qla_nx.c index 7d2b18f26..b6b4cfdd7 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_nx.c +++ b/kernel/drivers/scsi/qla2xxx/qla_nx.c @@ -347,32 +347,31 @@ char *qdev_state(uint32_t dev_state) } /* - * In: 'off' is offset from CRB space in 128M pci map - * Out: 'off' is 2M pci map addr + * In: 'off_in' is offset from CRB space in 128M pci map + * Out: 'off_out' is 2M pci map addr * side effect: lock crb window */ static void -qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off) +qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong off_in, + void __iomem **off_out) { u32 win_read; scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - ha->crb_win = CRB_HI(*off); - writel(ha->crb_win, - (void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase)); + ha->crb_win = CRB_HI(off_in); + writel(ha->crb_win, CRB_WINDOW_2M + ha->nx_pcibase); /* Read back value to make sure write has gone through before trying * to use it. */ - win_read = RD_REG_DWORD((void __iomem *) - (CRB_WINDOW_2M + ha->nx_pcibase)); + win_read = RD_REG_DWORD(CRB_WINDOW_2M + ha->nx_pcibase); if (win_read != ha->crb_win) { ql_dbg(ql_dbg_p3p, vha, 0xb000, "%s: Written crbwin (0x%x) " "!= Read crbwin (0x%x), off=0x%lx.\n", - __func__, ha->crb_win, win_read, *off); + __func__, ha->crb_win, win_read, off_in); } - *off = (*off & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase; + *off_out = (off_in & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase; } static inline unsigned long @@ -417,32 +416,34 @@ qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off) } static int -qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off) +qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong off_in, + void __iomem **off_out) { struct crb_128M_2M_sub_block_map *m; - if (*off >= QLA82XX_CRB_MAX) + if (off_in >= QLA82XX_CRB_MAX) return -1; - if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) { - *off = (*off - QLA82XX_PCI_CAMQM) + + if (off_in >= QLA82XX_PCI_CAMQM && off_in < QLA82XX_PCI_CAMQM_2M_END) { + *off_out = (off_in - QLA82XX_PCI_CAMQM) + QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase; return 0; } - if (*off < QLA82XX_PCI_CRBSPACE) + if (off_in < QLA82XX_PCI_CRBSPACE) return -1; - *off -= QLA82XX_PCI_CRBSPACE; + off_in -= QLA82XX_PCI_CRBSPACE; /* Try direct map */ - m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; + m = &crb_128M_2M_map[CRB_BLK(off_in)].sub_block[CRB_SUBBLK(off_in)]; - if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) { - *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase; + if (m->valid && (m->start_128M <= off_in) && (m->end_128M > off_in)) { + *off_out = off_in + m->start_2M - m->start_128M + ha->nx_pcibase; return 0; } /* Not in direct map, use crb window */ + *off_out = (void __iomem *)off_in; return 1; } @@ -465,51 +466,61 @@ static int qla82xx_crb_win_lock(struct qla_hw_data *ha) } int -qla82xx_wr_32(struct qla_hw_data *ha, ulong off, u32 data) +qla82xx_wr_32(struct qla_hw_data *ha, ulong off_in, u32 data) { + void __iomem *off; unsigned long flags = 0; int rv; - rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + rv = qla82xx_pci_get_crb_addr_2M(ha, off_in, &off); BUG_ON(rv == -1); if (rv == 1) { +#ifndef __CHECKER__ write_lock_irqsave(&ha->hw_lock, flags); +#endif qla82xx_crb_win_lock(ha); - qla82xx_pci_set_crbwindow_2M(ha, &off); + qla82xx_pci_set_crbwindow_2M(ha, off_in, &off); } writel(data, (void __iomem *)off); if (rv == 1) { qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); +#ifndef __CHECKER__ write_unlock_irqrestore(&ha->hw_lock, flags); +#endif } return 0; } int -qla82xx_rd_32(struct qla_hw_data *ha, ulong off) +qla82xx_rd_32(struct qla_hw_data *ha, ulong off_in) { + void __iomem *off; unsigned long flags = 0; int rv; u32 data; - rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + rv = qla82xx_pci_get_crb_addr_2M(ha, off_in, &off); BUG_ON(rv == -1); if (rv == 1) { +#ifndef __CHECKER__ write_lock_irqsave(&ha->hw_lock, flags); +#endif qla82xx_crb_win_lock(ha); - qla82xx_pci_set_crbwindow_2M(ha, &off); + qla82xx_pci_set_crbwindow_2M(ha, off_in, &off); } - data = RD_REG_DWORD((void __iomem *)off); + data = RD_REG_DWORD(off); if (rv == 1) { qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); +#ifndef __CHECKER__ write_unlock_irqrestore(&ha->hw_lock, flags); +#endif } return data; } @@ -547,9 +558,6 @@ void qla82xx_idc_unlock(struct qla_hw_data *ha) qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_UNLOCK)); } -/* PCI Windowing for DDR regions. */ -#define QLA82XX_ADDR_IN_RANGE(addr, low, high) \ - (((addr) <= (high)) && ((addr) >= (low))) /* * check memory access boundary. * used by test agent. support ddr access only for now @@ -558,9 +566,9 @@ static unsigned long qla82xx_pci_mem_bound_check(struct qla_hw_data *ha, unsigned long long addr, int size) { - if (!QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + if (!addr_in_range(addr, QLA82XX_ADDR_DDR_NET, QLA82XX_ADDR_DDR_NET_MAX) || - !QLA82XX_ADDR_IN_RANGE(addr + size - 1, QLA82XX_ADDR_DDR_NET, + !addr_in_range(addr + size - 1, QLA82XX_ADDR_DDR_NET, QLA82XX_ADDR_DDR_NET_MAX) || ((size != 1) && (size != 2) && (size != 4) && (size != 8))) return 0; @@ -577,7 +585,7 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) u32 win_read; scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + if (addr_in_range(addr, QLA82XX_ADDR_DDR_NET, QLA82XX_ADDR_DDR_NET_MAX)) { /* DDR network side */ window = MN_WIN(addr); @@ -592,7 +600,7 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) __func__, window, win_read); } addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_DDR_NET; - } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + } else if (addr_in_range(addr, QLA82XX_ADDR_OCM0, QLA82XX_ADDR_OCM0_MAX)) { unsigned int temp1; if ((addr & 0x00ff800) == 0xff800) { @@ -615,7 +623,7 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) } addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_OCM0_2M; - } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, + } else if (addr_in_range(addr, QLA82XX_ADDR_QDR_NET, QLA82XX_P3_ADDR_QDR_NET_MAX)) { /* QDR network side */ window = MS_WIN(addr); @@ -656,16 +664,16 @@ static int qla82xx_pci_is_same_window(struct qla_hw_data *ha, qdr_max = QLA82XX_P3_ADDR_QDR_NET_MAX; /* DDR network side */ - if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + if (addr_in_range(addr, QLA82XX_ADDR_DDR_NET, QLA82XX_ADDR_DDR_NET_MAX)) BUG(); - else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + else if (addr_in_range(addr, QLA82XX_ADDR_OCM0, QLA82XX_ADDR_OCM0_MAX)) return 1; - else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM1, + else if (addr_in_range(addr, QLA82XX_ADDR_OCM1, QLA82XX_ADDR_OCM1_MAX)) return 1; - else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, qdr_max)) { + else if (addr_in_range(addr, QLA82XX_ADDR_QDR_NET, qdr_max)) { /* QDR network side */ window = ((addr - QLA82XX_ADDR_QDR_NET) >> 22) & 0x3f; if (ha->qdr_sn_window == window) @@ -922,20 +930,18 @@ qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag) { uint32_t off_value, rval = 0; - WRT_REG_DWORD((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase), - (off & 0xFFFF0000)); + WRT_REG_DWORD(CRB_WINDOW_2M + ha->nx_pcibase, off & 0xFFFF0000); /* Read back value to make sure write has gone through */ - RD_REG_DWORD((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase)); + RD_REG_DWORD(CRB_WINDOW_2M + ha->nx_pcibase); off_value = (off & 0x0000FFFF); if (flag) - WRT_REG_DWORD((void __iomem *) - (off_value + CRB_INDIRECT_2M + ha->nx_pcibase), - data); + WRT_REG_DWORD(off_value + CRB_INDIRECT_2M + ha->nx_pcibase, + data); else - rval = RD_REG_DWORD((void __iomem *) - (off_value + CRB_INDIRECT_2M + ha->nx_pcibase)); + rval = RD_REG_DWORD(off_value + CRB_INDIRECT_2M + + ha->nx_pcibase); return rval; } @@ -1663,8 +1669,7 @@ qla82xx_iospace_config(struct qla_hw_data *ha) } len = pci_resource_len(ha->pdev, 0); - ha->nx_pcibase = - (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len); + ha->nx_pcibase = ioremap(pci_resource_start(ha->pdev, 0), len); if (!ha->nx_pcibase) { ql_log_pci(ql_log_fatal, ha->pdev, 0x000e, "Cannot remap pcibase MMIO, aborting.\n"); @@ -1673,17 +1678,13 @@ qla82xx_iospace_config(struct qla_hw_data *ha) /* Mapping of IO base pointer */ if (IS_QLA8044(ha)) { - ha->iobase = - (device_reg_t *)((uint8_t *)ha->nx_pcibase); + ha->iobase = ha->nx_pcibase; } else if (IS_QLA82XX(ha)) { - ha->iobase = - (device_reg_t *)((uint8_t *)ha->nx_pcibase + - 0xbc000 + (ha->pdev->devfn << 11)); + ha->iobase = ha->nx_pcibase + 0xbc000 + (ha->pdev->devfn << 11); } if (!ql2xdbwr) { - ha->nxdb_wr_ptr = - (unsigned long)ioremap((pci_resource_start(ha->pdev, 4) + + ha->nxdb_wr_ptr = ioremap((pci_resource_start(ha->pdev, 4) + (ha->pdev->devfn << 12)), 4); if (!ha->nxdb_wr_ptr) { ql_log_pci(ql_log_fatal, ha->pdev, 0x000f, @@ -1694,10 +1695,10 @@ qla82xx_iospace_config(struct qla_hw_data *ha) /* Mapping of IO base pointer, * door bell read and write pointer */ - ha->nxdb_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) + + ha->nxdb_rd_ptr = ha->nx_pcibase + (512 * 1024) + (ha->pdev->devfn * 8); } else { - ha->nxdb_wr_ptr = (ha->pdev->devfn == 6 ? + ha->nxdb_wr_ptr = (void __iomem *)(ha->pdev->devfn == 6 ? QLA82XX_CAMRAM_DB1 : QLA82XX_CAMRAM_DB2); } @@ -1707,12 +1708,12 @@ qla82xx_iospace_config(struct qla_hw_data *ha) ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc006, "nx_pci_base=%p iobase=%p " "max_req_queues=%d msix_count=%d.\n", - (void *)ha->nx_pcibase, ha->iobase, + ha->nx_pcibase, ha->iobase, ha->max_req_queues, ha->msix_count); ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0010, "nx_pci_base=%p iobase=%p " "max_req_queues=%d msix_count=%d.\n", - (void *)ha->nx_pcibase, ha->iobase, + ha->nx_pcibase, ha->iobase, ha->max_req_queues, ha->msix_count); return 0; @@ -1740,8 +1741,8 @@ qla82xx_pci_config(scsi_qla_host_t *vha) ret = pci_set_mwi(ha->pdev); ha->chip_revision = ha->pdev->revision; ql_dbg(ql_dbg_init, vha, 0x0043, - "Chip revision:%d.\n", - ha->chip_revision); + "Chip revision:%d; pci_set_mwi() returned %d.\n", + ha->chip_revision, ret); return 0; } @@ -1768,8 +1769,8 @@ void qla82xx_config_rings(struct scsi_qla_host *vha) /* Setup ring parameters in initialization control block. */ icb = (struct init_cb_81xx *)ha->init_cb; - icb->request_q_outpointer = __constant_cpu_to_le16(0); - icb->response_q_inpointer = __constant_cpu_to_le16(0); + icb->request_q_outpointer = cpu_to_le16(0); + icb->response_q_inpointer = cpu_to_le16(0); icb->request_q_length = cpu_to_le16(req->length); icb->response_q_length = cpu_to_le16(rsp->length); icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); @@ -1777,9 +1778,9 @@ void qla82xx_config_rings(struct scsi_qla_host *vha) icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); - WRT_REG_DWORD((unsigned long __iomem *)®->req_q_out[0], 0); - WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_in[0], 0); - WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_out[0], 0); + WRT_REG_DWORD(®->req_q_out[0], 0); + WRT_REG_DWORD(®->rsp_q_in[0], 0); + WRT_REG_DWORD(®->rsp_q_out[0], 0); } static int @@ -1843,7 +1844,7 @@ qla82xx_set_product_offset(struct qla_hw_data *ha) ptab_desc = qla82xx_get_table_desc(unirom, QLA82XX_URI_DIR_SECT_PRODUCT_TBL); - if (!ptab_desc) + if (!ptab_desc) return -1; entries = cpu_to_le32(ptab_desc->num_entries); @@ -2298,7 +2299,7 @@ void qla82xx_init_flags(struct qla_hw_data *ha) ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg; } -inline void +static inline void qla82xx_set_idc_version(scsi_qla_host_t *vha) { int idc_ver; @@ -2481,14 +2482,12 @@ try_blob_fw: ql_log(ql_log_info, vha, 0x00a5, "Firmware loaded successfully from binary blob.\n"); return QLA_SUCCESS; - } else { - ql_log(ql_log_fatal, vha, 0x00a6, - "Firmware load failed for binary blob.\n"); - blob->fw = NULL; - blob = NULL; - goto fw_load_failed; } - return QLA_SUCCESS; + + ql_log(ql_log_fatal, vha, 0x00a6, + "Firmware load failed for binary blob.\n"); + blob->fw = NULL; + blob = NULL; fw_load_failed: return QLA_FUNCTION_FAILED; @@ -2549,7 +2548,7 @@ qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, "Do ROM fast read failed.\n"); goto done_read; } - dwptr[i] = __constant_cpu_to_le32(val); + dwptr[i] = cpu_to_le32(val); } done_read: return dwptr; @@ -2671,7 +2670,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, { int ret; uint32_t liter; - uint32_t sec_mask, rest_addr; + uint32_t rest_addr; dma_addr_t optrom_dma; void *optrom = NULL; int page_mode = 0; @@ -2693,7 +2692,6 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, } rest_addr = ha->fdt_block_size - 1; - sec_mask = ~rest_addr; ret = qla82xx_unprotect_flash(ha); if (ret) { @@ -2789,7 +2787,6 @@ qla82xx_start_iocbs(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; - struct device_reg_82xx __iomem *reg; uint32_t dbval; /* Adjust ring index. */ @@ -2800,18 +2797,16 @@ qla82xx_start_iocbs(scsi_qla_host_t *vha) } else req->ring_ptr++; - reg = &ha->iobase->isp82; dbval = 0x04 | (ha->portnum << 5); dbval = dbval | (req->id << 8) | (req->ring_index << 16); if (ql2xdbwr) - qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + qla82xx_wr_32(ha, (unsigned long)ha->nxdb_wr_ptr, dbval); else { - WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); + WRT_REG_DWORD(ha->nxdb_wr_ptr, dbval); wmb(); - while (RD_REG_DWORD((void __iomem *)ha->nxdb_rd_ptr) != dbval) { - WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, - dbval); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD(ha->nxdb_wr_ptr, dbval); wmb(); } } @@ -3842,8 +3837,7 @@ qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha, loop_cnt = ocm_hdr->op_count; for (i = 0; i < loop_cnt; i++) { - r_value = RD_REG_DWORD((void __iomem *) - (r_addr + ha->nx_pcibase)); + r_value = RD_REG_DWORD(r_addr + ha->nx_pcibase); *data_ptr++ = cpu_to_le32(r_value); r_addr += r_stride; } diff --git a/kernel/drivers/scsi/qla2xxx/qla_nx2.c b/kernel/drivers/scsi/qla2xxx/qla_nx2.c index ed4d6b6b5..007192d7b 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_nx2.c +++ b/kernel/drivers/scsi/qla2xxx/qla_nx2.c @@ -397,11 +397,11 @@ qla8044_idc_lock(struct qla_hw_data *ha) * has the lock, wait for 2secs * and retry */ - ql_dbg(ql_dbg_p3p, vha, 0xb08a, - "%s: IDC lock Recovery by %d " - "failed, Retrying timeout\n", __func__, - ha->portnum); - timeout = 0; + ql_dbg(ql_dbg_p3p, vha, 0xb08a, + "%s: IDC lock Recovery by %d " + "failed, Retrying timeout\n", __func__, + ha->portnum); + timeout = 0; } } msleep(QLA8044_DRV_LOCK_MSLEEP); @@ -462,12 +462,11 @@ qla8044_flash_lock(scsi_qla_host_t *vha) static void qla8044_flash_unlock(scsi_qla_host_t *vha) { - int ret_val; struct qla_hw_data *ha = vha->hw; /* Reading FLASH_UNLOCK register unlocks the Flash */ qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, 0xFF); - ret_val = qla8044_rd_reg(ha, QLA8044_FLASH_UNLOCK); + qla8044_rd_reg(ha, QLA8044_FLASH_UNLOCK); } @@ -561,7 +560,7 @@ qla8044_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, return buf; } -inline int +static inline int qla8044_need_reset(struct scsi_qla_host *vha) { uint32_t drv_state, drv_active; @@ -1130,9 +1129,9 @@ qla8044_ms_mem_write_128b(struct scsi_qla_host *vha, } for (i = 0; i < count; i++, addr += 16) { - if (!((QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_QDR_NET, + if (!((addr_in_range(addr, QLA8044_ADDR_QDR_NET, QLA8044_ADDR_QDR_NET_MAX)) || - (QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_DDR_NET, + (addr_in_range(addr, QLA8044_ADDR_DDR_NET, QLA8044_ADDR_DDR_NET_MAX)))) { ret_val = QLA_FUNCTION_FAILED; goto exit_ms_mem_write_unlock; @@ -1605,7 +1604,7 @@ qla8044_set_idc_dontreset(struct scsi_qla_host *vha) qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl); } -inline void +static inline void qla8044_set_rst_ready(struct scsi_qla_host *vha) { uint32_t drv_state; @@ -2992,7 +2991,7 @@ qla8044_minidump_process_rddfe(struct scsi_qla_host *vha, uint32_t addr1, addr2, value, data, temp, wrVal; uint8_t stride, stride2; uint16_t count; - uint32_t poll, mask, data_size, modify_mask; + uint32_t poll, mask, modify_mask; uint32_t wait_count = 0; uint32_t *data_ptr = *d_ptr; @@ -3009,7 +3008,6 @@ qla8044_minidump_process_rddfe(struct scsi_qla_host *vha, poll = rddfe->poll; mask = rddfe->mask; modify_mask = rddfe->modify_mask; - data_size = rddfe->data_size; addr2 = addr1 + stride; @@ -3091,7 +3089,7 @@ qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha, uint8_t stride1, stride2; uint32_t addr3, addr4, addr5, addr6, addr7; uint16_t count, loop_cnt; - uint32_t poll, mask; + uint32_t mask; uint32_t *data_ptr = *d_ptr; struct qla8044_minidump_entry_rdmdio *rdmdio; @@ -3105,7 +3103,6 @@ qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha, stride2 = rdmdio->stride_2; count = rdmdio->count; - poll = rdmdio->poll; mask = rdmdio->mask; value2 = rdmdio->value_2; @@ -3141,8 +3138,7 @@ qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha, goto error; addr7 = addr2 - (4 * stride1); - data = qla8044_ipmdio_rd_reg(vha, addr1, addr3, - mask, addr7); + data = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr7); if (data == -1) goto error; @@ -3165,7 +3161,7 @@ error: static uint32_t qla8044_minidump_process_pollwr(struct scsi_qla_host *vha, struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) { - uint32_t addr1, addr2, value1, value2, poll, mask, r_value; + uint32_t addr1, addr2, value1, value2, poll, r_value; uint32_t wait_count = 0; struct qla8044_minidump_entry_pollwr *pollwr_hdr; @@ -3176,7 +3172,6 @@ static uint32_t qla8044_minidump_process_pollwr(struct scsi_qla_host *vha, value2 = pollwr_hdr->value_2; poll = pollwr_hdr->poll; - mask = pollwr_hdr->mask; while (wait_count < poll) { qla8044_rd_reg_indirect(vha, addr1, &r_value); diff --git a/kernel/drivers/scsi/qla2xxx/qla_nx2.h b/kernel/drivers/scsi/qla2xxx/qla_nx2.h index ada36057d..02fe3c4cd 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_nx2.h +++ b/kernel/drivers/scsi/qla2xxx/qla_nx2.h @@ -58,8 +58,10 @@ #define QLA8044_PCI_QDR_NET_MAX ((unsigned long)0x043fffff) /* PCI Windowing for DDR regions. */ -#define QLA8044_ADDR_IN_RANGE(addr, low, high) \ - (((addr) <= (high)) && ((addr) >= (low))) +static inline bool addr_in_range(u64 addr, u64 low, u64 high) +{ + return addr <= high && addr >= low; +} /* Indirectly Mapped Registers */ #define QLA8044_FLASH_SPI_STATUS 0x2808E010 diff --git a/kernel/drivers/scsi/qla2xxx/qla_os.c b/kernel/drivers/scsi/qla2xxx/qla_os.c index 7462dd70b..fc6674db4 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_os.c +++ b/kernel/drivers/scsi/qla2xxx/qla_os.c @@ -267,7 +267,6 @@ struct scsi_host_template qla2xxx_driver_template = { .shost_attrs = qla2x00_host_attrs, .supported_mode = MODE_INITIATOR, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -398,6 +397,9 @@ static void qla2x00_free_queues(struct qla_hw_data *ha) int cnt; for (cnt = 0; cnt < ha->max_req_queues; cnt++) { + if (!test_bit(cnt, ha->req_qid_map)) + continue; + req = ha->req_q_map[cnt]; qla2x00_free_req_que(ha, req); } @@ -405,6 +407,9 @@ static void qla2x00_free_queues(struct qla_hw_data *ha) ha->req_q_map = NULL; for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) { + if (!test_bit(cnt, ha->rsp_qid_map)) + continue; + rsp = ha->rsp_q_map[cnt]; qla2x00_free_rsp_que(ha, rsp); } @@ -656,7 +661,7 @@ qla2x00_sp_compl(void *data, void *ptr, int res) "SP reference-count to ZERO -- sp=%p cmd=%p.\n", sp, GET_CMD_SP(sp)); if (ql2xextended_error_logging & ql_dbg_io) - BUG(); + WARN_ON(atomic_read(&sp->ref_count) == 0); return; } if (!atomic_dec_and_test(&sp->ref_count)) @@ -958,8 +963,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } ql_dbg(ql_dbg_taskm, vha, 0x8002, - "Aborting from RISC nexus=%ld:%d:%llu sp=%p cmd=%p\n", - vha->host_no, id, lun, sp, cmd); + "Aborting from RISC nexus=%ld:%d:%llu sp=%p cmd=%p handle=%x\n", + vha->host_no, id, lun, sp, cmd, sp->handle); /* Get a reference to the sp and drop the lock.*/ sp_get(sp); @@ -967,14 +972,9 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) spin_unlock_irqrestore(&ha->hardware_lock, flags); rval = ha->isp_ops->abort_command(sp); if (rval) { - if (rval == QLA_FUNCTION_PARAMETER_ERROR) { - /* - * Decrement the ref_count since we can't find the - * command - */ - atomic_dec(&sp->ref_count); + if (rval == QLA_FUNCTION_PARAMETER_ERROR) ret = SUCCESS; - } else + else ret = FAILED; ql_dbg(ql_dbg_taskm, vha, 0x8003, @@ -986,12 +986,6 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } spin_lock_irqsave(&ha->hardware_lock, flags); - /* - * Clear the slot in the oustanding_cmds array if we can't find the - * command to reclaim the resources. - */ - if (rval == QLA_FUNCTION_PARAMETER_ERROR) - vha->req->outstanding_cmds[sp->handle] = NULL; sp->done(ha, sp, 0); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -2219,6 +2213,13 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; + case PCI_DEVICE_ID_QLOGIC_ISP2261: + ha->device_type |= DT_ISP2261; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; } if (IS_QLA82XX(ha)) @@ -2296,7 +2297,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pdev->device == PCI_DEVICE_ID_QLOGIC_ISPF001 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8044 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2071 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2271) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2271 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2261) { bars = pci_select_bars(pdev, IORESOURCE_MEM); mem_only = 1; ql_dbg_pci(ql_dbg_init, pdev, 0x0007, @@ -2504,6 +2506,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; rsp_length = RESPONSE_ENTRY_CNT_2300; + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_81xx); ha->gid_list_info_size = 8; @@ -2973,7 +2976,6 @@ qla2x00_shutdown(struct pci_dev *pdev) static void qla2x00_delete_all_vps(struct qla_hw_data *ha, scsi_qla_host_t *base_vha) { - struct Scsi_Host *scsi_host; scsi_qla_host_t *vha; unsigned long flags; @@ -2984,7 +2986,7 @@ qla2x00_delete_all_vps(struct qla_hw_data *ha, scsi_qla_host_t *base_vha) BUG_ON(base_vha->list.next == &ha->vp_list); /* This assumes first entry in ha->vp_list is always base vha */ vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list); - scsi_host = scsi_host_get(vha->host); + scsi_host_get(vha->host); spin_unlock_irqrestore(&ha->vport_slock, flags); mutex_unlock(&ha->vport_lock); @@ -3229,11 +3231,15 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, spin_lock_irqsave(vha->host->host_lock, flags); fcport->drport = rport; spin_unlock_irqrestore(vha->host->host_lock, flags); + qlt_do_generation_tick(vha, &base_vha->total_fcport_update_gen); set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); qla2xxx_wake_dpc(base_vha); } else { - fc_remote_port_delete(rport); - qlt_fc_port_deleted(vha, fcport); + int now; + if (rport) + fc_remote_port_delete(rport); + qlt_do_generation_tick(vha, &now); + qlt_fc_port_deleted(vha, fcport, now); } } @@ -3270,9 +3276,10 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, if (!do_login) return; + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + if (fcport->login_retry == 0) { fcport->login_retry = vha->hw->login_retry_count; - set_bit(RELOGIN_NEEDED, &vha->dpc_flags); ql_dbg(ql_dbg_disc, vha, 0x2067, "Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", @@ -3763,8 +3770,11 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, INIT_LIST_HEAD(&vha->vp_fcports); INIT_LIST_HEAD(&vha->work_list); INIT_LIST_HEAD(&vha->list); + INIT_LIST_HEAD(&vha->qla_cmd_list); + INIT_LIST_HEAD(&vha->qla_sess_op_cmd_list); spin_lock_init(&vha->work_lock); + spin_lock_init(&vha->cmd_list_lock); sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no); ql_dbg(ql_dbg_init, vha, 0x0041, @@ -4418,7 +4428,10 @@ retry_lock2: void qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id) { - uint16_t options = (requester_id << 15) | BIT_7, retry; +#if 0 + uint16_t options = (requester_id << 15) | BIT_7; +#endif + uint16_t retry; uint32_t data; struct qla_hw_data *ha = base_vha->hw; @@ -4454,6 +4467,7 @@ retry_unlock: return; +#if 0 /* XXX: IDC-unlock implementation using access-control mbx */ retry = 0; retry_unlock2: @@ -4469,6 +4483,7 @@ retry_unlock2: } return; +#endif } int @@ -4788,7 +4803,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) static int qla2x00_do_dpc(void *data) { - int rval; scsi_qla_host_t *base_vha; struct qla_hw_data *ha; @@ -5020,7 +5034,7 @@ loop_resync_check: if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &base_vha->dpc_flags))) { - rval = qla2x00_loop_resync(base_vha); + qla2x00_loop_resync(base_vha); clear_bit(LOOP_RESYNC_ACTIVE, &base_vha->dpc_flags); @@ -5704,6 +5718,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8044) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2071) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2271) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2261) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); diff --git a/kernel/drivers/scsi/qla2xxx/qla_sup.c b/kernel/drivers/scsi/qla2xxx/qla_sup.c index 028e8c8a7..3272ed5bb 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_sup.c +++ b/kernel/drivers/scsi/qla2xxx/qla_sup.c @@ -316,7 +316,7 @@ qla2x00_clear_nvram_protection(struct qla_hw_data *ha) wprot_old = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base)); stat = qla2x00_write_nvram_word_tmo(ha, ha->nvram_base, - __constant_cpu_to_le16(0x1234), 100000); + cpu_to_le16(0x1234), 100000); wprot = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base)); if (stat != QLA_SUCCESS || wprot != 0x1234) { /* Write enable. */ @@ -691,9 +691,9 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) region = (struct qla_flt_region *)&flt[1]; ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, flt_addr << 2, OPTROM_BURST_SIZE); - if (*wptr == __constant_cpu_to_le16(0xffff)) + if (*wptr == cpu_to_le16(0xffff)) goto no_flash_data; - if (flt->version != __constant_cpu_to_le16(1)) { + if (flt->version != cpu_to_le16(1)) { ql_log(ql_log_warn, vha, 0x0047, "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n", le16_to_cpu(flt->version), le16_to_cpu(flt->length), @@ -892,7 +892,7 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) fdt = (struct qla_fdt_layout *)req->ring; ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, ha->flt_region_fdt << 2, OPTROM_BURST_SIZE); - if (*wptr == __constant_cpu_to_le16(0xffff)) + if (*wptr == cpu_to_le16(0xffff)) goto no_flash_data; if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' || fdt->sig[3] != 'D') @@ -991,7 +991,7 @@ qla2xxx_get_idc_param(scsi_qla_host_t *vha) ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, QLA82XX_IDC_PARAM_ADDR , 8); - if (*wptr == __constant_cpu_to_le32(0xffffffff)) { + if (*wptr == cpu_to_le32(0xffffffff)) { ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; ha->fcoe_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; } else { @@ -1051,9 +1051,9 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); - if (hdr.version == __constant_cpu_to_le16(0xffff)) + if (hdr.version == cpu_to_le16(0xffff)) return; - if (hdr.version != __constant_cpu_to_le16(1)) { + if (hdr.version != cpu_to_le16(1)) { ql_dbg(ql_dbg_user, vha, 0x7090, "Unsupported NPIV-Config " "detected: version=0x%x entries=0x%x checksum=0x%x.\n", @@ -1697,7 +1697,7 @@ qla83xx_select_led_port(struct qla_hw_data *ha) { uint32_t led_select_value = 0; - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) goto out; if (ha->port_no == 0) diff --git a/kernel/drivers/scsi/qla2xxx/qla_target.c b/kernel/drivers/scsi/qla2xxx/qla_target.c index 496a733d0..75514a15b 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_target.c +++ b/kernel/drivers/scsi/qla2xxx/qla_target.c @@ -114,6 +114,10 @@ static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, struct atio_from_isp *atio, uint16_t status, int qfull); static void qlt_disable_vha(struct scsi_qla_host *vha); static void qlt_clear_tgt_db(struct qla_tgt *tgt); +static void qlt_send_notify_ack(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *ntfy, + uint32_t add_flags, uint16_t resp_code, int resp_code_valid, + uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan); /* * Global Variables */ @@ -123,6 +127,16 @@ static struct workqueue_struct *qla_tgt_wq; static DEFINE_MUTEX(qla_tgt_mutex); static LIST_HEAD(qla_tgt_glist); +/* This API intentionally takes dest as a parameter, rather than returning + * int value to avoid caller forgetting to issue wmb() after the store */ +void qlt_do_generation_tick(struct scsi_qla_host *vha, int *dest) +{ + scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev); + *dest = atomic_inc_return(&base_vha->generation_tick); + /* memory barrier */ + wmb(); +} + /* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ static struct qla_tgt_sess *qlt_find_sess_by_port_name( struct qla_tgt *tgt, @@ -382,14 +396,73 @@ static void qlt_free_session_done(struct work_struct *work) struct qla_tgt *tgt = sess->tgt; struct scsi_qla_host *vha = sess->vha; struct qla_hw_data *ha = vha->hw; + unsigned long flags; + bool logout_started = false; + fc_port_t fcport; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084, + "%s: se_sess %p / sess %p from port %8phC loop_id %#04x" + " s_id %02x:%02x:%02x logout %d keep %d plogi %d\n", + __func__, sess->se_sess, sess, sess->port_name, sess->loop_id, + sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, + sess->logout_on_delete, sess->keep_nport_handle, + sess->plogi_ack_needed); BUG_ON(!tgt); + + if (sess->logout_on_delete) { + int rc; + + memset(&fcport, 0, sizeof(fcport)); + fcport.loop_id = sess->loop_id; + fcport.d_id = sess->s_id; + memcpy(fcport.port_name, sess->port_name, WWN_SIZE); + fcport.vha = vha; + fcport.tgt_session = sess; + + rc = qla2x00_post_async_logout_work(vha, &fcport, NULL); + if (rc != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0xf085, + "Schedule logo failed sess %p rc %d\n", + sess, rc); + else + logout_started = true; + } + /* * Release the target session for FC Nexus from fabric module code. */ if (sess->se_sess != NULL) ha->tgt.tgt_ops->free_session(sess); + if (logout_started) { + bool traced = false; + + while (!ACCESS_ONCE(sess->logout_completed)) { + if (!traced) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf086, + "%s: waiting for sess %p logout\n", + __func__, sess); + traced = true; + } + msleep(100); + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf087, + "%s: sess %p logout completed\n", + __func__, sess); + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (sess->plogi_ack_needed) + qlt_send_notify_ack(vha, &sess->tm_iocb, + 0, 0, 0, 0, 0, 0); + + list_del(&sess->sess_list_entry); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, "Unregistration of sess %p finished\n", sess); @@ -410,9 +483,9 @@ void qlt_unreg_sess(struct qla_tgt_sess *sess) vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); - list_del(&sess->sess_list_entry); - if (sess->deleted) - list_del(&sess->del_list_entry); + if (!list_empty(&sess->del_list_entry)) + list_del_init(&sess->del_list_entry); + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; INIT_WORK(&sess->free_work, qlt_free_session_done); schedule_work(&sess->free_work); @@ -490,27 +563,38 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, struct qla_tgt *tgt = sess->tgt; uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; - if (sess->deleted) - return; + if (sess->deleted) { + /* Upgrade to unconditional deletion in case it was temporary */ + if (immediate && sess->deleted == QLA_SESS_DELETION_PENDING) + list_del(&sess->del_list_entry); + else + return; + } ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, "Scheduling sess %p for deletion\n", sess); - list_add_tail(&sess->del_list_entry, &tgt->del_sess_list); - sess->deleted = 1; - if (immediate) + if (immediate) { dev_loss_tmo = 0; + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; + list_add(&sess->del_list_entry, &tgt->del_sess_list); + } else { + sess->deleted = QLA_SESS_DELETION_PENDING; + list_add_tail(&sess->del_list_entry, &tgt->del_sess_list); + } sess->expires = jiffies + dev_loss_tmo * HZ; ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, - "qla_target(%d): session for port %8phC (loop ID %d) scheduled for " - "deletion in %u secs (expires: %lu) immed: %d\n", - sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo, - sess->expires, immediate); + "qla_target(%d): session for port %8phC (loop ID %d s_id %02x:%02x:%02x)" + " scheduled for deletion in %u secs (expires: %lu) immed: %d, logout: %d, gen: %#x\n", + sess->vha->vp_idx, sess->port_name, sess->loop_id, + sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa, + dev_loss_tmo, sess->expires, immediate, sess->logout_on_delete, + sess->generation); if (immediate) - schedule_delayed_work(&tgt->sess_del_work, 0); + mod_delayed_work(system_wq, &tgt->sess_del_work, 0); else schedule_delayed_work(&tgt->sess_del_work, sess->expires - jiffies); @@ -579,9 +663,9 @@ out_free_id_list: /* ha->hardware_lock supposed to be held on entry */ static void qlt_undelete_sess(struct qla_tgt_sess *sess) { - BUG_ON(!sess->deleted); + BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING); - list_del(&sess->del_list_entry); + list_del_init(&sess->del_list_entry); sess->deleted = 0; } @@ -600,7 +684,9 @@ static void qlt_del_sess_work_fn(struct delayed_work *work) del_list_entry); elapsed = jiffies; if (time_after_eq(elapsed, sess->expires)) { - qlt_undelete_sess(sess); + /* No turning back */ + list_del_init(&sess->del_list_entry); + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, "Timeout: sess %p about to be deleted\n", @@ -644,6 +730,13 @@ static struct qla_tgt_sess *qlt_create_sess( fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id); + /* Cannot undelete at this point */ + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + return NULL; + } + if (sess->deleted) qlt_undelete_sess(sess); @@ -653,6 +746,9 @@ static struct qla_tgt_sess *qlt_create_sess( if (sess->local && !local) sess->local = 0; + + qlt_do_generation_tick(vha, &sess->generation); + spin_unlock_irqrestore(&ha->hardware_lock, flags); return sess; @@ -674,6 +770,14 @@ static struct qla_tgt_sess *qlt_create_sess( sess->s_id = fcport->d_id; sess->loop_id = fcport->loop_id; sess->local = local; + INIT_LIST_HEAD(&sess->del_list_entry); + + /* Under normal circumstances we want to logout from firmware when + * session eventually ends and release corresponding nport handle. + * In the exception cases (e.g. when new PLOGI is waiting) corresponding + * code will adjust these flags as necessary. */ + sess->logout_on_delete = 1; + sess->keep_nport_handle = 0; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", @@ -706,6 +810,7 @@ static struct qla_tgt_sess *qlt_create_sess( spin_lock_irqsave(&ha->hardware_lock, flags); list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list); vha->vha_tgt.qla_tgt->sess_count++; + qlt_do_generation_tick(vha, &sess->generation); spin_unlock_irqrestore(&ha->hardware_lock, flags); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, @@ -719,7 +824,7 @@ static struct qla_tgt_sess *qlt_create_sess( } /* - * Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port() + * Called from qla2x00_reg_remote_port() */ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) { @@ -751,6 +856,10 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) mutex_unlock(&vha->vha_tgt.tgt_mutex); spin_lock_irqsave(&ha->hardware_lock, flags); + } else if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + /* Point of no return */ + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; } else { kref_get(&sess->se_sess->sess_kref); @@ -781,7 +890,12 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) spin_unlock_irqrestore(&ha->hardware_lock, flags); } -void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport) +/* + * max_gen - specifies maximum session generation + * at which this deletion requestion is still valid + */ +void +qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport, int max_gen) { struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt_sess *sess; @@ -800,6 +914,15 @@ void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport) return; } + if (max_gen - sess->generation < 0) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf092, + "Ignoring stale deletion request for se_sess %p / sess %p" + " for port %8phC, req_gen %d, sess_gen %d\n", + sess->se_sess, sess, sess->port_name, max_gen, + sess->generation); + return; + } + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess); sess->local = 1; @@ -1018,7 +1141,7 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { nack->u.isp24.flags = ntfy->u.isp24.flags & - __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); } nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id; nack->u.isp24.status = ntfy->u.isp24.status; @@ -1076,7 +1199,7 @@ static void qlt_24xx_send_abts_resp(struct scsi_qla_host *vha, resp->sof_type = abts->sof_type; resp->exchange_address = abts->exchange_address; resp->fcp_hdr_le = abts->fcp_hdr_le; - f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | + f_ctl = cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | F_CTL_LAST_SEQ | F_CTL_END_SEQ | F_CTL_SEQ_INITIATIVE); p = (uint8_t *)&f_ctl; @@ -1151,15 +1274,14 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha, ctio->entry_count = 1; ctio->nport_handle = entry->nport_handle; ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; - ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); ctio->vp_index = vha->vp_idx; ctio->initiator_id[0] = entry->fcp_hdr_le.d_id[0]; ctio->initiator_id[1] = entry->fcp_hdr_le.d_id[1]; ctio->initiator_id[2] = entry->fcp_hdr_le.d_id[2]; ctio->exchange_addr = entry->exchange_addr_to_abort; - ctio->u.status1.flags = - __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | - CTIO7_FLAGS_TERMINATE); + ctio->u.status1.flags = cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | + CTIO7_FLAGS_TERMINATE); ctio->u.status1.ox_id = cpu_to_le16(entry->fcp_hdr_le.ox_id); /* Memory Barrier */ @@ -1170,6 +1292,70 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha, FCP_TMF_CMPL, true); } +static int abort_cmd_for_tag(struct scsi_qla_host *vha, uint32_t tag) +{ + struct qla_tgt_sess_op *op; + struct qla_tgt_cmd *cmd; + + spin_lock(&vha->cmd_list_lock); + + list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) { + if (tag == op->atio.u.isp24.exchange_addr) { + op->aborted = true; + spin_unlock(&vha->cmd_list_lock); + return 1; + } + } + + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { + if (tag == cmd->atio.u.isp24.exchange_addr) { + cmd->state = QLA_TGT_STATE_ABORTED; + spin_unlock(&vha->cmd_list_lock); + return 1; + } + } + + spin_unlock(&vha->cmd_list_lock); + return 0; +} + +/* drop cmds for the given lun + * XXX only looks for cmds on the port through which lun reset was recieved + * XXX does not go through the list of other port (which may have cmds + * for the same lun) + */ +static void abort_cmds_for_lun(struct scsi_qla_host *vha, + uint32_t lun, uint8_t *s_id) +{ + struct qla_tgt_sess_op *op; + struct qla_tgt_cmd *cmd; + uint32_t key; + + key = sid_to_key(s_id); + spin_lock(&vha->cmd_list_lock); + list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) { + uint32_t op_key; + uint32_t op_lun; + + op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id); + op_lun = scsilun_to_int( + (struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun); + if (op_key == key && op_lun == lun) + op->aborted = true; + } + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { + uint32_t cmd_key; + uint32_t cmd_lun; + + cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id); + cmd_lun = scsilun_to_int( + (struct scsi_lun *)&cmd->atio.u.isp24.fcp_cmnd.lun); + if (cmd_key == key && cmd_lun == lun) + cmd->state = QLA_TGT_STATE_ABORTED; + } + spin_unlock(&vha->cmd_list_lock); +} + /* ha->hardware_lock supposed to be held on entry */ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess) @@ -1186,7 +1372,7 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, list_for_each_entry(se_cmd, &se_sess->sess_cmd_list, se_cmd_list) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - if (cmd->tag == abts->exchange_addr_to_abort) { + if (se_cmd->tag == abts->exchange_addr_to_abort) { lun = cmd->unpacked_lun; found_lun = true; break; @@ -1194,8 +1380,19 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, } spin_unlock(&se_sess->sess_cmd_lock); - if (!found_lun) - return -ENOENT; + /* cmd not in LIO lists, look in qla list */ + if (!found_lun) { + if (abort_cmd_for_tag(vha, abts->exchange_addr_to_abort)) { + /* send TASK_ABORT response immediately */ + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_CMPL, false); + return 0; + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf081, + "unable to find cmd in driver or LIO for tag 0x%x\n", + abts->exchange_addr_to_abort); + return -ENOENT; + } + } ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f, "qla_target(%d): task abort (tag=%d)\n", @@ -1279,6 +1476,11 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, return; } + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); + return; + } + rc = __qlt_24xx_handle_abts(vha, abts, sess); if (rc != 0) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054, @@ -1319,20 +1521,19 @@ static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha, ctio->entry_count = 1; ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; ctio->nport_handle = mcmd->sess->loop_id; - ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); ctio->vp_index = ha->vp_idx; ctio->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; ctio->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; ctio->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; ctio->exchange_addr = atio->u.isp24.exchange_addr; ctio->u.status1.flags = (atio->u.isp24.attr << 9) | - __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | - CTIO7_FLAGS_SEND_STATUS); + cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS); temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); ctio->u.status1.ox_id = cpu_to_le16(temp); ctio->u.status1.scsi_status = - __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID); - ctio->u.status1.response_len = __constant_cpu_to_le16(8); + cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID); + ctio->u.status1.response_len = cpu_to_le16(8); ctio->u.status1.sense_data[0] = resp_code; /* Memory Barrier */ @@ -1583,7 +1784,7 @@ static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm, pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK; pkt->nport_handle = prm->cmd->loop_id; - pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + pkt->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; @@ -1721,21 +1922,6 @@ static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd, struct qla_hw_data *ha = vha->hw; struct se_cmd *se_cmd = &cmd->se_cmd; - if (unlikely(cmd->aborted)) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, - "qla_target(%d): terminating exchange " - "for aborted cmd=%p (se_cmd=%p, tag=%d)", vha->vp_idx, cmd, - se_cmd, cmd->tag); - - cmd->state = QLA_TGT_STATE_ABORTED; - cmd->cmd_flags |= BIT_6; - - qlt_send_term_exchange(vha, cmd, &cmd->atio, 0); - - /* !! At this point cmd could be already freed !! */ - return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED; - } - prm->cmd = cmd; prm->tgt = tgt; prm->rq_result = scsi_status; @@ -1760,18 +1946,17 @@ static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd, if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { prm->residual = se_cmd->residual_count; ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x305c, - "Residual underflow: %d (tag %d, " - "op %x, bufflen %d, rq_result %x)\n", prm->residual, - cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, - cmd->bufflen, prm->rq_result); + "Residual underflow: %d (tag %lld, op %x, bufflen %d, rq_result %x)\n", + prm->residual, se_cmd->tag, + se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, + cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_UNDER; } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { prm->residual = se_cmd->residual_count; ql_dbg(ql_dbg_io, vha, 0x305d, - "Residual overflow: %d (tag %d, " - "op %x, bufflen %d, rq_result %x)\n", prm->residual, - cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, - cmd->bufflen, prm->rq_result); + "Residual overflow: %d (tag %lld, op %x, bufflen %d, rq_result %x)\n", + prm->residual, se_cmd->tag, se_cmd->t_task_cdb ? + se_cmd->t_task_cdb[0] : 0, cmd->bufflen, prm->rq_result); prm->rq_result |= SS_RESIDUAL_OVER; } @@ -1844,7 +2029,7 @@ static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) == 50) { *xmit_type &= ~QLA_TGT_XMIT_STATUS; ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf015, - "Dropping cmd %p (tag %d) status", cmd, cmd->tag); + "Dropping cmd %p (tag %d) status", cmd, se_cmd->tag); } #endif /* @@ -1868,7 +2053,7 @@ static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf016, "Cutting cmd %p (tag %d) buffer" " tail to len %d, sg_cnt %d (cmd->bufflen %d," - " cmd->sg_cnt %d)", cmd, cmd->tag, tot_len, leave, + " cmd->sg_cnt %d)", cmd, se_cmd->tag, tot_len, leave, cmd->bufflen, cmd->sg_cnt); cmd->bufflen = tot_len; @@ -1880,13 +2065,13 @@ static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf017, "Cutting cmd %p (tag %d) buffer head " - "to offset %d (cmd->bufflen %d)", cmd, cmd->tag, offset, + "to offset %d (cmd->bufflen %d)", cmd, se_cmd->tag, offset, cmd->bufflen); if (offset == 0) *xmit_type &= ~QLA_TGT_XMIT_DATA; else if (qlt_set_data_offset(cmd, offset)) { ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf018, - "qlt_set_data_offset() failed (tag %d)", cmd->tag); + "qlt_set_data_offset() failed (tag %d)", se_cmd->tag); } } } @@ -1900,10 +2085,9 @@ static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio, { prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len, (uint32_t)sizeof(ctio->u.status1.sense_data)); - ctio->u.status0.flags |= - __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); + ctio->u.status0.flags |= cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) { - ctio->u.status0.flags |= __constant_cpu_to_le16( + ctio->u.status0.flags |= cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } @@ -1920,17 +2104,17 @@ static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio, "non GOOD status\n"); goto skip_explict_conf; } - ctio->u.status1.flags |= __constant_cpu_to_le16( + ctio->u.status1.flags |= cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } skip_explict_conf: ctio->u.status1.flags &= - ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); ctio->u.status1.flags |= - __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); ctio->u.status1.scsi_status |= - __constant_cpu_to_le16(SS_SENSE_LEN_VALID); + cpu_to_le16(SS_SENSE_LEN_VALID); ctio->u.status1.sense_length = cpu_to_le16(prm->sense_buffer_len); for (i = 0; i < prm->sense_buffer_len/4; i++) @@ -1950,9 +2134,9 @@ skip_explict_conf: #endif } else { ctio->u.status1.flags &= - ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ~cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); ctio->u.status1.flags |= - __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); ctio->u.status1.sense_length = 0; memset(ctio->u.status1.sense_data, 0, sizeof(ctio->u.status1.sense_data)); @@ -2074,7 +2258,6 @@ static inline int qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) { uint32_t *cur_dsd; - int sgc; uint32_t transfer_length = 0; uint32_t data_bytes; uint32_t dif_bytes; @@ -2091,7 +2274,6 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) struct atio_from_isp *atio = &prm->cmd->atio; uint16_t t16; - sgc = 0; ha = vha->hw; pkt = (struct ctio_crc2_to_fw *)vha->req->ring_ptr; @@ -2181,7 +2363,7 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK; pkt->nport_handle = prm->cmd->loop_id; - pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + pkt->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; @@ -2197,9 +2379,9 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) /* Set transfer direction */ if (cmd->dma_data_direction == DMA_TO_DEVICE) - pkt->flags = __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN); + pkt->flags = cpu_to_le16(CTIO7_FLAGS_DATA_IN); else if (cmd->dma_data_direction == DMA_FROM_DEVICE) - pkt->flags = __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT); + pkt->flags = cpu_to_le16(CTIO7_FLAGS_DATA_OUT); pkt->dseg_count = prm->tot_dsds; @@ -2251,11 +2433,11 @@ qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) crc_ctx_pkt->blk_size = cpu_to_le16(cmd->blk_sz); crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts); crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes); - crc_ctx_pkt->guard_seed = __constant_cpu_to_le16(0); + crc_ctx_pkt->guard_seed = cpu_to_le16(0); /* Walks data segments */ - pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DSD_PTR); + pkt->flags |= cpu_to_le16(CTIO7_FLAGS_DSD_PTR); if (!bundling && prm->prot_seg_cnt) { if (qla24xx_walk_and_build_sglist_no_difb(ha, NULL, cur_dsd, @@ -2298,6 +2480,19 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, unsigned long flags = 0; int res; + spin_lock_irqsave(&ha->hardware_lock, flags); + if (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + cmd->state = QLA_TGT_STATE_PROCESSED; + if (cmd->sess->logout_completed) + /* no need to terminate. FW already freed exchange. */ + qlt_abort_cmd_on_host_reset(cmd->vha, cmd); + else + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return 0; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + memset(&prm, 0, sizeof(prm)); qlt_check_srr_debug(cmd, &xmit_type); @@ -2310,9 +2505,6 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, &full_req_cnt); if (unlikely(res != 0)) { - if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED) - return 0; - return res; } @@ -2351,7 +2543,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, if (qlt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) { pkt->u.status0.flags |= - __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN | + cpu_to_le16(CTIO7_FLAGS_DATA_IN | CTIO7_FLAGS_STATUS_MODE_0); if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) @@ -2363,11 +2555,11 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, cpu_to_le16(prm.rq_result); pkt->u.status0.residual = cpu_to_le32(prm.residual); - pkt->u.status0.flags |= __constant_cpu_to_le16( + pkt->u.status0.flags |= cpu_to_le16( CTIO7_FLAGS_SEND_STATUS); if (qlt_need_explicit_conf(ha, cmd, 0)) { pkt->u.status0.flags |= - __constant_cpu_to_le16( + cpu_to_le16( CTIO7_FLAGS_EXPLICIT_CONFORM | CTIO7_FLAGS_CONFORM_REQ); } @@ -2395,12 +2587,12 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, ctio->entry_count = 1; ctio->entry_type = CTIO_TYPE7; ctio->dseg_count = 0; - ctio->u.status1.flags &= ~__constant_cpu_to_le16( + ctio->u.status1.flags &= ~cpu_to_le16( CTIO7_FLAGS_DATA_IN); /* Real finish is ctio_m1's finish */ pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK; - pkt->u.status0.flags |= __constant_cpu_to_le16( + pkt->u.status0.flags |= cpu_to_le16( CTIO7_FLAGS_DONT_RET_CTIO); /* qlt_24xx_init_ctio_to_isp will correct @@ -2459,7 +2651,8 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) spin_lock_irqsave(&ha->hardware_lock, flags); - if (qla2x00_reset_active(vha) || cmd->reset_count != ha->chip_reset) { + if (qla2x00_reset_active(vha) || (cmd->reset_count != ha->chip_reset) || + (cmd->sess && cmd->sess->deleted == QLA_SESS_DELETION_IN_PROGRESS)) { /* * Either a chip reset is active or this request was from * previous life, just abort the processing. @@ -2489,7 +2682,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) } pkt = (struct ctio7_to_24xx *)prm.pkt; - pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT | + pkt->u.status0.flags |= cpu_to_le16(CTIO7_FLAGS_DATA_OUT | CTIO7_FLAGS_STATUS_MODE_0); if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) @@ -2564,7 +2757,7 @@ qlt_handle_dif_error(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd, /* Update protection tag */ if (cmd->prot_sg_cnt) { - uint32_t i, j = 0, k = 0, num_ent; + uint32_t i, k = 0, num_ent; struct scatterlist *sg, *sgl; @@ -2577,7 +2770,6 @@ qlt_handle_dif_error(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd, k += num_ent; continue; } - j = blocks_done - k - 1; k = blocks_done; break; } @@ -2650,6 +2842,89 @@ out: } +/* If hardware_lock held on entry, might drop it, then reaquire */ +/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */ +static int __qlt_send_term_imm_notif(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *ntfy) +{ + struct nack_to_isp *nack; + struct qla_hw_data *ha = vha->hw; + request_t *pkt; + int ret = 0; + + ql_dbg(ql_dbg_tgt_tmr, vha, 0xe01c, + "Sending TERM ELS CTIO (ha=%p)\n", ha); + + pkt = (request_t *)qla2x00_alloc_iocbs_ready(vha, NULL); + if (pkt == NULL) { + ql_dbg(ql_dbg_tgt, vha, 0xe080, + "qla_target(%d): %s failed: unable to allocate " + "request packet\n", vha->vp_idx, __func__); + return -ENOMEM; + } + + pkt->entry_type = NOTIFY_ACK_TYPE; + pkt->entry_count = 1; + pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + + nack = (struct nack_to_isp *)pkt; + nack->ox_id = ntfy->ox_id; + + nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; + if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { + nack->u.isp24.flags = ntfy->u.isp24.flags & + __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + } + + /* terminate */ + nack->u.isp24.flags |= + __constant_cpu_to_le16(NOTIFY_ACK_FLAGS_TERMINATE); + + nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id; + nack->u.isp24.status = ntfy->u.isp24.status; + nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode; + nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle; + nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address; + nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs; + nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui; + nack->u.isp24.vp_index = ntfy->u.isp24.vp_index; + + qla2x00_start_iocbs(vha, vha->req); + return ret; +} + +static void qlt_send_term_imm_notif(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *imm, int ha_locked) +{ + unsigned long flags = 0; + int rc; + + if (qlt_issue_marker(vha, ha_locked) < 0) + return; + + if (ha_locked) { + rc = __qlt_send_term_imm_notif(vha, imm); + +#if 0 /* Todo */ + if (rc == -ENOMEM) + qlt_alloc_qfull_cmd(vha, imm, 0, 0); +#endif + goto done; + } + + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + rc = __qlt_send_term_imm_notif(vha, imm); + +#if 0 /* Todo */ + if (rc == -ENOMEM) + qlt_alloc_qfull_cmd(vha, imm, 0, 0); +#endif + +done: + if (!ha_locked) + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); +} + /* If hardware_lock held on entry, might drop it, then reaquire */ /* This function sends the appropriate CTIO to ISP 2xxx or 24xx */ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, @@ -2688,14 +2963,14 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, ctio24 = (struct ctio7_to_24xx *)pkt; ctio24->entry_type = CTIO_TYPE7; ctio24->nport_handle = cmd ? cmd->loop_id : CTIO7_NHANDLE_UNRECOGNIZED; - ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio24->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); ctio24->vp_index = vha->vp_idx; ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; ctio24->exchange_addr = atio->u.isp24.exchange_addr; ctio24->u.status1.flags = (atio->u.isp24.attr << 9) | - __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | + cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); ctio24->u.status1.ox_id = cpu_to_le16(temp); @@ -2794,6 +3069,24 @@ static void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha) } +void qlt_abort_cmd(struct qla_tgt_cmd *cmd) +{ + struct qla_tgt *tgt = cmd->tgt; + struct scsi_qla_host *vha = tgt->vha; + struct se_cmd *se_cmd = &cmd->se_cmd; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, + "qla_target(%d): terminating exchange for aborted cmd=%p " + "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, &cmd->se_cmd, + se_cmd->tag); + + cmd->state = QLA_TGT_STATE_ABORTED; + cmd->cmd_flags |= BIT_6; + + qlt_send_term_exchange(vha, cmd, &cmd->atio, 0); +} +EXPORT_SYMBOL(qlt_abort_cmd); + void qlt_free_cmd(struct qla_tgt_cmd *cmd) { struct qla_tgt_sess *sess = cmd->sess; @@ -2917,7 +3210,7 @@ static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio, if (ctio != NULL) { struct ctio7_from_24xx *c = (struct ctio7_from_24xx *)ctio; term = !(c->flags & - __constant_cpu_to_le16(OF_TERM_EXCH)); + cpu_to_le16(OF_TERM_EXCH)); } else term = 1; @@ -3017,7 +3310,7 @@ qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) dump_stack(); } - cmd->cmd_flags |= BIT_12; + cmd->cmd_flags |= BIT_17; ha->tgt.tgt_ops->free_cmd(cmd); } @@ -3065,7 +3358,6 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, { struct qla_hw_data *ha = vha->hw; struct se_cmd *se_cmd; - const struct target_core_fabric_ops *tfo; struct qla_tgt_cmd *cmd; if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) { @@ -3083,7 +3375,6 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, return; se_cmd = &cmd->se_cmd; - tfo = se_cmd->se_tfo; cmd->cmd_sent_to_fw = 0; qlt_unmap_sg(vha, cmd); @@ -3179,23 +3470,21 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, skip_term: if (cmd->state == QLA_TGT_STATE_PROCESSED) { - ; + cmd->cmd_flags |= BIT_12; } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) { - int rx_status = 0; - cmd->state = QLA_TGT_STATE_DATA_IN; - if (unlikely(status != CTIO_SUCCESS)) - rx_status = -EIO; - else + if (status == CTIO_SUCCESS) cmd->write_data_transferred = 1; ha->tgt.tgt_ops->handle_data(cmd); return; } else if (cmd->state == QLA_TGT_STATE_ABORTED) { + cmd->cmd_flags |= BIT_18; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e, - "Aborted command %p (tag %d) finished\n", cmd, cmd->tag); + "Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag); } else { + cmd->cmd_flags |= BIT_19; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c, "qla_target(%d): A command in state (%d) should " "not return a CTIO complete\n", vha->vp_idx, cmd->state); @@ -3207,7 +3496,6 @@ skip_term: dump_stack(); } - ha->tgt.tgt_ops->free_cmd(cmd); } @@ -3265,8 +3553,15 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) if (tgt->tgt_stop) goto out_term; + if (cmd->state == QLA_TGT_STATE_ABORTED) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf082, + "cmd with tag %u is aborted\n", + cmd->atio.u.isp24.exchange_addr); + goto out_term; + } + cdb = &atio->u.isp24.fcp_cmnd.cdb[0]; - cmd->tag = atio->u.isp24.exchange_addr; + cmd->se_cmd.tag = atio->u.isp24.exchange_addr; cmd->unpacked_lun = scsilun_to_int( (struct scsi_lun *)&atio->u.isp24.fcp_cmnd.lun); @@ -3318,6 +3613,12 @@ out_term: static void qlt_do_work(struct work_struct *work) { struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + scsi_qla_host_t *vha = cmd->vha; + unsigned long flags; + + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_del(&cmd->cmd_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); __qlt_do_work(cmd); } @@ -3369,14 +3670,25 @@ static void qlt_create_sess_from_atio(struct work_struct *work) unsigned long flags; uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id; + spin_lock_irqsave(&vha->cmd_list_lock, flags); + list_del(&op->cmd_list); + spin_unlock_irqrestore(&vha->cmd_list_lock, flags); + + if (op->aborted) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf083, + "sess_op with tag %u is aborted\n", + op->atio.u.isp24.exchange_addr); + goto out_term; + } + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022, - "qla_target(%d): Unable to find wwn login" - " (s_id %x:%x:%x), trying to create it manually\n", - vha->vp_idx, s_id[0], s_id[1], s_id[2]); + "qla_target(%d): Unable to find wwn login" + " (s_id %x:%x:%x), trying to create it manually\n", + vha->vp_idx, s_id[0], s_id[1], s_id[2]); if (op->atio.u.raw.entry_count > 1) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023, - "Dropping multy entry atio %p\n", &op->atio); + "Dropping multy entry atio %p\n", &op->atio); goto out_term; } @@ -3441,10 +3753,25 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, memcpy(&op->atio, atio, sizeof(*atio)); op->vha = vha; + + spin_lock(&vha->cmd_list_lock); + list_add_tail(&op->cmd_list, &vha->qla_sess_op_cmd_list); + spin_unlock(&vha->cmd_list_lock); + INIT_WORK(&op->work, qlt_create_sess_from_atio); queue_work(qla_tgt_wq, &op->work); return 0; } + + /* Another WWN used to have our s_id. Our PLOGI scheduled its + * session deletion, but it's still in sess_del_work wq */ + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + ql_dbg(ql_dbg_io, vha, 0x3061, + "New command while old session %p is being deleted\n", + sess); + return -EFAULT; + } + /* * Do kref_get() before returning + dropping qla_hw_data->hardware_lock. */ @@ -3460,6 +3787,11 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, cmd->cmd_in_wq = 1; cmd->cmd_flags |= BIT_0; + + spin_lock(&vha->cmd_list_lock); + list_add_tail(&cmd->cmd_list, &vha->qla_cmd_list); + spin_unlock(&vha->cmd_list_lock); + INIT_WORK(&cmd->work, qlt_do_work); queue_work(qla_tgt_wq, &cmd->work); return 0; @@ -3473,6 +3805,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, struct scsi_qla_host *vha = sess->vha; struct qla_hw_data *ha = vha->hw; struct qla_tgt_mgmt_cmd *mcmd; + struct atio_from_isp *a = (struct atio_from_isp *)iocb; int res; uint8_t tmr_func; @@ -3513,6 +3846,7 @@ static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002, "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx); tmr_func = TMR_LUN_RESET; + abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id); break; case QLA_TGT_CLEAR_TS: @@ -3582,12 +3916,11 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) struct qla_tgt *tgt; struct qla_tgt_sess *sess; uint32_t lun, unpacked_lun; - int lun_size, fn; + int fn; tgt = vha->vha_tgt.qla_tgt; lun = a->u.isp24.fcp_cmnd.lun; - lun_size = sizeof(a->u.isp24.fcp_cmnd.lun); fn = a->u.isp24.fcp_cmnd.task_mgmt_flags; sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, a->u.isp24.fcp_hdr.s_id); @@ -3601,6 +3934,9 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) sizeof(struct atio_from_isp)); } + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) + return -EFAULT; + return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); } @@ -3666,22 +4002,280 @@ static int qlt_abort_task(struct scsi_qla_host *vha, return __qlt_abort_task(vha, iocb, sess); } +void qlt_logo_completion_handler(fc_port_t *fcport, int rc) +{ + if (fcport->tgt_session) { + if (rc != MBS_COMMAND_COMPLETE) { + ql_dbg(ql_dbg_tgt_mgt, fcport->vha, 0xf093, + "%s: se_sess %p / sess %p from" + " port %8phC loop_id %#04x s_id %02x:%02x:%02x" + " LOGO failed: %#x\n", + __func__, + fcport->tgt_session->se_sess, + fcport->tgt_session, + fcport->port_name, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa, rc); + } + + fcport->tgt_session->logout_completed = 1; + } +} + +static void qlt_swap_imm_ntfy_iocb(struct imm_ntfy_from_isp *a, + struct imm_ntfy_from_isp *b) +{ + struct imm_ntfy_from_isp tmp; + memcpy(&tmp, a, sizeof(struct imm_ntfy_from_isp)); + memcpy(a, b, sizeof(struct imm_ntfy_from_isp)); + memcpy(b, &tmp, sizeof(struct imm_ntfy_from_isp)); +} + +/* +* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) +* +* Schedules sessions with matching port_id/loop_id but different wwn for +* deletion. Returns existing session with matching wwn if present. +* Null otherwise. +*/ +static struct qla_tgt_sess * +qlt_find_sess_invalidate_other(struct qla_tgt *tgt, uint64_t wwn, + port_id_t port_id, uint16_t loop_id) +{ + struct qla_tgt_sess *sess = NULL, *other_sess; + uint64_t other_wwn; + + list_for_each_entry(other_sess, &tgt->sess_list, sess_list_entry) { + + other_wwn = wwn_to_u64(other_sess->port_name); + + if (wwn == other_wwn) { + WARN_ON(sess); + sess = other_sess; + continue; + } + + /* find other sess with nport_id collision */ + if (port_id.b24 == other_sess->s_id.b24) { + if (loop_id != other_sess->loop_id) { + ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000c, + "Invalidating sess %p loop_id %d wwn %llx.\n", + other_sess, other_sess->loop_id, other_wwn); + + /* + * logout_on_delete is set by default, but another + * session that has the same s_id/loop_id combo + * might have cleared it when requested this session + * deletion, so don't touch it + */ + qlt_schedule_sess_for_deletion(other_sess, true); + } else { + /* + * Another wwn used to have our s_id/loop_id + * combo - kill the session, but don't log out + */ + sess->logout_on_delete = 0; + qlt_schedule_sess_for_deletion(other_sess, + true); + } + continue; + } + + /* find other sess with nport handle collision */ + if (loop_id == other_sess->loop_id) { + ql_dbg(ql_dbg_tgt_tmr, tgt->vha, 0x1000d, + "Invalidating sess %p loop_id %d wwn %llx.\n", + other_sess, other_sess->loop_id, other_wwn); + + /* Same loop_id but different s_id + * Ok to kill and logout */ + qlt_schedule_sess_for_deletion(other_sess, true); + } + } + + return sess; +} + +/* Abort any commands for this s_id waiting on qla_tgt_wq workqueue */ +static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id) +{ + struct qla_tgt_sess_op *op; + struct qla_tgt_cmd *cmd; + uint32_t key; + int count = 0; + + key = (((u32)s_id->b.domain << 16) | + ((u32)s_id->b.area << 8) | + ((u32)s_id->b.al_pa)); + + spin_lock(&vha->cmd_list_lock); + list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) { + uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id); + if (op_key == key) { + op->aborted = true; + count++; + } + } + list_for_each_entry(cmd, &vha->qla_cmd_list, cmd_list) { + uint32_t cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id); + if (cmd_key == key) { + cmd->state = QLA_TGT_STATE_ABORTED; + count++; + } + } + spin_unlock(&vha->cmd_list_lock); + + return count; +} + /* * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, struct imm_ntfy_from_isp *iocb) { + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess = NULL; + uint64_t wwn; + port_id_t port_id; + uint16_t loop_id; + uint16_t wd3_lo; int res = 0; + wwn = wwn_to_u64(iocb->u.isp24.port_name); + + port_id.b.domain = iocb->u.isp24.port_id[2]; + port_id.b.area = iocb->u.isp24.port_id[1]; + port_id.b.al_pa = iocb->u.isp24.port_id[0]; + port_id.b.rsvd_1 = 0; + + loop_id = le16_to_cpu(iocb->u.isp24.nport_handle); + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); + /* res = 1 means ack at the end of thread + * res = 0 means ack async/later. + */ switch (iocb->u.isp24.status_subcode) { case ELS_PLOGI: - case ELS_FLOGI: + + /* Mark all stale commands in qla_tgt_wq for deletion */ + abort_cmds_for_s_id(vha, &port_id); + + if (wwn) + sess = qlt_find_sess_invalidate_other(tgt, wwn, + port_id, loop_id); + + if (!sess || IS_SW_RESV_ADDR(sess->s_id)) { + res = 1; + break; + } + + if (sess->plogi_ack_needed) { + /* + * Initiator sent another PLOGI before last PLOGI could + * finish. Swap plogi iocbs and terminate old one + * without acking, new one will get acked when session + * deletion completes. + */ + ql_log(ql_log_warn, sess->vha, 0xf094, + "sess %p received double plogi.\n", sess); + + qlt_swap_imm_ntfy_iocb(iocb, &sess->tm_iocb); + + qlt_send_term_imm_notif(vha, iocb, 1); + + res = 0; + break; + } + + res = 0; + + /* + * Save immediate Notif IOCB for Ack when sess is done + * and being deleted. + */ + memcpy(&sess->tm_iocb, iocb, sizeof(sess->tm_iocb)); + sess->plogi_ack_needed = 1; + + /* + * Under normal circumstances we want to release nport handle + * during LOGO process to avoid nport handle leaks inside FW. + * The exception is when LOGO is done while another PLOGI with + * the same nport handle is waiting as might be the case here. + * Note: there is always a possibily of a race where session + * deletion has already started for other reasons (e.g. ACL + * removal) and now PLOGI arrives: + * 1. if PLOGI arrived in FW after nport handle has been freed, + * FW must have assigned this PLOGI a new/same handle and we + * can proceed ACK'ing it as usual when session deletion + * completes. + * 2. if PLOGI arrived in FW before LOGO with LCF_FREE_NPORT + * bit reached it, the handle has now been released. We'll + * get an error when we ACK this PLOGI. Nothing will be sent + * back to initiator. Initiator should eventually retry + * PLOGI and situation will correct itself. + */ + sess->keep_nport_handle = ((sess->loop_id == loop_id) && + (sess->s_id.b24 == port_id.b24)); + qlt_schedule_sess_for_deletion(sess, true); + break; + case ELS_PRLI: + wd3_lo = le16_to_cpu(iocb->u.isp24.u.prli.wd3_lo); + + if (wwn) + sess = qlt_find_sess_invalidate_other(tgt, wwn, port_id, + loop_id); + + if (sess != NULL) { + if (sess->deleted) { + /* + * Impatient initiator sent PRLI before last + * PLOGI could finish. Will force him to re-try, + * while last one finishes. + */ + ql_log(ql_log_warn, sess->vha, 0xf095, + "sess %p PRLI received, before plogi ack.\n", + sess); + qlt_send_term_imm_notif(vha, iocb, 1); + res = 0; + break; + } + + /* + * This shouldn't happen under normal circumstances, + * since we have deleted the old session during PLOGI + */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf096, + "PRLI (loop_id %#04x) for existing sess %p (loop_id %#04x)\n", + sess->loop_id, sess, iocb->u.isp24.nport_handle); + + sess->local = 0; + sess->loop_id = loop_id; + sess->s_id = port_id; + + if (wd3_lo & BIT_7) + sess->conf_compl_supported = 1; + + } + res = 1; /* send notify ack */ + + /* Make session global (not used in fabric mode) */ + if (ha->current_topology != ISP_CFG_F) { + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } else { + /* todo: else - create sess here. */ + res = 1; /* send notify ack */ + } + + break; + case ELS_LOGO: case ELS_PRLO: res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); @@ -3699,6 +4293,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, break; } + case ELS_FLOGI: /* should never happen */ default: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061, "qla_target(%d): Unsupported ELS command %x " @@ -3712,6 +4307,14 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset) { +#if 1 + /* + * FIXME: Reject non zero SRR relative offset until we can test + * this code properly. + */ + pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset); + return -1; +#else struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL; size_t first_offset = 0, rem_offset = offset, tmp = 0; int i, sg_srr_cnt, bufflen = 0; @@ -3721,13 +4324,6 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset) "cmd->sg_cnt: %u, direction: %d\n", cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); - /* - * FIXME: Reject non zero SRR relative offset until we can test - * this code properly. - */ - pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset); - return -1; - if (!cmd->sg || !cmd->sg_cnt) { ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055, "Missing cmd->sg or zero cmd->sg_cnt in" @@ -3810,6 +4406,7 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset) BUG(); return 0; +#endif } static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd, @@ -3891,9 +4488,8 @@ static void qlt_handle_srr(struct scsi_qla_host *vha, resp = 1; } else { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf064, - "qla_target(%d): SRR for in data for cmd " - "without them (tag %d, SCSI status %d), " - "reject", vha->vp_idx, cmd->tag, + "qla_target(%d): SRR for in data for cmd without them (tag %lld, SCSI status %d), reject", + vha->vp_idx, se_cmd->tag, cmd->se_cmd.scsi_status); goto out_reject; } @@ -3927,10 +4523,8 @@ static void qlt_handle_srr(struct scsi_qla_host *vha, } } else { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf066, - "qla_target(%d): SRR for out data for cmd " - "without them (tag %d, SCSI status %d), " - "reject", vha->vp_idx, cmd->tag, - cmd->se_cmd.scsi_status); + "qla_target(%d): SRR for out data for cmd without them (tag %lld, SCSI status %d), reject", + vha->vp_idx, se_cmd->tag, cmd->se_cmd.scsi_status); goto out_reject; } break; @@ -3971,16 +4565,20 @@ static void qlt_reject_free_srr_imm(struct scsi_qla_host *vha, struct qla_hw_data *ha = vha->hw; unsigned long flags = 0; +#ifndef __CHECKER__ if (!ha_locked) spin_lock_irqsave(&ha->hardware_lock, flags); +#endif qlt_send_notify_ack(vha, (void *)&imm->imm_ntfy, 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_REJECT, NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); +#ifndef __CHECKER__ if (!ha_locked) spin_unlock_irqrestore(&ha->hardware_lock, flags); +#endif kfree(imm); } @@ -4051,10 +4649,9 @@ restart: cmd->sg = se_cmd->t_data_sg; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02c, - "SRR cmd %p (se_cmd %p, tag %d, op %x), " - "sg_cnt=%d, offset=%d", cmd, &cmd->se_cmd, cmd->tag, - se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, - cmd->sg_cnt, cmd->offset); + "SRR cmd %p (se_cmd %p, tag %lld, op %x), sg_cnt=%d, offset=%d", + cmd, &cmd->se_cmd, se_cmd->tag, se_cmd->t_task_cdb ? + se_cmd->t_task_cdb[0] : 0, cmd->sg_cnt, cmd->offset); qlt_handle_srr(vha, sctio, imm); @@ -4325,14 +4922,14 @@ static int __qlt_send_busy(struct scsi_qla_host *vha, ctio24 = (struct ctio7_to_24xx *)pkt; ctio24->entry_type = CTIO_TYPE7; ctio24->nport_handle = sess->loop_id; - ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio24->timeout = cpu_to_le16(QLA_TGT_TIMEOUT); ctio24->vp_index = vha->vp_idx; ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; ctio24->exchange_addr = atio->u.isp24.exchange_addr; ctio24->u.status1.flags = (atio->u.isp24.attr << 9) | - __constant_cpu_to_le16( + cpu_to_le16( CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS | CTIO7_FLAGS_DONT_RET_CTIO); /* @@ -4660,7 +5257,7 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) struct atio_from_isp *atio = (struct atio_from_isp *)pkt; int rc; if (atio->u.isp2x.status != - __constant_cpu_to_le16(ATIO_CDB_VALID)) { + cpu_to_le16(ATIO_CDB_VALID)) { ql_dbg(ql_dbg_tgt, vha, 0xe05e, "qla_target(%d): ATIO with error " "status %x received\n", vha->vp_idx, @@ -4734,7 +5331,7 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) le16_to_cpu(entry->u.isp2x.status)); tgt->notify_ack_expected--; if (entry->u.isp2x.status != - __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) { + cpu_to_le16(NOTIFY_ACK_SUCCESS)) { ql_dbg(ql_dbg_tgt, vha, 0xe061, "qla_target(%d): NOTIFY_ACK " "failed %x\n", vha->vp_idx, @@ -5016,6 +5613,11 @@ static void qlt_abort_work(struct qla_tgt *tgt, if (!sess) goto out_term; } else { + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + sess = NULL; + goto out_term; + } + kref_get(&sess->se_sess->sess_kref); } @@ -5048,7 +5650,7 @@ static void qlt_tmr_work(struct qla_tgt *tgt, uint8_t *s_id = NULL; /* to hide compiler warnings */ int rc; uint32_t lun, unpacked_lun; - int lun_size, fn; + int fn; void *iocb; spin_lock_irqsave(&ha->hardware_lock, flags); @@ -5070,12 +5672,16 @@ static void qlt_tmr_work(struct qla_tgt *tgt, if (!sess) goto out_term; } else { + if (sess->deleted == QLA_SESS_DELETION_IN_PROGRESS) { + sess = NULL; + goto out_term; + } + kref_get(&sess->se_sess->sess_kref); } iocb = a; lun = a->u.isp24.fcp_cmnd.lun; - lun_size = sizeof(lun); fn = a->u.isp24.fcp_cmnd.task_mgmt_flags; unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); @@ -5556,6 +6162,7 @@ qlt_24xx_process_atio_queue(struct scsi_qla_host *vha) /* Adjust ring index */ WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index); + RD_REG_DWORD_RELAXED(ISP_ATIO_Q_OUT(vha)); } void @@ -5598,19 +6205,19 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) ha->tgt.saved_set = 1; } - nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + nv->exchange_count = cpu_to_le16(0xFFFF); /* Enable target mode */ - nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); + nv->firmware_options_1 |= cpu_to_le32(BIT_4); /* Disable ini mode, if requested */ if (!qla_ini_mode_enabled(vha)) - nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5); + nv->firmware_options_1 |= cpu_to_le32(BIT_5); /* Disable Full Login after LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + nv->firmware_options_1 &= cpu_to_le32(~BIT_13); /* Enable initial LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + nv->firmware_options_1 &= cpu_to_le32(~BIT_9); if (ql2xtgt_tape_enable) /* Enable FC Tape support */ nv->firmware_options_2 |= cpu_to_le32(BIT_12); @@ -5619,9 +6226,9 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) nv->firmware_options_2 &= cpu_to_le32(~BIT_12); /* Disable Full Login after LIP */ - nv->host_p &= __constant_cpu_to_le32(~BIT_10); + nv->host_p &= cpu_to_le32(~BIT_10); /* Enable target PRLI control */ - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14); + nv->firmware_options_2 |= cpu_to_le32(BIT_14); } else { if (ha->tgt.saved_set) { nv->exchange_count = ha->tgt.saved_exchange_count; @@ -5643,12 +6250,12 @@ qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) fc_host_supported_classes(vha->host) = FC_COS_CLASS2 | FC_COS_CLASS3; - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + nv->firmware_options_2 |= cpu_to_le32(BIT_8); } else { if (vha->flags.init_done) fc_host_supported_classes(vha->host) = FC_COS_CLASS3; - nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + nv->firmware_options_2 &= ~cpu_to_le32(BIT_8); } } @@ -5660,7 +6267,7 @@ qlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha, if (ha->tgt.node_name_set) { memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); - icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + icb->firmware_options_1 |= cpu_to_le32(BIT_14); } } @@ -5685,20 +6292,19 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) ha->tgt.saved_set = 1; } - nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + nv->exchange_count = cpu_to_le16(0xFFFF); /* Enable target mode */ - nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); + nv->firmware_options_1 |= cpu_to_le32(BIT_4); /* Disable ini mode, if requested */ if (!qla_ini_mode_enabled(vha)) - nv->firmware_options_1 |= - __constant_cpu_to_le32(BIT_5); + nv->firmware_options_1 |= cpu_to_le32(BIT_5); /* Disable Full Login after LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + nv->firmware_options_1 &= cpu_to_le32(~BIT_13); /* Enable initial LIP */ - nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + nv->firmware_options_1 &= cpu_to_le32(~BIT_9); if (ql2xtgt_tape_enable) /* Enable FC tape support */ nv->firmware_options_2 |= cpu_to_le32(BIT_12); @@ -5707,9 +6313,9 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) nv->firmware_options_2 &= cpu_to_le32(~BIT_12); /* Disable Full Login after LIP */ - nv->host_p &= __constant_cpu_to_le32(~BIT_10); + nv->host_p &= cpu_to_le32(~BIT_10); /* Enable target PRLI control */ - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14); + nv->firmware_options_2 |= cpu_to_le32(BIT_14); } else { if (ha->tgt.saved_set) { nv->exchange_count = ha->tgt.saved_exchange_count; @@ -5731,12 +6337,12 @@ qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) fc_host_supported_classes(vha->host) = FC_COS_CLASS2 | FC_COS_CLASS3; - nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + nv->firmware_options_2 |= cpu_to_le32(BIT_8); } else { if (vha->flags.init_done) fc_host_supported_classes(vha->host) = FC_COS_CLASS3; - nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + nv->firmware_options_2 &= ~cpu_to_le32(BIT_8); } } @@ -5751,7 +6357,7 @@ qlt_81xx_config_nvram_stage2(struct scsi_qla_host *vha, if (ha->tgt.node_name_set) { memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); - icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + icb->firmware_options_1 |= cpu_to_le32(BIT_14); } } @@ -5797,7 +6403,7 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) if (!QLA_TGT_MODE_ENABLED()) return; - if (ha->mqenable || IS_QLA83XX(ha)) { + if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) { ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in; ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out; } else { diff --git a/kernel/drivers/scsi/qla2xxx/qla_target.h b/kernel/drivers/scsi/qla2xxx/qla_target.h index 332086776..bca584ae4 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_target.h +++ b/kernel/drivers/scsi/qla2xxx/qla_target.h @@ -167,7 +167,24 @@ struct imm_ntfy_from_isp { uint32_t srr_rel_offs; uint16_t srr_ui; uint16_t srr_ox_id; - uint8_t reserved_4[19]; + union { + struct { + uint8_t node_name[8]; + } plogi; /* PLOGI/ADISC/PDISC */ + struct { + /* PRLI word 3 bit 0-15 */ + uint16_t wd3_lo; + uint8_t resv0[6]; + } prli; + struct { + uint8_t port_id[3]; + uint8_t resv1; + uint16_t nport_handle; + uint16_t resv2; + } req_els; + } u; + uint8_t port_name[8]; + uint8_t resv3[3]; uint8_t vp_index; uint32_t reserved_5; uint8_t port_id[3]; @@ -234,6 +251,7 @@ struct nack_to_isp { uint8_t reserved[2]; uint16_t ox_id; } __packed; +#define NOTIFY_ACK_FLAGS_TERMINATE BIT_3 #define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 #define NOTIFY_ACK_SRR_FLAGS_REJECT 1 @@ -790,13 +808,6 @@ int qla2x00_wait_for_hba_online(struct scsi_qla_host *); #define FC_TM_REJECT 4 #define FC_TM_FAILED 5 -/* - * Error code of qlt_pre_xmit_response() meaning that cmd's exchange was - * terminated, so no more actions is needed and success should be returned - * to target. - */ -#define QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED 0x1717 - #if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G) #define pci_dma_lo32(a) (a & 0xffffffff) #define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff) @@ -874,6 +885,15 @@ struct qla_tgt_sess_op { struct scsi_qla_host *vha; struct atio_from_isp atio; struct work_struct work; + struct list_head cmd_list; + bool aborted; +}; + +enum qla_sess_deletion { + QLA_SESS_DELETION_NONE = 0, + QLA_SESS_DELETION_PENDING = 1, /* hopefully we can get rid of + * this one */ + QLA_SESS_DELETION_IN_PROGRESS = 2, }; /* @@ -884,8 +904,15 @@ struct qla_tgt_sess { port_id_t s_id; unsigned int conf_compl_supported:1; - unsigned int deleted:1; + unsigned int deleted:2; unsigned int local:1; + unsigned int logout_on_delete:1; + unsigned int plogi_ack_needed:1; + unsigned int keep_nport_handle:1; + + unsigned char logout_completed; + + int generation; struct se_session *se_sess; struct scsi_qla_host *vha; @@ -897,6 +924,10 @@ struct qla_tgt_sess { uint8_t port_name[WWN_SIZE]; struct work_struct free_work; + + union { + struct imm_ntfy_from_isp tm_iocb; + }; }; struct qla_tgt_cmd { @@ -912,7 +943,6 @@ struct qla_tgt_cmd { unsigned int conf_compl_supported:1; unsigned int sg_mapped:1; unsigned int free_sg:1; - unsigned int aborted:1; /* Needed in case of SRR */ unsigned int write_data_transferred:1; unsigned int ctx_dsd_alloced:1; unsigned int q_full:1; @@ -924,7 +954,6 @@ struct qla_tgt_cmd { int sg_cnt; /* SG segments count */ int bufflen; /* cmd buffer length */ int offset; - uint32_t tag; uint32_t unpacked_lun; enum dma_data_direction dma_data_direction; uint32_t reset_count; @@ -962,6 +991,9 @@ struct qla_tgt_cmd { * BIT_14 - Back end data received/sent. * BIT_15 - SRR prepare ctio * BIT_16 - complete free + * BIT_17 - flush - qlt_abort_cmd_on_host_reset + * BIT_18 - completion w/abort status + * BIT_19 - completion w/unknown status */ uint32_t cmd_flags; }; @@ -1027,6 +1059,10 @@ struct qla_tgt_srr_ctio { struct qla_tgt_cmd *cmd; }; +/* Check for Switch reserved address */ +#define IS_SW_RESV_ADDR(_s_id) \ + ((_s_id.b.domain == 0xff) && (_s_id.b.area == 0xfc)) + #define QLA_TGT_XMIT_DATA 1 #define QLA_TGT_XMIT_STATUS 2 #define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA) @@ -1044,7 +1080,7 @@ extern int qlt_lport_register(void *, u64, u64, u64, extern void qlt_lport_deregister(struct scsi_qla_host *); extern void qlt_unreg_sess(struct qla_tgt_sess *); extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); -extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); +extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *, int); extern int __init qlt_init(void); extern void qlt_exit(void); extern void qlt_update_vp_map(struct scsi_qla_host *, int); @@ -1074,12 +1110,23 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) ha->host->active_mode |= MODE_INITIATOR; } +static inline uint32_t sid_to_key(const uint8_t *s_id) +{ + uint32_t key; + + key = (((unsigned long)s_id[0] << 16) | + ((unsigned long)s_id[1] << 8) | + (unsigned long)s_id[2]); + return key; +} + /* * Exported symbols from qla_target.c LLD logic used by qla2xxx code.. */ extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); +extern void qlt_abort_cmd(struct qla_tgt_cmd *); extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); @@ -1110,5 +1157,7 @@ extern void qlt_stop_phase2(struct qla_tgt *); extern irqreturn_t qla83xx_msix_atio_q(int, void *); extern void qlt_83xx_iospace_config(struct qla_hw_data *); extern int qlt_free_qfull_cmds(struct scsi_qla_host *); +extern void qlt_logo_completion_handler(fc_port_t *, int); +extern void qlt_do_generation_tick(struct scsi_qla_host *, int *); #endif /* __QLA_TARGET_H */ diff --git a/kernel/drivers/scsi/qla2xxx/qla_tmpl.c b/kernel/drivers/scsi/qla2xxx/qla_tmpl.c index 962cb89fe..c3e622524 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/kernel/drivers/scsi/qla2xxx/qla_tmpl.c @@ -137,39 +137,39 @@ qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len) } static inline void -qla27xx_read8(void *window, void *buf, ulong *len) +qla27xx_read8(void __iomem *window, void *buf, ulong *len) { uint8_t value = ~0; if (buf) { - value = RD_REG_BYTE((__iomem void *)window); + value = RD_REG_BYTE(window); } qla27xx_insert32(value, buf, len); } static inline void -qla27xx_read16(void *window, void *buf, ulong *len) +qla27xx_read16(void __iomem *window, void *buf, ulong *len) { uint16_t value = ~0; if (buf) { - value = RD_REG_WORD((__iomem void *)window); + value = RD_REG_WORD(window); } qla27xx_insert32(value, buf, len); } static inline void -qla27xx_read32(void *window, void *buf, ulong *len) +qla27xx_read32(void __iomem *window, void *buf, ulong *len) { uint32_t value = ~0; if (buf) { - value = RD_REG_DWORD((__iomem void *)window); + value = RD_REG_DWORD(window); } qla27xx_insert32(value, buf, len); } -static inline void (*qla27xx_read_vector(uint width))(void *, void *, ulong *) +static inline void (*qla27xx_read_vector(uint width))(void __iomem*, void *, ulong *) { return (width == 1) ? qla27xx_read8 : @@ -181,7 +181,7 @@ static inline void qla27xx_read_reg(__iomem struct device_reg_24xx *reg, uint offset, void *buf, ulong *len) { - void *window = (void *)reg + offset; + void __iomem *window = (void __iomem *)reg + offset; qla27xx_read32(window, buf, len); } @@ -202,8 +202,8 @@ qla27xx_read_window(__iomem struct device_reg_24xx *reg, uint32_t addr, uint offset, uint count, uint width, void *buf, ulong *len) { - void *window = (void *)reg + offset; - void (*readn)(void *, void *, ulong *) = qla27xx_read_vector(width); + void __iomem *window = (void __iomem *)reg + offset; + void (*readn)(void __iomem*, void *, ulong *) = qla27xx_read_vector(width); qla27xx_write_reg(reg, IOBASE_ADDR, addr, buf); while (count--) { @@ -395,6 +395,10 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, if (ent->t263.queue_type == T263_QUEUE_TYPE_REQ) { for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; + + if (!test_bit(i, vha->hw->req_qid_map)) + continue; + if (req || !buf) { length = req ? req->length : REQUEST_ENTRY_CNT_24XX; @@ -408,6 +412,10 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, } else if (ent->t263.queue_type == T263_QUEUE_TYPE_RSP) { for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + + if (!test_bit(i, vha->hw->rsp_qid_map)) + continue; + if (rsp || !buf) { length = rsp ? rsp->length : RESPONSE_ENTRY_CNT_MQ; @@ -634,6 +642,10 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, if (ent->t274.queue_type == T274_QUEUE_TYPE_REQ_SHAD) { for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; + + if (!test_bit(i, vha->hw->req_qid_map)) + continue; + if (req || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); @@ -645,6 +657,10 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, } else if (ent->t274.queue_type == T274_QUEUE_TYPE_RSP_SHAD) { for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + + if (!test_bit(i, vha->hw->rsp_qid_map)) + continue; + if (rsp || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); @@ -805,9 +821,8 @@ static void qla27xx_driver_info(struct qla27xx_fwdt_template *tmp) { uint8_t v[] = { 0, 0, 0, 0, 0, 0 }; - int rval = 0; - rval = sscanf(qla2x00_version_str, "%hhu.%hhu.%hhu.%hhu.%hhu.%hhu", + sscanf(qla2x00_version_str, "%hhu.%hhu.%hhu.%hhu.%hhu.%hhu", v+0, v+1, v+2, v+3, v+4, v+5); tmp->driver_info[0] = v[3] << 24 | v[2] << 16 | v[1] << 8 | v[0]; @@ -940,8 +955,10 @@ qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked) { ulong flags = 0; +#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&vha->hw->hardware_lock, flags); +#endif if (!vha->hw->fw_dump) ql_log(ql_log_warn, vha, 0xd01e, "fwdump buffer missing.\n"); @@ -954,6 +971,8 @@ qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked) else qla27xx_execute_fwdt_template(vha); +#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); +#endif } diff --git a/kernel/drivers/scsi/qla2xxx/qla_version.h b/kernel/drivers/scsi/qla2xxx/qla_version.h index 2ed9ab90a..6d31faa8c 100644 --- a/kernel/drivers/scsi/qla2xxx/qla_version.h +++ b/kernel/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.07.00.18-k" +#define QLA2XXX_VERSION "8.07.00.26-k" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 7 diff --git a/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 5c9e680aa..81af294f1 100644 --- a/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -42,9 +43,6 @@ #include #include #include -#include -#include -#include #include "qla_def.h" #include "qla_target.h" @@ -53,9 +51,6 @@ static struct workqueue_struct *tcm_qla2xxx_free_wq; static struct workqueue_struct *tcm_qla2xxx_cmd_wq; -static const struct target_core_fabric_ops tcm_qla2xxx_ops; -static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops; - /* * Parse WWN. * If strict, we require lower-case hex and colon separators to be sure @@ -190,23 +185,6 @@ static char *tcm_qla2xxx_npiv_get_fabric_name(void) return "qla2xxx_npiv"; } -static u8 tcm_qla2xxx_get_fabric_proto_ident(struct se_portal_group *se_tpg) -{ - struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, - struct tcm_qla2xxx_tpg, se_tpg); - struct tcm_qla2xxx_lport *lport = tpg->lport; - u8 proto_id; - - switch (lport->lport_proto_id) { - case SCSI_PROTOCOL_FCP: - default: - proto_id = fc_get_fabric_proto_ident(se_tpg); - break; - } - - return proto_id; -} - static char *tcm_qla2xxx_get_fabric_wwn(struct se_portal_group *se_tpg) { struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, @@ -223,78 +201,6 @@ static u16 tcm_qla2xxx_get_tag(struct se_portal_group *se_tpg) return tpg->lport_tpgt; } -static u32 tcm_qla2xxx_get_default_depth(struct se_portal_group *se_tpg) -{ - return 1; -} - -static u32 tcm_qla2xxx_get_pr_transport_id( - struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl, - struct t10_pr_registration *pr_reg, - int *format_code, - unsigned char *buf) -{ - struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, - struct tcm_qla2xxx_tpg, se_tpg); - struct tcm_qla2xxx_lport *lport = tpg->lport; - int ret = 0; - - switch (lport->lport_proto_id) { - case SCSI_PROTOCOL_FCP: - default: - ret = fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg, - format_code, buf); - break; - } - - return ret; -} - -static u32 tcm_qla2xxx_get_pr_transport_id_len( - struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl, - struct t10_pr_registration *pr_reg, - int *format_code) -{ - struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, - struct tcm_qla2xxx_tpg, se_tpg); - struct tcm_qla2xxx_lport *lport = tpg->lport; - int ret = 0; - - switch (lport->lport_proto_id) { - case SCSI_PROTOCOL_FCP: - default: - ret = fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, - format_code); - break; - } - - return ret; -} - -static char *tcm_qla2xxx_parse_pr_out_transport_id( - struct se_portal_group *se_tpg, - const char *buf, - u32 *out_tid_len, - char **port_nexus_ptr) -{ - struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, - struct tcm_qla2xxx_tpg, se_tpg); - struct tcm_qla2xxx_lport *lport = tpg->lport; - char *tid = NULL; - - switch (lport->lport_proto_id) { - case SCSI_PROTOCOL_FCP: - default: - tid = fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, - port_nexus_ptr); - break; - } - - return tid; -} - static int tcm_qla2xxx_check_demo_mode(struct se_portal_group *se_tpg) { struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, @@ -343,29 +249,6 @@ static int tcm_qla2xxx_check_prot_fabric_only(struct se_portal_group *se_tpg) return tpg->tpg_attrib.fabric_prot_type; } -static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl( - struct se_portal_group *se_tpg) -{ - struct tcm_qla2xxx_nacl *nacl; - - nacl = kzalloc(sizeof(struct tcm_qla2xxx_nacl), GFP_KERNEL); - if (!nacl) { - pr_err("Unable to allocate struct tcm_qla2xxx_nacl\n"); - return NULL; - } - - return &nacl->se_node_acl; -} - -static void tcm_qla2xxx_release_fabric_acl( - struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl) -{ - struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl, - struct tcm_qla2xxx_nacl, se_node_acl); - kfree(nacl); -} - static u32 tcm_qla2xxx_tpg_get_inst_index(struct se_portal_group *se_tpg) { struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, @@ -429,7 +312,7 @@ static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd) cmd->cmd_flags |= BIT_14; } - return target_put_sess_cmd(se_cmd->se_sess, se_cmd); + return target_put_sess_cmd(se_cmd); } /* tcm_qla2xxx_release_cmd - Callback from TCM Core to release underlying @@ -489,7 +372,7 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - + cmd->cmd_flags |= BIT_3; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); @@ -520,7 +403,7 @@ static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd) se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) { spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); wait_for_completion_timeout(&se_cmd->t_transport_stop_comp, - 3000); + 3 * HZ); return 0; } spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); @@ -533,21 +416,14 @@ static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl) return; } -static u32 tcm_qla2xxx_get_task_tag(struct se_cmd *se_cmd) -{ - struct qla_tgt_cmd *cmd; - - /* check for task mgmt cmd */ - if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) - return 0xffffffff; - - cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - - return cmd->tag; -} - static int tcm_qla2xxx_get_cmd_state(struct se_cmd *se_cmd) { + if (!(se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) { + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + return cmd->state; + } + return 0; } @@ -669,12 +545,10 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) cmd->cmd_flags |= BIT_4; cmd->bufflen = se_cmd->data_length; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); - cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED); cmd->sg_cnt = se_cmd->t_data_nents; cmd->sg = se_cmd->t_data_sg; cmd->offset = 0; - cmd->cmd_flags |= BIT_3; cmd->prot_sg_cnt = se_cmd->t_prot_nents; cmd->prot_sg = se_cmd->t_prot_sg; @@ -699,7 +573,6 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) cmd->sg_cnt = 0; cmd->offset = 0; cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); - cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED); if (cmd->cmd_flags & BIT_5) { pr_crit("Bit_5 already set for cmd = %p.\n", cmd); dump_stack(); @@ -764,14 +637,7 @@ static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - struct scsi_qla_host *vha = cmd->vha; - struct qla_hw_data *ha = vha->hw; - - if (!cmd->sg_mapped) - return; - - pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); - cmd->sg_mapped = 0; + qlt_abort_cmd(cmd); } static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, @@ -826,17 +692,6 @@ static void tcm_qla2xxx_release_session(struct kref *kref) qlt_unreg_sess(se_sess->fabric_sess_ptr); } -static void tcm_qla2xxx_put_session(struct se_session *se_sess) -{ - struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr; - struct qla_hw_data *ha = sess->vha->hw; - unsigned long flags; - - spin_lock_irqsave(&ha->hardware_lock, flags); - kref_put(&se_sess->sess_kref, tcm_qla2xxx_release_session); - spin_unlock_irqrestore(&ha->hardware_lock, flags); -} - static void tcm_qla2xxx_put_sess(struct qla_tgt_sess *sess) { if (!sess) @@ -852,76 +707,43 @@ static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess) target_sess_cmd_list_set_waiting(sess->se_sess); } -static struct se_node_acl *tcm_qla2xxx_make_nodeacl( - struct se_portal_group *se_tpg, - struct config_group *group, - const char *name) +static int tcm_qla2xxx_init_nodeacl(struct se_node_acl *se_nacl, + const char *name) { - struct se_node_acl *se_nacl, *se_nacl_new; - struct tcm_qla2xxx_nacl *nacl; + struct tcm_qla2xxx_nacl *nacl = + container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); u64 wwnn; - u32 qla2xxx_nexus_depth; if (tcm_qla2xxx_parse_wwn(name, &wwnn, 1) < 0) - return ERR_PTR(-EINVAL); - - se_nacl_new = tcm_qla2xxx_alloc_fabric_acl(se_tpg); - if (!se_nacl_new) - return ERR_PTR(-ENOMEM); -/* #warning FIXME: Hardcoded qla2xxx_nexus depth in tcm_qla2xxx_make_nodeacl */ - qla2xxx_nexus_depth = 1; + return -EINVAL; - /* - * se_nacl_new may be released by core_tpg_add_initiator_node_acl() - * when converting a NodeACL from demo mode -> explict - */ - se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new, - name, qla2xxx_nexus_depth); - if (IS_ERR(se_nacl)) { - tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new); - return se_nacl; - } - /* - * Locate our struct tcm_qla2xxx_nacl and set the FC Nport WWPN - */ - nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); nacl->nport_wwnn = wwnn; tcm_qla2xxx_format_wwn(&nacl->nport_name[0], TCM_QLA2XXX_NAMELEN, wwnn); - return se_nacl; -} - -static void tcm_qla2xxx_drop_nodeacl(struct se_node_acl *se_acl) -{ - struct se_portal_group *se_tpg = se_acl->se_tpg; - struct tcm_qla2xxx_nacl *nacl = container_of(se_acl, - struct tcm_qla2xxx_nacl, se_node_acl); - - core_tpg_del_initiator_node_acl(se_tpg, se_acl, 1); - kfree(nacl); + return 0; } /* Start items for tcm_qla2xxx_tpg_attrib_cit */ #define DEF_QLA_TPG_ATTRIB(name) \ \ -static ssize_t tcm_qla2xxx_tpg_attrib_show_##name( \ - struct se_portal_group *se_tpg, \ - char *page) \ +static ssize_t tcm_qla2xxx_tpg_attrib_##name##_show( \ + struct config_item *item, char *page) \ { \ + struct se_portal_group *se_tpg = attrib_to_tpg(item); \ struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, \ struct tcm_qla2xxx_tpg, se_tpg); \ \ return sprintf(page, "%u\n", tpg->tpg_attrib.name); \ } \ \ -static ssize_t tcm_qla2xxx_tpg_attrib_store_##name( \ - struct se_portal_group *se_tpg, \ - const char *page, \ - size_t count) \ +static ssize_t tcm_qla2xxx_tpg_attrib_##name##_store( \ + struct config_item *item, const char *page, size_t count) \ { \ + struct se_portal_group *se_tpg = attrib_to_tpg(item); \ struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, \ struct tcm_qla2xxx_tpg, se_tpg); \ + struct tcm_qla2xxx_tpg_attrib *a = &tpg->tpg_attrib; \ unsigned long val; \ int ret; \ \ @@ -931,81 +753,39 @@ static ssize_t tcm_qla2xxx_tpg_attrib_store_##name( \ " ret: %d\n", ret); \ return -EINVAL; \ } \ - ret = tcm_qla2xxx_set_attrib_##name(tpg, val); \ - \ - return (!ret) ? count : -EINVAL; \ -} - -#define DEF_QLA_TPG_ATTR_BOOL(_name) \ - \ -static int tcm_qla2xxx_set_attrib_##_name( \ - struct tcm_qla2xxx_tpg *tpg, \ - unsigned long val) \ -{ \ - struct tcm_qla2xxx_tpg_attrib *a = &tpg->tpg_attrib; \ \ if ((val != 0) && (val != 1)) { \ pr_err("Illegal boolean value %lu\n", val); \ return -EINVAL; \ } \ \ - a->_name = val; \ - return 0; \ -} - -#define QLA_TPG_ATTR(_name, _mode) \ - TF_TPG_ATTRIB_ATTR(tcm_qla2xxx, _name, _mode); + a->name = val; \ + \ + return count; \ +} \ +CONFIGFS_ATTR(tcm_qla2xxx_tpg_attrib_, name) -/* - * Define tcm_qla2xxx_tpg_attrib_s_generate_node_acls - */ -DEF_QLA_TPG_ATTR_BOOL(generate_node_acls); DEF_QLA_TPG_ATTRIB(generate_node_acls); -QLA_TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR); - -/* - Define tcm_qla2xxx_attrib_s_cache_dynamic_acls - */ -DEF_QLA_TPG_ATTR_BOOL(cache_dynamic_acls); DEF_QLA_TPG_ATTRIB(cache_dynamic_acls); -QLA_TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR); - -/* - * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_write_protect - */ -DEF_QLA_TPG_ATTR_BOOL(demo_mode_write_protect); DEF_QLA_TPG_ATTRIB(demo_mode_write_protect); -QLA_TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR); - -/* - * Define tcm_qla2xxx_tpg_attrib_s_prod_mode_write_protect - */ -DEF_QLA_TPG_ATTR_BOOL(prod_mode_write_protect); DEF_QLA_TPG_ATTRIB(prod_mode_write_protect); -QLA_TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR); - -/* - * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_login_only - */ -DEF_QLA_TPG_ATTR_BOOL(demo_mode_login_only); DEF_QLA_TPG_ATTRIB(demo_mode_login_only); -QLA_TPG_ATTR(demo_mode_login_only, S_IRUGO | S_IWUSR); static struct configfs_attribute *tcm_qla2xxx_tpg_attrib_attrs[] = { - &tcm_qla2xxx_tpg_attrib_generate_node_acls.attr, - &tcm_qla2xxx_tpg_attrib_cache_dynamic_acls.attr, - &tcm_qla2xxx_tpg_attrib_demo_mode_write_protect.attr, - &tcm_qla2xxx_tpg_attrib_prod_mode_write_protect.attr, - &tcm_qla2xxx_tpg_attrib_demo_mode_login_only.attr, + &tcm_qla2xxx_tpg_attrib_attr_generate_node_acls, + &tcm_qla2xxx_tpg_attrib_attr_cache_dynamic_acls, + &tcm_qla2xxx_tpg_attrib_attr_demo_mode_write_protect, + &tcm_qla2xxx_tpg_attrib_attr_prod_mode_write_protect, + &tcm_qla2xxx_tpg_attrib_attr_demo_mode_login_only, NULL, }; /* End items for tcm_qla2xxx_tpg_attrib_cit */ -static ssize_t tcm_qla2xxx_tpg_show_enable( - struct se_portal_group *se_tpg, - char *page) +static ssize_t tcm_qla2xxx_tpg_enable_show(struct config_item *item, + char *page) { + struct se_portal_group *se_tpg = to_tpg(item); struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, struct tcm_qla2xxx_tpg, se_tpg); @@ -1041,11 +821,10 @@ static void tcm_qla2xxx_undepend_tpg(struct work_struct *work) complete(&base_tpg->tpg_base_comp); } -static ssize_t tcm_qla2xxx_tpg_store_enable( - struct se_portal_group *se_tpg, - const char *page, - size_t count) +static ssize_t tcm_qla2xxx_tpg_enable_store(struct config_item *item, + const char *page, size_t count) { + struct se_portal_group *se_tpg = to_tpg(item); struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, struct tcm_qla2xxx_tpg, se_tpg); unsigned long op; @@ -1085,22 +864,16 @@ static ssize_t tcm_qla2xxx_tpg_store_enable( return count; } -TF_TPG_BASE_ATTR(tcm_qla2xxx, enable, S_IRUGO | S_IWUSR); - -static ssize_t tcm_qla2xxx_tpg_show_dynamic_sessions( - struct se_portal_group *se_tpg, - char *page) +static ssize_t tcm_qla2xxx_tpg_dynamic_sessions_show(struct config_item *item, + char *page) { - return target_show_dynamic_sessions(se_tpg, page); + return target_show_dynamic_sessions(to_tpg(item), page); } -TF_TPG_BASE_ATTR_RO(tcm_qla2xxx, dynamic_sessions); - -static ssize_t tcm_qla2xxx_tpg_store_fabric_prot_type( - struct se_portal_group *se_tpg, - const char *page, - size_t count) +static ssize_t tcm_qla2xxx_tpg_fabric_prot_type_store(struct config_item *item, + const char *page, size_t count) { + struct se_portal_group *se_tpg = to_tpg(item); struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, struct tcm_qla2xxx_tpg, se_tpg); unsigned long val; @@ -1119,21 +892,24 @@ static ssize_t tcm_qla2xxx_tpg_store_fabric_prot_type( return count; } -static ssize_t tcm_qla2xxx_tpg_show_fabric_prot_type( - struct se_portal_group *se_tpg, - char *page) +static ssize_t tcm_qla2xxx_tpg_fabric_prot_type_show(struct config_item *item, + char *page) { + struct se_portal_group *se_tpg = to_tpg(item); struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, struct tcm_qla2xxx_tpg, se_tpg); return sprintf(page, "%d\n", tpg->tpg_attrib.fabric_prot_type); } -TF_TPG_BASE_ATTR(tcm_qla2xxx, fabric_prot_type, S_IRUGO | S_IWUSR); + +CONFIGFS_ATTR(tcm_qla2xxx_tpg_, enable); +CONFIGFS_ATTR_RO(tcm_qla2xxx_tpg_, dynamic_sessions); +CONFIGFS_ATTR(tcm_qla2xxx_tpg_, fabric_prot_type); static struct configfs_attribute *tcm_qla2xxx_tpg_attrs[] = { - &tcm_qla2xxx_tpg_enable.attr, - &tcm_qla2xxx_tpg_dynamic_sessions.attr, - &tcm_qla2xxx_tpg_fabric_prot_type.attr, + &tcm_qla2xxx_tpg_attr_enable, + &tcm_qla2xxx_tpg_attr_dynamic_sessions, + &tcm_qla2xxx_tpg_attr_fabric_prot_type, NULL, }; @@ -1174,8 +950,7 @@ static struct se_portal_group *tcm_qla2xxx_make_tpg( tpg->tpg_attrib.cache_dynamic_acls = 1; tpg->tpg_attrib.demo_mode_login_only = 1; - ret = core_tpg_register(&tcm_qla2xxx_ops, wwn, - &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); + ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP); if (ret < 0) { kfree(tpg); return NULL; @@ -1207,18 +982,16 @@ static void tcm_qla2xxx_drop_tpg(struct se_portal_group *se_tpg) kfree(tpg); } -static ssize_t tcm_qla2xxx_npiv_tpg_show_enable( - struct se_portal_group *se_tpg, - char *page) +static ssize_t tcm_qla2xxx_npiv_tpg_enable_show(struct config_item *item, + char *page) { - return tcm_qla2xxx_tpg_show_enable(se_tpg, page); + return tcm_qla2xxx_tpg_enable_show(item, page); } -static ssize_t tcm_qla2xxx_npiv_tpg_store_enable( - struct se_portal_group *se_tpg, - const char *page, - size_t count) +static ssize_t tcm_qla2xxx_npiv_tpg_enable_store(struct config_item *item, + const char *page, size_t count) { + struct se_portal_group *se_tpg = to_tpg(item); struct se_wwn *se_wwn = se_tpg->se_tpg_wwn; struct tcm_qla2xxx_lport *lport = container_of(se_wwn, struct tcm_qla2xxx_lport, lport_wwn); @@ -1254,10 +1027,10 @@ static ssize_t tcm_qla2xxx_npiv_tpg_store_enable( return count; } -TF_TPG_BASE_ATTR(tcm_qla2xxx_npiv, enable, S_IRUGO | S_IWUSR); +CONFIGFS_ATTR(tcm_qla2xxx_npiv_tpg_, enable); static struct configfs_attribute *tcm_qla2xxx_npiv_tpg_attrs[] = { - &tcm_qla2xxx_npiv_tpg_enable.attr, + &tcm_qla2xxx_npiv_tpg_attr_enable, NULL, }; @@ -1294,8 +1067,7 @@ static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg( tpg->tpg_attrib.cache_dynamic_acls = 1; tpg->tpg_attrib.demo_mode_login_only = 1; - ret = core_tpg_register(&tcm_qla2xxx_npiv_ops, wwn, - &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); + ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP); if (ret < 0) { kfree(tpg); return NULL; @@ -1323,9 +1095,7 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( return NULL; } - key = (((unsigned long)s_id[0] << 16) | - ((unsigned long)s_id[1] << 8) | - (unsigned long)s_id[2]); + key = sid_to_key(s_id); pr_debug("find_sess_by_s_id: 0x%06x\n", key); se_nacl = btree_lookup32(&lport->lport_fcport_map, key); @@ -1360,9 +1130,7 @@ static void tcm_qla2xxx_set_sess_by_s_id( void *slot; int rc; - key = (((unsigned long)s_id[0] << 16) | - ((unsigned long)s_id[1] << 8) | - (unsigned long)s_id[2]); + key = sid_to_key(s_id); pr_debug("set_sess_by_s_id: %06x\n", key); slot = btree_lookup32(&lport->lport_fcport_map, key); @@ -1541,9 +1309,7 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) struct qla_hw_data *ha = tgt->ha; scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); struct se_session *se_sess; - struct se_node_acl *se_nacl; struct tcm_qla2xxx_lport *lport; - struct tcm_qla2xxx_nacl *nacl; BUG_ON(in_interrupt()); @@ -1553,8 +1319,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) dump_stack(); return; } - se_nacl = se_sess->se_node_acl; - nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); lport = vha->vha_tgt.target_lport_ptr; if (!lport) { @@ -1718,6 +1482,10 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, } sess->conf_compl_supported = conf_compl_supported; + + /* Reset logout parameters to default */ + sess->logout_on_delete = 1; + sess->keep_nport_handle = 0; } /* @@ -1858,7 +1626,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, (struct tcm_qla2xxx_lport *)target_lport_ptr; struct tcm_qla2xxx_lport *base_lport = (struct tcm_qla2xxx_lport *)base_vha->vha_tgt.target_lport_ptr; - struct tcm_qla2xxx_tpg *base_tpg; struct fc_vport_identifiers vport_id; if (!qla_tgt_mode_enabled(base_vha)) { @@ -1871,7 +1638,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, pr_err("qla2xxx base_lport or tpg_1 not available\n"); return -EPERM; } - base_tpg = base_lport->tpg_1; memset(&vport_id, 0, sizeof(vport_id)); vport_id.port_name = npiv_wwpn; @@ -1967,9 +1733,8 @@ static void tcm_qla2xxx_npiv_drop_lport(struct se_wwn *wwn) } -static ssize_t tcm_qla2xxx_wwn_show_attr_version( - struct target_fabric_configfs *tf, - char *page) +static ssize_t tcm_qla2xxx_wwn_version_show(struct config_item *item, + char *page) { return sprintf(page, "TCM QLOGIC QLA2XXX NPIV capable fabric module %s on %s/%s on " @@ -1977,24 +1742,25 @@ static ssize_t tcm_qla2xxx_wwn_show_attr_version( utsname()->machine); } -TF_WWN_ATTR_RO(tcm_qla2xxx, version); +CONFIGFS_ATTR_RO(tcm_qla2xxx_wwn_, version); static struct configfs_attribute *tcm_qla2xxx_wwn_attrs[] = { - &tcm_qla2xxx_wwn_version.attr, + &tcm_qla2xxx_wwn_attr_version, NULL, }; static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .module = THIS_MODULE, .name = "qla2xxx", + .node_acl_size = sizeof(struct tcm_qla2xxx_nacl), + /* + * XXX: Limit assumes single page per scatter-gather-list entry. + * Current maximum is ~4.9 MB per se_cmd->t_data_sg with PAGE_SIZE=4096 + */ + .max_data_sg_nents = 1200, .get_fabric_name = tcm_qla2xxx_get_fabric_name, - .get_fabric_proto_ident = tcm_qla2xxx_get_fabric_proto_ident, .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, .tpg_get_tag = tcm_qla2xxx_get_tag, - .tpg_get_default_depth = tcm_qla2xxx_get_default_depth, - .tpg_get_pr_transport_id = tcm_qla2xxx_get_pr_transport_id, - .tpg_get_pr_transport_id_len = tcm_qla2xxx_get_pr_transport_id_len, - .tpg_parse_pr_out_transport_id = tcm_qla2xxx_parse_pr_out_transport_id, .tpg_check_demo_mode = tcm_qla2xxx_check_demo_mode, .tpg_check_demo_mode_cache = tcm_qla2xxx_check_demo_mode_cache, .tpg_check_demo_mode_write_protect = @@ -2003,12 +1769,9 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { tcm_qla2xxx_check_prod_write_protect, .tpg_check_prot_fabric_only = tcm_qla2xxx_check_prot_fabric_only, .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only, - .tpg_alloc_fabric_acl = tcm_qla2xxx_alloc_fabric_acl, - .tpg_release_fabric_acl = tcm_qla2xxx_release_fabric_acl, .tpg_get_inst_index = tcm_qla2xxx_tpg_get_inst_index, .check_stop_free = tcm_qla2xxx_check_stop_free, .release_cmd = tcm_qla2xxx_release_cmd, - .put_session = tcm_qla2xxx_put_session, .shutdown_session = tcm_qla2xxx_shutdown_session, .close_session = tcm_qla2xxx_close_session, .sess_get_index = tcm_qla2xxx_sess_get_index, @@ -2016,7 +1779,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .write_pending = tcm_qla2xxx_write_pending, .write_pending_status = tcm_qla2xxx_write_pending_status, .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, - .get_task_tag = tcm_qla2xxx_get_task_tag, .get_cmd_state = tcm_qla2xxx_get_cmd_state, .queue_data_in = tcm_qla2xxx_queue_data_in, .queue_status = tcm_qla2xxx_queue_status, @@ -2030,12 +1792,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { .fabric_drop_wwn = tcm_qla2xxx_drop_lport, .fabric_make_tpg = tcm_qla2xxx_make_tpg, .fabric_drop_tpg = tcm_qla2xxx_drop_tpg, - .fabric_post_link = NULL, - .fabric_pre_unlink = NULL, - .fabric_make_np = NULL, - .fabric_drop_np = NULL, - .fabric_make_nodeacl = tcm_qla2xxx_make_nodeacl, - .fabric_drop_nodeacl = tcm_qla2xxx_drop_nodeacl, + .fabric_init_nodeacl = tcm_qla2xxx_init_nodeacl, .tfc_wwn_attrs = tcm_qla2xxx_wwn_attrs, .tfc_tpg_base_attrs = tcm_qla2xxx_tpg_attrs, @@ -2045,26 +1802,19 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = { static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .module = THIS_MODULE, .name = "qla2xxx_npiv", + .node_acl_size = sizeof(struct tcm_qla2xxx_nacl), .get_fabric_name = tcm_qla2xxx_npiv_get_fabric_name, - .get_fabric_proto_ident = tcm_qla2xxx_get_fabric_proto_ident, .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, .tpg_get_tag = tcm_qla2xxx_get_tag, - .tpg_get_default_depth = tcm_qla2xxx_get_default_depth, - .tpg_get_pr_transport_id = tcm_qla2xxx_get_pr_transport_id, - .tpg_get_pr_transport_id_len = tcm_qla2xxx_get_pr_transport_id_len, - .tpg_parse_pr_out_transport_id = tcm_qla2xxx_parse_pr_out_transport_id, .tpg_check_demo_mode = tcm_qla2xxx_check_demo_mode, .tpg_check_demo_mode_cache = tcm_qla2xxx_check_demo_mode_cache, .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_demo_mode, .tpg_check_prod_mode_write_protect = tcm_qla2xxx_check_prod_write_protect, .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only, - .tpg_alloc_fabric_acl = tcm_qla2xxx_alloc_fabric_acl, - .tpg_release_fabric_acl = tcm_qla2xxx_release_fabric_acl, .tpg_get_inst_index = tcm_qla2xxx_tpg_get_inst_index, .check_stop_free = tcm_qla2xxx_check_stop_free, .release_cmd = tcm_qla2xxx_release_cmd, - .put_session = tcm_qla2xxx_put_session, .shutdown_session = tcm_qla2xxx_shutdown_session, .close_session = tcm_qla2xxx_close_session, .sess_get_index = tcm_qla2xxx_sess_get_index, @@ -2072,7 +1822,6 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .write_pending = tcm_qla2xxx_write_pending, .write_pending_status = tcm_qla2xxx_write_pending_status, .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, - .get_task_tag = tcm_qla2xxx_get_task_tag, .get_cmd_state = tcm_qla2xxx_get_cmd_state, .queue_data_in = tcm_qla2xxx_queue_data_in, .queue_status = tcm_qla2xxx_queue_status, @@ -2086,12 +1835,7 @@ static const struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .fabric_drop_wwn = tcm_qla2xxx_npiv_drop_lport, .fabric_make_tpg = tcm_qla2xxx_npiv_make_tpg, .fabric_drop_tpg = tcm_qla2xxx_drop_tpg, - .fabric_post_link = NULL, - .fabric_pre_unlink = NULL, - .fabric_make_np = NULL, - .fabric_drop_np = NULL, - .fabric_make_nodeacl = tcm_qla2xxx_make_nodeacl, - .fabric_drop_nodeacl = tcm_qla2xxx_drop_nodeacl, + .fabric_init_nodeacl = tcm_qla2xxx_init_nodeacl, .tfc_wwn_attrs = tcm_qla2xxx_wwn_attrs, .tfc_tpg_base_attrs = tcm_qla2xxx_npiv_tpg_attrs, @@ -2162,7 +1906,7 @@ static void __exit tcm_qla2xxx_exit(void) tcm_qla2xxx_deregister_configfs(); } -MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver"); +MODULE_DESCRIPTION("TCM QLA24XX+ series NPIV enabled fabric driver"); MODULE_LICENSE("GPL"); module_init(tcm_qla2xxx_init); module_exit(tcm_qla2xxx_exit); diff --git a/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.h index 23295115c..3bbf4cb6f 100644 --- a/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.h +++ b/kernel/drivers/scsi/qla2xxx/tcm_qla2xxx.h @@ -13,6 +13,8 @@ #include "qla_target.h" struct tcm_qla2xxx_nacl { + struct se_node_acl se_node_acl; + /* From libfc struct fc_rport->port_id */ u32 nport_id; /* Binary World Wide unique Node Name for remote FC Initiator Nport */ @@ -23,8 +25,6 @@ struct tcm_qla2xxx_nacl { struct qla_tgt_sess *qla_tgt_sess; /* Pointer to TCM FC nexus */ struct se_session *nport_nexus; - /* Returned by tcm_qla2xxx_make_nodeacl() */ - struct se_node_acl se_node_acl; }; struct tcm_qla2xxx_tpg_attrib { @@ -57,8 +57,6 @@ struct tcm_qla2xxx_fc_loopid { }; struct tcm_qla2xxx_lport { - /* SCSI protocol the lport is providing */ - u8 lport_proto_id; /* Binary World Wide unique Port Name for FC Target Lport */ u64 lport_wwpn; /* Binary World Wide unique Port Name for FC NPIV Target Lport */ diff --git a/kernel/drivers/scsi/qla4xxx/ql4_83xx.c b/kernel/drivers/scsi/qla4xxx/ql4_83xx.c index 556c1525f..5d4f8e67f 100644 --- a/kernel/drivers/scsi/qla4xxx/ql4_83xx.c +++ b/kernel/drivers/scsi/qla4xxx/ql4_83xx.c @@ -828,7 +828,7 @@ void qla4_83xx_read_reset_template(struct scsi_qla_host *ha) ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff, tmplt_hdr_def_size); if (ret_val != QLA_SUCCESS) { - ql4_printk(KERN_ERR, ha, "%s: Failed to read reset tempelate\n", + ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n", __func__); goto exit_read_template_error; } diff --git a/kernel/drivers/scsi/qla4xxx/ql4_bsg.c b/kernel/drivers/scsi/qla4xxx/ql4_bsg.c index 9f92cbf96..415ee5eb3 100644 --- a/kernel/drivers/scsi/qla4xxx/ql4_bsg.c +++ b/kernel/drivers/scsi/qla4xxx/ql4_bsg.c @@ -571,7 +571,7 @@ static int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha, if ((config & ENABLE_INTERNAL_LOOPBACK) || (config & ENABLE_EXTERNAL_LOOPBACK)) { - ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid requiest\n", + ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid request\n", __func__); goto exit_pre_loopback_config; } diff --git a/kernel/drivers/scsi/qla4xxx/ql4_def.h b/kernel/drivers/scsi/qla4xxx/ql4_def.h index 8f6d0fb2c..a7cfc270b 100644 --- a/kernel/drivers/scsi/qla4xxx/ql4_def.h +++ b/kernel/drivers/scsi/qla4xxx/ql4_def.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/kernel/drivers/scsi/qla4xxx/ql4_nx.c b/kernel/drivers/scsi/qla4xxx/ql4_nx.c index 7c3365864..ae87d6c19 100644 --- a/kernel/drivers/scsi/qla4xxx/ql4_nx.c +++ b/kernel/drivers/scsi/qla4xxx/ql4_nx.c @@ -12,7 +12,7 @@ #include "ql4_glbl.h" #include "ql4_inline.h" -#include +#include #define TIMEOUT_100_MS 100 #define MASK(n) DMA_BIT_MASK(n) diff --git a/kernel/drivers/scsi/qla4xxx/ql4_os.c b/kernel/drivers/scsi/qla4xxx/ql4_os.c index 6d25879d8..01c3610a6 100644 --- a/kernel/drivers/scsi/qla4xxx/ql4_os.c +++ b/kernel/drivers/scsi/qla4xxx/ql4_os.c @@ -212,7 +212,6 @@ static struct scsi_host_template qla4xxx_driver_template = { .shost_attrs = qla4xxx_host_attrs, .host_reset = qla4xxx_host_reset, .vendor_id = SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC, - .use_blk_tags = 1, }; static struct iscsi_transport qla4xxx_iscsi_transport = { @@ -8697,13 +8696,6 @@ static int qla4xxx_probe_adapter(struct pci_dev *pdev, host->can_queue = MAX_SRBS ; host->transportt = qla4xxx_scsi_transport; - ret = scsi_init_shared_tag_map(host, MAX_SRBS); - if (ret) { - ql4_printk(KERN_WARNING, ha, - "%s: scsi_init_shared_tag_map failed\n", __func__); - goto probe_failed; - } - pci_set_drvdata(pdev, ha); ret = scsi_add_host(host, &pdev->dev); diff --git a/kernel/drivers/scsi/qlogicfas.c b/kernel/drivers/scsi/qlogicfas.c index a22bb1b40..61cac87fb 100644 --- a/kernel/drivers/scsi/qlogicfas.c +++ b/kernel/drivers/scsi/qlogicfas.c @@ -193,7 +193,6 @@ static struct scsi_host_template qlogicfas_driver_template = { .can_queue = 1, .this_id = -1, .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/qlogicpti.c b/kernel/drivers/scsi/qlogicpti.c index fe122700c..676385ff2 100644 --- a/kernel/drivers/scsi/qlogicpti.c +++ b/kernel/drivers/scsi/qlogicpti.c @@ -1287,7 +1287,6 @@ static struct scsi_host_template qpti_template = { .can_queue = QLOGICPTI_REQ_QUEUE_LEN, .this_id = 7, .sg_tablesize = QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN), - .cmd_per_lun = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/scsi.c b/kernel/drivers/scsi/scsi.c index 3833bf59f..d07fb653f 100644 --- a/kernel/drivers/scsi/scsi.c +++ b/kernel/drivers/scsi/scsi.c @@ -98,52 +98,6 @@ EXPORT_SYMBOL(scsi_sd_probe_domain); ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain); EXPORT_SYMBOL(scsi_sd_pm_domain); -/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. - * You may not alter any existing entry (although adding new ones is - * encouraged once assigned by ANSI/INCITS T10 - */ -static const char *const scsi_device_types[] = { - "Direct-Access ", - "Sequential-Access", - "Printer ", - "Processor ", - "WORM ", - "CD-ROM ", - "Scanner ", - "Optical Device ", - "Medium Changer ", - "Communications ", - "ASC IT8 ", - "ASC IT8 ", - "RAID ", - "Enclosure ", - "Direct-Access-RBC", - "Optical card ", - "Bridge controller", - "Object storage ", - "Automation/Drive ", - "Security Manager ", - "Direct-Access-ZBC", -}; - -/** - * scsi_device_type - Return 17 char string indicating device type. - * @type: type number to look up - */ - -const char * scsi_device_type(unsigned type) -{ - if (type == 0x1e) - return "Well-known LUN "; - if (type == 0x1f) - return "No Device "; - if (type >= ARRAY_SIZE(scsi_device_types)) - return "Unknown "; - return scsi_device_types[type]; -} - -EXPORT_SYMBOL(scsi_device_type); - struct scsi_host_cmd_pool { struct kmem_cache *cmd_slab; struct kmem_cache *sense_slab; @@ -662,32 +616,11 @@ void scsi_finish_command(struct scsi_cmnd *cmd) */ int scsi_change_queue_depth(struct scsi_device *sdev, int depth) { - unsigned long flags; - - if (depth <= 0) - goto out; - - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); - - /* - * Check to see if the queue is managed by the block layer. - * If it is, and we fail to adjust the depth, exit. - * - * Do not resize the tag map if it is a host wide share bqt, - * because the size should be the hosts's can_queue. If there - * is more IO than the LLD's can_queue (so there are not enuogh - * tags) request_fn's host queue ready check will handle it. - */ - if (!shost_use_blk_mq(sdev->host) && !sdev->host->bqt) { - if (blk_queue_tagged(sdev->request_queue) && - blk_queue_resize_tags(sdev->request_queue, depth) != 0) - goto out_unlock; + if (depth > 0) { + sdev->queue_depth = depth; + wmb(); } - sdev->queue_depth = depth; -out_unlock: - spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); -out: return sdev->queue_depth; } EXPORT_SYMBOL(scsi_change_queue_depth); diff --git a/kernel/drivers/scsi/scsi_common.c b/kernel/drivers/scsi/scsi_common.c new file mode 100644 index 000000000..c12696613 --- /dev/null +++ b/kernel/drivers/scsi/scsi_common.c @@ -0,0 +1,287 @@ +/* + * SCSI functions used by both the initiator and the target code. + */ + +#include +#include +#include +#include +#include +#include + +/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. + * You may not alter any existing entry (although adding new ones is + * encouraged once assigned by ANSI/INCITS T10 + */ +static const char *const scsi_device_types[] = { + "Direct-Access ", + "Sequential-Access", + "Printer ", + "Processor ", + "WORM ", + "CD-ROM ", + "Scanner ", + "Optical Device ", + "Medium Changer ", + "Communications ", + "ASC IT8 ", + "ASC IT8 ", + "RAID ", + "Enclosure ", + "Direct-Access-RBC", + "Optical card ", + "Bridge controller", + "Object storage ", + "Automation/Drive ", + "Security Manager ", + "Direct-Access-ZBC", +}; + +/** + * scsi_device_type - Return 17 char string indicating device type. + * @type: type number to look up + */ +const char *scsi_device_type(unsigned type) +{ + if (type == 0x1e) + return "Well-known LUN "; + if (type == 0x1f) + return "No Device "; + if (type >= ARRAY_SIZE(scsi_device_types)) + return "Unknown "; + return scsi_device_types[type]; +} +EXPORT_SYMBOL(scsi_device_type); + +/** + * scsilun_to_int - convert a scsi_lun to an int + * @scsilun: struct scsi_lun to be converted. + * + * Description: + * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered + * integer, and return the result. The caller must check for + * truncation before using this function. + * + * Notes: + * For a description of the LUN format, post SCSI-3 see the SCSI + * Architecture Model, for SCSI-3 see the SCSI Controller Commands. + * + * Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function + * returns the integer: 0x0b03d204 + * + * This encoding will return a standard integer LUN for LUNs smaller + * than 256, which typically use a single level LUN structure with + * addressing method 0. + */ +u64 scsilun_to_int(struct scsi_lun *scsilun) +{ + int i; + u64 lun; + + lun = 0; + for (i = 0; i < sizeof(lun); i += 2) + lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) | + ((u64)scsilun->scsi_lun[i + 1] << (i * 8))); + return lun; +} +EXPORT_SYMBOL(scsilun_to_int); + +/** + * int_to_scsilun - reverts an int into a scsi_lun + * @lun: integer to be reverted + * @scsilun: struct scsi_lun to be set. + * + * Description: + * Reverts the functionality of the scsilun_to_int, which packed + * an 8-byte lun value into an int. This routine unpacks the int + * back into the lun value. + * + * Notes: + * Given an integer : 0x0b03d204, this function returns a + * struct scsi_lun of: d2 04 0b 03 00 00 00 00 + * + */ +void int_to_scsilun(u64 lun, struct scsi_lun *scsilun) +{ + int i; + + memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); + + for (i = 0; i < sizeof(lun); i += 2) { + scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; + scsilun->scsi_lun[i+1] = lun & 0xFF; + lun = lun >> 16; + } +} +EXPORT_SYMBOL(int_to_scsilun); + +/** + * scsi_normalize_sense - normalize main elements from either fixed or + * descriptor sense data format into a common format. + * + * @sense_buffer: byte array containing sense data returned by device + * @sb_len: number of valid bytes in sense_buffer + * @sshdr: pointer to instance of structure that common + * elements are written to. + * + * Notes: + * The "main elements" from sense data are: response_code, sense_key, + * asc, ascq and additional_length (only for descriptor format). + * + * Typically this function can be called after a device has + * responded to a SCSI command with the CHECK_CONDITION status. + * + * Return value: + * true if valid sense data information found, else false; + */ +bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, + struct scsi_sense_hdr *sshdr) +{ + if (!sense_buffer || !sb_len) + return false; + + memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); + + sshdr->response_code = (sense_buffer[0] & 0x7f); + + if (!scsi_sense_valid(sshdr)) + return false; + + if (sshdr->response_code >= 0x72) { + /* + * descriptor format + */ + if (sb_len > 1) + sshdr->sense_key = (sense_buffer[1] & 0xf); + if (sb_len > 2) + sshdr->asc = sense_buffer[2]; + if (sb_len > 3) + sshdr->ascq = sense_buffer[3]; + if (sb_len > 7) + sshdr->additional_length = sense_buffer[7]; + } else { + /* + * fixed format + */ + if (sb_len > 2) + sshdr->sense_key = (sense_buffer[2] & 0xf); + if (sb_len > 7) { + sb_len = (sb_len < (sense_buffer[7] + 8)) ? + sb_len : (sense_buffer[7] + 8); + if (sb_len > 12) + sshdr->asc = sense_buffer[12]; + if (sb_len > 13) + sshdr->ascq = sense_buffer[13]; + } + } + + return true; +} +EXPORT_SYMBOL(scsi_normalize_sense); + +/** + * scsi_sense_desc_find - search for a given descriptor type in descriptor sense data format. + * @sense_buffer: byte array of descriptor format sense data + * @sb_len: number of valid bytes in sense_buffer + * @desc_type: value of descriptor type to find + * (e.g. 0 -> information) + * + * Notes: + * only valid when sense data is in descriptor format + * + * Return value: + * pointer to start of (first) descriptor if found else NULL + */ +const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, + int desc_type) +{ + int add_sen_len, add_len, desc_len, k; + const u8 * descp; + + if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7]))) + return NULL; + if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73)) + return NULL; + add_sen_len = (add_sen_len < (sb_len - 8)) ? + add_sen_len : (sb_len - 8); + descp = &sense_buffer[8]; + for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { + descp += desc_len; + add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; + desc_len = add_len + 2; + if (descp[0] == desc_type) + return descp; + if (add_len < 0) // short descriptor ?? + break; + } + return NULL; +} +EXPORT_SYMBOL(scsi_sense_desc_find); + +/** + * scsi_build_sense_buffer - build sense data in a buffer + * @desc: Sense format (non zero == descriptor format, + * 0 == fixed format) + * @buf: Where to build sense data + * @key: Sense key + * @asc: Additional sense code + * @ascq: Additional sense code qualifier + * + **/ +void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) +{ + if (desc) { + buf[0] = 0x72; /* descriptor, current */ + buf[1] = key; + buf[2] = asc; + buf[3] = ascq; + buf[7] = 0; + } else { + buf[0] = 0x70; /* fixed, current */ + buf[2] = key; + buf[7] = 0xa; + buf[12] = asc; + buf[13] = ascq; + } +} +EXPORT_SYMBOL(scsi_build_sense_buffer); + +/** + * scsi_set_sense_information - set the information field in a + * formatted sense data buffer + * @buf: Where to build sense data + * @buf_len: buffer length + * @info: 64-bit information value to be set + * + * Return value: + * 0 on success or EINVAL for invalid sense buffer length + **/ +int scsi_set_sense_information(u8 *buf, int buf_len, u64 info) +{ + if ((buf[0] & 0x7f) == 0x72) { + u8 *ucp, len; + + len = buf[7]; + ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0); + if (!ucp) { + buf[7] = len + 0xc; + ucp = buf + 8 + len; + } + + if (buf_len < len + 0xc) + /* Not enough room for info */ + return -EINVAL; + + ucp[0] = 0; + ucp[1] = 0xa; + ucp[2] = 0x80; /* Valid bit */ + ucp[3] = 0; + put_unaligned_be64(info, &ucp[4]); + } else if ((buf[0] & 0x7f) == 0x70) { + buf[0] |= 0x80; + put_unaligned_be64(info, &buf[3]); + } + + return 0; +} +EXPORT_SYMBOL(scsi_set_sense_information); diff --git a/kernel/drivers/scsi/scsi_debug.c b/kernel/drivers/scsi/scsi_debug.c index 1f8e2dc9c..d09d60293 100644 --- a/kernel/drivers/scsi/scsi_debug.c +++ b/kernel/drivers/scsi/scsi_debug.c @@ -25,6 +25,9 @@ * module options to "modprobe scsi_debug num_tgts=2" [20021221] */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ + #include #include @@ -201,7 +204,6 @@ static const char *scsi_debug_version_date = "20141022"; /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1) * or "peripheral device" addressing (value 0) */ #define SAM2_LUN_ADDRESS_METHOD 0 -#define SAM2_WLUN_REPORT_LUNS 0xc101 /* SCSI_DEBUG_CANQUEUE is the maximum number of commands that can be queued * (for response) at one time. Can be reduced by max_queue option. Command @@ -463,8 +465,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { 0} }, {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, - {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* VERIFY */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x2f, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, NULL, NULL, /* VERIFY(10) */ + {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, + 0, 0, 0, 0, 0, 0} }, {1, 0x7f, 0x9, F_SA_HIGH | F_D_IN | FF_DIRECT_IO, resp_read_dt0, vl_iarr, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0x9, 0xfe, 0, 0xff, 0xff, 0xff, 0xff} },/* VARIABLE LENGTH, READ(32) */ @@ -475,8 +478,8 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = { {10, 0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} }, /* 20 */ - {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ALLOW REMOVAL */ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, + {0, 0x1e, 0, 0, NULL, NULL, /* ALLOW REMOVAL */ + {6, 0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */ {6, 0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }, {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */ @@ -698,7 +701,7 @@ static void sdebug_max_tgts_luns(void) else hpnt->max_id = scsi_debug_num_tgts; /* scsi_debug_max_luns; */ - hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; + hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1; } spin_unlock(&sdebug_host_list_lock); } @@ -1288,7 +1291,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; - have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS); + have_wlun = (scp->device->lun == SCSI_W_LUN_REPORT_LUNS); if (have_wlun) pq_pdt = 0x1e; /* present, wlun */ else if (scsi_debug_no_lun_0 && (0 == devip->lun)) @@ -1427,12 +1430,11 @@ static int resp_requests(struct scsi_cmnd * scp, unsigned char * sbuff; unsigned char *cmd = scp->cmnd; unsigned char arr[SCSI_SENSE_BUFFERSIZE]; - bool dsense, want_dsense; + bool dsense; int len = 18; memset(arr, 0, sizeof(arr)); dsense = !!(cmd[1] & 1); - want_dsense = dsense || scsi_debug_dsense; sbuff = scp->sense_buffer; if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) { if (dsense) { @@ -2363,17 +2365,13 @@ do_device_access(struct scsi_cmnd *scmd, u64 lba, u32 num, bool do_write) u64 block, rest = 0; struct scsi_data_buffer *sdb; enum dma_data_direction dir; - size_t (*func)(struct scatterlist *, unsigned int, void *, size_t, - off_t); if (do_write) { sdb = scsi_out(scmd); dir = DMA_TO_DEVICE; - func = sg_pcopy_to_buffer; } else { sdb = scsi_in(scmd); dir = DMA_FROM_DEVICE; - func = sg_pcopy_from_buffer; } if (!sdb->length) @@ -2385,16 +2383,16 @@ do_device_access(struct scsi_cmnd *scmd, u64 lba, u32 num, bool do_write) if (block + num > sdebug_store_sectors) rest = block + num - sdebug_store_sectors; - ret = func(sdb->table.sgl, sdb->table.nents, + ret = sg_copy_buffer(sdb->table.sgl, sdb->table.nents, fake_storep + (block * scsi_debug_sector_size), - (num - rest) * scsi_debug_sector_size, 0); + (num - rest) * scsi_debug_sector_size, 0, do_write); if (ret != (num - rest) * scsi_debug_sector_size) return ret; if (rest) { - ret += func(sdb->table.sgl, sdb->table.nents, + ret += sg_copy_buffer(sdb->table.sgl, sdb->table.nents, fake_storep, rest * scsi_debug_sector_size, - (num - rest) * scsi_debug_sector_size); + (num - rest) * scsi_debug_sector_size, do_write); } return ret; @@ -2450,8 +2448,7 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, __be16 csum = dif_compute_csum(data, scsi_debug_sector_size); if (sdt->guard_tag != csum) { - pr_err("%s: GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n", - __func__, + pr_err("GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n", (unsigned long)sector, be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); @@ -2459,14 +2456,14 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data, } if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { - pr_err("%s: REF check failed on sector %lu\n", - __func__, (unsigned long)sector); + pr_err("REF check failed on sector %lu\n", + (unsigned long)sector); return 0x03; } if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && be32_to_cpu(sdt->ref_tag) != ei_lba) { - pr_err("%s: REF check failed on sector %lu\n", - __func__, (unsigned long)sector); + pr_err("REF check failed on sector %lu\n", + (unsigned long)sector); return 0x03; } return 0; @@ -2684,7 +2681,7 @@ resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) return 0; } -void dump_sector(unsigned char *buf, int len) +static void dump_sector(unsigned char *buf, int len) { int i, j, n; @@ -3369,8 +3366,8 @@ static int resp_report_luns(struct scsi_cmnd * scp, one_lun[i].scsi_lun[1] = lun & 0xff; } if (want_wlun) { - one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff; - one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff; + one_lun[i].scsi_lun[0] = (SCSI_W_LUN_REPORT_LUNS >> 8) & 0xff; + one_lun[i].scsi_lun[1] = SCSI_W_LUN_REPORT_LUNS & 0xff; i++; } alloc_len = (unsigned char *)(one_lun + i) - arr; @@ -3453,7 +3450,7 @@ static void sdebug_q_cmd_complete(unsigned long indx) atomic_inc(&sdebug_completions); qa_indx = indx; if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) { - pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx); + pr_err("wild qa_indx=%d\n", qa_indx); return; } spin_lock_irqsave(&queued_arr_lock, iflags); @@ -3461,21 +3458,21 @@ static void sdebug_q_cmd_complete(unsigned long indx) scp = sqcp->a_cmnd; if (NULL == scp) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: scp is NULL\n", __func__); + pr_err("scp is NULL\n"); return; } devip = (struct sdebug_dev_info *)scp->device->hostdata; if (devip) atomic_dec(&devip->num_in_q); else - pr_err("%s: devip=NULL\n", __func__); + pr_err("devip=NULL\n"); if (atomic_read(&retired_max_queue) > 0) retiring = 1; sqcp->a_cmnd = NULL; if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: Unexpected completion\n", __func__); + pr_err("Unexpected completion\n"); return; } @@ -3485,7 +3482,7 @@ static void sdebug_q_cmd_complete(unsigned long indx) retval = atomic_read(&retired_max_queue); if (qa_indx >= retval) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: index %d too large\n", __func__, retval); + pr_err("index %d too large\n", retval); return; } k = find_last_bit(queued_in_use_bm, retval); @@ -3513,7 +3510,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) atomic_inc(&sdebug_completions); qa_indx = sd_hrtp->qa_indx; if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) { - pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx); + pr_err("wild qa_indx=%d\n", qa_indx); goto the_end; } spin_lock_irqsave(&queued_arr_lock, iflags); @@ -3521,21 +3518,21 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) scp = sqcp->a_cmnd; if (NULL == scp) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: scp is NULL\n", __func__); + pr_err("scp is NULL\n"); goto the_end; } devip = (struct sdebug_dev_info *)scp->device->hostdata; if (devip) atomic_dec(&devip->num_in_q); else - pr_err("%s: devip=NULL\n", __func__); + pr_err("devip=NULL\n"); if (atomic_read(&retired_max_queue) > 0) retiring = 1; sqcp->a_cmnd = NULL; if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: Unexpected completion\n", __func__); + pr_err("Unexpected completion\n"); goto the_end; } @@ -3545,7 +3542,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer) retval = atomic_read(&retired_max_queue); if (qa_indx >= retval) { spin_unlock_irqrestore(&queued_arr_lock, iflags); - pr_err("%s: index %d too large\n", __func__, retval); + pr_err("index %d too large\n", retval); goto the_end; } k = find_last_bit(queued_in_use_bm, retval); @@ -3584,7 +3581,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) return devip; sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host); if (!sdbg_host) { - pr_err("%s: Host info NULL\n", __func__); + pr_err("Host info NULL\n"); return NULL; } list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) { @@ -3600,8 +3597,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) if (!open_devip) { /* try and make a new one */ open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC); if (!open_devip) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); return NULL; } } @@ -3619,7 +3615,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) static int scsi_debug_slave_alloc(struct scsi_device *sdp) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %llu>\n", + pr_info("slave_alloc <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; @@ -3630,7 +3626,7 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp) struct sdebug_dev_info *devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %llu>\n", + pr_info("slave_configure <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; @@ -3650,7 +3646,7 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp) (struct sdebug_dev_info *)sdp->hostdata; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) - printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %llu>\n", + pr_info("slave_destroy <%u %u %u %llu>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot available for re-use */ @@ -3901,8 +3897,7 @@ static void __init sdebug_build_parts(unsigned char *ramp, return; if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) { scsi_debug_num_parts = SDEBUG_MAX_PARTS; - pr_warn("%s: reducing partitions to %d\n", __func__, - SDEBUG_MAX_PARTS); + pr_warn("reducing partitions to %d\n", SDEBUG_MAX_PARTS); } num_sectors = (int)sdebug_store_sectors; sectors_per_part = (num_sectors - sdebug_sectors_per) @@ -3946,14 +3941,20 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, unsigned long iflags; int k, num_in_q, qdepth, inject; struct sdebug_queued_cmd *sqcp = NULL; - struct scsi_device *sdp = cmnd->device; + struct scsi_device *sdp; - if (NULL == cmnd || NULL == devip) { - pr_warn("%s: called with NULL cmnd or devip pointer\n", - __func__); + /* this should never happen */ + if (WARN_ON(!cmnd)) + return SCSI_MLQUEUE_HOST_BUSY; + + if (NULL == devip) { + pr_warn("called devip == NULL\n"); /* no particularly good error to report back */ return SCSI_MLQUEUE_HOST_BUSY; } + + sdp = cmnd->device; + if ((scsi_result) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n", __func__, scsi_result); @@ -4387,8 +4388,7 @@ static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf, fake_storep = vmalloc(sz); if (NULL == fake_storep) { - pr_err("%s: out of memory, 9\n", - __func__); + pr_err("out of memory, 9\n"); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -4788,8 +4788,7 @@ static int __init scsi_debug_init(void) atomic_set(&retired_max_queue, 0); if (scsi_debug_ndelay >= 1000000000) { - pr_warn("%s: ndelay must be less than 1 second, ignored\n", - __func__); + pr_warn("ndelay must be less than 1 second, ignored\n"); scsi_debug_ndelay = 0; } else if (scsi_debug_ndelay > 0) scsi_debug_delay = DELAY_OVERRIDDEN; @@ -4801,8 +4800,7 @@ static int __init scsi_debug_init(void) case 4096: break; default: - pr_err("%s: invalid sector_size %d\n", __func__, - scsi_debug_sector_size); + pr_err("invalid sector_size %d\n", scsi_debug_sector_size); return -EINVAL; } @@ -4815,29 +4813,28 @@ static int __init scsi_debug_init(void) break; default: - pr_err("%s: dif must be 0, 1, 2 or 3\n", __func__); + pr_err("dif must be 0, 1, 2 or 3\n"); return -EINVAL; } if (scsi_debug_guard > 1) { - pr_err("%s: guard must be 0 or 1\n", __func__); + pr_err("guard must be 0 or 1\n"); return -EINVAL; } if (scsi_debug_ato > 1) { - pr_err("%s: ato must be 0 or 1\n", __func__); + pr_err("ato must be 0 or 1\n"); return -EINVAL; } if (scsi_debug_physblk_exp > 15) { - pr_err("%s: invalid physblk_exp %u\n", __func__, - scsi_debug_physblk_exp); + pr_err("invalid physblk_exp %u\n", scsi_debug_physblk_exp); return -EINVAL; } if (scsi_debug_lowest_aligned > 0x3fff) { - pr_err("%s: lowest_aligned too big: %u\n", __func__, - scsi_debug_lowest_aligned); + pr_err("lowest_aligned too big: %u\n", + scsi_debug_lowest_aligned); return -EINVAL; } @@ -4867,7 +4864,7 @@ static int __init scsi_debug_init(void) if (0 == scsi_debug_fake_rw) { fake_storep = vmalloc(sz); if (NULL == fake_storep) { - pr_err("%s: out of memory, 1\n", __func__); + pr_err("out of memory, 1\n"); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -4881,11 +4878,10 @@ static int __init scsi_debug_init(void) dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple); dif_storep = vmalloc(dif_size); - pr_err("%s: dif_storep %u bytes @ %p\n", __func__, dif_size, - dif_storep); + pr_err("dif_storep %u bytes @ %p\n", dif_size, dif_storep); if (dif_storep == NULL) { - pr_err("%s: out of mem. (DIX)\n", __func__); + pr_err("out of mem. (DIX)\n"); ret = -ENOMEM; goto free_vm; } @@ -4907,18 +4903,17 @@ static int __init scsi_debug_init(void) if (scsi_debug_unmap_alignment && scsi_debug_unmap_granularity <= scsi_debug_unmap_alignment) { - pr_err("%s: ERR: unmap_granularity <= unmap_alignment\n", - __func__); + pr_err("ERR: unmap_granularity <= unmap_alignment\n"); return -EINVAL; } map_size = lba_to_map_index(sdebug_store_sectors - 1) + 1; map_storep = vmalloc(BITS_TO_LONGS(map_size) * sizeof(long)); - pr_info("%s: %lu provisioning blocks\n", __func__, map_size); + pr_info("%lu provisioning blocks\n", map_size); if (map_storep == NULL) { - pr_err("%s: out of mem. (MAP)\n", __func__); + pr_err("out of mem. (MAP)\n"); ret = -ENOMEM; goto free_vm; } @@ -4932,18 +4927,18 @@ static int __init scsi_debug_init(void) pseudo_primary = root_device_register("pseudo_0"); if (IS_ERR(pseudo_primary)) { - pr_warn("%s: root_device_register() error\n", __func__); + pr_warn("root_device_register() error\n"); ret = PTR_ERR(pseudo_primary); goto free_vm; } ret = bus_register(&pseudo_lld_bus); if (ret < 0) { - pr_warn("%s: bus_register error: %d\n", __func__, ret); + pr_warn("bus_register error: %d\n", ret); goto dev_unreg; } ret = driver_register(&sdebug_driverfs_driver); if (ret < 0) { - pr_warn("%s: driver_register error: %d\n", __func__, ret); + pr_warn("driver_register error: %d\n", ret); goto bus_unreg; } @@ -4952,16 +4947,14 @@ static int __init scsi_debug_init(void) for (k = 0; k < host_to_add; k++) { if (sdebug_add_adapter()) { - pr_err("%s: sdebug_add_adapter failed k=%d\n", - __func__, k); + pr_err("sdebug_add_adapter failed k=%d\n", k); break; } } - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { - pr_info("%s: built %d host(s)\n", __func__, - scsi_debug_add_host); - } + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + pr_info("built %d host(s)\n", scsi_debug_add_host); + return 0; bus_unreg: @@ -4969,10 +4962,8 @@ bus_unreg: dev_unreg: root_device_unregister(pseudo_primary); free_vm: - if (map_storep) - vfree(map_storep); - if (dif_storep) - vfree(dif_storep); + vfree(map_storep); + vfree(dif_storep); vfree(fake_storep); return ret; @@ -4990,9 +4981,7 @@ static void __exit scsi_debug_exit(void) bus_unregister(&pseudo_lld_bus); root_device_unregister(pseudo_primary); - if (dif_storep) - vfree(dif_storep); - + vfree(dif_storep); vfree(fake_storep); } @@ -5016,8 +5005,7 @@ static int sdebug_add_adapter(void) sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL); if (NULL == sdbg_host) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); return -ENOMEM; } @@ -5027,8 +5015,7 @@ static int sdebug_add_adapter(void) for (k = 0; k < devs_per_host; k++) { sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL); if (!sdbg_devinfo) { - printk(KERN_ERR "%s: out of memory at line %d\n", - __func__, __LINE__); + pr_err("out of memory at line %d\n", __LINE__); error = -ENOMEM; goto clean; } @@ -5182,7 +5169,7 @@ scsi_debug_queuecommand(struct scsi_cmnd *scp) } sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b); } - has_wlun_rl = (sdp->lun == SAM2_WLUN_REPORT_LUNS); + has_wlun_rl = (sdp->lun == SCSI_W_LUN_REPORT_LUNS); if ((sdp->lun >= scsi_debug_max_luns) && !has_wlun_rl) return schedule_resp(scp, NULL, errsts_no_connect, 0); @@ -5342,7 +5329,7 @@ static int sdebug_driver_probe(struct device * dev) sdebug_driver_template.use_clustering = ENABLE_CLUSTERING; hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); if (NULL == hpnt) { - pr_err("%s: scsi_host_alloc failed\n", __func__); + pr_err("scsi_host_alloc failed\n"); error = -ENODEV; return error; } @@ -5353,7 +5340,8 @@ static int sdebug_driver_probe(struct device * dev) hpnt->max_id = scsi_debug_num_tgts + 1; else hpnt->max_id = scsi_debug_num_tgts; - hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* = scsi_debug_max_luns; */ + /* = scsi_debug_max_luns; */ + hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1; host_prot = 0; @@ -5385,7 +5373,7 @@ static int sdebug_driver_probe(struct device * dev) scsi_host_set_prot(hpnt, host_prot); - printk(KERN_INFO "scsi_debug: host protection%s%s%s%s%s%s%s\n", + pr_info("host protection%s%s%s%s%s%s%s\n", (host_prot & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "", (host_prot & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "", (host_prot & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "", @@ -5413,7 +5401,7 @@ static int sdebug_driver_probe(struct device * dev) error = scsi_add_host(hpnt, &sdbg_host->dev); if (error) { - printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); + pr_err("scsi_add_host failed\n"); error = -ENODEV; scsi_host_put(hpnt); } else @@ -5430,8 +5418,7 @@ static int sdebug_driver_remove(struct device * dev) sdbg_host = to_sdebug_host(dev); if (!sdbg_host) { - printk(KERN_ERR "%s: Unable to locate host info\n", - __func__); + pr_err("Unable to locate host info\n"); return -ENODEV; } diff --git a/kernel/drivers/scsi/scsi_devinfo.c b/kernel/drivers/scsi/scsi_devinfo.c index 9f77d2323..da2e068ee 100644 --- a/kernel/drivers/scsi/scsi_devinfo.c +++ b/kernel/drivers/scsi/scsi_devinfo.c @@ -205,6 +205,7 @@ static struct { {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC}, {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"Marvell", "Console", NULL, BLIST_SKIP_VPD_PAGES}, {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA", "DMC-LC5", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, {"MATSHITA", "DMC-LC40", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36}, @@ -227,6 +228,7 @@ static struct { {"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC}, {"Promise", "", NULL, BLIST_SPARSELUN}, {"QNAP", "iSCSI Storage", NULL, BLIST_MAX_1024}, + {"SYNOLOGY", "iSCSI Storage", NULL, BLIST_MAX_1024}, {"QUANTUM", "XP34301", "1071", BLIST_NOTQ}, {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, {"SanDisk", "ImageMate CF-SD1", NULL, BLIST_FORCELUN}, @@ -390,25 +392,57 @@ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, EXPORT_SYMBOL(scsi_dev_info_list_add_keyed); /** - * scsi_dev_info_list_del_keyed - remove one dev_info list entry. + * scsi_dev_info_list_find - find a matching dev_info list entry. * @vendor: vendor string * @model: model (product) string * @key: specify list to use * * Description: - * Remove and destroy one dev_info entry for @vendor, @model + * Finds the first dev_info entry matching @vendor, @model * in list specified by @key. * - * Returns: 0 OK, -error on failure. + * Returns: pointer to matching entry, or ERR_PTR on failure. **/ -int scsi_dev_info_list_del_keyed(char *vendor, char *model, int key) +static struct scsi_dev_info_list *scsi_dev_info_list_find(const char *vendor, + const char *model, int key) { - struct scsi_dev_info_list *devinfo, *found = NULL; + struct scsi_dev_info_list *devinfo; struct scsi_dev_info_list_table *devinfo_table = scsi_devinfo_lookup_by_key(key); + size_t vmax, mmax; + const char *vskip, *mskip; if (IS_ERR(devinfo_table)) - return PTR_ERR(devinfo_table); + return (struct scsi_dev_info_list *) devinfo_table; + + /* Prepare for "compatible" matches */ + + /* + * XXX why skip leading spaces? If an odd INQUIRY + * value, that should have been part of the + * scsi_static_device_list[] entry, such as " FOO" + * rather than "FOO". Since this code is already + * here, and we don't know what device it is + * trying to work with, leave it as-is. + */ + vmax = 8; /* max length of vendor */ + vskip = vendor; + while (vmax > 0 && *vskip == ' ') { + vmax--; + vskip++; + } + /* Also skip trailing spaces */ + while (vmax > 0 && vskip[vmax - 1] == ' ') + --vmax; + + mmax = 16; /* max length of model */ + mskip = model; + while (mmax > 0 && *mskip == ' ') { + mmax--; + mskip++; + } + while (mmax > 0 && mskip[mmax - 1] == ' ') + --mmax; list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list, dev_info_list) { @@ -416,61 +450,48 @@ int scsi_dev_info_list_del_keyed(char *vendor, char *model, int key) /* * Behave like the older version of get_device_flags. */ - size_t max; - /* - * XXX why skip leading spaces? If an odd INQUIRY - * value, that should have been part of the - * scsi_static_device_list[] entry, such as " FOO" - * rather than "FOO". Since this code is already - * here, and we don't know what device it is - * trying to work with, leave it as-is. - */ - max = 8; /* max length of vendor */ - while ((max > 0) && *vendor == ' ') { - max--; - vendor++; - } - /* - * XXX removing the following strlen() would be - * good, using it means that for a an entry not in - * the list, we scan every byte of every vendor - * listed in scsi_static_device_list[], and never match - * a single one (and still have to compare at - * least the first byte of each vendor). - */ - if (memcmp(devinfo->vendor, vendor, - min(max, strlen(devinfo->vendor)))) + if (memcmp(devinfo->vendor, vskip, vmax) || + devinfo->vendor[vmax]) continue; - /* - * Skip spaces again. - */ - max = 16; /* max length of model */ - while ((max > 0) && *model == ' ') { - max--; - model++; - } - if (memcmp(devinfo->model, model, - min(max, strlen(devinfo->model)))) + if (memcmp(devinfo->model, mskip, mmax) || + devinfo->model[mmax]) continue; - found = devinfo; + return devinfo; } else { if (!memcmp(devinfo->vendor, vendor, sizeof(devinfo->vendor)) && !memcmp(devinfo->model, model, sizeof(devinfo->model))) - found = devinfo; + return devinfo; } - if (found) - break; } - if (found) { - list_del(&found->dev_info_list); - kfree(found); - return 0; - } + return ERR_PTR(-ENOENT); +} + +/** + * scsi_dev_info_list_del_keyed - remove one dev_info list entry. + * @vendor: vendor string + * @model: model (product) string + * @key: specify list to use + * + * Description: + * Remove and destroy one dev_info entry for @vendor, @model + * in list specified by @key. + * + * Returns: 0 OK, -error on failure. + **/ +int scsi_dev_info_list_del_keyed(char *vendor, char *model, int key) +{ + struct scsi_dev_info_list *found; - return -ENOENT; + found = scsi_dev_info_list_find(vendor, model, key); + if (IS_ERR(found)) + return PTR_ERR(found); + + list_del(&found->dev_info_list); + kfree(found); + return 0; } EXPORT_SYMBOL(scsi_dev_info_list_del_keyed); @@ -565,64 +586,16 @@ int scsi_get_device_flags_keyed(struct scsi_device *sdev, int key) { struct scsi_dev_info_list *devinfo; - struct scsi_dev_info_list_table *devinfo_table; + int err; - devinfo_table = scsi_devinfo_lookup_by_key(key); + devinfo = scsi_dev_info_list_find(vendor, model, key); + if (!IS_ERR(devinfo)) + return devinfo->flags; - if (IS_ERR(devinfo_table)) - return PTR_ERR(devinfo_table); + err = PTR_ERR(devinfo); + if (err != -ENOENT) + return err; - list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list, - dev_info_list) { - if (devinfo->compatible) { - /* - * Behave like the older version of get_device_flags. - */ - size_t max; - /* - * XXX why skip leading spaces? If an odd INQUIRY - * value, that should have been part of the - * scsi_static_device_list[] entry, such as " FOO" - * rather than "FOO". Since this code is already - * here, and we don't know what device it is - * trying to work with, leave it as-is. - */ - max = 8; /* max length of vendor */ - while ((max > 0) && *vendor == ' ') { - max--; - vendor++; - } - /* - * XXX removing the following strlen() would be - * good, using it means that for a an entry not in - * the list, we scan every byte of every vendor - * listed in scsi_static_device_list[], and never match - * a single one (and still have to compare at - * least the first byte of each vendor). - */ - if (memcmp(devinfo->vendor, vendor, - min(max, strlen(devinfo->vendor)))) - continue; - /* - * Skip spaces again. - */ - max = 16; /* max length of model */ - while ((max > 0) && *model == ' ') { - max--; - model++; - } - if (memcmp(devinfo->model, model, - min(max, strlen(devinfo->model)))) - continue; - return devinfo->flags; - } else { - if (!memcmp(devinfo->vendor, vendor, - sizeof(devinfo->vendor)) && - !memcmp(devinfo->model, model, - sizeof(devinfo->model))) - return devinfo->flags; - } - } /* nothing found, return nothing */ if (key != SCSI_DEVINFO_GLOBAL) return 0; diff --git a/kernel/drivers/scsi/scsi_dh.c b/kernel/drivers/scsi/scsi_dh.c new file mode 100644 index 000000000..e7649ed3f --- /dev/null +++ b/kernel/drivers/scsi/scsi_dh.c @@ -0,0 +1,441 @@ +/* + * SCSI device handler infrastruture. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * 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. + * + * Copyright IBM Corporation, 2007 + * Authors: + * Chandra Seetharaman + * Mike Anderson + */ + +#include +#include +#include +#include "scsi_priv.h" + +static DEFINE_SPINLOCK(list_lock); +static LIST_HEAD(scsi_dh_list); + +struct scsi_dh_blist { + const char *vendor; + const char *model; + const char *driver; +}; + +static const struct scsi_dh_blist scsi_dh_blist[] = { + {"DGC", "RAID", "clariion" }, + {"DGC", "DISK", "clariion" }, + {"DGC", "VRAID", "clariion" }, + + {"COMPAQ", "MSA1000 VOLUME", "hp_sw" }, + {"COMPAQ", "HSV110", "hp_sw" }, + {"HP", "HSV100", "hp_sw"}, + {"DEC", "HSG80", "hp_sw"}, + + {"IBM", "1722", "rdac", }, + {"IBM", "1724", "rdac", }, + {"IBM", "1726", "rdac", }, + {"IBM", "1742", "rdac", }, + {"IBM", "1745", "rdac", }, + {"IBM", "1746", "rdac", }, + {"IBM", "1813", "rdac", }, + {"IBM", "1814", "rdac", }, + {"IBM", "1815", "rdac", }, + {"IBM", "1818", "rdac", }, + {"IBM", "3526", "rdac", }, + {"SGI", "TP9", "rdac", }, + {"SGI", "IS", "rdac", }, + {"STK", "OPENstorage D280", "rdac", }, + {"STK", "FLEXLINE 380", "rdac", }, + {"SUN", "CSM", "rdac", }, + {"SUN", "LCSM100", "rdac", }, + {"SUN", "STK6580_6780", "rdac", }, + {"SUN", "SUN_6180", "rdac", }, + {"SUN", "ArrayStorage", "rdac", }, + {"DELL", "MD3", "rdac", }, + {"NETAPP", "INF-01-00", "rdac", }, + {"LSI", "INF-01-00", "rdac", }, + {"ENGENIO", "INF-01-00", "rdac", }, + {NULL, NULL, NULL }, +}; + +static const char * +scsi_dh_find_driver(struct scsi_device *sdev) +{ + const struct scsi_dh_blist *b; + + if (scsi_device_tpgs(sdev)) + return "alua"; + + for (b = scsi_dh_blist; b->vendor; b++) { + if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) && + !strncmp(sdev->model, b->model, strlen(b->model))) { + return b->driver; + } + } + return NULL; +} + + +static struct scsi_device_handler *__scsi_dh_lookup(const char *name) +{ + struct scsi_device_handler *tmp, *found = NULL; + + spin_lock(&list_lock); + list_for_each_entry(tmp, &scsi_dh_list, list) { + if (!strncmp(tmp->name, name, strlen(tmp->name))) { + found = tmp; + break; + } + } + spin_unlock(&list_lock); + return found; +} + +static struct scsi_device_handler *scsi_dh_lookup(const char *name) +{ + struct scsi_device_handler *dh; + + dh = __scsi_dh_lookup(name); + if (!dh) { + request_module("scsi_dh_%s", name); + dh = __scsi_dh_lookup(name); + } + + return dh; +} + +/* + * scsi_dh_handler_attach - Attach a device handler to a device + * @sdev - SCSI device the device handler should attach to + * @scsi_dh - The device handler to attach + */ +static int scsi_dh_handler_attach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) +{ + int error; + + if (!try_module_get(scsi_dh->module)) + return -EINVAL; + + error = scsi_dh->attach(sdev); + if (error) { + sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n", + scsi_dh->name, error); + module_put(scsi_dh->module); + } else + sdev->handler = scsi_dh; + + return error; +} + +/* + * scsi_dh_handler_detach - Detach a device handler from a device + * @sdev - SCSI device the device handler should be detached from + */ +static void scsi_dh_handler_detach(struct scsi_device *sdev) +{ + sdev->handler->detach(sdev); + sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name); + module_put(sdev->handler->module); +} + +/* + * Functions for sysfs attribute 'dh_state' + */ +static ssize_t +store_dh_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct scsi_device_handler *scsi_dh; + int err = -EINVAL; + + if (sdev->sdev_state == SDEV_CANCEL || + sdev->sdev_state == SDEV_DEL) + return -ENODEV; + + if (!sdev->handler) { + /* + * Attach to a device handler + */ + scsi_dh = scsi_dh_lookup(buf); + if (!scsi_dh) + return err; + err = scsi_dh_handler_attach(sdev, scsi_dh); + } else { + if (!strncmp(buf, "detach", 6)) { + /* + * Detach from a device handler + */ + sdev_printk(KERN_WARNING, sdev, + "can't detach handler %s.\n", + sdev->handler->name); + err = -EINVAL; + } else if (!strncmp(buf, "activate", 8)) { + /* + * Activate a device handler + */ + if (sdev->handler->activate) + err = sdev->handler->activate(sdev, NULL, NULL); + else + err = 0; + } + } + + return err<0?err:count; +} + +static ssize_t +show_dh_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + + if (!sdev->handler) + return snprintf(buf, 20, "detached\n"); + + return snprintf(buf, 20, "%s\n", sdev->handler->name); +} + +static struct device_attribute scsi_dh_state_attr = + __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state, + store_dh_state); + +int scsi_dh_add_device(struct scsi_device *sdev) +{ + struct scsi_device_handler *devinfo = NULL; + const char *drv; + int err; + + err = device_create_file(&sdev->sdev_gendev, &scsi_dh_state_attr); + if (err) + return err; + + drv = scsi_dh_find_driver(sdev); + if (drv) + devinfo = __scsi_dh_lookup(drv); + if (devinfo) + err = scsi_dh_handler_attach(sdev, devinfo); + return err; +} + +void scsi_dh_release_device(struct scsi_device *sdev) +{ + if (sdev->handler) + scsi_dh_handler_detach(sdev); +} + +void scsi_dh_remove_device(struct scsi_device *sdev) +{ + device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr); +} + +/* + * scsi_register_device_handler - register a device handler personality + * module. + * @scsi_dh - device handler to be registered. + * + * Returns 0 on success, -EBUSY if handler already registered. + */ +int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) +{ + if (__scsi_dh_lookup(scsi_dh->name)) + return -EBUSY; + + if (!scsi_dh->attach || !scsi_dh->detach) + return -EINVAL; + + spin_lock(&list_lock); + list_add(&scsi_dh->list, &scsi_dh_list); + spin_unlock(&list_lock); + + printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); + + return SCSI_DH_OK; +} +EXPORT_SYMBOL_GPL(scsi_register_device_handler); + +/* + * scsi_unregister_device_handler - register a device handler personality + * module. + * @scsi_dh - device handler to be unregistered. + * + * Returns 0 on success, -ENODEV if handler not registered. + */ +int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) +{ + if (!__scsi_dh_lookup(scsi_dh->name)) + return -ENODEV; + + spin_lock(&list_lock); + list_del(&scsi_dh->list); + spin_unlock(&list_lock); + printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); + + return SCSI_DH_OK; +} +EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); + +static struct scsi_device *get_sdev_from_queue(struct request_queue *q) +{ + struct scsi_device *sdev; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + return sdev; +} + +/* + * scsi_dh_activate - activate the path associated with the scsi_device + * corresponding to the given request queue. + * Returns immediately without waiting for activation to be completed. + * @q - Request queue that is associated with the scsi_device to be + * activated. + * @fn - Function to be called upon completion of the activation. + * Function fn is called with data (below) and the error code. + * Function fn may be called from the same calling context. So, + * do not hold the lock in the caller which may be needed in fn. + * @data - data passed to the function fn upon completion. + * + */ +int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) +{ + struct scsi_device *sdev; + int err = SCSI_DH_NOSYS; + + sdev = get_sdev_from_queue(q); + if (!sdev) { + if (fn) + fn(data, err); + return err; + } + + if (!sdev->handler) + goto out_fn; + err = SCSI_DH_NOTCONN; + if (sdev->sdev_state == SDEV_CANCEL || + sdev->sdev_state == SDEV_DEL) + goto out_fn; + + err = SCSI_DH_DEV_OFFLINED; + if (sdev->sdev_state == SDEV_OFFLINE) + goto out_fn; + + if (sdev->handler->activate) + err = sdev->handler->activate(sdev, fn, data); + +out_put_device: + put_device(&sdev->sdev_gendev); + return err; + +out_fn: + if (fn) + fn(data, err); + goto out_put_device; +} +EXPORT_SYMBOL_GPL(scsi_dh_activate); + +/* + * scsi_dh_set_params - set the parameters for the device as per the + * string specified in params. + * @q - Request queue that is associated with the scsi_device for + * which the parameters to be set. + * @params - parameters in the following format + * "no_of_params\0param1\0param2\0param3\0...\0" + * for example, string for 2 parameters with value 10 and 21 + * is specified as "2\010\021\0". + */ +int scsi_dh_set_params(struct request_queue *q, const char *params) +{ + struct scsi_device *sdev; + int err = -SCSI_DH_NOSYS; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return err; + + if (sdev->handler && sdev->handler->set_params) + err = sdev->handler->set_params(sdev, params); + put_device(&sdev->sdev_gendev); + return err; +} +EXPORT_SYMBOL_GPL(scsi_dh_set_params); + +/* + * scsi_dh_attach - Attach device handler + * @q - Request queue that is associated with the scsi_device + * the handler should be attached to + * @name - name of the handler to attach + */ +int scsi_dh_attach(struct request_queue *q, const char *name) +{ + struct scsi_device *sdev; + struct scsi_device_handler *scsi_dh; + int err = 0; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return -ENODEV; + + scsi_dh = scsi_dh_lookup(name); + if (!scsi_dh) { + err = -EINVAL; + goto out_put_device; + } + + if (sdev->handler) { + if (sdev->handler != scsi_dh) + err = -EBUSY; + goto out_put_device; + } + + err = scsi_dh_handler_attach(sdev, scsi_dh); + +out_put_device: + put_device(&sdev->sdev_gendev); + return err; +} +EXPORT_SYMBOL_GPL(scsi_dh_attach); + +/* + * scsi_dh_attached_handler_name - Get attached device handler's name + * @q - Request queue that is associated with the scsi_device + * that may have a device handler attached + * @gfp - the GFP mask used in the kmalloc() call when allocating memory + * + * Returns name of attached handler, NULL if no handler is attached. + * Caller must take care to free the returned string. + */ +const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) +{ + struct scsi_device *sdev; + const char *handler_name = NULL; + + sdev = get_sdev_from_queue(q); + if (!sdev) + return NULL; + + if (sdev->handler) + handler_name = kstrdup(sdev->handler->name, gfp); + put_device(&sdev->sdev_gendev); + return handler_name; +} +EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); diff --git a/kernel/drivers/scsi/scsi_error.c b/kernel/drivers/scsi/scsi_error.c index ce6c770d7..984ddcb47 100644 --- a/kernel/drivers/scsi/scsi_error.c +++ b/kernel/drivers/scsi/scsi_error.c @@ -33,9 +33,11 @@ #include #include #include +#include #include #include #include +#include #include #include "scsi_priv.h" @@ -420,6 +422,10 @@ static void scsi_report_sense(struct scsi_device *sdev, evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED; sdev_printk(KERN_WARNING, sdev, "Mode parameters changed"); + } else if (sshdr->asc == 0x2a && sshdr->ascq == 0x06) { + evt_type = SDEV_EVT_ALUA_STATE_CHANGE_REPORTED; + sdev_printk(KERN_WARNING, sdev, + "Asymmetric access state changed"); } else if (sshdr->asc == 0x2a && sshdr->ascq == 0x09) { evt_type = SDEV_EVT_CAPACITY_CHANGE_REPORTED; sdev_printk(KERN_WARNING, sdev, @@ -459,11 +465,10 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scsi_sense_is_deferred(&sshdr)) return NEEDS_RETRY; - if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && - sdev->scsi_dh_data->scsi_dh->check_sense) { + if (sdev->handler && sdev->handler->check_sense) { int rc; - rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr); + rc = sdev->handler->check_sense(sdev, &sshdr); if (rc != SCSI_RETURN_NOT_HANDLED) return rc; /* handler does not care. Drop down to default handling */ @@ -1155,8 +1160,13 @@ int scsi_eh_get_sense(struct list_head *work_q, struct Scsi_Host *shost; int rtn; + /* + * If SCSI_EH_ABORT_SCHEDULED has been set, it is timeout IO, + * should not get sense. + */ list_for_each_entry_safe(scmd, next, work_q, eh_entry) { if ((scmd->eh_eflags & SCSI_EH_CANCEL_CMD) || + (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) || SCSI_SENSE_VALID(scmd)) continue; @@ -1960,7 +1970,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) struct request *req; /* - * blk_get_request with GFP_KERNEL (__GFP_WAIT) sleeps until a + * blk_get_request with GFP_KERNEL (__GFP_RECLAIM) sleeps until a * request becomes available */ req = blk_get_request(sdev->request_queue, READ, GFP_KERNEL); @@ -2169,8 +2179,17 @@ int scsi_error_handler(void *data) * We never actually get interrupted because kthread_run * disables signal delivery for the created thread. */ - while (!kthread_should_stop()) { + while (true) { + /* + * The sequence in kthread_stop() sets the stop flag first + * then wakes the process. To avoid missed wakeups, the task + * should always be in a non running state before the stop + * flag is checked + */ set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || shost->host_failed != atomic_read(&shost->host_busy)) { SCSI_LOG_ERROR_RECOVERY(1, @@ -2398,70 +2417,6 @@ out_put_autopm_host: } EXPORT_SYMBOL(scsi_ioctl_reset); -/** - * scsi_normalize_sense - normalize main elements from either fixed or - * descriptor sense data format into a common format. - * - * @sense_buffer: byte array containing sense data returned by device - * @sb_len: number of valid bytes in sense_buffer - * @sshdr: pointer to instance of structure that common - * elements are written to. - * - * Notes: - * The "main elements" from sense data are: response_code, sense_key, - * asc, ascq and additional_length (only for descriptor format). - * - * Typically this function can be called after a device has - * responded to a SCSI command with the CHECK_CONDITION status. - * - * Return value: - * true if valid sense data information found, else false; - */ -bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, - struct scsi_sense_hdr *sshdr) -{ - if (!sense_buffer || !sb_len) - return false; - - memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); - - sshdr->response_code = (sense_buffer[0] & 0x7f); - - if (!scsi_sense_valid(sshdr)) - return false; - - if (sshdr->response_code >= 0x72) { - /* - * descriptor format - */ - if (sb_len > 1) - sshdr->sense_key = (sense_buffer[1] & 0xf); - if (sb_len > 2) - sshdr->asc = sense_buffer[2]; - if (sb_len > 3) - sshdr->ascq = sense_buffer[3]; - if (sb_len > 7) - sshdr->additional_length = sense_buffer[7]; - } else { - /* - * fixed format - */ - if (sb_len > 2) - sshdr->sense_key = (sense_buffer[2] & 0xf); - if (sb_len > 7) { - sb_len = (sb_len < (sense_buffer[7] + 8)) ? - sb_len : (sense_buffer[7] + 8); - if (sb_len > 12) - sshdr->asc = sense_buffer[12]; - if (sb_len > 13) - sshdr->ascq = sense_buffer[13]; - } - } - - return true; -} -EXPORT_SYMBOL(scsi_normalize_sense); - bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd, struct scsi_sense_hdr *sshdr) { @@ -2470,45 +2425,6 @@ bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd, } EXPORT_SYMBOL(scsi_command_normalize_sense); -/** - * scsi_sense_desc_find - search for a given descriptor type in descriptor sense data format. - * @sense_buffer: byte array of descriptor format sense data - * @sb_len: number of valid bytes in sense_buffer - * @desc_type: value of descriptor type to find - * (e.g. 0 -> information) - * - * Notes: - * only valid when sense data is in descriptor format - * - * Return value: - * pointer to start of (first) descriptor if found else NULL - */ -const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, - int desc_type) -{ - int add_sen_len, add_len, desc_len, k; - const u8 * descp; - - if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7]))) - return NULL; - if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73)) - return NULL; - add_sen_len = (add_sen_len < (sb_len - 8)) ? - add_sen_len : (sb_len - 8); - descp = &sense_buffer[8]; - for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) { - descp += desc_len; - add_len = (k < (add_sen_len - 1)) ? descp[1]: -1; - desc_len = add_len + 2; - if (descp[0] == desc_type) - return descp; - if (add_len < 0) // short descriptor ?? - break; - } - return NULL; -} -EXPORT_SYMBOL(scsi_sense_desc_find); - /** * scsi_get_sense_info_fld - get information field from sense data (either fixed or descriptor format) * @sense_buffer: byte array of sense data @@ -2558,31 +2474,3 @@ int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len, } } EXPORT_SYMBOL(scsi_get_sense_info_fld); - -/** - * scsi_build_sense_buffer - build sense data in a buffer - * @desc: Sense format (non zero == descriptor format, - * 0 == fixed format) - * @buf: Where to build sense data - * @key: Sense key - * @asc: Additional sense code - * @ascq: Additional sense code qualifier - * - **/ -void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) -{ - if (desc) { - buf[0] = 0x72; /* descriptor, current */ - buf[1] = key; - buf[2] = asc; - buf[3] = ascq; - buf[7] = 0; - } else { - buf[0] = 0x70; /* fixed, current */ - buf[2] = key; - buf[7] = 0xa; - buf[12] = asc; - buf[13] = ascq; - } -} -EXPORT_SYMBOL(scsi_build_sense_buffer); diff --git a/kernel/drivers/scsi/scsi_lib.c b/kernel/drivers/scsi/scsi_lib.c index 448ebdaa3..dd8ad2a44 100644 --- a/kernel/drivers/scsi/scsi_lib.c +++ b/kernel/drivers/scsi/scsi_lib.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -221,13 +222,13 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int write = (data_direction == DMA_TO_DEVICE); int ret = DRIVER_ERROR << 24; - req = blk_get_request(sdev->request_queue, write, __GFP_WAIT); + req = blk_get_request(sdev->request_queue, write, __GFP_RECLAIM); if (IS_ERR(req)) return ret; blk_rq_set_block_pc(req); if (bufflen && blk_rq_map_kern(sdev->request_queue, req, - buffer, bufflen, __GFP_WAIT)) + buffer, bufflen, __GFP_RECLAIM)) goto out; req->cmd_len = COMMAND_SIZE(cmd[0]); @@ -1248,9 +1249,8 @@ static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) { struct scsi_cmnd *cmd = req->special; - if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh - && sdev->scsi_dh_data->scsi_dh->prep_fn)) { - int ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req); + if (unlikely(sdev->handler && sdev->handler->prep_fn)) { + int ret = sdev->handler->prep_fn(sdev, req); if (ret != BLKPREP_OK) return ret; } @@ -1957,7 +1957,7 @@ static int scsi_mq_prep_fn(struct request *req) static void scsi_mq_done(struct scsi_cmnd *cmd) { trace_scsi_dispatch_cmd_done(cmd); - blk_mq_complete_request(cmd->request); + blk_mq_complete_request(cmd->request, cmd->request->errors); } static int scsi_queue_rq(struct blk_mq_hw_ctx *hctx, @@ -2423,7 +2423,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, unsigned char cmd[12]; int use_10_for_ms; int header_length; - int result; + int result, retry_count = retries; struct scsi_sense_hdr my_sshdr; memset(data, 0, sizeof(*data)); @@ -2502,6 +2502,11 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, data->block_descriptor_length = buffer[3]; } data->header_length = header_length; + } else if ((status_byte(result) == CHECK_CONDITION) && + scsi_sense_valid(sshdr) && + sshdr->sense_key == UNIT_ATTENTION && retry_count) { + retry_count--; + goto retry; } return result; @@ -2707,6 +2712,9 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) case SDEV_EVT_LUN_CHANGE_REPORTED: envp[idx++] = "SDEV_UA=REPORTED_LUNS_DATA_HAS_CHANGED"; break; + case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: + envp[idx++] = "SDEV_UA=ASYMMETRIC_ACCESS_STATE_CHANGED"; + break; default: /* do nothing */ break; @@ -2810,6 +2818,7 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED: case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: case SDEV_EVT_LUN_CHANGE_REPORTED: + case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: default: /* do nothing */ break; diff --git a/kernel/drivers/scsi/scsi_pm.c b/kernel/drivers/scsi/scsi_pm.c index e4b799837..459abe1dc 100644 --- a/kernel/drivers/scsi/scsi_pm.c +++ b/kernel/drivers/scsi/scsi_pm.c @@ -219,13 +219,13 @@ static int sdev_runtime_suspend(struct device *dev) struct scsi_device *sdev = to_scsi_device(dev); int err = 0; - if (pm && pm->runtime_suspend) { - err = blk_pre_runtime_suspend(sdev->request_queue); - if (err) - return err; + err = blk_pre_runtime_suspend(sdev->request_queue); + if (err) + return err; + if (pm && pm->runtime_suspend) err = pm->runtime_suspend(dev); - blk_post_runtime_suspend(sdev->request_queue, err); - } + blk_post_runtime_suspend(sdev->request_queue, err); + return err; } @@ -248,11 +248,11 @@ static int sdev_runtime_resume(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int err = 0; - if (pm && pm->runtime_resume) { - blk_pre_runtime_resume(sdev->request_queue); + blk_pre_runtime_resume(sdev->request_queue); + if (pm && pm->runtime_resume) err = pm->runtime_resume(dev); - blk_post_runtime_resume(sdev->request_queue, err); - } + blk_post_runtime_resume(sdev->request_queue, err); + return err; } diff --git a/kernel/drivers/scsi/scsi_priv.h b/kernel/drivers/scsi/scsi_priv.h index e3902fc66..4d01cdb1b 100644 --- a/kernel/drivers/scsi/scsi_priv.h +++ b/kernel/drivers/scsi/scsi_priv.h @@ -170,6 +170,17 @@ static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} extern struct async_domain scsi_sd_pm_domain; extern struct async_domain scsi_sd_probe_domain; +/* scsi_dh.c */ +#ifdef CONFIG_SCSI_DH +int scsi_dh_add_device(struct scsi_device *sdev); +void scsi_dh_release_device(struct scsi_device *sdev); +void scsi_dh_remove_device(struct scsi_device *sdev); +#else +static inline int scsi_dh_add_device(struct scsi_device *sdev) { return 0; } +static inline void scsi_dh_release_device(struct scsi_device *sdev) { } +static inline void scsi_dh_remove_device(struct scsi_device *sdev) { } +#endif + /* * internal scsi timeout functions: for use by mid-layer and transport * classes. diff --git a/kernel/drivers/scsi/scsi_scan.c b/kernel/drivers/scsi/scsi_scan.c index 6efab1c45..054923e33 100644 --- a/kernel/drivers/scsi/scsi_scan.c +++ b/kernel/drivers/scsi/scsi_scan.c @@ -55,6 +55,7 @@ * Default timeout */ #define SCSI_TIMEOUT (2*HZ) +#define SCSI_REPORT_LUNS_TIMEOUT (30*HZ) /* * Prefix values for the SCSI id's (stored in sysfs name field) @@ -274,13 +275,13 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, WARN_ON_ONCE(!blk_get_queue(sdev->request_queue)); sdev->request_queue->queuedata = sdev; - if (!shost_use_blk_mq(sdev->host) && - (shost->bqt || shost->hostt->use_blk_tags)) { + if (!shost_use_blk_mq(sdev->host)) { blk_queue_init_tags(sdev->request_queue, sdev->host->cmd_per_lun, shost->bqt, shost->hostt->tag_alloc_policy); } - scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun); + scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun ? + sdev->host->cmd_per_lun : 1); scsi_sysfs_device_initialize(sdev); @@ -700,9 +701,12 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, * strings. */ if (sdev->inquiry_len < 36) { - sdev_printk(KERN_INFO, sdev, - "scsi scan: INQUIRY result too short (%d)," - " using 36\n", sdev->inquiry_len); + if (!sdev->host->short_inquiry) { + shost_printk(KERN_INFO, sdev->host, + "scsi scan: INQUIRY result too short (%d)," + " using 36\n", sdev->inquiry_len); + sdev->host->short_inquiry = 1; + } sdev->inquiry_len = 36; } @@ -1268,68 +1272,6 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget, return; } -/** - * scsilun_to_int - convert a scsi_lun to an int - * @scsilun: struct scsi_lun to be converted. - * - * Description: - * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered - * integer, and return the result. The caller must check for - * truncation before using this function. - * - * Notes: - * For a description of the LUN format, post SCSI-3 see the SCSI - * Architecture Model, for SCSI-3 see the SCSI Controller Commands. - * - * Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function - * returns the integer: 0x0b03d204 - * - * This encoding will return a standard integer LUN for LUNs smaller - * than 256, which typically use a single level LUN structure with - * addressing method 0. - **/ -u64 scsilun_to_int(struct scsi_lun *scsilun) -{ - int i; - u64 lun; - - lun = 0; - for (i = 0; i < sizeof(lun); i += 2) - lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) | - ((u64)scsilun->scsi_lun[i + 1] << (i * 8))); - return lun; -} -EXPORT_SYMBOL(scsilun_to_int); - -/** - * int_to_scsilun - reverts an int into a scsi_lun - * @lun: integer to be reverted - * @scsilun: struct scsi_lun to be set. - * - * Description: - * Reverts the functionality of the scsilun_to_int, which packed - * an 8-byte lun value into an int. This routine unpacks the int - * back into the lun value. - * - * Notes: - * Given an integer : 0x0b03d204, this function returns a - * struct scsi_lun of: d2 04 0b 03 00 00 00 00 - * - **/ -void int_to_scsilun(u64 lun, struct scsi_lun *scsilun) -{ - int i; - - memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); - - for (i = 0; i < sizeof(lun); i += 2) { - scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; - scsilun->scsi_lun[i+1] = lun & 0xFF; - lun = lun >> 16; - } -} -EXPORT_SYMBOL(int_to_scsilun); - /** * scsi_report_lun_scan - Scan using SCSI REPORT LUN results * @starget: which target @@ -1444,7 +1386,7 @@ retry: result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, lun_data, length, &sshdr, - SCSI_TIMEOUT + 4 * HZ, 3, NULL); + SCSI_REPORT_LUNS_TIMEOUT, 3, NULL); SCSI_LOG_SCAN_BUS(3, sdev_printk (KERN_INFO, sdev, "scsi scan: REPORT LUNS" @@ -1773,8 +1715,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) return NULL; if (shost->async_scan) { - shost_printk(KERN_INFO, shost, "%s called twice\n", __func__); - dump_stack(); + shost_printk(KERN_DEBUG, shost, "%s called twice\n", __func__); return NULL; } diff --git a/kernel/drivers/scsi/scsi_sysfs.c b/kernel/drivers/scsi/scsi_sysfs.c index 9ad41168d..c8115b4fe 100644 --- a/kernel/drivers/scsi/scsi_sysfs.c +++ b/kernel/drivers/scsi/scsi_sysfs.c @@ -399,6 +399,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) sdev = container_of(work, struct scsi_device, ew.work); + scsi_dh_release_device(sdev); + parent = sdev->sdev_gendev.parent; spin_lock_irqsave(sdev->host->host_lock, flags); @@ -773,6 +775,29 @@ static struct bin_attribute dev_attr_vpd_##_page = { \ sdev_vpd_pg_attr(pg83); sdev_vpd_pg_attr(pg80); +static ssize_t show_inquiry(struct file *filep, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct scsi_device *sdev = to_scsi_device(dev); + + if (!sdev->inquiry) + return -EINVAL; + + return memory_read_from_buffer(buf, count, &off, sdev->inquiry, + sdev->inquiry_len); +} + +static struct bin_attribute dev_attr_inquiry = { + .attr = { + .name = "inquiry", + .mode = S_IRUGO, + }, + .size = 0, + .read = show_inquiry, +}; + static ssize_t show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf) @@ -898,7 +923,7 @@ sdev_store_queue_ramp_up_period(struct device *dev, return -EINVAL; sdev->queue_ramp_up_period = msecs_to_jiffies(period); - return period; + return count; } static DEVICE_ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR, @@ -957,6 +982,7 @@ static struct attribute *scsi_sdev_attrs[] = { static struct bin_attribute *scsi_sdev_bin_attrs[] = { &dev_attr_vpd_pg83, &dev_attr_vpd_pg80, + &dev_attr_inquiry, NULL }; static struct attribute_group scsi_sdev_attr_group = { @@ -1030,11 +1056,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) "failed to add device: %d\n", error); return error; } + + error = scsi_dh_add_device(sdev); + if (error) { + sdev_printk(KERN_INFO, sdev, + "failed to add device handler: %d\n", error); + return error; + } + device_enable_async_suspend(&sdev->sdev_dev); error = device_add(&sdev->sdev_dev); if (error) { sdev_printk(KERN_INFO, sdev, "failed to add class device: %d\n", error); + scsi_dh_remove_device(sdev); device_del(&sdev->sdev_gendev); return error; } @@ -1067,6 +1102,14 @@ void __scsi_remove_device(struct scsi_device *sdev) { struct device *dev = &sdev->sdev_gendev; + /* + * This cleanup path is not reentrant and while it is impossible + * to get a new reference with scsi_device_get() someone can still + * hold a previously acquired one. + */ + if (sdev->sdev_state == SDEV_DEL) + return; + if (sdev->is_visible) { if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) return; @@ -1074,6 +1117,7 @@ void __scsi_remove_device(struct scsi_device *sdev) bsg_unregister_queue(sdev->request_queue); device_unregister(&sdev->sdev_dev); transport_remove_device(dev); + scsi_dh_remove_device(sdev); device_del(dev); } else put_device(&sdev->sdev_dev); @@ -1148,31 +1192,25 @@ static void __scsi_remove_target(struct scsi_target *starget) void scsi_remove_target(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev->parent); - struct scsi_target *starget, *last = NULL; + struct scsi_target *starget, *last_target = NULL; unsigned long flags; - /* remove targets being careful to lookup next entry before - * deleting the last - */ +restart: spin_lock_irqsave(shost->host_lock, flags); list_for_each_entry(starget, &shost->__targets, siblings) { - if (starget->state == STARGET_DEL) + if (starget->state == STARGET_DEL || + starget == last_target) continue; if (starget->dev.parent == dev || &starget->dev == dev) { - /* assuming new targets arrive at the end */ kref_get(&starget->reap_ref); + last_target = starget; spin_unlock_irqrestore(shost->host_lock, flags); - if (last) - scsi_target_reap(last); - last = starget; __scsi_remove_target(starget); - spin_lock_irqsave(shost->host_lock, flags); + scsi_target_reap(starget); + goto restart; } } spin_unlock_irqrestore(shost->host_lock, flags); - - if (last) - scsi_target_reap(last); } EXPORT_SYMBOL(scsi_remove_target); diff --git a/kernel/drivers/scsi/scsi_transport_iscsi.c b/kernel/drivers/scsi/scsi_transport_iscsi.c index 67d43e356..e4b3d8f4f 100644 --- a/kernel/drivers/scsi/scsi_transport_iscsi.c +++ b/kernel/drivers/scsi/scsi_transport_iscsi.c @@ -204,6 +204,8 @@ iscsi_create_endpoint(int dd_size) iscsi_match_epid); if (!dev) break; + else + put_device(dev); } if (id == ISCSI_MAX_EPID) { printk(KERN_ERR "Too many connections. Max supported %u\n", @@ -2040,6 +2042,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, session->transport = transport; session->creator = -1; session->recovery_tmo = 120; + session->recovery_tmo_sysfs_override = false; session->state = ISCSI_SESSION_FREE; INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); INIT_LIST_HEAD(&session->sess_list); @@ -2784,7 +2787,8 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) switch (ev->u.set_param.param) { case ISCSI_PARAM_SESS_RECOVERY_TMO: sscanf(data, "%d", &value); - session->recovery_tmo = value; + if (!session->recovery_tmo_sysfs_override) + session->recovery_tmo = value; break; default: err = transport->set_param(conn, ev->u.set_param.param, @@ -3035,7 +3039,7 @@ iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh) shost = scsi_host_lookup(ev->u.get_chap.host_no); if (!shost) { - printk(KERN_ERR "%s: failed. Cound not find host no %u\n", + printk(KERN_ERR "%s: failed. Could not find host no %u\n", __func__, ev->u.get_chap.host_no); return -ENODEV; } @@ -4047,13 +4051,15 @@ store_priv_session_##field(struct device *dev, \ if ((session->state == ISCSI_SESSION_FREE) || \ (session->state == ISCSI_SESSION_FAILED)) \ return -EBUSY; \ - if (strncmp(buf, "off", 3) == 0) \ + if (strncmp(buf, "off", 3) == 0) { \ session->field = -1; \ - else { \ + session->field##_sysfs_override = true; \ + } else { \ val = simple_strtoul(buf, &cp, 0); \ if (*cp != '\0' && *cp != '\n') \ return -EINVAL; \ session->field = val; \ + session->field##_sysfs_override = true; \ } \ return count; \ } @@ -4064,6 +4070,7 @@ store_priv_session_##field(struct device *dev, \ static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ show_priv_session_##field, \ store_priv_session_##field) + iscsi_priv_session_rw_attr(recovery_tmo, "%d"); static struct attribute *iscsi_session_attrs[] = { diff --git a/kernel/drivers/scsi/scsi_transport_sas.c b/kernel/drivers/scsi/scsi_transport_sas.c index 9a058194b..30d26e345 100644 --- a/kernel/drivers/scsi/scsi_transport_sas.c +++ b/kernel/drivers/scsi/scsi_transport_sas.c @@ -1222,13 +1222,6 @@ show_sas_rphy_enclosure_identifier(struct device *dev, u64 identifier; int error; - /* - * Only devices behind an expander are supported, because the - * enclosure identifier is a SMP feature. - */ - if (scsi_is_sas_phy_local(phy)) - return -EINVAL; - error = i->f->get_enclosure_identifier(rphy, &identifier); if (error) return error; @@ -1248,9 +1241,6 @@ show_sas_rphy_bay_identifier(struct device *dev, struct sas_internal *i = to_sas_internal(shost->transportt); int val; - if (scsi_is_sas_phy_local(phy)) - return -EINVAL; - val = i->f->get_bay_identifier(rphy); if (val < 0) return val; diff --git a/kernel/drivers/scsi/scsi_transport_spi.c b/kernel/drivers/scsi/scsi_transport_spi.c index 31bbb0da3..319868f3f 100644 --- a/kernel/drivers/scsi/scsi_transport_spi.c +++ b/kernel/drivers/scsi/scsi_transport_spi.c @@ -786,10 +786,10 @@ spi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr, * IU, then QAS (if we can control them), then finally * fall down the periods */ if (i->f->set_iu && spi_iu(starget)) { - starget_printk(KERN_ERR, starget, "Domain Validation Disabing Information Units\n"); + starget_printk(KERN_ERR, starget, "Domain Validation Disabling Information Units\n"); DV_SET(iu, 0); } else if (i->f->set_qas && spi_qas(starget)) { - starget_printk(KERN_ERR, starget, "Domain Validation Disabing Quick Arbitration and Selection\n"); + starget_printk(KERN_ERR, starget, "Domain Validation Disabling Quick Arbitration and Selection\n"); DV_SET(qas, 0); } else { newperiod = spi_period(starget); diff --git a/kernel/drivers/scsi/scsi_transport_srp.c b/kernel/drivers/scsi/scsi_transport_srp.c index f115f67a6..e3cd3ece4 100644 --- a/kernel/drivers/scsi/scsi_transport_srp.c +++ b/kernel/drivers/scsi/scsi_transport_srp.c @@ -61,6 +61,11 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r) return dev_to_shost(r->dev.parent); } +static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost) +{ + return transport_class_to_srp_rport(&shost->shost_gendev); +} + /** * srp_tmo_valid() - check timeout combination validity * @reconnect_delay: Reconnect delay in seconds. @@ -198,7 +203,7 @@ static ssize_t srp_show_tmo(char *buf, int tmo) return tmo >= 0 ? sprintf(buf, "%d\n", tmo) : sprintf(buf, "off\n"); } -static int srp_parse_tmo(int *tmo, const char *buf) +int srp_parse_tmo(int *tmo, const char *buf) { int res = 0; @@ -209,6 +214,7 @@ static int srp_parse_tmo(int *tmo, const char *buf) return res; } +EXPORT_SYMBOL(srp_parse_tmo); static ssize_t show_reconnect_delay(struct device *dev, struct device_attribute *attr, char *buf) @@ -628,9 +634,11 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) struct scsi_device *sdev = scmd->device; struct Scsi_Host *shost = sdev->host; struct srp_internal *i = to_srp_internal(shost->transportt); + struct srp_rport *rport = shost_to_rport(shost); pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev)); - return i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? + return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 && + i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; } diff --git a/kernel/drivers/scsi/sd.c b/kernel/drivers/scsi/sd.c index 11ea52b2c..bb669d32c 100644 --- a/kernel/drivers/scsi/sd.c +++ b/kernel/drivers/scsi/sd.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -204,6 +205,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr, buffer_data[2] &= ~0x05; buffer_data[2] |= wce << 2 | rcd; sp = buffer_data[0] & 0x80 ? 1 : 0; + buffer_data[0] &= ~0x80; if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT, SD_MAX_RETRIES, &data, &sshdr)) { @@ -636,18 +638,31 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) unsigned int max_blocks = 0; q->limits.discard_zeroes_data = 0; - q->limits.discard_alignment = sdkp->unmap_alignment * - logical_block_size; - q->limits.discard_granularity = - max(sdkp->physical_block_size, - sdkp->unmap_granularity * logical_block_size); + + /* + * When LBPRZ is reported, discard alignment and granularity + * must be fixed to the logical block size. Otherwise the block + * layer will drop misaligned portions of the request which can + * lead to data corruption. If LBPRZ is not set, we honor the + * device preference. + */ + if (sdkp->lbprz) { + q->limits.discard_alignment = 0; + q->limits.discard_granularity = 1; + } else { + q->limits.discard_alignment = sdkp->unmap_alignment * + logical_block_size; + q->limits.discard_granularity = + max(sdkp->physical_block_size, + sdkp->unmap_granularity * logical_block_size); + } sdkp->provisioning_mode = mode; switch (mode) { case SD_LBP_DISABLE: - q->limits.max_discard_sectors = 0; + blk_queue_max_discard_sectors(q, 0); queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q); return; @@ -675,7 +690,7 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) break; } - q->limits.max_discard_sectors = max_blocks * (logical_block_size >> 9); + blk_queue_max_discard_sectors(q, max_blocks * (logical_block_size >> 9)); queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); } @@ -1535,6 +1550,100 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, } #endif +static char sd_pr_type(enum pr_type type) +{ + switch (type) { + case PR_WRITE_EXCLUSIVE: + return 0x01; + case PR_EXCLUSIVE_ACCESS: + return 0x03; + case PR_WRITE_EXCLUSIVE_REG_ONLY: + return 0x05; + case PR_EXCLUSIVE_ACCESS_REG_ONLY: + return 0x06; + case PR_WRITE_EXCLUSIVE_ALL_REGS: + return 0x07; + case PR_EXCLUSIVE_ACCESS_ALL_REGS: + return 0x08; + default: + return 0; + } +}; + +static int sd_pr_command(struct block_device *bdev, u8 sa, + u64 key, u64 sa_key, u8 type, u8 flags) +{ + struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; + struct scsi_sense_hdr sshdr; + int result; + u8 cmd[16] = { 0, }; + u8 data[24] = { 0, }; + + cmd[0] = PERSISTENT_RESERVE_OUT; + cmd[1] = sa; + cmd[2] = type; + put_unaligned_be32(sizeof(data), &cmd[5]); + + put_unaligned_be64(key, &data[0]); + put_unaligned_be64(sa_key, &data[8]); + data[20] = flags; + + result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, &data, sizeof(data), + &sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL); + + if ((driver_byte(result) & DRIVER_SENSE) && + (scsi_sense_valid(&sshdr))) { + sdev_printk(KERN_INFO, sdev, "PR command failed: %d\n", result); + scsi_print_sense_hdr(sdev, NULL, &sshdr); + } + + return result; +} + +static int sd_pr_register(struct block_device *bdev, u64 old_key, u64 new_key, + u32 flags) +{ + if (flags & ~PR_FL_IGNORE_KEY) + return -EOPNOTSUPP; + return sd_pr_command(bdev, (flags & PR_FL_IGNORE_KEY) ? 0x06 : 0x00, + old_key, new_key, 0, + (1 << 0) /* APTPL */ | + (1 << 2) /* ALL_TG_PT */); +} + +static int sd_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type, + u32 flags) +{ + if (flags) + return -EOPNOTSUPP; + return sd_pr_command(bdev, 0x01, key, 0, sd_pr_type(type), 0); +} + +static int sd_pr_release(struct block_device *bdev, u64 key, enum pr_type type) +{ + return sd_pr_command(bdev, 0x02, key, 0, sd_pr_type(type), 0); +} + +static int sd_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key, + enum pr_type type, bool abort) +{ + return sd_pr_command(bdev, abort ? 0x05 : 0x04, old_key, new_key, + sd_pr_type(type), 0); +} + +static int sd_pr_clear(struct block_device *bdev, u64 key) +{ + return sd_pr_command(bdev, 0x03, key, 0, 0, 0); +} + +static const struct pr_ops sd_pr_ops = { + .pr_register = sd_pr_register, + .pr_reserve = sd_pr_reserve, + .pr_release = sd_pr_release, + .pr_preempt = sd_pr_preempt, + .pr_clear = sd_pr_clear, +}; + static const struct block_device_operations sd_fops = { .owner = THIS_MODULE, .open = sd_open, @@ -1547,6 +1656,7 @@ static const struct block_device_operations sd_fops = { .check_events = sd_check_events, .revalidate_disk = sd_revalidate_disk, .unlock_native_capacity = sd_unlock_native_capacity, + .pr_ops = &sd_pr_ops, }; /** @@ -2224,11 +2334,8 @@ got_data: } } - if (sdkp->capacity > 0xffffffff) { + if (sdkp->capacity > 0xffffffff) sdp->use_16_for_rw = 1; - sdkp->max_xfer_blocks = SD_MAX_XFER_BLOCKS; - } else - sdkp->max_xfer_blocks = SD_DEF_XFER_BLOCKS; /* Rescale capacity to 512-byte units */ if (sector_size == 4096) @@ -2545,7 +2652,6 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) { unsigned int sector_sz = sdkp->device->sector_size; const int vpd_len = 64; - u32 max_xfer_length; unsigned char *buffer = kmalloc(vpd_len, GFP_KERNEL); if (!buffer || @@ -2553,14 +2659,11 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) scsi_get_vpd_page(sdkp->device, 0xb0, buffer, vpd_len)) goto out; - max_xfer_length = get_unaligned_be32(&buffer[8]); - if (max_xfer_length) - sdkp->max_xfer_blocks = max_xfer_length; - blk_queue_io_min(sdkp->disk->queue, get_unaligned_be16(&buffer[6]) * sector_sz); - blk_queue_io_opt(sdkp->disk->queue, - get_unaligned_be32(&buffer[12]) * sector_sz); + + sdkp->max_xfer_blocks = get_unaligned_be32(&buffer[8]); + sdkp->opt_xfer_blocks = get_unaligned_be32(&buffer[12]); if (buffer[3] == 0x3c) { unsigned int lba_count, desc_count; @@ -2709,6 +2812,11 @@ static int sd_try_extended_inquiry(struct scsi_device *sdp) return 0; } +static inline u32 logical_to_sectors(struct scsi_device *sdev, u32 blocks) +{ + return blocks << (ilog2(sdev->sector_size) - 9); +} + /** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. @@ -2718,8 +2826,9 @@ static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device; + struct request_queue *q = sdkp->disk->queue; unsigned char *buffer; - unsigned int max_xfer; + unsigned int dev_max, rw_max; SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_revalidate_disk\n")); @@ -2767,11 +2876,29 @@ static int sd_revalidate_disk(struct gendisk *disk) */ sd_set_flush_flag(sdkp); - max_xfer = sdkp->max_xfer_blocks; - max_xfer <<= ilog2(sdp->sector_size) - 9; + /* Initial block count limit based on CDB TRANSFER LENGTH field size. */ + dev_max = sdp->use_16_for_rw ? SD_MAX_XFER_BLOCKS : SD_DEF_XFER_BLOCKS; + + /* Some devices report a maximum block count for READ/WRITE requests. */ + dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks); + q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max); - sdkp->disk->queue->limits.max_sectors = - min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), max_xfer); + /* + * Use the device's preferred I/O size for reads and writes + * unless the reported value is unreasonably small, large, or + * garbage. + */ + if (sdkp->opt_xfer_blocks && + sdkp->opt_xfer_blocks <= dev_max && + sdkp->opt_xfer_blocks <= SD_DEF_XFER_BLOCKS && + sdkp->opt_xfer_blocks * sdp->sector_size >= PAGE_CACHE_SIZE) + rw_max = q->limits.io_opt = + sdkp->opt_xfer_blocks * sdp->sector_size; + else + rw_max = BLK_DEF_MAX_SECTORS; + + /* Combine with controller limits */ + q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q)); set_capacity(disk, sdkp->capacity); sd_config_write_same(sdkp); @@ -2988,7 +3115,8 @@ static int sd_probe(struct device *dev) sdkp->dev.class = &sd_disk_class; dev_set_name(&sdkp->dev, "%s", dev_name(dev)); - if (device_add(&sdkp->dev)) + error = device_add(&sdkp->dev); + if (error) goto out_free_index; get_device(dev); @@ -3067,7 +3195,6 @@ static void scsi_disk_release(struct device *dev) ida_remove(&sd_index_ida, sdkp->index); spin_unlock(&sd_index_lock); - blk_integrity_unregister(disk); disk->private_data = NULL; put_disk(disk); put_device(&sdkp->device->sdev_gendev); @@ -3141,8 +3268,8 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) struct scsi_disk *sdkp = dev_get_drvdata(dev); int ret = 0; - if (!sdkp) - return 0; /* this can happen */ + if (!sdkp) /* E.g.: runtime suspend following sd_remove() */ + return 0; if (sdkp->WCE && sdkp->media_present) { sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n"); @@ -3181,6 +3308,9 @@ static int sd_resume(struct device *dev) { struct scsi_disk *sdkp = dev_get_drvdata(dev); + if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */ + return 0; + if (!sdkp->device->manage_start_stop) return 0; diff --git a/kernel/drivers/scsi/sd.h b/kernel/drivers/scsi/sd.h index 63ba5ca7f..5f2a84aff 100644 --- a/kernel/drivers/scsi/sd.h +++ b/kernel/drivers/scsi/sd.h @@ -67,6 +67,7 @@ struct scsi_disk { atomic_t openers; sector_t capacity; /* size in 512-byte sectors */ u32 max_xfer_blocks; + u32 opt_xfer_blocks; u32 max_ws_blocks; u32 max_unmap_blocks; u32 unmap_granularity; diff --git a/kernel/drivers/scsi/sd_dif.c b/kernel/drivers/scsi/sd_dif.c index 5c06d292b..987bf392c 100644 --- a/kernel/drivers/scsi/sd_dif.c +++ b/kernel/drivers/scsi/sd_dif.c @@ -43,6 +43,7 @@ void sd_dif_config_host(struct scsi_disk *sdkp) struct scsi_device *sdp = sdkp->device; struct gendisk *disk = sdkp->disk; u8 type = sdkp->protection_type; + struct blk_integrity bi; int dif, dix; dif = scsi_host_dif_capable(sdp->host, type); @@ -55,39 +56,43 @@ void sd_dif_config_host(struct scsi_disk *sdkp) if (!dix) return; + memset(&bi, 0, sizeof(bi)); + /* Enable DMA of protection information */ if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) { if (type == SD_DIF_TYPE3_PROTECTION) - blk_integrity_register(disk, &t10_pi_type3_ip); + bi.profile = &t10_pi_type3_ip; else - blk_integrity_register(disk, &t10_pi_type1_ip); + bi.profile = &t10_pi_type1_ip; - disk->integrity->flags |= BLK_INTEGRITY_IP_CHECKSUM; + bi.flags |= BLK_INTEGRITY_IP_CHECKSUM; } else if (type == SD_DIF_TYPE3_PROTECTION) - blk_integrity_register(disk, &t10_pi_type3_crc); + bi.profile = &t10_pi_type3_crc; else - blk_integrity_register(disk, &t10_pi_type1_crc); + bi.profile = &t10_pi_type1_crc; + bi.tuple_size = sizeof(struct t10_pi_tuple); sd_printk(KERN_NOTICE, sdkp, - "Enabling DIX %s protection\n", disk->integrity->name); + "Enabling DIX %s protection\n", bi.profile->name); - /* Signal to block layer that we support sector tagging */ if (dif && type) { - - disk->integrity->flags |= BLK_INTEGRITY_DEVICE_CAPABLE; + bi.flags |= BLK_INTEGRITY_DEVICE_CAPABLE; if (!sdkp->ATO) - return; + goto out; if (type == SD_DIF_TYPE3_PROTECTION) - disk->integrity->tag_size = sizeof(u16) + sizeof(u32); + bi.tag_size = sizeof(u16) + sizeof(u32); else - disk->integrity->tag_size = sizeof(u16); + bi.tag_size = sizeof(u16); sd_printk(KERN_NOTICE, sdkp, "DIF application tag size %u\n", - disk->integrity->tag_size); + bi.tag_size); } + +out: + blk_integrity_register(disk, &bi); } /* diff --git a/kernel/drivers/scsi/ses.c b/kernel/drivers/scsi/ses.c index dcb0d76d7..044d06410 100644 --- a/kernel/drivers/scsi/ses.c +++ b/kernel/drivers/scsi/ses.c @@ -84,6 +84,7 @@ static void init_device_slot_control(unsigned char *dest_desc, static int ses_recv_diag(struct scsi_device *sdev, int page_code, void *buf, int bufflen) { + int ret; unsigned char cmd[] = { RECEIVE_DIAGNOSTIC, 1, /* Set PCV bit */ @@ -92,9 +93,26 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code, bufflen & 0xff, 0 }; + unsigned char recv_page_code; - return scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, + ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, NULL, SES_TIMEOUT, SES_RETRIES, NULL); + if (unlikely(!ret)) + return ret; + + recv_page_code = ((unsigned char *)buf)[0]; + + if (likely(recv_page_code == page_code)) + return ret; + + /* successful diagnostic but wrong page code. This happens to some + * USB devices, just print a message and pretend there was an error */ + + sdev_printk(KERN_ERR, sdev, + "Wrong diagnostic page; asked for %d got %u\n", + page_code, recv_page_code); + + return -EINVAL; } static int ses_send_diag(struct scsi_device *sdev, int page_code, @@ -541,7 +559,15 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, if (desc_ptr) desc_ptr += len; - if (addl_desc_ptr) + if (addl_desc_ptr && + /* only find additional descriptions for specific devices */ + (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || + type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE || + type_ptr[0] == ENCLOSURE_COMPONENT_SAS_EXPANDER || + /* these elements are optional */ + type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_TARGET_PORT || + type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT || + type_ptr[0] == ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS)) addl_desc_ptr += addl_desc_ptr[1] + 2; } diff --git a/kernel/drivers/scsi/sg.c b/kernel/drivers/scsi/sg.c index 9d7b7db75..5e8206744 100644 --- a/kernel/drivers/scsi/sg.c +++ b/kernel/drivers/scsi/sg.c @@ -787,8 +787,14 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, return k; /* probably out of space --> ENOMEM */ } if (atomic_read(&sdp->detaching)) { - if (srp->bio) + if (srp->bio) { + if (srp->rq->cmd != srp->rq->__cmd) + kfree(srp->rq->cmd); + blk_end_request_all(srp->rq, -EIO); + srp->rq = NULL; + } + sg_finish_rem_req(srp); return -ENODEV; } @@ -1255,7 +1261,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) } sfp->mmap_called = 1; - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; return 0; diff --git a/kernel/drivers/scsi/snic/Makefile b/kernel/drivers/scsi/snic/Makefile new file mode 100644 index 000000000..ef7c0dd47 --- /dev/null +++ b/kernel/drivers/scsi/snic/Makefile @@ -0,0 +1,17 @@ +obj-$(CONFIG_SCSI_SNIC) += snic.o + +snic-y := \ + snic_attrs.o \ + snic_main.o \ + snic_res.o \ + snic_isr.o \ + snic_ctl.o \ + snic_io.o \ + snic_scsi.o \ + snic_disc.o \ + vnic_cq.o \ + vnic_intr.o \ + vnic_dev.o \ + vnic_wq.o + +snic-$(CONFIG_SCSI_SNIC_DEBUG_FS) += snic_debugfs.o snic_trc.o diff --git a/kernel/drivers/scsi/snic/cq_desc.h b/kernel/drivers/scsi/snic/cq_desc.h new file mode 100644 index 000000000..a5290562c --- /dev/null +++ b/kernel/drivers/scsi/snic/cq_desc.h @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _CQ_DESC_H_ +#define _CQ_DESC_H_ + +/* + * Completion queue descriptor types + */ +enum cq_desc_types { + CQ_DESC_TYPE_WQ_ENET = 0, + CQ_DESC_TYPE_DESC_COPY = 1, + CQ_DESC_TYPE_WQ_EXCH = 2, + CQ_DESC_TYPE_RQ_ENET = 3, + CQ_DESC_TYPE_RQ_FCP = 4, +}; + +/* Completion queue descriptor: 16B + * + * All completion queues have this basic layout. The + * type_specific area is unique for each completion + * queue type. + */ +struct cq_desc { + __le16 completed_index; + __le16 q_number; + u8 type_specific[11]; + u8 type_color; +}; + +#define CQ_DESC_TYPE_BITS 4 +#define CQ_DESC_TYPE_MASK ((1 << CQ_DESC_TYPE_BITS) - 1) +#define CQ_DESC_COLOR_MASK 1 +#define CQ_DESC_COLOR_SHIFT 7 +#define CQ_DESC_Q_NUM_BITS 10 +#define CQ_DESC_Q_NUM_MASK ((1 << CQ_DESC_Q_NUM_BITS) - 1) +#define CQ_DESC_COMP_NDX_BITS 12 +#define CQ_DESC_COMP_NDX_MASK ((1 << CQ_DESC_COMP_NDX_BITS) - 1) + +static inline void cq_desc_dec(const struct cq_desc *desc_arg, + u8 *type, u8 *color, u16 *q_number, u16 *completed_index) +{ + const struct cq_desc *desc = desc_arg; + const u8 type_color = desc->type_color; + + *color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK; + + /* + * Make sure color bit is read from desc *before* other fields + * are read from desc. Hardware guarantees color bit is last + * bit (byte) written. Adding the rmb() prevents the compiler + * and/or CPU from reordering the reads which would potentially + * result in reading stale values. + */ + rmb(); + + *type = type_color & CQ_DESC_TYPE_MASK; + *q_number = le16_to_cpu(desc->q_number) & CQ_DESC_Q_NUM_MASK; + *completed_index = le16_to_cpu(desc->completed_index) & + CQ_DESC_COMP_NDX_MASK; +} + +#endif /* _CQ_DESC_H_ */ diff --git a/kernel/drivers/scsi/snic/cq_enet_desc.h b/kernel/drivers/scsi/snic/cq_enet_desc.h new file mode 100644 index 000000000..0a1be2ed0 --- /dev/null +++ b/kernel/drivers/scsi/snic/cq_enet_desc.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _CQ_ENET_DESC_H_ +#define _CQ_ENET_DESC_H_ + +#include "cq_desc.h" + +/* Ethernet completion queue descriptor: 16B */ +struct cq_enet_wq_desc { + __le16 completed_index; + __le16 q_number; + u8 reserved[11]; + u8 type_color; +}; + +static inline void cq_enet_wq_desc_dec(struct cq_enet_wq_desc *desc, + u8 *type, u8 *color, u16 *q_number, u16 *completed_index) +{ + cq_desc_dec((struct cq_desc *)desc, type, + color, q_number, completed_index); +} + +#endif /* _CQ_ENET_DESC_H_ */ diff --git a/kernel/drivers/scsi/snic/snic.h b/kernel/drivers/scsi/snic/snic.h new file mode 100644 index 000000000..d7f5ba6ba --- /dev/null +++ b/kernel/drivers/scsi/snic/snic.h @@ -0,0 +1,414 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _SNIC_H_ +#define _SNIC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snic_disc.h" +#include "snic_io.h" +#include "snic_res.h" +#include "snic_trc.h" +#include "snic_stats.h" +#include "vnic_dev.h" +#include "vnic_wq.h" +#include "vnic_cq.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "vnic_snic.h" + +#define SNIC_DRV_NAME "snic" +#define SNIC_DRV_DESCRIPTION "Cisco SCSI NIC Driver" +#define SNIC_DRV_VERSION "0.0.1.18" +#define PFX SNIC_DRV_NAME ":" +#define DFX SNIC_DRV_NAME "%d: " + +#define DESC_CLEAN_LOW_WATERMARK 8 +#define SNIC_UCSM_DFLT_THROTTLE_CNT_BLD 16 /* UCSM default throttle count */ +#define SNIC_MAX_IO_REQ 50 /* scsi_cmnd tag map entries */ +#define SNIC_MIN_IO_REQ 8 /* Min IO throttle count */ +#define SNIC_IO_LOCKS 64 /* IO locks: power of 2 */ +#define SNIC_DFLT_QUEUE_DEPTH 32 /* Default Queue Depth */ +#define SNIC_MAX_QUEUE_DEPTH 64 /* Max Queue Depth */ +#define SNIC_DFLT_CMD_TIMEOUT 90 /* Extended tmo for FW */ + +/* + * Tag bits used for special requests. + */ +#define SNIC_TAG_ABORT BIT(30) /* Tag indicating abort */ +#define SNIC_TAG_DEV_RST BIT(29) /* Tag for device reset */ +#define SNIC_TAG_IOCTL_DEV_RST BIT(28) /* Tag for User Device Reset */ +#define SNIC_TAG_MASK (BIT(24) - 1) /* Mask for lookup */ +#define SNIC_NO_TAG -1 + +/* + * Command flags to identify the type of command and for other future use + */ +#define SNIC_NO_FLAGS 0 +#define SNIC_IO_INITIALIZED BIT(0) +#define SNIC_IO_ISSUED BIT(1) +#define SNIC_IO_DONE BIT(2) +#define SNIC_IO_REQ_NULL BIT(3) +#define SNIC_IO_ABTS_PENDING BIT(4) +#define SNIC_IO_ABORTED BIT(5) +#define SNIC_IO_ABTS_ISSUED BIT(6) +#define SNIC_IO_TERM_ISSUED BIT(7) +#define SNIC_IO_ABTS_TIMEDOUT BIT(8) +#define SNIC_IO_ABTS_TERM_DONE BIT(9) +#define SNIC_IO_ABTS_TERM_REQ_NULL BIT(10) +#define SNIC_IO_ABTS_TERM_TIMEDOUT BIT(11) +#define SNIC_IO_INTERNAL_TERM_PENDING BIT(12) +#define SNIC_IO_INTERNAL_TERM_ISSUED BIT(13) +#define SNIC_DEVICE_RESET BIT(14) +#define SNIC_DEV_RST_ISSUED BIT(15) +#define SNIC_DEV_RST_TIMEDOUT BIT(16) +#define SNIC_DEV_RST_ABTS_ISSUED BIT(17) +#define SNIC_DEV_RST_TERM_ISSUED BIT(18) +#define SNIC_DEV_RST_DONE BIT(19) +#define SNIC_DEV_RST_REQ_NULL BIT(20) +#define SNIC_DEV_RST_ABTS_DONE BIT(21) +#define SNIC_DEV_RST_TERM_DONE BIT(22) +#define SNIC_DEV_RST_ABTS_PENDING BIT(23) +#define SNIC_DEV_RST_PENDING BIT(24) +#define SNIC_DEV_RST_NOTSUP BIT(25) +#define SNIC_SCSI_CLEANUP BIT(26) +#define SNIC_HOST_RESET_ISSUED BIT(27) + +#define SNIC_ABTS_TIMEOUT 30000 /* msec */ +#define SNIC_LUN_RESET_TIMEOUT 30000 /* msec */ +#define SNIC_HOST_RESET_TIMEOUT 30000 /* msec */ + + +/* + * These are protected by the hashed req_lock. + */ +#define CMD_SP(Cmnd) \ + (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->rqi) +#define CMD_STATE(Cmnd) \ + (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->state) +#define CMD_ABTS_STATUS(Cmnd) \ + (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->abts_status) +#define CMD_LR_STATUS(Cmnd) \ + (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->lr_status) +#define CMD_FLAGS(Cmnd) \ + (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->flags) + +#define SNIC_INVALID_CODE 0x100 /* Hdr Status val unused by firmware */ + +#define SNIC_MAX_TARGET 256 +#define SNIC_FLAGS_NONE (0) + +/* snic module params */ +extern unsigned int snic_max_qdepth; + +/* snic debugging */ +extern unsigned int snic_log_level; + +#define SNIC_MAIN_LOGGING 0x1 +#define SNIC_SCSI_LOGGING 0x2 +#define SNIC_ISR_LOGGING 0x8 +#define SNIC_DESC_LOGGING 0x10 + +#define SNIC_CHECK_LOGGING(LEVEL, CMD) \ +do { \ + if (unlikely(snic_log_level & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ +} while (0) + +#define SNIC_MAIN_DBG(host, fmt, args...) \ + SNIC_CHECK_LOGGING(SNIC_MAIN_LOGGING, \ + shost_printk(KERN_INFO, host, fmt, ## args);) + +#define SNIC_SCSI_DBG(host, fmt, args...) \ + SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING, \ + shost_printk(KERN_INFO, host, fmt, ##args);) + +#define SNIC_DISC_DBG(host, fmt, args...) \ + SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING, \ + shost_printk(KERN_INFO, host, fmt, ##args);) + +#define SNIC_ISR_DBG(host, fmt, args...) \ + SNIC_CHECK_LOGGING(SNIC_ISR_LOGGING, \ + shost_printk(KERN_INFO, host, fmt, ##args);) + +#define SNIC_HOST_ERR(host, fmt, args...) \ + shost_printk(KERN_ERR, host, fmt, ##args) + +#define SNIC_HOST_INFO(host, fmt, args...) \ + shost_printk(KERN_INFO, host, fmt, ##args) + +#define SNIC_INFO(fmt, args...) \ + pr_info(PFX fmt, ## args) + +#define SNIC_DBG(fmt, args...) \ + pr_info(PFX fmt, ## args) + +#define SNIC_ERR(fmt, args...) \ + pr_err(PFX fmt, ## args) + +#ifdef DEBUG +#define SNIC_BUG_ON(EXPR) \ + ({ \ + if (EXPR) { \ + SNIC_ERR("SNIC BUG(%s)\n", #EXPR); \ + BUG_ON(EXPR); \ + } \ + }) +#else +#define SNIC_BUG_ON(EXPR) \ + ({ \ + if (EXPR) { \ + SNIC_ERR("SNIC BUG(%s) at %s : %d\n", \ + #EXPR, __func__, __LINE__); \ + WARN_ON_ONCE(EXPR); \ + } \ + }) +#endif + +/* Soft assert */ +#define SNIC_ASSERT_NOT_IMPL(EXPR) \ + ({ \ + if (EXPR) {\ + SNIC_INFO("Functionality not impl'ed at %s:%d\n", \ + __func__, __LINE__); \ + WARN_ON_ONCE(EXPR); \ + } \ + }) + + +extern const char *snic_state_str[]; + +enum snic_intx_intr_index { + SNIC_INTX_WQ_RQ_COPYWQ, + SNIC_INTX_ERR, + SNIC_INTX_NOTIFY, + SNIC_INTX_INTR_MAX, +}; + +enum snic_msix_intr_index { + SNIC_MSIX_WQ, + SNIC_MSIX_IO_CMPL, + SNIC_MSIX_ERR_NOTIFY, + SNIC_MSIX_INTR_MAX, +}; + +struct snic_msix_entry { + int requested; + char devname[IFNAMSIZ]; + irqreturn_t (*isr)(int, void *); + void *devid; +}; + +enum snic_state { + SNIC_INIT = 0, + SNIC_ERROR, + SNIC_ONLINE, + SNIC_OFFLINE, + SNIC_FWRESET, +}; + +#define SNIC_WQ_MAX 1 +#define SNIC_CQ_IO_CMPL_MAX 1 +#define SNIC_CQ_MAX (SNIC_WQ_MAX + SNIC_CQ_IO_CMPL_MAX) + +/* firmware version information */ +struct snic_fw_info { + u32 fw_ver; + u32 hid; /* u16 hid | u16 vnic id */ + u32 max_concur_ios; /* max concurrent ios */ + u32 max_sgs_per_cmd; /* max sgls per IO */ + u32 max_io_sz; /* max io size supported */ + u32 hba_cap; /* hba capabilities */ + u32 max_tgts; /* max tgts supported */ + u16 io_tmo; /* FW Extended timeout */ + struct completion *wait; /* protected by snic lock*/ +}; + +/* + * snic_work item : defined to process asynchronous events + */ +struct snic_work { + struct work_struct work; + u16 ev_id; + u64 *ev_data; +}; + +/* + * snic structure to represent SCSI vNIC + */ +struct snic { + /* snic specific members */ + struct list_head list; + char name[IFNAMSIZ]; + atomic_t state; + spinlock_t snic_lock; + struct completion *remove_wait; + bool in_remove; + bool stop_link_events; /* stop processing link events */ + + /* discovery related */ + struct snic_disc disc; + + /* Scsi Host info */ + struct Scsi_Host *shost; + + /* vnic related structures */ + struct vnic_dev_bar bar0; + + struct vnic_stats *stats; + unsigned long stats_time; + unsigned long stats_reset_time; + + struct vnic_dev *vdev; + + /* hw resource info */ + unsigned int wq_count; + unsigned int cq_count; + unsigned int intr_count; + unsigned int err_intr_offset; + + int link_status; /* retrieved from svnic_dev_link_status() */ + u32 link_down_cnt; + + /* pci related */ + struct pci_dev *pdev; + struct msix_entry msix_entry[SNIC_MSIX_INTR_MAX]; + struct snic_msix_entry msix[SNIC_MSIX_INTR_MAX]; + + /* io related info */ + mempool_t *req_pool[SNIC_REQ_MAX_CACHES]; /* (??) */ + ____cacheline_aligned spinlock_t io_req_lock[SNIC_IO_LOCKS]; + + /* Maintain snic specific commands, cmds with no tag in spl_cmd_list */ + ____cacheline_aligned spinlock_t spl_cmd_lock; + struct list_head spl_cmd_list; + + unsigned int max_tag_id; + atomic_t ios_inflight; /* io in flight counter */ + + struct vnic_snic_config config; + + struct work_struct link_work; + + /* firmware information */ + struct snic_fw_info fwinfo; + + /* Work for processing Target related work */ + struct work_struct tgt_work; + + /* Work for processing Discovery */ + struct work_struct disc_work; + + /* stats related */ + unsigned int reset_stats; + atomic64_t io_cmpl_skip; + struct snic_stats s_stats; /* Per SNIC driver stats */ + + /* platform specific */ +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + struct dentry *stats_host; /* Per snic debugfs root */ + struct dentry *stats_file; /* Per snic debugfs file */ + struct dentry *reset_stats_file;/* Per snic reset stats file */ +#endif + + /* completion queue cache line section */ + ____cacheline_aligned struct vnic_cq cq[SNIC_CQ_MAX]; + + /* work queue cache line section */ + ____cacheline_aligned struct vnic_wq wq[SNIC_WQ_MAX]; + spinlock_t wq_lock[SNIC_WQ_MAX]; + + /* interrupt resource cache line section */ + ____cacheline_aligned struct vnic_intr intr[SNIC_MSIX_INTR_MAX]; +}; /* end of snic structure */ + +/* + * SNIC Driver's Global Data + */ +struct snic_global { + struct list_head snic_list; + spinlock_t snic_list_lock; + + struct kmem_cache *req_cache[SNIC_REQ_MAX_CACHES]; + + struct workqueue_struct *event_q; + +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + /* debugfs related global data */ + struct dentry *trc_root; + struct dentry *stats_root; + + struct snic_trc trc ____cacheline_aligned; +#endif +}; + +extern struct snic_global *snic_glob; + +int snic_glob_init(void); +void snic_glob_cleanup(void); + +extern struct workqueue_struct *snic_event_queue; +extern struct device_attribute *snic_attrs[]; + +int snic_queuecommand(struct Scsi_Host *, struct scsi_cmnd *); +int snic_abort_cmd(struct scsi_cmnd *); +int snic_device_reset(struct scsi_cmnd *); +int snic_host_reset(struct scsi_cmnd *); +int snic_reset(struct Scsi_Host *, struct scsi_cmnd *); +void snic_shutdown_scsi_cleanup(struct snic *); + + +int snic_request_intr(struct snic *); +void snic_free_intr(struct snic *); +int snic_set_intr_mode(struct snic *); +void snic_clear_intr_mode(struct snic *); + +int snic_fwcq_cmpl_handler(struct snic *, int); +int snic_wq_cmpl_handler(struct snic *, int); +void snic_free_wq_buf(struct vnic_wq *, struct vnic_wq_buf *); + + +void snic_log_q_error(struct snic *); +void snic_handle_link_event(struct snic *); +void snic_handle_link(struct work_struct *); + +int snic_queue_exch_ver_req(struct snic *); +int snic_io_exch_ver_cmpl_handler(struct snic *, struct snic_fw_req *); + +int snic_queue_wq_desc(struct snic *, void *os_buf, u16 len); + +void snic_handle_untagged_req(struct snic *, struct snic_req_info *); +void snic_release_untagged_req(struct snic *, struct snic_req_info *); +void snic_free_all_untagged_reqs(struct snic *); +int snic_get_conf(struct snic *); +void snic_set_state(struct snic *, enum snic_state); +int snic_get_state(struct snic *); +const char *snic_state_to_str(unsigned int); +void snic_hex_dump(char *, char *, int); +void snic_print_desc(const char *fn, char *os_buf, int len); +const char *show_opcode_name(int val); +#endif /* _SNIC_H */ diff --git a/kernel/drivers/scsi/snic/snic_attrs.c b/kernel/drivers/scsi/snic/snic_attrs.c new file mode 100644 index 000000000..32d5d556b --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_attrs.c @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "snic.h" + +static ssize_t +snic_show_sym_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snic *snic = shost_priv(class_to_shost(dev)); + + return snprintf(buf, PAGE_SIZE, "%s\n", snic->name); +} + +static ssize_t +snic_show_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snic *snic = shost_priv(class_to_shost(dev)); + + return snprintf(buf, PAGE_SIZE, "%s\n", + snic_state_str[snic_get_state(snic)]); +} + +static ssize_t +snic_show_drv_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", SNIC_DRV_VERSION); +} + +static ssize_t +snic_show_link_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct snic *snic = shost_priv(class_to_shost(dev)); + + if (snic->config.xpt_type == SNIC_DAS) + snic->link_status = svnic_dev_link_status(snic->vdev); + + return snprintf(buf, PAGE_SIZE, "%s\n", + (snic->link_status) ? "Link Up" : "Link Down"); +} + +static DEVICE_ATTR(snic_sym_name, S_IRUGO, snic_show_sym_name, NULL); +static DEVICE_ATTR(snic_state, S_IRUGO, snic_show_state, NULL); +static DEVICE_ATTR(drv_version, S_IRUGO, snic_show_drv_version, NULL); +static DEVICE_ATTR(link_state, S_IRUGO, snic_show_link_state, NULL); + +struct device_attribute *snic_attrs[] = { + &dev_attr_snic_sym_name, + &dev_attr_snic_state, + &dev_attr_drv_version, + &dev_attr_link_state, + NULL, +}; diff --git a/kernel/drivers/scsi/snic/snic_ctl.c b/kernel/drivers/scsi/snic/snic_ctl.c new file mode 100644 index 000000000..aebe75320 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_ctl.c @@ -0,0 +1,279 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "snic_io.h" +#include "snic.h" +#include "cq_enet_desc.h" +#include "snic_fwint.h" + +/* + * snic_handle_link : Handles link flaps. + */ +void +snic_handle_link(struct work_struct *work) +{ + struct snic *snic = container_of(work, struct snic, link_work); + + if (snic->config.xpt_type != SNIC_DAS) { + SNIC_HOST_INFO(snic->shost, "Link Event Received.\n"); + SNIC_ASSERT_NOT_IMPL(1); + + return; + } + + snic->link_status = svnic_dev_link_status(snic->vdev); + snic->link_down_cnt = svnic_dev_link_down_cnt(snic->vdev); + SNIC_HOST_INFO(snic->shost, "Link Event: Link %s.\n", + ((snic->link_status) ? "Up" : "Down")); +} + + +/* + * snic_ver_enc : Encodes version str to int + * version string is similar to netmask string + */ +static int +snic_ver_enc(const char *s) +{ + int v[4] = {0}; + int i = 0, x = 0; + char c; + const char *p = s; + + /* validate version string */ + if ((strlen(s) > 15) || (strlen(s) < 7)) + goto end; + + while ((c = *p++)) { + if (c == '.') { + i++; + continue; + } + + if (i > 4 || !isdigit(c)) + goto end; + + v[i] = v[i] * 10 + (c - '0'); + } + + /* validate sub version numbers */ + for (i = 3; i >= 0; i--) + if (v[i] > 0xff) + goto end; + + x |= (v[0] << 24) | v[1] << 16 | v[2] << 8 | v[3]; + +end: + if (x == 0) { + SNIC_ERR("Invalid version string [%s].\n", s); + + return -1; + } + + return x; +} /* end of snic_ver_enc */ + +/* + * snic_qeueue_exch_ver_req : + * + * Queues Exchange Version Request, to communicate host information + * in return, it gets firmware version details + */ +int +snic_queue_exch_ver_req(struct snic *snic) +{ + struct snic_req_info *rqi = NULL; + struct snic_host_req *req = NULL; + u32 ver = 0; + int ret = 0; + + SNIC_HOST_INFO(snic->shost, "Exch Ver Req Preparing...\n"); + + rqi = snic_req_init(snic, 0); + if (!rqi) { + SNIC_HOST_ERR(snic->shost, + "Queuing Exch Ver Req failed, err = %d\n", + ret); + + ret = -ENOMEM; + goto error; + } + + req = rqi_to_req(rqi); + + /* Initialize snic_host_req */ + snic_io_hdr_enc(&req->hdr, SNIC_REQ_EXCH_VER, 0, SCSI_NO_TAG, + snic->config.hid, 0, (ulong)rqi); + ver = snic_ver_enc(SNIC_DRV_VERSION); + req->u.exch_ver.drvr_ver = cpu_to_le32(ver); + req->u.exch_ver.os_type = cpu_to_le32(SNIC_OS_LINUX); + + snic_handle_untagged_req(snic, rqi); + + ret = snic_queue_wq_desc(snic, req, sizeof(*req)); + if (ret) { + snic_release_untagged_req(snic, rqi); + SNIC_HOST_ERR(snic->shost, + "Queuing Exch Ver Req failed, err = %d\n", + ret); + goto error; + } + + SNIC_HOST_INFO(snic->shost, "Exch Ver Req is issued. ret = %d\n", ret); + +error: + return ret; +} /* end of snic_queue_exch_ver_req */ + +/* + * snic_io_exch_ver_cmpl_handler + */ +int +snic_io_exch_ver_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + struct snic_req_info *rqi = NULL; + struct snic_exch_ver_rsp *exv_cmpl = &fwreq->u.exch_ver_cmpl; + u8 typ, hdr_stat; + u32 cmnd_id, hid, max_sgs; + ulong ctx = 0; + unsigned long flags; + int ret = 0; + + SNIC_HOST_INFO(snic->shost, "Exch Ver Compl Received.\n"); + snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); + SNIC_BUG_ON(snic->config.hid != hid); + rqi = (struct snic_req_info *) ctx; + + if (hdr_stat) { + SNIC_HOST_ERR(snic->shost, + "Exch Ver Completed w/ err status %d\n", + hdr_stat); + + goto exch_cmpl_end; + } + + spin_lock_irqsave(&snic->snic_lock, flags); + snic->fwinfo.fw_ver = le32_to_cpu(exv_cmpl->version); + snic->fwinfo.hid = le32_to_cpu(exv_cmpl->hid); + snic->fwinfo.max_concur_ios = le32_to_cpu(exv_cmpl->max_concur_ios); + snic->fwinfo.max_sgs_per_cmd = le32_to_cpu(exv_cmpl->max_sgs_per_cmd); + snic->fwinfo.max_io_sz = le32_to_cpu(exv_cmpl->max_io_sz); + snic->fwinfo.max_tgts = le32_to_cpu(exv_cmpl->max_tgts); + snic->fwinfo.io_tmo = le16_to_cpu(exv_cmpl->io_timeout); + + SNIC_HOST_INFO(snic->shost, + "vers %u hid %u max_concur_ios %u max_sgs_per_cmd %u max_io_sz %u max_tgts %u fw tmo %u\n", + snic->fwinfo.fw_ver, + snic->fwinfo.hid, + snic->fwinfo.max_concur_ios, + snic->fwinfo.max_sgs_per_cmd, + snic->fwinfo.max_io_sz, + snic->fwinfo.max_tgts, + snic->fwinfo.io_tmo); + + SNIC_HOST_INFO(snic->shost, + "HBA Capabilities = 0x%x\n", + le32_to_cpu(exv_cmpl->hba_cap)); + + /* Updating SGList size */ + max_sgs = snic->fwinfo.max_sgs_per_cmd; + if (max_sgs && max_sgs < SNIC_MAX_SG_DESC_CNT) { + snic->shost->sg_tablesize = max_sgs; + SNIC_HOST_INFO(snic->shost, "Max SGs set to %d\n", + snic->shost->sg_tablesize); + } else if (max_sgs > snic->shost->sg_tablesize) { + SNIC_HOST_INFO(snic->shost, + "Target type %d Supports Larger Max SGList %d than driver's Max SG List %d.\n", + snic->config.xpt_type, max_sgs, + snic->shost->sg_tablesize); + } + + if (snic->shost->can_queue > snic->fwinfo.max_concur_ios) + snic->shost->can_queue = snic->fwinfo.max_concur_ios; + + snic->shost->max_sectors = snic->fwinfo.max_io_sz >> 9; + if (snic->fwinfo.wait) + complete(snic->fwinfo.wait); + + spin_unlock_irqrestore(&snic->snic_lock, flags); + +exch_cmpl_end: + snic_release_untagged_req(snic, rqi); + + SNIC_HOST_INFO(snic->shost, "Exch_cmpl Done, hdr_stat %d.\n", hdr_stat); + + return ret; +} /* end of snic_io_exch_ver_cmpl_handler */ + +/* + * snic_get_conf + * + * Synchronous call, and Retrieves snic params. + */ +int +snic_get_conf(struct snic *snic) +{ + DECLARE_COMPLETION_ONSTACK(wait); + unsigned long flags; + int ret; + int nr_retries = 3; + + SNIC_HOST_INFO(snic->shost, "Retrieving snic params.\n"); + spin_lock_irqsave(&snic->snic_lock, flags); + memset(&snic->fwinfo, 0, sizeof(snic->fwinfo)); + snic->fwinfo.wait = &wait; + spin_unlock_irqrestore(&snic->snic_lock, flags); + + /* Additional delay to handle HW Resource initialization. */ + msleep(50); + + /* + * Exch ver req can be ignored by FW, if HW Resource initialization + * is in progress, Hence retry. + */ + do { + ret = snic_queue_exch_ver_req(snic); + if (ret) + return ret; + + wait_for_completion_timeout(&wait, msecs_to_jiffies(2000)); + spin_lock_irqsave(&snic->snic_lock, flags); + ret = (snic->fwinfo.fw_ver != 0) ? 0 : -ETIMEDOUT; + if (ret) + SNIC_HOST_ERR(snic->shost, + "Failed to retrieve snic params,\n"); + + /* Unset fwinfo.wait, on success or on last retry */ + if (ret == 0 || nr_retries == 1) + snic->fwinfo.wait = NULL; + + spin_unlock_irqrestore(&snic->snic_lock, flags); + } while (ret && --nr_retries); + + return ret; +} /* end of snic_get_info */ diff --git a/kernel/drivers/scsi/snic/snic_debugfs.c b/kernel/drivers/scsi/snic/snic_debugfs.c new file mode 100644 index 000000000..1686f0196 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_debugfs.c @@ -0,0 +1,560 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "snic.h" + +/* + * snic_debugfs_init - Initialize debugfs for snic debug logging + * + * Description: + * When Debugfs is configured this routine sets up fnic debugfs + * filesystem. If not already created. this routine will crate the + * fnic directory and statistics directory for trace buffer and + * stats logging + */ + +int +snic_debugfs_init(void) +{ + int rc = -1; + struct dentry *de = NULL; + + de = debugfs_create_dir("snic", NULL); + if (!de) { + SNIC_DBG("Cannot create debugfs root\n"); + + return rc; + } + snic_glob->trc_root = de; + + de = debugfs_create_dir("statistics", snic_glob->trc_root); + if (!de) { + SNIC_DBG("Cannot create Statistics directory\n"); + + return rc; + } + snic_glob->stats_root = de; + + rc = 0; + + return rc; +} /* end of snic_debugfs_init */ + +/* + * snic_debugfs_term - Tear down debugfs intrastructure + * + * Description: + * When Debufs is configured this routine removes debugfs file system + * elements that are specific to snic + */ +void +snic_debugfs_term(void) +{ + debugfs_remove(snic_glob->stats_root); + snic_glob->stats_root = NULL; + + debugfs_remove(snic_glob->trc_root); + snic_glob->trc_root = NULL; +} + +/* + * snic_reset_stats_open - Open the reset_stats file + */ +static int +snic_reset_stats_open(struct inode *inode, struct file *filp) +{ + SNIC_BUG_ON(!inode->i_private); + filp->private_data = inode->i_private; + + return 0; +} + +/* + * snic_reset_stats_read - Read a reset_stats debugfs file + * @filp: The file pointer to read from. + * @ubuf: The buffer tocopy the data to. + * @cnt: The number of bytes to read. + * @ppos: The position in the file to start reading frm. + * + * Description: + * This routine reads value of variable reset_stats + * and stores into local @buf. It will start reading file @ppos and + * copy up to @cnt of data to @ubuf from @buf. + * + * Returns: + * This function returns the amount of data that was read. + */ +static ssize_t +snic_reset_stats_read(struct file *filp, + char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct snic *snic = (struct snic *) filp->private_data; + char buf[64]; + int len; + + len = sprintf(buf, "%u\n", snic->reset_stats); + + return simple_read_from_buffer(ubuf, cnt, ppos, buf, len); +} + +/* + * snic_reset_stats_write - Write to reset_stats debugfs file + * @filp: The file pointer to write from + * @ubuf: The buffer to copy the data from. + * @cnt: The number of bytes to write. + * @ppos: The position in the file to start writing to. + * + * Description: + * This routine writes data from user buffer @ubuf to buffer @buf and + * resets cumulative stats of snic. + * + * Returns: + * This function returns the amount of data that was written. + */ +static ssize_t +snic_reset_stats_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct snic *snic = (struct snic *) filp->private_data; + struct snic_stats *stats = &snic->s_stats; + u64 *io_stats_p = (u64 *) &stats->io; + u64 *fw_stats_p = (u64 *) &stats->fw; + char buf[64]; + unsigned long val; + int ret; + + if (cnt >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, cnt)) + return -EFAULT; + + buf[cnt] = '\0'; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + snic->reset_stats = val; + + if (snic->reset_stats) { + /* Skip variable is used to avoid descrepancies to Num IOs + * and IO Completions stats. Skip incrementing No IO Compls + * for pending active IOs after reset_stats + */ + atomic64_set(&snic->io_cmpl_skip, + atomic64_read(&stats->io.active)); + memset(&stats->abts, 0, sizeof(struct snic_abort_stats)); + memset(&stats->reset, 0, sizeof(struct snic_reset_stats)); + memset(&stats->misc, 0, sizeof(struct snic_misc_stats)); + memset(io_stats_p+1, + 0, + sizeof(struct snic_io_stats) - sizeof(u64)); + memset(fw_stats_p+1, + 0, + sizeof(struct snic_fw_stats) - sizeof(u64)); + } + + (*ppos)++; + + SNIC_HOST_INFO(snic->shost, "Reset Op: Driver statistics.\n"); + + return cnt; +} + +static int +snic_reset_stats_release(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + + return 0; +} + +/* + * snic_stats_show - Formats and prints per host specific driver stats. + */ +static int +snic_stats_show(struct seq_file *sfp, void *data) +{ + struct snic *snic = (struct snic *) sfp->private; + struct snic_stats *stats = &snic->s_stats; + struct timespec last_isr_tms, last_ack_tms; + u64 maxio_tm; + int i; + + /* Dump IO Stats */ + seq_printf(sfp, + "------------------------------------------\n" + "\t\t IO Statistics\n" + "------------------------------------------\n"); + + maxio_tm = (u64) atomic64_read(&stats->io.max_time); + seq_printf(sfp, + "Active IOs : %lld\n" + "Max Active IOs : %lld\n" + "Total IOs : %lld\n" + "IOs Completed : %lld\n" + "IOs Failed : %lld\n" + "IOs Not Found : %lld\n" + "Memory Alloc Failures : %lld\n" + "REQs Null : %lld\n" + "SCSI Cmd Pointers Null : %lld\n" + "Max SGL for any IO : %lld\n" + "Max IO Size : %lld Sectors\n" + "Max Queuing Time : %lld\n" + "Max Completion Time : %lld\n" + "Max IO Process Time(FW) : %lld (%u msec)\n", + (u64) atomic64_read(&stats->io.active), + (u64) atomic64_read(&stats->io.max_active), + (u64) atomic64_read(&stats->io.num_ios), + (u64) atomic64_read(&stats->io.compl), + (u64) atomic64_read(&stats->io.fail), + (u64) atomic64_read(&stats->io.io_not_found), + (u64) atomic64_read(&stats->io.alloc_fail), + (u64) atomic64_read(&stats->io.req_null), + (u64) atomic64_read(&stats->io.sc_null), + (u64) atomic64_read(&stats->io.max_sgl), + (u64) atomic64_read(&stats->io.max_io_sz), + (u64) atomic64_read(&stats->io.max_qtime), + (u64) atomic64_read(&stats->io.max_cmpl_time), + maxio_tm, + jiffies_to_msecs(maxio_tm)); + + seq_puts(sfp, "\nSGL Counters\n"); + + for (i = 0; i < SNIC_MAX_SG_DESC_CNT; i++) { + seq_printf(sfp, + "%10lld ", + (u64) atomic64_read(&stats->io.sgl_cnt[i])); + + if ((i + 1) % 8 == 0) + seq_puts(sfp, "\n"); + } + + /* Dump Abort Stats */ + seq_printf(sfp, + "\n-------------------------------------------\n" + "\t\t Abort Statistics\n" + "---------------------------------------------\n"); + + seq_printf(sfp, + "Aborts : %lld\n" + "Aborts Fail : %lld\n" + "Aborts Driver Timeout : %lld\n" + "Abort FW Timeout : %lld\n" + "Abort IO NOT Found : %lld\n", + (u64) atomic64_read(&stats->abts.num), + (u64) atomic64_read(&stats->abts.fail), + (u64) atomic64_read(&stats->abts.drv_tmo), + (u64) atomic64_read(&stats->abts.fw_tmo), + (u64) atomic64_read(&stats->abts.io_not_found)); + + /* Dump Reset Stats */ + seq_printf(sfp, + "\n-------------------------------------------\n" + "\t\t Reset Statistics\n" + "---------------------------------------------\n"); + + seq_printf(sfp, + "HBA Resets : %lld\n" + "HBA Reset Cmpls : %lld\n" + "HBA Reset Fail : %lld\n", + (u64) atomic64_read(&stats->reset.hba_resets), + (u64) atomic64_read(&stats->reset.hba_reset_cmpl), + (u64) atomic64_read(&stats->reset.hba_reset_fail)); + + /* Dump Firmware Stats */ + seq_printf(sfp, + "\n-------------------------------------------\n" + "\t\t Firmware Statistics\n" + "---------------------------------------------\n"); + + seq_printf(sfp, + "Active FW Requests : %lld\n" + "Max FW Requests : %lld\n" + "FW Out Of Resource Errs : %lld\n" + "FW IO Errors : %lld\n" + "FW SCSI Errors : %lld\n", + (u64) atomic64_read(&stats->fw.actv_reqs), + (u64) atomic64_read(&stats->fw.max_actv_reqs), + (u64) atomic64_read(&stats->fw.out_of_res), + (u64) atomic64_read(&stats->fw.io_errs), + (u64) atomic64_read(&stats->fw.scsi_errs)); + + + /* Dump Miscellenous Stats */ + seq_printf(sfp, + "\n---------------------------------------------\n" + "\t\t Other Statistics\n" + "\n---------------------------------------------\n"); + + jiffies_to_timespec(stats->misc.last_isr_time, &last_isr_tms); + jiffies_to_timespec(stats->misc.last_ack_time, &last_ack_tms); + + seq_printf(sfp, + "Last ISR Time : %llu (%8lu.%8lu)\n" + "Last Ack Time : %llu (%8lu.%8lu)\n" + "ISRs : %llu\n" + "Max CQ Entries : %lld\n" + "Data Count Mismatch : %lld\n" + "IOs w/ Timeout Status : %lld\n" + "IOs w/ Aborted Status : %lld\n" + "IOs w/ SGL Invalid Stat : %lld\n" + "WQ Desc Alloc Fail : %lld\n" + "Queue Full : %lld\n" + "Target Not Ready : %lld\n", + (u64) stats->misc.last_isr_time, + last_isr_tms.tv_sec, last_isr_tms.tv_nsec, + (u64)stats->misc.last_ack_time, + last_ack_tms.tv_sec, last_ack_tms.tv_nsec, + (u64) atomic64_read(&stats->misc.isr_cnt), + (u64) atomic64_read(&stats->misc.max_cq_ents), + (u64) atomic64_read(&stats->misc.data_cnt_mismat), + (u64) atomic64_read(&stats->misc.io_tmo), + (u64) atomic64_read(&stats->misc.io_aborted), + (u64) atomic64_read(&stats->misc.sgl_inval), + (u64) atomic64_read(&stats->misc.wq_alloc_fail), + (u64) atomic64_read(&stats->misc.qfull), + (u64) atomic64_read(&stats->misc.tgt_not_rdy)); + + return 0; +} + +/* + * snic_stats_open - Open the stats file for specific host + * + * Description: + * This routine opens a debugfs file stats of specific host + */ +static int +snic_stats_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, snic_stats_show, inode->i_private); +} + +static const struct file_operations snic_stats_fops = { + .owner = THIS_MODULE, + .open = snic_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations snic_reset_stats_fops = { + .owner = THIS_MODULE, + .open = snic_reset_stats_open, + .read = snic_reset_stats_read, + .write = snic_reset_stats_write, + .release = snic_reset_stats_release, +}; + +/* + * snic_stats_init - Initialize stats struct and create stats file + * per snic + * + * Description: + * When debugfs is cofigured this routine sets up the stats file per snic + * It will create file stats and reset_stats under statistics/host# directory + * to log per snic stats + */ +int +snic_stats_debugfs_init(struct snic *snic) +{ + int rc = -1; + char name[16]; + struct dentry *de = NULL; + + snprintf(name, sizeof(name), "host%d", snic->shost->host_no); + if (!snic_glob->stats_root) { + SNIC_DBG("snic_stats root doesn't exist\n"); + + return rc; + } + + de = debugfs_create_dir(name, snic_glob->stats_root); + if (!de) { + SNIC_DBG("Cannot create host directory\n"); + + return rc; + } + snic->stats_host = de; + + de = debugfs_create_file("stats", + S_IFREG|S_IRUGO, + snic->stats_host, + snic, + &snic_stats_fops); + if (!de) { + SNIC_DBG("Cannot create host's stats file\n"); + + return rc; + } + snic->stats_file = de; + + de = debugfs_create_file("reset_stats", + S_IFREG|S_IRUGO|S_IWUSR, + snic->stats_host, + snic, + &snic_reset_stats_fops); + + if (!de) { + SNIC_DBG("Cannot create host's reset_stats file\n"); + + return rc; + } + snic->reset_stats_file = de; + rc = 0; + + return rc; +} /* end of snic_stats_debugfs_init */ + +/* + * snic_stats_debugfs_remove - Tear down debugfs infrastructure of stats + * + * Description: + * When Debufs is configured this routine removes debugfs file system + * elements that are specific to to snic stats + */ +void +snic_stats_debugfs_remove(struct snic *snic) +{ + debugfs_remove(snic->stats_file); + snic->stats_file = NULL; + + debugfs_remove(snic->reset_stats_file); + snic->reset_stats_file = NULL; + + debugfs_remove(snic->stats_host); + snic->stats_host = NULL; +} + +/* Trace Facility related API */ +static void * +snic_trc_seq_start(struct seq_file *sfp, loff_t *pos) +{ + return &snic_glob->trc; +} + +static void * +snic_trc_seq_next(struct seq_file *sfp, void *data, loff_t *pos) +{ + return NULL; +} + +static void +snic_trc_seq_stop(struct seq_file *sfp, void *data) +{ +} + +#define SNIC_TRC_PBLEN 256 +static int +snic_trc_seq_show(struct seq_file *sfp, void *data) +{ + char buf[SNIC_TRC_PBLEN]; + + if (snic_get_trc_data(buf, SNIC_TRC_PBLEN) > 0) + seq_printf(sfp, "%s\n", buf); + + return 0; +} + +static const struct seq_operations snic_trc_seq_ops = { + .start = snic_trc_seq_start, + .next = snic_trc_seq_next, + .stop = snic_trc_seq_stop, + .show = snic_trc_seq_show, +}; + +static int +snic_trc_open(struct inode *inode, struct file *filp) +{ + return seq_open(filp, &snic_trc_seq_ops); +} + +static const struct file_operations snic_trc_fops = { + .owner = THIS_MODULE, + .open = snic_trc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* + * snic_trc_debugfs_init : creates trace/tracing_enable files for trace + * under debugfs + */ +int +snic_trc_debugfs_init(void) +{ + struct dentry *de = NULL; + int ret = -1; + + if (!snic_glob->trc_root) { + SNIC_ERR("Debugfs root directory for snic doesn't exist.\n"); + + return ret; + } + + de = debugfs_create_bool("tracing_enable", + S_IFREG | S_IRUGO | S_IWUSR, + snic_glob->trc_root, + &snic_glob->trc.enable); + + if (!de) { + SNIC_ERR("Can't create trace_enable file.\n"); + + return ret; + } + snic_glob->trc.trc_enable = de; + + de = debugfs_create_file("trace", + S_IFREG | S_IRUGO | S_IWUSR, + snic_glob->trc_root, + NULL, + &snic_trc_fops); + + if (!de) { + SNIC_ERR("Cann't create trace file.\n"); + + return ret; + } + snic_glob->trc.trc_file = de; + ret = 0; + + return ret; +} /* end of snic_trc_debugfs_init */ + +/* + * snic_trc_debugfs_term : cleans up the files created for trace under debugfs + */ +void +snic_trc_debugfs_term(void) +{ + debugfs_remove(snic_glob->trc.trc_file); + snic_glob->trc.trc_file = NULL; + + debugfs_remove(snic_glob->trc.trc_enable); + snic_glob->trc.trc_enable = NULL; +} diff --git a/kernel/drivers/scsi/snic/snic_disc.c b/kernel/drivers/scsi/snic/snic_disc.c new file mode 100644 index 000000000..5f6321759 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_disc.c @@ -0,0 +1,551 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include + +#include "snic_disc.h" +#include "snic.h" +#include "snic_io.h" + + +/* snic target types */ +static const char * const snic_tgt_type_str[] = { + [SNIC_TGT_DAS] = "DAS", + [SNIC_TGT_SAN] = "SAN", +}; + +static inline const char * +snic_tgt_type_to_str(int typ) +{ + return ((typ > SNIC_TGT_NONE && typ <= SNIC_TGT_SAN) ? + snic_tgt_type_str[typ] : "Unknown"); +} + +static const char * const snic_tgt_state_str[] = { + [SNIC_TGT_STAT_INIT] = "INIT", + [SNIC_TGT_STAT_ONLINE] = "ONLINE", + [SNIC_TGT_STAT_OFFLINE] = "OFFLINE", + [SNIC_TGT_STAT_DEL] = "DELETION IN PROGRESS", +}; + +const char * +snic_tgt_state_to_str(int state) +{ + return ((state >= SNIC_TGT_STAT_INIT && state <= SNIC_TGT_STAT_DEL) ? + snic_tgt_state_str[state] : "UNKNOWN"); +} + +/* + * Initiate report_tgt req desc + */ +static void +snic_report_tgt_init(struct snic_host_req *req, u32 hid, u8 *buf, u32 len, + dma_addr_t rsp_buf_pa, ulong ctx) +{ + struct snic_sg_desc *sgd = NULL; + + + snic_io_hdr_enc(&req->hdr, SNIC_REQ_REPORT_TGTS, 0, SCSI_NO_TAG, hid, + 1, ctx); + + req->u.rpt_tgts.sg_cnt = cpu_to_le16(1); + sgd = req_to_sgl(req); + sgd[0].addr = cpu_to_le64(rsp_buf_pa); + sgd[0].len = cpu_to_le32(len); + sgd[0]._resvd = 0; + req->u.rpt_tgts.sg_addr = cpu_to_le64((ulong)sgd); +} + +/* + * snic_queue_report_tgt_req: Queues report target request. + */ +static int +snic_queue_report_tgt_req(struct snic *snic) +{ + struct snic_req_info *rqi = NULL; + u32 ntgts, buf_len = 0; + u8 *buf = NULL; + dma_addr_t pa = 0; + int ret = 0; + + rqi = snic_req_init(snic, 1); + if (!rqi) { + ret = -ENOMEM; + goto error; + } + + if (snic->fwinfo.max_tgts) + ntgts = min_t(u32, snic->fwinfo.max_tgts, snic->shost->max_id); + else + ntgts = snic->shost->max_id; + + /* Allocate Response Buffer */ + SNIC_BUG_ON(ntgts == 0); + buf_len = ntgts * sizeof(struct snic_tgt_id) + SNIC_SG_DESC_ALIGN; + + buf = kzalloc(buf_len, GFP_KERNEL|GFP_DMA); + if (!buf) { + snic_req_free(snic, rqi); + SNIC_HOST_ERR(snic->shost, "Resp Buf Alloc Failed.\n"); + + ret = -ENOMEM; + goto error; + } + + SNIC_BUG_ON((((unsigned long)buf) % SNIC_SG_DESC_ALIGN) != 0); + + pa = pci_map_single(snic->pdev, buf, buf_len, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(snic->pdev, pa)) { + kfree(buf); + snic_req_free(snic, rqi); + SNIC_HOST_ERR(snic->shost, + "Rpt-tgt rspbuf %p: PCI DMA Mapping Failed\n", + buf); + ret = -EINVAL; + + goto error; + } + + + SNIC_BUG_ON(pa == 0); + rqi->sge_va = (ulong) buf; + + snic_report_tgt_init(rqi->req, + snic->config.hid, + buf, + buf_len, + pa, + (ulong)rqi); + + snic_handle_untagged_req(snic, rqi); + + ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len); + if (ret) { + pci_unmap_single(snic->pdev, pa, buf_len, PCI_DMA_FROMDEVICE); + kfree(buf); + rqi->sge_va = 0; + snic_release_untagged_req(snic, rqi); + SNIC_HOST_ERR(snic->shost, "Queuing Report Tgts Failed.\n"); + + goto error; + } + + SNIC_DISC_DBG(snic->shost, "Report Targets Issued.\n"); + + return ret; + +error: + SNIC_HOST_ERR(snic->shost, + "Queuing Report Targets Failed, err = %d\n", + ret); + return ret; +} /* end of snic_queue_report_tgt_req */ + +/* call into SML */ +static void +snic_scsi_scan_tgt(struct work_struct *work) +{ + struct snic_tgt *tgt = container_of(work, struct snic_tgt, scan_work); + struct Scsi_Host *shost = dev_to_shost(&tgt->dev); + unsigned long flags; + + SNIC_HOST_INFO(shost, "Scanning Target id 0x%x\n", tgt->id); + scsi_scan_target(&tgt->dev, + tgt->channel, + tgt->scsi_tgt_id, + SCAN_WILD_CARD, + 1); + + spin_lock_irqsave(shost->host_lock, flags); + tgt->flags &= ~SNIC_TGT_SCAN_PENDING; + spin_unlock_irqrestore(shost->host_lock, flags); +} /* end of snic_scsi_scan_tgt */ + +/* + * snic_tgt_lookup : + */ +static struct snic_tgt * +snic_tgt_lookup(struct snic *snic, struct snic_tgt_id *tgtid) +{ + struct list_head *cur, *nxt; + struct snic_tgt *tgt = NULL; + + list_for_each_safe(cur, nxt, &snic->disc.tgt_list) { + tgt = list_entry(cur, struct snic_tgt, list); + if (tgt->id == le32_to_cpu(tgtid->tgt_id)) + return tgt; + tgt = NULL; + } + + return tgt; +} /* end of snic_tgt_lookup */ + +/* + * snic_tgt_dev_release : Called on dropping last ref for snic_tgt object + */ +void +snic_tgt_dev_release(struct device *dev) +{ + struct snic_tgt *tgt = dev_to_tgt(dev); + + SNIC_HOST_INFO(snic_tgt_to_shost(tgt), + "Target Device ID %d (%s) Permanently Deleted.\n", + tgt->id, + dev_name(dev)); + + SNIC_BUG_ON(!list_empty(&tgt->list)); + kfree(tgt); +} + +/* + * snic_tgt_del : work function to delete snic_tgt + */ +static void +snic_tgt_del(struct work_struct *work) +{ + struct snic_tgt *tgt = container_of(work, struct snic_tgt, del_work); + struct Scsi_Host *shost = snic_tgt_to_shost(tgt); + + if (tgt->flags & SNIC_TGT_SCAN_PENDING) + scsi_flush_work(shost); + + /* Block IOs on child devices, stops new IOs */ + scsi_target_block(&tgt->dev); + + /* Cleanup IOs */ + snic_tgt_scsi_abort_io(tgt); + + /* Unblock IOs now, to flush if there are any. */ + scsi_target_unblock(&tgt->dev, SDEV_TRANSPORT_OFFLINE); + + /* Delete SCSI Target and sdevs */ + scsi_remove_target(&tgt->dev); /* ?? */ + device_del(&tgt->dev); + put_device(&tgt->dev); +} /* end of snic_tgt_del */ + +/* snic_tgt_create: checks for existence of snic_tgt, if it doesn't + * it creates one. + */ +static struct snic_tgt * +snic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid) +{ + struct snic_tgt *tgt = NULL; + unsigned long flags; + int ret; + + tgt = snic_tgt_lookup(snic, tgtid); + if (tgt) { + /* update the information if required */ + return tgt; + } + + tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); + if (!tgt) { + SNIC_HOST_ERR(snic->shost, "Failure to allocate snic_tgt.\n"); + ret = -ENOMEM; + + return tgt; + } + + INIT_LIST_HEAD(&tgt->list); + tgt->id = le32_to_cpu(tgtid->tgt_id); + tgt->channel = 0; + + SNIC_BUG_ON(le16_to_cpu(tgtid->tgt_type) > SNIC_TGT_SAN); + tgt->tdata.typ = le16_to_cpu(tgtid->tgt_type); + + /* + * Plugging into SML Device Tree + */ + tgt->tdata.disc_id = 0; + tgt->state = SNIC_TGT_STAT_INIT; + device_initialize(&tgt->dev); + tgt->dev.parent = get_device(&snic->shost->shost_gendev); + tgt->dev.release = snic_tgt_dev_release; + INIT_WORK(&tgt->scan_work, snic_scsi_scan_tgt); + INIT_WORK(&tgt->del_work, snic_tgt_del); + switch (tgt->tdata.typ) { + case SNIC_TGT_DAS: + dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d", + snic->shost->host_no, tgt->channel, tgt->id); + break; + + case SNIC_TGT_SAN: + dev_set_name(&tgt->dev, "snic_san_tgt:%d:%d-%d", + snic->shost->host_no, tgt->channel, tgt->id); + break; + + default: + SNIC_HOST_INFO(snic->shost, "Target type Unknown Detected.\n"); + dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d", + snic->shost->host_no, tgt->channel, tgt->id); + break; + } + + spin_lock_irqsave(snic->shost->host_lock, flags); + list_add_tail(&tgt->list, &snic->disc.tgt_list); + tgt->scsi_tgt_id = snic->disc.nxt_tgt_id++; + tgt->state = SNIC_TGT_STAT_ONLINE; + spin_unlock_irqrestore(snic->shost->host_lock, flags); + + SNIC_HOST_INFO(snic->shost, + "Tgt %d, type = %s detected. Adding..\n", + tgt->id, snic_tgt_type_to_str(tgt->tdata.typ)); + + ret = device_add(&tgt->dev); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "Snic Tgt: device_add, with err = %d\n", + ret); + + put_device(&snic->shost->shost_gendev); + kfree(tgt); + tgt = NULL; + + return tgt; + } + + SNIC_HOST_INFO(snic->shost, "Scanning %s.\n", dev_name(&tgt->dev)); + + scsi_queue_work(snic->shost, &tgt->scan_work); + + return tgt; +} /* end of snic_tgt_create */ + +/* Handler for discovery */ +void +snic_handle_tgt_disc(struct work_struct *work) +{ + struct snic *snic = container_of(work, struct snic, tgt_work); + struct snic_tgt_id *tgtid = NULL; + struct snic_tgt *tgt = NULL; + unsigned long flags; + int i; + + spin_lock_irqsave(&snic->snic_lock, flags); + if (snic->in_remove) { + spin_unlock_irqrestore(&snic->snic_lock, flags); + kfree(snic->disc.rtgt_info); + + return; + } + spin_unlock_irqrestore(&snic->snic_lock, flags); + + mutex_lock(&snic->disc.mutex); + /* Discover triggered during disc in progress */ + if (snic->disc.req_cnt) { + snic->disc.state = SNIC_DISC_DONE; + snic->disc.req_cnt = 0; + mutex_unlock(&snic->disc.mutex); + kfree(snic->disc.rtgt_info); + snic->disc.rtgt_info = NULL; + + SNIC_HOST_INFO(snic->shost, "tgt_disc: Discovery restart.\n"); + /* Start Discovery Again */ + snic_disc_start(snic); + + return; + } + + tgtid = (struct snic_tgt_id *)snic->disc.rtgt_info; + + SNIC_BUG_ON(snic->disc.rtgt_cnt == 0 || tgtid == NULL); + + for (i = 0; i < snic->disc.rtgt_cnt; i++) { + tgt = snic_tgt_create(snic, &tgtid[i]); + if (!tgt) { + int buf_sz = snic->disc.rtgt_cnt * sizeof(*tgtid); + + SNIC_HOST_ERR(snic->shost, "Failed to create tgt.\n"); + snic_hex_dump("rpt_tgt_rsp", (char *)tgtid, buf_sz); + break; + } + } + + snic->disc.rtgt_info = NULL; + snic->disc.state = SNIC_DISC_DONE; + mutex_unlock(&snic->disc.mutex); + + SNIC_HOST_INFO(snic->shost, "Discovery Completed.\n"); + + kfree(tgtid); +} /* end of snic_handle_tgt_disc */ + + +int +snic_report_tgt_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + + u8 typ, cmpl_stat; + u32 cmnd_id, hid, tgt_cnt = 0; + ulong ctx; + struct snic_req_info *rqi = NULL; + struct snic_tgt_id *tgtid; + int i, ret = 0; + + snic_io_hdr_dec(&fwreq->hdr, &typ, &cmpl_stat, &cmnd_id, &hid, &ctx); + rqi = (struct snic_req_info *) ctx; + tgtid = (struct snic_tgt_id *) rqi->sge_va; + + tgt_cnt = le32_to_cpu(fwreq->u.rpt_tgts_cmpl.tgt_cnt); + if (tgt_cnt == 0) { + SNIC_HOST_ERR(snic->shost, "No Targets Found on this host.\n"); + ret = 1; + + goto end; + } + + /* printing list of targets here */ + SNIC_HOST_INFO(snic->shost, "Target Count = %d\n", tgt_cnt); + + SNIC_BUG_ON(tgt_cnt > snic->fwinfo.max_tgts); + + for (i = 0; i < tgt_cnt; i++) + SNIC_HOST_INFO(snic->shost, + "Tgt id = 0x%x\n", + le32_to_cpu(tgtid[i].tgt_id)); + + /* + * Queue work for further processing, + * Response Buffer Memory is freed after creating targets + */ + snic->disc.rtgt_cnt = tgt_cnt; + snic->disc.rtgt_info = (u8 *) tgtid; + queue_work(snic_glob->event_q, &snic->tgt_work); + ret = 0; + +end: + /* Unmap Response Buffer */ + snic_pci_unmap_rsp_buf(snic, rqi); + if (ret) + kfree(tgtid); + + rqi->sge_va = 0; + snic_release_untagged_req(snic, rqi); + + return ret; +} /* end of snic_report_tgt_cmpl_handler */ + +/* Discovery init fn */ +void +snic_disc_init(struct snic_disc *disc) +{ + INIT_LIST_HEAD(&disc->tgt_list); + mutex_init(&disc->mutex); + disc->disc_id = 0; + disc->nxt_tgt_id = 0; + disc->state = SNIC_DISC_INIT; + disc->req_cnt = 0; + disc->rtgt_cnt = 0; + disc->rtgt_info = NULL; + disc->cb = NULL; +} /* end of snic_disc_init */ + +/* Discovery, uninit fn */ +void +snic_disc_term(struct snic *snic) +{ + struct snic_disc *disc = &snic->disc; + + mutex_lock(&disc->mutex); + if (disc->req_cnt) { + disc->req_cnt = 0; + SNIC_SCSI_DBG(snic->shost, "Terminating Discovery.\n"); + } + mutex_unlock(&disc->mutex); +} + +/* + * snic_disc_start: Discovery Start ... + */ +int +snic_disc_start(struct snic *snic) +{ + struct snic_disc *disc = &snic->disc; + int ret = 0; + + SNIC_SCSI_DBG(snic->shost, "Discovery Start.\n"); + + mutex_lock(&disc->mutex); + if (disc->state == SNIC_DISC_PENDING) { + disc->req_cnt++; + mutex_unlock(&disc->mutex); + + return ret; + } + disc->state = SNIC_DISC_PENDING; + mutex_unlock(&disc->mutex); + + ret = snic_queue_report_tgt_req(snic); + if (ret) + SNIC_HOST_INFO(snic->shost, "Discovery Failed, err=%d.\n", ret); + + return ret; +} /* end of snic_disc_start */ + +/* + * snic_disc_work : + */ +void +snic_handle_disc(struct work_struct *work) +{ + struct snic *snic = container_of(work, struct snic, disc_work); + int ret = 0; + + SNIC_HOST_INFO(snic->shost, "disc_work: Discovery\n"); + + ret = snic_disc_start(snic); + if (ret) + goto disc_err; + +disc_err: + SNIC_HOST_ERR(snic->shost, + "disc_work: Discovery Failed w/ err = %d\n", + ret); +} /* end of snic_disc_work */ + +/* + * snic_tgt_del_all : cleanup all snic targets + * Called on unbinding the interface + */ +void +snic_tgt_del_all(struct snic *snic) +{ + struct snic_tgt *tgt = NULL; + struct list_head *cur, *nxt; + unsigned long flags; + + mutex_lock(&snic->disc.mutex); + spin_lock_irqsave(snic->shost->host_lock, flags); + + list_for_each_safe(cur, nxt, &snic->disc.tgt_list) { + tgt = list_entry(cur, struct snic_tgt, list); + tgt->state = SNIC_TGT_STAT_DEL; + list_del_init(&tgt->list); + SNIC_HOST_INFO(snic->shost, "Tgt %d q'ing for del\n", tgt->id); + queue_work(snic_glob->event_q, &tgt->del_work); + tgt = NULL; + } + spin_unlock_irqrestore(snic->shost->host_lock, flags); + + scsi_flush_work(snic->shost); + mutex_unlock(&snic->disc.mutex); +} /* end of snic_tgt_del_all */ diff --git a/kernel/drivers/scsi/snic/snic_disc.h b/kernel/drivers/scsi/snic/snic_disc.h new file mode 100644 index 000000000..97fa3f5c5 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_disc.h @@ -0,0 +1,124 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __SNIC_DISC_H +#define __SNIC_DISC_H + +#include "snic_fwint.h" + +enum snic_disc_state { + SNIC_DISC_NONE, + SNIC_DISC_INIT, + SNIC_DISC_PENDING, + SNIC_DISC_DONE +}; + +struct snic; +struct snic_disc { + struct list_head tgt_list; + enum snic_disc_state state; + struct mutex mutex; + u16 disc_id; + u8 req_cnt; + u32 nxt_tgt_id; + u32 rtgt_cnt; + u8 *rtgt_info; + struct delayed_work disc_timeout; + void (*cb)(struct snic *); +}; + +#define SNIC_TGT_NAM_LEN 16 + +enum snic_tgt_state { + SNIC_TGT_STAT_NONE, + SNIC_TGT_STAT_INIT, + SNIC_TGT_STAT_ONLINE, /* Target is Online */ + SNIC_TGT_STAT_OFFLINE, /* Target is Offline */ + SNIC_TGT_STAT_DEL, +}; + +struct snic_tgt_priv { + struct list_head list; + enum snic_tgt_type typ; + u16 disc_id; + char *name[SNIC_TGT_NAM_LEN]; + + union { + /*DAS Target specific info */ + /*SAN Target specific info */ + u8 dummmy; + } u; +}; + +/* snic tgt flags */ +#define SNIC_TGT_SCAN_PENDING 0x01 + +struct snic_tgt { + struct list_head list; + u16 id; + u16 channel; + u32 flags; + u32 scsi_tgt_id; + enum snic_tgt_state state; + struct device dev; + struct work_struct scan_work; + struct work_struct del_work; + struct snic_tgt_priv tdata; +}; + + +struct snic_fw_req; + +void snic_disc_init(struct snic_disc *); +int snic_disc_start(struct snic *); +void snic_disc_term(struct snic *); +int snic_report_tgt_cmpl_handler(struct snic *, struct snic_fw_req *); +int snic_tgtinfo_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq); +void snic_process_report_tgts_rsp(struct work_struct *); +void snic_handle_tgt_disc(struct work_struct *); +void snic_handle_disc(struct work_struct *); +void snic_tgt_dev_release(struct device *); +void snic_tgt_del_all(struct snic *); + +#define dev_to_tgt(d) \ + container_of(d, struct snic_tgt, dev) + +static inline int +is_snic_target(struct device *dev) +{ + return dev->release == snic_tgt_dev_release; +} + +#define starget_to_tgt(st) \ + (is_snic_target(((struct scsi_target *) st)->dev.parent) ? \ + dev_to_tgt(st->dev.parent) : NULL) + +#define snic_tgt_to_shost(t) \ + dev_to_shost(t->dev.parent) + +static inline int +snic_tgt_chkready(struct snic_tgt *tgt) +{ + if (tgt->state == SNIC_TGT_STAT_ONLINE) + return 0; + else + return DID_NO_CONNECT << 16; +} + +const char *snic_tgt_state_to_str(int); +int snic_tgt_scsi_abort_io(struct snic_tgt *); +#endif /* end of __SNIC_DISC_H */ diff --git a/kernel/drivers/scsi/snic/snic_fwint.h b/kernel/drivers/scsi/snic/snic_fwint.h new file mode 100644 index 000000000..2cfaf2dc9 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_fwint.h @@ -0,0 +1,525 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __SNIC_FWINT_H +#define __SNIC_FWINT_H + +#define SNIC_CDB_LEN 32 /* SCSI CDB size 32, can be used for 16 bytes */ +#define LUN_ADDR_LEN 8 + +/* + * Command entry type + */ +enum snic_io_type { + /* + * Initiator request types + */ + SNIC_REQ_REPORT_TGTS = 0x2, /* Report Targets */ + SNIC_REQ_ICMND, /* Initiator command for SCSI IO */ + SNIC_REQ_ITMF, /* Initiator command for Task Mgmt */ + SNIC_REQ_HBA_RESET, /* SNIC Reset */ + SNIC_REQ_EXCH_VER, /* Exchange Version Information */ + SNIC_REQ_TGT_INFO, /* Backend/Target Information */ + SNIC_REQ_BOOT_LUNS, + + /* + * Response type + */ + SNIC_RSP_REPORT_TGTS_CMPL = 0x12,/* Report Targets Completion */ + SNIC_RSP_ICMND_CMPL, /* SCSI IO Completion */ + SNIC_RSP_ITMF_CMPL, /* Task Management Completion */ + SNIC_RSP_HBA_RESET_CMPL, /* SNIC Reset Completion */ + SNIC_RSP_EXCH_VER_CMPL, /* Exchange Version Completion*/ + SNIC_RSP_BOOT_LUNS_CMPL, + + /* + * Misc Request types + */ + SNIC_MSG_ACK = 0x80, /* Ack: snic_notify_msg */ + SNIC_MSG_ASYNC_EVNOTIFY, /* Asynchronous Event Notification */ +}; /* end of enum snic_io_type */ + + +/* + * Header status codes from firmware + */ +enum snic_io_status { + SNIC_STAT_IO_SUCCESS = 0, /* request was successful */ + + /* + * If a request to the fw is rejected, the original request header + * will be returned with the status set to one of the following: + */ + SNIC_STAT_INVALID_HDR, /* header contains invalid data */ + SNIC_STAT_OUT_OF_RES, /* out of resources to complete request */ + SNIC_STAT_INVALID_PARM, /* some parameter in request is not valid */ + SNIC_STAT_REQ_NOT_SUP, /* req type is not supported */ + SNIC_STAT_IO_NOT_FOUND, /* requested IO was not found */ + + /* + * Once a request is processed, the fw will usually return + * a cmpl message type. In cases where errors occurred, + * the header status would be filled in with one of the following: + */ + SNIC_STAT_ABORTED, /* req was aborted */ + SNIC_STAT_TIMEOUT, /* req was timed out */ + SNIC_STAT_SGL_INVALID, /* req was aborted due to sgl error */ + SNIC_STAT_DATA_CNT_MISMATCH, /*recv/sent more/less data than expec */ + SNIC_STAT_FW_ERR, /* req was terminated due to fw error */ + SNIC_STAT_ITMF_REJECT, /* itmf req was rejected by target */ + SNIC_STAT_ITMF_FAIL, /* itmf req was failed */ + SNIC_STAT_ITMF_INCORRECT_LUN, /* itmf req has incorrect LUN id*/ + SNIC_STAT_CMND_REJECT, /* req was invalid and rejected */ + SNIC_STAT_DEV_OFFLINE, /* req sent to offline device */ + SNIC_STAT_NO_BOOTLUN, + SNIC_STAT_SCSI_ERR, /* SCSI error returned by Target. */ + SNIC_STAT_NOT_READY, /* sNIC Subsystem is not ready */ + SNIC_STAT_FATAL_ERROR, /* sNIC is in unrecoverable state */ +}; /* end of enum snic_io_status */ + +/* + * snic_io_hdr : host <--> firmare + * + * for any other message that will be queued to firmware should + * have the following request header + */ +struct snic_io_hdr { + __le32 hid; + __le32 cmnd_id; /* tag here */ + ulong init_ctx; /* initiator context */ + u8 type; /* request/response type */ + u8 status; /* header status entry */ + u8 protocol; /* Protocol specific, may needed for RoCE*/ + u8 flags; + __le16 sg_cnt; + u16 resvd; +}; + +/* auxillary funciton for encoding the snic_io_hdr */ +static inline void +snic_io_hdr_enc(struct snic_io_hdr *hdr, u8 typ, u8 status, u32 id, u32 hid, + u16 sg_cnt, ulong ctx) +{ + hdr->type = typ; + hdr->status = status; + hdr->protocol = 0; + hdr->hid = cpu_to_le32(hid); + hdr->cmnd_id = cpu_to_le32(id); + hdr->sg_cnt = cpu_to_le16(sg_cnt); + hdr->init_ctx = ctx; + hdr->flags = 0; +} + +/* auxillary funciton for decoding the snic_io_hdr */ +static inline void +snic_io_hdr_dec(struct snic_io_hdr *hdr, u8 *typ, u8 *stat, u32 *cmnd_id, + u32 *hid, ulong *ctx) +{ + *typ = hdr->type; + *stat = hdr->status; + *hid = le32_to_cpu(hdr->hid); + *cmnd_id = le32_to_cpu(hdr->cmnd_id); + *ctx = hdr->init_ctx; +} + +/* + * snic_host_info: host -> firmware + * + * Used for sending host information to firmware, and request fw version + */ +struct snic_exch_ver_req { + __le32 drvr_ver; /* for debugging, when fw dump captured */ + __le32 os_type; /* for OS specific features */ +}; + +/* + * os_type flags + * Bit 0-7 : OS information + * Bit 8-31: Feature/Capability Information + */ +#define SNIC_OS_LINUX 0x1 +#define SNIC_OS_WIN 0x2 +#define SNIC_OS_ESX 0x3 + +/* + * HBA Capabilities + * Bit 1: Reserved. + * Bit 2: Dynamic Discovery of LUNs. + * Bit 3: Async event notifications on on tgt online/offline events. + * Bit 4: IO timeout support in FW. + * Bit 5-31: Reserved. + */ +#define SNIC_HBA_CAP_DDL 0x02 /* Supports Dynamic Discovery of LUNs */ +#define SNIC_HBA_CAP_AEN 0x04 /* Supports Async Event Noitifcation */ +#define SNIC_HBA_CAP_TMO 0x08 /* Supports IO timeout in FW */ + +/* + * snic_exch_ver_rsp : firmware -> host + * + * Used by firmware to send response to version request + */ +struct snic_exch_ver_rsp { + __le32 version; + __le32 hid; + __le32 max_concur_ios; /* max concurrent ios */ + __le32 max_sgs_per_cmd; /* max sgls per IO */ + __le32 max_io_sz; /* max io size supported */ + __le32 hba_cap; /* hba capabilities */ + __le32 max_tgts; /* max tgts supported */ + __le16 io_timeout; /* FW extended timeout */ + u16 rsvd; +}; + + +/* + * snic_report_tgts : host -> firmware request + * + * Used by the host to request list of targets + */ +struct snic_report_tgts { + __le16 sg_cnt; + __le16 flags; /* specific flags from fw */ + u8 _resvd[4]; + __le64 sg_addr; /* Points to SGL */ + __le64 sense_addr; +}; + +enum snic_type { + SNIC_NONE = 0x0, + SNIC_DAS, + SNIC_SAN, +}; + + +/* Report Target Response */ +enum snic_tgt_type { + SNIC_TGT_NONE = 0x0, + SNIC_TGT_DAS, /* DAS Target */ + SNIC_TGT_SAN, /* SAN Target */ +}; + +/* target id format */ +struct snic_tgt_id { + __le32 tgt_id; /* target id */ + __le16 tgt_type; /* tgt type */ + __le16 vnic_id; /* corresponding vnic id */ +}; + +/* + * snic_report_tgts_cmpl : firmware -> host response + * + * Used by firmware to send response to Report Targets request + */ +struct snic_report_tgts_cmpl { + __le32 tgt_cnt; /* Number of Targets accessible */ + u32 _resvd; +}; + +/* + * Command flags + * + * Bit 0: Read flags + * Bit 1: Write flag + * Bit 2: ESGL - sg/esg array contains extended sg + * ESGE - is a host buffer contains sg elements + * Bit 3-4: Task Attributes + * 00b - simple + * 01b - head of queue + * 10b - ordered + * Bit 5-7: Priority - future use + * Bit 8-15: Reserved + */ + +#define SNIC_ICMND_WR 0x01 /* write command */ +#define SNIC_ICMND_RD 0x02 /* read command */ +#define SNIC_ICMND_ESGL 0x04 /* SGE/ESGE array contains valid data*/ + +/* + * Priority/Task Attribute settings + */ +#define SNIC_ICMND_TSK_SHIFT 2 /* task attr starts at bit 2 */ +#define SNIC_ICMND_TSK_MASK(x) ((x>>SNIC_ICMND_TSK_SHIFT) & ~(0xffff)) +#define SNIC_ICMND_TSK_SIMPLE 0 /* simple task attr */ +#define SNIC_ICMND_TSK_HEAD_OF_QUEUE 1 /* head of qeuue task attr */ +#define SNIC_ICMND_TSK_ORDERED 2 /* ordered task attr */ + +#define SNIC_ICMND_PRI_SHIFT 5 /* prio val starts at bit 5 */ + +/* + * snic_icmnd : host-> firmware request + * + * used for sending out an initiator SCSI 16/32-byte command + */ +struct snic_icmnd { + __le16 sg_cnt; /* Number of SG Elements */ + __le16 flags; /* flags */ + __le32 sense_len; /* Sense buffer length */ + __le64 tgt_id; /* Destination Target ID */ + __le64 lun_id; /* Destination LUN ID */ + u8 cdb_len; + u8 _resvd; + __le16 time_out; /* ms time for Res allocations fw to handle io*/ + __le32 data_len; /* Total number of bytes to be transferred */ + u8 cdb[SNIC_CDB_LEN]; + __le64 sg_addr; /* Points to SG List */ + __le64 sense_addr; /* Sense buffer address */ +}; + + +/* Response flags */ +/* Bit 0: Under run + * Bit 1: Over Run + * Bit 2-7: Reserved + */ +#define SNIC_ICMND_CMPL_UNDR_RUN 0x01 /* resid under and valid */ +#define SNIC_ICMND_CMPL_OVER_RUN 0x02 /* resid over and valid */ + +/* + * snic_icmnd_cmpl: firmware -> host response + * + * Used for sending the host a response to an icmnd (initiator command) + */ +struct snic_icmnd_cmpl { + u8 scsi_status; /* value as per SAM */ + u8 flags; + __le16 sense_len; /* Sense Length */ + __le32 resid; /* Residue : # bytes under or over run */ +}; + +/* + * snic_itmf: host->firmware request + * + * used for requesting the firmware to abort a request and/or send out + * a task management function + * + * the req_id field is valid in case of abort task and clear task + */ +struct snic_itmf { + u8 tm_type; /* SCSI Task Management request */ + u8 resvd; + __le16 flags; /* flags */ + __le32 req_id; /* Command id of snic req to be aborted */ + __le64 tgt_id; /* Target ID */ + __le64 lun_id; /* Destination LUN ID */ + __le16 timeout; /* in sec */ +}; + +/* + * Task Management Request + */ +enum snic_itmf_tm_type { + SNIC_ITMF_ABTS_TASK = 0x01, /* Abort Task */ + SNIC_ITMF_ABTS_TASK_SET, /* Abort Task Set */ + SNIC_ITMF_CLR_TASK, /* Clear Task */ + SNIC_ITMF_CLR_TASKSET, /* Clear Task Set */ + SNIC_ITMF_LUN_RESET, /* Lun Reset */ + SNIC_ITMF_ABTS_TASK_TERM, /* Supported for SAN Targets */ +}; + +/* + * snic_itmf_cmpl: firmware -> host resposne + * + * used for sending the host a response for a itmf request + */ +struct snic_itmf_cmpl { + __le32 nterminated; /* # IOs terminated as a result of tmf */ + u8 flags; /* flags */ + u8 _resvd[3]; +}; + +/* + * itmfl_cmpl flags + * Bit 0 : 1 - Num terminated field valid + * Bit 1 - 7 : Reserved + */ +#define SNIC_NUM_TERM_VALID 0x01 /* Number of IOs terminated */ + +/* + * snic_hba_reset: host -> firmware request + * + * used for requesting firmware to reset snic + */ +struct snic_hba_reset { + __le16 flags; /* flags */ + u8 _resvd[6]; +}; + +/* + * snic_hba_reset_cmpl: firmware -> host response + * + * Used by firmware to respond to the host's hba reset request + */ +struct snic_hba_reset_cmpl { + u8 flags; /* flags : more info needs to be added*/ + u8 _resvd[7]; +}; + +/* + * snic_notify_msg: firmware -> host response + * + * Used by firmware to notify host of the last work queue entry received + */ +struct snic_notify_msg { + __le32 wqe_num; /* wq entry number */ + u8 flags; /* flags, macros */ + u8 _resvd[4]; +}; + + +#define SNIC_EVDATA_LEN 24 /* in bytes */ +/* snic_async_evnotify: firmware -> host notification + * + * Used by firmware to notify the host about configuration/state changes + */ +struct snic_async_evnotify { + u8 FLS_EVENT_DESC; + u8 vnic; /* vnic id */ + u8 _resvd[2]; + __le32 ev_id; /* Event ID */ + u8 ev_data[SNIC_EVDATA_LEN]; /* Event Data */ + u8 _resvd2[4]; +}; + +/* async event flags */ +enum snic_ev_type { + SNIC_EV_TGT_OFFLINE = 0x01, /* Target Offline, PL contains TGT ID */ + SNIC_EV_TGT_ONLINE, /* Target Online, PL contains TGT ID */ + SNIC_EV_LUN_OFFLINE, /* LUN Offline, PL contains LUN ID */ + SNIC_EV_LUN_ONLINE, /* LUN Online, PL contains LUN ID */ + SNIC_EV_CONF_CHG, /* Dev Config/Attr Change Event */ + SNIC_EV_TGT_ADDED, /* Target Added */ + SNIC_EV_TGT_DELTD, /* Target Del'd, PL contains TGT ID */ + SNIC_EV_LUN_ADDED, /* LUN Added */ + SNIC_EV_LUN_DELTD, /* LUN Del'd, PL cont. TGT & LUN ID */ + + SNIC_EV_DISC_CMPL = 0x10, /* Discovery Completed Event */ +}; + + +#define SNIC_HOST_REQ_LEN 128 /*Exp length of host req, wq desc sz*/ +/* Payload 88 bytes = 128 - 24 - 16 */ +#define SNIC_HOST_REQ_PAYLOAD ((int)(SNIC_HOST_REQ_LEN - \ + sizeof(struct snic_io_hdr) - \ + (2 * sizeof(u64)))) + +/* + * snic_host_req: host -> firmware request + * + * Basic structure for all snic requests that are sent from the host to + * firmware. They are 128 bytes in size. + */ +struct snic_host_req { + u64 ctrl_data[2]; /*16 bytes - Control Data */ + struct snic_io_hdr hdr; + union { + /* + * Entry specific space, last byte contains color + */ + u8 buf[SNIC_HOST_REQ_PAYLOAD]; + + /* + * Exchange firmware version + */ + struct snic_exch_ver_req exch_ver; + + /* report targets */ + struct snic_report_tgts rpt_tgts; + + /* io request */ + struct snic_icmnd icmnd; + + /* task management request */ + struct snic_itmf itmf; + + /* hba reset */ + struct snic_hba_reset reset; + } u; +}; /* end of snic_host_req structure */ + + +#define SNIC_FW_REQ_LEN 64 /* Expected length of fw req */ +struct snic_fw_req { + struct snic_io_hdr hdr; + union { + /* + * Entry specific space, last byte contains color + */ + u8 buf[SNIC_FW_REQ_LEN - sizeof(struct snic_io_hdr)]; + + /* Exchange Version Response */ + struct snic_exch_ver_rsp exch_ver_cmpl; + + /* Report Targets Response */ + struct snic_report_tgts_cmpl rpt_tgts_cmpl; + + /* scsi response */ + struct snic_icmnd_cmpl icmnd_cmpl; + + /* task management response */ + struct snic_itmf_cmpl itmf_cmpl; + + /* hba reset response */ + struct snic_hba_reset_cmpl reset_cmpl; + + /* notify message */ + struct snic_notify_msg ack; + + /* async notification event */ + struct snic_async_evnotify async_ev; + + } u; +}; /* end of snic_fw_req structure */ + +/* + * Auxillary macro to verify specific snic req/cmpl structures + * to ensure that it will be aligned to 64 bit, and not using + * color bit field + */ +#define VERIFY_REQ_SZ(x) +#define VERIFY_CMPL_SZ(x) + +/* + * Access routines to encode and decode the color bit, which is the most + * significant bit of the structure. + */ +static inline void +snic_color_enc(struct snic_fw_req *req, u8 color) +{ + u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1; + + if (color) + *c |= 0x80; + else + *c &= ~0x80; +} + +static inline void +snic_color_dec(struct snic_fw_req *req, u8 *color) +{ + u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1; + + *color = *c >> 7; + + /* Make sure color bit is read from desc *before* other fields + * are read from desc. Hardware guarantees color bit is last + * bit (byte) written. Adding the rmb() prevents the compiler + * and/or CPU from reordering the reads which would potentially + * result in reading stale values. + */ + rmb(); +} +#endif /* end of __SNIC_FWINT_H */ diff --git a/kernel/drivers/scsi/snic/snic_io.c b/kernel/drivers/scsi/snic/snic_io.c new file mode 100644 index 000000000..993db7de4 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_io.c @@ -0,0 +1,518 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "snic_io.h" +#include "snic.h" +#include "cq_enet_desc.h" +#include "snic_fwint.h" + +static void +snic_wq_cmpl_frame_send(struct vnic_wq *wq, + struct cq_desc *cq_desc, + struct vnic_wq_buf *buf, + void *opaque) +{ + struct snic *snic = svnic_dev_priv(wq->vdev); + + SNIC_BUG_ON(buf->os_buf == NULL); + + if (snic_log_level & SNIC_DESC_LOGGING) + SNIC_HOST_INFO(snic->shost, + "Ack received for snic_host_req %p.\n", + buf->os_buf); + + SNIC_TRC(snic->shost->host_no, 0, 0, + ((ulong)(buf->os_buf) - sizeof(struct snic_req_info)), 0, 0, + 0); + pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE); + buf->os_buf = NULL; +} + +static int +snic_wq_cmpl_handler_cont(struct vnic_dev *vdev, + struct cq_desc *cq_desc, + u8 type, + u16 q_num, + u16 cmpl_idx, + void *opaque) +{ + struct snic *snic = svnic_dev_priv(vdev); + unsigned long flags; + + SNIC_BUG_ON(q_num != 0); + + spin_lock_irqsave(&snic->wq_lock[q_num], flags); + svnic_wq_service(&snic->wq[q_num], + cq_desc, + cmpl_idx, + snic_wq_cmpl_frame_send, + NULL); + spin_unlock_irqrestore(&snic->wq_lock[q_num], flags); + + return 0; +} /* end of snic_cmpl_handler_cont */ + +int +snic_wq_cmpl_handler(struct snic *snic, int work_to_do) +{ + unsigned int work_done = 0; + unsigned int i; + + snic->s_stats.misc.last_ack_time = jiffies; + for (i = 0; i < snic->wq_count; i++) { + work_done += svnic_cq_service(&snic->cq[i], + work_to_do, + snic_wq_cmpl_handler_cont, + NULL); + } + + return work_done; +} /* end of snic_wq_cmpl_handler */ + +void +snic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) +{ + + struct snic_host_req *req = buf->os_buf; + struct snic *snic = svnic_dev_priv(wq->vdev); + struct snic_req_info *rqi = NULL; + unsigned long flags; + + pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE); + + rqi = req_to_rqi(req); + spin_lock_irqsave(&snic->spl_cmd_lock, flags); + if (list_empty(&rqi->list)) { + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); + goto end; + } + + SNIC_BUG_ON(rqi->list.next == NULL); /* if not added to spl_cmd_list */ + list_del_init(&rqi->list); + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); + + if (rqi->sge_va) { + snic_pci_unmap_rsp_buf(snic, rqi); + kfree((void *)rqi->sge_va); + rqi->sge_va = 0; + } + snic_req_free(snic, rqi); + SNIC_HOST_INFO(snic->shost, "snic_free_wq_buf .. freed.\n"); + +end: + return; +} + +/* Criteria to select work queue in multi queue mode */ +static int +snic_select_wq(struct snic *snic) +{ + /* No multi queue support for now */ + BUILD_BUG_ON(SNIC_WQ_MAX > 1); + + return 0; +} + +int +snic_queue_wq_desc(struct snic *snic, void *os_buf, u16 len) +{ + dma_addr_t pa = 0; + unsigned long flags; + struct snic_fw_stats *fwstats = &snic->s_stats.fw; + long act_reqs; + int q_num = 0; + + snic_print_desc(__func__, os_buf, len); + + /* Map request buffer */ + pa = pci_map_single(snic->pdev, os_buf, len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(snic->pdev, pa)) { + SNIC_HOST_ERR(snic->shost, "qdesc: PCI DMA Mapping Fail.\n"); + + return -ENOMEM; + } + + q_num = snic_select_wq(snic); + + spin_lock_irqsave(&snic->wq_lock[q_num], flags); + if (!svnic_wq_desc_avail(snic->wq)) { + pci_unmap_single(snic->pdev, pa, len, PCI_DMA_TODEVICE); + spin_unlock_irqrestore(&snic->wq_lock[q_num], flags); + atomic64_inc(&snic->s_stats.misc.wq_alloc_fail); + SNIC_DBG("host = %d, WQ is Full\n", snic->shost->host_no); + + return -ENOMEM; + } + + snic_queue_wq_eth_desc(&snic->wq[q_num], os_buf, pa, len, 0, 0, 1); + spin_unlock_irqrestore(&snic->wq_lock[q_num], flags); + + /* Update stats */ + act_reqs = atomic64_inc_return(&fwstats->actv_reqs); + if (act_reqs > atomic64_read(&fwstats->max_actv_reqs)) + atomic64_set(&fwstats->max_actv_reqs, act_reqs); + + return 0; +} /* end of snic_queue_wq_desc() */ + +/* + * snic_handle_untagged_req: Adds snic specific requests to spl_cmd_list. + * Purpose : Used during driver unload to clean up the requests. + */ +void +snic_handle_untagged_req(struct snic *snic, struct snic_req_info *rqi) +{ + unsigned long flags; + + INIT_LIST_HEAD(&rqi->list); + + spin_lock_irqsave(&snic->spl_cmd_lock, flags); + list_add_tail(&rqi->list, &snic->spl_cmd_list); + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); +} + +/* + * snic_req_init: + * Allocates snic_req_info + snic_host_req + sgl data, and initializes. + */ +struct snic_req_info * +snic_req_init(struct snic *snic, int sg_cnt) +{ + u8 typ; + struct snic_req_info *rqi = NULL; + + typ = (sg_cnt <= SNIC_REQ_CACHE_DFLT_SGL) ? + SNIC_REQ_CACHE_DFLT_SGL : SNIC_REQ_CACHE_MAX_SGL; + + rqi = mempool_alloc(snic->req_pool[typ], GFP_ATOMIC); + if (!rqi) { + atomic64_inc(&snic->s_stats.io.alloc_fail); + SNIC_HOST_ERR(snic->shost, + "Failed to allocate memory from snic req pool id = %d\n", + typ); + return rqi; + } + + memset(rqi, 0, sizeof(*rqi)); + rqi->rq_pool_type = typ; + rqi->start_time = jiffies; + rqi->req = (struct snic_host_req *) (rqi + 1); + rqi->req_len = sizeof(struct snic_host_req); + rqi->snic = snic; + + rqi->req = (struct snic_host_req *)(rqi + 1); + + if (sg_cnt == 0) + goto end; + + rqi->req_len += (sg_cnt * sizeof(struct snic_sg_desc)); + + if (sg_cnt > atomic64_read(&snic->s_stats.io.max_sgl)) + atomic64_set(&snic->s_stats.io.max_sgl, sg_cnt); + + SNIC_BUG_ON(sg_cnt > SNIC_MAX_SG_DESC_CNT); + atomic64_inc(&snic->s_stats.io.sgl_cnt[sg_cnt - 1]); + +end: + memset(rqi->req, 0, rqi->req_len); + + /* pre initialization of init_ctx to support req_to_rqi */ + rqi->req->hdr.init_ctx = (ulong) rqi; + + SNIC_SCSI_DBG(snic->shost, "Req_alloc:rqi = %p allocatd.\n", rqi); + + return rqi; +} /* end of snic_req_init */ + +/* + * snic_abort_req_init : Inits abort request. + */ +struct snic_host_req * +snic_abort_req_init(struct snic *snic, struct snic_req_info *rqi) +{ + struct snic_host_req *req = NULL; + + SNIC_BUG_ON(!rqi); + + /* If abort to be issued second time, then reuse */ + if (rqi->abort_req) + return rqi->abort_req; + + + req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC); + if (!req) { + SNIC_HOST_ERR(snic->shost, "abts:Failed to alloc tm req.\n"); + WARN_ON_ONCE(1); + + return NULL; + } + + rqi->abort_req = req; + memset(req, 0, sizeof(struct snic_host_req)); + /* pre initialization of init_ctx to support req_to_rqi */ + req->hdr.init_ctx = (ulong) rqi; + + return req; +} /* end of snic_abort_req_init */ + +/* + * snic_dr_req_init : Inits device reset req + */ +struct snic_host_req * +snic_dr_req_init(struct snic *snic, struct snic_req_info *rqi) +{ + struct snic_host_req *req = NULL; + + SNIC_BUG_ON(!rqi); + + req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC); + if (!req) { + SNIC_HOST_ERR(snic->shost, "dr:Failed to alloc tm req.\n"); + WARN_ON_ONCE(1); + + return NULL; + } + + SNIC_BUG_ON(rqi->dr_req != NULL); + rqi->dr_req = req; + memset(req, 0, sizeof(struct snic_host_req)); + /* pre initialization of init_ctx to support req_to_rqi */ + req->hdr.init_ctx = (ulong) rqi; + + return req; +} /* end of snic_dr_req_init */ + +/* frees snic_req_info and snic_host_req */ +void +snic_req_free(struct snic *snic, struct snic_req_info *rqi) +{ + SNIC_BUG_ON(rqi->req == rqi->abort_req); + SNIC_BUG_ON(rqi->req == rqi->dr_req); + SNIC_BUG_ON(rqi->sge_va != 0); + + SNIC_SCSI_DBG(snic->shost, + "Req_free:rqi %p:ioreq %p:abt %p:dr %p\n", + rqi, rqi->req, rqi->abort_req, rqi->dr_req); + + if (rqi->abort_req) + mempool_free(rqi->abort_req, snic->req_pool[SNIC_REQ_TM_CACHE]); + + if (rqi->dr_req) + mempool_free(rqi->dr_req, snic->req_pool[SNIC_REQ_TM_CACHE]); + + mempool_free(rqi, snic->req_pool[rqi->rq_pool_type]); +} + +void +snic_pci_unmap_rsp_buf(struct snic *snic, struct snic_req_info *rqi) +{ + struct snic_sg_desc *sgd; + + sgd = req_to_sgl(rqi_to_req(rqi)); + SNIC_BUG_ON(sgd[0].addr == 0); + pci_unmap_single(snic->pdev, + le64_to_cpu(sgd[0].addr), + le32_to_cpu(sgd[0].len), + PCI_DMA_FROMDEVICE); +} + +/* + * snic_free_all_untagged_reqs: Walks through untagged reqs and frees them. + */ +void +snic_free_all_untagged_reqs(struct snic *snic) +{ + struct snic_req_info *rqi; + struct list_head *cur, *nxt; + unsigned long flags; + + spin_lock_irqsave(&snic->spl_cmd_lock, flags); + list_for_each_safe(cur, nxt, &snic->spl_cmd_list) { + rqi = list_entry(cur, struct snic_req_info, list); + list_del_init(&rqi->list); + if (rqi->sge_va) { + snic_pci_unmap_rsp_buf(snic, rqi); + kfree((void *)rqi->sge_va); + rqi->sge_va = 0; + } + + snic_req_free(snic, rqi); + } + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); +} + +/* + * snic_release_untagged_req : Unlinks the untagged req and frees it. + */ +void +snic_release_untagged_req(struct snic *snic, struct snic_req_info *rqi) +{ + unsigned long flags; + + spin_lock_irqsave(&snic->snic_lock, flags); + if (snic->in_remove) { + spin_unlock_irqrestore(&snic->snic_lock, flags); + goto end; + } + spin_unlock_irqrestore(&snic->snic_lock, flags); + + spin_lock_irqsave(&snic->spl_cmd_lock, flags); + if (list_empty(&rqi->list)) { + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); + goto end; + } + list_del_init(&rqi->list); + spin_unlock_irqrestore(&snic->spl_cmd_lock, flags); + snic_req_free(snic, rqi); + +end: + return; +} + +/* dump buf in hex fmt */ +void +snic_hex_dump(char *pfx, char *data, int len) +{ + SNIC_INFO("%s Dumping Data of Len = %d\n", pfx, len); + print_hex_dump_bytes(pfx, DUMP_PREFIX_NONE, data, len); +} + +#define LINE_BUFSZ 128 /* for snic_print_desc fn */ +static void +snic_dump_desc(const char *fn, char *os_buf, int len) +{ + struct snic_host_req *req = (struct snic_host_req *) os_buf; + struct snic_fw_req *fwreq = (struct snic_fw_req *) os_buf; + struct snic_req_info *rqi = NULL; + char line[LINE_BUFSZ] = { '\0' }; + char *cmd_str = NULL; + + if (req->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL) + rqi = (struct snic_req_info *) fwreq->hdr.init_ctx; + else + rqi = (struct snic_req_info *) req->hdr.init_ctx; + + SNIC_BUG_ON(rqi == NULL || rqi->req == NULL); + switch (req->hdr.type) { + case SNIC_REQ_REPORT_TGTS: + cmd_str = "report-tgt : "; + snprintf(line, LINE_BUFSZ, "SNIC_REQ_REPORT_TGTS :"); + break; + + case SNIC_REQ_ICMND: + cmd_str = "icmnd : "; + snprintf(line, LINE_BUFSZ, "SNIC_REQ_ICMND : 0x%x :", + req->u.icmnd.cdb[0]); + break; + + case SNIC_REQ_ITMF: + cmd_str = "itmf : "; + snprintf(line, LINE_BUFSZ, "SNIC_REQ_ITMF :"); + break; + + case SNIC_REQ_HBA_RESET: + cmd_str = "hba reset :"; + snprintf(line, LINE_BUFSZ, "SNIC_REQ_HBA_RESET :"); + break; + + case SNIC_REQ_EXCH_VER: + cmd_str = "exch ver : "; + snprintf(line, LINE_BUFSZ, "SNIC_REQ_EXCH_VER :"); + break; + + case SNIC_REQ_TGT_INFO: + cmd_str = "tgt info : "; + break; + + case SNIC_RSP_REPORT_TGTS_CMPL: + cmd_str = "report tgt cmpl : "; + snprintf(line, LINE_BUFSZ, "SNIC_RSP_REPORT_TGTS_CMPL :"); + break; + + case SNIC_RSP_ICMND_CMPL: + cmd_str = "icmnd_cmpl : "; + snprintf(line, LINE_BUFSZ, "SNIC_RSP_ICMND_CMPL : 0x%x :", + rqi->req->u.icmnd.cdb[0]); + break; + + case SNIC_RSP_ITMF_CMPL: + cmd_str = "itmf_cmpl : "; + snprintf(line, LINE_BUFSZ, "SNIC_RSP_ITMF_CMPL :"); + break; + + case SNIC_RSP_HBA_RESET_CMPL: + cmd_str = "hba_reset_cmpl : "; + snprintf(line, LINE_BUFSZ, "SNIC_RSP_HBA_RESET_CMPL :"); + break; + + case SNIC_RSP_EXCH_VER_CMPL: + cmd_str = "exch_ver_cmpl : "; + snprintf(line, LINE_BUFSZ, "SNIC_RSP_EXCH_VER_CMPL :"); + break; + + case SNIC_MSG_ACK: + cmd_str = "msg ack : "; + snprintf(line, LINE_BUFSZ, "SNIC_MSG_ACK :"); + break; + + case SNIC_MSG_ASYNC_EVNOTIFY: + cmd_str = "async notify : "; + snprintf(line, LINE_BUFSZ, "SNIC_MSG_ASYNC_EVNOTIFY :"); + break; + + default: + cmd_str = "unknown : "; + SNIC_BUG_ON(1); + break; + } + + SNIC_INFO("%s:%s >>cmndid=%x:sg_cnt = %x:status = %x:ctx = %lx.\n", + fn, line, req->hdr.cmnd_id, req->hdr.sg_cnt, req->hdr.status, + req->hdr.init_ctx); + + /* Enable it, to dump byte stream */ + if (snic_log_level & 0x20) + snic_hex_dump(cmd_str, os_buf, len); +} /* end of __snic_print_desc */ + +void +snic_print_desc(const char *fn, char *os_buf, int len) +{ + if (snic_log_level & SNIC_DESC_LOGGING) + snic_dump_desc(fn, os_buf, len); +} + +void +snic_calc_io_process_time(struct snic *snic, struct snic_req_info *rqi) +{ + u64 duration; + + duration = jiffies - rqi->start_time; + + if (duration > atomic64_read(&snic->s_stats.io.max_time)) + atomic64_set(&snic->s_stats.io.max_time, duration); +} diff --git a/kernel/drivers/scsi/snic/snic_io.h b/kernel/drivers/scsi/snic/snic_io.h new file mode 100644 index 000000000..093d6524c --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_io.h @@ -0,0 +1,118 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _SNIC_IO_H +#define _SNIC_IO_H + +#define SNIC_DFLT_SG_DESC_CNT 32 /* Default descriptors for sgl */ +#define SNIC_MAX_SG_DESC_CNT 60 /* Max descriptor for sgl */ +#define SNIC_SG_DESC_ALIGN 16 /* Descriptor address alignment */ + +/* SG descriptor for snic */ +struct snic_sg_desc { + __le64 addr; + __le32 len; + u32 _resvd; +}; + +struct snic_dflt_sgl { + struct snic_sg_desc sg_desc[SNIC_DFLT_SG_DESC_CNT]; +}; + +struct snic_max_sgl { + struct snic_sg_desc sg_desc[SNIC_MAX_SG_DESC_CNT]; +}; + +enum snic_req_cache_type { + SNIC_REQ_CACHE_DFLT_SGL = 0, /* cache with default size sgl */ + SNIC_REQ_CACHE_MAX_SGL, /* cache with max size sgl */ + SNIC_REQ_TM_CACHE, /* cache for task mgmt reqs contains + snic_host_req objects only*/ + SNIC_REQ_MAX_CACHES /* number of sgl caches */ +}; + +/* Per IO internal state */ +struct snic_internal_io_state { + char *rqi; + u64 flags; + u32 state; + u32 abts_status; /* Abort completion status */ + u32 lr_status; /* device reset completion status */ +}; + +/* IO state machine */ +enum snic_ioreq_state { + SNIC_IOREQ_NOT_INITED = 0, + SNIC_IOREQ_PENDING, + SNIC_IOREQ_ABTS_PENDING, + SNIC_IOREQ_ABTS_COMPLETE, + SNIC_IOREQ_LR_PENDING, + SNIC_IOREQ_LR_COMPLETE, + SNIC_IOREQ_COMPLETE, +}; + +struct snic; +struct snic_host_req; + +/* + * snic_req_info : Contains info about IO, one per scsi command. + * Notes: Make sure that the structure is aligned to 16 B + * this helps in easy access to snic_req_info from snic_host_req + */ +struct snic_req_info { + struct list_head list; + struct snic_host_req *req; + u64 start_time; /* start time in jiffies */ + u16 rq_pool_type; /* noticion of request pool type */ + u16 req_len; /* buf len passing to fw (req + sgl)*/ + u32 tgt_id; + + u32 tm_tag; + u8 io_cmpl:1; /* sets to 1 when fw completes IO */ + u8 resvd[3]; + struct scsi_cmnd *sc; /* Associated scsi cmd */ + struct snic *snic; /* Associated snic */ + ulong sge_va; /* Pointer to Resp Buffer */ + u64 snsbuf_va; + + struct snic_host_req *abort_req; + struct completion *abts_done; + + struct snic_host_req *dr_req; + struct completion *dr_done; +}; + + +#define rqi_to_req(rqi) \ + ((struct snic_host_req *) (((struct snic_req_info *)rqi)->req)) + +#define req_to_rqi(req) \ + ((struct snic_req_info *) (((struct snic_host_req *)req)->hdr.init_ctx)) + +#define req_to_sgl(req) \ + ((struct snic_sg_desc *) (((struct snic_host_req *)req)+1)) + +struct snic_req_info * +snic_req_init(struct snic *, int sg_cnt); +void snic_req_free(struct snic *, struct snic_req_info *); +void snic_calc_io_process_time(struct snic *, struct snic_req_info *); +void snic_pci_unmap_rsp_buf(struct snic *, struct snic_req_info *); +struct snic_host_req * +snic_abort_req_init(struct snic *, struct snic_req_info *); +struct snic_host_req * +snic_dr_req_init(struct snic *, struct snic_req_info *); +#endif /* _SNIC_IO_H */ diff --git a/kernel/drivers/scsi/snic/snic_isr.c b/kernel/drivers/scsi/snic/snic_isr.c new file mode 100644 index 000000000..a85fae25e --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_isr.c @@ -0,0 +1,204 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "vnic_dev.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "snic_io.h" +#include "snic.h" + + +/* + * snic_isr_msix_wq : MSIx ISR for work queue. + */ + +static irqreturn_t +snic_isr_msix_wq(int irq, void *data) +{ + struct snic *snic = data; + unsigned long wq_work_done = 0; + + snic->s_stats.misc.last_isr_time = jiffies; + atomic64_inc(&snic->s_stats.misc.isr_cnt); + + wq_work_done = snic_wq_cmpl_handler(snic, -1); + svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ], + wq_work_done, + 1 /* unmask intr */, + 1 /* reset intr timer */); + + return IRQ_HANDLED; +} /* end of snic_isr_msix_wq */ + +static irqreturn_t +snic_isr_msix_io_cmpl(int irq, void *data) +{ + struct snic *snic = data; + unsigned long iocmpl_work_done = 0; + + snic->s_stats.misc.last_isr_time = jiffies; + atomic64_inc(&snic->s_stats.misc.isr_cnt); + + iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1); + svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL], + iocmpl_work_done, + 1 /* unmask intr */, + 1 /* reset intr timer */); + + return IRQ_HANDLED; +} /* end of snic_isr_msix_io_cmpl */ + +static irqreturn_t +snic_isr_msix_err_notify(int irq, void *data) +{ + struct snic *snic = data; + + snic->s_stats.misc.last_isr_time = jiffies; + atomic64_inc(&snic->s_stats.misc.isr_cnt); + + svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]); + snic_log_q_error(snic); + + /*Handling link events */ + snic_handle_link_event(snic); + + return IRQ_HANDLED; +} /* end of snic_isr_msix_err_notify */ + + +void +snic_free_intr(struct snic *snic) +{ + int i; + + /* ONLY interrupt mode MSIX is supported */ + for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { + if (snic->msix[i].requested) { + free_irq(snic->msix_entry[i].vector, + snic->msix[i].devid); + } + } +} /* end of snic_free_intr */ + +int +snic_request_intr(struct snic *snic) +{ + int ret = 0, i; + enum vnic_dev_intr_mode intr_mode; + + intr_mode = svnic_dev_get_intr_mode(snic->vdev); + SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX); + + /* + * Currently HW supports single WQ and CQ. So passing devid as snic. + * When hardware supports multiple WQs and CQs, one idea is + * to pass devid as corresponding WQ or CQ ptr and retrieve snic + * from queue ptr. + * Except for err_notify, which is always one. + */ + sprintf(snic->msix[SNIC_MSIX_WQ].devname, + "%.11s-scsi-wq", + snic->name); + snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq; + snic->msix[SNIC_MSIX_WQ].devid = snic; + + sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname, + "%.11s-io-cmpl", + snic->name); + snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl; + snic->msix[SNIC_MSIX_IO_CMPL].devid = snic; + + sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname, + "%.11s-err-notify", + snic->name); + snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify; + snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic; + + for (i = 0; i < ARRAY_SIZE(snic->msix); i++) { + ret = request_irq(snic->msix_entry[i].vector, + snic->msix[i].isr, + 0, + snic->msix[i].devname, + snic->msix[i].devid); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "MSI-X: requrest_irq(%d) failed %d\n", + i, + ret); + snic_free_intr(snic); + break; + } + snic->msix[i].requested = 1; + } + + return ret; +} /* end of snic_requrest_intr */ + +int +snic_set_intr_mode(struct snic *snic) +{ + unsigned int n = ARRAY_SIZE(snic->wq); + unsigned int m = SNIC_CQ_IO_CMPL_MAX; + unsigned int i; + + /* + * We need n WQs, m CQs, and n+m+1 INTRs + * (last INTR is used for WQ/CQ errors and notification area + */ + + BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) > + ARRAY_SIZE(snic->intr)); + SNIC_BUG_ON(ARRAY_SIZE(snic->msix_entry) < (n + m + 1)); + + for (i = 0; i < (n + m + 1); i++) + snic->msix_entry[i].entry = i; + + if (snic->wq_count >= n && snic->cq_count >= (n + m)) { + if (!pci_enable_msix(snic->pdev, + snic->msix_entry, + (n + m + 1))) { + snic->wq_count = n; + snic->cq_count = n + m; + snic->intr_count = n + m + 1; + snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY; + + SNIC_ISR_DBG(snic->shost, + "Using MSI-X Interrupts\n"); + svnic_dev_set_intr_mode(snic->vdev, + VNIC_DEV_INTR_MODE_MSIX); + + return 0; + } + } + + svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN); + + return -EINVAL; +} /* end of snic_set_intr_mode */ + +void +snic_clear_intr_mode(struct snic *snic) +{ + pci_disable_msix(snic->pdev); + + svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX); +} diff --git a/kernel/drivers/scsi/snic/snic_main.c b/kernel/drivers/scsi/snic/snic_main.c new file mode 100644 index 000000000..2b3c25371 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_main.c @@ -0,0 +1,1034 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snic.h" +#include "snic_fwint.h" + +#define PCI_DEVICE_ID_CISCO_SNIC 0x0046 + +/* Supported devices by snic module */ +static struct pci_device_id snic_id_table[] = { + {PCI_DEVICE(0x1137, PCI_DEVICE_ID_CISCO_SNIC) }, + { 0, } /* end of table */ +}; + +unsigned int snic_log_level = 0x0; +module_param(snic_log_level, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(snic_log_level, "bitmask for snic logging levels"); + +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS +unsigned int snic_trace_max_pages = 16; +module_param(snic_trace_max_pages, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(snic_trace_max_pages, + "Total allocated memory pages for snic trace buffer"); + +#endif +unsigned int snic_max_qdepth = SNIC_DFLT_QUEUE_DEPTH; +module_param(snic_max_qdepth, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(snic_max_qdepth, "Queue depth to report for each LUN"); + +/* + * snic_slave_alloc : callback function to SCSI Mid Layer, called on + * scsi device initialization. + */ +static int +snic_slave_alloc(struct scsi_device *sdev) +{ + struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev)); + + if (!tgt || snic_tgt_chkready(tgt)) + return -ENXIO; + + return 0; +} + +/* + * snic_slave_configure : callback function to SCSI Mid Layer, called on + * scsi device initialization. + */ +static int +snic_slave_configure(struct scsi_device *sdev) +{ + struct snic *snic = shost_priv(sdev->host); + u32 qdepth = 0, max_ios = 0; + int tmo = SNIC_DFLT_CMD_TIMEOUT * HZ; + + /* Set Queue Depth */ + max_ios = snic_max_qdepth; + qdepth = min_t(u32, max_ios, SNIC_MAX_QUEUE_DEPTH); + scsi_change_queue_depth(sdev, qdepth); + + if (snic->fwinfo.io_tmo > 1) + tmo = snic->fwinfo.io_tmo * HZ; + + /* FW requires extended timeouts */ + blk_queue_rq_timeout(sdev->request_queue, tmo); + + return 0; +} + +static int +snic_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + int qsz = 0; + + qsz = min_t(u32, qdepth, SNIC_MAX_QUEUE_DEPTH); + scsi_change_queue_depth(sdev, qsz); + SNIC_INFO("QDepth Changed to %d\n", sdev->queue_depth); + + return sdev->queue_depth; +} + +static struct scsi_host_template snic_host_template = { + .module = THIS_MODULE, + .name = SNIC_DRV_NAME, + .queuecommand = snic_queuecommand, + .eh_abort_handler = snic_abort_cmd, + .eh_device_reset_handler = snic_device_reset, + .eh_host_reset_handler = snic_host_reset, + .slave_alloc = snic_slave_alloc, + .slave_configure = snic_slave_configure, + .change_queue_depth = snic_change_queue_depth, + .this_id = -1, + .cmd_per_lun = SNIC_DFLT_QUEUE_DEPTH, + .can_queue = SNIC_MAX_IO_REQ, + .use_clustering = ENABLE_CLUSTERING, + .sg_tablesize = SNIC_MAX_SG_DESC_CNT, + .max_sectors = 0x800, + .shost_attrs = snic_attrs, + .track_queue_depth = 1, + .cmd_size = sizeof(struct snic_internal_io_state), + .proc_name = "snic_scsi", +}; + +/* + * snic_handle_link_event : Handles link events such as link up/down/error + */ +void +snic_handle_link_event(struct snic *snic) +{ + unsigned long flags; + + spin_lock_irqsave(&snic->snic_lock, flags); + if (snic->stop_link_events) { + spin_unlock_irqrestore(&snic->snic_lock, flags); + + return; + } + spin_unlock_irqrestore(&snic->snic_lock, flags); + + queue_work(snic_glob->event_q, &snic->link_work); +} /* end of snic_handle_link_event */ + +/* + * snic_notify_set : sets notification area + * This notification area is to receive events from fw + * Note: snic supports only MSIX interrupts, in which we can just call + * svnic_dev_notify_set directly + */ +static int +snic_notify_set(struct snic *snic) +{ + int ret = 0; + enum vnic_dev_intr_mode intr_mode; + + intr_mode = svnic_dev_get_intr_mode(snic->vdev); + + if (intr_mode == VNIC_DEV_INTR_MODE_MSIX) { + ret = svnic_dev_notify_set(snic->vdev, SNIC_MSIX_ERR_NOTIFY); + } else { + SNIC_HOST_ERR(snic->shost, + "Interrupt mode should be setup before devcmd notify set %d\n", + intr_mode); + ret = -1; + } + + return ret; +} /* end of snic_notify_set */ + +/* + * snic_dev_wait : polls vnic open status. + */ +static int +snic_dev_wait(struct vnic_dev *vdev, + int (*start)(struct vnic_dev *, int), + int (*finished)(struct vnic_dev *, int *), + int arg) +{ + unsigned long time; + int ret, done; + int retry_cnt = 0; + + ret = start(vdev, arg); + if (ret) + return ret; + + /* + * Wait for func to complete...2 seconds max. + * + * Sometimes schedule_timeout_uninterruptible take long time + * to wakeup, which results skipping retry. The retry counter + * ensures to retry at least two times. + */ + time = jiffies + (HZ * 2); + do { + ret = finished(vdev, &done); + if (ret) + return ret; + + if (done) + return 0; + schedule_timeout_uninterruptible(HZ/10); + ++retry_cnt; + } while (time_after(time, jiffies) || (retry_cnt < 3)); + + return -ETIMEDOUT; +} /* end of snic_dev_wait */ + +/* + * snic_cleanup: called by snic_remove + * Stops the snic device, masks all interrupts, Completed CQ entries are + * drained. Posted WQ/RQ/Copy-WQ entries are cleanup + */ +static int +snic_cleanup(struct snic *snic) +{ + unsigned int i; + int ret; + + svnic_dev_disable(snic->vdev); + for (i = 0; i < snic->intr_count; i++) + svnic_intr_mask(&snic->intr[i]); + + for (i = 0; i < snic->wq_count; i++) { + ret = svnic_wq_disable(&snic->wq[i]); + if (ret) + return ret; + } + + /* Clean up completed IOs */ + snic_fwcq_cmpl_handler(snic, -1); + + snic_wq_cmpl_handler(snic, -1); + + /* Clean up the IOs that have not completed */ + for (i = 0; i < snic->wq_count; i++) + svnic_wq_clean(&snic->wq[i], snic_free_wq_buf); + + for (i = 0; i < snic->cq_count; i++) + svnic_cq_clean(&snic->cq[i]); + + for (i = 0; i < snic->intr_count; i++) + svnic_intr_clean(&snic->intr[i]); + + /* Cleanup snic specific requests */ + snic_free_all_untagged_reqs(snic); + + /* Cleanup Pending SCSI commands */ + snic_shutdown_scsi_cleanup(snic); + + for (i = 0; i < SNIC_REQ_MAX_CACHES; i++) + mempool_destroy(snic->req_pool[i]); + + return 0; +} /* end of snic_cleanup */ + + +static void +snic_iounmap(struct snic *snic) +{ + if (snic->bar0.vaddr) + iounmap(snic->bar0.vaddr); +} + +/* + * snic_vdev_open_done : polls for svnic_dev_open cmd completion. + */ +static int +snic_vdev_open_done(struct vnic_dev *vdev, int *done) +{ + struct snic *snic = svnic_dev_priv(vdev); + int ret; + int nretries = 5; + + do { + ret = svnic_dev_open_done(vdev, done); + if (ret == 0) + break; + + SNIC_HOST_INFO(snic->shost, "VNIC_DEV_OPEN Timedout.\n"); + } while (nretries--); + + return ret; +} /* end of snic_vdev_open_done */ + +/* + * snic_add_host : registers scsi host with ML + */ +static int +snic_add_host(struct Scsi_Host *shost, struct pci_dev *pdev) +{ + int ret = 0; + + ret = scsi_add_host(shost, &pdev->dev); + if (ret) { + SNIC_HOST_ERR(shost, + "snic: scsi_add_host failed. %d\n", + ret); + + return ret; + } + + SNIC_BUG_ON(shost->work_q != NULL); + snprintf(shost->work_q_name, sizeof(shost->work_q_name), "scsi_wq_%d", + shost->host_no); + shost->work_q = create_singlethread_workqueue(shost->work_q_name); + if (!shost->work_q) { + SNIC_HOST_ERR(shost, "Failed to Create ScsiHost wq.\n"); + + ret = -ENOMEM; + } + + return ret; +} /* end of snic_add_host */ + +static void +snic_del_host(struct Scsi_Host *shost) +{ + if (!shost->work_q) + return; + + destroy_workqueue(shost->work_q); + shost->work_q = NULL; + scsi_remove_host(shost); +} + +int +snic_get_state(struct snic *snic) +{ + return atomic_read(&snic->state); +} + +void +snic_set_state(struct snic *snic, enum snic_state state) +{ + SNIC_HOST_INFO(snic->shost, "snic state change from %s to %s\n", + snic_state_to_str(snic_get_state(snic)), + snic_state_to_str(state)); + + atomic_set(&snic->state, state); +} + +/* + * snic_probe : Initialize the snic interface. + */ +static int +snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct Scsi_Host *shost; + struct snic *snic; + mempool_t *pool; + unsigned long flags; + u32 max_ios = 0; + int ret, i; + + /* Device Information */ + SNIC_INFO("snic device %4x:%4x:%4x:%4x: ", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + + SNIC_INFO("snic device bus %x: slot %x: fn %x\n", + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + + /* + * Allocate SCSI Host and setup association between host, and snic + */ + shost = scsi_host_alloc(&snic_host_template, sizeof(struct snic)); + if (!shost) { + SNIC_ERR("Unable to alloc scsi_host\n"); + ret = -ENOMEM; + + goto prob_end; + } + snic = shost_priv(shost); + snic->shost = shost; + + snprintf(snic->name, sizeof(snic->name) - 1, "%s%d", SNIC_DRV_NAME, + shost->host_no); + + SNIC_HOST_INFO(shost, + "snic%d = %p shost = %p device bus %x: slot %x: fn %x\n", + shost->host_no, snic, shost, pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + /* Per snic debugfs init */ + ret = snic_stats_debugfs_init(snic); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "Failed to initialize debugfs stats\n"); + snic_stats_debugfs_remove(snic); + } +#endif + + /* Setup PCI Resources */ + pci_set_drvdata(pdev, snic); + snic->pdev = pdev; + + ret = pci_enable_device(pdev); + if (ret) { + SNIC_HOST_ERR(shost, + "Cannot enable PCI Resources, aborting : %d\n", + ret); + + goto err_free_snic; + } + + ret = pci_request_regions(pdev, SNIC_DRV_NAME); + if (ret) { + SNIC_HOST_ERR(shost, + "Cannot obtain PCI Resources, aborting : %d\n", + ret); + + goto err_pci_disable; + } + + pci_set_master(pdev); + + /* + * Query PCI Controller on system for DMA addressing + * limitation for the device. Try 43-bit first, and + * fail to 32-bit. + */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(43)); + if (ret) { + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + SNIC_HOST_ERR(shost, + "No Usable DMA Configuration, aborting %d\n", + ret); + + goto err_rel_regions; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + SNIC_HOST_ERR(shost, + "Unable to obtain 32-bit DMA for consistent allocations, aborting: %d\n", + ret); + + goto err_rel_regions; + } + } else { + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(43)); + if (ret) { + SNIC_HOST_ERR(shost, + "Unable to obtain 43-bit DMA for consistent allocations. aborting: %d\n", + ret); + + goto err_rel_regions; + } + } + + + /* Map vNIC resources from BAR0 */ + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { + SNIC_HOST_ERR(shost, "BAR0 not memory mappable aborting.\n"); + + ret = -ENODEV; + goto err_rel_regions; + } + + snic->bar0.vaddr = pci_iomap(pdev, 0, 0); + if (!snic->bar0.vaddr) { + SNIC_HOST_ERR(shost, + "Cannot memory map BAR0 res hdr aborting.\n"); + + ret = -ENODEV; + goto err_rel_regions; + } + + snic->bar0.bus_addr = pci_resource_start(pdev, 0); + snic->bar0.len = pci_resource_len(pdev, 0); + SNIC_BUG_ON(snic->bar0.bus_addr == 0); + + /* Devcmd2 Resource Allocation and Initialization */ + snic->vdev = svnic_dev_alloc_discover(NULL, snic, pdev, &snic->bar0, 1); + if (!snic->vdev) { + SNIC_HOST_ERR(shost, "vNIC Resource Discovery Failed.\n"); + + ret = -ENODEV; + goto err_iounmap; + } + + ret = svnic_dev_cmd_init(snic->vdev, 0); + if (ret) { + SNIC_HOST_INFO(shost, "Devcmd2 Init Failed. err = %d\n", ret); + + goto err_vnic_unreg; + } + + ret = snic_dev_wait(snic->vdev, svnic_dev_open, snic_vdev_open_done, 0); + if (ret) { + SNIC_HOST_ERR(shost, + "vNIC dev open failed, aborting. %d\n", + ret); + + goto err_vnic_unreg; + } + + ret = svnic_dev_init(snic->vdev, 0); + if (ret) { + SNIC_HOST_ERR(shost, + "vNIC dev init failed. aborting. %d\n", + ret); + + goto err_dev_close; + } + + /* Get vNIC information */ + ret = snic_get_vnic_config(snic); + if (ret) { + SNIC_HOST_ERR(shost, + "Get vNIC configuration failed, aborting. %d\n", + ret); + + goto err_dev_close; + } + + /* Configure Maximum Outstanding IO reqs */ + max_ios = snic->config.io_throttle_count; + if (max_ios != SNIC_UCSM_DFLT_THROTTLE_CNT_BLD) + shost->can_queue = min_t(u32, SNIC_MAX_IO_REQ, + max_t(u32, SNIC_MIN_IO_REQ, max_ios)); + + snic->max_tag_id = shost->can_queue; + + shost->max_lun = snic->config.luns_per_tgt; + shost->max_id = SNIC_MAX_TARGET; + + shost->max_cmd_len = MAX_COMMAND_SIZE; /*defined in scsi_cmnd.h*/ + + snic_get_res_counts(snic); + + /* + * Assumption: Only MSIx is supported + */ + ret = snic_set_intr_mode(snic); + if (ret) { + SNIC_HOST_ERR(shost, + "Failed to set intr mode aborting. %d\n", + ret); + + goto err_dev_close; + } + + ret = snic_alloc_vnic_res(snic); + if (ret) { + SNIC_HOST_ERR(shost, + "Failed to alloc vNIC resources aborting. %d\n", + ret); + + goto err_clear_intr; + } + + /* Initialize specific lists */ + INIT_LIST_HEAD(&snic->list); + + /* + * spl_cmd_list for maintaining snic specific cmds + * such as EXCH_VER_REQ, REPORT_TARGETS etc + */ + INIT_LIST_HEAD(&snic->spl_cmd_list); + spin_lock_init(&snic->spl_cmd_lock); + + /* initialize all snic locks */ + spin_lock_init(&snic->snic_lock); + + for (i = 0; i < SNIC_WQ_MAX; i++) + spin_lock_init(&snic->wq_lock[i]); + + for (i = 0; i < SNIC_IO_LOCKS; i++) + spin_lock_init(&snic->io_req_lock[i]); + + pool = mempool_create_slab_pool(2, + snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); + if (!pool) { + SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n"); + + goto err_free_res; + } + + snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL] = pool; + + pool = mempool_create_slab_pool(2, + snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); + if (!pool) { + SNIC_HOST_ERR(shost, "max sgl pool creation failed\n"); + + goto err_free_dflt_sgl_pool; + } + + snic->req_pool[SNIC_REQ_CACHE_MAX_SGL] = pool; + + pool = mempool_create_slab_pool(2, + snic_glob->req_cache[SNIC_REQ_TM_CACHE]); + if (!pool) { + SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n"); + + goto err_free_max_sgl_pool; + } + + snic->req_pool[SNIC_REQ_TM_CACHE] = pool; + + /* Initialize snic state */ + atomic_set(&snic->state, SNIC_INIT); + + atomic_set(&snic->ios_inflight, 0); + + /* Setup notification buffer area */ + ret = snic_notify_set(snic); + if (ret) { + SNIC_HOST_ERR(shost, + "Failed to alloc notify buffer aborting. %d\n", + ret); + + goto err_free_tmreq_pool; + } + + /* + * Initialization done with PCI system, hardware, firmware. + * Add shost to SCSI + */ + ret = snic_add_host(shost, pdev); + if (ret) { + SNIC_HOST_ERR(shost, + "Adding scsi host Failed ... exiting. %d\n", + ret); + + goto err_notify_unset; + } + + spin_lock_irqsave(&snic_glob->snic_list_lock, flags); + list_add_tail(&snic->list, &snic_glob->snic_list); + spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags); + + snic_disc_init(&snic->disc); + INIT_WORK(&snic->tgt_work, snic_handle_tgt_disc); + INIT_WORK(&snic->disc_work, snic_handle_disc); + INIT_WORK(&snic->link_work, snic_handle_link); + + /* Enable all queues */ + for (i = 0; i < snic->wq_count; i++) + svnic_wq_enable(&snic->wq[i]); + + ret = svnic_dev_enable_wait(snic->vdev); + if (ret) { + SNIC_HOST_ERR(shost, + "vNIC dev enable failed w/ error %d\n", + ret); + + goto err_vdev_enable; + } + + ret = snic_request_intr(snic); + if (ret) { + SNIC_HOST_ERR(shost, "Unable to request irq. %d\n", ret); + + goto err_req_intr; + } + + for (i = 0; i < snic->intr_count; i++) + svnic_intr_unmask(&snic->intr[i]); + + snic_set_state(snic, SNIC_ONLINE); + + /* Get snic params */ + ret = snic_get_conf(snic); + if (ret) { + SNIC_HOST_ERR(shost, + "Failed to get snic io config from FW w err %d\n", + ret); + + goto err_get_conf; + } + + ret = snic_disc_start(snic); + if (ret) { + SNIC_HOST_ERR(shost, "snic_probe:Discovery Failed w err = %d\n", + ret); + + goto err_get_conf; + } + + SNIC_HOST_INFO(shost, "SNIC Device Probe Successful.\n"); + + return 0; + +err_get_conf: + snic_free_all_untagged_reqs(snic); + + for (i = 0; i < snic->intr_count; i++) + svnic_intr_mask(&snic->intr[i]); + + snic_free_intr(snic); + +err_req_intr: + svnic_dev_disable(snic->vdev); + +err_vdev_enable: + for (i = 0; i < snic->wq_count; i++) { + int rc = 0; + + rc = svnic_wq_disable(&snic->wq[i]); + if (rc) { + SNIC_HOST_ERR(shost, + "WQ Disable Failed w/ err = %d\n", rc); + + break; + } + } + snic_del_host(snic->shost); + +err_notify_unset: + svnic_dev_notify_unset(snic->vdev); + +err_free_tmreq_pool: + mempool_destroy(snic->req_pool[SNIC_REQ_TM_CACHE]); + +err_free_max_sgl_pool: + mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_MAX_SGL]); + +err_free_dflt_sgl_pool: + mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL]); + +err_free_res: + snic_free_vnic_res(snic); + +err_clear_intr: + snic_clear_intr_mode(snic); + +err_dev_close: + svnic_dev_close(snic->vdev); + +err_vnic_unreg: + svnic_dev_unregister(snic->vdev); + +err_iounmap: + snic_iounmap(snic); + +err_rel_regions: + pci_release_regions(pdev); + +err_pci_disable: + pci_disable_device(pdev); + +err_free_snic: +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + snic_stats_debugfs_remove(snic); +#endif + scsi_host_put(shost); + pci_set_drvdata(pdev, NULL); + +prob_end: + SNIC_INFO("sNIC device : bus %d: slot %d: fn %d Registration Failed.\n", + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + + return ret; +} /* end of snic_probe */ + + +/* + * snic_remove : invoked on unbinding the interface to cleanup the + * resources allocated in snic_probe on initialization. + */ +static void +snic_remove(struct pci_dev *pdev) +{ + struct snic *snic = pci_get_drvdata(pdev); + unsigned long flags; + + if (!snic) { + SNIC_INFO("sNIC dev: bus %d slot %d fn %d snic inst is null.\n", + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + + return; + } + + /* + * Mark state so that the workqueue thread stops forwarding + * received frames and link events. ISR and other threads + * that can queue work items will also stop creating work + * items on the snic workqueue + */ + snic_set_state(snic, SNIC_OFFLINE); + spin_lock_irqsave(&snic->snic_lock, flags); + snic->stop_link_events = 1; + spin_unlock_irqrestore(&snic->snic_lock, flags); + + flush_workqueue(snic_glob->event_q); + snic_disc_term(snic); + + spin_lock_irqsave(&snic->snic_lock, flags); + snic->in_remove = 1; + spin_unlock_irqrestore(&snic->snic_lock, flags); + + /* + * This stops the snic device, masks all interrupts, Completed + * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are + * cleanup + */ + snic_cleanup(snic); + + spin_lock_irqsave(&snic_glob->snic_list_lock, flags); + list_del(&snic->list); + spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags); + + snic_tgt_del_all(snic); +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + snic_stats_debugfs_remove(snic); +#endif + snic_del_host(snic->shost); + + svnic_dev_notify_unset(snic->vdev); + snic_free_intr(snic); + snic_free_vnic_res(snic); + snic_clear_intr_mode(snic); + svnic_dev_close(snic->vdev); + svnic_dev_unregister(snic->vdev); + snic_iounmap(snic); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + /* this frees Scsi_Host and snic memory (continuous chunk) */ + scsi_host_put(snic->shost); +} /* end of snic_remove */ + + +struct snic_global *snic_glob; + +/* + * snic_global_data_init: Initialize SNIC Global Data + * Notes: All the global lists, variables should be part of global data + * this helps in debugging. + */ +static int +snic_global_data_init(void) +{ + int ret = 0; + struct kmem_cache *cachep; + ssize_t len = 0; + + snic_glob = kzalloc(sizeof(*snic_glob), GFP_KERNEL); + + if (!snic_glob) { + SNIC_ERR("Failed to allocate Global Context.\n"); + + ret = -ENOMEM; + goto gdi_end; + } + +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + /* Debugfs related Initialization */ + /* Create debugfs entries for snic */ + ret = snic_debugfs_init(); + if (ret < 0) { + SNIC_ERR("Failed to create sysfs dir for tracing and stats.\n"); + snic_debugfs_term(); + /* continue even if it fails */ + } + + /* Trace related Initialization */ + /* Allocate memory for trace buffer */ + ret = snic_trc_init(); + if (ret < 0) { + SNIC_ERR("Trace buffer init failed, SNIC tracing disabled\n"); + snic_trc_free(); + /* continue even if it fails */ + } + +#endif + INIT_LIST_HEAD(&snic_glob->snic_list); + spin_lock_init(&snic_glob->snic_list_lock); + + /* Create a cache for allocation of snic_host_req+default size ESGLs */ + len = sizeof(struct snic_req_info); + len += sizeof(struct snic_host_req) + sizeof(struct snic_dflt_sgl); + cachep = kmem_cache_create("snic_req_dfltsgl", len, SNIC_SG_DESC_ALIGN, + SLAB_HWCACHE_ALIGN, NULL); + if (!cachep) { + SNIC_ERR("Failed to create snic default sgl slab\n"); + ret = -ENOMEM; + + goto err_dflt_req_slab; + } + snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL] = cachep; + + /* Create a cache for allocation of max size Extended SGLs */ + len = sizeof(struct snic_req_info); + len += sizeof(struct snic_host_req) + sizeof(struct snic_max_sgl); + cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN, + SLAB_HWCACHE_ALIGN, NULL); + if (!cachep) { + SNIC_ERR("Failed to create snic max sgl slab\n"); + ret = -ENOMEM; + + goto err_max_req_slab; + } + snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL] = cachep; + + len = sizeof(struct snic_host_req); + cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN, + SLAB_HWCACHE_ALIGN, NULL); + if (!cachep) { + SNIC_ERR("Failed to create snic tm req slab\n"); + ret = -ENOMEM; + + goto err_tmreq_slab; + } + snic_glob->req_cache[SNIC_REQ_TM_CACHE] = cachep; + + /* snic_event queue */ + snic_glob->event_q = create_singlethread_workqueue("snic_event_wq"); + if (!snic_glob->event_q) { + SNIC_ERR("snic event queue create failed\n"); + ret = -ENOMEM; + + goto err_eventq; + } + + return ret; + +err_eventq: + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]); + +err_tmreq_slab: + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); + +err_max_req_slab: + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); + +err_dflt_req_slab: +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + snic_trc_free(); + snic_debugfs_term(); +#endif + kfree(snic_glob); + snic_glob = NULL; + +gdi_end: + return ret; +} /* end of snic_glob_init */ + +/* + * snic_global_data_cleanup : Frees SNIC Global Data + */ +static void +snic_global_data_cleanup(void) +{ + SNIC_BUG_ON(snic_glob == NULL); + + destroy_workqueue(snic_glob->event_q); + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]); + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]); + kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]); + +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + /* Freeing Trace Resources */ + snic_trc_free(); + + /* Freeing Debugfs Resources */ + snic_debugfs_term(); +#endif + kfree(snic_glob); + snic_glob = NULL; +} /* end of snic_glob_cleanup */ + +static struct pci_driver snic_driver = { + .name = SNIC_DRV_NAME, + .id_table = snic_id_table, + .probe = snic_probe, + .remove = snic_remove, +}; + +static int __init +snic_init_module(void) +{ + int ret = 0; + +#ifndef __x86_64__ + SNIC_INFO("SNIC Driver is supported only for x86_64 platforms!\n"); + add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK); +#endif + + SNIC_INFO("%s, ver %s\n", SNIC_DRV_DESCRIPTION, SNIC_DRV_VERSION); + + ret = snic_global_data_init(); + if (ret) { + SNIC_ERR("Failed to Initialize Global Data.\n"); + + return ret; + } + + ret = pci_register_driver(&snic_driver); + if (ret < 0) { + SNIC_ERR("PCI driver register error\n"); + + goto err_pci_reg; + } + + return ret; + +err_pci_reg: + snic_global_data_cleanup(); + + return ret; +} + +static void __exit +snic_cleanup_module(void) +{ + pci_unregister_driver(&snic_driver); + snic_global_data_cleanup(); +} + +module_init(snic_init_module); +module_exit(snic_cleanup_module); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(SNIC_DRV_DESCRIPTION); +MODULE_VERSION(SNIC_DRV_VERSION); +MODULE_DEVICE_TABLE(pci, snic_id_table); +MODULE_AUTHOR("Narsimhulu Musini , " + "Sesidhar Baddela "); diff --git a/kernel/drivers/scsi/snic/snic_res.c b/kernel/drivers/scsi/snic/snic_res.c new file mode 100644 index 000000000..b54912c8c --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_res.c @@ -0,0 +1,295 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "wq_enet_desc.h" +#include "cq_enet_desc.h" +#include "vnic_resource.h" +#include "vnic_dev.h" +#include "vnic_wq.h" +#include "vnic_cq.h" +#include "vnic_intr.h" +#include "vnic_stats.h" +#include "snic.h" + +int +snic_get_vnic_config(struct snic *snic) +{ + struct vnic_snic_config *c = &snic->config; + int ret; + +#define GET_CONFIG(m) \ + do { \ + ret = svnic_dev_spec(snic->vdev, \ + offsetof(struct vnic_snic_config, m), \ + sizeof(c->m), \ + &c->m); \ + if (ret) { \ + SNIC_HOST_ERR(snic->shost, \ + "Error getting %s, %d\n", #m, ret); \ + return ret; \ + } \ + } while (0) + + GET_CONFIG(wq_enet_desc_count); + GET_CONFIG(maxdatafieldsize); + GET_CONFIG(intr_timer); + GET_CONFIG(intr_timer_type); + GET_CONFIG(flags); + GET_CONFIG(io_throttle_count); + GET_CONFIG(port_down_timeout); + GET_CONFIG(port_down_io_retries); + GET_CONFIG(luns_per_tgt); + GET_CONFIG(xpt_type); + GET_CONFIG(hid); + + c->wq_enet_desc_count = min_t(u32, + VNIC_SNIC_WQ_DESCS_MAX, + max_t(u32, + VNIC_SNIC_WQ_DESCS_MIN, + c->wq_enet_desc_count)); + + c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16); + + c->maxdatafieldsize = min_t(u32, + VNIC_SNIC_MAXDATAFIELDSIZE_MAX, + max_t(u32, + VNIC_SNIC_MAXDATAFIELDSIZE_MIN, + c->maxdatafieldsize)); + + c->io_throttle_count = min_t(u32, + VNIC_SNIC_IO_THROTTLE_COUNT_MAX, + max_t(u32, + VNIC_SNIC_IO_THROTTLE_COUNT_MIN, + c->io_throttle_count)); + + c->port_down_timeout = min_t(u32, + VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX, + c->port_down_timeout); + + c->port_down_io_retries = min_t(u32, + VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX, + c->port_down_io_retries); + + c->luns_per_tgt = min_t(u32, + VNIC_SNIC_LUNS_PER_TARGET_MAX, + max_t(u32, + VNIC_SNIC_LUNS_PER_TARGET_MIN, + c->luns_per_tgt)); + + c->intr_timer = min_t(u32, VNIC_INTR_TIMER_MAX, c->intr_timer); + + SNIC_INFO("vNIC resources wq %d\n", c->wq_enet_desc_count); + SNIC_INFO("vNIC mtu %d intr timer %d\n", + c->maxdatafieldsize, + c->intr_timer); + + SNIC_INFO("vNIC flags 0x%x luns per tgt %d\n", + c->flags, + c->luns_per_tgt); + + SNIC_INFO("vNIC io throttle count %d\n", c->io_throttle_count); + SNIC_INFO("vNIC port down timeout %d port down io retries %d\n", + c->port_down_timeout, + c->port_down_io_retries); + + SNIC_INFO("vNIC back end type = %d\n", c->xpt_type); + SNIC_INFO("vNIC hid = %d\n", c->hid); + + return 0; +} + +void +snic_get_res_counts(struct snic *snic) +{ + snic->wq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_WQ); + SNIC_BUG_ON(snic->wq_count == 0); + snic->cq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_CQ); + SNIC_BUG_ON(snic->cq_count == 0); + snic->intr_count = svnic_dev_get_res_count(snic->vdev, + RES_TYPE_INTR_CTRL); + SNIC_BUG_ON(snic->intr_count == 0); +} + +void +snic_free_vnic_res(struct snic *snic) +{ + unsigned int i; + + for (i = 0; i < snic->wq_count; i++) + svnic_wq_free(&snic->wq[i]); + + for (i = 0; i < snic->cq_count; i++) + svnic_cq_free(&snic->cq[i]); + + for (i = 0; i < snic->intr_count; i++) + svnic_intr_free(&snic->intr[i]); +} + +int +snic_alloc_vnic_res(struct snic *snic) +{ + enum vnic_dev_intr_mode intr_mode; + unsigned int mask_on_assertion; + unsigned int intr_offset; + unsigned int err_intr_enable; + unsigned int err_intr_offset; + unsigned int i; + int ret; + + intr_mode = svnic_dev_get_intr_mode(snic->vdev); + + SNIC_INFO("vNIC interrupt mode: %s\n", + ((intr_mode == VNIC_DEV_INTR_MODE_INTX) ? + "Legacy PCI INTx" : + ((intr_mode == VNIC_DEV_INTR_MODE_MSI) ? + "MSI" : + ((intr_mode == VNIC_DEV_INTR_MODE_MSIX) ? + "MSI-X" : "Unknown")))); + + /* only MSI-X is supported */ + SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX); + + SNIC_INFO("wq %d cq %d intr %d\n", snic->wq_count, + snic->cq_count, + snic->intr_count); + + + /* Allocate WQs used for SCSI IOs */ + for (i = 0; i < snic->wq_count; i++) { + ret = svnic_wq_alloc(snic->vdev, + &snic->wq[i], + i, + snic->config.wq_enet_desc_count, + sizeof(struct wq_enet_desc)); + if (ret) + goto error_cleanup; + } + + /* CQ for each WQ */ + for (i = 0; i < snic->wq_count; i++) { + ret = svnic_cq_alloc(snic->vdev, + &snic->cq[i], + i, + snic->config.wq_enet_desc_count, + sizeof(struct cq_enet_wq_desc)); + if (ret) + goto error_cleanup; + } + + SNIC_BUG_ON(snic->cq_count != 2 * snic->wq_count); + /* CQ for FW TO host */ + for (i = snic->wq_count; i < snic->cq_count; i++) { + ret = svnic_cq_alloc(snic->vdev, + &snic->cq[i], + i, + (snic->config.wq_enet_desc_count * 3), + sizeof(struct snic_fw_req)); + if (ret) + goto error_cleanup; + } + + for (i = 0; i < snic->intr_count; i++) { + ret = svnic_intr_alloc(snic->vdev, &snic->intr[i], i); + if (ret) + goto error_cleanup; + } + + /* + * Init WQ Resources. + * WQ[0 to n] points to CQ[0 to n-1] + * firmware to host comm points to CQ[n to m+1] + */ + err_intr_enable = 1; + err_intr_offset = snic->err_intr_offset; + + for (i = 0; i < snic->wq_count; i++) { + svnic_wq_init(&snic->wq[i], + i, + err_intr_enable, + err_intr_offset); + } + + for (i = 0; i < snic->cq_count; i++) { + intr_offset = i; + + svnic_cq_init(&snic->cq[i], + 0 /* flow_control_enable */, + 1 /* color_enable */, + 0 /* cq_head */, + 0 /* cq_tail */, + 1 /* cq_tail_color */, + 1 /* interrupt_enable */, + 1 /* cq_entry_enable */, + 0 /* cq_message_enable */, + intr_offset, + 0 /* cq_message_addr */); + } + + /* + * Init INTR resources + * Assumption : snic is always in MSI-X mode + */ + SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX); + mask_on_assertion = 1; + + for (i = 0; i < snic->intr_count; i++) { + svnic_intr_init(&snic->intr[i], + snic->config.intr_timer, + snic->config.intr_timer_type, + mask_on_assertion); + } + + /* init the stats memory by making the first call here */ + ret = svnic_dev_stats_dump(snic->vdev, &snic->stats); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "svnic_dev_stats_dump failed - x%x\n", + ret); + goto error_cleanup; + } + + /* Clear LIF stats */ + svnic_dev_stats_clear(snic->vdev); + ret = 0; + + return ret; + +error_cleanup: + snic_free_vnic_res(snic); + + return ret; +} + +void +snic_log_q_error(struct snic *snic) +{ + unsigned int i; + u32 err_status; + + for (i = 0; i < snic->wq_count; i++) { + err_status = ioread32(&snic->wq[i].ctrl->error_status); + if (err_status) + SNIC_HOST_ERR(snic->shost, + "WQ[%d] error status %d\n", + i, + err_status); + } +} /* end of snic_log_q_error */ diff --git a/kernel/drivers/scsi/snic/snic_res.h b/kernel/drivers/scsi/snic/snic_res.h new file mode 100644 index 000000000..273f72f2a --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_res.h @@ -0,0 +1,97 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __SNIC_RES_H +#define __SNIC_RES_H + +#include "snic_io.h" +#include "wq_enet_desc.h" +#include "vnic_wq.h" +#include "snic_fwint.h" +#include "vnic_cq_fw.h" + +static inline void +snic_icmnd_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, u64 ctx, + u16 flags, u64 tgt_id, u8 *lun, u8 *scsi_cdb, u8 cdb_len, + u32 data_len, u16 sg_cnt, ulong sgl_addr, + dma_addr_t sns_addr_pa, u32 sense_len) +{ + snic_io_hdr_enc(&req->hdr, SNIC_REQ_ICMND, 0, cmnd_id, host_id, sg_cnt, + ctx); + + req->u.icmnd.flags = cpu_to_le16(flags); + req->u.icmnd.tgt_id = cpu_to_le64(tgt_id); + memcpy(&req->u.icmnd.lun_id, lun, LUN_ADDR_LEN); + req->u.icmnd.cdb_len = cdb_len; + memset(req->u.icmnd.cdb, 0, SNIC_CDB_LEN); + memcpy(req->u.icmnd.cdb, scsi_cdb, cdb_len); + req->u.icmnd.data_len = cpu_to_le32(data_len); + req->u.icmnd.sg_addr = cpu_to_le64(sgl_addr); + req->u.icmnd.sense_len = cpu_to_le32(sense_len); + req->u.icmnd.sense_addr = cpu_to_le64(sns_addr_pa); +} + +static inline void +snic_itmf_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, ulong ctx, + u16 flags, u32 req_id, u64 tgt_id, u8 *lun, u8 tm_type) +{ + snic_io_hdr_enc(&req->hdr, SNIC_REQ_ITMF, 0, cmnd_id, host_id, 0, ctx); + + req->u.itmf.tm_type = tm_type; + req->u.itmf.flags = cpu_to_le16(flags); + /* req_id valid only in abort, clear task */ + req->u.itmf.req_id = cpu_to_le32(req_id); + req->u.itmf.tgt_id = cpu_to_le64(tgt_id); + memcpy(&req->u.itmf.lun_id, lun, LUN_ADDR_LEN); +} + +static inline void +snic_queue_wq_eth_desc(struct vnic_wq *wq, + void *os_buf, + dma_addr_t dma_addr, + unsigned int len, + int vlan_tag_insert, + unsigned int vlan_tag, + int cq_entry) +{ + struct wq_enet_desc *desc = svnic_wq_next_desc(wq); + + wq_enet_desc_enc(desc, + (u64)dma_addr | VNIC_PADDR_TARGET, + (u16)len, + 0, /* mss_or_csum_offset */ + 0, /* fc_eof */ + 0, /* offload mode */ + 1, /* eop */ + (u8)cq_entry, + 0, /* fcoe_encap */ + (u8)vlan_tag_insert, + (u16)vlan_tag, + 0 /* loopback */); + + svnic_wq_post(wq, os_buf, dma_addr, len, 1, 1); +} + +struct snic; + +int snic_get_vnic_config(struct snic *); +int snic_alloc_vnic_res(struct snic *); +void snic_free_vnic_res(struct snic *); +void snic_get_res_counts(struct snic *); +void snic_log_q_error(struct snic *); +int snic_get_vnic_resources_size(struct snic *); +#endif /* __SNIC_RES_H */ diff --git a/kernel/drivers/scsi/snic/snic_scsi.c b/kernel/drivers/scsi/snic/snic_scsi.c new file mode 100644 index 000000000..2c7b4c321 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_scsi.c @@ -0,0 +1,2632 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snic_io.h" +#include "snic.h" + +#define snic_cmd_tag(sc) (((struct scsi_cmnd *) sc)->request->tag) + +const char *snic_state_str[] = { + [SNIC_INIT] = "SNIC_INIT", + [SNIC_ERROR] = "SNIC_ERROR", + [SNIC_ONLINE] = "SNIC_ONLINE", + [SNIC_OFFLINE] = "SNIC_OFFLINE", + [SNIC_FWRESET] = "SNIC_FWRESET", +}; + +static const char * const snic_req_state_str[] = { + [SNIC_IOREQ_NOT_INITED] = "SNIC_IOREQ_NOT_INITED", + [SNIC_IOREQ_PENDING] = "SNIC_IOREQ_PENDING", + [SNIC_IOREQ_ABTS_PENDING] = "SNIC_IOREQ_ABTS_PENDING", + [SNIC_IOREQ_ABTS_COMPLETE] = "SNIC_IOREQ_ABTS_COMPELTE", + [SNIC_IOREQ_LR_PENDING] = "SNIC_IOREQ_LR_PENDING", + [SNIC_IOREQ_LR_COMPLETE] = "SNIC_IOREQ_LR_COMPELTE", + [SNIC_IOREQ_COMPLETE] = "SNIC_IOREQ_CMD_COMPELTE", +}; + +/* snic cmd status strings */ +static const char * const snic_io_status_str[] = { + [SNIC_STAT_IO_SUCCESS] = "SNIC_STAT_IO_SUCCESS", /* 0x0 */ + [SNIC_STAT_INVALID_HDR] = "SNIC_STAT_INVALID_HDR", + [SNIC_STAT_OUT_OF_RES] = "SNIC_STAT_OUT_OF_RES", + [SNIC_STAT_INVALID_PARM] = "SNIC_STAT_INVALID_PARM", + [SNIC_STAT_REQ_NOT_SUP] = "SNIC_STAT_REQ_NOT_SUP", + [SNIC_STAT_IO_NOT_FOUND] = "SNIC_STAT_IO_NOT_FOUND", + [SNIC_STAT_ABORTED] = "SNIC_STAT_ABORTED", + [SNIC_STAT_TIMEOUT] = "SNIC_STAT_TIMEOUT", + [SNIC_STAT_SGL_INVALID] = "SNIC_STAT_SGL_INVALID", + [SNIC_STAT_DATA_CNT_MISMATCH] = "SNIC_STAT_DATA_CNT_MISMATCH", + [SNIC_STAT_FW_ERR] = "SNIC_STAT_FW_ERR", + [SNIC_STAT_ITMF_REJECT] = "SNIC_STAT_ITMF_REJECT", + [SNIC_STAT_ITMF_FAIL] = "SNIC_STAT_ITMF_FAIL", + [SNIC_STAT_ITMF_INCORRECT_LUN] = "SNIC_STAT_ITMF_INCORRECT_LUN", + [SNIC_STAT_CMND_REJECT] = "SNIC_STAT_CMND_REJECT", + [SNIC_STAT_DEV_OFFLINE] = "SNIC_STAT_DEV_OFFLINE", + [SNIC_STAT_NO_BOOTLUN] = "SNIC_STAT_NO_BOOTLUN", + [SNIC_STAT_SCSI_ERR] = "SNIC_STAT_SCSI_ERR", + [SNIC_STAT_NOT_READY] = "SNIC_STAT_NOT_READY", + [SNIC_STAT_FATAL_ERROR] = "SNIC_STAT_FATAL_ERROR", +}; + +static void snic_scsi_cleanup(struct snic *, int); + +const char * +snic_state_to_str(unsigned int state) +{ + if (state >= ARRAY_SIZE(snic_state_str) || !snic_state_str[state]) + return "Unknown"; + + return snic_state_str[state]; +} + +static const char * +snic_io_status_to_str(unsigned int state) +{ + if ((state >= ARRAY_SIZE(snic_io_status_str)) || + (!snic_io_status_str[state])) + return "Unknown"; + + return snic_io_status_str[state]; +} + +static const char * +snic_ioreq_state_to_str(unsigned int state) +{ + if (state >= ARRAY_SIZE(snic_req_state_str) || + !snic_req_state_str[state]) + return "Unknown"; + + return snic_req_state_str[state]; +} + +static inline spinlock_t * +snic_io_lock_hash(struct snic *snic, struct scsi_cmnd *sc) +{ + u32 hash = snic_cmd_tag(sc) & (SNIC_IO_LOCKS - 1); + + return &snic->io_req_lock[hash]; +} + +static inline spinlock_t * +snic_io_lock_tag(struct snic *snic, int tag) +{ + return &snic->io_req_lock[tag & (SNIC_IO_LOCKS - 1)]; +} + +/* snic_release_req_buf : Releases snic_req_info */ +static void +snic_release_req_buf(struct snic *snic, + struct snic_req_info *rqi, + struct scsi_cmnd *sc) +{ + struct snic_host_req *req = rqi_to_req(rqi); + + /* Freeing cmd without marking completion, not okay */ + SNIC_BUG_ON(!((CMD_STATE(sc) == SNIC_IOREQ_COMPLETE) || + (CMD_STATE(sc) == SNIC_IOREQ_ABTS_COMPLETE) || + (CMD_FLAGS(sc) & SNIC_DEV_RST_NOTSUP) || + (CMD_FLAGS(sc) & SNIC_IO_INTERNAL_TERM_ISSUED) || + (CMD_FLAGS(sc) & SNIC_DEV_RST_TERM_ISSUED) || + (CMD_FLAGS(sc) & SNIC_SCSI_CLEANUP) || + (CMD_STATE(sc) == SNIC_IOREQ_LR_COMPLETE))); + + SNIC_SCSI_DBG(snic->shost, + "Rel_req:sc %p:tag %x:rqi %p:ioreq %p:abt %p:dr %p: state %s:flags 0x%llx\n", + sc, snic_cmd_tag(sc), rqi, rqi->req, rqi->abort_req, + rqi->dr_req, snic_ioreq_state_to_str(CMD_STATE(sc)), + CMD_FLAGS(sc)); + + if (req->u.icmnd.sense_addr) + pci_unmap_single(snic->pdev, + le64_to_cpu(req->u.icmnd.sense_addr), + SCSI_SENSE_BUFFERSIZE, + PCI_DMA_FROMDEVICE); + + scsi_dma_unmap(sc); + + snic_req_free(snic, rqi); +} /* end of snic_release_req_buf */ + +/* + * snic_queue_icmnd_req : Queues snic_icmnd request + */ +static int +snic_queue_icmnd_req(struct snic *snic, + struct snic_req_info *rqi, + struct scsi_cmnd *sc, + int sg_cnt) +{ + struct scatterlist *sg; + struct snic_sg_desc *sgd; + dma_addr_t pa = 0; + struct scsi_lun lun; + u16 flags = 0; + int ret = 0; + unsigned int i; + + if (sg_cnt) { + flags = SNIC_ICMND_ESGL; + sgd = (struct snic_sg_desc *) req_to_sgl(rqi->req); + + for_each_sg(scsi_sglist(sc), sg, sg_cnt, i) { + sgd->addr = cpu_to_le64(sg_dma_address(sg)); + sgd->len = cpu_to_le32(sg_dma_len(sg)); + sgd->_resvd = 0; + sgd++; + } + } + + pa = pci_map_single(snic->pdev, + sc->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(snic->pdev, pa)) { + SNIC_HOST_ERR(snic->shost, + "QIcmnd:PCI Map Failed for sns buf %p tag %x\n", + sc->sense_buffer, snic_cmd_tag(sc)); + ret = -ENOMEM; + + return ret; + } + + int_to_scsilun(sc->device->lun, &lun); + if (sc->sc_data_direction == DMA_FROM_DEVICE) + flags |= SNIC_ICMND_RD; + if (sc->sc_data_direction == DMA_TO_DEVICE) + flags |= SNIC_ICMND_WR; + + /* Initialize icmnd */ + snic_icmnd_init(rqi->req, + snic_cmd_tag(sc), + snic->config.hid, /* hid */ + (ulong) rqi, + flags, /* command flags */ + rqi->tgt_id, + lun.scsi_lun, + sc->cmnd, + sc->cmd_len, + scsi_bufflen(sc), + sg_cnt, + (ulong) req_to_sgl(rqi->req), + pa, /* sense buffer pa */ + SCSI_SENSE_BUFFERSIZE); + + ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len); + if (ret) + SNIC_HOST_ERR(snic->shost, + "QIcmnd: Queuing Icmnd Failed. ret = %d\n", + ret); + + return ret; +} /* end of snic_queue_icmnd_req */ + +/* + * snic_issue_scsi_req : Prepares IO request and Issues to FW. + */ +static int +snic_issue_scsi_req(struct snic *snic, + struct snic_tgt *tgt, + struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + int sg_cnt = 0; + int ret = 0; + u32 tag = snic_cmd_tag(sc); + u64 cmd_trc = 0, cmd_st_flags = 0; + spinlock_t *io_lock = NULL; + unsigned long flags; + + CMD_STATE(sc) = SNIC_IOREQ_NOT_INITED; + CMD_FLAGS(sc) = SNIC_NO_FLAGS; + sg_cnt = scsi_dma_map(sc); + if (sg_cnt < 0) { + SNIC_TRC((u16)snic->shost->host_no, tag, (ulong) sc, 0, + sc->cmnd[0], sg_cnt, CMD_STATE(sc)); + + SNIC_HOST_ERR(snic->shost, "issue_sc:Failed to map SG List.\n"); + ret = -ENOMEM; + + goto issue_sc_end; + } + + rqi = snic_req_init(snic, sg_cnt); + if (!rqi) { + scsi_dma_unmap(sc); + ret = -ENOMEM; + + goto issue_sc_end; + } + + rqi->tgt_id = tgt->id; + rqi->sc = sc; + + CMD_STATE(sc) = SNIC_IOREQ_PENDING; + CMD_SP(sc) = (char *) rqi; + cmd_trc = SNIC_TRC_CMD(sc); + CMD_FLAGS(sc) |= (SNIC_IO_INITIALIZED | SNIC_IO_ISSUED); + cmd_st_flags = SNIC_TRC_CMD_STATE_FLAGS(sc); + io_lock = snic_io_lock_hash(snic, sc); + + /* create wq desc and enqueue it */ + ret = snic_queue_icmnd_req(snic, rqi, sc, sg_cnt); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "issue_sc: icmnd qing Failed for sc %p, err %d\n", + sc, ret); + + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + CMD_SP(sc) = NULL; + CMD_STATE(sc) = SNIC_IOREQ_COMPLETE; + CMD_FLAGS(sc) &= ~SNIC_IO_ISSUED; /* turn off the flag */ + spin_unlock_irqrestore(io_lock, flags); + + if (rqi) + snic_release_req_buf(snic, rqi, sc); + + SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, 0, 0, 0, + SNIC_TRC_CMD_STATE_FLAGS(sc)); + } else { + u32 io_sz = scsi_bufflen(sc) >> 9; + u32 qtime = jiffies - rqi->start_time; + struct snic_io_stats *iostats = &snic->s_stats.io; + + if (io_sz > atomic64_read(&iostats->max_io_sz)) + atomic64_set(&iostats->max_io_sz, io_sz); + + if (qtime > atomic64_read(&iostats->max_qtime)) + atomic64_set(&iostats->max_qtime, qtime); + + SNIC_SCSI_DBG(snic->shost, + "issue_sc:sc %p, tag %d queued to WQ.\n", + sc, tag); + + SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, (ulong) rqi, + sg_cnt, cmd_trc, cmd_st_flags); + } + +issue_sc_end: + + return ret; +} /* end of snic_issue_scsi_req */ + + +/* + * snic_queuecommand + * Routine to send a scsi cdb to LLD + * Called with host_lock held and interrupts disabled + */ +int +snic_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc) +{ + struct snic_tgt *tgt = NULL; + struct snic *snic = shost_priv(shost); + int ret; + + tgt = starget_to_tgt(scsi_target(sc->device)); + ret = snic_tgt_chkready(tgt); + if (ret) { + SNIC_HOST_ERR(shost, "Tgt %p id %d Not Ready.\n", tgt, tgt->id); + atomic64_inc(&snic->s_stats.misc.tgt_not_rdy); + sc->result = ret; + sc->scsi_done(sc); + + return 0; + } + + if (snic_get_state(snic) != SNIC_ONLINE) { + SNIC_HOST_ERR(shost, "snic state is %s\n", + snic_state_str[snic_get_state(snic)]); + + return SCSI_MLQUEUE_HOST_BUSY; + } + atomic_inc(&snic->ios_inflight); + + SNIC_SCSI_DBG(shost, "sc %p Tag %d (sc %0x) lun %lld in snic_qcmd\n", + sc, snic_cmd_tag(sc), sc->cmnd[0], sc->device->lun); + + memset(scsi_cmd_priv(sc), 0, sizeof(struct snic_internal_io_state)); + + ret = snic_issue_scsi_req(snic, tgt, sc); + if (ret) { + SNIC_HOST_ERR(shost, "Failed to Q, Scsi Req w/ err %d.\n", ret); + ret = SCSI_MLQUEUE_HOST_BUSY; + } else + snic_stats_update_active_ios(&snic->s_stats); + + atomic_dec(&snic->ios_inflight); + + return ret; +} /* end of snic_queuecommand */ + +/* + * snic_process_abts_pending_state: + * caller should hold IO lock + */ +static void +snic_proc_tmreq_pending_state(struct snic *snic, + struct scsi_cmnd *sc, + u8 cmpl_status) +{ + int state = CMD_STATE(sc); + + if (state == SNIC_IOREQ_ABTS_PENDING) + CMD_FLAGS(sc) |= SNIC_IO_ABTS_PENDING; + else if (state == SNIC_IOREQ_LR_PENDING) + CMD_FLAGS(sc) |= SNIC_DEV_RST_PENDING; + else + SNIC_BUG_ON(1); + + switch (cmpl_status) { + case SNIC_STAT_IO_SUCCESS: + CMD_FLAGS(sc) |= SNIC_IO_DONE; + break; + + case SNIC_STAT_ABORTED: + CMD_FLAGS(sc) |= SNIC_IO_ABORTED; + break; + + default: + SNIC_BUG_ON(1); + } +} + +/* + * snic_process_io_failed_state: + * Processes IO's error states + */ +static void +snic_process_io_failed_state(struct snic *snic, + struct snic_icmnd_cmpl *icmnd_cmpl, + struct scsi_cmnd *sc, + u8 cmpl_stat) +{ + int res = 0; + + switch (cmpl_stat) { + case SNIC_STAT_TIMEOUT: /* Req was timedout */ + atomic64_inc(&snic->s_stats.misc.io_tmo); + res = DID_TIME_OUT; + break; + + case SNIC_STAT_ABORTED: /* Req was aborted */ + atomic64_inc(&snic->s_stats.misc.io_aborted); + res = DID_ABORT; + break; + + case SNIC_STAT_DATA_CNT_MISMATCH:/* Recv/Sent more/less data than exp */ + atomic64_inc(&snic->s_stats.misc.data_cnt_mismat); + scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid)); + res = DID_ERROR; + break; + + case SNIC_STAT_OUT_OF_RES: /* Out of resources to complete request */ + atomic64_inc(&snic->s_stats.fw.out_of_res); + res = DID_REQUEUE; + break; + + case SNIC_STAT_IO_NOT_FOUND: /* Requested I/O was not found */ + atomic64_inc(&snic->s_stats.io.io_not_found); + res = DID_ERROR; + break; + + case SNIC_STAT_SGL_INVALID: /* Req was aborted to due to sgl error*/ + atomic64_inc(&snic->s_stats.misc.sgl_inval); + res = DID_ERROR; + break; + + case SNIC_STAT_FW_ERR: /* Req terminated due to FW Error */ + atomic64_inc(&snic->s_stats.fw.io_errs); + res = DID_ERROR; + break; + + case SNIC_STAT_SCSI_ERR: /* FW hits SCSI Error */ + atomic64_inc(&snic->s_stats.fw.scsi_errs); + break; + + case SNIC_STAT_NOT_READY: /* XPT yet to initialize */ + case SNIC_STAT_DEV_OFFLINE: /* Device offline */ + res = DID_NO_CONNECT; + break; + + case SNIC_STAT_INVALID_HDR: /* Hdr contains invalid data */ + case SNIC_STAT_INVALID_PARM: /* Some param in req is invalid */ + case SNIC_STAT_REQ_NOT_SUP: /* Req type is not supported */ + case SNIC_STAT_CMND_REJECT: /* Req rejected */ + case SNIC_STAT_FATAL_ERROR: /* XPT Error */ + default: + SNIC_SCSI_DBG(snic->shost, + "Invalid Hdr/Param or Req Not Supported or Cmnd Rejected or Device Offline. or Unknown\n"); + res = DID_ERROR; + break; + } + + SNIC_HOST_ERR(snic->shost, "fw returns failed status %s flags 0x%llx\n", + snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc)); + + /* Set sc->result */ + sc->result = (res << 16) | icmnd_cmpl->scsi_status; +} /* end of snic_process_io_failed_state */ + +/* + * snic_tmreq_pending : is task management in progress. + */ +static int +snic_tmreq_pending(struct scsi_cmnd *sc) +{ + int state = CMD_STATE(sc); + + return ((state == SNIC_IOREQ_ABTS_PENDING) || + (state == SNIC_IOREQ_LR_PENDING)); +} + +/* + * snic_process_icmnd_cmpl_status: + * Caller should hold io_lock + */ +static int +snic_process_icmnd_cmpl_status(struct snic *snic, + struct snic_icmnd_cmpl *icmnd_cmpl, + u8 cmpl_stat, + struct scsi_cmnd *sc) +{ + u8 scsi_stat = icmnd_cmpl->scsi_status; + u64 xfer_len = 0; + int ret = 0; + + /* Mark the IO as complete */ + CMD_STATE(sc) = SNIC_IOREQ_COMPLETE; + + if (likely(cmpl_stat == SNIC_STAT_IO_SUCCESS)) { + sc->result = (DID_OK << 16) | scsi_stat; + + xfer_len = scsi_bufflen(sc); + + /* Update SCSI Cmd with resid value */ + scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid)); + + if (icmnd_cmpl->flags & SNIC_ICMND_CMPL_UNDR_RUN) { + xfer_len -= le32_to_cpu(icmnd_cmpl->resid); + atomic64_inc(&snic->s_stats.misc.io_under_run); + } + + if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL) + atomic64_inc(&snic->s_stats.misc.qfull); + + ret = 0; + } else { + snic_process_io_failed_state(snic, icmnd_cmpl, sc, cmpl_stat); + atomic64_inc(&snic->s_stats.io.fail); + SNIC_HOST_ERR(snic->shost, + "icmnd_cmpl: IO Failed : Hdr Status %s flags 0x%llx\n", + snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc)); + ret = 1; + } + + return ret; +} /* end of snic_process_icmnd_cmpl_status */ + + +/* + * snic_icmnd_cmpl_handler + * Routine to handle icmnd completions + */ +static void +snic_icmnd_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + u8 typ, hdr_stat; + u32 cmnd_id, hid; + ulong ctx; + struct scsi_cmnd *sc = NULL; + struct snic_icmnd_cmpl *icmnd_cmpl = NULL; + struct snic_host_req *req = NULL; + struct snic_req_info *rqi = NULL; + unsigned long flags, start_time; + spinlock_t *io_lock; + u8 sc_stat = 0; + + snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); + icmnd_cmpl = &fwreq->u.icmnd_cmpl; + sc_stat = icmnd_cmpl->scsi_status; + + SNIC_SCSI_DBG(snic->shost, + "Icmnd_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,i ctx = %lx\n", + typ, hdr_stat, cmnd_id, hid, ctx); + + if (cmnd_id >= snic->max_tag_id) { + SNIC_HOST_ERR(snic->shost, + "Icmnd_cmpl:Tag Error:Out of Range Tag %d, hdr status = %s\n", + cmnd_id, snic_io_status_to_str(hdr_stat)); + return; + } + + sc = scsi_host_find_tag(snic->shost, cmnd_id); + WARN_ON_ONCE(!sc); + + if (!sc) { + atomic64_inc(&snic->s_stats.io.sc_null); + SNIC_HOST_ERR(snic->shost, + "Icmnd_cmpl: Scsi Cmnd Not found, sc = NULL Hdr Status = %s tag = 0x%x fwreq = 0x%p\n", + snic_io_status_to_str(hdr_stat), + cmnd_id, + fwreq); + + SNIC_TRC(snic->shost->host_no, cmnd_id, 0, + ((u64)hdr_stat << 16 | + (u64)sc_stat << 8 | (u64)icmnd_cmpl->flags), + (ulong) fwreq, le32_to_cpu(icmnd_cmpl->resid), ctx); + + return; + } + + io_lock = snic_io_lock_hash(snic, sc); + + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + SNIC_SCSI_DBG(snic->shost, + "Icmnd_cmpl:lun %lld sc %p cmd %xtag %d flags 0x%llx rqi %p\n", + sc->device->lun, sc, sc->cmnd[0], snic_cmd_tag(sc), + CMD_FLAGS(sc), rqi); + + SNIC_BUG_ON(rqi != (struct snic_req_info *)ctx); + WARN_ON_ONCE(req); + if (!rqi) { + atomic64_inc(&snic->s_stats.io.req_null); + CMD_FLAGS(sc) |= SNIC_IO_REQ_NULL; + spin_unlock_irqrestore(io_lock, flags); + + SNIC_HOST_ERR(snic->shost, + "Icmnd_cmpl:Host Req Not Found(null), Hdr Status %s, Tag 0x%x, sc 0x%p flags 0x%llx\n", + snic_io_status_to_str(hdr_stat), + cmnd_id, sc, CMD_FLAGS(sc)); + return; + } + + rqi = (struct snic_req_info *) ctx; + start_time = rqi->start_time; + + /* firmware completed the io */ + rqi->io_cmpl = 1; + + /* + * if SCSI-ML has already issued abort on this command, + * ignore completion of the IO. The abts path will clean it up + */ + if (unlikely(snic_tmreq_pending(sc))) { + snic_proc_tmreq_pending_state(snic, sc, hdr_stat); + spin_unlock_irqrestore(io_lock, flags); + + snic_stats_update_io_cmpl(&snic->s_stats); + + /* Expected value is SNIC_STAT_ABORTED */ + if (likely(hdr_stat == SNIC_STAT_ABORTED)) + return; + + SNIC_SCSI_DBG(snic->shost, + "icmnd_cmpl:TM Req Pending(%s), Hdr Status %s sc 0x%p scsi status %x resid %d flags 0x%llx\n", + snic_ioreq_state_to_str(CMD_STATE(sc)), + snic_io_status_to_str(hdr_stat), + sc, sc_stat, le32_to_cpu(icmnd_cmpl->resid), + CMD_FLAGS(sc)); + + SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), (ulong) fwreq, + SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); + + return; + } + + if (snic_process_icmnd_cmpl_status(snic, icmnd_cmpl, hdr_stat, sc)) { + scsi_print_command(sc); + SNIC_HOST_ERR(snic->shost, + "icmnd_cmpl:IO Failed, sc 0x%p Tag %d Cmd %x Hdr Status %s flags 0x%llx\n", + sc, sc->cmnd[0], cmnd_id, + snic_io_status_to_str(hdr_stat), CMD_FLAGS(sc)); + } + + /* Break link with the SCSI Command */ + CMD_SP(sc) = NULL; + CMD_FLAGS(sc) |= SNIC_IO_DONE; + + spin_unlock_irqrestore(io_lock, flags); + + /* For now, consider only successful IO. */ + snic_calc_io_process_time(snic, rqi); + + snic_release_req_buf(snic, rqi, sc); + + SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), (ulong) fwreq, + SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); + + + if (sc->scsi_done) + sc->scsi_done(sc); + + snic_stats_update_io_cmpl(&snic->s_stats); +} /* end of snic_icmnd_cmpl_handler */ + +static void +snic_proc_dr_cmpl_locked(struct snic *snic, + struct snic_fw_req *fwreq, + u8 cmpl_stat, + u32 cmnd_id, + struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = (struct snic_req_info *) CMD_SP(sc); + u32 start_time = rqi->start_time; + + CMD_LR_STATUS(sc) = cmpl_stat; + + SNIC_SCSI_DBG(snic->shost, "itmf_cmpl: Cmd State = %s\n", + snic_ioreq_state_to_str(CMD_STATE(sc))); + + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) { + CMD_FLAGS(sc) |= SNIC_DEV_RST_ABTS_PENDING; + + SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), + (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc)); + + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl: Terminate Pending Dev Reset Cmpl Recvd.id %x, status %s flags 0x%llx\n", + (int)(cmnd_id & SNIC_TAG_MASK), + snic_io_status_to_str(cmpl_stat), + CMD_FLAGS(sc)); + + return; + } + + + if (CMD_FLAGS(sc) & SNIC_DEV_RST_TIMEDOUT) { + SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), + (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc)); + + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl:Dev Reset Completion Received after timeout. id %d cmpl status %s flags 0x%llx\n", + (int)(cmnd_id & SNIC_TAG_MASK), + snic_io_status_to_str(cmpl_stat), + CMD_FLAGS(sc)); + + return; + } + + CMD_STATE(sc) = SNIC_IOREQ_LR_COMPLETE; + CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE; + + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl:Dev Reset Cmpl Recvd id %d cmpl status %s flags 0x%llx\n", + (int)(cmnd_id & SNIC_TAG_MASK), + snic_io_status_to_str(cmpl_stat), + CMD_FLAGS(sc)); + + if (rqi->dr_done) + complete(rqi->dr_done); +} /* end of snic_proc_dr_cmpl_locked */ + +/* + * snic_update_abort_stats : Updates abort stats based on completion status. + */ +static void +snic_update_abort_stats(struct snic *snic, u8 cmpl_stat) +{ + struct snic_abort_stats *abt_stats = &snic->s_stats.abts; + + SNIC_SCSI_DBG(snic->shost, "Updating Abort stats.\n"); + + switch (cmpl_stat) { + case SNIC_STAT_IO_SUCCESS: + break; + + case SNIC_STAT_TIMEOUT: + atomic64_inc(&abt_stats->fw_tmo); + break; + + case SNIC_STAT_IO_NOT_FOUND: + atomic64_inc(&abt_stats->io_not_found); + break; + + default: + atomic64_inc(&abt_stats->fail); + break; + } +} + +static int +snic_process_itmf_cmpl(struct snic *snic, + struct snic_fw_req *fwreq, + u32 cmnd_id, + u8 cmpl_stat, + struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + u32 tm_tags = 0; + spinlock_t *io_lock = NULL; + unsigned long flags; + u32 start_time = 0; + int ret = 0; + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + WARN_ON_ONCE(!rqi); + + if (!rqi) { + atomic64_inc(&snic->s_stats.io.req_null); + spin_unlock_irqrestore(io_lock, flags); + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL; + SNIC_HOST_ERR(snic->shost, + "itmf_cmpl: rqi is null,Hdr stat = %s Tag = 0x%x sc = 0x%p flags 0x%llx\n", + snic_io_status_to_str(cmpl_stat), cmnd_id, sc, + CMD_FLAGS(sc)); + + return ret; + } + + /* Extract task management flags */ + tm_tags = cmnd_id & ~(SNIC_TAG_MASK); + + start_time = rqi->start_time; + cmnd_id &= (SNIC_TAG_MASK); + + switch (tm_tags) { + case SNIC_TAG_ABORT: + /* Abort only issued on cmd */ + snic_update_abort_stats(snic, cmpl_stat); + + if (CMD_STATE(sc) != SNIC_IOREQ_ABTS_PENDING) { + /* This is a late completion. Ignore it. */ + ret = -1; + spin_unlock_irqrestore(io_lock, flags); + break; + } + + CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE; + CMD_ABTS_STATUS(sc) = cmpl_stat; + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE; + + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl:Abort Cmpl Recvd.Tag 0x%x Status %s flags 0x%llx\n", + cmnd_id, + snic_io_status_to_str(cmpl_stat), + CMD_FLAGS(sc)); + + /* + * If scsi_eh thread is blocked waiting for abts complete, + * signal completion to it. IO will be cleaned in the thread, + * else clean it in this context. + */ + if (rqi->abts_done) { + complete(rqi->abts_done); + spin_unlock_irqrestore(io_lock, flags); + + break; /* jump out */ + } + + CMD_SP(sc) = NULL; + sc->result = (DID_ERROR << 16); + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl: Completing IO. sc %p flags 0x%llx\n", + sc, CMD_FLAGS(sc)); + + spin_unlock_irqrestore(io_lock, flags); + + snic_release_req_buf(snic, rqi, sc); + + if (sc->scsi_done) { + SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), + (ulong) fwreq, SNIC_TRC_CMD(sc), + SNIC_TRC_CMD_STATE_FLAGS(sc)); + + sc->scsi_done(sc); + } + + break; + + case SNIC_TAG_DEV_RST: + case SNIC_TAG_DEV_RST | SNIC_TAG_IOCTL_DEV_RST: + snic_proc_dr_cmpl_locked(snic, fwreq, cmpl_stat, cmnd_id, sc); + spin_unlock_irqrestore(io_lock, flags); + ret = 0; + + break; + + case SNIC_TAG_ABORT | SNIC_TAG_DEV_RST: + /* Abort and terminate completion of device reset req */ + + CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE; + CMD_ABTS_STATUS(sc) = cmpl_stat; + CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE; + + SNIC_SCSI_DBG(snic->shost, + "itmf_cmpl:dev reset abts cmpl recvd. id %d status %s flags 0x%llx\n", + cmnd_id, snic_io_status_to_str(cmpl_stat), + CMD_FLAGS(sc)); + + if (rqi->abts_done) + complete(rqi->abts_done); + + spin_unlock_irqrestore(io_lock, flags); + + break; + + default: + spin_unlock_irqrestore(io_lock, flags); + SNIC_HOST_ERR(snic->shost, + "itmf_cmpl: Unknown TM tag bit 0x%x\n", tm_tags); + + SNIC_HOST_ERR(snic->shost, + "itmf_cmpl:Unexpected itmf io stat %s Tag = 0x%x flags 0x%llx\n", + snic_ioreq_state_to_str(CMD_STATE(sc)), + cmnd_id, + CMD_FLAGS(sc)); + ret = -1; + SNIC_BUG_ON(1); + + break; + } + + return ret; +} /* end of snic_process_itmf_cmpl_status */ + +/* + * snic_itmf_cmpl_handler. + * Routine to handle itmf completions. + */ +static void +snic_itmf_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + struct scsi_cmnd *sc = NULL; + struct snic_req_info *rqi = NULL; + struct snic_itmf_cmpl *itmf_cmpl = NULL; + ulong ctx; + u32 cmnd_id; + u32 hid; + u8 typ; + u8 hdr_stat; + + snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); + SNIC_SCSI_DBG(snic->shost, + "Itmf_cmpl: %s: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,ctx = %lx\n", + __func__, typ, hdr_stat, cmnd_id, hid, ctx); + + itmf_cmpl = &fwreq->u.itmf_cmpl; + SNIC_SCSI_DBG(snic->shost, + "Itmf_cmpl: nterm %u , flags 0x%x\n", + le32_to_cpu(itmf_cmpl->nterminated), itmf_cmpl->flags); + + /* spl case, dev reset issued through ioctl */ + if (cmnd_id & SNIC_TAG_IOCTL_DEV_RST) { + rqi = (struct snic_req_info *) ctx; + sc = rqi->sc; + + goto ioctl_dev_rst; + } + + if ((cmnd_id & SNIC_TAG_MASK) >= snic->max_tag_id) { + SNIC_HOST_ERR(snic->shost, + "Itmf_cmpl: Tag 0x%x out of Range,HdrStat %s\n", + cmnd_id, snic_io_status_to_str(hdr_stat)); + SNIC_BUG_ON(1); + + return; + } + + sc = scsi_host_find_tag(snic->shost, cmnd_id & SNIC_TAG_MASK); + WARN_ON_ONCE(!sc); + +ioctl_dev_rst: + if (!sc) { + atomic64_inc(&snic->s_stats.io.sc_null); + SNIC_HOST_ERR(snic->shost, + "Itmf_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n", + snic_io_status_to_str(hdr_stat), cmnd_id); + + return; + } + + snic_process_itmf_cmpl(snic, fwreq, cmnd_id, hdr_stat, sc); +} /* end of snic_itmf_cmpl_handler */ + + + +static void +snic_hba_reset_scsi_cleanup(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_stats *st = &snic->s_stats; + long act_ios = 0, act_fwreqs = 0; + + SNIC_SCSI_DBG(snic->shost, "HBA Reset scsi cleanup.\n"); + snic_scsi_cleanup(snic, snic_cmd_tag(sc)); + + /* Update stats on pending IOs */ + act_ios = atomic64_read(&st->io.active); + atomic64_add(act_ios, &st->io.compl); + atomic64_sub(act_ios, &st->io.active); + + act_fwreqs = atomic64_read(&st->fw.actv_reqs); + atomic64_sub(act_fwreqs, &st->fw.actv_reqs); +} + +/* + * snic_hba_reset_cmpl_handler : + * + * Notes : + * 1. Cleanup all the scsi cmds, release all snic specific cmds + * 2. Issue Report Targets in case of SAN targets + */ +static int +snic_hba_reset_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + ulong ctx; + u32 cmnd_id; + u32 hid; + u8 typ; + u8 hdr_stat; + struct scsi_cmnd *sc = NULL; + struct snic_req_info *rqi = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags, gflags; + int ret = 0; + + SNIC_HOST_INFO(snic->shost, + "reset_cmpl:HBA Reset Completion received.\n"); + + snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); + SNIC_SCSI_DBG(snic->shost, + "reset_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n", + typ, hdr_stat, cmnd_id, hid, ctx); + + /* spl case, host reset issued through ioctl */ + if (cmnd_id == SCSI_NO_TAG) { + rqi = (struct snic_req_info *) ctx; + sc = rqi->sc; + + goto ioctl_hba_rst; + } + + if (cmnd_id >= snic->max_tag_id) { + SNIC_HOST_ERR(snic->shost, + "reset_cmpl: Tag 0x%x out of Range,HdrStat %s\n", + cmnd_id, snic_io_status_to_str(hdr_stat)); + SNIC_BUG_ON(1); + + return 1; + } + + sc = scsi_host_find_tag(snic->shost, cmnd_id); +ioctl_hba_rst: + if (!sc) { + atomic64_inc(&snic->s_stats.io.sc_null); + SNIC_HOST_ERR(snic->shost, + "reset_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n", + snic_io_status_to_str(hdr_stat), cmnd_id); + ret = 1; + + return ret; + } + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + + if (!snic->remove_wait) { + spin_unlock_irqrestore(io_lock, flags); + SNIC_HOST_ERR(snic->shost, + "reset_cmpl:host reset completed after timout\n"); + ret = 1; + + return ret; + } + + rqi = (struct snic_req_info *) CMD_SP(sc); + WARN_ON_ONCE(!rqi); + + if (!rqi) { + atomic64_inc(&snic->s_stats.io.req_null); + spin_unlock_irqrestore(io_lock, flags); + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL; + SNIC_HOST_ERR(snic->shost, + "reset_cmpl: rqi is null,Hdr stat %s Tag 0x%x sc 0x%p flags 0x%llx\n", + snic_io_status_to_str(hdr_stat), cmnd_id, sc, + CMD_FLAGS(sc)); + + ret = 1; + + return ret; + } + /* stats */ + spin_unlock_irqrestore(io_lock, flags); + + /* scsi cleanup */ + snic_hba_reset_scsi_cleanup(snic, sc); + + SNIC_BUG_ON(snic_get_state(snic) != SNIC_OFFLINE && + snic_get_state(snic) != SNIC_FWRESET); + + /* Careful locking between snic_lock and io lock */ + spin_lock_irqsave(io_lock, flags); + spin_lock_irqsave(&snic->snic_lock, gflags); + if (snic_get_state(snic) == SNIC_FWRESET) + snic_set_state(snic, SNIC_ONLINE); + spin_unlock_irqrestore(&snic->snic_lock, gflags); + + if (snic->remove_wait) + complete(snic->remove_wait); + + spin_unlock_irqrestore(io_lock, flags); + atomic64_inc(&snic->s_stats.reset.hba_reset_cmpl); + + ret = 0; + /* Rediscovery is for SAN */ + if (snic->config.xpt_type == SNIC_DAS) + return ret; + + SNIC_SCSI_DBG(snic->shost, "reset_cmpl: Queuing discovery work.\n"); + queue_work(snic_glob->event_q, &snic->disc_work); + + return ret; +} + +static void +snic_msg_ack_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + SNIC_HOST_INFO(snic->shost, "Message Ack Received.\n"); + + SNIC_ASSERT_NOT_IMPL(1); +} + +static void +snic_aen_handler(struct snic *snic, struct snic_fw_req *fwreq) +{ + u8 typ, hdr_stat; + u32 cmnd_id, hid; + ulong ctx; + struct snic_async_evnotify *aen = &fwreq->u.async_ev; + u32 event_id = 0; + + snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx); + SNIC_SCSI_DBG(snic->shost, + "aen: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n", + typ, hdr_stat, cmnd_id, hid, ctx); + + event_id = le32_to_cpu(aen->ev_id); + + switch (event_id) { + case SNIC_EV_TGT_OFFLINE: + SNIC_HOST_INFO(snic->shost, "aen:TGT_OFFLINE Event Recvd.\n"); + break; + + case SNIC_EV_TGT_ONLINE: + SNIC_HOST_INFO(snic->shost, "aen:TGT_ONLINE Event Recvd.\n"); + break; + + case SNIC_EV_LUN_OFFLINE: + SNIC_HOST_INFO(snic->shost, "aen:LUN_OFFLINE Event Recvd.\n"); + break; + + case SNIC_EV_LUN_ONLINE: + SNIC_HOST_INFO(snic->shost, "aen:LUN_ONLINE Event Recvd.\n"); + break; + + case SNIC_EV_CONF_CHG: + SNIC_HOST_INFO(snic->shost, "aen:Config Change Event Recvd.\n"); + break; + + case SNIC_EV_TGT_ADDED: + SNIC_HOST_INFO(snic->shost, "aen:TGT_ADD Event Recvd.\n"); + break; + + case SNIC_EV_TGT_DELTD: + SNIC_HOST_INFO(snic->shost, "aen:TGT_DEL Event Recvd.\n"); + break; + + case SNIC_EV_LUN_ADDED: + SNIC_HOST_INFO(snic->shost, "aen:LUN_ADD Event Recvd.\n"); + break; + + case SNIC_EV_LUN_DELTD: + SNIC_HOST_INFO(snic->shost, "aen:LUN_DEL Event Recvd.\n"); + break; + + case SNIC_EV_DISC_CMPL: + SNIC_HOST_INFO(snic->shost, "aen:DISC_CMPL Event Recvd.\n"); + break; + + default: + SNIC_HOST_INFO(snic->shost, "aen:Unknown Event Recvd.\n"); + SNIC_BUG_ON(1); + break; + } + + SNIC_ASSERT_NOT_IMPL(1); +} /* end of snic_aen_handler */ + +/* + * snic_io_cmpl_handler + * Routine to process CQ entries(IO Completions) posted by fw. + */ +static int +snic_io_cmpl_handler(struct vnic_dev *vdev, + unsigned int cq_idx, + struct snic_fw_req *fwreq) +{ + struct snic *snic = svnic_dev_priv(vdev); + u64 start = jiffies, cmpl_time; + + snic_print_desc(__func__, (char *)fwreq, sizeof(*fwreq)); + + /* Update FW Stats */ + if ((fwreq->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL) && + (fwreq->hdr.type <= SNIC_RSP_BOOT_LUNS_CMPL)) + atomic64_dec(&snic->s_stats.fw.actv_reqs); + + SNIC_BUG_ON((fwreq->hdr.type > SNIC_RSP_BOOT_LUNS_CMPL) && + (fwreq->hdr.type < SNIC_MSG_ASYNC_EVNOTIFY)); + + /* Check for snic subsys errors */ + switch (fwreq->hdr.status) { + case SNIC_STAT_NOT_READY: /* XPT yet to initialize */ + SNIC_HOST_ERR(snic->shost, + "sNIC SubSystem is NOT Ready.\n"); + break; + + case SNIC_STAT_FATAL_ERROR: /* XPT Error */ + SNIC_HOST_ERR(snic->shost, + "sNIC SubSystem in Unrecoverable State.\n"); + break; + } + + switch (fwreq->hdr.type) { + case SNIC_RSP_EXCH_VER_CMPL: + snic_io_exch_ver_cmpl_handler(snic, fwreq); + break; + + case SNIC_RSP_REPORT_TGTS_CMPL: + snic_report_tgt_cmpl_handler(snic, fwreq); + break; + + case SNIC_RSP_ICMND_CMPL: + snic_icmnd_cmpl_handler(snic, fwreq); + break; + + case SNIC_RSP_ITMF_CMPL: + snic_itmf_cmpl_handler(snic, fwreq); + break; + + case SNIC_RSP_HBA_RESET_CMPL: + snic_hba_reset_cmpl_handler(snic, fwreq); + break; + + case SNIC_MSG_ACK: + snic_msg_ack_handler(snic, fwreq); + break; + + case SNIC_MSG_ASYNC_EVNOTIFY: + snic_aen_handler(snic, fwreq); + break; + + default: + SNIC_BUG_ON(1); + SNIC_SCSI_DBG(snic->shost, + "Unknown Firmwqre completion request type %d\n", + fwreq->hdr.type); + break; + } + + /* Update Stats */ + cmpl_time = jiffies - start; + if (cmpl_time > atomic64_read(&snic->s_stats.io.max_cmpl_time)) + atomic64_set(&snic->s_stats.io.max_cmpl_time, cmpl_time); + + return 0; +} /* end of snic_io_cmpl_handler */ + +/* + * snic_fwcq_cmpl_handler + * Routine to process fwCQ + * This CQ is independent, and not associated with wq/rq/wq_copy queues + */ +int +snic_fwcq_cmpl_handler(struct snic *snic, int io_cmpl_work) +{ + unsigned int num_ent = 0; /* number cq entries processed */ + unsigned int cq_idx; + unsigned int nent_per_cq; + struct snic_misc_stats *misc_stats = &snic->s_stats.misc; + + for (cq_idx = snic->wq_count; cq_idx < snic->cq_count; cq_idx++) { + nent_per_cq = vnic_cq_fw_service(&snic->cq[cq_idx], + snic_io_cmpl_handler, + io_cmpl_work); + num_ent += nent_per_cq; + + if (nent_per_cq > atomic64_read(&misc_stats->max_cq_ents)) + atomic64_set(&misc_stats->max_cq_ents, nent_per_cq); + } + + return num_ent; +} /* end of snic_fwcq_cmpl_handler */ + +/* + * snic_queue_itmf_req: Common API to queue Task Management requests. + * Use rqi->tm_tag for passing special tags. + * @req_id : aborted request's tag, -1 for lun reset. + */ +static int +snic_queue_itmf_req(struct snic *snic, + struct snic_host_req *tmreq, + struct scsi_cmnd *sc, + u32 tmf, + u32 req_id) +{ + struct snic_req_info *rqi = req_to_rqi(tmreq); + struct scsi_lun lun; + int tm_tag = snic_cmd_tag(sc) | rqi->tm_tag; + int ret = 0; + + SNIC_BUG_ON(!rqi); + SNIC_BUG_ON(!rqi->tm_tag); + + /* fill in lun info */ + int_to_scsilun(sc->device->lun, &lun); + + /* Initialize snic_host_req: itmf */ + snic_itmf_init(tmreq, + tm_tag, + snic->config.hid, + (ulong) rqi, + 0 /* flags */, + req_id, /* Command to be aborted. */ + rqi->tgt_id, + lun.scsi_lun, + tmf); + + /* + * In case of multiple aborts on same cmd, + * use try_wait_for_completion and completion_done() to check + * whether it queues aborts even after completion of abort issued + * prior.SNIC_BUG_ON(completion_done(&rqi->done)); + */ + + ret = snic_queue_wq_desc(snic, tmreq, sizeof(*tmreq)); + if (ret) + SNIC_HOST_ERR(snic->shost, + "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d tag %d Failed, ret = %d\n", + tmf, sc, rqi, req_id, snic_cmd_tag(sc), ret); + else + SNIC_SCSI_DBG(snic->shost, + "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d, tag %d (req_id)- Success.", + tmf, sc, rqi, req_id, snic_cmd_tag(sc)); + + return ret; +} /* end of snic_queue_itmf_req */ + +static int +snic_issue_tm_req(struct snic *snic, + struct snic_req_info *rqi, + struct scsi_cmnd *sc, + int tmf) +{ + struct snic_host_req *tmreq = NULL; + int req_id = 0, tag = snic_cmd_tag(sc); + int ret = 0; + + if (snic_get_state(snic) == SNIC_FWRESET) + return -EBUSY; + + atomic_inc(&snic->ios_inflight); + + SNIC_SCSI_DBG(snic->shost, + "issu_tmreq: Task mgmt req %d. rqi %p w/ tag %x\n", + tmf, rqi, tag); + + + if (tmf == SNIC_ITMF_LUN_RESET) { + tmreq = snic_dr_req_init(snic, rqi); + req_id = SCSI_NO_TAG; + } else { + tmreq = snic_abort_req_init(snic, rqi); + req_id = tag; + } + + if (!tmreq) { + ret = -ENOMEM; + + goto tmreq_err; + } + + ret = snic_queue_itmf_req(snic, tmreq, sc, tmf, req_id); + if (ret) + goto tmreq_err; + + ret = 0; + +tmreq_err: + if (ret) { + SNIC_HOST_ERR(snic->shost, + "issu_tmreq: Queing ITMF(%d) Req, sc %p rqi %p req_id %d tag %x fails err = %d\n", + tmf, sc, rqi, req_id, tag, ret); + } else { + SNIC_SCSI_DBG(snic->shost, + "issu_tmreq: Queuing ITMF(%d) Req, sc %p, rqi %p, req_id %d tag %x - Success.\n", + tmf, sc, rqi, req_id, tag); + } + + atomic_dec(&snic->ios_inflight); + + return ret; +} + +/* + * snic_queue_abort_req : Queues abort req to WQ + */ +static int +snic_queue_abort_req(struct snic *snic, + struct snic_req_info *rqi, + struct scsi_cmnd *sc, + int tmf) +{ + SNIC_SCSI_DBG(snic->shost, "q_abtreq: sc %p, rqi %p, tag %x, tmf %d\n", + sc, rqi, snic_cmd_tag(sc), tmf); + + /* Add special tag for abort */ + rqi->tm_tag |= SNIC_TAG_ABORT; + + return snic_issue_tm_req(snic, rqi, sc, tmf); +} + +/* + * snic_abort_finish : called by snic_abort_cmd on queuing abort successfully. + */ +static int +snic_abort_finish(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + int ret = 0, tag = snic_cmd_tag(sc); + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + atomic64_inc(&snic->s_stats.io.req_null); + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL; + + SNIC_SCSI_DBG(snic->shost, + "abt_fini:req info is null tag 0x%x, sc 0x%p flags 0x%llx\n", + tag, sc, CMD_FLAGS(sc)); + ret = FAILED; + + goto abort_fail; + } + + rqi->abts_done = NULL; + + ret = FAILED; + + /* Check the abort status. */ + switch (CMD_ABTS_STATUS(sc)) { + case SNIC_INVALID_CODE: + /* Firmware didn't complete abort req, timedout */ + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TIMEDOUT; + atomic64_inc(&snic->s_stats.abts.drv_tmo); + SNIC_SCSI_DBG(snic->shost, + "abt_fini:sc %p Tag %x Driver Timeout.flags 0x%llx\n", + sc, snic_cmd_tag(sc), CMD_FLAGS(sc)); + /* do not release snic request in timedout case */ + rqi = NULL; + + goto abort_fail; + + case SNIC_STAT_IO_SUCCESS: + case SNIC_STAT_IO_NOT_FOUND: + ret = SUCCESS; + break; + + default: + /* Firmware completed abort with error */ + ret = FAILED; + break; + } + + CMD_SP(sc) = NULL; + SNIC_HOST_INFO(snic->shost, + "abt_fini: Tag %x, Cmpl Status %s flags 0x%llx\n", + tag, snic_io_status_to_str(CMD_ABTS_STATUS(sc)), + CMD_FLAGS(sc)); + +abort_fail: + spin_unlock_irqrestore(io_lock, flags); + if (rqi) + snic_release_req_buf(snic, rqi, sc); + + return ret; +} /* end of snic_abort_finish */ + +/* + * snic_send_abort_and_wait : Issues Abort, and Waits + */ +static int +snic_send_abort_and_wait(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + enum snic_ioreq_state sv_state; + struct snic_tgt *tgt = NULL; + spinlock_t *io_lock = NULL; + DECLARE_COMPLETION_ONSTACK(tm_done); + unsigned long flags; + int ret = 0, tmf = 0, tag = snic_cmd_tag(sc); + + tgt = starget_to_tgt(scsi_target(sc->device)); + if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN)) + tmf = SNIC_ITMF_ABTS_TASK_TERM; + else + tmf = SNIC_ITMF_ABTS_TASK; + + /* stats */ + + io_lock = snic_io_lock_hash(snic, sc); + + /* + * Avoid a race between SCSI issuing the abort and the device + * completing the command. + * + * If the command is already completed by fw_cmpl code, + * we just return SUCCESS from here. This means that the abort + * succeeded. In the SCSI ML, since the timeout for command has + * happend, the completion wont actually complete the command + * and it will be considered as an aborted command + * + * The CMD_SP will not be cleared except while holding io_lock + */ + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + spin_unlock_irqrestore(io_lock, flags); + + SNIC_HOST_ERR(snic->shost, + "abt_cmd: rqi is null. Tag %d flags 0x%llx\n", + tag, CMD_FLAGS(sc)); + + ret = SUCCESS; + + goto send_abts_end; + } + + rqi->abts_done = &tm_done; + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) { + spin_unlock_irqrestore(io_lock, flags); + + ret = 0; + goto abts_pending; + } + SNIC_BUG_ON(!rqi->abts_done); + + /* Save Command State, should be restored on failed to Queue. */ + sv_state = CMD_STATE(sc); + + /* + * Command is still pending, need to abort it + * If the fw completes the command after this point, + * the completion won't be done till mid-layer, since abot + * has already started. + */ + CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING; + CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE; + + SNIC_SCSI_DBG(snic->shost, "send_abt_cmd: TAG 0x%x\n", tag); + + spin_unlock_irqrestore(io_lock, flags); + + /* Now Queue the abort command to firmware */ + ret = snic_queue_abort_req(snic, rqi, sc, tmf); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "send_abt_cmd: IO w/ Tag 0x%x fail w/ err %d flags 0x%llx\n", + tag, ret, CMD_FLAGS(sc)); + + spin_lock_irqsave(io_lock, flags); + /* Restore Command's previous state */ + CMD_STATE(sc) = sv_state; + rqi = (struct snic_req_info *) CMD_SP(sc); + if (rqi) + rqi->abts_done = NULL; + spin_unlock_irqrestore(io_lock, flags); + ret = FAILED; + + goto send_abts_end; + } + + spin_lock_irqsave(io_lock, flags); + if (tmf == SNIC_ITMF_ABTS_TASK) { + CMD_FLAGS(sc) |= SNIC_IO_ABTS_ISSUED; + atomic64_inc(&snic->s_stats.abts.num); + } else { + /* term stats */ + CMD_FLAGS(sc) |= SNIC_IO_TERM_ISSUED; + } + spin_unlock_irqrestore(io_lock, flags); + + SNIC_SCSI_DBG(snic->shost, + "send_abt_cmd: sc %p Tag %x flags 0x%llx\n", + sc, tag, CMD_FLAGS(sc)); + + + ret = 0; + +abts_pending: + /* + * Queued an abort IO, wait for its completion. + * Once the fw completes the abort command, it will + * wakeup this thread. + */ + wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT); + +send_abts_end: + return ret; +} /* end of snic_send_abort_and_wait */ + +/* + * This function is exported to SCSI for sending abort cmnds. + * A SCSI IO is represent by snic_ioreq in the driver. + * The snic_ioreq is linked to the SCSI Cmd, thus a link with the ULP'S IO + */ +int +snic_abort_cmd(struct scsi_cmnd *sc) +{ + struct snic *snic = shost_priv(sc->device->host); + int ret = SUCCESS, tag = snic_cmd_tag(sc); + u32 start_time = jiffies; + + SNIC_SCSI_DBG(snic->shost, "abt_cmd:sc %p :0x%x :req = %p :tag = %d\n", + sc, sc->cmnd[0], sc->request, tag); + + if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) { + SNIC_HOST_ERR(snic->shost, + "abt_cmd: tag %x Parent Devs are not rdy\n", + tag); + ret = FAST_IO_FAIL; + + goto abort_end; + } + + + ret = snic_send_abort_and_wait(snic, sc); + if (ret) + goto abort_end; + + ret = snic_abort_finish(snic, sc); + +abort_end: + SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), 0, + SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); + + SNIC_SCSI_DBG(snic->shost, + "abts: Abort Req Status = %s\n", + (ret == SUCCESS) ? "SUCCESS" : + ((ret == FAST_IO_FAIL) ? "FAST_IO_FAIL" : "FAILED")); + + return ret; +} + + + +static int +snic_is_abts_pending(struct snic *snic, struct scsi_cmnd *lr_sc) +{ + struct snic_req_info *rqi = NULL; + struct scsi_cmnd *sc = NULL; + struct scsi_device *lr_sdev = NULL; + spinlock_t *io_lock = NULL; + u32 tag; + unsigned long flags; + + if (lr_sc) + lr_sdev = lr_sc->device; + + /* walk through the tag map, an dcheck if IOs are still pending in fw*/ + for (tag = 0; tag < snic->max_tag_id; tag++) { + io_lock = snic_io_lock_tag(snic, tag); + + spin_lock_irqsave(io_lock, flags); + sc = scsi_host_find_tag(snic->shost, tag); + + if (!sc || (lr_sc && (sc->device != lr_sdev || sc == lr_sc))) { + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + + /* + * Found IO that is still pending w/ firmware and belongs to + * the LUN that is under reset, if lr_sc != NULL + */ + SNIC_SCSI_DBG(snic->shost, "Found IO in %s on LUN\n", + snic_ioreq_state_to_str(CMD_STATE(sc))); + + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) { + spin_unlock_irqrestore(io_lock, flags); + + return 1; + } + + spin_unlock_irqrestore(io_lock, flags); + } + + return 0; +} /* end of snic_is_abts_pending */ + +static int +snic_dr_clean_single_req(struct snic *snic, + u32 tag, + struct scsi_device *lr_sdev) +{ + struct snic_req_info *rqi = NULL; + struct snic_tgt *tgt = NULL; + struct scsi_cmnd *sc = NULL; + spinlock_t *io_lock = NULL; + u32 sv_state = 0, tmf = 0; + DECLARE_COMPLETION_ONSTACK(tm_done); + unsigned long flags; + int ret = 0; + + io_lock = snic_io_lock_tag(snic, tag); + spin_lock_irqsave(io_lock, flags); + sc = scsi_host_find_tag(snic->shost, tag); + + /* Ignore Cmd that don't belong to Lun Reset device */ + if (!sc || sc->device != lr_sdev) + goto skip_clean; + + rqi = (struct snic_req_info *) CMD_SP(sc); + + if (!rqi) + goto skip_clean; + + + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) + goto skip_clean; + + + if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) && + (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) { + + SNIC_SCSI_DBG(snic->shost, + "clean_single_req: devrst is not pending sc 0x%p\n", + sc); + + goto skip_clean; + } + + SNIC_SCSI_DBG(snic->shost, + "clean_single_req: Found IO in %s on lun\n", + snic_ioreq_state_to_str(CMD_STATE(sc))); + + /* Save Command State */ + sv_state = CMD_STATE(sc); + + /* + * Any pending IO issued prior to reset is expected to be + * in abts pending state, if not we need to set SNIC_IOREQ_ABTS_PENDING + * to indicate the IO is abort pending. + * When IO is completed, the IO will be handed over and handled + * in this function. + */ + + CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING; + SNIC_BUG_ON(rqi->abts_done); + + if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) { + rqi->tm_tag = SNIC_TAG_DEV_RST; + + SNIC_SCSI_DBG(snic->shost, + "clean_single_req:devrst sc 0x%p\n", sc); + } + + CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE; + rqi->abts_done = &tm_done; + spin_unlock_irqrestore(io_lock, flags); + + tgt = starget_to_tgt(scsi_target(sc->device)); + if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN)) + tmf = SNIC_ITMF_ABTS_TASK_TERM; + else + tmf = SNIC_ITMF_ABTS_TASK; + + /* Now queue the abort command to firmware */ + ret = snic_queue_abort_req(snic, rqi, sc, tmf); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "clean_single_req_err:sc %p, tag %d abt failed. tm_tag %d flags 0x%llx\n", + sc, tag, rqi->tm_tag, CMD_FLAGS(sc)); + + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (rqi) + rqi->abts_done = NULL; + + /* Restore Command State */ + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) + CMD_STATE(sc) = sv_state; + + ret = 1; + goto skip_clean; + } + + spin_lock_irqsave(io_lock, flags); + if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) + CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED; + + CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED; + spin_unlock_irqrestore(io_lock, flags); + + wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT); + + /* Recheck cmd state to check if it now aborted. */ + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL; + goto skip_clean; + } + rqi->abts_done = NULL; + + /* if abort is still pending w/ fw, fail */ + if (CMD_ABTS_STATUS(sc) == SNIC_INVALID_CODE) { + SNIC_HOST_ERR(snic->shost, + "clean_single_req_err:sc %p tag %d abt still pending w/ fw, tm_tag %d flags 0x%llx\n", + sc, tag, rqi->tm_tag, CMD_FLAGS(sc)); + + CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE; + ret = 1; + + goto skip_clean; + } + + CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE; + CMD_SP(sc) = NULL; + spin_unlock_irqrestore(io_lock, flags); + + snic_release_req_buf(snic, rqi, sc); + + ret = 0; + + return ret; + +skip_clean: + spin_unlock_irqrestore(io_lock, flags); + + return ret; +} /* end of snic_dr_clean_single_req */ + +static int +snic_dr_clean_pending_req(struct snic *snic, struct scsi_cmnd *lr_sc) +{ + struct scsi_device *lr_sdev = lr_sc->device; + u32 tag = 0; + int ret = FAILED; + + for (tag = 0; tag < snic->max_tag_id; tag++) { + if (tag == snic_cmd_tag(lr_sc)) + continue; + + ret = snic_dr_clean_single_req(snic, tag, lr_sdev); + if (ret) { + SNIC_HOST_ERR(snic->shost, "clean_err:tag = %d\n", tag); + + goto clean_err; + } + } + + schedule_timeout(msecs_to_jiffies(100)); + + /* Walk through all the cmds and check abts status. */ + if (snic_is_abts_pending(snic, lr_sc)) { + ret = FAILED; + + goto clean_err; + } + + ret = 0; + SNIC_SCSI_DBG(snic->shost, "clean_pending_req: Success.\n"); + + return ret; + +clean_err: + ret = FAILED; + SNIC_HOST_ERR(snic->shost, + "Failed to Clean Pending IOs on %s device.\n", + dev_name(&lr_sdev->sdev_gendev)); + + return ret; + +} /* end of snic_dr_clean_pending_req */ + +/* + * snic_dr_finish : Called by snic_device_reset + */ +static int +snic_dr_finish(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + int lr_res = 0; + int ret = FAILED; + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + spin_unlock_irqrestore(io_lock, flags); + SNIC_SCSI_DBG(snic->shost, + "dr_fini: rqi is null tag 0x%x sc 0x%p flags 0x%llx\n", + snic_cmd_tag(sc), sc, CMD_FLAGS(sc)); + + ret = FAILED; + goto dr_fini_end; + } + + rqi->dr_done = NULL; + + lr_res = CMD_LR_STATUS(sc); + + switch (lr_res) { + case SNIC_INVALID_CODE: + /* stats */ + SNIC_SCSI_DBG(snic->shost, + "dr_fini: Tag %x Dev Reset Timedout. flags 0x%llx\n", + snic_cmd_tag(sc), CMD_FLAGS(sc)); + + CMD_FLAGS(sc) |= SNIC_DEV_RST_TIMEDOUT; + ret = FAILED; + + goto dr_failed; + + case SNIC_STAT_IO_SUCCESS: + SNIC_SCSI_DBG(snic->shost, + "dr_fini: Tag %x Dev Reset cmpl\n", + snic_cmd_tag(sc)); + ret = 0; + break; + + default: + SNIC_HOST_ERR(snic->shost, + "dr_fini:Device Reset completed& failed.Tag = %x lr_status %s flags 0x%llx\n", + snic_cmd_tag(sc), + snic_io_status_to_str(lr_res), CMD_FLAGS(sc)); + ret = FAILED; + goto dr_failed; + } + spin_unlock_irqrestore(io_lock, flags); + + /* + * Cleanup any IOs on this LUN that have still not completed. + * If any of these fail, then LUN Reset fails. + * Cleanup cleans all commands on this LUN except + * the lun reset command. If all cmds get cleaned, the LUN Reset + * succeeds. + */ + + ret = snic_dr_clean_pending_req(snic, sc); + if (ret) { + spin_lock_irqsave(io_lock, flags); + SNIC_SCSI_DBG(snic->shost, + "dr_fini: Device Reset Failed since could not abort all IOs. Tag = %x.\n", + snic_cmd_tag(sc)); + rqi = (struct snic_req_info *) CMD_SP(sc); + + goto dr_failed; + } else { + /* Cleanup LUN Reset Command */ + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (rqi) + ret = SUCCESS; /* Completed Successfully */ + else + ret = FAILED; + } + +dr_failed: + SNIC_BUG_ON(!spin_is_locked(io_lock)); + if (rqi) + CMD_SP(sc) = NULL; + spin_unlock_irqrestore(io_lock, flags); + + if (rqi) + snic_release_req_buf(snic, rqi, sc); + +dr_fini_end: + return ret; +} /* end of snic_dr_finish */ + +static int +snic_queue_dr_req(struct snic *snic, + struct snic_req_info *rqi, + struct scsi_cmnd *sc) +{ + /* Add special tag for device reset */ + rqi->tm_tag |= SNIC_TAG_DEV_RST; + + return snic_issue_tm_req(snic, rqi, sc, SNIC_ITMF_LUN_RESET); +} + +static int +snic_send_dr_and_wait(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + enum snic_ioreq_state sv_state; + spinlock_t *io_lock = NULL; + unsigned long flags; + DECLARE_COMPLETION_ONSTACK(tm_done); + int ret = FAILED, tag = snic_cmd_tag(sc); + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + CMD_FLAGS(sc) |= SNIC_DEVICE_RESET; + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + SNIC_HOST_ERR(snic->shost, + "send_dr: rqi is null, Tag 0x%x flags 0x%llx\n", + tag, CMD_FLAGS(sc)); + spin_unlock_irqrestore(io_lock, flags); + + ret = FAILED; + goto send_dr_end; + } + + /* Save Command state to restore in case Queuing failed. */ + sv_state = CMD_STATE(sc); + + CMD_STATE(sc) = SNIC_IOREQ_LR_PENDING; + CMD_LR_STATUS(sc) = SNIC_INVALID_CODE; + + SNIC_SCSI_DBG(snic->shost, "dr: TAG = %x\n", tag); + + rqi->dr_done = &tm_done; + SNIC_BUG_ON(!rqi->dr_done); + + spin_unlock_irqrestore(io_lock, flags); + /* + * The Command state is changed to IOREQ_PENDING, + * in this case, if the command is completed, the icmnd_cmpl will + * mark the cmd as completed. + * This logic still makes LUN Reset is inevitable. + */ + + ret = snic_queue_dr_req(snic, rqi, sc); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "send_dr: IO w/ Tag 0x%x Failed err = %d. flags 0x%llx\n", + tag, ret, CMD_FLAGS(sc)); + + spin_lock_irqsave(io_lock, flags); + /* Restore State */ + CMD_STATE(sc) = sv_state; + rqi = (struct snic_req_info *) CMD_SP(sc); + if (rqi) + rqi->dr_done = NULL; + /* rqi is freed in caller. */ + spin_unlock_irqrestore(io_lock, flags); + ret = FAILED; + + goto send_dr_end; + } + + spin_lock_irqsave(io_lock, flags); + CMD_FLAGS(sc) |= SNIC_DEV_RST_ISSUED; + spin_unlock_irqrestore(io_lock, flags); + + ret = 0; + + wait_for_completion_timeout(&tm_done, SNIC_LUN_RESET_TIMEOUT); + +send_dr_end: + return ret; +} + +/* + * auxillary funciton to check lun reset op is supported or not + * Not supported if returns 0 + */ +static int +snic_dev_reset_supported(struct scsi_device *sdev) +{ + struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev)); + + if (tgt->tdata.typ == SNIC_TGT_DAS) + return 0; + + return 1; +} + +static void +snic_unlink_and_release_req(struct snic *snic, struct scsi_cmnd *sc, int flag) +{ + struct snic_req_info *rqi = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + u32 start_time = jiffies; + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (rqi) { + start_time = rqi->start_time; + CMD_SP(sc) = NULL; + } + + CMD_FLAGS(sc) |= flag; + spin_unlock_irqrestore(io_lock, flags); + + if (rqi) + snic_release_req_buf(snic, rqi, sc); + + SNIC_TRC(snic->shost->host_no, snic_cmd_tag(sc), (ulong) sc, + jiffies_to_msecs(jiffies - start_time), (ulong) rqi, + SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); +} + +/* + * SCSI Eh thread issues a LUN Reset when one or more commands on a LUN + * fail to get aborted. It calls driver's eh_device_reset with a SCSI + * command on the LUN. + */ +int +snic_device_reset(struct scsi_cmnd *sc) +{ + struct Scsi_Host *shost = sc->device->host; + struct snic *snic = shost_priv(shost); + struct snic_req_info *rqi = NULL; + int tag = snic_cmd_tag(sc); + int start_time = jiffies; + int ret = FAILED; + int dr_supp = 0; + + SNIC_SCSI_DBG(shost, "dev_reset:sc %p :0x%x :req = %p :tag = %d\n", + sc, sc->cmnd[0], sc->request, + snic_cmd_tag(sc)); + dr_supp = snic_dev_reset_supported(sc->device); + if (!dr_supp) { + /* device reset op is not supported */ + SNIC_HOST_INFO(shost, "LUN Reset Op not supported.\n"); + snic_unlink_and_release_req(snic, sc, SNIC_DEV_RST_NOTSUP); + + goto dev_rst_end; + } + + if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) { + snic_unlink_and_release_req(snic, sc, 0); + SNIC_HOST_ERR(shost, "Devrst: Parent Devs are not online.\n"); + + goto dev_rst_end; + } + + /* There is no tag when lun reset is issue through ioctl. */ + if (unlikely(tag <= SNIC_NO_TAG)) { + SNIC_HOST_INFO(snic->shost, + "Devrst: LUN Reset Recvd thru IOCTL.\n"); + + rqi = snic_req_init(snic, 0); + if (!rqi) + goto dev_rst_end; + + memset(scsi_cmd_priv(sc), 0, + sizeof(struct snic_internal_io_state)); + CMD_SP(sc) = (char *)rqi; + CMD_FLAGS(sc) = SNIC_NO_FLAGS; + + /* Add special tag for dr coming from user spc */ + rqi->tm_tag = SNIC_TAG_IOCTL_DEV_RST; + rqi->sc = sc; + } + + ret = snic_send_dr_and_wait(snic, sc); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "Devrst: IO w/ Tag %x Failed w/ err = %d\n", + tag, ret); + + snic_unlink_and_release_req(snic, sc, 0); + + goto dev_rst_end; + } + + ret = snic_dr_finish(snic, sc); + +dev_rst_end: + SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, + jiffies_to_msecs(jiffies - start_time), + 0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); + + SNIC_SCSI_DBG(snic->shost, + "Devrst: Returning from Device Reset : %s\n", + (ret == SUCCESS) ? "SUCCESS" : "FAILED"); + + return ret; +} /* end of snic_device_reset */ + +/* + * SCSI Error handling calls driver's eh_host_reset if all prior + * error handling levels return FAILED. + * + * Host Reset is the highest level of error recovery. If this fails, then + * host is offlined by SCSI. + */ +/* + * snic_issue_hba_reset : Queues FW Reset Request. + */ +static int +snic_issue_hba_reset(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + struct snic_host_req *req = NULL; + spinlock_t *io_lock = NULL; + DECLARE_COMPLETION_ONSTACK(wait); + unsigned long flags; + int ret = -ENOMEM; + + rqi = snic_req_init(snic, 0); + if (!rqi) { + ret = -ENOMEM; + + goto hba_rst_end; + } + + if (snic_cmd_tag(sc) == SCSI_NO_TAG) { + memset(scsi_cmd_priv(sc), 0, + sizeof(struct snic_internal_io_state)); + SNIC_HOST_INFO(snic->shost, "issu_hr:Host reset thru ioctl.\n"); + rqi->sc = sc; + } + + req = rqi_to_req(rqi); + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + SNIC_BUG_ON(CMD_SP(sc) != NULL); + CMD_STATE(sc) = SNIC_IOREQ_PENDING; + CMD_SP(sc) = (char *) rqi; + CMD_FLAGS(sc) |= SNIC_IO_INITIALIZED; + snic->remove_wait = &wait; + spin_unlock_irqrestore(io_lock, flags); + + /* Initialize Request */ + snic_io_hdr_enc(&req->hdr, SNIC_REQ_HBA_RESET, 0, snic_cmd_tag(sc), + snic->config.hid, 0, (ulong) rqi); + + req->u.reset.flags = 0; + + ret = snic_queue_wq_desc(snic, req, sizeof(*req)); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "issu_hr:Queuing HBA Reset Failed. w err %d\n", + ret); + + goto hba_rst_err; + } + + spin_lock_irqsave(io_lock, flags); + CMD_FLAGS(sc) |= SNIC_HOST_RESET_ISSUED; + spin_unlock_irqrestore(io_lock, flags); + atomic64_inc(&snic->s_stats.reset.hba_resets); + SNIC_HOST_INFO(snic->shost, "Queued HBA Reset Successfully.\n"); + + wait_for_completion_timeout(snic->remove_wait, + SNIC_HOST_RESET_TIMEOUT); + + if (snic_get_state(snic) == SNIC_FWRESET) { + SNIC_HOST_ERR(snic->shost, "reset_cmpl: Reset Timedout.\n"); + ret = -ETIMEDOUT; + + goto hba_rst_err; + } + + spin_lock_irqsave(io_lock, flags); + snic->remove_wait = NULL; + rqi = (struct snic_req_info *) CMD_SP(sc); + CMD_SP(sc) = NULL; + spin_unlock_irqrestore(io_lock, flags); + + if (rqi) + snic_req_free(snic, rqi); + + ret = 0; + + return ret; + +hba_rst_err: + spin_lock_irqsave(io_lock, flags); + snic->remove_wait = NULL; + rqi = (struct snic_req_info *) CMD_SP(sc); + CMD_SP(sc) = NULL; + spin_unlock_irqrestore(io_lock, flags); + + if (rqi) + snic_req_free(snic, rqi); + +hba_rst_end: + SNIC_HOST_ERR(snic->shost, + "reset:HBA Reset Failed w/ err = %d.\n", + ret); + + return ret; +} /* end of snic_issue_hba_reset */ + +int +snic_reset(struct Scsi_Host *shost, struct scsi_cmnd *sc) +{ + struct snic *snic = shost_priv(shost); + enum snic_state sv_state; + unsigned long flags; + int ret = FAILED; + + /* Set snic state as SNIC_FWRESET*/ + sv_state = snic_get_state(snic); + + spin_lock_irqsave(&snic->snic_lock, flags); + if (snic_get_state(snic) == SNIC_FWRESET) { + spin_unlock_irqrestore(&snic->snic_lock, flags); + SNIC_HOST_INFO(shost, "reset:prev reset is in progres\n"); + + msleep(SNIC_HOST_RESET_TIMEOUT); + ret = SUCCESS; + + goto reset_end; + } + + snic_set_state(snic, SNIC_FWRESET); + spin_unlock_irqrestore(&snic->snic_lock, flags); + + + /* Wait for all the IOs that are entered in Qcmd */ + while (atomic_read(&snic->ios_inflight)) + schedule_timeout(msecs_to_jiffies(1)); + + ret = snic_issue_hba_reset(snic, sc); + if (ret) { + SNIC_HOST_ERR(shost, + "reset:Host Reset Failed w/ err %d.\n", + ret); + spin_lock_irqsave(&snic->snic_lock, flags); + snic_set_state(snic, sv_state); + spin_unlock_irqrestore(&snic->snic_lock, flags); + atomic64_inc(&snic->s_stats.reset.hba_reset_fail); + ret = FAILED; + + goto reset_end; + } + + ret = SUCCESS; + +reset_end: + return ret; +} /* end of snic_reset */ + +/* + * SCSI Error handling calls driver's eh_host_reset if all prior + * error handling levels return FAILED. + * + * Host Reset is the highest level of error recovery. If this fails, then + * host is offlined by SCSI. + */ +int +snic_host_reset(struct scsi_cmnd *sc) +{ + struct Scsi_Host *shost = sc->device->host; + u32 start_time = jiffies; + int ret = FAILED; + + SNIC_SCSI_DBG(shost, + "host reset:sc %p sc_cmd 0x%x req %p tag %d flags 0x%llx\n", + sc, sc->cmnd[0], sc->request, + snic_cmd_tag(sc), CMD_FLAGS(sc)); + + ret = snic_reset(shost, sc); + + SNIC_TRC(shost->host_no, snic_cmd_tag(sc), (ulong) sc, + jiffies_to_msecs(jiffies - start_time), + 0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc)); + + return ret; +} /* end of snic_host_reset */ + +/* + * snic_cmpl_pending_tmreq : Caller should hold io_lock + */ +static void +snic_cmpl_pending_tmreq(struct snic *snic, struct scsi_cmnd *sc) +{ + struct snic_req_info *rqi = NULL; + + SNIC_SCSI_DBG(snic->shost, + "Completing Pending TM Req sc %p, state %s flags 0x%llx\n", + sc, snic_io_status_to_str(CMD_STATE(sc)), CMD_FLAGS(sc)); + + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) + return; + + if (rqi->dr_done) + complete(rqi->dr_done); + else if (rqi->abts_done) + complete(rqi->abts_done); +} + +/* + * snic_scsi_cleanup: Walks through tag map and releases the reqs + */ +static void +snic_scsi_cleanup(struct snic *snic, int ex_tag) +{ + struct snic_req_info *rqi = NULL; + struct scsi_cmnd *sc = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + int tag; + u64 st_time = 0; + + SNIC_SCSI_DBG(snic->shost, "sc_clean: scsi cleanup.\n"); + + for (tag = 0; tag < snic->max_tag_id; tag++) { + /* Skip ex_tag */ + if (tag == ex_tag) + continue; + + io_lock = snic_io_lock_tag(snic, tag); + spin_lock_irqsave(io_lock, flags); + sc = scsi_host_find_tag(snic->shost, tag); + if (!sc) { + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + + if (unlikely(snic_tmreq_pending(sc))) { + /* + * When FW Completes reset w/o sending completions + * for outstanding ios. + */ + snic_cmpl_pending_tmreq(snic, sc); + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) { + spin_unlock_irqrestore(io_lock, flags); + + goto cleanup; + } + + SNIC_SCSI_DBG(snic->shost, + "sc_clean: sc %p, rqi %p, tag %d flags 0x%llx\n", + sc, rqi, tag, CMD_FLAGS(sc)); + + CMD_SP(sc) = NULL; + CMD_FLAGS(sc) |= SNIC_SCSI_CLEANUP; + spin_unlock_irqrestore(io_lock, flags); + st_time = rqi->start_time; + + SNIC_HOST_INFO(snic->shost, + "sc_clean: Releasing rqi %p : flags 0x%llx\n", + rqi, CMD_FLAGS(sc)); + + snic_release_req_buf(snic, rqi, sc); + +cleanup: + sc->result = DID_TRANSPORT_DISRUPTED << 16; + SNIC_HOST_INFO(snic->shost, + "sc_clean: DID_TRANSPORT_DISRUPTED for sc %p. rqi %p duration %llu msecs\n", + sc, rqi, (jiffies - st_time)); + + /* Update IO stats */ + snic_stats_update_io_cmpl(&snic->s_stats); + + if (sc->scsi_done) { + SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, + jiffies_to_msecs(jiffies - st_time), 0, + SNIC_TRC_CMD(sc), + SNIC_TRC_CMD_STATE_FLAGS(sc)); + + sc->scsi_done(sc); + } + } +} /* end of snic_scsi_cleanup */ + +void +snic_shutdown_scsi_cleanup(struct snic *snic) +{ + SNIC_HOST_INFO(snic->shost, "Shutdown time SCSI Cleanup.\n"); + + snic_scsi_cleanup(snic, SCSI_NO_TAG); +} /* end of snic_shutdown_scsi_cleanup */ + +/* + * snic_internal_abort_io + * called by : snic_tgt_scsi_abort_io + */ +static int +snic_internal_abort_io(struct snic *snic, struct scsi_cmnd *sc, int tmf) +{ + struct snic_req_info *rqi = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + u32 sv_state = 0; + int ret = 0; + + io_lock = snic_io_lock_hash(snic, sc); + spin_lock_irqsave(io_lock, flags); + rqi = (struct snic_req_info *) CMD_SP(sc); + if (!rqi) + goto skip_internal_abts; + + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) + goto skip_internal_abts; + + if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) && + (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) { + + SNIC_SCSI_DBG(snic->shost, + "internal_abts: dev rst not pending sc 0x%p\n", + sc); + + goto skip_internal_abts; + } + + + if (!(CMD_FLAGS(sc) & SNIC_IO_ISSUED)) { + SNIC_SCSI_DBG(snic->shost, + "internal_abts: IO not yet issued sc 0x%p tag 0x%x flags 0x%llx state %d\n", + sc, snic_cmd_tag(sc), CMD_FLAGS(sc), CMD_STATE(sc)); + + goto skip_internal_abts; + } + + sv_state = CMD_STATE(sc); + CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING; + CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE; + CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_PENDING; + + if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) { + /* stats */ + rqi->tm_tag = SNIC_TAG_DEV_RST; + SNIC_SCSI_DBG(snic->shost, "internal_abts:dev rst sc %p\n", sc); + } + + SNIC_SCSI_DBG(snic->shost, "internal_abts: Issuing abts tag %x\n", + snic_cmd_tag(sc)); + SNIC_BUG_ON(rqi->abts_done); + spin_unlock_irqrestore(io_lock, flags); + + ret = snic_queue_abort_req(snic, rqi, sc, tmf); + if (ret) { + SNIC_HOST_ERR(snic->shost, + "internal_abts: Tag = %x , Failed w/ err = %d\n", + snic_cmd_tag(sc), ret); + + spin_lock_irqsave(io_lock, flags); + + if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) + CMD_STATE(sc) = sv_state; + + goto skip_internal_abts; + } + + spin_lock_irqsave(io_lock, flags); + if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) + CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED; + else + CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED; + + ret = SUCCESS; + +skip_internal_abts: + SNIC_BUG_ON(!spin_is_locked(io_lock)); + spin_unlock_irqrestore(io_lock, flags); + + return ret; +} /* end of snic_internal_abort_io */ + +/* + * snic_tgt_scsi_abort_io : called by snic_tgt_del + */ +int +snic_tgt_scsi_abort_io(struct snic_tgt *tgt) +{ + struct snic *snic = NULL; + struct scsi_cmnd *sc = NULL; + struct snic_tgt *sc_tgt = NULL; + spinlock_t *io_lock = NULL; + unsigned long flags; + int ret = 0, tag, abt_cnt = 0, tmf = 0; + + if (!tgt) + return -1; + + snic = shost_priv(snic_tgt_to_shost(tgt)); + SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: Cleaning Pending IOs.\n"); + + if (tgt->tdata.typ == SNIC_TGT_DAS) + tmf = SNIC_ITMF_ABTS_TASK; + else + tmf = SNIC_ITMF_ABTS_TASK_TERM; + + for (tag = 0; tag < snic->max_tag_id; tag++) { + io_lock = snic_io_lock_tag(snic, tag); + + spin_lock_irqsave(io_lock, flags); + sc = scsi_host_find_tag(snic->shost, tag); + if (!sc) { + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + + sc_tgt = starget_to_tgt(scsi_target(sc->device)); + if (sc_tgt != tgt) { + spin_unlock_irqrestore(io_lock, flags); + + continue; + } + spin_unlock_irqrestore(io_lock, flags); + + ret = snic_internal_abort_io(snic, sc, tmf); + if (ret < 0) { + SNIC_HOST_ERR(snic->shost, + "tgt_abt_io: Tag %x, Failed w err = %d\n", + tag, ret); + + continue; + } + + if (ret == SUCCESS) + abt_cnt++; + } + + SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: abt_cnt = %d\n", abt_cnt); + + return 0; +} /* end of snic_tgt_scsi_abort_io */ diff --git a/kernel/drivers/scsi/snic/snic_stats.h b/kernel/drivers/scsi/snic/snic_stats.h new file mode 100644 index 000000000..11e614849 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_stats.h @@ -0,0 +1,123 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __SNIC_STATS_H +#define __SNIC_STATS_H + +struct snic_io_stats { + atomic64_t active; /* Active IOs */ + atomic64_t max_active; /* Max # active IOs */ + atomic64_t max_sgl; /* Max # SGLs for any IO */ + atomic64_t max_time; /* Max time to process IO */ + atomic64_t max_qtime; /* Max time to Queue the IO */ + atomic64_t max_cmpl_time; /* Max time to complete the IO */ + atomic64_t sgl_cnt[SNIC_MAX_SG_DESC_CNT]; /* SGL Counters */ + atomic64_t max_io_sz; /* Max IO Size */ + atomic64_t compl; /* IO Completions */ + atomic64_t fail; /* IO Failures */ + atomic64_t req_null; /* req or req info is NULL */ + atomic64_t alloc_fail; /* Alloc Failures */ + atomic64_t sc_null; + atomic64_t io_not_found; /* IO Not Found */ + atomic64_t num_ios; /* Number of IOs */ +}; + +struct snic_abort_stats { + atomic64_t num; /* Abort counter */ + atomic64_t fail; /* Abort Failure Counter */ + atomic64_t drv_tmo; /* Abort Driver Timeouts */ + atomic64_t fw_tmo; /* Abort Firmware Timeouts */ + atomic64_t io_not_found;/* Abort IO Not Found */ +}; + +struct snic_reset_stats { + atomic64_t dev_resets; /* Device Reset Counter */ + atomic64_t dev_reset_fail; /* Device Reset Failures */ + atomic64_t dev_reset_aborts; /* Device Reset Aborts */ + atomic64_t dev_reset_tmo; /* Device Reset Timeout */ + atomic64_t dev_reset_terms; /* Device Reset terminate */ + atomic64_t hba_resets; /* hba/firmware resets */ + atomic64_t hba_reset_cmpl; /* hba/firmware reset completions */ + atomic64_t hba_reset_fail; /* hba/firmware failures */ + atomic64_t snic_resets; /* snic resets */ + atomic64_t snic_reset_compl; /* snic reset completions */ + atomic64_t snic_reset_fail; /* snic reset failures */ +}; + +struct snic_fw_stats { + atomic64_t actv_reqs; /* Active Requests */ + atomic64_t max_actv_reqs; /* Max Active Requests */ + atomic64_t out_of_res; /* Firmware Out Of Resources */ + atomic64_t io_errs; /* Firmware IO Firmware Errors */ + atomic64_t scsi_errs; /* Target hits check condition */ +}; + +struct snic_misc_stats { + u64 last_isr_time; + u64 last_ack_time; + atomic64_t isr_cnt; + atomic64_t max_cq_ents; /* Max CQ Entries */ + atomic64_t data_cnt_mismat; /* Data Count Mismatch */ + atomic64_t io_tmo; + atomic64_t io_aborted; + atomic64_t sgl_inval; /* SGL Invalid */ + atomic64_t abts_wq_alloc_fail; /* Abort Path WQ desc alloc failure */ + atomic64_t devrst_wq_alloc_fail;/* Device Reset - WQ desc alloc fail */ + atomic64_t wq_alloc_fail; /* IO WQ desc alloc failure */ + atomic64_t no_icmnd_itmf_cmpls; + atomic64_t io_under_run; + atomic64_t qfull; + atomic64_t tgt_not_rdy; +}; + +struct snic_stats { + struct snic_io_stats io; + struct snic_abort_stats abts; + struct snic_reset_stats reset; + struct snic_fw_stats fw; + struct snic_misc_stats misc; + atomic64_t io_cmpl_skip; +}; + +int snic_stats_debugfs_init(struct snic *); +void snic_stats_debugfs_remove(struct snic *); + +/* Auxillary function to update active IO counter */ +static inline void +snic_stats_update_active_ios(struct snic_stats *s_stats) +{ + struct snic_io_stats *io = &s_stats->io; + u32 nr_active_ios; + + nr_active_ios = atomic64_inc_return(&io->active); + if (atomic64_read(&io->max_active) < nr_active_ios) + atomic64_set(&io->max_active, nr_active_ios); + + atomic64_inc(&io->num_ios); +} + +/* Auxillary function to update IO completion counter */ +static inline void +snic_stats_update_io_cmpl(struct snic_stats *s_stats) +{ + atomic64_dec(&s_stats->io.active); + if (unlikely(atomic64_read(&s_stats->io_cmpl_skip))) + atomic64_dec(&s_stats->io_cmpl_skip); + else + atomic64_inc(&s_stats->io.compl); +} +#endif /* __SNIC_STATS_H */ diff --git a/kernel/drivers/scsi/snic/snic_trc.c b/kernel/drivers/scsi/snic/snic_trc.c new file mode 100644 index 000000000..f00ebf471 --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_trc.c @@ -0,0 +1,181 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "snic_io.h" +#include "snic.h" + +/* + * snic_get_trc_buf : Allocates a trace record and returns. + */ +struct snic_trc_data * +snic_get_trc_buf(void) +{ + struct snic_trc *trc = &snic_glob->trc; + struct snic_trc_data *td = NULL; + unsigned long flags; + + spin_lock_irqsave(&trc->lock, flags); + td = &trc->buf[trc->wr_idx]; + trc->wr_idx++; + + if (trc->wr_idx == trc->max_idx) + trc->wr_idx = 0; + + if (trc->wr_idx != trc->rd_idx) { + spin_unlock_irqrestore(&trc->lock, flags); + + goto end; + } + + trc->rd_idx++; + if (trc->rd_idx == trc->max_idx) + trc->rd_idx = 0; + + td->ts = 0; /* Marker for checking the record, for complete data*/ + spin_unlock_irqrestore(&trc->lock, flags); + +end: + + return td; +} /* end of snic_get_trc_buf */ + +/* + * snic_fmt_trc_data : Formats trace data for printing. + */ +static int +snic_fmt_trc_data(struct snic_trc_data *td, char *buf, int buf_sz) +{ + int len = 0; + struct timespec tmspec; + + jiffies_to_timespec(td->ts, &tmspec); + + len += snprintf(buf, buf_sz, + "%lu.%10lu %-25s %3d %4x %16llx %16llx %16llx %16llx %16llx\n", + tmspec.tv_sec, + tmspec.tv_nsec, + td->fn, + td->hno, + td->tag, + td->data[0], td->data[1], td->data[2], td->data[3], + td->data[4]); + + return len; +} /* end of snic_fmt_trc_data */ + +/* + * snic_get_trc_data : Returns a formatted trace buffer. + */ +int +snic_get_trc_data(char *buf, int buf_sz) +{ + struct snic_trc_data *td = NULL; + struct snic_trc *trc = &snic_glob->trc; + unsigned long flags; + + spin_lock_irqsave(&trc->lock, flags); + if (trc->rd_idx == trc->wr_idx) { + spin_unlock_irqrestore(&trc->lock, flags); + + return -1; + } + td = &trc->buf[trc->rd_idx]; + + if (td->ts == 0) { + /* write in progress. */ + spin_unlock_irqrestore(&trc->lock, flags); + + return -1; + } + + trc->rd_idx++; + if (trc->rd_idx == trc->max_idx) + trc->rd_idx = 0; + spin_unlock_irqrestore(&trc->lock, flags); + + return snic_fmt_trc_data(td, buf, buf_sz); +} /* end of snic_get_trc_data */ + +/* + * snic_trc_init() : Configures Trace Functionality for snic. + */ +int +snic_trc_init(void) +{ + struct snic_trc *trc = &snic_glob->trc; + void *tbuf = NULL; + int tbuf_sz = 0, ret; + + tbuf_sz = (snic_trace_max_pages * PAGE_SIZE); + tbuf = vmalloc(tbuf_sz); + if (!tbuf) { + SNIC_ERR("Failed to Allocate Trace Buffer Size. %d\n", tbuf_sz); + SNIC_ERR("Trace Facility not enabled.\n"); + ret = -ENOMEM; + + return ret; + } + + memset(tbuf, 0, tbuf_sz); + trc->buf = (struct snic_trc_data *) tbuf; + spin_lock_init(&trc->lock); + + ret = snic_trc_debugfs_init(); + if (ret) { + SNIC_ERR("Failed to create Debugfs Files.\n"); + + goto error; + } + + trc->max_idx = (tbuf_sz / SNIC_TRC_ENTRY_SZ); + trc->rd_idx = trc->wr_idx = 0; + trc->enable = true; + SNIC_INFO("Trace Facility Enabled.\n Trace Buffer SZ %lu Pages.\n", + tbuf_sz / PAGE_SIZE); + ret = 0; + + return ret; + +error: + snic_trc_free(); + + return ret; +} /* end of snic_trc_init */ + +/* + * snic_trc_free : Releases the trace buffer and disables the tracing. + */ +void +snic_trc_free(void) +{ + struct snic_trc *trc = &snic_glob->trc; + + trc->enable = false; + snic_trc_debugfs_term(); + + if (trc->buf) { + vfree(trc->buf); + trc->buf = NULL; + } + + SNIC_INFO("Trace Facility Disabled.\n"); +} /* end of snic_trc_free */ diff --git a/kernel/drivers/scsi/snic/snic_trc.h b/kernel/drivers/scsi/snic/snic_trc.h new file mode 100644 index 000000000..b37f8867b --- /dev/null +++ b/kernel/drivers/scsi/snic/snic_trc.h @@ -0,0 +1,121 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __SNIC_TRC_H +#define __SNIC_TRC_H + +#ifdef CONFIG_SCSI_SNIC_DEBUG_FS + +extern ssize_t simple_read_from_buffer(void __user *to, + size_t count, + loff_t *ppos, + const void *from, + size_t available); + +extern unsigned int snic_trace_max_pages; + +/* Global Data structure for trace to manage trace functionality */ +struct snic_trc_data { + u64 ts; /* Time Stamp */ + char *fn; /* Ptr to Function Name */ + u32 hno; /* SCSI Host ID */ + u32 tag; /* Command Tag */ + u64 data[5]; +} __attribute__((__packed__)); + +#define SNIC_TRC_ENTRY_SZ 64 /* in Bytes */ + +struct snic_trc { + spinlock_t lock; + struct snic_trc_data *buf; /* Trace Buffer */ + u32 max_idx; /* Max Index into trace buffer */ + u32 rd_idx; + u32 wr_idx; + bool enable; /* Control Variable for Tracing */ + + struct dentry *trc_enable; /* debugfs file object */ + struct dentry *trc_file; +}; + +int snic_trc_init(void); +void snic_trc_free(void); +int snic_trc_debugfs_init(void); +void snic_trc_debugfs_term(void); +struct snic_trc_data *snic_get_trc_buf(void); +int snic_get_trc_data(char *buf, int buf_sz); + +int snic_debugfs_init(void); +void snic_debugfs_term(void); + +static inline void +snic_trace(char *fn, u16 hno, u32 tag, u64 d1, u64 d2, u64 d3, u64 d4, u64 d5) +{ + struct snic_trc_data *tr_rec = snic_get_trc_buf(); + + if (!tr_rec) + return; + + tr_rec->fn = (char *)fn; + tr_rec->hno = hno; + tr_rec->tag = tag; + tr_rec->data[0] = d1; + tr_rec->data[1] = d2; + tr_rec->data[2] = d3; + tr_rec->data[3] = d4; + tr_rec->data[4] = d5; + tr_rec->ts = jiffies; /* Update time stamp at last */ +} + +#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5) \ + do { \ + if (unlikely(snic_glob->trc.enable)) \ + snic_trace((char *)__func__, \ + (u16)(_hno), \ + (u32)(_tag), \ + (u64)(d1), \ + (u64)(d2), \ + (u64)(d3), \ + (u64)(d4), \ + (u64)(d5)); \ + } while (0) +#else + +#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5) \ + do { \ + if (unlikely(snic_log_level & 0x2)) \ + SNIC_DBG("SnicTrace: %s %2u %2u %llx %llx %llx %llx %llx", \ + (char *)__func__, \ + (u16)(_hno), \ + (u32)(_tag), \ + (u64)(d1), \ + (u64)(d2), \ + (u64)(d3), \ + (u64)(d4), \ + (u64)(d5)); \ + } while (0) +#endif /* end of CONFIG_SCSI_SNIC_DEBUG_FS */ + +#define SNIC_TRC_CMD(sc) \ + ((u64)sc->cmnd[0] << 56 | (u64)sc->cmnd[7] << 40 | \ + (u64)sc->cmnd[8] << 32 | (u64)sc->cmnd[2] << 24 | \ + (u64)sc->cmnd[3] << 16 | (u64)sc->cmnd[4] << 8 | \ + (u64)sc->cmnd[5]) + +#define SNIC_TRC_CMD_STATE_FLAGS(sc) \ + ((u64) CMD_FLAGS(sc) << 32 | CMD_STATE(sc)) + +#endif /* end of __SNIC_TRC_H */ diff --git a/kernel/drivers/scsi/snic/vnic_cq.c b/kernel/drivers/scsi/snic/vnic_cq.c new file mode 100644 index 000000000..4c8e64e4f --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_cq.c @@ -0,0 +1,86 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "vnic_dev.h" +#include "vnic_cq.h" + +void svnic_cq_free(struct vnic_cq *cq) +{ + svnic_dev_free_desc_ring(cq->vdev, &cq->ring); + + cq->ctrl = NULL; +} + +int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, + unsigned int index, unsigned int desc_count, unsigned int desc_size) +{ + int err; + + cq->index = index; + cq->vdev = vdev; + + cq->ctrl = svnic_dev_get_res(vdev, RES_TYPE_CQ, index); + if (!cq->ctrl) { + pr_err("Failed to hook CQ[%d] resource\n", index); + + return -EINVAL; + } + + err = svnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size); + if (err) + return err; + + return 0; +} + +void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable, + unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail, + unsigned int cq_tail_color, unsigned int interrupt_enable, + unsigned int cq_entry_enable, unsigned int cq_message_enable, + unsigned int interrupt_offset, u64 cq_message_addr) +{ + u64 paddr; + + paddr = (u64)cq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &cq->ctrl->ring_base); + iowrite32(cq->ring.desc_count, &cq->ctrl->ring_size); + iowrite32(flow_control_enable, &cq->ctrl->flow_control_enable); + iowrite32(color_enable, &cq->ctrl->color_enable); + iowrite32(cq_head, &cq->ctrl->cq_head); + iowrite32(cq_tail, &cq->ctrl->cq_tail); + iowrite32(cq_tail_color, &cq->ctrl->cq_tail_color); + iowrite32(interrupt_enable, &cq->ctrl->interrupt_enable); + iowrite32(cq_entry_enable, &cq->ctrl->cq_entry_enable); + iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable); + iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset); + writeq(cq_message_addr, &cq->ctrl->cq_message_addr); +} + +void svnic_cq_clean(struct vnic_cq *cq) +{ + cq->to_clean = 0; + cq->last_color = 0; + + iowrite32(0, &cq->ctrl->cq_head); + iowrite32(0, &cq->ctrl->cq_tail); + iowrite32(1, &cq->ctrl->cq_tail_color); + + svnic_dev_clear_desc_ring(&cq->ring); +} diff --git a/kernel/drivers/scsi/snic/vnic_cq.h b/kernel/drivers/scsi/snic/vnic_cq.h new file mode 100644 index 000000000..6e651c3e1 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_cq.h @@ -0,0 +1,110 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_CQ_H_ +#define _VNIC_CQ_H_ + +#include "cq_desc.h" +#include "vnic_dev.h" + +/* Completion queue control */ +struct vnic_cq_ctrl { + u64 ring_base; /* 0x00 */ + u32 ring_size; /* 0x08 */ + u32 pad0; + u32 flow_control_enable; /* 0x10 */ + u32 pad1; + u32 color_enable; /* 0x18 */ + u32 pad2; + u32 cq_head; /* 0x20 */ + u32 pad3; + u32 cq_tail; /* 0x28 */ + u32 pad4; + u32 cq_tail_color; /* 0x30 */ + u32 pad5; + u32 interrupt_enable; /* 0x38 */ + u32 pad6; + u32 cq_entry_enable; /* 0x40 */ + u32 pad7; + u32 cq_message_enable; /* 0x48 */ + u32 pad8; + u32 interrupt_offset; /* 0x50 */ + u32 pad9; + u64 cq_message_addr; /* 0x58 */ + u32 pad10; +}; + +struct vnic_cq { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_cq_ctrl __iomem *ctrl; /* memory-mapped */ + struct vnic_dev_ring ring; + unsigned int to_clean; + unsigned int last_color; +}; + +static inline unsigned int svnic_cq_service(struct vnic_cq *cq, + unsigned int work_to_do, + int (*q_service)(struct vnic_dev *vdev, struct cq_desc *cq_desc, + u8 type, u16 q_number, u16 completed_index, void *opaque), + void *opaque) +{ + struct cq_desc *cq_desc; + unsigned int work_done = 0; + u16 q_number, completed_index; + u8 type, color; + + cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + cq_desc_dec(cq_desc, &type, &color, + &q_number, &completed_index); + + while (color != cq->last_color) { + + if ((*q_service)(cq->vdev, cq_desc, type, + q_number, completed_index, opaque)) + break; + + cq->to_clean++; + if (cq->to_clean == cq->ring.desc_count) { + cq->to_clean = 0; + cq->last_color = cq->last_color ? 0 : 1; + } + + cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + cq_desc_dec(cq_desc, &type, &color, + &q_number, &completed_index); + + work_done++; + if (work_done >= work_to_do) + break; + } + + return work_done; +} + +void svnic_cq_free(struct vnic_cq *cq); +int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq, + unsigned int index, unsigned int desc_count, unsigned int desc_size); +void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable, + unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail, + unsigned int cq_tail_color, unsigned int interrupt_enable, + unsigned int cq_entry_enable, unsigned int message_enable, + unsigned int interrupt_offset, u64 message_addr); +void svnic_cq_clean(struct vnic_cq *cq); +#endif /* _VNIC_CQ_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_cq_fw.h b/kernel/drivers/scsi/snic/vnic_cq_fw.h new file mode 100644 index 000000000..c2d1bbd44 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_cq_fw.h @@ -0,0 +1,62 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_CQ_FW_H_ +#define _VNIC_CQ_FW_H_ + +#include "snic_fwint.h" + +static inline unsigned int +vnic_cq_fw_service(struct vnic_cq *cq, + int (*q_service)(struct vnic_dev *vdev, + unsigned int index, + struct snic_fw_req *desc), + unsigned int work_to_do) + +{ + struct snic_fw_req *desc; + unsigned int work_done = 0; + u8 color; + + desc = (struct snic_fw_req *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + snic_color_dec(desc, &color); + + while (color != cq->last_color) { + + if ((*q_service)(cq->vdev, cq->index, desc)) + break; + + cq->to_clean++; + if (cq->to_clean == cq->ring.desc_count) { + cq->to_clean = 0; + cq->last_color = cq->last_color ? 0 : 1; + } + + desc = (struct snic_fw_req *)((u8 *)cq->ring.descs + + cq->ring.desc_size * cq->to_clean); + snic_color_dec(desc, &color); + + work_done++; + if (work_done >= work_to_do) + break; + } + + return work_done; +} + +#endif /* _VNIC_CQ_FW_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_dev.c b/kernel/drivers/scsi/snic/vnic_dev.c new file mode 100644 index 000000000..e0b5549bc --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_dev.c @@ -0,0 +1,748 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "vnic_resource.h" +#include "vnic_devcmd.h" +#include "vnic_dev.h" +#include "vnic_stats.h" +#include "vnic_wq.h" + +#define VNIC_DVCMD_TMO 10000 /* Devcmd Timeout value */ +#define VNIC_NOTIFY_INTR_MASK 0x0000ffff00000000ULL + +struct devcmd2_controller { + struct vnic_wq_ctrl __iomem *wq_ctrl; + struct vnic_dev_ring results_ring; + struct vnic_wq wq; + struct vnic_devcmd2 *cmd_ring; + struct devcmd2_result *result; + u16 next_result; + u16 result_size; + int color; +}; + +struct vnic_res { + void __iomem *vaddr; + unsigned int count; +}; + +struct vnic_dev { + void *priv; + struct pci_dev *pdev; + struct vnic_res res[RES_TYPE_MAX]; + enum vnic_dev_intr_mode intr_mode; + struct vnic_devcmd __iomem *devcmd; + struct vnic_devcmd_notify *notify; + struct vnic_devcmd_notify notify_copy; + dma_addr_t notify_pa; + u32 *linkstatus; + dma_addr_t linkstatus_pa; + struct vnic_stats *stats; + dma_addr_t stats_pa; + struct vnic_devcmd_fw_info *fw_info; + dma_addr_t fw_info_pa; + u64 args[VNIC_DEVCMD_NARGS]; + struct devcmd2_controller *devcmd2; + + int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + int wait); +}; + +#define VNIC_MAX_RES_HDR_SIZE \ + (sizeof(struct vnic_resource_header) + \ + sizeof(struct vnic_resource) * RES_TYPE_MAX) +#define VNIC_RES_STRIDE 128 + +void *svnic_dev_priv(struct vnic_dev *vdev) +{ + return vdev->priv; +} + +static int vnic_dev_discover_res(struct vnic_dev *vdev, + struct vnic_dev_bar *bar, unsigned int num_bars) +{ + struct vnic_resource_header __iomem *rh; + struct vnic_resource __iomem *r; + u8 type; + + if (num_bars == 0) + return -EINVAL; + + if (bar->len < VNIC_MAX_RES_HDR_SIZE) { + pr_err("vNIC BAR0 res hdr length error\n"); + + return -EINVAL; + } + + rh = bar->vaddr; + if (!rh) { + pr_err("vNIC BAR0 res hdr not mem-mapped\n"); + + return -EINVAL; + } + + if (ioread32(&rh->magic) != VNIC_RES_MAGIC || + ioread32(&rh->version) != VNIC_RES_VERSION) { + pr_err("vNIC BAR0 res magic/version error exp (%lx/%lx) curr (%x/%x)\n", + VNIC_RES_MAGIC, VNIC_RES_VERSION, + ioread32(&rh->magic), ioread32(&rh->version)); + + return -EINVAL; + } + + r = (struct vnic_resource __iomem *)(rh + 1); + + while ((type = ioread8(&r->type)) != RES_TYPE_EOL) { + + u8 bar_num = ioread8(&r->bar); + u32 bar_offset = ioread32(&r->bar_offset); + u32 count = ioread32(&r->count); + u32 len; + + r++; + + if (bar_num >= num_bars) + continue; + + if (!bar[bar_num].len || !bar[bar_num].vaddr) + continue; + + switch (type) { + case RES_TYPE_WQ: + case RES_TYPE_RQ: + case RES_TYPE_CQ: + case RES_TYPE_INTR_CTRL: + /* each count is stride bytes long */ + len = count * VNIC_RES_STRIDE; + if (len + bar_offset > bar->len) { + pr_err("vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n", + type, bar_offset, + len, + bar->len); + + return -EINVAL; + } + break; + + case RES_TYPE_INTR_PBA_LEGACY: + case RES_TYPE_DEVCMD: + case RES_TYPE_DEVCMD2: + len = count; + break; + + default: + continue; + } + + vdev->res[type].count = count; + vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset; + } + + return 0; +} + +unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev, + enum vnic_res_type type) +{ + return vdev->res[type].count; +} + +void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, + unsigned int index) +{ + if (!vdev->res[type].vaddr) + return NULL; + + switch (type) { + case RES_TYPE_WQ: + case RES_TYPE_RQ: + case RES_TYPE_CQ: + case RES_TYPE_INTR_CTRL: + return (char __iomem *)vdev->res[type].vaddr + + index * VNIC_RES_STRIDE; + + default: + return (char __iomem *)vdev->res[type].vaddr; + } +} + +unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring, + unsigned int desc_count, + unsigned int desc_size) +{ + /* The base address of the desc rings must be 512 byte aligned. + * Descriptor count is aligned to groups of 32 descriptors. A + * count of 0 means the maximum 4096 descriptors. Descriptor + * size is aligned to 16 bytes. + */ + + unsigned int count_align = 32; + unsigned int desc_align = 16; + + ring->base_align = 512; + + if (desc_count == 0) + desc_count = 4096; + + ring->desc_count = ALIGN(desc_count, count_align); + + ring->desc_size = ALIGN(desc_size, desc_align); + + ring->size = ring->desc_count * ring->desc_size; + ring->size_unaligned = ring->size + ring->base_align; + + return ring->size_unaligned; +} + +void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring) +{ + memset(ring->descs, 0, ring->size); +} + +int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size) +{ + svnic_dev_desc_ring_size(ring, desc_count, desc_size); + + ring->descs_unaligned = pci_alloc_consistent(vdev->pdev, + ring->size_unaligned, + &ring->base_addr_unaligned); + + if (!ring->descs_unaligned) { + pr_err("Failed to allocate ring (size=%d), aborting\n", + (int)ring->size); + + return -ENOMEM; + } + + ring->base_addr = ALIGN(ring->base_addr_unaligned, + ring->base_align); + ring->descs = (u8 *)ring->descs_unaligned + + (ring->base_addr - ring->base_addr_unaligned); + + svnic_dev_clear_desc_ring(ring); + + ring->desc_avail = ring->desc_count - 1; + + return 0; +} + +void svnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring) +{ + if (ring->descs) { + pci_free_consistent(vdev->pdev, + ring->size_unaligned, + ring->descs_unaligned, + ring->base_addr_unaligned); + ring->descs = NULL; + } +} + +static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + int wait) +{ + struct devcmd2_controller *dc2c = vdev->devcmd2; + struct devcmd2_result *result = dc2c->result + dc2c->next_result; + unsigned int i; + int delay; + int err; + u32 posted; + u32 new_posted; + + posted = ioread32(&dc2c->wq_ctrl->posted_index); + + if (posted == 0xFFFFFFFF) { /* check for hardware gone */ + /* Hardware surprise removal: return error */ + return -ENODEV; + } + + new_posted = (posted + 1) % DEVCMD2_RING_SIZE; + dc2c->cmd_ring[posted].cmd = cmd; + dc2c->cmd_ring[posted].flags = 0; + + if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT)) + dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT; + + if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) { + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + dc2c->cmd_ring[posted].args[i] = vdev->args[i]; + } + /* Adding write memory barrier prevents compiler and/or CPU + * reordering, thus avoiding descriptor posting before + * descriptor is initialized. Otherwise, hardware can read + * stale descriptor fields. + */ + wmb(); + iowrite32(new_posted, &dc2c->wq_ctrl->posted_index); + + if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT) + return 0; + + for (delay = 0; delay < wait; delay++) { + udelay(100); + if (result->color == dc2c->color) { + dc2c->next_result++; + if (dc2c->next_result == dc2c->result_size) { + dc2c->next_result = 0; + dc2c->color = dc2c->color ? 0 : 1; + } + if (result->error) { + err = (int) result->error; + if (err != ERR_ECMDUNKNOWN || + cmd != CMD_CAPABILITY) + pr_err("Error %d devcmd %d\n", + err, _CMD_N(cmd)); + + return err; + } + if (_CMD_DIR(cmd) & _CMD_DIR_READ) { + /* + * Adding the rmb() prevents the compiler + * and/or CPU from reordering the reads which + * would potentially result in reading stale + * values. + */ + rmb(); + for (i = 0; i < VNIC_DEVCMD_NARGS; i++) + vdev->args[i] = result->results[i]; + } + + return 0; + } + } + + pr_err("Timed out devcmd %d\n", _CMD_N(cmd)); + + return -ETIMEDOUT; +} + +static int svnic_dev_init_devcmd2(struct vnic_dev *vdev) +{ + struct devcmd2_controller *dc2c = NULL; + unsigned int fetch_idx; + int ret; + void __iomem *p; + + if (vdev->devcmd2) + return 0; + + p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0); + if (!p) + return -ENODEV; + + dc2c = kzalloc(sizeof(*dc2c), GFP_ATOMIC); + if (!dc2c) + return -ENOMEM; + + vdev->devcmd2 = dc2c; + + dc2c->color = 1; + dc2c->result_size = DEVCMD2_RING_SIZE; + + ret = vnic_wq_devcmd2_alloc(vdev, + &dc2c->wq, + DEVCMD2_RING_SIZE, + DEVCMD2_DESC_SIZE); + if (ret) + goto err_free_devcmd2; + + fetch_idx = ioread32(&dc2c->wq.ctrl->fetch_index); + if (fetch_idx == 0xFFFFFFFF) { /* check for hardware gone */ + /* Hardware surprise removal: reset fetch_index */ + fetch_idx = 0; + } + + /* + * Don't change fetch_index ever and + * set posted_index same as fetch_index + * when setting up the WQ for devcmd2. + */ + vnic_wq_init_start(&dc2c->wq, 0, fetch_idx, fetch_idx, 0, 0); + svnic_wq_enable(&dc2c->wq); + ret = svnic_dev_alloc_desc_ring(vdev, + &dc2c->results_ring, + DEVCMD2_RING_SIZE, + DEVCMD2_DESC_SIZE); + if (ret) + goto err_free_wq; + + dc2c->result = (struct devcmd2_result *) dc2c->results_ring.descs; + dc2c->cmd_ring = (struct vnic_devcmd2 *) dc2c->wq.ring.descs; + dc2c->wq_ctrl = dc2c->wq.ctrl; + vdev->args[0] = (u64) dc2c->results_ring.base_addr | VNIC_PADDR_TARGET; + vdev->args[1] = DEVCMD2_RING_SIZE; + + ret = _svnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, VNIC_DVCMD_TMO); + if (ret < 0) + goto err_free_desc_ring; + + vdev->devcmd_rtn = &_svnic_dev_cmd2; + pr_info("DEVCMD2 Initialized.\n"); + + return ret; + +err_free_desc_ring: + svnic_dev_free_desc_ring(vdev, &dc2c->results_ring); + +err_free_wq: + svnic_wq_disable(&dc2c->wq); + svnic_wq_free(&dc2c->wq); + +err_free_devcmd2: + kfree(dc2c); + vdev->devcmd2 = NULL; + + return ret; +} /* end of svnic_dev_init_devcmd2 */ + +static void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev) +{ + struct devcmd2_controller *dc2c = vdev->devcmd2; + + vdev->devcmd2 = NULL; + vdev->devcmd_rtn = NULL; + + svnic_dev_free_desc_ring(vdev, &dc2c->results_ring); + svnic_wq_disable(&dc2c->wq); + svnic_wq_free(&dc2c->wq); + kfree(dc2c); +} + +int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait) +{ + int err; + + memset(vdev->args, 0, sizeof(vdev->args)); + vdev->args[0] = *a0; + vdev->args[1] = *a1; + + err = (*vdev->devcmd_rtn)(vdev, cmd, wait); + + *a0 = vdev->args[0]; + *a1 = vdev->args[1]; + + return err; +} + +int svnic_dev_fw_info(struct vnic_dev *vdev, + struct vnic_devcmd_fw_info **fw_info) +{ + u64 a0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + int err = 0; + + if (!vdev->fw_info) { + vdev->fw_info = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_fw_info), + &vdev->fw_info_pa); + if (!vdev->fw_info) + return -ENOMEM; + + a0 = vdev->fw_info_pa; + + /* only get fw_info once and cache it */ + err = svnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait); + } + + *fw_info = vdev->fw_info; + + return err; +} + +int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, + unsigned int size, void *value) +{ + u64 a0, a1; + int wait = VNIC_DVCMD_TMO; + int err; + + a0 = offset; + a1 = size; + + err = svnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait); + + switch (size) { + case 1: + *(u8 *)value = (u8)a0; + break; + case 2: + *(u16 *)value = (u16)a0; + break; + case 4: + *(u32 *)value = (u32)a0; + break; + case 8: + *(u64 *)value = a0; + break; + default: + BUG(); + break; + } + + return err; +} + +int svnic_dev_stats_clear(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + + return svnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait); +} + +int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats) +{ + u64 a0, a1; + int wait = VNIC_DVCMD_TMO; + + if (!vdev->stats) { + vdev->stats = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_stats), &vdev->stats_pa); + if (!vdev->stats) + return -ENOMEM; + } + + *stats = vdev->stats; + a0 = vdev->stats_pa; + a1 = sizeof(struct vnic_stats); + + return svnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait); +} + +int svnic_dev_close(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + + return svnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait); +} + +int svnic_dev_enable_wait(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + int err = 0; + + err = svnic_dev_cmd(vdev, CMD_ENABLE_WAIT, &a0, &a1, wait); + if (err == ERR_ECMDUNKNOWN) + return svnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait); + + return err; +} + +int svnic_dev_disable(struct vnic_dev *vdev) +{ + u64 a0 = 0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + + return svnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait); +} + +int svnic_dev_open(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = VNIC_DVCMD_TMO; + + return svnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait); +} + +int svnic_dev_open_done(struct vnic_dev *vdev, int *done) +{ + u64 a0 = 0, a1 = 0; + int wait = VNIC_DVCMD_TMO; + int err; + + *done = 0; + + err = svnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait); + if (err) + return err; + + *done = (a0 == 0); + + return 0; +} + +int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr) +{ + u64 a0, a1; + int wait = VNIC_DVCMD_TMO; + + if (!vdev->notify) { + vdev->notify = pci_alloc_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_notify), + &vdev->notify_pa); + if (!vdev->notify) + return -ENOMEM; + } + + a0 = vdev->notify_pa; + a1 = ((u64)intr << 32) & VNIC_NOTIFY_INTR_MASK; + a1 += sizeof(struct vnic_devcmd_notify); + + return svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); +} + +void svnic_dev_notify_unset(struct vnic_dev *vdev) +{ + u64 a0, a1; + int wait = VNIC_DVCMD_TMO; + + a0 = 0; /* paddr = 0 to unset notify buffer */ + a1 = VNIC_NOTIFY_INTR_MASK; /* intr num = -1 to unreg for intr */ + a1 += sizeof(struct vnic_devcmd_notify); + + svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait); +} + +static int vnic_dev_notify_ready(struct vnic_dev *vdev) +{ + u32 *words; + unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4; + unsigned int i; + u32 csum; + + if (!vdev->notify) + return 0; + + do { + csum = 0; + memcpy(&vdev->notify_copy, vdev->notify, + sizeof(struct vnic_devcmd_notify)); + words = (u32 *)&vdev->notify_copy; + for (i = 1; i < nwords; i++) + csum += words[i]; + } while (csum != words[0]); + + return 1; +} + +int svnic_dev_init(struct vnic_dev *vdev, int arg) +{ + u64 a0 = (u32)arg, a1 = 0; + int wait = VNIC_DVCMD_TMO; + + return svnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait); +} + +int svnic_dev_link_status(struct vnic_dev *vdev) +{ + if (vdev->linkstatus) + return *vdev->linkstatus; + + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.link_state; +} + +u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev) +{ + if (!vnic_dev_notify_ready(vdev)) + return 0; + + return vdev->notify_copy.link_down_cnt; +} + +void svnic_dev_set_intr_mode(struct vnic_dev *vdev, + enum vnic_dev_intr_mode intr_mode) +{ + vdev->intr_mode = intr_mode; +} + +enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev) +{ + return vdev->intr_mode; +} + +void svnic_dev_unregister(struct vnic_dev *vdev) +{ + if (vdev) { + if (vdev->notify) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_notify), + vdev->notify, + vdev->notify_pa); + if (vdev->linkstatus) + pci_free_consistent(vdev->pdev, + sizeof(u32), + vdev->linkstatus, + vdev->linkstatus_pa); + if (vdev->stats) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_stats), + vdev->stats, vdev->stats_pa); + if (vdev->fw_info) + pci_free_consistent(vdev->pdev, + sizeof(struct vnic_devcmd_fw_info), + vdev->fw_info, vdev->fw_info_pa); + if (vdev->devcmd2) + vnic_dev_deinit_devcmd2(vdev); + kfree(vdev); + } +} + +struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev, + void *priv, + struct pci_dev *pdev, + struct vnic_dev_bar *bar, + unsigned int num_bars) +{ + if (!vdev) { + vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC); + if (!vdev) + return NULL; + } + + vdev->priv = priv; + vdev->pdev = pdev; + + if (vnic_dev_discover_res(vdev, bar, num_bars)) + goto err_out; + + return vdev; + +err_out: + svnic_dev_unregister(vdev); + + return NULL; +} /* end of svnic_dev_alloc_discover */ + +/* + * fallback option is left to keep the interface common for other vnics. + */ +int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback) +{ + int err = -ENODEV; + void __iomem *p; + + p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0); + if (p) + err = svnic_dev_init_devcmd2(vdev); + else + pr_err("DEVCMD2 resource not found.\n"); + + return err; +} /* end of svnic_dev_cmd_init */ diff --git a/kernel/drivers/scsi/snic/vnic_dev.h b/kernel/drivers/scsi/snic/vnic_dev.h new file mode 100644 index 000000000..e65726da6 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_dev.h @@ -0,0 +1,110 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_DEV_H_ +#define _VNIC_DEV_H_ + +#include "vnic_resource.h" +#include "vnic_devcmd.h" + +#ifndef VNIC_PADDR_TARGET +#define VNIC_PADDR_TARGET 0x0000000000000000ULL +#endif + +#ifndef readq +static inline u64 readq(void __iomem *reg) +{ + return ((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg); +} + +static inline void writeq(u64 val, void __iomem *reg) +{ + writel(lower_32_bits(val), reg); + writel(upper_32_bits(val), reg + 0x4UL); +} +#endif + +enum vnic_dev_intr_mode { + VNIC_DEV_INTR_MODE_UNKNOWN, + VNIC_DEV_INTR_MODE_INTX, + VNIC_DEV_INTR_MODE_MSI, + VNIC_DEV_INTR_MODE_MSIX, +}; + +struct vnic_dev_bar { + void __iomem *vaddr; + dma_addr_t bus_addr; + unsigned long len; +}; + +struct vnic_dev_ring { + void *descs; + size_t size; + dma_addr_t base_addr; + size_t base_align; + void *descs_unaligned; + size_t size_unaligned; + dma_addr_t base_addr_unaligned; + unsigned int desc_size; + unsigned int desc_count; + unsigned int desc_avail; +}; + +struct vnic_dev; +struct vnic_stats; + +void *svnic_dev_priv(struct vnic_dev *vdev); +unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev, + enum vnic_res_type type); +void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type, + unsigned int index); +unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring, + unsigned int desc_count, + unsigned int desc_size); +void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring); +int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring, + unsigned int desc_count, unsigned int desc_size); +void svnic_dev_free_desc_ring(struct vnic_dev *vdev, + struct vnic_dev_ring *ring); +int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, + u64 *a0, u64 *a1, int wait); +int svnic_dev_fw_info(struct vnic_dev *vdev, + struct vnic_devcmd_fw_info **fw_info); +int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset, + unsigned int size, void *value); +int svnic_dev_stats_clear(struct vnic_dev *vdev); +int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats); +int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr); +void svnic_dev_notify_unset(struct vnic_dev *vdev); +int svnic_dev_link_status(struct vnic_dev *vdev); +u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev); +int svnic_dev_close(struct vnic_dev *vdev); +int svnic_dev_enable_wait(struct vnic_dev *vdev); +int svnic_dev_disable(struct vnic_dev *vdev); +int svnic_dev_open(struct vnic_dev *vdev, int arg); +int svnic_dev_open_done(struct vnic_dev *vdev, int *done); +int svnic_dev_init(struct vnic_dev *vdev, int arg); +struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev, + void *priv, struct pci_dev *pdev, + struct vnic_dev_bar *bar, + unsigned int num_bars); +void svnic_dev_set_intr_mode(struct vnic_dev *vdev, + enum vnic_dev_intr_mode intr_mode); +enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev); +void svnic_dev_unregister(struct vnic_dev *vdev); +int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback); +#endif /* _VNIC_DEV_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_devcmd.h b/kernel/drivers/scsi/snic/vnic_devcmd.h new file mode 100644 index 000000000..d81b4f0ce --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_devcmd.h @@ -0,0 +1,270 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_DEVCMD_H_ +#define _VNIC_DEVCMD_H_ + +#define _CMD_NBITS 14 +#define _CMD_VTYPEBITS 10 +#define _CMD_FLAGSBITS 6 +#define _CMD_DIRBITS 2 + +#define _CMD_NMASK ((1 << _CMD_NBITS)-1) +#define _CMD_VTYPEMASK ((1 << _CMD_VTYPEBITS)-1) +#define _CMD_FLAGSMASK ((1 << _CMD_FLAGSBITS)-1) +#define _CMD_DIRMASK ((1 << _CMD_DIRBITS)-1) + +#define _CMD_NSHIFT 0 +#define _CMD_VTYPESHIFT (_CMD_NSHIFT+_CMD_NBITS) +#define _CMD_FLAGSSHIFT (_CMD_VTYPESHIFT+_CMD_VTYPEBITS) +#define _CMD_DIRSHIFT (_CMD_FLAGSSHIFT+_CMD_FLAGSBITS) + +/* + * Direction bits (from host perspective). + */ +#define _CMD_DIR_NONE 0U +#define _CMD_DIR_WRITE 1U +#define _CMD_DIR_READ 2U +#define _CMD_DIR_RW (_CMD_DIR_WRITE | _CMD_DIR_READ) + +/* + * Flag bits. + */ +#define _CMD_FLAGS_NONE 0U +#define _CMD_FLAGS_NOWAIT 1U + +/* + * vNIC type bits. + */ +#define _CMD_VTYPE_NONE 0U +#define _CMD_VTYPE_ENET 1U +#define _CMD_VTYPE_FC 2U +#define _CMD_VTYPE_SCSI 4U +#define _CMD_VTYPE_ALL (_CMD_VTYPE_ENET | _CMD_VTYPE_FC | _CMD_VTYPE_SCSI) + +/* + * Used to create cmds.. +*/ +#define _CMDCF(dir, flags, vtype, nr) \ + (((dir) << _CMD_DIRSHIFT) | \ + ((flags) << _CMD_FLAGSSHIFT) | \ + ((vtype) << _CMD_VTYPESHIFT) | \ + ((nr) << _CMD_NSHIFT)) +#define _CMDC(dir, vtype, nr) _CMDCF(dir, 0, vtype, nr) +#define _CMDCNW(dir, vtype, nr) _CMDCF(dir, _CMD_FLAGS_NOWAIT, vtype, nr) + +/* + * Used to decode cmds.. +*/ +#define _CMD_DIR(cmd) (((cmd) >> _CMD_DIRSHIFT) & _CMD_DIRMASK) +#define _CMD_FLAGS(cmd) (((cmd) >> _CMD_FLAGSSHIFT) & _CMD_FLAGSMASK) +#define _CMD_VTYPE(cmd) (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK) +#define _CMD_N(cmd) (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK) + +enum vnic_devcmd_cmd { + CMD_NONE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0), + + /* mcpu fw info in mem: (u64)a0=paddr to struct vnic_devcmd_fw_info */ + CMD_MCPU_FW_INFO = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 1), + + /* dev-specific block member: + * in: (u16)a0=offset,(u8)a1=size + * out: a0=value */ + CMD_DEV_SPEC = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2), + + /* stats clear */ + CMD_STATS_CLEAR = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 3), + + /* stats dump in mem: (u64)a0=paddr to stats area, + * (u16)a1=sizeof stats area */ + CMD_STATS_DUMP = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4), + + /* nic_cfg in (u32)a0 */ + CMD_NIC_CFG = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 16), + + /* set struct vnic_devcmd_notify buffer in mem: + * in: + * (u64)a0=paddr to notify (set paddr=0 to unset) + * (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify) + * (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr) + * out: + * (u32)a1 = effective size + */ + CMD_NOTIFY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 21), + + /* initiate open sequence (u32)a0=flags (see CMD_OPENF_*) */ + CMD_OPEN = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 23), + + /* open status: + * out: a0=0 open complete, a0=1 open in progress */ + CMD_OPEN_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 24), + + /* close vnic */ + CMD_CLOSE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25), + + /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */ + CMD_INIT = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26), + + /* enable virtual link */ + CMD_ENABLE = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28), + + /* enable virtual link, waiting variant. */ + CMD_ENABLE_WAIT = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28), + + /* disable virtual link */ + CMD_DISABLE = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29), + + /* stats dump all vnics on uplink in mem: (u64)a0=paddr (u32)a1=uif */ + CMD_STATS_DUMP_ALL = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 30), + + /* init status: + * out: a0=0 init complete, a0=1 init in progress + * if a0=0, a1=errno */ + CMD_INIT_STATUS = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 31), + + /* undo initialize of virtual link */ + CMD_DEINIT = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34), + + /* check fw capability of a cmd: + * in: (u32)a0=cmd + * out: (u32)a0=errno, 0:valid cmd, a1=supported VNIC_STF_* bits */ + CMD_CAPABILITY = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 36), + + /* + * Initialization for the devcmd2 interface. + * in: (u64) a0=host result buffer physical address + * in: (u16) a1=number of entries in result buffer + */ + CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57) +}; + +/* flags for CMD_OPEN */ +#define CMD_OPENF_OPROM 0x1 /* open coming from option rom */ + +/* flags for CMD_INIT */ +#define CMD_INITF_DEFAULT_MAC 0x1 /* init with default mac addr */ + +/* flags for CMD_PACKET_FILTER */ +#define CMD_PFILTER_DIRECTED 0x01 +#define CMD_PFILTER_MULTICAST 0x02 +#define CMD_PFILTER_BROADCAST 0x04 +#define CMD_PFILTER_PROMISCUOUS 0x08 +#define CMD_PFILTER_ALL_MULTICAST 0x10 + +enum vnic_devcmd_status { + STAT_NONE = 0, + STAT_BUSY = 1 << 0, /* cmd in progress */ + STAT_ERROR = 1 << 1, /* last cmd caused error (code in a0) */ +}; + +enum vnic_devcmd_error { + ERR_SUCCESS = 0, + ERR_EINVAL = 1, + ERR_EFAULT = 2, + ERR_EPERM = 3, + ERR_EBUSY = 4, + ERR_ECMDUNKNOWN = 5, + ERR_EBADSTATE = 6, + ERR_ENOMEM = 7, + ERR_ETIMEDOUT = 8, + ERR_ELINKDOWN = 9, +}; + +struct vnic_devcmd_fw_info { + char fw_version[32]; + char fw_build[32]; + char hw_version[32]; + char hw_serial_number[32]; +}; + +struct vnic_devcmd_notify { + u32 csum; /* checksum over following words */ + + u32 link_state; /* link up == 1 */ + u32 port_speed; /* effective port speed (rate limit) */ + u32 mtu; /* MTU */ + u32 msglvl; /* requested driver msg lvl */ + u32 uif; /* uplink interface */ + u32 status; /* status bits (see VNIC_STF_*) */ + u32 error; /* error code (see ERR_*) for first ERR */ + u32 link_down_cnt; /* running count of link down transitions */ +}; +#define VNIC_STF_FATAL_ERR 0x0001 /* fatal fw error */ + +struct vnic_devcmd_provinfo { + u8 oui[3]; + u8 type; + u8 data[0]; +}; + +/* + * Writing cmd register causes STAT_BUSY to get set in status register. + * When cmd completes, STAT_BUSY will be cleared. + * + * If cmd completed successfully STAT_ERROR will be clear + * and args registers contain cmd-specific results. + * + * If cmd error, STAT_ERROR will be set and args[0] contains error code. + * + * status register is read-only. While STAT_BUSY is set, + * all other register contents are read-only. + */ + +/* Make sizeof(vnic_devcmd) a power-of-2 for I/O BAR. */ +#define VNIC_DEVCMD_NARGS 15 +struct vnic_devcmd { + u32 status; /* RO */ + u32 cmd; /* RW */ + u64 args[VNIC_DEVCMD_NARGS]; /* RW cmd args (little-endian) */ +}; + + +/* + * Version 2 of the interface. + * + * Some things are carried over, notably the vnic_devcmd_cmd enum. + */ + +/* + * Flags for vnic_devcmd2.flags + */ + +#define DEVCMD2_FNORESULT 0x1 /* Don't copy result to host */ + +#define VNIC_DEVCMD2_NARGS VNIC_DEVCMD_NARGS +struct vnic_devcmd2 { + u16 pad; + u16 flags; + u32 cmd; /* same command #defines as original */ + u64 args[VNIC_DEVCMD2_NARGS]; +}; + +#define VNIC_DEVCMD2_NRESULTS VNIC_DEVCMD_NARGS +struct devcmd2_result { + u64 results[VNIC_DEVCMD2_NRESULTS]; + u32 pad; + u16 completed_index; /* into copy WQ */ + u8 error; /* same error codes as original */ + u8 color; /* 0 or 1 as with completion queues */ +}; + +#define DEVCMD2_RING_SIZE 32 +#define DEVCMD2_DESC_SIZE 128 + +#define DEVCMD2_RESULTS_SIZE_MAX ((1 << 16) - 1) + +#endif /* _VNIC_DEVCMD_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_intr.c b/kernel/drivers/scsi/snic/vnic_intr.c new file mode 100644 index 000000000..a7d548067 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_intr.c @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "vnic_dev.h" +#include "vnic_intr.h" + +void svnic_intr_free(struct vnic_intr *intr) +{ + intr->ctrl = NULL; +} + +int svnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr, + unsigned int index) +{ + intr->index = index; + intr->vdev = vdev; + + intr->ctrl = svnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index); + if (!intr->ctrl) { + pr_err("Failed to hook INTR[%d].ctrl resource\n", + index); + return -EINVAL; + } + + return 0; +} + +void svnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer, + unsigned int coalescing_type, unsigned int mask_on_assertion) +{ + iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer); + iowrite32(coalescing_type, &intr->ctrl->coalescing_type); + iowrite32(mask_on_assertion, &intr->ctrl->mask_on_assertion); + iowrite32(0, &intr->ctrl->int_credits); +} + +void svnic_intr_clean(struct vnic_intr *intr) +{ + iowrite32(0, &intr->ctrl->int_credits); +} diff --git a/kernel/drivers/scsi/snic/vnic_intr.h b/kernel/drivers/scsi/snic/vnic_intr.h new file mode 100644 index 000000000..4547f603f --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_intr.h @@ -0,0 +1,105 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_INTR_H_ +#define _VNIC_INTR_H_ + +#include +#include "vnic_dev.h" + +#define VNIC_INTR_TIMER_MAX 0xffff + +#define VNIC_INTR_TIMER_TYPE_ABS 0 +#define VNIC_INTR_TIMER_TYPE_QUIET 1 + +/* Interrupt control */ +struct vnic_intr_ctrl { + u32 coalescing_timer; /* 0x00 */ + u32 pad0; + u32 coalescing_value; /* 0x08 */ + u32 pad1; + u32 coalescing_type; /* 0x10 */ + u32 pad2; + u32 mask_on_assertion; /* 0x18 */ + u32 pad3; + u32 mask; /* 0x20 */ + u32 pad4; + u32 int_credits; /* 0x28 */ + u32 pad5; + u32 int_credit_return; /* 0x30 */ + u32 pad6; +}; + +struct vnic_intr { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_intr_ctrl __iomem *ctrl; /* memory-mapped */ +}; + +static inline void +svnic_intr_unmask(struct vnic_intr *intr) +{ + iowrite32(0, &intr->ctrl->mask); +} + +static inline void +svnic_intr_mask(struct vnic_intr *intr) +{ + iowrite32(1, &intr->ctrl->mask); +} + +static inline void +svnic_intr_return_credits(struct vnic_intr *intr, + unsigned int credits, + int unmask, + int reset_timer) +{ +#define VNIC_INTR_UNMASK_SHIFT 16 +#define VNIC_INTR_RESET_TIMER_SHIFT 17 + + u32 int_credit_return = (credits & 0xffff) | + (unmask ? (1 << VNIC_INTR_UNMASK_SHIFT) : 0) | + (reset_timer ? (1 << VNIC_INTR_RESET_TIMER_SHIFT) : 0); + + iowrite32(int_credit_return, &intr->ctrl->int_credit_return); +} + +static inline unsigned int +svnic_intr_credits(struct vnic_intr *intr) +{ + return ioread32(&intr->ctrl->int_credits); +} + +static inline void +svnic_intr_return_all_credits(struct vnic_intr *intr) +{ + unsigned int credits = svnic_intr_credits(intr); + int unmask = 1; + int reset_timer = 1; + + svnic_intr_return_credits(intr, credits, unmask, reset_timer); +} + +void svnic_intr_free(struct vnic_intr *); +int svnic_intr_alloc(struct vnic_dev *, struct vnic_intr *, unsigned int); +void svnic_intr_init(struct vnic_intr *intr, + unsigned int coalescing_timer, + unsigned int coalescing_type, + unsigned int mask_on_assertion); +void svnic_intr_clean(struct vnic_intr *); + +#endif /* _VNIC_INTR_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_resource.h b/kernel/drivers/scsi/snic/vnic_resource.h new file mode 100644 index 000000000..9713d6835 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_resource.h @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_RESOURCE_H_ +#define _VNIC_RESOURCE_H_ + +#define VNIC_RES_MAGIC 0x766E6963L /* 'vnic' */ +#define VNIC_RES_VERSION 0x00000000L + +/* vNIC resource types */ +enum vnic_res_type { + RES_TYPE_EOL, /* End-of-list */ + RES_TYPE_WQ, /* Work queues */ + RES_TYPE_RQ, /* Receive queues */ + RES_TYPE_CQ, /* Completion queues */ + RES_TYPE_RSVD1, + RES_TYPE_NIC_CFG, /* Enet NIC config registers */ + RES_TYPE_RSVD2, + RES_TYPE_RSVD3, + RES_TYPE_RSVD4, + RES_TYPE_RSVD5, + RES_TYPE_INTR_CTRL, /* Interrupt ctrl table */ + RES_TYPE_INTR_TABLE, /* MSI/MSI-X Interrupt table */ + RES_TYPE_INTR_PBA, /* MSI/MSI-X PBA table */ + RES_TYPE_INTR_PBA_LEGACY, /* Legacy intr status */ + RES_TYPE_RSVD6, + RES_TYPE_RSVD7, + RES_TYPE_DEVCMD, /* Device command region */ + RES_TYPE_PASS_THRU_PAGE, /* Pass-thru page */ + RES_TYPE_SUBVNIC, /* subvnic resource type */ + RES_TYPE_MQ_WQ, /* MQ Work queues */ + RES_TYPE_MQ_RQ, /* MQ Receive queues */ + RES_TYPE_MQ_CQ, /* MQ Completion queues */ + RES_TYPE_DEPRECATED1, /* Old version of devcmd 2 */ + RES_TYPE_DEPRECATED2, /* Old version of devcmd 2 */ + RES_TYPE_DEVCMD2, /* Device control region */ + + RES_TYPE_MAX, /* Count of resource types */ +}; + +struct vnic_resource_header { + u32 magic; + u32 version; +}; + +struct vnic_resource { + u8 type; + u8 bar; + u8 pad[2]; + u32 bar_offset; + u32 count; +}; + +#endif /* _VNIC_RESOURCE_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_snic.h b/kernel/drivers/scsi/snic/vnic_snic.h new file mode 100644 index 000000000..514d39f5c --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_snic.h @@ -0,0 +1,54 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_SNIC_H_ +#define _VNIC_SNIC_H_ + +#define VNIC_SNIC_WQ_DESCS_MIN 64 +#define VNIC_SNIC_WQ_DESCS_MAX 1024 + +#define VNIC_SNIC_MAXDATAFIELDSIZE_MIN 256 +#define VNIC_SNIC_MAXDATAFIELDSIZE_MAX 2112 + +#define VNIC_SNIC_IO_THROTTLE_COUNT_MIN 1 +#define VNIC_SNIC_IO_THROTTLE_COUNT_MAX 1024 + +#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MIN 0 +#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX 240000 + +#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MIN 0 +#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX 255 + +#define VNIC_SNIC_LUNS_PER_TARGET_MIN 1 +#define VNIC_SNIC_LUNS_PER_TARGET_MAX 1024 + +/* Device-specific region: scsi configuration */ +struct vnic_snic_config { + u32 flags; + u32 wq_enet_desc_count; + u32 io_throttle_count; + u32 port_down_timeout; + u32 port_down_io_retries; + u32 luns_per_tgt; + u16 maxdatafieldsize; + u16 intr_timer; + u8 intr_timer_type; + u8 _resvd2; + u8 xpt_type; + u8 hid; +}; +#endif /* _VNIC_SNIC_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_stats.h b/kernel/drivers/scsi/snic/vnic_stats.h new file mode 100644 index 000000000..370a37c97 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_stats.h @@ -0,0 +1,68 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_STATS_H_ +#define _VNIC_STATS_H_ + +/* Tx statistics */ +struct vnic_tx_stats { + u64 tx_frames_ok; + u64 tx_unicast_frames_ok; + u64 tx_multicast_frames_ok; + u64 tx_broadcast_frames_ok; + u64 tx_bytes_ok; + u64 tx_unicast_bytes_ok; + u64 tx_multicast_bytes_ok; + u64 tx_broadcast_bytes_ok; + u64 tx_drops; + u64 tx_errors; + u64 tx_tso; + u64 rsvd[16]; +}; + +/* Rx statistics */ +struct vnic_rx_stats { + u64 rx_frames_ok; + u64 rx_frames_total; + u64 rx_unicast_frames_ok; + u64 rx_multicast_frames_ok; + u64 rx_broadcast_frames_ok; + u64 rx_bytes_ok; + u64 rx_unicast_bytes_ok; + u64 rx_multicast_bytes_ok; + u64 rx_broadcast_bytes_ok; + u64 rx_drop; + u64 rx_no_bufs; + u64 rx_errors; + u64 rx_rss; + u64 rx_crc_errors; + u64 rx_frames_64; + u64 rx_frames_127; + u64 rx_frames_255; + u64 rx_frames_511; + u64 rx_frames_1023; + u64 rx_frames_1518; + u64 rx_frames_to_max; + u64 rsvd[16]; +}; + +struct vnic_stats { + struct vnic_tx_stats tx; + struct vnic_rx_stats rx; +}; + +#endif /* _VNIC_STATS_H_ */ diff --git a/kernel/drivers/scsi/snic/vnic_wq.c b/kernel/drivers/scsi/snic/vnic_wq.c new file mode 100644 index 000000000..1e91d4320 --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_wq.c @@ -0,0 +1,237 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "vnic_dev.h" +#include "vnic_wq.h" + +static inline int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int index, enum vnic_res_type res_type) +{ + wq->ctrl = svnic_dev_get_res(vdev, res_type, index); + if (!wq->ctrl) + return -EINVAL; + + return 0; +} + +static inline int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int index, unsigned int desc_count, unsigned int desc_size) +{ + return svnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count, + desc_size); +} + +static int vnic_wq_alloc_bufs(struct vnic_wq *wq) +{ + struct vnic_wq_buf *buf; + unsigned int i, j, count = wq->ring.desc_count; + unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count); + + for (i = 0; i < blks; i++) { + wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC); + if (!wq->bufs[i]) { + pr_err("Failed to alloc wq_bufs\n"); + + return -ENOMEM; + } + } + + for (i = 0; i < blks; i++) { + buf = wq->bufs[i]; + for (j = 0; j < VNIC_WQ_BUF_DFLT_BLK_ENTRIES; j++) { + buf->index = i * VNIC_WQ_BUF_DFLT_BLK_ENTRIES + j; + buf->desc = (u8 *)wq->ring.descs + + wq->ring.desc_size * buf->index; + if (buf->index + 1 == count) { + buf->next = wq->bufs[0]; + break; + } else if (j + 1 == VNIC_WQ_BUF_DFLT_BLK_ENTRIES) { + buf->next = wq->bufs[i + 1]; + } else { + buf->next = buf + 1; + buf++; + } + } + } + + wq->to_use = wq->to_clean = wq->bufs[0]; + + return 0; +} + +void svnic_wq_free(struct vnic_wq *wq) +{ + struct vnic_dev *vdev; + unsigned int i; + + vdev = wq->vdev; + + svnic_dev_free_desc_ring(vdev, &wq->ring); + + for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) { + kfree(wq->bufs[i]); + wq->bufs[i] = NULL; + } + + wq->ctrl = NULL; + +} + +int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int desc_count, unsigned int desc_size) +{ + int err; + + wq->index = 0; + wq->vdev = vdev; + + err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2); + if (err) { + pr_err("Failed to get devcmd2 resource\n"); + + return err; + } + + svnic_wq_disable(wq); + + err = vnic_wq_alloc_ring(vdev, wq, 0, desc_count, desc_size); + if (err) + return err; + + return 0; +} + +int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int index, unsigned int desc_count, unsigned int desc_size) +{ + int err; + + wq->index = index; + wq->vdev = vdev; + + err = vnic_wq_get_ctrl(vdev, wq, index, RES_TYPE_WQ); + if (err) { + pr_err("Failed to hook WQ[%d] resource\n", index); + + return err; + } + + svnic_wq_disable(wq); + + err = vnic_wq_alloc_ring(vdev, wq, index, desc_count, desc_size); + if (err) + return err; + + err = vnic_wq_alloc_bufs(wq); + if (err) { + svnic_wq_free(wq); + + return err; + } + + return 0; +} + +void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, + unsigned int fetch_index, unsigned int posted_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset) +{ + u64 paddr; + unsigned int count = wq->ring.desc_count; + + paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET; + writeq(paddr, &wq->ctrl->ring_base); + iowrite32(count, &wq->ctrl->ring_size); + iowrite32(fetch_index, &wq->ctrl->fetch_index); + iowrite32(posted_index, &wq->ctrl->posted_index); + iowrite32(cq_index, &wq->ctrl->cq_index); + iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable); + iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset); + iowrite32(0, &wq->ctrl->error_status); + + wq->to_use = wq->to_clean = + &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES(count)] + [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES(count)]; +} + +void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset) +{ + vnic_wq_init_start(wq, cq_index, 0, 0, error_interrupt_enable, + error_interrupt_offset); +} + +unsigned int svnic_wq_error_status(struct vnic_wq *wq) +{ + return ioread32(&wq->ctrl->error_status); +} + +void svnic_wq_enable(struct vnic_wq *wq) +{ + iowrite32(1, &wq->ctrl->enable); +} + +int svnic_wq_disable(struct vnic_wq *wq) +{ + unsigned int wait; + + iowrite32(0, &wq->ctrl->enable); + + /* Wait for HW to ACK disable request */ + for (wait = 0; wait < 100; wait++) { + if (!(ioread32(&wq->ctrl->running))) + return 0; + udelay(1); + } + + pr_err("Failed to disable WQ[%d]\n", wq->index); + + return -ETIMEDOUT; +} + +void svnic_wq_clean(struct vnic_wq *wq, + void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf)) +{ + struct vnic_wq_buf *buf; + + BUG_ON(ioread32(&wq->ctrl->enable)); + + buf = wq->to_clean; + + while (svnic_wq_desc_used(wq) > 0) { + + (*buf_clean)(wq, buf); + + buf = wq->to_clean = buf->next; + wq->ring.desc_avail++; + } + + wq->to_use = wq->to_clean = wq->bufs[0]; + + iowrite32(0, &wq->ctrl->fetch_index); + iowrite32(0, &wq->ctrl->posted_index); + iowrite32(0, &wq->ctrl->error_status); + + svnic_dev_clear_desc_ring(&wq->ring); +} diff --git a/kernel/drivers/scsi/snic/vnic_wq.h b/kernel/drivers/scsi/snic/vnic_wq.h new file mode 100644 index 000000000..7cc031c7c --- /dev/null +++ b/kernel/drivers/scsi/snic/vnic_wq.h @@ -0,0 +1,170 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VNIC_WQ_H_ +#define _VNIC_WQ_H_ + +#include +#include "vnic_dev.h" +#include "vnic_cq.h" + +/* Work queue control */ +struct vnic_wq_ctrl { + u64 ring_base; /* 0x00 */ + u32 ring_size; /* 0x08 */ + u32 pad0; + u32 posted_index; /* 0x10 */ + u32 pad1; + u32 cq_index; /* 0x18 */ + u32 pad2; + u32 enable; /* 0x20 */ + u32 pad3; + u32 running; /* 0x28 */ + u32 pad4; + u32 fetch_index; /* 0x30 */ + u32 pad5; + u32 dca_value; /* 0x38 */ + u32 pad6; + u32 error_interrupt_enable; /* 0x40 */ + u32 pad7; + u32 error_interrupt_offset; /* 0x48 */ + u32 pad8; + u32 error_status; /* 0x50 */ + u32 pad9; +}; + +struct vnic_wq_buf { + struct vnic_wq_buf *next; + dma_addr_t dma_addr; + void *os_buf; + unsigned int len; + unsigned int index; + int sop; + void *desc; +}; + +/* Break the vnic_wq_buf allocations into blocks of 64 entries */ +#define VNIC_WQ_BUF_MIN_BLK_ENTRIES 32 +#define VNIC_WQ_BUF_DFLT_BLK_ENTRIES 64 +#define VNIC_WQ_BUF_BLK_ENTRIES(entries) \ + ((unsigned int)(entries < VNIC_WQ_BUF_DFLT_BLK_ENTRIES) ? \ + VNIC_WQ_BUF_MIN_BLK_ENTRIES : VNIC_WQ_BUF_DFLT_BLK_ENTRIES) +#define VNIC_WQ_BUF_BLK_SZ \ + (VNIC_WQ_BUF_DFLT_BLK_ENTRIES * sizeof(struct vnic_wq_buf)) +#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \ + DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES) +#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \ + DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES) +#define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096) + +struct vnic_wq { + unsigned int index; + struct vnic_dev *vdev; + struct vnic_wq_ctrl __iomem *ctrl; /* memory-mapped */ + struct vnic_dev_ring ring; + struct vnic_wq_buf *bufs[VNIC_WQ_BUF_BLKS_MAX]; + struct vnic_wq_buf *to_use; + struct vnic_wq_buf *to_clean; + unsigned int pkts_outstanding; +}; + +static inline unsigned int svnic_wq_desc_avail(struct vnic_wq *wq) +{ + /* how many does SW own? */ + return wq->ring.desc_avail; +} + +static inline unsigned int svnic_wq_desc_used(struct vnic_wq *wq) +{ + /* how many does HW own? */ + return wq->ring.desc_count - wq->ring.desc_avail - 1; +} + +static inline void *svnic_wq_next_desc(struct vnic_wq *wq) +{ + return wq->to_use->desc; +} + +static inline void svnic_wq_post(struct vnic_wq *wq, + void *os_buf, dma_addr_t dma_addr, + unsigned int len, int sop, int eop) +{ + struct vnic_wq_buf *buf = wq->to_use; + + buf->sop = sop; + buf->os_buf = eop ? os_buf : NULL; + buf->dma_addr = dma_addr; + buf->len = len; + + buf = buf->next; + if (eop) { + /* Adding write memory barrier prevents compiler and/or CPU + * reordering, thus avoiding descriptor posting before + * descriptor is initialized. Otherwise, hardware can read + * stale descriptor fields. + */ + wmb(); + iowrite32(buf->index, &wq->ctrl->posted_index); + } + wq->to_use = buf; + + wq->ring.desc_avail--; +} + +static inline void svnic_wq_service(struct vnic_wq *wq, + struct cq_desc *cq_desc, u16 completed_index, + void (*buf_service)(struct vnic_wq *wq, + struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque), + void *opaque) +{ + struct vnic_wq_buf *buf; + + buf = wq->to_clean; + while (1) { + + (*buf_service)(wq, cq_desc, buf, opaque); + + wq->ring.desc_avail++; + + wq->to_clean = buf->next; + + if (buf->index == completed_index) + break; + + buf = wq->to_clean; + } +} + +void svnic_wq_free(struct vnic_wq *wq); +int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int index, unsigned int desc_count, unsigned int desc_size); +int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq, + unsigned int desc_count, unsigned int desc_size); +void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index, + unsigned int fetch_index, unsigned int post_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset); + +void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index, + unsigned int error_interrupt_enable, + unsigned int error_interrupt_offset); +unsigned int svnic_wq_error_status(struct vnic_wq *wq); +void svnic_wq_enable(struct vnic_wq *wq); +int svnic_wq_disable(struct vnic_wq *wq); +void svnic_wq_clean(struct vnic_wq *wq, + void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf)); +#endif /* _VNIC_WQ_H_ */ diff --git a/kernel/drivers/scsi/snic/wq_enet_desc.h b/kernel/drivers/scsi/snic/wq_enet_desc.h new file mode 100644 index 000000000..68f62b6d1 --- /dev/null +++ b/kernel/drivers/scsi/snic/wq_enet_desc.h @@ -0,0 +1,96 @@ +/* + * Copyright 2014 Cisco Systems, Inc. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _WQ_ENET_DESC_H_ +#define _WQ_ENET_DESC_H_ + +/* Ethernet work queue descriptor: 16B */ +struct wq_enet_desc { + __le64 address; + __le16 length; + __le16 mss_loopback; + __le16 header_length_flags; + __le16 vlan_tag; +}; + +#define WQ_ENET_ADDR_BITS 64 +#define WQ_ENET_LEN_BITS 14 +#define WQ_ENET_LEN_MASK ((1 << WQ_ENET_LEN_BITS) - 1) +#define WQ_ENET_MSS_BITS 14 +#define WQ_ENET_MSS_MASK ((1 << WQ_ENET_MSS_BITS) - 1) +#define WQ_ENET_MSS_SHIFT 2 +#define WQ_ENET_LOOPBACK_SHIFT 1 +#define WQ_ENET_HDRLEN_BITS 10 +#define WQ_ENET_HDRLEN_MASK ((1 << WQ_ENET_HDRLEN_BITS) - 1) +#define WQ_ENET_FLAGS_OM_BITS 2 +#define WQ_ENET_FLAGS_OM_MASK ((1 << WQ_ENET_FLAGS_OM_BITS) - 1) +#define WQ_ENET_FLAGS_EOP_SHIFT 12 +#define WQ_ENET_FLAGS_CQ_ENTRY_SHIFT 13 +#define WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT 14 +#define WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT 15 + +#define WQ_ENET_OFFLOAD_MODE_CSUM 0 +#define WQ_ENET_OFFLOAD_MODE_RESERVED 1 +#define WQ_ENET_OFFLOAD_MODE_CSUM_L4 2 +#define WQ_ENET_OFFLOAD_MODE_TSO 3 + +static inline void wq_enet_desc_enc(struct wq_enet_desc *desc, + u64 address, u16 length, u16 mss, u16 header_length, + u8 offload_mode, u8 eop, u8 cq_entry, u8 fcoe_encap, + u8 vlan_tag_insert, u16 vlan_tag, u8 loopback) +{ + desc->address = cpu_to_le64(address); + desc->length = cpu_to_le16(length & WQ_ENET_LEN_MASK); + desc->mss_loopback = cpu_to_le16((mss & WQ_ENET_MSS_MASK) << + WQ_ENET_MSS_SHIFT | (loopback & 1) << WQ_ENET_LOOPBACK_SHIFT); + desc->header_length_flags = cpu_to_le16( + (header_length & WQ_ENET_HDRLEN_MASK) | + (offload_mode & WQ_ENET_FLAGS_OM_MASK) << WQ_ENET_HDRLEN_BITS | + (eop & 1) << WQ_ENET_FLAGS_EOP_SHIFT | + (cq_entry & 1) << WQ_ENET_FLAGS_CQ_ENTRY_SHIFT | + (fcoe_encap & 1) << WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT | + (vlan_tag_insert & 1) << WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT); + desc->vlan_tag = cpu_to_le16(vlan_tag); +} + +static inline void wq_enet_desc_dec(struct wq_enet_desc *desc, + u64 *address, u16 *length, u16 *mss, u16 *header_length, + u8 *offload_mode, u8 *eop, u8 *cq_entry, u8 *fcoe_encap, + u8 *vlan_tag_insert, u16 *vlan_tag, u8 *loopback) +{ + *address = le64_to_cpu(desc->address); + *length = le16_to_cpu(desc->length) & WQ_ENET_LEN_MASK; + *mss = (le16_to_cpu(desc->mss_loopback) >> WQ_ENET_MSS_SHIFT) & + WQ_ENET_MSS_MASK; + *loopback = (u8)((le16_to_cpu(desc->mss_loopback) >> + WQ_ENET_LOOPBACK_SHIFT) & 1); + *header_length = le16_to_cpu(desc->header_length_flags) & + WQ_ENET_HDRLEN_MASK; + *offload_mode = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_HDRLEN_BITS) & WQ_ENET_FLAGS_OM_MASK); + *eop = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_EOP_SHIFT) & 1); + *cq_entry = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_CQ_ENTRY_SHIFT) & 1); + *fcoe_encap = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT) & 1); + *vlan_tag_insert = (u8)((le16_to_cpu(desc->header_length_flags) >> + WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT) & 1); + *vlan_tag = le16_to_cpu(desc->vlan_tag); +} + +#endif /* _WQ_ENET_DESC_H_ */ diff --git a/kernel/drivers/scsi/sr.c b/kernel/drivers/scsi/sr.c index 8bd54a64e..64c867405 100644 --- a/kernel/drivers/scsi/sr.c +++ b/kernel/drivers/scsi/sr.c @@ -144,6 +144,9 @@ static int sr_runtime_suspend(struct device *dev) { struct scsi_cd *cd = dev_get_drvdata(dev); + if (!cd) /* E.g.: runtime suspend following sr_remove() */ + return 0; + if (cd->media_present) return -EBUSY; else @@ -985,6 +988,7 @@ static int sr_remove(struct device *dev) scsi_autopm_get_device(cd->device); del_gendisk(cd->disk); + dev_set_drvdata(dev, NULL); mutex_lock(&sr_ref_mutex); kref_put(&cd->kref, sr_kref_release); diff --git a/kernel/drivers/scsi/st.c b/kernel/drivers/scsi/st.c index 525ab4c1f..2e522951b 100644 --- a/kernel/drivers/scsi/st.c +++ b/kernel/drivers/scsi/st.c @@ -85,6 +85,7 @@ static int debug_flag; static struct class st_sysfs_class; static const struct attribute_group *st_dev_groups[]; +static const struct attribute_group *st_drv_groups[]; MODULE_AUTHOR("Kai Makisara"); MODULE_DESCRIPTION("SCSI tape (st) driver"); @@ -198,15 +199,13 @@ static int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int); static int st_probe(struct device *); static int st_remove(struct device *); -static int do_create_sysfs_files(void); -static void do_remove_sysfs_files(void); - static struct scsi_driver st_template = { .gendrv = { .name = "st", .owner = THIS_MODULE, .probe = st_probe, .remove = st_remove, + .groups = st_drv_groups, }, }; @@ -471,6 +470,47 @@ static void st_release_request(struct st_request *streq) kfree(streq); } +static void st_do_stats(struct scsi_tape *STp, struct request *req) +{ + ktime_t now; + + now = ktime_get(); + if (req->cmd[0] == WRITE_6) { + now = ktime_sub(now, STp->stats->write_time); + atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time); + atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); + atomic64_inc(&STp->stats->write_cnt); + if (req->errors) { + atomic64_add(atomic_read(&STp->stats->last_write_size) + - STp->buffer->cmdstat.residual, + &STp->stats->write_byte_cnt); + if (STp->buffer->cmdstat.residual > 0) + atomic64_inc(&STp->stats->resid_cnt); + } else + atomic64_add(atomic_read(&STp->stats->last_write_size), + &STp->stats->write_byte_cnt); + } else if (req->cmd[0] == READ_6) { + now = ktime_sub(now, STp->stats->read_time); + atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time); + atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); + atomic64_inc(&STp->stats->read_cnt); + if (req->errors) { + atomic64_add(atomic_read(&STp->stats->last_read_size) + - STp->buffer->cmdstat.residual, + &STp->stats->read_byte_cnt); + if (STp->buffer->cmdstat.residual > 0) + atomic64_inc(&STp->stats->resid_cnt); + } else + atomic64_add(atomic_read(&STp->stats->last_read_size), + &STp->stats->read_byte_cnt); + } else { + now = ktime_sub(now, STp->stats->other_time); + atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); + atomic64_inc(&STp->stats->other_cnt); + } + atomic64_dec(&STp->stats->in_flight); +} + static void st_scsi_execute_end(struct request *req, int uptodate) { struct st_request *SRpnt = req->end_io_data; @@ -480,6 +520,8 @@ static void st_scsi_execute_end(struct request *req, int uptodate) STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors; STp->buffer->cmdstat.residual = req->resid_len; + st_do_stats(STp, req); + tmp = SRpnt->bio; if (SRpnt->waiting) complete(SRpnt->waiting); @@ -496,6 +538,7 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd, struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; int err = 0; int write = (data_direction == DMA_TO_DEVICE); + struct scsi_tape *STp = SRpnt->stp; req = blk_get_request(SRpnt->stp->device->request_queue, write, GFP_KERNEL); @@ -516,6 +559,17 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd, } } + atomic64_inc(&STp->stats->in_flight); + if (cmd[0] == WRITE_6) { + atomic_set(&STp->stats->last_write_size, bufflen); + STp->stats->write_time = ktime_get(); + } else if (cmd[0] == READ_6) { + atomic_set(&STp->stats->last_read_size, bufflen); + STp->stats->read_time = ktime_get(); + } else { + STp->stats->other_time = ktime_get(); + } + SRpnt->bio = req->bio; req->cmd_len = COMMAND_SIZE(cmd[0]); memset(req->cmd, 0, BLK_MAX_CDB); @@ -4029,6 +4083,7 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew) } cdev->owner = THIS_MODULE; cdev->ops = &st_fops; + STm->cdevs[rew] = cdev; error = cdev_add(cdev, cdev_devno, 1); if (error) { @@ -4037,7 +4092,6 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew) pr_err("st%d: Device not attached.\n", dev_num); goto out_free; } - STm->cdevs[rew] = cdev; i = mode << (4 - ST_NBR_MODE_BITS); snprintf(name, 10, "%s%s%s", rew ? "n" : "", @@ -4056,8 +4110,9 @@ static int create_one_cdev(struct scsi_tape *tape, int mode, int rew) return 0; out_free: cdev_del(STm->cdevs[rew]); - STm->cdevs[rew] = NULL; out: + STm->cdevs[rew] = NULL; + STm->devs[rew] = NULL; return error; } @@ -4222,6 +4277,12 @@ static int st_probe(struct device *dev) } tpnt->index = error; sprintf(disk->disk_name, "st%d", tpnt->index); + tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL); + if (tpnt->stats == NULL) { + sdev_printk(KERN_ERR, SDp, + "st: Can't allocate statistics.\n"); + goto out_idr_remove; + } dev_set_drvdata(dev, tpnt); @@ -4241,6 +4302,8 @@ static int st_probe(struct device *dev) out_remove_devs: remove_cdevs(tpnt); + kfree(tpnt->stats); +out_idr_remove: spin_lock(&st_index_lock); idr_remove(&st_index_idr, tpnt->index); spin_unlock(&st_index_lock); @@ -4298,6 +4361,7 @@ static void scsi_tape_release(struct kref *kref) disk->private_data = NULL; put_disk(disk); + kfree(tpnt->stats); kfree(tpnt); return; } @@ -4340,14 +4404,8 @@ static int __init init_st(void) if (err) goto err_chrdev; - err = do_create_sysfs_files(); - if (err) - goto err_scsidrv; - return 0; -err_scsidrv: - scsi_unregister_driver(&st_template.gendrv); err_chrdev: unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), ST_MAX_TAPE_ENTRIES); @@ -4358,11 +4416,11 @@ err_class: static void __exit exit_st(void) { - do_remove_sysfs_files(); scsi_unregister_driver(&st_template.gendrv); unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), ST_MAX_TAPE_ENTRIES); class_unregister(&st_sysfs_class); + idr_destroy(&st_index_idr); printk(KERN_INFO "st: Unloaded.\n"); } @@ -4371,68 +4429,68 @@ module_exit(exit_st); /* The sysfs driver interface. Read-only at the moment */ -static ssize_t st_try_direct_io_show(struct device_driver *ddp, char *buf) +static ssize_t try_direct_io_show(struct device_driver *ddp, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", try_direct_io); + return scnprintf(buf, PAGE_SIZE, "%d\n", try_direct_io); } -static DRIVER_ATTR(try_direct_io, S_IRUGO, st_try_direct_io_show, NULL); +static DRIVER_ATTR_RO(try_direct_io); -static ssize_t st_fixed_buffer_size_show(struct device_driver *ddp, char *buf) +static ssize_t fixed_buffer_size_show(struct device_driver *ddp, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", st_fixed_buffer_size); + return scnprintf(buf, PAGE_SIZE, "%d\n", st_fixed_buffer_size); } -static DRIVER_ATTR(fixed_buffer_size, S_IRUGO, st_fixed_buffer_size_show, NULL); +static DRIVER_ATTR_RO(fixed_buffer_size); -static ssize_t st_max_sg_segs_show(struct device_driver *ddp, char *buf) +static ssize_t max_sg_segs_show(struct device_driver *ddp, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", st_max_sg_segs); + return scnprintf(buf, PAGE_SIZE, "%d\n", st_max_sg_segs); } -static DRIVER_ATTR(max_sg_segs, S_IRUGO, st_max_sg_segs_show, NULL); +static DRIVER_ATTR_RO(max_sg_segs); -static ssize_t st_version_show(struct device_driver *ddd, char *buf) +static ssize_t version_show(struct device_driver *ddd, char *buf) { - return snprintf(buf, PAGE_SIZE, "[%s]\n", verstr); + return scnprintf(buf, PAGE_SIZE, "[%s]\n", verstr); } -static DRIVER_ATTR(version, S_IRUGO, st_version_show, NULL); +static DRIVER_ATTR_RO(version); -static int do_create_sysfs_files(void) +#if DEBUG +static ssize_t debug_flag_store(struct device_driver *ddp, + const char *buf, size_t count) { - struct device_driver *sysfs = &st_template.gendrv; - int err; - - err = driver_create_file(sysfs, &driver_attr_try_direct_io); - if (err) - return err; - err = driver_create_file(sysfs, &driver_attr_fixed_buffer_size); - if (err) - goto err_try_direct_io; - err = driver_create_file(sysfs, &driver_attr_max_sg_segs); - if (err) - goto err_attr_fixed_buf; - err = driver_create_file(sysfs, &driver_attr_version); - if (err) - goto err_attr_max_sg; - - return 0; - -err_attr_max_sg: - driver_remove_file(sysfs, &driver_attr_max_sg_segs); -err_attr_fixed_buf: - driver_remove_file(sysfs, &driver_attr_fixed_buffer_size); -err_try_direct_io: - driver_remove_file(sysfs, &driver_attr_try_direct_io); - return err; +/* We only care what the first byte of the data is the rest is unused. + * if it's a '1' we turn on debug and if it's a '0' we disable it. All + * other values have -EINVAL returned if they are passed in. + */ + if (count > 0) { + if (buf[0] == '0') { + debugging = NO_DEBUG; + return count; + } else if (buf[0] == '1') { + debugging = 1; + return count; + } + } + return -EINVAL; } -static void do_remove_sysfs_files(void) +static ssize_t debug_flag_show(struct device_driver *ddp, char *buf) { - struct device_driver *sysfs = &st_template.gendrv; - - driver_remove_file(sysfs, &driver_attr_version); - driver_remove_file(sysfs, &driver_attr_max_sg_segs); - driver_remove_file(sysfs, &driver_attr_fixed_buffer_size); - driver_remove_file(sysfs, &driver_attr_try_direct_io); + return scnprintf(buf, PAGE_SIZE, "%d\n", debugging); } +static DRIVER_ATTR_RW(debug_flag); +#endif + +static struct attribute *st_drv_attrs[] = { + &driver_attr_try_direct_io.attr, + &driver_attr_fixed_buffer_size.attr, + &driver_attr_max_sg_segs.attr, + &driver_attr_version.attr, +#if DEBUG + &driver_attr_debug_flag.attr, +#endif + NULL, +}; +ATTRIBUTE_GROUPS(st_drv); /* The sysfs simple class interface */ static ssize_t @@ -4513,6 +4571,184 @@ options_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(options); +/* Support for tape stats */ + +/** + * read_cnt_show - return read count - count of reads made from tape drive + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t read_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->read_cnt)); +} +static DEVICE_ATTR_RO(read_cnt); + +/** + * read_byte_cnt_show - return read byte count - tape drives + * may use blocks less than 512 bytes this gives the raw byte count of + * of data read from the tape drive. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t read_byte_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->read_byte_cnt)); +} +static DEVICE_ATTR_RO(read_byte_cnt); + +/** + * read_us_show - return read us - overall time spent waiting on reads in ns. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t read_ns_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->tot_read_time)); +} +static DEVICE_ATTR_RO(read_ns); + +/** + * write_cnt_show - write count - number of user calls + * to write(2) that have written data to tape. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t write_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->write_cnt)); +} +static DEVICE_ATTR_RO(write_cnt); + +/** + * write_byte_cnt_show - write byte count - raw count of + * bytes written to tape. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t write_byte_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->write_byte_cnt)); +} +static DEVICE_ATTR_RO(write_byte_cnt); + +/** + * write_ns_show - write ns - number of nanoseconds waiting on write + * requests to complete. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t write_ns_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->tot_write_time)); +} +static DEVICE_ATTR_RO(write_ns); + +/** + * in_flight_show - number of I/Os currently in flight - + * in most cases this will be either 0 or 1. It may be higher if someone + * has also issued other SCSI commands such as via an ioctl. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t in_flight_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->in_flight)); +} +static DEVICE_ATTR_RO(in_flight); + +/** + * io_ns_show - io wait ns - this is the number of ns spent + * waiting on all I/O to complete. This includes tape movement commands + * such as rewinding, seeking to end of file or tape, it also includes + * read and write. To determine the time spent on tape movement + * subtract the read and write ns from this value. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t io_ns_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->tot_io_time)); +} +static DEVICE_ATTR_RO(io_ns); + +/** + * other_cnt_show - other io count - this is the number of + * I/O requests other than read and write requests. + * Typically these are tape movement requests but will include driver + * tape movement. This includes only requests issued by the st driver. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t other_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->other_cnt)); +} +static DEVICE_ATTR_RO(other_cnt); + +/** + * resid_cnt_show - A count of the number of times we get a residual + * count - this should indicate someone issuing reads larger than the + * block size on tape. + * @dev: struct device + * @attr: attribute structure + * @buf: buffer to return formatted data in + */ +static ssize_t resid_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_modedef *STm = dev_get_drvdata(dev); + + return sprintf(buf, "%lld", + (long long)atomic64_read(&STm->tape->stats->resid_cnt)); +} +static DEVICE_ATTR_RO(resid_cnt); + static struct attribute *st_dev_attrs[] = { &dev_attr_defined.attr, &dev_attr_default_blksize.attr, @@ -4521,7 +4757,35 @@ static struct attribute *st_dev_attrs[] = { &dev_attr_options.attr, NULL, }; -ATTRIBUTE_GROUPS(st_dev); + +static struct attribute *st_stats_attrs[] = { + &dev_attr_read_cnt.attr, + &dev_attr_read_byte_cnt.attr, + &dev_attr_read_ns.attr, + &dev_attr_write_cnt.attr, + &dev_attr_write_byte_cnt.attr, + &dev_attr_write_ns.attr, + &dev_attr_in_flight.attr, + &dev_attr_io_ns.attr, + &dev_attr_other_cnt.attr, + &dev_attr_resid_cnt.attr, + NULL, +}; + +static struct attribute_group stats_group = { + .name = "stats", + .attrs = st_stats_attrs, +}; + +static struct attribute_group st_group = { + .attrs = st_dev_attrs, +}; + +static const struct attribute_group *st_dev_groups[] = { + &st_group, + &stats_group, + NULL, +}; /* The following functions may be useful for a larger audience. */ static int sgl_map_user_pages(struct st_buffer *STbp, diff --git a/kernel/drivers/scsi/st.h b/kernel/drivers/scsi/st.h index f3eee0f9f..b6486b5d8 100644 --- a/kernel/drivers/scsi/st.h +++ b/kernel/drivers/scsi/st.h @@ -92,6 +92,27 @@ struct st_partstat { int drv_file; }; +/* Tape statistics */ +struct scsi_tape_stats { + atomic64_t read_byte_cnt; /* bytes read */ + atomic64_t write_byte_cnt; /* bytes written */ + atomic64_t in_flight; /* Number of I/Os in flight */ + atomic64_t read_cnt; /* Count of read requests */ + atomic64_t write_cnt; /* Count of write requests */ + atomic64_t other_cnt; /* Count of other requests either + * implicit or from user space + * ioctl. */ + atomic64_t resid_cnt; /* Count of resid_len > 0 */ + atomic64_t tot_read_time; /* ktime spent completing reads */ + atomic64_t tot_write_time; /* ktime spent completing writes */ + atomic64_t tot_io_time; /* ktime spent doing any I/O */ + ktime_t read_time; /* holds ktime request was queued */ + ktime_t write_time; /* holds ktime request was queued */ + ktime_t other_time; /* holds ktime request was queued */ + atomic_t last_read_size; /* Number of bytes issued for last read */ + atomic_t last_write_size; /* Number of bytes issued for last write */ +}; + #define ST_NBR_PARTITIONS 4 /* The tape drive descriptor */ @@ -171,6 +192,7 @@ struct scsi_tape { #endif struct gendisk *disk; struct kref kref; + struct scsi_tape_stats *stats; }; /* Bit masks for use_pf */ diff --git a/kernel/drivers/scsi/stex.c b/kernel/drivers/scsi/stex.c index 98a62bc15..2de28d7a0 100644 --- a/kernel/drivers/scsi/stex.c +++ b/kernel/drivers/scsi/stex.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -362,14 +363,6 @@ MODULE_DESCRIPTION("Promise Technology SuperTrak EX Controllers"); MODULE_LICENSE("GPL"); MODULE_VERSION(ST_DRIVER_VERSION); -static void stex_gettime(__le64 *time) -{ - struct timeval tv; - - do_gettimeofday(&tv); - *time = cpu_to_le64(tv.tv_sec); -} - static struct status_msg *stex_get_status(struct st_hba *hba) { struct status_msg *status = hba->status_buffer + hba->status_tail; @@ -1002,7 +995,7 @@ static int stex_common_handshake(struct st_hba *hba) h->req_cnt = cpu_to_le16(hba->rq_count+1); h->status_sz = cpu_to_le16(sizeof(struct status_msg)); h->status_cnt = cpu_to_le16(hba->sts_count+1); - stex_gettime(&h->hosttime); + h->hosttime = cpu_to_le64(ktime_get_real_seconds()); h->partner_type = HMU_PARTNER_TYPE; if (hba->extra_offset) { h->extra_offset = cpu_to_le32(hba->extra_offset); @@ -1076,7 +1069,7 @@ static int stex_ss_handshake(struct st_hba *hba) h->req_cnt = cpu_to_le16(hba->rq_count+1); h->status_sz = cpu_to_le16(sizeof(struct status_msg)); h->status_cnt = cpu_to_le16(hba->sts_count+1); - stex_gettime(&h->hosttime); + h->hosttime = cpu_to_le64(ktime_get_real_seconds()); h->partner_type = HMU_PARTNER_TYPE; h->extra_offset = h->extra_size = 0; scratch_size = (hba->sts_count+1)*sizeof(u32); @@ -1374,7 +1367,6 @@ static struct scsi_host_template driver_template = { .eh_abort_handler = stex_abort, .eh_host_reset_handler = stex_reset, .this_id = -1, - .use_blk_tags = 1, }; static struct pci_device_id stex_pci_tbl[] = { @@ -1659,13 +1651,6 @@ static int stex_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto out_free_irq; - err = scsi_init_shared_tag_map(host, host->can_queue); - if (err) { - printk(KERN_ERR DRV_NAME "(%s): init shared queue failed\n", - pci_name(pdev)); - goto out_free_irq; - } - pci_set_drvdata(pdev, hba); err = scsi_add_host(host, &pdev->dev); diff --git a/kernel/drivers/scsi/storvsc_drv.c b/kernel/drivers/scsi/storvsc_drv.c index 3c6584ff6..3fba42ad9 100644 --- a/kernel/drivers/scsi/storvsc_drv.c +++ b/kernel/drivers/scsi/storvsc_drv.c @@ -56,15 +56,18 @@ * V1 RC > 2008/1/31: 2.0 * Win7: 4.2 * Win8: 5.1 + * Win8.1: 6.0 + * Win10: 6.2 */ +#define VMSTOR_PROTO_VERSION(MAJOR_, MINOR_) ((((MAJOR_) & 0xff) << 8) | \ + (((MINOR_) & 0xff))) -#define VMSTOR_WIN7_MAJOR 4 -#define VMSTOR_WIN7_MINOR 2 - -#define VMSTOR_WIN8_MAJOR 5 -#define VMSTOR_WIN8_MINOR 1 - +#define VMSTOR_PROTO_VERSION_WIN6 VMSTOR_PROTO_VERSION(2, 0) +#define VMSTOR_PROTO_VERSION_WIN7 VMSTOR_PROTO_VERSION(4, 2) +#define VMSTOR_PROTO_VERSION_WIN8 VMSTOR_PROTO_VERSION(5, 1) +#define VMSTOR_PROTO_VERSION_WIN8_1 VMSTOR_PROTO_VERSION(6, 0) +#define VMSTOR_PROTO_VERSION_WIN10 VMSTOR_PROTO_VERSION(6, 2) /* Packet structure describing virtual storage requests. */ enum vstor_packet_operation { @@ -148,21 +151,18 @@ struct hv_fc_wwn_packet { /* * Sense buffer size changed in win8; have a run-time - * variable to track the size we should use. + * variable to track the size we should use. This value will + * likely change during protocol negotiation but it is valid + * to start by assuming pre-Win8. */ -static int sense_buffer_size; +static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE; /* - * The size of the vmscsi_request has changed in win8. The - * additional size is because of new elements added to the - * structure. These elements are valid only when we are talking - * to a win8 host. - * Track the correction to size we need to apply. - */ - -static int vmscsi_size_delta; -static int vmstor_current_major; -static int vmstor_current_minor; + * The storage protocol version is determined during the + * initial exchange with the host. It will indicate which + * storage functionality is available in the host. +*/ +static int vmstor_proto_version; struct vmscsi_win8_extension { /* @@ -206,6 +206,56 @@ struct vmscsi_request { } __attribute((packed)); +/* + * The size of the vmscsi_request has changed in win8. The + * additional size is because of new elements added to the + * structure. These elements are valid only when we are talking + * to a win8 host. + * Track the correction to size we need to apply. This value + * will likely change during protocol negotiation but it is + * valid to start by assuming pre-Win8. + */ +static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); + +/* + * The list of storage protocols in order of preference. + */ +struct vmstor_protocol { + int protocol_version; + int sense_buffer_size; + int vmscsi_size_delta; +}; + + +static const struct vmstor_protocol vmstor_protocols[] = { + { + VMSTOR_PROTO_VERSION_WIN10, + POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, + 0 + }, + { + VMSTOR_PROTO_VERSION_WIN8_1, + POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, + 0 + }, + { + VMSTOR_PROTO_VERSION_WIN8, + POST_WIN7_STORVSC_SENSE_BUFFER_SIZE, + 0 + }, + { + VMSTOR_PROTO_VERSION_WIN7, + PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE, + sizeof(struct vmscsi_win8_extension), + }, + { + VMSTOR_PROTO_VERSION_WIN6, + PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE, + sizeof(struct vmscsi_win8_extension), + } +}; + + /* * This structure is sent during the intialization phase to get the different * properties of the channel. @@ -299,11 +349,14 @@ enum storvsc_request_type { */ #define SRB_STATUS_AUTOSENSE_VALID 0x80 +#define SRB_STATUS_QUEUE_FROZEN 0x40 #define SRB_STATUS_INVALID_LUN 0x20 #define SRB_STATUS_SUCCESS 0x01 #define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS(status) \ + (status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) /* * This is the end of Protocol specific defines. */ @@ -343,9 +396,6 @@ static void storvsc_on_channel_callback(void *context); struct storvsc_cmd_request { struct scsi_cmnd *cmd; - unsigned int bounce_sgl_count; - struct scatterlist *bounce_sgl; - struct hv_device *device; /* Synchronize the request/response if needed */ @@ -426,7 +476,6 @@ static void storvsc_host_scan(struct work_struct *work) struct storvsc_scan_work *wrk; struct Scsi_Host *host; struct scsi_device *sdev; - unsigned long flags; wrk = container_of(work, struct storvsc_scan_work, work); host = wrk->host; @@ -443,14 +492,8 @@ static void storvsc_host_scan(struct work_struct *work) * may have been removed this way. */ mutex_lock(&host->scan_mutex); - spin_lock_irqsave(host->host_lock, flags); - list_for_each_entry(sdev, &host->__devices, siblings) { - spin_unlock_irqrestore(host->host_lock, flags); + shost_for_each_device(sdev, host) scsi_test_unit_ready(sdev, 1, 1, NULL); - spin_lock_irqsave(host->host_lock, flags); - continue; - } - spin_unlock_irqrestore(host->host_lock, flags); mutex_unlock(&host->scan_mutex); /* * Now scan the host to discover LUNs that may have been added. @@ -481,18 +524,6 @@ done: kfree(wrk); } -/* - * Major/minor macros. Minor version is in LSB, meaning that earlier flat - * version numbers will be interpreted as "0.x" (i.e., 1 becomes 0.1). - */ - -static inline u16 storvsc_get_version(u8 major, u8 minor) -{ - u16 version; - - version = ((major << 8) | minor); - return version; -} /* * We can get incoming messages from the host that are not in response to @@ -555,241 +586,6 @@ get_in_err: } -static void destroy_bounce_buffer(struct scatterlist *sgl, - unsigned int sg_count) -{ - int i; - struct page *page_buf; - - for (i = 0; i < sg_count; i++) { - page_buf = sg_page((&sgl[i])); - if (page_buf != NULL) - __free_page(page_buf); - } - - kfree(sgl); -} - -static int do_bounce_buffer(struct scatterlist *sgl, unsigned int sg_count) -{ - int i; - - /* No need to check */ - if (sg_count < 2) - return -1; - - /* We have at least 2 sg entries */ - for (i = 0; i < sg_count; i++) { - if (i == 0) { - /* make sure 1st one does not have hole */ - if (sgl[i].offset + sgl[i].length != PAGE_SIZE) - return i; - } else if (i == sg_count - 1) { - /* make sure last one does not have hole */ - if (sgl[i].offset != 0) - return i; - } else { - /* make sure no hole in the middle */ - if (sgl[i].length != PAGE_SIZE || sgl[i].offset != 0) - return i; - } - } - return -1; -} - -static struct scatterlist *create_bounce_buffer(struct scatterlist *sgl, - unsigned int sg_count, - unsigned int len, - int write) -{ - int i; - int num_pages; - struct scatterlist *bounce_sgl; - struct page *page_buf; - unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); - - num_pages = ALIGN(len, PAGE_SIZE) >> PAGE_SHIFT; - - bounce_sgl = kcalloc(num_pages, sizeof(struct scatterlist), GFP_ATOMIC); - if (!bounce_sgl) - return NULL; - - sg_init_table(bounce_sgl, num_pages); - for (i = 0; i < num_pages; i++) { - page_buf = alloc_page(GFP_ATOMIC); - if (!page_buf) - goto cleanup; - sg_set_page(&bounce_sgl[i], page_buf, buf_len, 0); - } - - return bounce_sgl; - -cleanup: - destroy_bounce_buffer(bounce_sgl, num_pages); - return NULL; -} - -/* Assume the original sgl has enough room */ -static unsigned int copy_from_bounce_buffer(struct scatterlist *orig_sgl, - struct scatterlist *bounce_sgl, - unsigned int orig_sgl_count, - unsigned int bounce_sgl_count) -{ - int i; - int j = 0; - unsigned long src, dest; - unsigned int srclen, destlen, copylen; - unsigned int total_copied = 0; - unsigned long bounce_addr = 0; - unsigned long dest_addr = 0; - unsigned long flags; - struct scatterlist *cur_dest_sgl; - struct scatterlist *cur_src_sgl; - - local_irq_save(flags); - cur_dest_sgl = orig_sgl; - cur_src_sgl = bounce_sgl; - for (i = 0; i < orig_sgl_count; i++) { - dest_addr = (unsigned long) - kmap_atomic(sg_page(cur_dest_sgl)) + - cur_dest_sgl->offset; - dest = dest_addr; - destlen = cur_dest_sgl->length; - - if (bounce_addr == 0) - bounce_addr = (unsigned long)kmap_atomic( - sg_page(cur_src_sgl)); - - while (destlen) { - src = bounce_addr + cur_src_sgl->offset; - srclen = cur_src_sgl->length - cur_src_sgl->offset; - - copylen = min(srclen, destlen); - memcpy((void *)dest, (void *)src, copylen); - - total_copied += copylen; - cur_src_sgl->offset += copylen; - destlen -= copylen; - dest += copylen; - - if (cur_src_sgl->offset == cur_src_sgl->length) { - /* full */ - kunmap_atomic((void *)bounce_addr); - j++; - - /* - * It is possible that the number of elements - * in the bounce buffer may not be equal to - * the number of elements in the original - * scatter list. Handle this correctly. - */ - - if (j == bounce_sgl_count) { - /* - * We are done; cleanup and return. - */ - kunmap_atomic((void *)(dest_addr - - cur_dest_sgl->offset)); - local_irq_restore(flags); - return total_copied; - } - - /* if we need to use another bounce buffer */ - if (destlen || i != orig_sgl_count - 1) { - cur_src_sgl = sg_next(cur_src_sgl); - bounce_addr = (unsigned long) - kmap_atomic( - sg_page(cur_src_sgl)); - } - } else if (destlen == 0 && i == orig_sgl_count - 1) { - /* unmap the last bounce that is < PAGE_SIZE */ - kunmap_atomic((void *)bounce_addr); - } - } - - kunmap_atomic((void *)(dest_addr - cur_dest_sgl->offset)); - cur_dest_sgl = sg_next(cur_dest_sgl); - } - - local_irq_restore(flags); - - return total_copied; -} - -/* Assume the bounce_sgl has enough room ie using the create_bounce_buffer() */ -static unsigned int copy_to_bounce_buffer(struct scatterlist *orig_sgl, - struct scatterlist *bounce_sgl, - unsigned int orig_sgl_count) -{ - int i; - int j = 0; - unsigned long src, dest; - unsigned int srclen, destlen, copylen; - unsigned int total_copied = 0; - unsigned long bounce_addr = 0; - unsigned long src_addr = 0; - unsigned long flags; - struct scatterlist *cur_src_sgl; - struct scatterlist *cur_dest_sgl; - - local_irq_save(flags); - - cur_src_sgl = orig_sgl; - cur_dest_sgl = bounce_sgl; - - for (i = 0; i < orig_sgl_count; i++) { - src_addr = (unsigned long) - kmap_atomic(sg_page(cur_src_sgl)) + - cur_src_sgl->offset; - src = src_addr; - srclen = cur_src_sgl->length; - - if (bounce_addr == 0) - bounce_addr = (unsigned long) - kmap_atomic(sg_page(cur_dest_sgl)); - - while (srclen) { - /* assume bounce offset always == 0 */ - dest = bounce_addr + cur_dest_sgl->length; - destlen = PAGE_SIZE - cur_dest_sgl->length; - - copylen = min(srclen, destlen); - memcpy((void *)dest, (void *)src, copylen); - - total_copied += copylen; - cur_dest_sgl->length += copylen; - srclen -= copylen; - src += copylen; - - if (cur_dest_sgl->length == PAGE_SIZE) { - /* full..move to next entry */ - kunmap_atomic((void *)bounce_addr); - bounce_addr = 0; - j++; - } - - /* if we need to use another bounce buffer */ - if (srclen && bounce_addr == 0) { - cur_dest_sgl = sg_next(cur_dest_sgl); - bounce_addr = (unsigned long) - kmap_atomic( - sg_page(cur_dest_sgl)); - } - - } - - kunmap_atomic((void *)(src_addr - cur_src_sgl->offset)); - cur_src_sgl = sg_next(cur_src_sgl); - } - - if (bounce_addr) - kunmap_atomic((void *)bounce_addr); - - local_irq_restore(flags); - - return total_copied; -} - static void handle_sc_creation(struct vmbus_channel *new_sc) { struct hv_device *device = new_sc->primary_channel->device_obj; @@ -885,7 +681,7 @@ static int storvsc_channel_init(struct hv_device *device) struct storvsc_device *stor_device; struct storvsc_cmd_request *request; struct vstor_packet *vstor_packet; - int ret, t; + int ret, t, i; int max_chns; bool process_sub_channels = false; @@ -921,41 +717,65 @@ static int storvsc_channel_init(struct hv_device *device) } if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) + vstor_packet->status != 0) { + ret = -EINVAL; goto cleanup; + } - /* reuse the packet for version range supported */ - memset(vstor_packet, 0, sizeof(struct vstor_packet)); - vstor_packet->operation = VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; - vstor_packet->flags = REQUEST_COMPLETION_FLAG; + for (i = 0; i < ARRAY_SIZE(vmstor_protocols); i++) { + /* reuse the packet for version range supported */ + memset(vstor_packet, 0, sizeof(struct vstor_packet)); + vstor_packet->operation = + VSTOR_OPERATION_QUERY_PROTOCOL_VERSION; + vstor_packet->flags = REQUEST_COMPLETION_FLAG; - vstor_packet->version.major_minor = - storvsc_get_version(vmstor_current_major, vmstor_current_minor); + vstor_packet->version.major_minor = + vmstor_protocols[i].protocol_version; - /* - * The revision number is only used in Windows; set it to 0. - */ - vstor_packet->version.revision = 0; + /* + * The revision number is only used in Windows; set it to 0. + */ + vstor_packet->version.revision = 0; - ret = vmbus_sendpacket(device->channel, vstor_packet, + ret = vmbus_sendpacket(device->channel, vstor_packet, (sizeof(struct vstor_packet) - vmscsi_size_delta), (unsigned long)request, VM_PKT_DATA_INBAND, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); - if (ret != 0) - goto cleanup; + if (ret != 0) + goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); - if (t == 0) { - ret = -ETIMEDOUT; - goto cleanup; + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); + if (t == 0) { + ret = -ETIMEDOUT; + goto cleanup; + } + + if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO) { + ret = -EINVAL; + goto cleanup; + } + + if (vstor_packet->status == 0) { + vmstor_proto_version = + vmstor_protocols[i].protocol_version; + + sense_buffer_size = + vmstor_protocols[i].sense_buffer_size; + + vmscsi_size_delta = + vmstor_protocols[i].vmscsi_size_delta; + + break; + } } - if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) + if (vstor_packet->status != 0) { + ret = -EINVAL; goto cleanup; + } memset(vstor_packet, 0, sizeof(struct vstor_packet)); @@ -979,8 +799,10 @@ static int storvsc_channel_init(struct hv_device *device) } if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) + vstor_packet->status != 0) { + ret = -EINVAL; goto cleanup; + } /* * Check to see if multi-channel support is there. @@ -988,8 +810,7 @@ static int storvsc_channel_init(struct hv_device *device) * support multi-channel. */ max_chns = vstor_packet->storage_channel_properties.max_channel_cnt; - if ((vmbus_proto_version != VERSION_WIN7) && - (vmbus_proto_version != VERSION_WS2008)) { + if (vmstor_proto_version >= VMSTOR_PROTO_VERSION_WIN8) { if (vstor_packet->storage_channel_properties.flags & STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL) process_sub_channels = true; @@ -1018,8 +839,10 @@ static int storvsc_channel_init(struct hv_device *device) } if (vstor_packet->operation != VSTOR_OPERATION_COMPLETE_IO || - vstor_packet->status != 0) + vstor_packet->status != 0) { + ret = -EINVAL; goto cleanup; + } if (process_sub_channels) handle_multichannel_storage(device, max_chns); @@ -1038,7 +861,7 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, void (*process_err_fn)(struct work_struct *work); bool do_work = false; - switch (vm_srb->srb_status) { + switch (SRB_STATUS(vm_srb->srb_status)) { case SRB_STATUS_ERROR: /* * If there is an error; offline the device since all @@ -1113,15 +936,6 @@ static void storvsc_command_completion(struct storvsc_cmd_request *cmd_request) host = stor_dev->host; vm_srb = &cmd_request->vstor_packet.vm_srb; - if (cmd_request->bounce_sgl_count) { - if (vm_srb->data_in == READ_TYPE) - copy_from_bounce_buffer(scsi_sglist(scmnd), - cmd_request->bounce_sgl, - scsi_sg_count(scmnd), - cmd_request->bounce_sgl_count); - destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); - } scmnd->result = vm_srb->scsi_status; @@ -1416,6 +1230,9 @@ static int storvsc_device_configure(struct scsi_device *sdevice) blk_queue_rq_timeout(sdevice->request_queue, (storvsc_timeout * HZ)); + /* Ensure there are no gaps in presented sgls */ + blk_queue_virt_boundary(sdevice->request_queue, PAGE_SIZE - 1); + sdevice->no_write_same = 1; /* @@ -1428,15 +1245,19 @@ static int storvsc_device_configure(struct scsi_device *sdevice) /* * If the host is WIN8 or WIN8 R2, claim conformance to SPC-3 - * if the device is a MSFT virtual device. + * if the device is a MSFT virtual device. If the host is + * WIN10 or newer, allow write_same. */ if (!strncmp(sdevice->vendor, "Msft", 4)) { - switch (vmbus_proto_version) { - case VERSION_WIN8: - case VERSION_WIN8_1: + switch (vmstor_proto_version) { + case VMSTOR_PROTO_VERSION_WIN8: + case VMSTOR_PROTO_VERSION_WIN8_1: sdevice->scsi_level = SCSI_SPC_3; break; } + + if (vmstor_proto_version >= VMSTOR_PROTO_VERSION_WIN10) + sdevice->no_write_same = 0; } return 0; @@ -1563,7 +1384,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) u32 payload_sz; u32 length; - if (vmstor_current_major <= VMSTOR_WIN8_MAJOR) { + if (vmstor_proto_version <= VMSTOR_PROTO_VERSION_WIN8) { /* * On legacy hosts filter unimplemented commands. * Future hosts are expected to correctly handle @@ -1585,8 +1406,7 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) vm_srb->win8_extension.time_out_value = 60; vm_srb->win8_extension.srb_flags |= - (SRB_FLAGS_QUEUE_ACTION_ENABLE | - SRB_FLAGS_DISABLE_SYNCH_TRANSFER); + SRB_FLAGS_DISABLE_SYNCH_TRANSFER; /* Build the SRB */ switch (scmnd->sc_data_direction) { @@ -1598,10 +1418,18 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) vm_srb->data_in = READ_TYPE; vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DATA_IN; break; - default: + case DMA_NONE: vm_srb->data_in = UNKNOWN_TYPE; vm_srb->win8_extension.srb_flags |= SRB_FLAGS_NO_DATA_TRANSFER; break; + default: + /* + * This is DMA_BIDIRECTIONAL or something else we are never + * supposed to see here. + */ + WARN(1, "Unexpected data direction: %d\n", + scmnd->sc_data_direction); + return -EINVAL; } @@ -1622,40 +1450,13 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) payload_sz = sizeof(cmd_request->mpb); if (sg_count) { - /* check if we need to bounce the sgl */ - if (do_bounce_buffer(sgl, scsi_sg_count(scmnd)) != -1) { - cmd_request->bounce_sgl = - create_bounce_buffer(sgl, sg_count, - length, - vm_srb->data_in); - if (!cmd_request->bounce_sgl) - return SCSI_MLQUEUE_HOST_BUSY; - - cmd_request->bounce_sgl_count = - ALIGN(length, PAGE_SIZE) >> PAGE_SHIFT; - - if (vm_srb->data_in == WRITE_TYPE) - copy_to_bounce_buffer(sgl, - cmd_request->bounce_sgl, sg_count); - - sgl = cmd_request->bounce_sgl; - sg_count = cmd_request->bounce_sgl_count; - } - - if (sg_count > MAX_PAGE_BUFFER_COUNT) { payload_sz = (sg_count * sizeof(void *) + sizeof(struct vmbus_packet_mpb_array)); payload = kmalloc(payload_sz, GFP_ATOMIC); - if (!payload) { - if (cmd_request->bounce_sgl_count) - destroy_bounce_buffer( - cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); - - return SCSI_MLQUEUE_DEVICE_BUSY; - } + if (!payload) + return SCSI_MLQUEUE_DEVICE_BUSY; } payload->range.len = length; @@ -1684,11 +1485,6 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd) if (ret == -EAGAIN) { /* no more space */ - - if (cmd_request->bounce_sgl_count) - destroy_bounce_buffer(cmd_request->bounce_sgl, - cmd_request->bounce_sgl_count); - return SCSI_MLQUEUE_DEVICE_BUSY; } @@ -1758,22 +1554,11 @@ static int storvsc_probe(struct hv_device *device, * set state to properly communicate with the host. */ - switch (vmbus_proto_version) { - case VERSION_WS2008: - case VERSION_WIN7: - sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE; - vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); - vmstor_current_major = VMSTOR_WIN7_MAJOR; - vmstor_current_minor = VMSTOR_WIN7_MINOR; + if (vmbus_proto_version < VERSION_WIN8) { max_luns_per_target = STORVSC_IDE_MAX_LUNS_PER_TARGET; max_targets = STORVSC_IDE_MAX_TARGETS; max_channels = STORVSC_IDE_MAX_CHANNELS; - break; - default: - sense_buffer_size = POST_WIN7_STORVSC_SENSE_BUFFER_SIZE; - vmscsi_size_delta = 0; - vmstor_current_major = VMSTOR_WIN8_MAJOR; - vmstor_current_minor = VMSTOR_WIN8_MINOR; + } else { max_luns_per_target = STORVSC_MAX_LUNS_PER_TARGET; max_targets = STORVSC_MAX_TARGETS; max_channels = STORVSC_MAX_CHANNELS; @@ -1783,7 +1568,6 @@ static int storvsc_probe(struct hv_device *device, * VCPUs in the guest. */ max_sub_channels = (num_cpus / storvsc_vcpus_per_sub_channel); - break; } scsi_driver.can_queue = (max_outstanding_req_per_channel * diff --git a/kernel/drivers/scsi/sun3x_esp.c b/kernel/drivers/scsi/sun3x_esp.c index e26e81de7..d50c5ed8f 100644 --- a/kernel/drivers/scsi/sun3x_esp.c +++ b/kernel/drivers/scsi/sun3x_esp.c @@ -12,9 +12,9 @@ #include #include #include +#include #include -#include #include #include diff --git a/kernel/drivers/scsi/sym53c416.c b/kernel/drivers/scsi/sym53c416.c index 0b7819f3e..5bdcbe8fa 100644 --- a/kernel/drivers/scsi/sym53c416.c +++ b/kernel/drivers/scsi/sym53c416.c @@ -838,7 +838,6 @@ static struct scsi_host_template driver_template = { .can_queue = 1, .this_id = SYM53C416_SCSI_ID, .sg_tablesize = 32, - .cmd_per_lun = 1, .unchecked_isa_dma = 1, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/ufs/Kconfig b/kernel/drivers/scsi/ufs/Kconfig index 8a1f4b355..5f4530744 100644 --- a/kernel/drivers/scsi/ufs/Kconfig +++ b/kernel/drivers/scsi/ufs/Kconfig @@ -72,8 +72,8 @@ config SCSI_UFSHCD_PLATFORM If unsure, say N. config SCSI_UFS_QCOM - bool "QCOM specific hooks to UFS controller platform driver" - depends on SCSI_UFSHCD_PLATFORM && ARCH_MSM + tristate "QCOM specific hooks to UFS controller platform driver" + depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM select PHY_QCOM_UFS help This selects the QCOM specific additions to UFSHCD platform driver. diff --git a/kernel/drivers/scsi/ufs/ufs-qcom.c b/kernel/drivers/scsi/ufs/ufs-qcom.c index 6652a8171..4f38d008b 100644 --- a/kernel/drivers/scsi/ufs/ufs-qcom.c +++ b/kernel/drivers/scsi/ufs/ufs-qcom.c @@ -19,16 +19,44 @@ #include #include "ufshcd.h" +#include "ufshcd-pltfrm.h" #include "unipro.h" #include "ufs-qcom.h" #include "ufshci.h" +#define UFS_QCOM_DEFAULT_DBG_PRINT_EN \ + (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) + +enum { + TSTBUS_UAWM, + TSTBUS_UARM, + TSTBUS_TXUC, + TSTBUS_RXUC, + TSTBUS_DFC, + TSTBUS_TRLUT, + TSTBUS_TMRLUT, + TSTBUS_OCSC, + TSTBUS_UTP_HCI, + TSTBUS_COMBINED, + TSTBUS_WRAPPER, + TSTBUS_UNIPRO, + TSTBUS_MAX, +}; static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; -static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); -static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, - const char *speed_mode); static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); +static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); +static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, + u32 clk_cycles); + +static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len, + char *prefix) +{ + print_hex_dump(KERN_ERR, prefix, + len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE, + 16, 4, (void __force *)hba->mmio_base + offset, + len * 4, false); +} static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) { @@ -149,13 +177,14 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", &host->tx_l1_sync_clk); + out: return err; } static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; u32 tx_lanes; int err = 0; @@ -181,7 +210,9 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) do { err = ufshcd_dme_get(hba, - UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val); + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx_fsm_val); if (err || tx_fsm_val == TX_FSM_HIBERN8) break; @@ -195,7 +226,9 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) */ if (time_after(jiffies, timeout)) err = ufshcd_dme_get(hba, - UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val); + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx_fsm_val); if (err) { dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", @@ -209,9 +242,18 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) return err; } +static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) +{ + ufshcd_rmwl(host->hba, QUNIPRO_SEL, + ufs_qcom_cap_qunipro(host) ? QUNIPRO_SEL : 0, + REG_UFS_CFG1); + /* make sure above configuration is applied before we return */ + mb(); +} + static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; int ret = 0; bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) @@ -223,9 +265,11 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) usleep_range(1000, 1100); ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); + if (ret) { - dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret = %d\n", - __func__, ret); + dev_err(hba->dev, + "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n", + __func__, ret); goto out; } @@ -246,9 +290,12 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) ret = ufs_qcom_phy_is_pcs_ready(phy); if (ret) - dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", + dev_err(hba->dev, + "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", __func__, ret); + ufs_qcom_select_unipro_mode(host); + out: return ret; } @@ -271,9 +318,10 @@ static void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba) mb(); } -static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status) +static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); int err = 0; switch (status) { @@ -301,12 +349,13 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status) } /** - * Returns non-zero for success (which rate of core_clk) and 0 - * in case of a failure + * Returns zero for success and non-zero in case of a failure */ -static unsigned long -ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) +static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, + u32 hs, u32 rate, bool update_link_startup_timer) { + int ret = 0; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_clk_info *clki; u32 core_clk_period_in_ns; u32 tx_clk_cycles_per_us = 0; @@ -323,13 +372,25 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) static u32 hs_fr_table_rA[][2] = { {UFS_HS_G1, 0x1F}, {UFS_HS_G2, 0x3e}, + {UFS_HS_G3, 0x7D}, }; static u32 hs_fr_table_rB[][2] = { {UFS_HS_G1, 0x24}, {UFS_HS_G2, 0x49}, + {UFS_HS_G3, 0x92}, }; + /* + * The Qunipro controller does not use following registers: + * SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG & + * UFS_REG_PA_LINK_STARTUP_TIMER + * But UTP controller uses SYS1CLK_1US_REG register for Interrupt + * Aggregation logic. + */ + if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba)) + goto out; + if (gear == 0) { dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear); goto out_error; @@ -345,7 +406,17 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) core_clk_rate = DEFAULT_CLK_RATE_HZ; core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; - ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); + if (ufshcd_readl(hba, REG_UFS_SYS1CLK_1US) != core_clk_cycles_per_us) { + ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); + /* + * make sure above write gets applied before we return from + * this function. + */ + mb(); + } + + if (ufs_qcom_cap_qunipro(host)) + goto out; core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; @@ -395,35 +466,59 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) goto out_error; } - /* this register 2 fields shall be written at once */ - ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, - REG_UFS_TX_SYMBOL_CLK_NS_US); + if (ufshcd_readl(hba, REG_UFS_TX_SYMBOL_CLK_NS_US) != + (core_clk_period_in_ns | tx_clk_cycles_per_us)) { + /* this register 2 fields shall be written at once */ + ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, + REG_UFS_TX_SYMBOL_CLK_NS_US); + /* + * make sure above write gets applied before we return from + * this function. + */ + mb(); + } + + if (update_link_startup_timer) { + ufshcd_writel(hba, ((core_clk_rate / MSEC_PER_SEC) * 100), + REG_UFS_PA_LINK_STARTUP_TIMER); + /* + * make sure that this configuration is applied before + * we return + */ + mb(); + } goto out; out_error: - core_clk_rate = 0; + ret = -EINVAL; out: - return core_clk_rate; + return ret; } -static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status) +static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) { - unsigned long core_clk_rate = 0; - u32 core_clk_cycles_per_100ms; + int err = 0; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); switch (status) { case PRE_CHANGE: - core_clk_rate = ufs_qcom_cfg_timers(hba, UFS_PWM_G1, - SLOWAUTO_MODE, 0); - if (!core_clk_rate) { + if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE, + 0, true)) { dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", __func__); - return -EINVAL; + err = -EINVAL; + goto out; } - core_clk_cycles_per_100ms = - (core_clk_rate / MSEC_PER_SEC) * 100; - ufshcd_writel(hba, core_clk_cycles_per_100ms, - REG_UFS_PA_LINK_STARTUP_TIMER); + + if (ufs_qcom_cap_qunipro(host)) + /* + * set unipro core clock cycles to 150 & clear clock + * divider + */ + err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, + 150); + break; case POST_CHANGE: ufs_qcom_link_startup_post_change(hba); @@ -432,12 +527,13 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status) break; } - return 0; +out: + return err; } static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; int ret = 0; @@ -459,8 +555,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) * If UniPro link is not active, PHY ref_clk, main PHY analog power * rail and low noise analog power rail for PLL can be switched off. */ - if (!ufs_qcom_is_link_active(hba)) + if (!ufs_qcom_is_link_active(hba)) { + ufs_qcom_disable_lane_clks(host); phy_power_off(phy); + } out: return ret; @@ -468,7 +566,7 @@ out: static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; int err; @@ -479,6 +577,10 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) goto out; } + err = ufs_qcom_enable_lane_clks(host); + if (err) + goto out; + hba->is_sys_suspended = false; out: @@ -583,6 +685,81 @@ static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, return 0; } +#ifdef CONFIG_MSM_BUS_SCALING +static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, + const char *speed_mode) +{ + struct device *dev = host->hba->dev; + struct device_node *np = dev->of_node; + int err; + const char *key = "qcom,bus-vector-names"; + + if (!speed_mode) { + err = -EINVAL; + goto out; + } + + if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) + err = of_property_match_string(np, key, "MAX"); + else + err = of_property_match_string(np, key, speed_mode); + +out: + if (err < 0) + dev_err(dev, "%s: Invalid %s mode %d\n", + __func__, speed_mode, err); + return err; +} + +static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) +{ + int gear = max_t(u32, p->gear_rx, p->gear_tx); + int lanes = max_t(u32, p->lane_rx, p->lane_tx); + int pwr; + + /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ + if (!gear) + gear = 1; + + if (!lanes) + lanes = 1; + + if (!p->pwr_rx && !p->pwr_tx) { + pwr = SLOWAUTO_MODE; + snprintf(result, BUS_VECTOR_NAME_LEN, "MIN"); + } else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || + p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { + pwr = FAST_MODE; + snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", + p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); + } else { + pwr = SLOW_MODE; + snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", + "PWM", gear, lanes); + } +} + +static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) +{ + int err = 0; + + if (vote != host->bus_vote.curr_vote) { + err = msm_bus_scale_client_update_request( + host->bus_vote.client_handle, vote); + if (err) { + dev_err(host->hba->dev, + "%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", + __func__, host->bus_vote.client_handle, + vote, err); + goto out; + } + + host->bus_vote.curr_vote = vote; + } +out: + return err; +} + static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) { int vote; @@ -604,13 +781,137 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) return err; } +static ssize_t +show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + + return snprintf(buf, PAGE_SIZE, "%u\n", + host->bus_vote.is_max_bw_needed); +} + +static ssize_t +store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + uint32_t value; + + if (!kstrtou32(buf, 0, &value)) { + host->bus_vote.is_max_bw_needed = !!value; + ufs_qcom_update_bus_bw_vote(host); + } + + return count; +} + +static int ufs_qcom_bus_register(struct ufs_qcom_host *host) +{ + int err; + struct msm_bus_scale_pdata *bus_pdata; + struct device *dev = host->hba->dev; + struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; + + bus_pdata = msm_bus_cl_get_pdata(pdev); + if (!bus_pdata) { + dev_err(dev, "%s: failed to get bus vectors\n", __func__); + err = -ENODATA; + goto out; + } + + err = of_property_count_strings(np, "qcom,bus-vector-names"); + if (err < 0 || err != bus_pdata->num_usecases) { + dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", + __func__, err); + goto out; + } + + host->bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); + if (!host->bus_vote.client_handle) { + dev_err(dev, "%s: msm_bus_scale_register_client failed\n", + __func__); + err = -EFAULT; + goto out; + } + + /* cache the vote index for minimum and maximum bandwidth */ + host->bus_vote.min_bw_vote = ufs_qcom_get_bus_vote(host, "MIN"); + host->bus_vote.max_bw_vote = ufs_qcom_get_bus_vote(host, "MAX"); + + host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; + host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; + sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); + host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; + host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; + err = device_create_file(dev, &host->bus_vote.max_bus_bw); +out: + return err; +} +#else /* CONFIG_MSM_BUS_SCALING */ +static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) +{ + return 0; +} + +static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) +{ + return 0; +} + +static int ufs_qcom_bus_register(struct ufs_qcom_host *host) +{ + return 0; +} +#endif /* CONFIG_MSM_BUS_SCALING */ + +static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) +{ + if (host->dev_ref_clk_ctrl_mmio && + (enable ^ host->is_dev_ref_clk_enabled)) { + u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio); + + if (enable) + temp |= host->dev_ref_clk_en_mask; + else + temp &= ~host->dev_ref_clk_en_mask; + + /* + * If we are here to disable this clock it might be immediately + * after entering into hibern8 in which case we need to make + * sure that device ref_clk is active at least 1us after the + * hibern8 enter. + */ + if (!enable) + udelay(1); + + writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio); + + /* ensure that ref_clk is enabled/disabled before we return */ + wmb(); + + /* + * If we call hibern8 exit after this, we need to make sure that + * device ref_clk is stable for at least 1us before the hibern8 + * exit command. + */ + if (enable) + udelay(1); + + host->is_dev_ref_clk_enabled = enable; + } +} + static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, - bool status, + enum ufs_notify_change_status status, struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) { u32 val; - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; struct ufs_qcom_dev_params ufs_qcom_cap; int ret = 0; @@ -638,6 +939,20 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, ufs_qcom_cap.desired_working_mode = UFS_QCOM_LIMIT_DESIRED_MODE; + if (host->hw_ver.major == 0x1) { + /* + * HS-G3 operations may not reliably work on legacy QCOM + * UFS host controller hardware even though capability + * exchange during link startup phase may end up + * negotiating maximum supported gear as G3. + * Hence downgrade the maximum supported gear to HS-G2. + */ + if (ufs_qcom_cap.hs_tx_gear > UFS_HS_G2) + ufs_qcom_cap.hs_tx_gear = UFS_HS_G2; + if (ufs_qcom_cap.hs_rx_gear > UFS_HS_G2) + ufs_qcom_cap.hs_rx_gear = UFS_HS_G2; + } + ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, dev_max_params, dev_req_params); @@ -649,9 +964,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, break; case POST_CHANGE: - if (!ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, + if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, dev_req_params->pwr_rx, - dev_req_params->hs_rate)) { + dev_req_params->hs_rate, false)) { dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", __func__); /* @@ -683,6 +998,16 @@ out: return ret; } +static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + + if (host->hw_ver.major == 0x1) + return UFSHCI_VERSION_11; + else + return UFSHCI_VERSION_20; +} + /** * ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller quirks * @hba: host controller instance @@ -694,93 +1019,55 @@ out: */ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); - if (host->hw_ver.major == 0x1) - hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS; + if (host->hw_ver.major == 0x01) { + hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS + | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP + | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE; + + if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001) + hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; + + hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; + } if (host->hw_ver.major >= 0x2) { + hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; + if (!ufs_qcom_cap_qunipro(host)) /* Legacy UniPro mode still need following quirks */ - hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS; + hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS + | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE + | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP); } } static void ufs_qcom_set_caps(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; - - if (host->hw_ver.major >= 0x2) - host->caps = UFS_QCOM_CAP_QUNIPRO; -} - -static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, - const char *speed_mode) -{ - struct device *dev = host->hba->dev; - struct device_node *np = dev->of_node; - int err; - const char *key = "qcom,bus-vector-names"; - - if (!speed_mode) { - err = -EINVAL; - goto out; - } - - if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) - err = of_property_match_string(np, key, "MAX"); - else - err = of_property_match_string(np, key, speed_mode); - -out: - if (err < 0) - dev_err(dev, "%s: Invalid %s mode %d\n", - __func__, speed_mode, err); - return err; -} - -static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) -{ - int err = 0; - - if (vote != host->bus_vote.curr_vote) - host->bus_vote.curr_vote = vote; - - return err; -} - -static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) -{ - int gear = max_t(u32, p->gear_rx, p->gear_tx); - int lanes = max_t(u32, p->lane_rx, p->lane_tx); - int pwr; - - /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ - if (!gear) - gear = 1; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); - if (!lanes) - lanes = 1; + hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; + hba->caps |= UFSHCD_CAP_CLK_SCALING; + hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; - if (!p->pwr_rx && !p->pwr_tx) { - pwr = SLOWAUTO_MODE; - snprintf(result, BUS_VECTOR_NAME_LEN, "MIN"); - } else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || - p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { - pwr = FAST_MODE; - snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", - p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); - } else { - pwr = SLOW_MODE; - snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", - "PWM", gear, lanes); + if (host->hw_ver.major >= 0x2) { + host->caps = UFS_QCOM_CAP_QUNIPRO | + UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE; } } +/** + * ufs_qcom_setup_clocks - enables/disable clocks + * @hba: host controller instance + * @on: If true, enable clocks else disable them. + * + * Returns 0 on success, non-zero on failure. + */ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) { - struct ufs_qcom_host *host = hba->priv; - int err = 0; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + int err; int vote = 0; /* @@ -803,20 +1090,18 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) ufs_qcom_phy_disable_iface_clk(host->generic_phy); goto out; } - /* enable the device ref clock */ - ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy); vote = host->bus_vote.saved_vote; if (vote == host->bus_vote.min_bw_vote) ufs_qcom_update_bus_bw_vote(host); + } else { + /* M-PHY RMMI interface clocks can be turned off */ ufs_qcom_phy_disable_iface_clk(host->generic_phy); - if (!ufs_qcom_is_link_active(hba)) { - /* turn off UFS local PHY ref_clk */ - ufs_qcom_phy_disable_ref_clk(host->generic_phy); + if (!ufs_qcom_is_link_active(hba)) /* disable device ref_clk */ - ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy); - } + ufs_qcom_dev_ref_clk_ctrl(host, false); + vote = host->bus_vote.min_bw_vote; } @@ -829,68 +1114,17 @@ out: return err; } -static ssize_t -show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - struct ufs_qcom_host *host = hba->priv; - - return snprintf(buf, PAGE_SIZE, "%u\n", - host->bus_vote.is_max_bw_needed); -} - -static ssize_t -store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ufs_hba *hba = dev_get_drvdata(dev); - struct ufs_qcom_host *host = hba->priv; - uint32_t value; - - if (!kstrtou32(buf, 0, &value)) { - host->bus_vote.is_max_bw_needed = !!value; - ufs_qcom_update_bus_bw_vote(host); - } - - return count; -} - -static int ufs_qcom_bus_register(struct ufs_qcom_host *host) -{ - int err; - struct device *dev = host->hba->dev; - struct device_node *np = dev->of_node; - - err = of_property_count_strings(np, "qcom,bus-vector-names"); - if (err < 0 ) { - dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", - __func__, err); - goto out; - } - - /* cache the vote index for minimum and maximum bandwidth */ - host->bus_vote.min_bw_vote = ufs_qcom_get_bus_vote(host, "MIN"); - host->bus_vote.max_bw_vote = ufs_qcom_get_bus_vote(host, "MAX"); - - host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; - host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; - sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); - host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; - host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; - err = device_create_file(dev, &host->bus_vote.max_bus_bw); -out: - return err; -} - #define ANDROID_BOOT_DEV_MAX 30 static char android_boot_dev[ANDROID_BOOT_DEV_MAX]; -static int get_android_boot_dev(char *str) + +#ifndef MODULE +static int __init get_android_boot_dev(char *str) { strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX); return 1; } __setup("androidboot.bootdevice=", get_android_boot_dev); +#endif /** * ufs_qcom_init - bind phy with controller @@ -906,7 +1140,9 @@ static int ufs_qcom_init(struct ufs_hba *hba) { int err; struct device *dev = hba->dev; + struct platform_device *pdev = to_platform_device(dev); struct ufs_qcom_host *host; + struct resource *res; if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) return -ENODEV; @@ -918,9 +1154,15 @@ static int ufs_qcom_init(struct ufs_hba *hba) goto out; } + /* Make a two way bind between the qcom host and the hba */ host->hba = hba; - hba->priv = (void *)host; + ufshcd_set_variant(hba, host); + /* + * voting/devoting device ref_clk source is time consuming hence + * skip devoting it during aggressive clock gating. This clock + * will still be gated off during runtime suspend. + */ host->generic_phy = devm_phy_get(dev, "ufsphy"); if (IS_ERR(host->generic_phy)) { @@ -936,6 +1178,30 @@ static int ufs_qcom_init(struct ufs_hba *hba) ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, &host->hw_ver.minor, &host->hw_ver.step); + /* + * for newer controllers, device reference clock control bit has + * moved inside UFS controller register address space itself. + */ + if (host->hw_ver.major >= 0x02) { + host->dev_ref_clk_ctrl_mmio = hba->mmio_base + REG_UFS_CFG1; + host->dev_ref_clk_en_mask = BIT(26); + } else { + /* "dev_ref_clk_ctrl_mem" is optional resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + host->dev_ref_clk_ctrl_mmio = + devm_ioremap_resource(dev, res); + if (IS_ERR(host->dev_ref_clk_ctrl_mmio)) { + dev_warn(dev, + "%s: could not map dev_ref_clk_ctrl_mmio, err %ld\n", + __func__, + PTR_ERR(host->dev_ref_clk_ctrl_mmio)); + host->dev_ref_clk_ctrl_mmio = NULL; + } + host->dev_ref_clk_en_mask = BIT(5); + } + } + /* update phy revision information before calling phy_init() */ ufs_qcom_phy_save_controller_version(host->generic_phy, host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); @@ -952,14 +1218,20 @@ static int ufs_qcom_init(struct ufs_hba *hba) ufs_qcom_set_caps(hba); ufs_qcom_advertise_quirks(hba); - hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING; - hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; - ufs_qcom_setup_clocks(hba, true); if (hba->dev->id < MAX_UFS_QCOM_HOSTS) ufs_qcom_hosts[hba->dev->id] = host; + host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN; + ufs_qcom_get_default_testbus_cfg(host); + err = ufs_qcom_testbus_config(host); + if (err) { + dev_warn(dev, "%s: failed to configure the testbus %d\n", + __func__, err); + err = 0; + } + goto out; out_disable_phy: @@ -968,43 +1240,270 @@ out_unregister_bus: phy_exit(host->generic_phy); out_host_free: devm_kfree(dev, host); - hba->priv = NULL; + ufshcd_set_variant(hba, NULL); out: return err; } static void ufs_qcom_exit(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); ufs_qcom_disable_lane_clks(host); phy_power_off(host->generic_phy); } -static -void ufs_qcom_clk_scale_notify(struct ufs_hba *hba) +static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, + u32 clk_cycles) +{ + int err; + u32 core_clk_ctrl_reg; + + if (clk_cycles > DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK) + return -EINVAL; + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), + &core_clk_ctrl_reg); + if (err) + goto out; + + core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK; + core_clk_ctrl_reg |= clk_cycles; + + /* Clear CORE_CLK_DIV_EN */ + core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; + + err = ufshcd_dme_set(hba, + UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), + core_clk_ctrl_reg); +out: + return err; +} + +static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) +{ + /* nothing to do as of now */ + return 0; +} + +static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + + if (!ufs_qcom_cap_qunipro(host)) + return 0; + + /* set unipro core clock cycles to 150 and clear clock divider */ + return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150); +} + +static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba) { - struct ufs_qcom_host *host = hba->priv; + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + int err; + u32 core_clk_ctrl_reg; + + if (!ufs_qcom_cap_qunipro(host)) + return 0; + + err = ufshcd_dme_get(hba, + UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), + &core_clk_ctrl_reg); + + /* make sure CORE_CLK_DIV_EN is cleared */ + if (!err && + (core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) { + core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; + err = ufshcd_dme_set(hba, + UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), + core_clk_ctrl_reg); + } + + return err; +} + +static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); + + if (!ufs_qcom_cap_qunipro(host)) + return 0; + + /* set unipro core clock cycles to 75 and clear clock divider */ + return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 75); +} + +static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, + bool scale_up, enum ufs_notify_change_status status) +{ + struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; + int err = 0; - if (!dev_req_params) - return; + if (status == PRE_CHANGE) { + if (scale_up) + err = ufs_qcom_clk_scale_up_pre_change(hba); + else + err = ufs_qcom_clk_scale_down_pre_change(hba); + } else { + if (scale_up) + err = ufs_qcom_clk_scale_up_post_change(hba); + else + err = ufs_qcom_clk_scale_down_post_change(hba); + + if (err || !dev_req_params) + goto out; + + ufs_qcom_cfg_timers(hba, + dev_req_params->gear_rx, + dev_req_params->pwr_rx, + dev_req_params->hs_rate, + false); + ufs_qcom_update_bus_bw_vote(host); + } + +out: + return err; +} + +static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) +{ + /* provide a legal default configuration */ + host->testbus.select_major = TSTBUS_UAWM; + host->testbus.select_minor = 1; +} + +static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) +{ + if (host->testbus.select_major >= TSTBUS_MAX) { + dev_err(host->hba->dev, + "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", + __func__, host->testbus.select_major); + return false; + } + + /* + * Not performing check for each individual select_major + * mappings of select_minor, since there is no harm in + * configuring a non-existent select_minor + */ + if (host->testbus.select_minor > 0x1F) { + dev_err(host->hba->dev, + "%s: 0x%05X is not a legal testbus option\n", + __func__, host->testbus.select_minor); + return false; + } + + return true; +} + +int ufs_qcom_testbus_config(struct ufs_qcom_host *host) +{ + int reg; + int offset; + u32 mask = TEST_BUS_SUB_SEL_MASK; + + if (!host) + return -EINVAL; + + if (!ufs_qcom_testbus_cfg_is_ok(host)) + return -EPERM; + + switch (host->testbus.select_major) { + case TSTBUS_UAWM: + reg = UFS_TEST_BUS_CTRL_0; + offset = 24; + break; + case TSTBUS_UARM: + reg = UFS_TEST_BUS_CTRL_0; + offset = 16; + break; + case TSTBUS_TXUC: + reg = UFS_TEST_BUS_CTRL_0; + offset = 8; + break; + case TSTBUS_RXUC: + reg = UFS_TEST_BUS_CTRL_0; + offset = 0; + break; + case TSTBUS_DFC: + reg = UFS_TEST_BUS_CTRL_1; + offset = 24; + break; + case TSTBUS_TRLUT: + reg = UFS_TEST_BUS_CTRL_1; + offset = 16; + break; + case TSTBUS_TMRLUT: + reg = UFS_TEST_BUS_CTRL_1; + offset = 8; + break; + case TSTBUS_OCSC: + reg = UFS_TEST_BUS_CTRL_1; + offset = 0; + break; + case TSTBUS_WRAPPER: + reg = UFS_TEST_BUS_CTRL_2; + offset = 16; + break; + case TSTBUS_COMBINED: + reg = UFS_TEST_BUS_CTRL_2; + offset = 8; + break; + case TSTBUS_UTP_HCI: + reg = UFS_TEST_BUS_CTRL_2; + offset = 0; + break; + case TSTBUS_UNIPRO: + reg = UFS_UNIPRO_CFG; + offset = 1; + break; + /* + * No need for a default case, since + * ufs_qcom_testbus_cfg_is_ok() checks that the configuration + * is legal + */ + } + mask <<= offset; + + pm_runtime_get_sync(host->hba->dev); + ufshcd_hold(host->hba, false); + ufshcd_rmwl(host->hba, TEST_BUS_SEL, + (u32)host->testbus.select_major << 19, + REG_UFS_CFG1); + ufshcd_rmwl(host->hba, mask, + (u32)host->testbus.select_minor << offset, + reg); + ufshcd_release(host->hba); + pm_runtime_put_sync(host->hba->dev); - ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, - dev_req_params->pwr_rx, - dev_req_params->hs_rate); + return 0; +} + +static void ufs_qcom_testbus_read(struct ufs_hba *hba) +{ + ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS "); } +static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) +{ + ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16, + "HCI Vendor Specific Registers "); + + ufs_qcom_testbus_read(hba); +} /** * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations * * The variant operations configure the necessary controller and PHY * handshake during initialization. */ -static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { +static struct ufs_hba_variant_ops ufs_hba_qcom_vops = { .name = "qcom", .init = ufs_qcom_init, .exit = ufs_qcom_exit, + .get_ufs_hci_version = ufs_qcom_get_ufs_hci_version, .clk_scale_notify = ufs_qcom_clk_scale_notify, .setup_clocks = ufs_qcom_setup_clocks, .hce_enable_notify = ufs_qcom_hce_enable_notify, @@ -1012,5 +1511,66 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { .pwr_change_notify = ufs_qcom_pwr_change_notify, .suspend = ufs_qcom_suspend, .resume = ufs_qcom_resume, + .dbg_register_dump = ufs_qcom_dump_dbg_regs, }; -EXPORT_SYMBOL(ufs_hba_qcom_vops); + +/** + * ufs_qcom_probe - probe routine of the driver + * @pdev: pointer to Platform device handle + * + * Return zero for success and non-zero for failure + */ +static int ufs_qcom_probe(struct platform_device *pdev) +{ + int err; + struct device *dev = &pdev->dev; + + /* Perform generic probe */ + err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops); + if (err) + dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err); + + return err; +} + +/** + * ufs_qcom_remove - set driver_data of the device to NULL + * @pdev: pointer to platform device handle + * + * Always return 0 + */ +static int ufs_qcom_remove(struct platform_device *pdev) +{ + struct ufs_hba *hba = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&(pdev)->dev); + ufshcd_remove(hba); + return 0; +} + +static const struct of_device_id ufs_qcom_of_match[] = { + { .compatible = "qcom,ufshc"}, + {}, +}; + +static const struct dev_pm_ops ufs_qcom_pm_ops = { + .suspend = ufshcd_pltfrm_suspend, + .resume = ufshcd_pltfrm_resume, + .runtime_suspend = ufshcd_pltfrm_runtime_suspend, + .runtime_resume = ufshcd_pltfrm_runtime_resume, + .runtime_idle = ufshcd_pltfrm_runtime_idle, +}; + +static struct platform_driver ufs_qcom_pltform = { + .probe = ufs_qcom_probe, + .remove = ufs_qcom_remove, + .shutdown = ufshcd_pltfrm_shutdown, + .driver = { + .name = "ufshcd-qcom", + .pm = &ufs_qcom_pm_ops, + .of_match_table = of_match_ptr(ufs_qcom_of_match), + }, +}; +module_platform_driver(ufs_qcom_pltform); + +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/scsi/ufs/ufs-qcom.h b/kernel/drivers/scsi/ufs/ufs-qcom.h index db2c0a00e..36249b35f 100644 --- a/kernel/drivers/scsi/ufs/ufs-qcom.h +++ b/kernel/drivers/scsi/ufs/ufs-qcom.h @@ -35,8 +35,8 @@ #define UFS_QCOM_LIMIT_NUM_LANES_RX 2 #define UFS_QCOM_LIMIT_NUM_LANES_TX 2 -#define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G2 -#define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G2 +#define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G3 +#define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G3 #define UFS_QCOM_LIMIT_PWMGEAR_RX UFS_PWM_G4 #define UFS_QCOM_LIMIT_PWMGEAR_TX UFS_PWM_G4 #define UFS_QCOM_LIMIT_RX_PWR_PWM SLOW_MODE @@ -58,6 +58,21 @@ enum { REG_UFS_CFG2 = 0xE0, REG_UFS_HW_VERSION = 0xE4, + UFS_TEST_BUS = 0xE8, + UFS_TEST_BUS_CTRL_0 = 0xEC, + UFS_TEST_BUS_CTRL_1 = 0xF0, + UFS_TEST_BUS_CTRL_2 = 0xF4, + UFS_UNIPRO_CFG = 0xF8, + + /* + * QCOM UFS host controller vendor specific registers + * added in HW Version 3.0.0 + */ + UFS_AH8_CFG = 0xFC, +}; + +/* QCOM UFS host controller vendor specific debug registers */ +enum { UFS_DBG_RD_REG_UAWM = 0x100, UFS_DBG_RD_REG_UARM = 0x200, UFS_DBG_RD_REG_TXUC = 0x300, @@ -73,6 +88,14 @@ enum { UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, }; +#define UFS_CNTLR_2_x_x_VEN_REGS_OFFSET(x) (0x000 + x) +#define UFS_CNTLR_3_x_x_VEN_REGS_OFFSET(x) (0x400 + x) + +/* bit definitions for REG_UFS_CFG1 register */ +#define QUNIPRO_SEL UFS_BIT(0) +#define TEST_BUS_EN BIT(18) +#define TEST_BUS_SEL GENMASK(22, 19) + /* bit definitions for REG_UFS_CFG2 register */ #define UAWM_HW_CGC_EN (1 << 0) #define UARM_HW_CGC_EN (1 << 1) @@ -83,6 +106,9 @@ enum { #define TMRLUT_HW_CGC_EN (1 << 6) #define OCSC_HW_CGC_EN (1 << 7) +/* bit definition for UFS_UFS_TEST_BUS_CTRL_n */ +#define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */ + #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\ TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\ DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\ @@ -106,6 +132,21 @@ enum ufs_qcom_phy_init_type { UFS_PHY_INIT_CFG_RESTORE, }; +/* QCOM UFS debug print bit mask */ +#define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0) +#define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1) +#define UFS_QCOM_DBG_PRINT_TEST_BUS_EN BIT(2) + +#define UFS_QCOM_DBG_PRINT_ALL \ + (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \ + UFS_QCOM_DBG_PRINT_TEST_BUS_EN) + +/* QUniPro Vendor specific attributes */ +#define DME_VS_CORE_CLK_CTRL 0xD002 +/* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */ +#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8) +#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF + static inline void ufs_qcom_get_controller_revision(struct ufs_hba *hba, u8 *major, u16 *minor, u16 *step) @@ -157,8 +198,13 @@ struct ufs_hw_version { u16 minor; u8 major; }; -struct ufs_qcom_host { +struct ufs_qcom_testbus { + u8 select_major; + u8 select_minor; +}; + +struct ufs_qcom_host { /* * Set this capability if host controller supports the QUniPro mode * and if driver wants the Host controller to operate in QUniPro mode. @@ -166,6 +212,12 @@ struct ufs_qcom_host { * controller supports the QUniPro mode. */ #define UFS_QCOM_CAP_QUNIPRO UFS_BIT(0) + + /* + * Set this capability if host controller can retain the secure + * configuration even after UFS controller core power collapse. + */ + #define UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE UFS_BIT(1) u32 caps; struct phy *generic_phy; @@ -178,13 +230,23 @@ struct ufs_qcom_host { struct clk *tx_l1_sync_clk; bool is_lane_clks_enabled; + void __iomem *dev_ref_clk_ctrl_mmio; + bool is_dev_ref_clk_enabled; struct ufs_hw_version hw_ver; + + u32 dev_ref_clk_en_mask; + + /* Bitmask for enabling debug prints */ + u32 dbg_print_en; + struct ufs_qcom_testbus testbus; }; #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) +int ufs_qcom_testbus_config(struct ufs_qcom_host *host); + static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host) { if (host->caps & UFS_QCOM_CAP_QUNIPRO) diff --git a/kernel/drivers/scsi/ufs/ufshcd-pltfrm.c b/kernel/drivers/scsi/ufs/ufshcd-pltfrm.c index 7db9564f5..9714f2a8b 100644 --- a/kernel/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/kernel/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -38,20 +38,7 @@ #include #include "ufshcd.h" - -static const struct of_device_id ufs_of_match[]; -static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) -{ - if (dev->of_node) { - const struct of_device_id *match; - - match = of_match_node(ufs_of_match, dev->of_node); - if (match) - return (struct ufs_hba_variant_ops *)match->data; - } - - return NULL; -} +#include "ufshcd-pltfrm.h" static int ufshcd_parse_clock_info(struct ufs_hba *hba) { @@ -245,10 +232,11 @@ out: * Returns 0 if successful * Returns non-zero otherwise */ -static int ufshcd_pltfrm_suspend(struct device *dev) +int ufshcd_pltfrm_suspend(struct device *dev) { return ufshcd_system_suspend(dev_get_drvdata(dev)); } +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_suspend); /** * ufshcd_pltfrm_resume - resume power management function @@ -257,43 +245,47 @@ static int ufshcd_pltfrm_suspend(struct device *dev) * Returns 0 if successful * Returns non-zero otherwise */ -static int ufshcd_pltfrm_resume(struct device *dev) +int ufshcd_pltfrm_resume(struct device *dev) { return ufshcd_system_resume(dev_get_drvdata(dev)); } +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_resume); -static int ufshcd_pltfrm_runtime_suspend(struct device *dev) +int ufshcd_pltfrm_runtime_suspend(struct device *dev) { return ufshcd_runtime_suspend(dev_get_drvdata(dev)); } -static int ufshcd_pltfrm_runtime_resume(struct device *dev) +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_suspend); + +int ufshcd_pltfrm_runtime_resume(struct device *dev) { return ufshcd_runtime_resume(dev_get_drvdata(dev)); } -static int ufshcd_pltfrm_runtime_idle(struct device *dev) +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_resume); + +int ufshcd_pltfrm_runtime_idle(struct device *dev) { return ufshcd_runtime_idle(dev_get_drvdata(dev)); } -#else /* !CONFIG_PM */ -#define ufshcd_pltfrm_suspend NULL -#define ufshcd_pltfrm_resume NULL -#define ufshcd_pltfrm_runtime_suspend NULL -#define ufshcd_pltfrm_runtime_resume NULL -#define ufshcd_pltfrm_runtime_idle NULL +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_idle); + #endif /* CONFIG_PM */ -static void ufshcd_pltfrm_shutdown(struct platform_device *pdev) +void ufshcd_pltfrm_shutdown(struct platform_device *pdev) { ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); } +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown); /** - * ufshcd_pltfrm_probe - probe routine of the driver + * ufshcd_pltfrm_init - probe routine of the driver * @pdev: pointer to Platform device handle + * @vops: pointer to variant ops * * Returns 0 on success, non-zero value on failure */ -static int ufshcd_pltfrm_probe(struct platform_device *pdev) +int ufshcd_pltfrm_init(struct platform_device *pdev, + struct ufs_hba_variant_ops *vops) { struct ufs_hba *hba; void __iomem *mmio_base; @@ -321,19 +313,19 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) goto out; } - hba->vops = get_variant_ops(&pdev->dev); + hba->vops = vops; err = ufshcd_parse_clock_info(hba); if (err) { dev_err(&pdev->dev, "%s: clock parse failed %d\n", __func__, err); - goto out; + goto dealloc_host; } err = ufshcd_parse_regulator_info(hba); if (err) { dev_err(&pdev->dev, "%s: regulator init failed %d\n", __func__, err); - goto out; + goto dealloc_host; } pm_runtime_set_active(&pdev->dev); @@ -352,50 +344,12 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) out_disable_rpm: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); +dealloc_host: + ufshcd_dealloc_host(hba); out: return err; } - -/** - * ufshcd_pltfrm_remove - remove platform driver routine - * @pdev: pointer to platform device handle - * - * Returns 0 on success, non-zero value on failure - */ -static int ufshcd_pltfrm_remove(struct platform_device *pdev) -{ - struct ufs_hba *hba = platform_get_drvdata(pdev); - - pm_runtime_get_sync(&(pdev)->dev); - ufshcd_remove(hba); - return 0; -} - -static const struct of_device_id ufs_of_match[] = { - { .compatible = "jedec,ufs-1.1"}, - {}, -}; - -static const struct dev_pm_ops ufshcd_dev_pm_ops = { - .suspend = ufshcd_pltfrm_suspend, - .resume = ufshcd_pltfrm_resume, - .runtime_suspend = ufshcd_pltfrm_runtime_suspend, - .runtime_resume = ufshcd_pltfrm_runtime_resume, - .runtime_idle = ufshcd_pltfrm_runtime_idle, -}; - -static struct platform_driver ufshcd_pltfrm_driver = { - .probe = ufshcd_pltfrm_probe, - .remove = ufshcd_pltfrm_remove, - .shutdown = ufshcd_pltfrm_shutdown, - .driver = { - .name = "ufshcd", - .pm = &ufshcd_dev_pm_ops, - .of_match_table = ufs_of_match, - }, -}; - -module_platform_driver(ufshcd_pltfrm_driver); +EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init); MODULE_AUTHOR("Santosh Yaragnavi "); MODULE_AUTHOR("Vinayak Holikatti "); diff --git a/kernel/drivers/scsi/ufs/ufshcd-pltfrm.h b/kernel/drivers/scsi/ufs/ufshcd-pltfrm.h new file mode 100644 index 000000000..df64c4180 --- /dev/null +++ b/kernel/drivers/scsi/ufs/ufshcd-pltfrm.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef UFSHCD_PLTFRM_H_ +#define UFSHCD_PLTFRM_H_ + +#include "ufshcd.h" + +int ufshcd_pltfrm_init(struct platform_device *pdev, + struct ufs_hba_variant_ops *vops); +void ufshcd_pltfrm_shutdown(struct platform_device *pdev); + +#ifdef CONFIG_PM + +int ufshcd_pltfrm_suspend(struct device *dev); +int ufshcd_pltfrm_resume(struct device *dev); +int ufshcd_pltfrm_runtime_suspend(struct device *dev); +int ufshcd_pltfrm_runtime_resume(struct device *dev); +int ufshcd_pltfrm_runtime_idle(struct device *dev); + +#else /* !CONFIG_PM */ + +#define ufshcd_pltfrm_suspend NULL +#define ufshcd_pltfrm_resume NULL +#define ufshcd_pltfrm_runtime_suspend NULL +#define ufshcd_pltfrm_runtime_resume NULL +#define ufshcd_pltfrm_runtime_idle NULL + +#endif /* CONFIG_PM */ + +#endif /* UFSHCD_PLTFRM_H_ */ diff --git a/kernel/drivers/scsi/ufs/ufshcd.c b/kernel/drivers/scsi/ufs/ufshcd.c index 648a44675..85cd2564c 100644 --- a/kernel/drivers/scsi/ufs/ufshcd.c +++ b/kernel/drivers/scsi/ufs/ufshcd.c @@ -188,6 +188,8 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba); static irqreturn_t ufshcd_intr(int irq, void *__hba); static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); +static int ufshcd_change_power_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *pwr_mode); static inline int ufshcd_enable_irq(struct ufs_hba *hba) { @@ -269,6 +271,9 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba) */ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) { + if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION) + return ufshcd_vops_get_ufs_hci_version(hba); + return ufshcd_readl(hba, REG_UFS_VERSION); } @@ -480,6 +485,15 @@ ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout) REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL); } +/** + * ufshcd_disable_intr_aggr - Disables interrupt aggregation. + * @hba: per adapter instance + */ +static inline void ufshcd_disable_intr_aggr(struct ufs_hba *hba) +{ + ufshcd_writel(hba, 0, REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL); +} + /** * ufshcd_enable_run_stop_reg - Enable run-stop registers, * When run-stop registers are set to 1, it indicates the @@ -611,6 +625,7 @@ start: out: return rc; } +EXPORT_SYMBOL_GPL(ufshcd_hold); static void ufshcd_gate_work(struct work_struct *work) { @@ -698,6 +713,7 @@ void ufshcd_release(struct ufs_hba *hba) __ufshcd_release(hba); spin_unlock_irqrestore(hba->host->host_lock, flags); } +EXPORT_SYMBOL_GPL(ufshcd_release); static ssize_t ufshcd_clkgate_delay_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1326,7 +1342,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) lrbp->sense_buffer = cmd->sense_buffer; lrbp->task_tag = tag; lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun); - lrbp->intr_cmd = false; + lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba) ? true : false; lrbp->command_type = UTP_CMD_TYPE_SCSI; /* form UPIU before issuing the command */ @@ -2147,6 +2163,31 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, }; const char *get = action[!!peer]; int ret; + struct ufs_pa_layer_attr orig_pwr_info; + struct ufs_pa_layer_attr temp_pwr_info; + bool pwr_mode_change = false; + + if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)) { + orig_pwr_info = hba->pwr_info; + temp_pwr_info = orig_pwr_info; + + if (orig_pwr_info.pwr_tx == FAST_MODE || + orig_pwr_info.pwr_rx == FAST_MODE) { + temp_pwr_info.pwr_tx = FASTAUTO_MODE; + temp_pwr_info.pwr_rx = FASTAUTO_MODE; + pwr_mode_change = true; + } else if (orig_pwr_info.pwr_tx == SLOW_MODE || + orig_pwr_info.pwr_rx == SLOW_MODE) { + temp_pwr_info.pwr_tx = SLOWAUTO_MODE; + temp_pwr_info.pwr_rx = SLOWAUTO_MODE; + pwr_mode_change = true; + } + if (pwr_mode_change) { + ret = ufshcd_change_power_mode(hba, &temp_pwr_info); + if (ret) + goto out; + } + } uic_cmd.command = peer ? UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET; @@ -2161,6 +2202,10 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, if (mib_val) *mib_val = uic_cmd.argument3; + + if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE) + && pwr_mode_change) + ufshcd_change_power_mode(hba, &orig_pwr_info); out: return ret; } @@ -2249,6 +2294,16 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) struct uic_command uic_cmd = {0}; int ret; + if (hba->quirks & UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP) { + ret = ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(PA_RXHSUNTERMCAP, 0), 1); + if (ret) { + dev_err(hba->dev, "%s: failed to enable PA_RXHSUNTERMCAP ret %d\n", + __func__, ret); + goto out; + } + } + uic_cmd.command = UIC_CMD_DME_SET; uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE); uic_cmd.argument3 = mode; @@ -2256,6 +2311,7 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd); ufshcd_release(hba); +out: return ret; } @@ -2417,9 +2473,8 @@ static int ufshcd_change_power_mode(struct ufs_hba *hba, dev_err(hba->dev, "%s: power mode change failed %d\n", __func__, ret); } else { - if (hba->vops && hba->vops->pwr_change_notify) - hba->vops->pwr_change_notify(hba, - POST_CHANGE, NULL, pwr_mode); + ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL, + pwr_mode); memcpy(&hba->pwr_info, pwr_mode, sizeof(struct ufs_pa_layer_attr)); @@ -2439,10 +2494,10 @@ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr final_params = { 0 }; int ret; - if (hba->vops && hba->vops->pwr_change_notify) - hba->vops->pwr_change_notify(hba, - PRE_CHANGE, desired_pwr_mode, &final_params); - else + ret = ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE, + desired_pwr_mode, &final_params); + + if (ret) memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); ret = ufshcd_change_power_mode(hba, &final_params); @@ -2522,7 +2577,10 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); /* Configure interrupt aggregation */ - ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO); + if (ufshcd_is_intr_aggr_allowed(hba)) + ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO); + else + ufshcd_disable_intr_aggr(hba); /* Configure UTRL and UTMRL base address registers */ ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr), @@ -2588,8 +2646,7 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) /* UniPro link is disabled at this point */ ufshcd_set_link_off(hba); - if (hba->vops && hba->vops->hce_enable_notify) - hba->vops->hce_enable_notify(hba, PRE_CHANGE); + ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE); /* start controller initialization sequence */ ufshcd_hba_start(hba); @@ -2622,12 +2679,47 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) /* enable UIC related interrupts */ ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); - if (hba->vops && hba->vops->hce_enable_notify) - hba->vops->hce_enable_notify(hba, POST_CHANGE); + ufshcd_vops_hce_enable_notify(hba, POST_CHANGE); return 0; } +static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) +{ + int tx_lanes, i, err = 0; + + if (!peer) + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), + &tx_lanes); + else + ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), + &tx_lanes); + for (i = 0; i < tx_lanes; i++) { + if (!peer) + err = ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), + 0); + else + err = ufshcd_dme_peer_set(hba, + UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), + 0); + if (err) { + dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d", + __func__, peer, i, err); + break; + } + } + + return err; +} + +static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba) +{ + return ufshcd_disable_tx_lcc(hba, true); +} + /** * ufshcd_link_startup - Initialize unipro link startup * @hba: per adapter instance @@ -2640,8 +2732,7 @@ static int ufshcd_link_startup(struct ufs_hba *hba) int retries = DME_LINKSTARTUP_RETRIES; do { - if (hba->vops && hba->vops->link_startup_notify) - hba->vops->link_startup_notify(hba, PRE_CHANGE); + ufshcd_vops_link_startup_notify(hba, PRE_CHANGE); ret = ufshcd_dme_link_startup(hba); @@ -2665,13 +2756,17 @@ static int ufshcd_link_startup(struct ufs_hba *hba) /* failed to get the link up... retire */ goto out; - /* Include any host controller configuration via UIC commands */ - if (hba->vops && hba->vops->link_startup_notify) { - ret = hba->vops->link_startup_notify(hba, POST_CHANGE); + if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) { + ret = ufshcd_disable_device_tx_lcc(hba); if (ret) goto out; } + /* Include any host controller configuration via UIC commands */ + ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE); + if (ret) + goto out; + ret = ufshcd_make_hba_operational(hba); out: if (ret) @@ -3073,7 +3168,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) * false interrupt if device completes another request after resetting * aggregation and before reading the DB. */ - ufshcd_reset_intr_aggr(hba); + if (ufshcd_is_intr_aggr_allowed(hba)) + ufshcd_reset_intr_aggr(hba); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); completed_reqs = tr_doorbell ^ hba->outstanding_reqs; @@ -4253,7 +4349,6 @@ static struct scsi_host_template ufshcd_driver_template = { .cmd_per_lun = UFSHCD_CMD_PER_LUN, .can_queue = UFSHCD_CAN_QUEUE, .max_host_blocked = 1, - .use_blk_tags = 1, .track_queue_depth = 1, }; @@ -4476,8 +4571,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, } } - if (hba->vops && hba->vops->setup_clocks) - ret = hba->vops->setup_clocks(hba, on); + ret = ufshcd_vops_setup_clocks(hba, on); out: if (ret) { list_for_each_entry(clki, head, list) { @@ -4543,27 +4637,22 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba) if (!hba->vops) goto out; - if (hba->vops->init) { - err = hba->vops->init(hba); - if (err) - goto out; - } + err = ufshcd_vops_init(hba); + if (err) + goto out; - if (hba->vops->setup_regulators) { - err = hba->vops->setup_regulators(hba, true); - if (err) - goto out_exit; - } + err = ufshcd_vops_setup_regulators(hba, true); + if (err) + goto out_exit; goto out; out_exit: - if (hba->vops->exit) - hba->vops->exit(hba); + ufshcd_vops_exit(hba); out: if (err) dev_err(hba->dev, "%s: variant %s init failed err %d\n", - __func__, hba->vops ? hba->vops->name : "", err); + __func__, ufshcd_get_var_name(hba), err); return err; } @@ -4572,14 +4661,11 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba) if (!hba->vops) return; - if (hba->vops->setup_clocks) - hba->vops->setup_clocks(hba, false); + ufshcd_vops_setup_clocks(hba, false); - if (hba->vops->setup_regulators) - hba->vops->setup_regulators(hba, false); + ufshcd_vops_setup_regulators(hba, false); - if (hba->vops->exit) - hba->vops->exit(hba); + ufshcd_vops_exit(hba); } static int ufshcd_hba_init(struct ufs_hba *hba) @@ -4956,17 +5042,13 @@ disable_clks: * vendor specific host controller register space call them before the * host clocks are ON. */ - if (hba->vops && hba->vops->suspend) { - ret = hba->vops->suspend(hba, pm_op); - if (ret) - goto set_link_active; - } + ret = ufshcd_vops_suspend(hba, pm_op); + if (ret) + goto set_link_active; - if (hba->vops && hba->vops->setup_clocks) { - ret = hba->vops->setup_clocks(hba, false); - if (ret) - goto vops_resume; - } + ret = ufshcd_vops_setup_clocks(hba, false); + if (ret) + goto vops_resume; if (!ufshcd_is_link_active(hba)) ufshcd_setup_clocks(hba, false); @@ -4977,7 +5059,7 @@ disable_clks: hba->clk_gating.state = CLKS_OFF; /* * Disable the host irq as host controller as there won't be any - * host controller trasanction expected till resume. + * host controller transaction expected till resume. */ ufshcd_disable_irq(hba); /* Put the host controller in low power mode if possible */ @@ -4985,8 +5067,7 @@ disable_clks: goto out; vops_resume: - if (hba->vops && hba->vops->resume) - hba->vops->resume(hba, pm_op); + ufshcd_vops_resume(hba, pm_op); set_link_active: ufshcd_vreg_set_hpm(hba); if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) @@ -5042,11 +5123,9 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) * vendor specific host controller register space call them when the * host clocks are ON. */ - if (hba->vops && hba->vops->resume) { - ret = hba->vops->resume(hba, pm_op); - if (ret) - goto disable_vreg; - } + ret = ufshcd_vops_resume(hba, pm_op); + if (ret) + goto disable_vreg; if (ufshcd_is_link_hibern8(hba)) { ret = ufshcd_uic_hibern8_exit(hba); @@ -5087,8 +5166,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) set_old_link_state: ufshcd_link_state_transition(hba, old_link_state, 0); vendor_suspend: - if (hba->vops && hba->vops->suspend) - hba->vops->suspend(hba, pm_op); + ufshcd_vops_suspend(hba, pm_op); disable_vreg: ufshcd_vreg_set_lpm(hba); disable_irq_and_vops_clks: @@ -5270,6 +5348,16 @@ void ufshcd_remove(struct ufs_hba *hba) } EXPORT_SYMBOL_GPL(ufshcd_remove); +/** + * ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA) + * @hba: pointer to Host Bus Adapter (HBA) + */ +void ufshcd_dealloc_host(struct ufs_hba *hba) +{ + scsi_host_put(hba->host); +} +EXPORT_SYMBOL_GPL(ufshcd_dealloc_host); + /** * ufshcd_set_dma_mask - Set dma mask based on the controller * addressing capability @@ -5331,6 +5419,10 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) if (!head || list_empty(head)) goto out; + ret = ufshcd_vops_clk_scale_notify(hba, scale_up, PRE_CHANGE); + if (ret) + return ret; + list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk)) { if (scale_up && clki->max_freq) { @@ -5361,8 +5453,9 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__, clki->name, clk_get_rate(clki->clk)); } - if (hba->vops->clk_scale_notify) - hba->vops->clk_scale_notify(hba); + + ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE); + out: return ret; } @@ -5517,13 +5610,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) hba->is_irq_enabled = true; } - /* Enable SCSI tag mapping */ - err = scsi_init_shared_tag_map(host, host->can_queue); - if (err) { - dev_err(hba->dev, "init shared queue failed\n"); - goto exit_gating; - } - err = scsi_add_host(host, hba->dev); if (err) { dev_err(hba->dev, "scsi_add_host failed\n"); diff --git a/kernel/drivers/scsi/ufs/ufshcd.h b/kernel/drivers/scsi/ufs/ufshcd.h index b47ff0769..2570d9477 100644 --- a/kernel/drivers/scsi/ufs/ufshcd.h +++ b/kernel/drivers/scsi/ufs/ufshcd.h @@ -223,8 +223,10 @@ struct ufs_clk_info { bool enabled; }; -#define PRE_CHANGE 0 -#define POST_CHANGE 1 +enum ufs_notify_change_status { + PRE_CHANGE, + POST_CHANGE, +}; struct ufs_pa_layer_attr { u32 gear_rx; @@ -246,6 +248,7 @@ struct ufs_pwr_mode_info { * @name: variant name * @init: called when the driver is initialized * @exit: called to cleanup everything done in init + * @get_ufs_hci_version: called to get UFS HCI version * @clk_scale_notify: notifies that clks are scaled up/down * @setup_clocks: called before touching any of the controller registers * @setup_regulators: called before accessing the host controller @@ -258,21 +261,28 @@ struct ufs_pwr_mode_info { * to be set. * @suspend: called during host controller PM callback * @resume: called during host controller PM callback + * @dbg_register_dump: used to dump controller debug information */ struct ufs_hba_variant_ops { const char *name; int (*init)(struct ufs_hba *); void (*exit)(struct ufs_hba *); - void (*clk_scale_notify)(struct ufs_hba *); - int (*setup_clocks)(struct ufs_hba *, bool); + u32 (*get_ufs_hci_version)(struct ufs_hba *); + int (*clk_scale_notify)(struct ufs_hba *, bool, + enum ufs_notify_change_status); + int (*setup_clocks)(struct ufs_hba *, bool); int (*setup_regulators)(struct ufs_hba *, bool); - int (*hce_enable_notify)(struct ufs_hba *, bool); - int (*link_startup_notify)(struct ufs_hba *, bool); + int (*hce_enable_notify)(struct ufs_hba *, + enum ufs_notify_change_status); + int (*link_startup_notify)(struct ufs_hba *, + enum ufs_notify_change_status); int (*pwr_change_notify)(struct ufs_hba *, - bool, struct ufs_pa_layer_attr *, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *, struct ufs_pa_layer_attr *); int (*suspend)(struct ufs_hba *, enum ufs_pm_op); int (*resume)(struct ufs_hba *, enum ufs_pm_op); + void (*dbg_register_dump)(struct ufs_hba *hba); }; /* clock gating state */ @@ -417,11 +427,45 @@ struct ufs_hba { unsigned int irq; bool is_irq_enabled; + /* Interrupt aggregation support is broken */ + #define UFSHCD_QUIRK_BROKEN_INTR_AGGR UFS_BIT(0) + /* * delay before each dme command is required as the unipro * layer has shown instabilities */ - #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(0) + #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(1) + + /* + * If UFS host controller is having issue in processing LCC (Line + * Control Command) coming from device then enable this quirk. + * When this quirk is enabled, host controller driver should disable + * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE + * attribute of device to 0). + */ + #define UFSHCD_QUIRK_BROKEN_LCC UFS_BIT(2) + + /* + * The attribute PA_RXHSUNTERMCAP specifies whether or not the + * inbound Link supports unterminated line in HS mode. Setting this + * attribute to 1 fixes moving to HS gear. + */ + #define UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP UFS_BIT(3) + + /* + * This quirk needs to be enabled if the host contoller only allows + * accessing the peer dme attributes in AUTO mode (FAST AUTO or + * SLOW AUTO). + */ + #define UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE UFS_BIT(4) + + /* + * This quirk needs to be enabled if the host contoller doesn't + * advertise the correct version in UFS_VER register. If this quirk + * is enabled, standard UFS host driver will call the vendor specific + * ops (get_ufs_hci_version) to get the correct version. + */ + #define UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION UFS_BIT(5) unsigned int quirks; /* Deviations from standard UFSHCI spec. */ @@ -478,6 +522,12 @@ struct ufs_hba { #define UFSHCD_CAP_CLK_SCALING (1 << 2) /* Allow auto bkops to enabled during runtime suspend */ #define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3) + /* + * This capability allows host controller driver to use the UFS HCI's + * interrupt aggregation capability. + * CAUTION: Enabling this might reduce overall UFS throughput. + */ +#define UFSHCD_CAP_INTR_AGGR (1 << 4) struct devfreq *devfreq; struct ufs_clk_scaling clk_scaling; @@ -502,6 +552,15 @@ static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba) return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND; } +static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba) +{ + if ((hba->caps & UFSHCD_CAP_INTR_AGGR) && + !(hba->quirks & UFSHCD_QUIRK_BROKEN_INTR_AGGR)) + return true; + else + return false; +} + #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) #define ufshcd_readl(hba, reg) \ @@ -525,6 +584,7 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg) } int ufshcd_alloc_host(struct device *, struct ufs_hba **); +void ufshcd_dealloc_host(struct ufs_hba *); int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int); void ufshcd_remove(struct ufs_hba *); @@ -543,6 +603,27 @@ static inline void check_upiu_size(void) GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); } +/** + * ufshcd_set_variant - set variant specific data to the hba + * @hba - per adapter instance + * @variant - pointer to variant specific data + */ +static inline void ufshcd_set_variant(struct ufs_hba *hba, void *variant) +{ + BUG_ON(!hba); + hba->priv = variant; +} + +/** + * ufshcd_get_variant - get variant specific data from the hba + * @hba - per adapter instance + */ +static inline void *ufshcd_get_variant(struct ufs_hba *hba) +{ + BUG_ON(!hba); + return hba->priv; +} + extern int ufshcd_runtime_suspend(struct ufs_hba *hba); extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba); @@ -602,4 +683,109 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); + +/* Wrapper functions for safely calling variant operations */ +static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) +{ + if (hba->vops) + return hba->vops->name; + return ""; +} + +static inline int ufshcd_vops_init(struct ufs_hba *hba) +{ + if (hba->vops && hba->vops->init) + return hba->vops->init(hba); + + return 0; +} + +static inline void ufshcd_vops_exit(struct ufs_hba *hba) +{ + if (hba->vops && hba->vops->exit) + return hba->vops->exit(hba); +} + +static inline u32 ufshcd_vops_get_ufs_hci_version(struct ufs_hba *hba) +{ + if (hba->vops && hba->vops->get_ufs_hci_version) + return hba->vops->get_ufs_hci_version(hba); + + return ufshcd_readl(hba, REG_UFS_VERSION); +} + +static inline int ufshcd_vops_clk_scale_notify(struct ufs_hba *hba, + bool up, enum ufs_notify_change_status status) +{ + if (hba->vops && hba->vops->clk_scale_notify) + return hba->vops->clk_scale_notify(hba, up, status); + return 0; +} + +static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on) +{ + if (hba->vops && hba->vops->setup_clocks) + return hba->vops->setup_clocks(hba, on); + return 0; +} + +static inline int ufshcd_vops_setup_regulators(struct ufs_hba *hba, bool status) +{ + if (hba->vops && hba->vops->setup_regulators) + return hba->vops->setup_regulators(hba, status); + + return 0; +} + +static inline int ufshcd_vops_hce_enable_notify(struct ufs_hba *hba, + bool status) +{ + if (hba->vops && hba->vops->hce_enable_notify) + return hba->vops->hce_enable_notify(hba, status); + + return 0; +} +static inline int ufshcd_vops_link_startup_notify(struct ufs_hba *hba, + bool status) +{ + if (hba->vops && hba->vops->link_startup_notify) + return hba->vops->link_startup_notify(hba, status); + + return 0; +} + +static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba, + bool status, + struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + if (hba->vops && hba->vops->pwr_change_notify) + return hba->vops->pwr_change_notify(hba, status, + dev_max_params, dev_req_params); + + return -ENOTSUPP; +} + +static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op) +{ + if (hba->vops && hba->vops->suspend) + return hba->vops->suspend(hba, op); + + return 0; +} + +static inline int ufshcd_vops_resume(struct ufs_hba *hba, enum ufs_pm_op op) +{ + if (hba->vops && hba->vops->resume) + return hba->vops->resume(hba, op); + + return 0; +} + +static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba) +{ + if (hba->vops && hba->vops->dbg_register_dump) + hba->vops->dbg_register_dump(hba); +} + #endif /* End of Header */ diff --git a/kernel/drivers/scsi/ufs/ufshci.h b/kernel/drivers/scsi/ufs/ufshci.h index d5721199e..0ae0967aa 100644 --- a/kernel/drivers/scsi/ufs/ufshci.h +++ b/kernel/drivers/scsi/ufs/ufshci.h @@ -89,8 +89,9 @@ enum { /* Controller UFSHCI version */ enum { - UFSHCI_VERSION_10 = 0x00010000, - UFSHCI_VERSION_11 = 0x00010100, + UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */ + UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ + UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ }; /* @@ -206,6 +207,9 @@ enum { #define CONFIG_RESULT_CODE_MASK 0xFF #define GENERIC_ERROR_CODE_MASK 0xFF +/* GenSelectorIndex calculation macros for M-PHY attributes */ +#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) + #define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ ((sel) & 0xFFFF)) #define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) diff --git a/kernel/drivers/scsi/ufs/unipro.h b/kernel/drivers/scsi/ufs/unipro.h index 3fc3e21b7..816a8a46e 100644 --- a/kernel/drivers/scsi/ufs/unipro.h +++ b/kernel/drivers/scsi/ufs/unipro.h @@ -198,6 +198,14 @@ enum ufs_hs_gear_tag { #define T_TC0TXMAXSDUSIZE 0x4060 #define T_TC1TXMAXSDUSIZE 0x4061 +#ifdef FALSE +#undef FALSE +#endif + +#ifdef TRUE +#undef TRUE +#endif + /* Boolean attribute values */ enum { FALSE = 0, diff --git a/kernel/drivers/scsi/virtio_scsi.c b/kernel/drivers/scsi/virtio_scsi.c index f164f24a4..7dbbb29d2 100644 --- a/kernel/drivers/scsi/virtio_scsi.c +++ b/kernel/drivers/scsi/virtio_scsi.c @@ -501,6 +501,7 @@ static void virtio_scsi_init_hdr(struct virtio_device *vdev, cmd->crn = 0; } +#ifdef CONFIG_BLK_DEV_INTEGRITY static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev, struct virtio_scsi_cmd_req_pi *cmd_pi, struct scsi_cmnd *sc) @@ -524,6 +525,7 @@ static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev, blk_rq_sectors(rq) * bi->tuple_size); } +#endif static int virtscsi_queuecommand(struct virtio_scsi *vscsi, struct virtio_scsi_vq *req_vq, @@ -546,11 +548,14 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi, BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE); +#ifdef CONFIG_BLK_DEV_INTEGRITY if (virtio_has_feature(vscsi->vdev, VIRTIO_SCSI_F_T10_PI)) { virtio_scsi_init_hdr_pi(vscsi->vdev, &cmd->req.cmd_pi, sc); memcpy(cmd->req.cmd_pi.cdb, sc->cmnd, sc->cmd_len); req_size = sizeof(cmd->req.cmd_pi); - } else { + } else +#endif + { virtio_scsi_init_hdr(vscsi->vdev, &cmd->req.cmd, sc); memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len); req_size = sizeof(cmd->req.cmd); @@ -944,7 +949,7 @@ static int virtscsi_probe(struct virtio_device *vdev) { struct Scsi_Host *shost; struct virtio_scsi *vscsi; - int err, host_prot; + int err; u32 sg_elems, num_targets; u32 cmd_per_lun; u32 num_queues; @@ -1002,7 +1007,10 @@ static int virtscsi_probe(struct virtio_device *vdev) shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; shost->nr_hw_queues = num_queues; +#ifdef CONFIG_BLK_DEV_INTEGRITY if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) { + int host_prot; + host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION; @@ -1010,6 +1018,7 @@ static int virtscsi_probe(struct virtio_device *vdev) scsi_host_set_prot(shost, host_prot); scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC); } +#endif err = scsi_add_host(shost, &vdev->dev); if (err) @@ -1090,7 +1099,9 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_SCSI_F_HOTPLUG, VIRTIO_SCSI_F_CHANGE, +#ifdef CONFIG_BLK_DEV_INTEGRITY VIRTIO_SCSI_F_T10_PI, +#endif }; static struct virtio_driver virtio_scsi_driver = { diff --git a/kernel/drivers/scsi/wd719x.c b/kernel/drivers/scsi/wd719x.c index 289ad016d..e3da1a2fd 100644 --- a/kernel/drivers/scsi/wd719x.c +++ b/kernel/drivers/scsi/wd719x.c @@ -590,7 +590,7 @@ static inline void wd719x_interrupt_SCB(struct wd719x *wd, dev_dbg(&wd->pdev->dev, "selection timeout\n"); break; case WD719X_SUE_RESET: - dev_dbg(&wd->pdev->dev, "bus reset occured\n"); + dev_dbg(&wd->pdev->dev, "bus reset occurred\n"); result = DID_RESET; break; case WD719X_SUE_BUSERROR: @@ -882,7 +882,6 @@ static struct scsi_host_template wd719x_template = { .can_queue = 255, .this_id = 7, .sg_tablesize = WD719X_SG, - .cmd_per_lun = WD719X_CMD_PER_LUN, .use_clustering = ENABLE_CLUSTERING, }; diff --git a/kernel/drivers/scsi/wd719x.h b/kernel/drivers/scsi/wd719x.h index 185e30e4e..9c6dd45f9 100644 --- a/kernel/drivers/scsi/wd719x.h +++ b/kernel/drivers/scsi/wd719x.h @@ -2,8 +2,6 @@ #define _WD719X_H_ #define WD719X_SG 255 /* Scatter/gather size */ -#define WD719X_CMD_PER_LUN 1 /* We should be able to do linked commands, but - * this is 1 for now to be safe. */ struct wd719x_sglist { __le32 ptr; diff --git a/kernel/drivers/scsi/xen-scsifront.c b/kernel/drivers/scsi/xen-scsifront.c index fad22caf0..9dc8687bf 100644 --- a/kernel/drivers/scsi/xen-scsifront.c +++ b/kernel/drivers/scsi/xen-scsifront.c @@ -377,7 +377,6 @@ static int map_data_for_request(struct vscsifrnt_info *info, unsigned int data_len = scsi_bufflen(sc); unsigned int data_grants = 0, seg_grants = 0; struct scatterlist *sg; - unsigned long mfn; struct scsiif_request_segment *seg; ring_req->nr_segments = 0; @@ -420,9 +419,9 @@ static int map_data_for_request(struct vscsifrnt_info *info, ref = gnttab_claim_grant_reference(&gref_head); BUG_ON(ref == -ENOSPC); - mfn = pfn_to_mfn(page_to_pfn(page)); gnttab_grant_foreign_access_ref(ref, - info->dev->otherend_id, mfn, 1); + info->dev->otherend_id, + xen_page_to_gfn(page), 1); shadow->gref[ref_cnt] = ref; ring_req->seg[ref_cnt].gref = ref; ring_req->seg[ref_cnt].offset = (uint16_t)off; @@ -454,9 +453,10 @@ static int map_data_for_request(struct vscsifrnt_info *info, ref = gnttab_claim_grant_reference(&gref_head); BUG_ON(ref == -ENOSPC); - mfn = pfn_to_mfn(page_to_pfn(page)); gnttab_grant_foreign_access_ref(ref, - info->dev->otherend_id, mfn, grant_ro); + info->dev->otherend_id, + xen_page_to_gfn(page), + grant_ro); shadow->gref[ref_cnt] = ref; seg->gref = ref; -- cgit 1.2.3-korg