From 048feec5548c0582ee96148c61b87cccbcb5f9be Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 22:19:45 -0700 Subject: [SCSI] qla2xxx: Defer enablement of RISC interrupts until ISP initialization completes. Josip Rodin noted (http://article.gmane.org/gmane.linux.ports.sparc/10152) the driver oopsing during registration of an rport to the FC-transport layer with a backtrace indicating a dereferencing of an shost->shost_data equal to NULL. David Miller identified a small window in driver logic where this could happen: > Look at how the driver registers the IRQ handler before the host has > been registered with the SCSI layer. > > That leads to a window of time where the shost hasn't been setup > fully, yet ISRs can come in and trigger DPC thread events, such as > loop resyncs, which expect the transport area to be setup. > > But it won't be setup, because scsi_add_host() hasn't finished yet. > > Note that in Josip's crash log, we don't even see the > > qla_printk(KERN_INFO, ha, "\n" > " QLogic Fibre Channel HBA Driver: %s\n" > " QLogic %s - %s\n" > " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n", > ... > > message yet. > > Which means that the crash occurs between qla2x00_request_irqs() > and printing that message. Close this window by enabling RISC interrupts after the host has been registered with the SCSI midlayer. Reported-by: Josip Rodin Cc: Stable Tree Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 1 - drivers/scsi/qla2xxx/qla_os.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 45a3b93eed57..bf41887cdd65 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1834,7 +1834,6 @@ clear_risc_ints: WRT_REG_WORD(®->isp.hccr, HCCR_CLR_HOST_INT); } spin_unlock_irq(&ha->hardware_lock); - ha->isp_ops->enable_intrs(ha); fail: return ret; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 26afe44265c7..6d0f0e5f2827 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1740,6 +1740,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto probe_failed; + ha->isp_ops->enable_intrs(ha); + scsi_scan_host(host); qla2x00_alloc_sysfs_attr(ha); -- cgit v1.2.3 From 44ea91c597ae4641d9ac21b8bbba0795d2f4261e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 20 Sep 2008 00:31:50 +0000 Subject: [SCSI] Fix hang with split requests Sometimes, particularly for USB devices with the last sector bug, requests get completed in chunks. There's a bug in this in that if one of the chunks gets an error, we complete that chunk with an error but never move on to the remaining ones, leading to the request hanging (because it's not fully completed). Fix this by completing all remaining chunks if an error is encountered. Cc: Alan Stern Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ff5d56b3ee4d..62307bd794a9 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -852,7 +852,7 @@ static void scsi_end_bidi_request(struct scsi_cmnd *cmd) void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) { int result = cmd->result; - int this_count = scsi_bufflen(cmd); + int this_count; struct request_queue *q = cmd->device->request_queue; struct request *req = cmd->request; int error = 0; @@ -908,6 +908,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) */ if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL) return; + this_count = blk_rq_bytes(req); /* good_bytes = 0, or (inclusive) there were leftovers and * result = 0, so scsi_end_request couldn't retry. -- cgit v1.2.3 From c9eeb248f3b44939532a9716bc06413383aa5d63 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Wed, 24 Sep 2008 12:00:22 +0300 Subject: [SCSI] qlogicpti: fix sg list traversal error in continuation entries The current sg list traversal logic for the continuation entries doesn't advance the list pointer once all seven slots are used, so the next continuation entry (if there is one) wrongly begins again at the start of the sg list. Fix by advancing the sg pointer after the for_each_sg(). Reported-by: Meelis Roos Cc: David Miller Signed-off-by: James Bottomley --- drivers/scsi/qlogicpti.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 4a1cf6377f6c..905350896725 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -914,6 +914,7 @@ static inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd, ds[i].d_count = sg_dma_len(s); } sg_count -= n; + sg = s; } } else { cmd->dataseg[0].d_base = 0; -- cgit v1.2.3 From 82ef04fb4c82542b3eda81cca461f0594ce9cd0b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 31 Jul 2008 17:02:40 +0900 Subject: libata: make SCR access ops per-link Logically, SCR access ops should take @link; however, there was no compelling reason to convert all SCR access ops when adding @link abstraction as there's one-to-one mapping between a port and a non-PMP link. However, that assumption won't hold anymore with the scheduled addition of slave link. Make SCR access ops per-link. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 24 ++++++++++++------------ drivers/ata/ata_piix.c | 15 +++++++++++---- drivers/ata/libata-core.c | 13 ++++--------- drivers/ata/sata_fsl.c | 26 +++++++++++++------------- drivers/ata/sata_inic162x.c | 8 ++++---- drivers/ata/sata_mv.c | 28 ++++++++++++++-------------- drivers/ata/sata_nv.c | 16 ++++++++-------- drivers/ata/sata_promise.c | 16 +++++++++------- drivers/ata/sata_qstor.c | 12 ++++++------ drivers/ata/sata_sil.c | 16 ++++++++-------- drivers/ata/sata_sil24.c | 12 ++++++------ drivers/ata/sata_sis.c | 28 ++++++++++++++++------------ drivers/ata/sata_svw.c | 10 ++++++---- drivers/ata/sata_uli.c | 24 ++++++++++++------------ drivers/ata/sata_via.c | 24 ++++++++++++------------ drivers/ata/sata_vsc.c | 10 ++++++---- drivers/scsi/libsas/sas_ata.c | 8 ++++---- include/linux/libata.h | 4 ++-- 18 files changed, 153 insertions(+), 141 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2e1a7cb2ed5f..6acea41eb7ca 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -267,8 +267,8 @@ struct ahci_port_priv { * per PM slot */ }; -static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); @@ -820,10 +820,10 @@ static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg) return 0; } -static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { - void __iomem *port_mmio = ahci_port_base(ap); - int offset = ahci_scr_offset(ap, sc_reg); + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); if (offset) { *val = readl(port_mmio + offset); @@ -832,10 +832,10 @@ static int ahci_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; } -static int ahci_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { - void __iomem *port_mmio = ahci_port_base(ap); - int offset = ahci_scr_offset(ap, sc_reg); + void __iomem *port_mmio = ahci_port_base(link->ap); + int offset = ahci_scr_offset(link->ap, sc_reg); if (offset) { writel(val, port_mmio + offset); @@ -973,7 +973,7 @@ static void ahci_disable_alpm(struct ata_port *ap) writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); /* go ahead and clean out PhyRdy Change from Serror too */ - ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); /* * Clear flag to indicate that we should ignore all PhyRdy @@ -1937,8 +1937,8 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat); /* AHCI needs SError cleared; otherwise, it might lock up */ - ahci_scr_read(ap, SCR_ERROR, &serror); - ahci_scr_write(ap, SCR_ERROR, serror); + ahci_scr_read(&ap->link, SCR_ERROR, &serror); + ahci_scr_write(&ap->link, SCR_ERROR, serror); host_ehi->serror |= serror; /* some controllers set IRQ_IF_ERR on device errors, ignore it */ @@ -2027,7 +2027,7 @@ static void ahci_port_intr(struct ata_port *ap) if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) && (status & PORT_IRQ_PHYRDY)) { status &= ~PORT_IRQ_PHYRDY; - ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); + ahci_scr_write(&ap->link, SCR_ERROR, ((1 << 16) | (1 << 18))); } if (unlikely(status & PORT_IRQ_ERROR)) { diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index e6b4606e36b6..81387ff48937 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -165,8 +165,10 @@ static void piix_set_dmamode(struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode(struct ata_port *ap, struct ata_device *adev); static int ich_pata_cable_detect(struct ata_port *ap); static u8 piix_vmw_bmdma_status(struct ata_port *ap); -static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val); -static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val); +static int piix_sidpr_scr_read(struct ata_link *link, + unsigned int reg, u32 *val); +static int piix_sidpr_scr_write(struct ata_link *link, + unsigned int reg, u32 val); #ifdef CONFIG_PM static int piix_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int piix_pci_device_resume(struct pci_dev *pdev); @@ -971,8 +973,10 @@ static u32 piix_merge_scr(u32 val0, u32 val1, const int * const *merge_tbl) return val; } -static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val) +static int piix_sidpr_scr_read(struct ata_link *link, + unsigned int reg, u32 *val) { + struct ata_port *ap = link->ap; const int * const sstatus_merge_tbl[] = { /* DET */ (const int []){ 1, 3, 0, 4, 3, -1 }, /* SPD */ (const int []){ 2, 1, 0, -1 }, @@ -1013,8 +1017,11 @@ static int piix_sidpr_scr_read(struct ata_port *ap, unsigned int reg, u32 *val) return 0; } -static int piix_sidpr_scr_write(struct ata_port *ap, unsigned int reg, u32 val) +static int piix_sidpr_scr_write(struct ata_link *link, + unsigned int reg, u32 val) { + struct ata_port *ap = link->ap; + if (reg >= ARRAY_SIZE(piix_sidx_map)) return -EINVAL; diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 79e3a8e7a84a..825461a33abe 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4868,10 +4868,8 @@ int sata_scr_valid(struct ata_link *link) int sata_scr_read(struct ata_link *link, int reg, u32 *val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_read(ap, reg, val); + return link->ap->ops->scr_read(link, reg, val); return -EOPNOTSUPP; } @@ -4897,10 +4895,8 @@ int sata_scr_read(struct ata_link *link, int reg, u32 *val) int sata_scr_write(struct ata_link *link, int reg, u32 val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; - if (sata_scr_valid(link)) - return ap->ops->scr_write(ap, reg, val); + return link->ap->ops->scr_write(link, reg, val); return -EOPNOTSUPP; } @@ -4925,13 +4921,12 @@ int sata_scr_write(struct ata_link *link, int reg, u32 val) int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) { if (ata_is_host_link(link)) { - struct ata_port *ap = link->ap; int rc; if (sata_scr_valid(link)) { - rc = ap->ops->scr_write(ap, reg, val); + rc = link->ap->ops->scr_write(link, reg, val); if (rc == 0) - rc = ap->ops->scr_read(ap, reg, &val); + rc = link->ap->ops->scr_read(link, reg, &val); return rc; } return -EOPNOTSUPP; diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 3924e7209a44..1a56db92ff7a 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -469,10 +469,10 @@ static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static int sata_fsl_scr_write(struct ata_port *ap, unsigned int sc_reg_in, - u32 val) +static int sata_fsl_scr_write(struct ata_link *link, + unsigned int sc_reg_in, u32 val) { - struct sata_fsl_host_priv *host_priv = ap->host->private_data; + struct sata_fsl_host_priv *host_priv = link->ap->host->private_data; void __iomem *ssr_base = host_priv->ssr_base; unsigned int sc_reg; @@ -493,10 +493,10 @@ static int sata_fsl_scr_write(struct ata_port *ap, unsigned int sc_reg_in, return 0; } -static int sata_fsl_scr_read(struct ata_port *ap, unsigned int sc_reg_in, - u32 *val) +static int sata_fsl_scr_read(struct ata_link *link, + unsigned int sc_reg_in, u32 *val) { - struct sata_fsl_host_priv *host_priv = ap->host->private_data; + struct sata_fsl_host_priv *host_priv = link->ap->host->private_data; void __iomem *ssr_base = host_priv->ssr_base; unsigned int sc_reg; @@ -645,12 +645,12 @@ static int sata_fsl_port_start(struct ata_port *ap) * Workaround for 8315DS board 3gbps link-up issue, * currently limit SATA port to GEN1 speed */ - sata_fsl_scr_read(ap, SCR_CONTROL, &temp); + sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); temp &= ~(0xF << 4); temp |= (0x1 << 4); - sata_fsl_scr_write(ap, SCR_CONTROL, temp); + sata_fsl_scr_write(&ap->link, SCR_CONTROL, temp); - sata_fsl_scr_read(ap, SCR_CONTROL, &temp); + sata_fsl_scr_read(&ap->link, SCR_CONTROL, &temp); dev_printk(KERN_WARNING, dev, "scr_control, speed limited to %x\n", temp); #endif @@ -868,7 +868,7 @@ issue_srst: ioread32(CQ + hcr_base), ioread32(CA + hcr_base), ioread32(CC + hcr_base)); - sata_fsl_scr_read(ap, SCR_ERROR, &Serror); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &Serror); DPRINTK("HStatus = 0x%x\n", ioread32(hcr_base + HSTATUS)); DPRINTK("HControl = 0x%x\n", ioread32(hcr_base + HCONTROL)); @@ -972,9 +972,9 @@ static void sata_fsl_error_intr(struct ata_port *ap) * Handle & Clear SError */ - sata_fsl_scr_read(ap, SCR_ERROR, &SError); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { - sata_fsl_scr_write(ap, SCR_ERROR, SError); + sata_fsl_scr_write(&ap->link, SCR_ERROR, SError); } DPRINTK("error_intr,hStat=0x%x,CE=0x%x,DE =0x%x,SErr=0x%x\n", @@ -1091,7 +1091,7 @@ static void sata_fsl_host_intr(struct ata_port *ap) hstatus = ioread32(hcr_base + HSTATUS); - sata_fsl_scr_read(ap, SCR_ERROR, &SError); + sata_fsl_scr_read(&ap->link, SCR_ERROR, &SError); if (unlikely(SError & 0xFFFF0000)) { DPRINTK("serror @host_intr : 0x%x\n", SError); diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 5032c32fa505..fbbd87c96f10 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -269,9 +269,9 @@ static void inic_reset_port(void __iomem *port_base) writeb(0xff, port_base + PORT_IRQ_STAT); } -static int inic_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) +static int inic_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) { - void __iomem *scr_addr = inic_port_base(ap) + PORT_SCR; + void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; void __iomem *addr; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) @@ -286,9 +286,9 @@ static int inic_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) return 0; } -static int inic_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int inic_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) { - void __iomem *scr_addr = inic_port_base(ap) + PORT_SCR; + void __iomem *scr_addr = inic_port_base(link->ap) + PORT_SCR; if (unlikely(sc_reg >= ARRAY_SIZE(scr_map))) return -EINVAL; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index c815f8ecf6e6..2b24ae58b52e 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -493,10 +493,10 @@ struct mv_hw_ops { void (*reset_bus)(struct ata_host *host, void __iomem *mmio); }; -static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); -static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); -static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val); -static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); +static int mv_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val); +static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val); +static int mv5_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val); +static int mv5_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val); static int mv_port_start(struct ata_port *ap); static void mv_port_stop(struct ata_port *ap); static int mv_qc_defer(struct ata_queued_cmd *qc); @@ -1070,23 +1070,23 @@ static unsigned int mv_scr_offset(unsigned int sc_reg_in) return ofs; } -static int mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) +static int mv_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { unsigned int ofs = mv_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { - *val = readl(mv_ap_base(ap) + ofs); + *val = readl(mv_ap_base(link->ap) + ofs); return 0; } else return -EINVAL; } -static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { - writelfl(val, mv_ap_base(ap) + ofs); + writelfl(val, mv_ap_base(link->ap) + ofs); return 0; } else return -EINVAL; @@ -2251,11 +2251,11 @@ static unsigned int mv5_scr_offset(unsigned int sc_reg_in) return ofs; } -static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) +static int mv5_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { - struct mv_host_priv *hpriv = ap->host->private_data; + struct mv_host_priv *hpriv = link->ap->host->private_data; void __iomem *mmio = hpriv->base; - void __iomem *addr = mv5_phy_base(mmio, ap->port_no); + void __iomem *addr = mv5_phy_base(mmio, link->ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { @@ -2265,11 +2265,11 @@ static int mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in, u32 *val) return -EINVAL; } -static int mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +static int mv5_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { - struct mv_host_priv *hpriv = ap->host->private_data; + struct mv_host_priv *hpriv = link->ap->host->private_data; void __iomem *mmio = hpriv->base; - void __iomem *addr = mv5_phy_base(mmio, ap->port_no); + void __iomem *addr = mv5_phy_base(mmio, link->ap->port_no); unsigned int ofs = mv5_scr_offset(sc_reg_in); if (ofs != 0xffffffffU) { diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 1e1f3f3757ae..88fd4aeacde0 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -302,8 +302,8 @@ static void nv_ck804_host_stop(struct ata_host *host); static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance); static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance); static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance); -static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static void nv_nf2_freeze(struct ata_port *ap); static void nv_nf2_thaw(struct ata_port *ap); @@ -1492,21 +1492,21 @@ static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance) return ret; } -static int nv_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = ioread32(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = ioread32(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int nv_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + iowrite32(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } @@ -2184,9 +2184,9 @@ static void nv_swncq_host_interrupt(struct ata_port *ap, u16 fis) if (!pp->qc_active) return; - if (ap->ops->scr_read(ap, SCR_ERROR, &serror)) + if (ap->ops->scr_read(&ap->link, SCR_ERROR, &serror)) return; - ap->ops->scr_write(ap, SCR_ERROR, serror); + ap->ops->scr_write(&ap->link, SCR_ERROR, serror); if (ata_stat & ATA_ERR) { ata_ehi_clear_desc(ehi); diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 030665ba76b7..750d8cdc00cd 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -137,8 +137,8 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; -static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int pdc_sata_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int pdc_sata_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int pdc_common_port_start(struct ata_port *ap); static int pdc_sata_port_start(struct ata_port *ap); @@ -386,19 +386,21 @@ static int pdc_sata_cable_detect(struct ata_port *ap) return ATA_CBL_SATA; } -static int pdc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int pdc_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int pdc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int pdc_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } @@ -731,7 +733,7 @@ static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, if (sata_scr_valid(&ap->link)) { u32 serror; - pdc_sata_scr_read(ap, SCR_ERROR, &serror); + pdc_sata_scr_read(&ap->link, SCR_ERROR, &serror); ehi->serror |= serror; } diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 1600107047cf..a000c86ac859 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -111,8 +111,8 @@ struct qs_port_priv { qs_state_t state; }; -static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int qs_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int qs_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int qs_port_start(struct ata_port *ap); static void qs_host_stop(struct ata_host *host); @@ -242,11 +242,11 @@ static int qs_prereset(struct ata_link *link, unsigned long deadline) return ata_sff_prereset(link, deadline); } -static int qs_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 8)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 8)); return 0; } @@ -256,11 +256,11 @@ static void qs_error_handler(struct ata_port *ap) ata_std_error_handler(ap); } -static int qs_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int qs_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 8)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 8)); return 0; } diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 88bf4212590f..031d7b7dee34 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -115,8 +115,8 @@ static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); static int sil_pci_device_resume(struct pci_dev *pdev); #endif static void sil_dev_config(struct ata_device *dev); -static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static int sil_set_mode(struct ata_link *link, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); @@ -317,9 +317,9 @@ static inline void __iomem *sil_scr_addr(struct ata_port *ap, return NULL; } -static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int sil_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { - void __iomem *mmio = sil_scr_addr(ap, sc_reg); + void __iomem *mmio = sil_scr_addr(link->ap, sc_reg); if (mmio) { *val = readl(mmio); @@ -328,9 +328,9 @@ static int sil_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; } -static int sil_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sil_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { - void __iomem *mmio = sil_scr_addr(ap, sc_reg); + void __iomem *mmio = sil_scr_addr(link->ap, sc_reg); if (mmio) { writel(val, mmio); @@ -352,8 +352,8 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2) * controllers continue to assert IRQ as long as * SError bits are pending. Clear SError immediately. */ - sil_scr_read(ap, SCR_ERROR, &serror); - sil_scr_write(ap, SCR_ERROR, serror); + sil_scr_read(&ap->link, SCR_ERROR, &serror); + sil_scr_write(&ap->link, SCR_ERROR, serror); /* Sometimes spurious interrupts occur, double check * it's PHYRDY CHG. diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 84ffcc26a74b..4621807a1a6a 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -340,8 +340,8 @@ struct sil24_port_priv { }; static void sil24_dev_config(struct ata_device *dev); -static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val); -static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); +static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val); +static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val); static int sil24_qc_defer(struct ata_queued_cmd *qc); static void sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); @@ -504,9 +504,9 @@ static int sil24_scr_map[] = { [SCR_ACTIVE] = 3, }; -static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) +static int sil24_scr_read(struct ata_link *link, unsigned sc_reg, u32 *val) { - void __iomem *scr_addr = sil24_port_base(ap) + PORT_SCONTROL; + void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; @@ -517,9 +517,9 @@ static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val) return -EINVAL; } -static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val) { - void __iomem *scr_addr = sil24_port_base(ap) + PORT_SCONTROL; + void __iomem *scr_addr = sil24_port_base(link->ap) + PORT_SCONTROL; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 1010b3069bd5..9c43b4e7c4a6 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -64,8 +64,8 @@ enum { }; static int sis_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static const struct pci_device_id sis_pci_tbl[] = { { PCI_VDEVICE(SI, 0x0180), sis_180 }, /* SiS 964/180 */ @@ -134,10 +134,11 @@ static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) return addr; } -static u32 sis_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static u32 sis_scr_cfg_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u32 val2 = 0; u8 pmr; @@ -158,10 +159,11 @@ static u32 sis_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return 0; } -static int sis_scr_cfg_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sis_scr_cfg_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u8 pmr; if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */ @@ -178,8 +180,9 @@ static int sis_scr_cfg_write(struct ata_port *ap, unsigned int sc_reg, u32 val) return 0; } -static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int sis_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { + struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 pmr; @@ -187,7 +190,7 @@ static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return -EINVAL; if (ap->flags & SIS_FLAG_CFGSCR) - return sis_scr_cfg_read(ap, sc_reg, val); + return sis_scr_cfg_read(link, sc_reg, val); pci_read_config_byte(pdev, SIS_PMR, &pmr); @@ -202,8 +205,9 @@ static int sis_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) return 0; } -static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int sis_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { + struct ata_port *ap = link->ap; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 pmr; @@ -213,7 +217,7 @@ static int sis_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) pci_read_config_byte(pdev, SIS_PMR, &pmr); if (ap->flags & SIS_FLAG_CFGSCR) - return sis_scr_cfg_write(ap, sc_reg, val); + return sis_scr_cfg_write(link, sc_reg, val); else { iowrite32(val, ap->ioaddr.scr_addr + (sc_reg * 4)); if ((pdev->device == 0x0182) || (pdev->device == 0x0183) || diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index fb13b82aacba..609d147813ae 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -123,20 +123,22 @@ static int k2_sata_check_atapi_dma(struct ata_queued_cmd *qc) } } -static int k2_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int k2_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int k2_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int k2_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index db529b849948..019575bb3e08 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -57,8 +57,8 @@ struct uli_priv { }; static int uli_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int uli_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int uli_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static const struct pci_device_id uli_pci_tbl[] = { { PCI_VDEVICE(AL, 0x5289), uli_5289 }, @@ -107,39 +107,39 @@ static unsigned int get_scr_cfg_addr(struct ata_port *ap, unsigned int sc_reg) return hpriv->scr_cfg_addr[ap->port_no] + (4 * sc_reg); } -static u32 uli_scr_cfg_read(struct ata_port *ap, unsigned int sc_reg) +static u32 uli_scr_cfg_read(struct ata_link *link, unsigned int sc_reg) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, sc_reg); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, sc_reg); u32 val; pci_read_config_dword(pdev, cfg_addr, &val); return val; } -static void uli_scr_cfg_write(struct ata_port *ap, unsigned int scr, u32 val) +static void uli_scr_cfg_write(struct ata_link *link, unsigned int scr, u32 val) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap, scr); + struct pci_dev *pdev = to_pci_dev(link->ap->host->dev); + unsigned int cfg_addr = get_scr_cfg_addr(link->ap, scr); pci_write_config_dword(pdev, cfg_addr, val); } -static int uli_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int uli_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = uli_scr_cfg_read(ap, sc_reg); + *val = uli_scr_cfg_read(link, sc_reg); return 0; } -static int uli_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int uli_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0 return -EINVAL; - uli_scr_cfg_write(ap, sc_reg, val); + uli_scr_cfg_write(link, sc_reg, val); return 0; } diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 96deeb354e16..1cfa74535d91 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -68,8 +68,8 @@ enum { }; static int svia_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); -static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val); -static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val); +static int svia_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); +static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); static void svia_noop_freeze(struct ata_port *ap); static int vt6420_prereset(struct ata_link *link, unsigned long deadline); static int vt6421_pata_cable_detect(struct ata_port *ap); @@ -152,19 +152,19 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, svia_pci_tbl); MODULE_VERSION(DRV_VERSION); -static int svia_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int svia_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = ioread32(ap->ioaddr.scr_addr + (4 * sc_reg)); + *val = ioread32(link->ap->ioaddr.scr_addr + (4 * sc_reg)); return 0; } -static int svia_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int svia_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - iowrite32(val, ap->ioaddr.scr_addr + (4 * sc_reg)); + iowrite32(val, link->ap->ioaddr.scr_addr + (4 * sc_reg)); return 0; } @@ -210,20 +210,20 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline) goto skip_scr; /* Resume phy. This is the old SATA resume sequence */ - svia_scr_write(ap, SCR_CONTROL, 0x300); - svia_scr_read(ap, SCR_CONTROL, &scontrol); /* flush */ + svia_scr_write(link, SCR_CONTROL, 0x300); + svia_scr_read(link, SCR_CONTROL, &scontrol); /* flush */ /* wait for phy to become ready, if necessary */ do { msleep(200); - svia_scr_read(ap, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_STATUS, &sstatus); if ((sstatus & 0xf) != 1) break; } while (time_before(jiffies, timeout)); /* open code sata_print_link_status() */ - svia_scr_read(ap, SCR_STATUS, &sstatus); - svia_scr_read(ap, SCR_CONTROL, &scontrol); + svia_scr_read(link, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_CONTROL, &scontrol); online = (sstatus & 0xf) == 0x3; @@ -232,7 +232,7 @@ static int vt6420_prereset(struct ata_link *link, unsigned long deadline) online ? "up" : "down", sstatus, scontrol); /* SStatus is read one more time */ - svia_scr_read(ap, SCR_STATUS, &sstatus); + svia_scr_read(link, SCR_STATUS, &sstatus); if (!online) { /* tell EH to bail */ diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index f3d635c0a2e9..c57cdff9e6bd 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -98,20 +98,22 @@ enum { VSC_SATA_INT_PHY_CHANGE), }; -static int vsc_sata_scr_read(struct ata_port *ap, unsigned int sc_reg, u32 *val) +static int vsc_sata_scr_read(struct ata_link *link, + unsigned int sc_reg, u32 *val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - *val = readl(ap->ioaddr.scr_addr + (sc_reg * 4)); + *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } -static int vsc_sata_scr_write(struct ata_port *ap, unsigned int sc_reg, u32 val) +static int vsc_sata_scr_write(struct ata_link *link, + unsigned int sc_reg, u32 val) { if (sc_reg > SCR_CONTROL) return -EINVAL; - writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4)); return 0; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48ee8c7f5bdd..e1872989710a 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -294,10 +294,10 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } } -static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, +static int sas_ata_scr_write(struct ata_link *link, unsigned int sc_reg_in, u32 val) { - struct domain_device *dev = ap->private_data; + struct domain_device *dev = link->ap->private_data; SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { @@ -319,10 +319,10 @@ static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, return 0; } -static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in, +static int sas_ata_scr_read(struct ata_link *link, unsigned int sc_reg_in, u32 *val) { - struct domain_device *dev = ap->private_data; + struct domain_device *dev = link->ap->private_data; SAS_DPRINTK("STUB %s\n", __func__); switch (sc_reg_in) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 225bfc5bd9ec..ffd622fa319c 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -772,8 +772,8 @@ struct ata_port_operations { /* * Optional features */ - int (*scr_read)(struct ata_port *ap, unsigned int sc_reg, u32 *val); - int (*scr_write)(struct ata_port *ap, unsigned int sc_reg, u32 val); + int (*scr_read)(struct ata_link *link, unsigned int sc_reg, u32 *val); + int (*scr_write)(struct ata_link *link, unsigned int sc_reg, u32 val); void (*pmp_attach)(struct ata_port *ap); void (*pmp_detach)(struct ata_port *ap); int (*enable_pm)(struct ata_port *ap, enum link_pm policy); -- cgit v1.2.3 From 315cb0ad124575e75da2d0e0a95990587fc23485 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 7 Aug 2008 20:49:30 -0400 Subject: [SCSI] scsi_host_lookup: error returns and NULL pointers This patch cleans up the behavior of scsi_host_lookup(). The original implementation attempted to use the dual role of either returning a pointer value, or a negative error code. User's needed to use IS_ERR() to check the result. Additionally, the IS_ERR() macro never checks for when a NULL pointer was returned, so a NULL pointer actually passes with a success case. Note: scsi_host_get(), used by scsi_host_lookup(), can return a NULL pointer. Talk about a mudhole for the unitiated to step into.... This patch converts scsi_host_lookup() to return either NULL or a valid pointer. The consumers were updated for the change. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 2 +- drivers/scsi/scsi_proc.c | 8 ++++---- drivers/scsi/scsi_tgt_lib.c | 6 +++--- drivers/scsi/scsi_transport_iscsi.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index fed0b02ebc1d..3fdbb13e80a8 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -464,7 +464,7 @@ static int __scsi_host_match(struct device *dev, void *data) struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) { struct device *cdev; - struct Scsi_Host *shost = ERR_PTR(-ENXIO); + struct Scsi_Host *shost = NULL; cdev = class_find_device(&shost_class, NULL, &hostnum, __scsi_host_match); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index c6a904a45bf9..82f7b2dd08a2 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -259,8 +259,8 @@ static int scsi_add_single_device(uint host, uint channel, uint id, uint lun) int error = -ENXIO; shost = scsi_host_lookup(host); - if (IS_ERR(shost)) - return PTR_ERR(shost); + if (!shost) + return error; if (shost->transportt->user_scan) error = shost->transportt->user_scan(shost, channel, id, lun); @@ -287,8 +287,8 @@ static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun) int error = -ENXIO; shost = scsi_host_lookup(host); - if (IS_ERR(shost)) - return PTR_ERR(shost); + if (!shost) + return error; sdev = scsi_device_lookup(shost, channel, id, lun); if (sdev) { scsi_remove_device(sdev); diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 257e097c39af..f26299dfc5d5 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -460,7 +460,7 @@ int scsi_tgt_kspace_exec(int host_no, u64 itn_id, int result, u64 tag, /* TODO: replace with a O(1) alg */ shost = scsi_host_lookup(host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "Could not find host no %d\n", host_no); return -EINVAL; } @@ -550,7 +550,7 @@ int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 itn_id, u64 mid, int result) dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid); shost = scsi_host_lookup(host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "Could not find host no %d\n", host_no); return err; } @@ -603,7 +603,7 @@ int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 itn_id, int result) dprintk("%d %d%llx\n", host_no, result, (unsigned long long)itn_id); shost = scsi_host_lookup(host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "Could not find host no %d\n", host_no); return err; } diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 043c3921164f..0ce5f7cdfe2a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1361,7 +1361,7 @@ iscsi_tgt_dscvr(struct iscsi_transport *transport, return -EINVAL; shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "target discovery could not find host no %u\n", ev->u.tgt_dscvr.host_no); return -ENODEV; @@ -1387,7 +1387,7 @@ iscsi_set_host_param(struct iscsi_transport *transport, return -ENOSYS; shost = scsi_host_lookup(ev->u.set_host_param.host_no); - if (IS_ERR(shost)) { + if (!shost) { printk(KERN_ERR "set_host_param could not find host no %u\n", ev->u.set_host_param.host_no); return -ENODEV; -- cgit v1.2.3 From 3fd7f938831b7f2e7b6103118ad1a455f6f03110 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 13 Aug 2008 21:36:55 -0700 Subject: [SCSI] qla2xxx: Remove semaphore.h Now that qla2xxx has been converted to mutexes, it no longer needs the semaphore include. Signed-off-by: Matthew Wilcox Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_def.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 94a720eabfd8..431c19e83001 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 557cc476c04146ee610f4bc77bfe20ce1c823d7c Mon Sep 17 00:00:00 2001 From: Nick Warne Date: Sun, 17 Aug 2008 00:00:44 +0200 Subject: [SCSI] tmscsim: Fixup KERN_INFO in printk Multiline kernel messages should contain a priority in every line, besides, some log daemons represent a tabulator as "^I", fix both these issues. Signed-off-by: Nick Warne Signed-off-by: Guennadi Liakhovetski Signed-off-by: James Bottomley --- drivers/scsi/tmscsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index 1723d71cbf3f..69ac6e590f1d 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -2573,8 +2573,8 @@ static struct pci_driver dc390_driver = { static int __init dc390_module_init(void) { if (!disable_clustering) - printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n" - "\twith \"disable_clustering=1\" and report to maintainers\n"); + printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n"); + printk(KERN_INFO " with \"disable_clustering=1\" and report to maintainers\n"); if (tmscsim[0] == -1 || tmscsim[0] > 15) { tmscsim[0] = 7; -- cgit v1.2.3 From 22447be7d15aefcfab84e9bec4859a28198b0c62 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 8 Aug 2008 02:14:18 -0400 Subject: [SCSI] scsi_netlink: Add transport and LLD recieve and event support This patch adds scsi netlink recieve and event support for transport and scsi LLDD's. It is a reimplementation of the patch posted last week by David Somayajulu. http://marc.info/?l=linux-scsi&m=121745486221819&w=2 There are a few things done differently: - Transport support is included - Event delivery is included - The vendor message is now its own unique message type, considered part of the generic "SCSI Transport". - LLDD entry points are now registered rather than included in the scsi_host_template. Background: When I started to implement the event handler via template, I had to either: muck up scsi_add_host and scsi_remove_host; or have the event handler search all possible shosts. Neither was acceptable. Moving to a registration solves this, and also limits the scope of the changes to something that could be backported to a distro without breaking an already-released-distro kabi. However, I admit it isn't as elegant, as the passing of the LLDD host template in the registration and the complexity around dynamic add/remove shows. - The receive path was augmented to require a unique identifier for the LLDD before the message was allowed to be handed off to the driver. Given how quickly very fatal errors occur if there's msg mismatches (which I saw in testing my own tools :), I believe this to be a very good thing. The id plays off the vendor id scheme already introduced for the vendor unique event messages used by FC. Additionally, the id use as the basis of the registration/deregistration. - Send assist functions, for both the transport and LLDDs are included. [fujita.tomonori@lab.ntt.co.jp: fix missing cast] Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/scsi_netlink.c | 523 +++++++++++++++++++++++++++++++++++++++++++- include/scsi/scsi_netlink.h | 62 +++++- 2 files changed, 576 insertions(+), 9 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index ae7ed9a22662..b37e133de805 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,39 @@ struct sock *scsi_nl_sock = NULL; EXPORT_SYMBOL_GPL(scsi_nl_sock); +static DEFINE_SPINLOCK(scsi_nl_lock); +static struct list_head scsi_nl_drivers; + +static u32 scsi_nl_state; +#define STATE_EHANDLER_BSY 0x00000001 + +struct scsi_nl_transport { + int (*msg_handler)(struct sk_buff *); + void (*event_handler)(struct notifier_block *, unsigned long, void *); + unsigned int refcnt; + int flags; +}; + +/* flags values (bit flags) */ +#define HANDLER_DELETING 0x1 + +static struct scsi_nl_transport transports[SCSI_NL_MAX_TRANSPORTS] = + { {NULL, }, }; + + +struct scsi_nl_drvr { + struct list_head next; + int (*dmsg_handler)(struct Scsi_Host *shost, void *payload, + u32 len, u32 pid); + void (*devt_handler)(struct notifier_block *nb, + unsigned long event, void *notify_ptr); + struct scsi_host_template *hostt; + u64 vendor_id; + unsigned int refcnt; + int flags; +}; + + /** * scsi_nl_rcv_msg - Receive message handler. @@ -45,8 +79,9 @@ scsi_nl_rcv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; struct scsi_nl_hdr *hdr; - uint32_t rlen; - int err; + unsigned long flags; + u32 rlen; + int err, tport; while (skb->len >= NLMSG_SPACE(0)) { err = 0; @@ -65,7 +100,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { err = -EBADMSG; - return; + goto next_msg; } hdr = NLMSG_DATA(nlh); @@ -83,12 +118,27 @@ scsi_nl_rcv_msg(struct sk_buff *skb) if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { printk(KERN_WARNING "%s: discarding partial message\n", __func__); - return; + goto next_msg; } /* - * We currently don't support anyone sending us a message + * Deliver message to the appropriate transport */ + spin_lock_irqsave(&scsi_nl_lock, flags); + + tport = hdr->transport; + if ((tport < SCSI_NL_MAX_TRANSPORTS) && + !(transports[tport].flags & HANDLER_DELETING) && + (transports[tport].msg_handler)) { + transports[tport].refcnt++; + spin_unlock_irqrestore(&scsi_nl_lock, flags); + err = transports[tport].msg_handler(skb); + spin_lock_irqsave(&scsi_nl_lock, flags); + transports[tport].refcnt--; + } else + err = -ENOENT; + + spin_unlock_irqrestore(&scsi_nl_lock, flags); next_msg: if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) @@ -110,14 +160,42 @@ static int scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netlink_notify *n = ptr; + struct scsi_nl_drvr *driver; + unsigned long flags; + int tport; if (n->protocol != NETLINK_SCSITRANSPORT) return NOTIFY_DONE; + spin_lock_irqsave(&scsi_nl_lock, flags); + scsi_nl_state |= STATE_EHANDLER_BSY; + /* - * Currently, we are not tracking PID's, etc. There is nothing - * to handle. + * Pass event on to any transports that may be listening */ + for (tport = 0; tport < SCSI_NL_MAX_TRANSPORTS; tport++) { + if (!(transports[tport].flags & HANDLER_DELETING) && + (transports[tport].event_handler)) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + transports[tport].event_handler(this, event, ptr); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + } + + /* + * Pass event on to any drivers that may be listening + */ + list_for_each_entry(driver, &scsi_nl_drivers, next) { + if (!(driver->flags & HANDLER_DELETING) && + (driver->devt_handler)) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + driver->devt_handler(this, event, ptr); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + } + + scsi_nl_state &= ~STATE_EHANDLER_BSY; + spin_unlock_irqrestore(&scsi_nl_lock, flags); return NOTIFY_DONE; } @@ -128,7 +206,281 @@ static struct notifier_block scsi_netlink_notifier = { /** - * scsi_netlink_init - Called by SCSI subsystem to intialize the SCSI transport netlink interface + * GENERIC SCSI transport receive and event handlers + **/ + +/** + * scsi_generic_msg_handler - receive message handler for GENERIC transport + * messages + * + * @skb: socket receive buffer + * + **/ +static int +scsi_generic_msg_handler(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = nlmsg_hdr(skb); + struct scsi_nl_hdr *snlh = NLMSG_DATA(nlh); + struct scsi_nl_drvr *driver; + struct Scsi_Host *shost; + unsigned long flags; + int err = 0, match, pid; + + pid = NETLINK_CREDS(skb)->pid; + + switch (snlh->msgtype) { + case SCSI_NL_SHOST_VENDOR: + { + struct scsi_nl_host_vendor_msg *msg = NLMSG_DATA(nlh); + + /* Locate the driver that corresponds to the message */ + spin_lock_irqsave(&scsi_nl_lock, flags); + match = 0; + list_for_each_entry(driver, &scsi_nl_drivers, next) { + if (driver->vendor_id == msg->vendor_id) { + match = 1; + break; + } + } + + if ((!match) || (!driver->dmsg_handler)) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + err = -ESRCH; + goto rcv_exit; + } + + if (driver->flags & HANDLER_DELETING) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + err = -ESHUTDOWN; + goto rcv_exit; + } + + driver->refcnt++; + spin_unlock_irqrestore(&scsi_nl_lock, flags); + + + /* if successful, scsi_host_lookup takes a shost reference */ + shost = scsi_host_lookup(msg->host_no); + if (!shost) { + err = -ENODEV; + goto driver_exit; + } + + /* is this host owned by the vendor ? */ + if (shost->hostt != driver->hostt) { + err = -EINVAL; + goto vendormsg_put; + } + + /* pass message on to the driver */ + err = driver->dmsg_handler(shost, (void *)&msg[1], + msg->vmsg_datalen, pid); + +vendormsg_put: + /* release reference by scsi_host_lookup */ + scsi_host_put(shost); + +driver_exit: + /* release our own reference on the registration object */ + spin_lock_irqsave(&scsi_nl_lock, flags); + driver->refcnt--; + spin_unlock_irqrestore(&scsi_nl_lock, flags); + break; + } + + default: + err = -EBADR; + break; + } + +rcv_exit: + if (err) + printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n", + __func__, snlh->msgtype, err); + return err; +} + + +/** + * scsi_nl_add_transport - + * Registers message and event handlers for a transport. Enables + * receipt of netlink messages and events to a transport. + * + * @tport: transport registering handlers + * @msg_handler: receive message handler callback + * @event_handler: receive event handler callback + **/ +int +scsi_nl_add_transport(u8 tport, + int (*msg_handler)(struct sk_buff *), + void (*event_handler)(struct notifier_block *, unsigned long, void *)) +{ + unsigned long flags; + int err = 0; + + if (tport >= SCSI_NL_MAX_TRANSPORTS) + return -EINVAL; + + spin_lock_irqsave(&scsi_nl_lock, flags); + + if (scsi_nl_state & STATE_EHANDLER_BSY) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + msleep(1); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + + if (transports[tport].msg_handler || transports[tport].event_handler) { + err = -EALREADY; + goto register_out; + } + + transports[tport].msg_handler = msg_handler; + transports[tport].event_handler = event_handler; + transports[tport].flags = 0; + transports[tport].refcnt = 0; + +register_out: + spin_unlock_irqrestore(&scsi_nl_lock, flags); + + return err; +} +EXPORT_SYMBOL_GPL(scsi_nl_add_transport); + + +/** + * scsi_nl_remove_transport - + * Disable transport receiption of messages and events + * + * @tport: transport deregistering handlers + * + **/ +void +scsi_nl_remove_transport(u8 tport) +{ + unsigned long flags; + + spin_lock_irqsave(&scsi_nl_lock, flags); + if (scsi_nl_state & STATE_EHANDLER_BSY) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + msleep(1); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + + if (tport < SCSI_NL_MAX_TRANSPORTS) { + transports[tport].flags |= HANDLER_DELETING; + + while (transports[tport].refcnt != 0) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + schedule_timeout_uninterruptible(HZ/4); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + transports[tport].msg_handler = NULL; + transports[tport].event_handler = NULL; + transports[tport].flags = 0; + } + + spin_unlock_irqrestore(&scsi_nl_lock, flags); + + return; +} +EXPORT_SYMBOL_GPL(scsi_nl_remove_transport); + + +/** + * scsi_nl_add_driver - + * A driver is registering its interfaces for SCSI netlink messages + * + * @vendor_id: A unique identification value for the driver. + * @hostt: address of the driver's host template. Used + * to verify an shost is bound to the driver + * @nlmsg_handler: receive message handler callback + * @nlevt_handler: receive event handler callback + * + * Returns: + * 0 on Success + * error result otherwise + **/ +int +scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt, + int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload, + u32 len, u32 pid), + void (*nlevt_handler)(struct notifier_block *nb, + unsigned long event, void *notify_ptr)) +{ + struct scsi_nl_drvr *driver; + unsigned long flags; + + driver = kzalloc(sizeof(*driver), GFP_KERNEL); + if (unlikely(!driver)) { + printk(KERN_ERR "%s: allocation failure\n", __func__); + return -ENOMEM; + } + + driver->dmsg_handler = nlmsg_handler; + driver->devt_handler = nlevt_handler; + driver->hostt = hostt; + driver->vendor_id = vendor_id; + + spin_lock_irqsave(&scsi_nl_lock, flags); + if (scsi_nl_state & STATE_EHANDLER_BSY) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + msleep(1); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + list_add_tail(&driver->next, &scsi_nl_drivers); + spin_unlock_irqrestore(&scsi_nl_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(scsi_nl_add_driver); + + +/** + * scsi_nl_remove_driver - + * An driver is unregistering with the SCSI netlink messages + * + * @vendor_id: The unique identification value for the driver. + **/ +void +scsi_nl_remove_driver(u64 vendor_id) +{ + struct scsi_nl_drvr *driver; + unsigned long flags; + + spin_lock_irqsave(&scsi_nl_lock, flags); + if (scsi_nl_state & STATE_EHANDLER_BSY) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + msleep(1); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + + list_for_each_entry(driver, &scsi_nl_drivers, next) { + if (driver->vendor_id == vendor_id) { + driver->flags |= HANDLER_DELETING; + while (driver->refcnt != 0) { + spin_unlock_irqrestore(&scsi_nl_lock, flags); + schedule_timeout_uninterruptible(HZ/4); + spin_lock_irqsave(&scsi_nl_lock, flags); + } + list_del(&driver->next); + kfree(driver); + spin_unlock_irqrestore(&scsi_nl_lock, flags); + return; + } + } + + spin_unlock_irqrestore(&scsi_nl_lock, flags); + + printk(KERN_ERR "%s: removal of driver failed - vendor_id 0x%llx\n", + __func__, (unsigned long long)vendor_id); + return; +} +EXPORT_SYMBOL_GPL(scsi_nl_remove_driver); + + +/** + * scsi_netlink_init - Called by SCSI subsystem to intialize + * the SCSI transport netlink interface * **/ void @@ -136,6 +488,8 @@ scsi_netlink_init(void) { int error; + INIT_LIST_HEAD(&scsi_nl_drivers); + error = netlink_register_notifier(&scsi_netlink_notifier); if (error) { printk(KERN_ERR "%s: register of event handler failed - %d\n", @@ -150,8 +504,15 @@ scsi_netlink_init(void) printk(KERN_ERR "%s: register of recieve handler failed\n", __func__); netlink_unregister_notifier(&scsi_netlink_notifier); + return; } + /* Register the entry points for the generic SCSI transport */ + error = scsi_nl_add_transport(SCSI_NL_TRANSPORT, + scsi_generic_msg_handler, NULL); + if (error) + printk(KERN_ERR "%s: register of GENERIC transport handler" + " failed - %d\n", __func__, error); return; } @@ -163,6 +524,8 @@ scsi_netlink_init(void) void scsi_netlink_exit(void) { + scsi_nl_remove_transport(SCSI_NL_TRANSPORT); + if (scsi_nl_sock) { netlink_kernel_release(scsi_nl_sock); netlink_unregister_notifier(&scsi_netlink_notifier); @@ -172,3 +535,147 @@ scsi_netlink_exit(void) } +/* + * Exported Interfaces + */ + +/** + * scsi_nl_send_transport_msg - + * Generic function to send a single message from a SCSI transport to + * a single process + * + * @pid: receiving pid + * @hdr: message payload + * + **/ +void +scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + const char *fn; + char *datab; + u32 len, skblen; + int err; + + if (!scsi_nl_sock) { + err = -ENOENT; + fn = "netlink socket"; + goto msg_fail; + } + + len = NLMSG_SPACE(hdr->msglen); + skblen = NLMSG_SPACE(len); + + skb = alloc_skb(skblen, GFP_KERNEL); + if (!skb) { + err = -ENOBUFS; + fn = "alloc_skb"; + goto msg_fail; + } + + nlh = nlmsg_put(skb, pid, 0, SCSI_TRANSPORT_MSG, len - sizeof(*nlh), 0); + if (!nlh) { + err = -ENOBUFS; + fn = "nlmsg_put"; + goto msg_fail_skb; + } + datab = NLMSG_DATA(nlh); + memcpy(datab, hdr, hdr->msglen); + + err = nlmsg_unicast(scsi_nl_sock, skb, pid); + if (err < 0) { + fn = "nlmsg_unicast"; + /* nlmsg_unicast already kfree_skb'd */ + goto msg_fail; + } + + return; + +msg_fail_skb: + kfree_skb(skb); +msg_fail: + printk(KERN_WARNING + "%s: Dropped Message : pid %d Transport %d, msgtype x%x, " + "msglen %d: %s : err %d\n", + __func__, pid, hdr->transport, hdr->msgtype, hdr->msglen, + fn, err); + return; +} +EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg); + + +/** + * scsi_nl_send_vendor_msg - called to send a shost vendor unique message + * to a specific process id. + * + * @pid: process id of the receiver + * @host_no: host # sending the message + * @vendor_id: unique identifier for the driver's vendor + * @data_len: amount, in bytes, of vendor unique payload data + * @data_buf: pointer to vendor unique data buffer + * + * Returns: + * 0 on succesful return + * otherwise, failing error code + * + * Notes: + * This routine assumes no locks are held on entry. + */ +int +scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id, + char *data_buf, u32 data_len) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct scsi_nl_host_vendor_msg *msg; + u32 len, skblen; + int err; + + if (!scsi_nl_sock) { + err = -ENOENT; + goto send_vendor_fail; + } + + len = SCSI_NL_MSGALIGN(sizeof(*msg) + data_len); + skblen = NLMSG_SPACE(len); + + skb = alloc_skb(skblen, GFP_KERNEL); + if (!skb) { + err = -ENOBUFS; + goto send_vendor_fail; + } + + nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, + skblen - sizeof(*nlh), 0); + if (!nlh) { + err = -ENOBUFS; + goto send_vendor_fail_skb; + } + msg = NLMSG_DATA(nlh); + + INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT, + SCSI_NL_SHOST_VENDOR, len); + msg->vendor_id = vendor_id; + msg->host_no = host_no; + msg->vmsg_datalen = data_len; /* bytes */ + memcpy(&msg[1], data_buf, data_len); + + err = nlmsg_unicast(scsi_nl_sock, skb, pid); + if (err) + /* nlmsg_multicast already kfree_skb'd */ + goto send_vendor_fail; + + return 0; + +send_vendor_fail_skb: + kfree_skb(skb); +send_vendor_fail: + printk(KERN_WARNING + "%s: Dropped SCSI Msg : host %d vendor_unique - err %d\n", + __func__, host_no, err); + return err; +} +EXPORT_SYMBOL(scsi_nl_send_vendor_msg); + + diff --git a/include/scsi/scsi_netlink.h b/include/scsi/scsi_netlink.h index 8c1470cc8209..536752c40d41 100644 --- a/include/scsi/scsi_netlink.h +++ b/include/scsi/scsi_netlink.h @@ -22,6 +22,9 @@ #ifndef SCSI_NETLINK_H #define SCSI_NETLINK_H +#include + + /* * This file intended to be included by both kernel and user space */ @@ -55,7 +58,41 @@ struct scsi_nl_hdr { #define SCSI_NL_TRANSPORT_FC 1 #define SCSI_NL_MAX_TRANSPORTS 2 -/* scsi_nl_hdr->msgtype values are defined in each transport */ +/* Transport-based scsi_nl_hdr->msgtype values are defined in each transport */ + +/* + * GENERIC SCSI scsi_nl_hdr->msgtype Values + */ + /* kernel -> user */ +#define SCSI_NL_SHOST_VENDOR 0x0001 + /* user -> kernel */ +/* SCSI_NL_SHOST_VENDOR msgtype is kernel->user and user->kernel */ + + +/* + * Message Structures : + */ + +/* macro to round up message lengths to 8byte boundary */ +#define SCSI_NL_MSGALIGN(len) (((len) + 7) & ~7) + + +/* + * SCSI HOST Vendor Unique messages : + * SCSI_NL_SHOST_VENDOR + * + * Note: The Vendor Unique message payload will begin directly after + * this structure, with the length of the payload per vmsg_datalen. + * + * Note: When specifying vendor_id, be sure to read the Vendor Type and ID + * formatting requirements specified below + */ +struct scsi_nl_host_vendor_msg { + struct scsi_nl_hdr snlh; /* must be 1st element ! */ + uint64_t vendor_id; + uint16_t host_no; + uint16_t vmsg_datalen; +} __attribute__((aligned(sizeof(uint64_t)))); /* @@ -83,5 +120,28 @@ struct scsi_nl_hdr { } +#ifdef __KERNEL__ + +#include + +/* Exported Kernel Interfaces */ +int scsi_nl_add_transport(u8 tport, + int (*msg_handler)(struct sk_buff *), + void (*event_handler)(struct notifier_block *, unsigned long, void *)); +void scsi_nl_remove_transport(u8 tport); + +int scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt, + int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload, + u32 len, u32 pid), + void (*nlevt_handler)(struct notifier_block *nb, + unsigned long event, void *notify_ptr)); +void scsi_nl_remove_driver(u64 vendor_id); + +void scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr); +int scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id, + char *data_buf, u32 data_len); + +#endif /* __KERNEL__ */ + #endif /* SCSI_NETLINK_H */ -- cgit v1.2.3 From 0f1d87a2acb8fd1f2ef8af109a785123ddc1a6cb Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Aug 2008 16:43:59 -0500 Subject: [SCSI] add inline functions for recognising created and blocked states The created and blocked states are very shortly going to correspond to mixed sdev_state states. Signed-off-by: James Bottomley --- drivers/scsi/scsi.c | 13 +++++++------ drivers/scsi/scsi_scan.c | 4 ++-- include/scsi/scsi_device.h | 11 +++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ee6be596503d..762a8797e0be 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -664,13 +664,14 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) goto out; } - /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */ - if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) { + /* Check to see if the scsi lld made this device blocked. */ + if (unlikely(scsi_device_blocked(cmd->device))) { /* - * in SDEV_BLOCK, the command is just put back on the device - * queue. The suspend state has already blocked the queue so - * future requests should not occur until the device - * transitions out of the suspend state. + * in blocked state, the command is just put back on + * the device queue. The suspend state has already + * blocked the queue so future requests should not + * occur until the device transitions out of the + * suspend state. */ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 34d0de6cd511..2926baaac31e 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -994,7 +994,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, */ sdev = scsi_device_lookup_by_target(starget, lun); if (sdev) { - if (rescan || sdev->sdev_state != SDEV_CREATED) { + if (rescan || !scsi_device_created(sdev)) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", sdev->sdev_gendev.bus_id)); @@ -1467,7 +1467,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, kfree(lun_data); out: scsi_device_put(sdev); - if (sdev->sdev_state == SDEV_CREATED) + if (scsi_device_created(sdev)) /* * the sdev we used didn't appear in the report luns scan */ diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 80b2e93c2936..cc46652e4658 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -384,10 +384,21 @@ static inline unsigned int sdev_id(struct scsi_device *sdev) #define scmd_id(scmd) sdev_id((scmd)->device) #define scmd_channel(scmd) sdev_channel((scmd)->device) +/* + * checks for positions of the SCSI state machine + */ static inline int scsi_device_online(struct scsi_device *sdev) { return sdev->sdev_state != SDEV_OFFLINE; } +static inline int scsi_device_blocked(struct scsi_device *sdev) +{ + return sdev->sdev_state == SDEV_BLOCK; +} +static inline int scsi_device_created(struct scsi_device *sdev) +{ + return sdev->sdev_state == SDEV_CREATED; +} /* accessor functions for the SCSI parameters */ static inline int scsi_device_sync(struct scsi_device *sdev) -- cgit v1.2.3 From 6f4267e3bd1211b3d09130e626b0b3d885077610 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Aug 2008 16:53:31 -0500 Subject: [SCSI] Update the SCSI state model to allow blocking in the created state Brian King reported that fibre channel devices can oops during scanning if their ports block (because the device goes from CREATED -> BLOCK -> RUNNING rather than CREATED -> BLOCK -> CREATED). Fix this by adding a new state: CREATED_BLOCK which can only transition back to CREATED and disallow the CREATED -> BLOCK transition. Now both the created and blocked states that the mid-layer recognises can include CREATED_BLOCK. Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 39 ++++++++++++++++++++++++++++++--------- drivers/scsi/scsi_scan.c | 16 ++++++++++++++-- drivers/scsi/scsi_sysfs.c | 1 + include/scsi/scsi_device.h | 14 +++++++++----- 4 files changed, 54 insertions(+), 16 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 62307bd794a9..d2884bffa1b9 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1251,6 +1251,7 @@ int scsi_prep_state_check(struct scsi_device *sdev, struct request *req) break; case SDEV_QUIESCE: case SDEV_BLOCK: + case SDEV_CREATED_BLOCK: /* * If the devices is blocked we defer normal commands. */ @@ -2064,10 +2065,13 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) switch (state) { case SDEV_CREATED: - /* There are no legal states that come back to - * created. This is the manually initialised start - * state */ - goto illegal; + switch (oldstate) { + case SDEV_CREATED_BLOCK: + break; + default: + goto illegal; + } + break; case SDEV_RUNNING: switch (oldstate) { @@ -2105,8 +2109,17 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) case SDEV_BLOCK: switch (oldstate) { - case SDEV_CREATED: case SDEV_RUNNING: + case SDEV_CREATED_BLOCK: + break; + default: + goto illegal; + } + break; + + case SDEV_CREATED_BLOCK: + switch (oldstate) { + case SDEV_CREATED: break; default: goto illegal; @@ -2394,8 +2407,12 @@ scsi_internal_device_block(struct scsi_device *sdev) int err = 0; err = scsi_device_set_state(sdev, SDEV_BLOCK); - if (err) - return err; + if (err) { + err = scsi_device_set_state(sdev, SDEV_CREATED_BLOCK); + + if (err) + return err; + } /* * The device has transitioned to SDEV_BLOCK. Stop the @@ -2438,8 +2455,12 @@ scsi_internal_device_unblock(struct scsi_device *sdev) * and goose the device queue if successful. */ err = scsi_device_set_state(sdev, SDEV_RUNNING); - if (err) - return err; + if (err) { + err = scsi_device_set_state(sdev, SDEV_CREATED); + + if (err) + return err; + } spin_lock_irqsave(q->queue_lock, flags); blk_start_queue(q); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 2926baaac31e..334862e26a1b 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -730,6 +730,8 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, int *bflags, int async) { + int ret; + /* * XXX do not save the inquiry, since it can change underneath us, * save just vendor/model/rev. @@ -885,7 +887,17 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, /* set the device running here so that slave configure * may do I/O */ - scsi_device_set_state(sdev, SDEV_RUNNING); + ret = scsi_device_set_state(sdev, SDEV_RUNNING); + if (ret) { + ret = scsi_device_set_state(sdev, SDEV_BLOCK); + + if (ret) { + sdev_printk(KERN_ERR, sdev, + "in wrong state %s to complete scan\n", + scsi_device_state_name(sdev->sdev_state)); + return SCSI_SCAN_NO_RESPONSE; + } + } if (*bflags & BLIST_MS_192_BYTES_FOR_3F) sdev->use_192_bytes_for_3f = 1; @@ -899,7 +911,7 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result, transport_configure_device(&sdev->sdev_gendev); if (sdev->host->hostt->slave_configure) { - int ret = sdev->host->hostt->slave_configure(sdev); + ret = sdev->host->hostt->slave_configure(sdev); if (ret) { /* * if LLDD reports slave not present, don't clutter diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ab3c71869be5..09d311d559d1 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -34,6 +34,7 @@ static const struct { { SDEV_QUIESCE, "quiesce" }, { SDEV_OFFLINE, "offline" }, { SDEV_BLOCK, "blocked" }, + { SDEV_CREATED_BLOCK, "created-blocked" }, }; const char *scsi_device_state_name(enum scsi_device_state state) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index cc46652e4658..b49e725be039 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -42,9 +42,11 @@ enum scsi_device_state { * originate in the mid-layer) */ SDEV_OFFLINE, /* Device offlined (by error handling or * user request */ - SDEV_BLOCK, /* Device blocked by scsi lld. No scsi - * commands from user or midlayer should be issued - * to the scsi lld. */ + SDEV_BLOCK, /* Device blocked by scsi lld. No + * scsi commands from user or midlayer + * should be issued to the scsi + * lld. */ + SDEV_CREATED_BLOCK, /* same as above but for created devices */ }; enum scsi_device_event { @@ -393,11 +395,13 @@ static inline int scsi_device_online(struct scsi_device *sdev) } static inline int scsi_device_blocked(struct scsi_device *sdev) { - return sdev->sdev_state == SDEV_BLOCK; + return sdev->sdev_state == SDEV_BLOCK || + sdev->sdev_state == SDEV_CREATED_BLOCK; } static inline int scsi_device_created(struct scsi_device *sdev) { - return sdev->sdev_state == SDEV_CREATED; + return sdev->sdev_state == SDEV_CREATED || + sdev->sdev_state == SDEV_CREATED_BLOCK; } /* accessor functions for the SCSI parameters */ -- cgit v1.2.3 From 114f1ea408944f9515e45aa7452238fb15a1fa00 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 25 Aug 2008 13:51:58 -0700 Subject: [SCSI] scsi_dh: no need to initialize rq->cmd with blk_get_request blk_get_request initializes rq->cmd (rq_init does) so the users don't need to do that. Signed-off-by: FUJITA Tomonori Signed-off-by: Chandra Seetharaman Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh_emc.c | 1 - drivers/scsi/device_handler/scsi_dh_hp_sw.c | 2 -- drivers/scsi/device_handler/scsi_dh_rdac.c | 2 -- 3 files changed, 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index ef693e8412e9..2c1167541726 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -278,7 +278,6 @@ static struct request *get_req(struct scsi_device *sdev, int cmd, return NULL; } - memset(rq->cmd, 0, BLK_MAX_CDB); rq->cmd_len = COMMAND_SIZE(cmd); rq->cmd[0] = cmd; diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index a6a4ef3ad51c..5e93c88ad66b 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -114,7 +114,6 @@ static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h) req->cmd_type = REQ_TYPE_BLOCK_PC; req->cmd_flags |= REQ_FAILFAST; req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); - memset(req->cmd, 0, MAX_COMMAND_SIZE); req->cmd[0] = TEST_UNIT_READY; req->timeout = HP_SW_TIMEOUT; req->sense = h->sense; @@ -207,7 +206,6 @@ static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) req->cmd_type = REQ_TYPE_BLOCK_PC; req->cmd_flags |= REQ_FAILFAST; req->cmd_len = COMMAND_SIZE(START_STOP); - memset(req->cmd, 0, MAX_COMMAND_SIZE); req->cmd[0] = START_STOP; req->cmd[4] = 1; /* Start spin cycle */ req->timeout = HP_SW_TIMEOUT; diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 6e2f130d56de..1ba989e5a25a 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -225,8 +225,6 @@ static struct request *get_rdac_req(struct scsi_device *sdev, return NULL; } - memset(rq->cmd, 0, BLK_MAX_CDB); - rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE; rq->retries = RDAC_RETRIES; -- cgit v1.2.3 From 7404ad3b6d04efbd918e9e2e776bf560fbedf47d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 31 Aug 2008 10:41:52 -0500 Subject: [SCSI] sd: use generic helper to print capacities in both binary and SI Signed-off-by: James Bottomley --- drivers/scsi/sd.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e5e7d7856454..af9e406a3ad8 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -1429,27 +1430,21 @@ got_data: */ sector_size = 512; } + blk_queue_hardsect_size(sdp->request_queue, sector_size); + { - /* - * The msdos fs needs to know the hardware sector size - * So I have created this table. See ll_rw_blk.c - * Jacques Gelinas (Jacques@solucorp.qc.ca) - */ - int hard_sector = sector_size; - sector_t sz = (sdkp->capacity/2) * (hard_sector/256); - struct request_queue *queue = sdp->request_queue; - sector_t mb = sz; + char cap_str_2[10], cap_str_10[10]; + u64 sz = sdkp->capacity << ffz(~sector_size); - blk_queue_hardsect_size(queue, hard_sector); - /* avoid 64-bit division on 32-bit platforms */ - sector_div(sz, 625); - mb -= sz - 974; - sector_div(mb, 1950); + string_get_size(sz, STRING_UNITS_2, cap_str_2, + sizeof(cap_str_2)); + string_get_size(sz, STRING_UNITS_10, cap_str_10, + sizeof(cap_str_10)); sd_printk(KERN_NOTICE, sdkp, - "%llu %d-byte hardware sectors (%llu MB)\n", + "%llu %d-byte hardware sectors: (%s/%s)\n", (unsigned long long)sdkp->capacity, - hard_sector, (unsigned long long)mb); + sector_size, cap_str_10, cap_str_2); } /* Rescale capacity to 512-byte units */ -- cgit v1.2.3 From a30c3f69e6336cb9b09a989595e417367e4e9b1b Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Fri, 18 Jul 2008 08:32:52 -0700 Subject: [SCSI] fc_transport: Add an API to allow an LLD to create vports There's already a fc_vport_termintate() call exported by the transport. This patch adds a symmetric call to the API to allow an NPIV-capable LLD to instantiate vports sans user intervention. Additional comments/updates: Re: scsi_fc_transport.txt Add a function prototype for fc_vport_terminate similar to what's done for fc_vport_create Re: fc_vport_create I recommend we pass the channel number in fc_vport_create rather than fixing it at zero. Also, ids->vport_type should be set to FC_PORTTYPE_NPIV prior to calling fc_vport_create. The comment is also meaningless. Added-by and Signed-off-by: James Smart Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- Documentation/scsi/scsi_fc_transport.txt | 36 +++++++++++++++++++++ drivers/scsi/scsi_transport_fc.c | 54 +++++++++++++++----------------- include/scsi/scsi_transport_fc.h | 23 +++++++++++++- 3 files changed, 84 insertions(+), 29 deletions(-) (limited to 'drivers/scsi') diff --git a/Documentation/scsi/scsi_fc_transport.txt b/Documentation/scsi/scsi_fc_transport.txt index 75143f0c23b6..38d324d62b25 100644 --- a/Documentation/scsi/scsi_fc_transport.txt +++ b/Documentation/scsi/scsi_fc_transport.txt @@ -436,6 +436,42 @@ Other: was updated to remove all vports for the fc_host as well. +Transport supplied functions +---------------------------- + +The following functions are supplied by the FC-transport for use by LLDs. + + fc_vport_create - create a vport + fc_vport_terminate - detach and remove a vport + +Details: + +/** + * fc_vport_create - Admin App or LLDD requests creation of a vport + * @shost: scsi host the virtual port is connected to. + * @ids: The world wide names, FC4 port roles, etc for + * the virtual port. + * + * Notes: + * This routine assumes no locks are held on entry. + */ +struct fc_vport * +fc_vport_create(struct Scsi_Host *shost, struct fc_vport_identifiers *ids) + +/** + * fc_vport_terminate - Admin App or LLDD requests termination of a vport + * @vport: fc_vport to be terminated + * + * Calls the LLDD vport_delete() function, then deallocates and removes + * the vport from the shost and object tree. + * + * Notes: + * This routine assumes no locks are held on entry. + */ +int +fc_vport_terminate(struct fc_vport *vport) + + Credits ======= The following people have contributed to this document: diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 56823fd1fb84..cb971f0b784d 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -40,31 +40,7 @@ static int fc_queue_work(struct Scsi_Host *, struct work_struct *); static void fc_vport_sched_delete(struct work_struct *work); - -/* - * This is a temporary carrier for creating a vport. It will eventually - * be replaced by a real message definition for sgio or netlink. - * - * fc_vport_identifiers: This set of data contains all elements - * to uniquely identify and instantiate a FC virtual port. - * - * Notes: - * symbolic_name: The driver is to append the symbolic_name string data - * to the symbolic_node_name data that it generates by default. - * the resulting combination should then be registered with the switch. - * It is expected that things like Xen may stuff a VM title into - * this field. - */ -struct fc_vport_identifiers { - u64 node_name; - u64 port_name; - u32 roles; - bool disable; - enum fc_port_type vport_type; /* only FC_PORTTYPE_NPIV allowed */ - char symbolic_name[FC_VPORT_SYMBOLIC_NAMELEN]; -}; - -static int fc_vport_create(struct Scsi_Host *shost, int channel, +static int fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, struct fc_vport_identifiers *ids, struct fc_vport **vport); @@ -1760,7 +1736,7 @@ store_fc_host_vport_create(struct device *dev, struct device_attribute *attr, vid.disable = false; /* always enabled */ /* we only allow support on Channel 0 !!! */ - stat = fc_vport_create(shost, 0, &shost->shost_gendev, &vid, &vport); + stat = fc_vport_setup(shost, 0, &shost->shost_gendev, &vid, &vport); return stat ? stat : count; } static FC_DEVICE_ATTR(host, vport_create, S_IWUSR, NULL, @@ -3103,7 +3079,7 @@ fc_scsi_scan_rport(struct work_struct *work) /** - * fc_vport_create - allocates and creates a FC virtual port. + * fc_vport_setup - allocates and creates a FC virtual port. * @shost: scsi host the virtual port is connected to. * @channel: Channel on shost port connected to. * @pdev: parent device for vport @@ -3118,7 +3094,7 @@ fc_scsi_scan_rport(struct work_struct *work) * This routine assumes no locks are held on entry. */ static int -fc_vport_create(struct Scsi_Host *shost, int channel, struct device *pdev, +fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, struct fc_vport_identifiers *ids, struct fc_vport **ret_vport) { struct fc_host_attrs *fc_host = shost_to_fc_host(shost); @@ -3231,6 +3207,28 @@ delete_vport: return error; } +/** + * fc_vport_create - Admin App or LLDD requests creation of a vport + * @shost: scsi host the virtual port is connected to. + * @channel: channel on shost port connected to. + * @ids: The world wide names, FC4 port roles, etc for + * the virtual port. + * + * Notes: + * This routine assumes no locks are held on entry. + */ +struct fc_vport * +fc_vport_create(struct Scsi_Host *shost, int channel, + struct fc_vport_identifiers *ids) +{ + int stat; + struct fc_vport *vport; + + stat = fc_vport_setup(shost, channel, &shost->shost_gendev, + ids, &vport); + return stat ? NULL : vport; +} +EXPORT_SYMBOL(fc_vport_create); /** * fc_vport_terminate - Admin App or LLDD requests termination of a vport diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 878373c32ef7..21018a4df452 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -167,6 +167,26 @@ enum fc_tgtid_binding_type { struct device_attribute dev_attr_vport_##_name = \ __ATTR(_name,_mode,_show,_store) +/* + * fc_vport_identifiers: This set of data contains all elements + * to uniquely identify and instantiate a FC virtual port. + * + * Notes: + * symbolic_name: The driver is to append the symbolic_name string data + * to the symbolic_node_name data that it generates by default. + * the resulting combination should then be registered with the switch. + * It is expected that things like Xen may stuff a VM title into + * this field. + */ +#define FC_VPORT_SYMBOLIC_NAMELEN 64 +struct fc_vport_identifiers { + u64 node_name; + u64 port_name; + u32 roles; + bool disable; + enum fc_port_type vport_type; /* only FC_PORTTYPE_NPIV allowed */ + char symbolic_name[FC_VPORT_SYMBOLIC_NAMELEN]; +}; /* * FC Virtual Port Attributes @@ -197,7 +217,6 @@ struct device_attribute dev_attr_vport_##_name = \ * managed by the transport w/o driver interaction. */ -#define FC_VPORT_SYMBOLIC_NAMELEN 64 struct fc_vport { /* Fixed Attributes */ @@ -732,6 +751,8 @@ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, * be sure to read the Vendor Type and ID formatting requirements * specified in scsi_netlink.h */ +struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, + struct fc_vport_identifiers *); int fc_vport_terminate(struct fc_vport *vport); #endif /* SCSI_TRANSPORT_FC_H */ -- cgit v1.2.3 From ac26d41dee65167109e7cdcd0289b44ca61cd741 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 6 Sep 2008 08:39:15 -0500 Subject: [SCSI] libiscsi: return error passed in during iscsi recovery Due to patch building error on my side, we are still passing DID_BUS_BUSY for commands that are running, when we want to return whatever the caller of fail_all_commands wanted. This replaces the hardcoded error code with the value that is passed in. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 299e075a7b34..0b7457d558f7 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1456,7 +1456,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun, if (lun == task->sc->device->lun || lun == -1) { debug_scsi("failing in progress sc %p itt 0x%x\n", task->sc, task->itt); - fail_command(conn, task, DID_BUS_BUSY << 16); + fail_command(conn, task, error << 16); } } } -- cgit v1.2.3 From 49fd462a1ba4a1b9bfbfe01d279d506017d85492 Mon Sep 17 00:00:00 2001 From: Harish Zunjarrao Date: Thu, 11 Sep 2008 21:22:47 -0700 Subject: [SCSI] qla2xxx: Add input/output byte-count statistics. Currently Firmware does not have counters for input megabytes and output megabytes, therefore driver counts these values depending on the status of the scsi command and direction of the command. The values are exported in the FC_HOST path. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_attr.c | 2 ++ drivers/scsi/qla2xxx/qla_def.h | 2 ++ drivers/scsi/qla2xxx/qla_iocb.c | 24 +++++++++++++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 45e7dcb4b34d..866b0a8b83a1 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1065,6 +1065,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat->dumped_frames = stats->dumped_frames; pfc_host_stat->nos_count = stats->nos_rcvd; } + pfc_host_stat->fcp_input_megabytes = ha->qla_stats.input_bytes >> 20; + pfc_host_stat->fcp_output_megabytes = ha->qla_stats.output_bytes >> 20; done_free: dma_pool_free(ha->s_dma_pool, stats, stats_dma); diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 431c19e83001..d1160d229d10 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2156,6 +2156,8 @@ struct qla_chip_state_84xx { struct qla_statistics { uint32_t total_isp_aborts; + uint64_t input_bytes; + uint64_t output_bytes; }; /* diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index d57669aa4615..9778f0b63ec1 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -21,17 +21,22 @@ static void qla2x00_isp_cmd(scsi_qla_host_t *ha); * Returns the proper CF_* direction based on CDB. */ static inline uint16_t -qla2x00_get_cmd_direction(struct scsi_cmnd *cmd) +qla2x00_get_cmd_direction(srb_t *sp) { uint16_t cflags; cflags = 0; /* Set transfer direction */ - if (cmd->sc_data_direction == DMA_TO_DEVICE) + if (sp->cmd->sc_data_direction == DMA_TO_DEVICE) { cflags = CF_WRITE; - else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + sp->fcport->ha->qla_stats.output_bytes += + scsi_bufflen(sp->cmd); + } else if (sp->cmd->sc_data_direction == DMA_FROM_DEVICE) { cflags = CF_READ; + sp->fcport->ha->qla_stats.input_bytes += + scsi_bufflen(sp->cmd); + } return (cflags); } @@ -169,7 +174,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, ha = sp->ha; - cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd)); + cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Three DSDs are available in the Command Type 2 IOCB */ avail_dsds = 3; @@ -228,7 +233,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, ha = sp->ha; - cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(cmd)); + cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp)); /* Two DSDs are available in the Command Type 3 IOCB */ avail_dsds = 2; @@ -625,12 +630,17 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, ha = sp->ha; /* Set transfer direction */ - if (cmd->sc_data_direction == DMA_TO_DEVICE) + if (cmd->sc_data_direction == DMA_TO_DEVICE) { cmd_pkt->task_mgmt_flags = __constant_cpu_to_le16(TMF_WRITE_DATA); - else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + sp->fcport->ha->qla_stats.output_bytes += + scsi_bufflen(sp->cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { cmd_pkt->task_mgmt_flags = __constant_cpu_to_le16(TMF_READ_DATA); + sp->fcport->ha->qla_stats.input_bytes += + scsi_bufflen(sp->cmd); + } /* One DSD is available in the Command Type 3 IOCB */ avail_dsds = 1; -- cgit v1.2.3 From 4b89258c7320bab4155b692e76ae9ffdd85e79be Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 21:22:48 -0700 Subject: [SCSI] qla2xxx: Change GFP_ATOMIC to GFP_KERNEL for non-atomic allocations. Both call-sites are sleeping-capable. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_init.c | 2 +- drivers/scsi/qla2xxx/qla_mbx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index ee89ddd64aae..57f24df626d9 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2561,7 +2561,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) rval = QLA_SUCCESS; /* Try GID_PT to get device list, else GAN. */ - swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_ATOMIC); + swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_KERNEL); if (!swl) { /*EMPTY*/ DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback " diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 813bc7784c0a..ef15ad580404 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1995,7 +1995,7 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map) char *pmap; dma_addr_t pmap_dma; - pmap = dma_pool_alloc(ha->s_dma_pool, GFP_ATOMIC, &pmap_dma); + pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma); if (pmap == NULL) { DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****", __func__, ha->host_no)); -- cgit v1.2.3 From c00d8994d91e51aa6b891ad0e877f66cc1011de2 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 21:22:49 -0700 Subject: [SCSI] qla2xxx: Add Flash Layout Table support. The Flash Layout Table (FLT) present on many recent HBAs encodes flash usage information, organizes data stored into separate regions and presents the information uniformly to the driver. Use this information rather than using specific hard-coded values based on ISP type. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_attr.c | 7 +- drivers/scsi/qla2xxx/qla_def.h | 8 +- drivers/scsi/qla2xxx/qla_fw.h | 45 +++++++- drivers/scsi/qla2xxx/qla_gbl.h | 2 +- drivers/scsi/qla2xxx/qla_init.c | 10 +- drivers/scsi/qla2xxx/qla_os.c | 2 - drivers/scsi/qla2xxx/qla_sup.c | 242 +++++++++++++++++++++++++++++++++++----- 7 files changed, 272 insertions(+), 44 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 866b0a8b83a1..0ddfe7106b3b 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -292,10 +292,11 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, valid = 0; if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) valid = 1; - else if (start == (FA_BOOT_CODE_ADDR*4) || - start == (FA_RISC_CODE_ADDR*4)) + else if (start == (ha->flt_region_boot * 4) || + start == (ha->flt_region_fw * 4)) valid = 1; - else if (IS_QLA25XX(ha) && start == (FA_VPD_NVRAM_ADDR*4)) + else if (IS_QLA25XX(ha) && + start == (ha->flt_region_vpd_nvram * 4)) valid = 1; if (!valid) { qla_printk(KERN_WARNING, ha, diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index d1160d229d10..880e71127b73 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2508,7 +2508,6 @@ typedef struct scsi_qla_host { uint64_t fce_wr, fce_rd; struct mutex fce_mutex; - uint32_t hw_event_start; uint32_t hw_event_ptr; uint32_t hw_event_pause_errors; @@ -2554,6 +2553,13 @@ typedef struct scsi_qla_host { uint32_t fdt_unprotect_sec_cmd; uint32_t fdt_protect_sec_cmd; + uint32_t flt_region_flt; + uint32_t flt_region_fdt; + uint32_t flt_region_boot; + uint32_t flt_region_fw; + uint32_t flt_region_vpd_nvram; + uint32_t flt_region_hw_event; + /* Needed for BEACON */ uint16_t beacon_blink_led; uint8_t beacon_color_state; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index cf194517400d..2ec986bf8344 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -789,12 +789,16 @@ struct device_reg_24xx { #define FA_RISC_CODE_ADDR 0x20000 #define FA_RISC_CODE_SEGMENTS 2 +#define FA_FLASH_DESCR_ADDR_24 0x11000 +#define FA_FLASH_LAYOUT_ADDR_24 0x11400 + #define FA_FW_AREA_ADDR 0x40000 #define FA_VPD_NVRAM_ADDR 0x48000 #define FA_FEATURE_ADDR 0x4C000 #define FA_FLASH_DESCR_ADDR 0x50000 +#define FA_FLASH_LAYOUT_ADDR 0x50400 #define FA_HW_EVENT0_ADDR 0x54000 -#define FA_HW_EVENT1_ADDR 0x54200 +#define FA_HW_EVENT1_ADDR 0x54400 #define FA_HW_EVENT_SIZE 0x200 #define FA_HW_EVENT_ENTRY_SIZE 4 /* @@ -806,10 +810,6 @@ struct device_reg_24xx { #define HW_EVENT_NVRAM_CHKSUM_ERR 0xF023 #define HW_EVENT_FLASH_FW_ERR 0xF024 -#define FA_BOOT_LOG_ADDR 0x58000 -#define FA_FW_DUMP0_ADDR 0x60000 -#define FA_FW_DUMP1_ADDR 0x70000 - uint32_t flash_data; /* Flash/NVRAM BIOS data. */ uint32_t ctrl_status; /* Control/Status. */ @@ -1203,6 +1203,41 @@ struct qla_fdt_layout { uint8_t unused2[65]; }; +/* Flash Layout Table ********************************************************/ + +struct qla_flt_location { + uint8_t sig[4]; + uint32_t start_lo; + uint32_t start_hi; + uint16_t unused; + uint16_t checksum; +}; + +struct qla_flt_header { + uint16_t version; + uint16_t length; + uint16_t checksum; + uint16_t unused; +}; + +#define FLT_REG_FW 0x01 +#define FLT_REG_BOOT_CODE 0x07 +#define FLT_REG_VPD_0 0x14 +#define FLT_REG_NVRAM_0 0x15 +#define FLT_REG_VPD_1 0x16 +#define FLT_REG_NVRAM_1 0x17 +#define FLT_REG_FDT 0x1a +#define FLT_REG_FLT 0x1c +#define FLT_REG_HW_EVENT_0 0x1d +#define FLT_REG_HW_EVENT_1 0x1f + +struct qla_flt_region { + uint32_t code; + uint32_t size; + uint32_t start; + uint32_t end; +}; + /* 84XX Support **************************************************************/ #define MBA_ISP84XX_ALERT 0x800f /* Alert Notification. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 0b156735e9a6..dbd9f93890e8 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -313,7 +313,7 @@ extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, uint16_t, uint16_t); -extern void qla2xxx_get_flash_info(scsi_qla_host_t *); +extern int qla2xxx_get_flash_info(scsi_qla_host_t *); extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); /* diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 57f24df626d9..20847fb15bdb 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -83,6 +83,13 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) ha->isp_ops->reset_chip(ha); + rval = qla2xxx_get_flash_info(ha); + if (rval) { + DEBUG2(printk("scsi(%ld): Unable to validate FLASH data.\n", + ha->host_no)); + return (rval); + } + ha->isp_ops->get_flash_version(ha, ha->request_ring); qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); @@ -109,7 +116,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) rval = qla2x00_setup_chip(ha); if (rval) return (rval); - qla2xxx_get_flash_info(ha); } if (IS_QLA84XX(ha)) { ha->cs84xx = qla84xx_get_chip(ha); @@ -3751,7 +3757,7 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; - faddr = FA_RISC_CODE_ADDR; + faddr = ha->flt_region_fw; dcode = (uint32_t *)ha->request_ring; *srisc_addr = 0; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 6d0f0e5f2827..c1ffc3070ebe 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1663,8 +1663,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->gid_list_info_size = 8; ha->optrom_size = OPTROM_SIZE_25XX; ha->isp_ops = &qla25xx_isp_ops; - ha->hw_event_start = PCI_FUNC(pdev->devfn) ? - FA_HW_EVENT1_ADDR: FA_HW_EVENT0_ADDR; } host->can_queue = ha->request_q_length + 128; diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 1bca74474935..6de1e3bc62a8 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -543,23 +543,186 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, } } -void -qla2xxx_get_flash_info(scsi_qla_host_t *ha) +static int +qla2xxx_find_flt_start(scsi_qla_host_t *ha, uint32_t *start) +{ + const char *loc, *locations[] = { "DEF", "PCI" }; + uint32_t pcihdr, pcids; + uint32_t *dcode; + uint8_t *buf, *bcode, last_image; + uint16_t cnt, chksum, *wptr; + struct qla_flt_location *fltl; + + /* + * FLT-location structure resides after the last PCI region. + */ + + /* Begin with sane defaults. */ + loc = locations[0]; + *start = IS_QLA24XX_TYPE(ha) ? FA_FLASH_LAYOUT_ADDR_24: + FA_FLASH_LAYOUT_ADDR; + + /* Begin with first PCI expansion ROM header. */ + buf = (uint8_t *)ha->request_ring; + dcode = (uint32_t *)ha->request_ring; + pcihdr = 0; + last_image = 1; + do { + /* Verify PCI expansion ROM header. */ + qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20); + bcode = buf + (pcihdr % 4); + if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) + goto end; + + /* Locate PCI data structure. */ + pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); + qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20); + bcode = buf + (pcihdr % 4); + + /* Validate signature of PCI data structure. */ + if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || + bcode[0x2] != 'I' || bcode[0x3] != 'R') + goto end; + + last_image = bcode[0x15] & BIT_7; + + /* Locate next PCI expansion ROM. */ + pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512; + } while (!last_image); + + /* Now verify FLT-location structure. */ + fltl = (struct qla_flt_location *)ha->request_ring; + qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, + sizeof(struct qla_flt_location) >> 2); + if (fltl->sig[0] != 'Q' || fltl->sig[1] != 'F' || + fltl->sig[2] != 'L' || fltl->sig[3] != 'T') + goto end; + + wptr = (uint16_t *)ha->request_ring; + cnt = sizeof(struct qla_flt_location) >> 1; + for (chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + qla_printk(KERN_ERR, ha, + "Inconsistent FLTL detected: checksum=0x%x.\n", chksum); + qla2x00_dump_buffer(buf, sizeof(struct qla_flt_location)); + return QLA_FUNCTION_FAILED; + } + + /* Good data. Use specified location. */ + loc = locations[1]; + *start = le16_to_cpu(fltl->start_hi) << 16 | + le16_to_cpu(fltl->start_lo); +end: + DEBUG2(qla_printk(KERN_DEBUG, ha, "FLTL[%s] = 0x%x.\n", loc, *start)); + return QLA_SUCCESS; +} + +static void +qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr) +{ + const char *loc, *locations[] = { "DEF", "FLT" }; + uint16_t *wptr; + uint16_t cnt, chksum; + uint32_t start; + struct qla_flt_header *flt; + struct qla_flt_region *region; + + ha->flt_region_flt = flt_addr; + wptr = (uint16_t *)ha->request_ring; + flt = (struct qla_flt_header *)ha->request_ring; + region = (struct qla_flt_region *)&flt[1]; + ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring, + flt_addr << 2, OPTROM_BURST_SIZE); + if (*wptr == __constant_cpu_to_le16(0xffff)) + goto no_flash_data; + if (flt->version != __constant_cpu_to_le16(1)) { + DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported FLT detected: " + "version=0x%x length=0x%x checksum=0x%x.\n", + le16_to_cpu(flt->version), le16_to_cpu(flt->length), + le16_to_cpu(flt->checksum))); + goto no_flash_data; + } + + cnt = (sizeof(struct qla_flt_header) + le16_to_cpu(flt->length)) >> 1; + for (chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FLT detected: " + "version=0x%x length=0x%x checksum=0x%x.\n", + le16_to_cpu(flt->version), le16_to_cpu(flt->length), + chksum)); + goto no_flash_data; + } + + loc = locations[1]; + cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region); + for ( ; cnt; cnt--, region++) { + /* Store addresses as DWORD offsets. */ + start = le32_to_cpu(region->start) >> 2; + + DEBUG3(qla_printk(KERN_DEBUG, ha, "FLT[%02x]: start=0x%x " + "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), start, + le32_to_cpu(region->end) >> 2, le32_to_cpu(region->size))); + + switch (le32_to_cpu(region->code)) { + case FLT_REG_FW: + ha->flt_region_fw = start; + break; + case FLT_REG_BOOT_CODE: + ha->flt_region_boot = start; + break; + case FLT_REG_VPD_0: + ha->flt_region_vpd_nvram = start; + break; + case FLT_REG_FDT: + ha->flt_region_fdt = start; + break; + case FLT_REG_HW_EVENT_0: + if (!PCI_FUNC(ha->pdev->devfn)) + ha->flt_region_hw_event = start; + break; + case FLT_REG_HW_EVENT_1: + if (PCI_FUNC(ha->pdev->devfn)) + ha->flt_region_hw_event = start; + break; + } + } + goto done; + +no_flash_data: + /* Use hardcoded defaults. */ + loc = locations[0]; + ha->flt_region_fw = FA_RISC_CODE_ADDR; + ha->flt_region_boot = FA_BOOT_CODE_ADDR; + ha->flt_region_vpd_nvram = FA_VPD_NVRAM_ADDR; + ha->flt_region_fdt = IS_QLA24XX_TYPE(ha) ? FA_FLASH_DESCR_ADDR_24: + FA_FLASH_DESCR_ADDR; + ha->flt_region_hw_event = !PCI_FUNC(ha->pdev->devfn) ? + FA_HW_EVENT0_ADDR: FA_HW_EVENT1_ADDR; +done: + DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x " + "vpd_nvram=0x%x fdt=0x%x flt=0x%x hwe=0x%x.\n", loc, + ha->flt_region_boot, ha->flt_region_fw, ha->flt_region_vpd_nvram, + ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_hw_event)); +} + +static void +qla2xxx_get_fdt_info(scsi_qla_host_t *ha) { #define FLASH_BLK_SIZE_32K 0x8000 #define FLASH_BLK_SIZE_64K 0x10000 + const char *loc, *locations[] = { "MID", "FDT" }; uint16_t cnt, chksum; uint16_t *wptr; struct qla_fdt_layout *fdt; uint8_t man_id, flash_id; - - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) - return; + uint16_t mid, fid; wptr = (uint16_t *)ha->request_ring; fdt = (struct qla_fdt_layout *)ha->request_ring; ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring, - FA_FLASH_DESCR_ADDR << 2, OPTROM_BURST_SIZE); + ha->flt_region_fdt << 2, OPTROM_BURST_SIZE); if (*wptr == __constant_cpu_to_le16(0xffff)) goto no_flash_data; if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' || @@ -577,7 +740,10 @@ qla2xxx_get_flash_info(scsi_qla_host_t *ha) goto no_flash_data; } - ha->fdt_odd_index = le16_to_cpu(fdt->man_id) == 0x1f; + loc = locations[1]; + mid = le16_to_cpu(fdt->man_id); + fid = le16_to_cpu(fdt->id); + ha->fdt_odd_index = mid == 0x1f; ha->fdt_wrt_disable = fdt->wrt_disable_bits; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); ha->fdt_block_size = le32_to_cpu(fdt->block_size); @@ -588,16 +754,12 @@ qla2xxx_get_flash_info(scsi_qla_host_t *ha) flash_conf_to_access_addr(0x0300 | fdt->protect_sec_cmd): flash_conf_to_access_addr(0x0336); } - - DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[FDT]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", - le16_to_cpu(fdt->man_id), le16_to_cpu(fdt->id), ha->fdt_erase_cmd, - ha->fdt_protect_sec_cmd, ha->fdt_unprotect_sec_cmd, - ha->fdt_odd_index, ha->fdt_wrt_disable, ha->fdt_block_size)); - return; - + goto done; no_flash_data: + loc = locations[0]; qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); + mid = man_id; + fid = flash_id; ha->fdt_wrt_disable = 0x9c; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x03d8); switch (man_id) { @@ -625,14 +787,33 @@ no_flash_data: ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; } - - DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[MID]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", man_id, flash_id, +done: + DEBUG2(qla_printk(KERN_DEBUG, ha, "FDT[%s]: (0x%x/0x%x) erase=0x%x " + "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", loc, mid, fid, ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, ha->fdt_unprotect_sec_cmd, ha->fdt_odd_index, ha->fdt_wrt_disable, ha->fdt_block_size)); } +int +qla2xxx_get_flash_info(scsi_qla_host_t *ha) +{ + int ret; + uint32_t flt_addr; + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + return QLA_SUCCESS; + + ret = qla2xxx_find_flt_start(ha, &flt_addr); + if (ret != QLA_SUCCESS) + return ret; + + qla2xxx_get_flt_info(ha, flt_addr); + qla2xxx_get_fdt_info(ha); + + return QLA_SUCCESS; +} + static void qla24xx_unprotect_flash(scsi_qla_host_t *ha) { @@ -920,7 +1101,8 @@ qla25xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, - flash_data_to_access_addr(FA_VPD_NVRAM_ADDR | naddr))); + flash_data_to_access_addr(ha->flt_region_vpd_nvram | + naddr))); return buf; } @@ -935,10 +1117,10 @@ qla25xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, dbuf = vmalloc(RMW_BUFFER_SIZE); if (!dbuf) return QLA_MEMORY_ALLOC_FAILED; - ha->isp_ops->read_optrom(ha, dbuf, FA_VPD_NVRAM_ADDR << 2, + ha->isp_ops->read_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2, RMW_BUFFER_SIZE); memcpy(dbuf + (naddr << 2), buf, bytes); - ha->isp_ops->write_optrom(ha, dbuf, FA_VPD_NVRAM_ADDR << 2, + ha->isp_ops->write_optrom(ha, dbuf, ha->flt_region_vpd_nvram << 2, RMW_BUFFER_SIZE); vfree(dbuf); @@ -2166,7 +2348,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) memset(dbyte, 0, 8); dcode = (uint16_t *)dbyte; - qla2x00_read_flash_data(ha, dbyte, FA_RISC_CODE_ADDR * 4 + 10, + qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10, 8); DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n", __func__, ha->host_no)); @@ -2177,7 +2359,7 @@ qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { DEBUG2(printk("%s(): Unrecognized fw revision at " - "%x.\n", __func__, FA_RISC_CODE_ADDR * 4)); + "%x.\n", __func__, ha->flt_region_fw * 4)); } else { /* values are in big endian */ ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1]; @@ -2212,7 +2394,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) dcode = mbuf; /* Begin with first PCI expansion ROM header. */ - pcihdr = 0; + pcihdr = ha->flt_region_boot; last_image = 1; do { /* Verify PCI expansion ROM header. */ @@ -2282,7 +2464,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); dcode = mbuf; - qla24xx_read_flash_data(ha, dcode, FA_RISC_CODE_ADDR + 4, 4); + qla24xx_read_flash_data(ha, dcode, ha->flt_region_fw + 4, 4); for (i = 0; i < 4; i++) dcode[i] = be32_to_cpu(dcode[i]); @@ -2291,7 +2473,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { DEBUG2(printk("%s(): Unrecognized fw version at %x.\n", - __func__, FA_RISC_CODE_ADDR)); + __func__, ha->flt_region_fw)); } else { ha->fw_revision[0] = dcode[0]; ha->fw_revision[1] = dcode[1]; @@ -2355,7 +2537,7 @@ qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata) /* Locate first empty entry. */ for (;;) { if (ha->hw_event_ptr >= - ha->hw_event_start + FA_HW_EVENT_SIZE) { + ha->flt_region_hw_event + FA_HW_EVENT_SIZE) { DEBUG2(qla_printk(KERN_WARNING, ha, "HW event -- Log Full!\n")); return QLA_MEMORY_ALLOC_FAILED; @@ -2391,7 +2573,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1, int rval; uint32_t marker[2], fdata[4]; - if (ha->hw_event_start == 0) + if (ha->flt_region_hw_event == 0) return QLA_FUNCTION_FAILED; DEBUG2(qla_printk(KERN_WARNING, ha, @@ -2406,7 +2588,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1, QLA_DRIVER_PATCH_VER, QLA_DRIVER_BETA_VER); /* Locate marker. */ - ha->hw_event_ptr = ha->hw_event_start; + ha->hw_event_ptr = ha->flt_region_hw_event; for (;;) { qla24xx_read_flash_data(ha, fdata, ha->hw_event_ptr, 4); @@ -2415,7 +2597,7 @@ qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1, break; ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; if (ha->hw_event_ptr >= - ha->hw_event_start + FA_HW_EVENT_SIZE) { + ha->flt_region_hw_event + FA_HW_EVENT_SIZE) { DEBUG2(qla_printk(KERN_WARNING, ha, "HW event -- Log Full!\n")); return QLA_MEMORY_ALLOC_FAILED; -- cgit v1.2.3 From 272976ca186982f7bbc4f22876c53d6c9f7b6e32 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 21:22:50 -0700 Subject: [SCSI] qla2xxx: Add NPIV-Config Table support. To instatiate pre-configured vport entities defined within an HBA's flash memory. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_def.h | 2 + drivers/scsi/qla2xxx/qla_fw.h | 26 +++++++++++ drivers/scsi/qla2xxx/qla_gbl.h | 2 + drivers/scsi/qla2xxx/qla_os.c | 7 +++ drivers/scsi/qla2xxx/qla_sup.c | 98 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 133 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 880e71127b73..83c819216771 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2239,6 +2239,7 @@ typedef struct scsi_qla_host { #define FCPORT_UPDATE_NEEDED 27 #define VP_DPC_NEEDED 28 /* wake up for VP dpc handling */ #define UNLOADING 29 +#define NPIV_CONFIG_NEEDED 30 uint32_t device_flags; #define DFLG_LOCAL_DEVICES BIT_0 @@ -2559,6 +2560,7 @@ typedef struct scsi_qla_host { uint32_t flt_region_fw; uint32_t flt_region_vpd_nvram; uint32_t flt_region_hw_event; + uint32_t flt_region_npiv_conf; /* Needed for BEACON */ uint16_t beacon_blink_led; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 2ec986bf8344..d1d14202575a 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -791,6 +791,8 @@ struct device_reg_24xx { #define FA_FLASH_DESCR_ADDR_24 0x11000 #define FA_FLASH_LAYOUT_ADDR_24 0x11400 +#define FA_NPIV_CONF0_ADDR_24 0x16000 +#define FA_NPIV_CONF1_ADDR_24 0x17000 #define FA_FW_AREA_ADDR 0x40000 #define FA_VPD_NVRAM_ADDR 0x48000 @@ -801,6 +803,9 @@ struct device_reg_24xx { #define FA_HW_EVENT1_ADDR 0x54400 #define FA_HW_EVENT_SIZE 0x200 #define FA_HW_EVENT_ENTRY_SIZE 4 +#define FA_NPIV_CONF0_ADDR 0x5C000 +#define FA_NPIV_CONF1_ADDR 0x5D000 + /* * Flash Error Log Event Codes. */ @@ -1230,6 +1235,8 @@ struct qla_flt_header { #define FLT_REG_FLT 0x1c #define FLT_REG_HW_EVENT_0 0x1d #define FLT_REG_HW_EVENT_1 0x1f +#define FLT_REG_NPIV_CONF_0 0x29 +#define FLT_REG_NPIV_CONF_1 0x2a struct qla_flt_region { uint32_t code; @@ -1238,6 +1245,25 @@ struct qla_flt_region { uint32_t end; }; +/* Flash NPIV Configuration Table ********************************************/ + +struct qla_npiv_header { + uint8_t sig[2]; + uint16_t version; + uint16_t entries; + uint16_t unused[4]; + uint16_t checksum; +}; + +struct qla_npiv_entry { + uint16_t flags; + uint16_t vf_id; + uint16_t qos; + uint16_t unused1; + uint8_t port_name[WWN_SIZE]; + uint8_t node_name[WWN_SIZE]; +}; + /* 84XX Support **************************************************************/ #define MBA_ISP84XX_ALERT 0x800f /* Alert Notification. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index dbd9f93890e8..753dbe6cce6e 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -316,6 +316,8 @@ extern int qla2xxx_hw_event_log(scsi_qla_host_t *, uint16_t , uint16_t, extern int qla2xxx_get_flash_info(scsi_qla_host_t *); extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); +extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *); + /* * Global Function Prototypes in qla_dbg.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c1ffc3070ebe..3433441b956a 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1517,6 +1517,7 @@ qla2xxx_scan_start(struct Scsi_Host *shost) set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); set_bit(RSCN_UPDATE, &ha->dpc_flags); + set_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags); } static int @@ -2431,6 +2432,12 @@ qla2x00_do_dpc(void *data) ha->host_no)); } + if (test_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags) && + atomic_read(&ha->loop_state) == LOOP_READY) { + clear_bit(NPIV_CONFIG_NEEDED, &ha->dpc_flags); + qla2xxx_flash_npiv_conf(ha); + } + if (!ha->interrupts_on) ha->isp_ops->enable_intrs(ha); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 6de1e3bc62a8..e2432ef1ecea 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -686,6 +686,14 @@ qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr) if (PCI_FUNC(ha->pdev->devfn)) ha->flt_region_hw_event = start; break; + case FLT_REG_NPIV_CONF_0: + if (!PCI_FUNC(ha->pdev->devfn)) + ha->flt_region_npiv_conf = start; + break; + case FLT_REG_NPIV_CONF_1: + if (PCI_FUNC(ha->pdev->devfn)) + ha->flt_region_npiv_conf = start; + break; } } goto done; @@ -700,11 +708,15 @@ no_flash_data: FA_FLASH_DESCR_ADDR; ha->flt_region_hw_event = !PCI_FUNC(ha->pdev->devfn) ? FA_HW_EVENT0_ADDR: FA_HW_EVENT1_ADDR; + ha->flt_region_npiv_conf = !PCI_FUNC(ha->pdev->devfn) ? + (IS_QLA24XX_TYPE(ha) ? FA_NPIV_CONF0_ADDR_24: FA_NPIV_CONF0_ADDR): + (IS_QLA24XX_TYPE(ha) ? FA_NPIV_CONF1_ADDR_24: FA_NPIV_CONF1_ADDR); done: DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x " - "vpd_nvram=0x%x fdt=0x%x flt=0x%x hwe=0x%x.\n", loc, + "vpd_nvram=0x%x fdt=0x%x flt=0x%x hwe=0x%x npiv=0x%x.\n", loc, ha->flt_region_boot, ha->flt_region_fw, ha->flt_region_vpd_nvram, - ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_hw_event)); + ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_hw_event, + ha->flt_region_npiv_conf)); } static void @@ -814,6 +826,88 @@ qla2xxx_get_flash_info(scsi_qla_host_t *ha) return QLA_SUCCESS; } +void +qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha) +{ +#define NPIV_CONFIG_SIZE (16*1024) + void *data; + uint16_t *wptr; + uint16_t cnt, chksum; + struct qla_npiv_header hdr; + struct qla_npiv_entry *entry; + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + return; + + ha->isp_ops->read_optrom(ha, (uint8_t *)&hdr, + ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); + if (hdr.version == __constant_cpu_to_le16(0xffff)) + return; + if (hdr.version != __constant_cpu_to_le16(1)) { + DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported NPIV-Config " + "detected: version=0x%x entries=0x%x checksum=0x%x.\n", + le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries), + le16_to_cpu(hdr.checksum))); + return; + } + + data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL); + if (!data) { + DEBUG2(qla_printk(KERN_INFO, ha, "NPIV-Config: Unable to " + "allocate memory.\n")); + return; + } + + ha->isp_ops->read_optrom(ha, (uint8_t *)data, + ha->flt_region_npiv_conf << 2, NPIV_CONFIG_SIZE); + + cnt = (sizeof(struct qla_npiv_header) + le16_to_cpu(hdr.entries) * + sizeof(struct qla_npiv_entry)) >> 1; + for (wptr = data, chksum = 0; cnt; cnt--) + chksum += le16_to_cpu(*wptr++); + if (chksum) { + DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent NPIV-Config " + "detected: version=0x%x entries=0x%x checksum=0x%x.\n", + le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries), + chksum)); + goto done; + } + + entry = data + sizeof(struct qla_npiv_header); + cnt = le16_to_cpu(hdr.entries); + for ( ; cnt; cnt--, entry++) { + uint16_t flags; + struct fc_vport_identifiers vid; + struct fc_vport *vport; + + flags = le16_to_cpu(entry->flags); + if (flags == 0xffff) + continue; + if ((flags & BIT_0) == 0) + continue; + + memset(&vid, 0, sizeof(vid)); + vid.roles = FC_PORT_ROLE_FCP_INITIATOR; + vid.vport_type = FC_PORTTYPE_NPIV; + vid.disable = false; + vid.port_name = wwn_to_u64(entry->port_name); + vid.node_name = wwn_to_u64(entry->node_name); + + DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx " + "wwnn=%llx vf_id=0x%x qos=0x%x.\n", cnt, vid.port_name, + vid.node_name, le16_to_cpu(entry->vf_id), + le16_to_cpu(entry->qos))); + + vport = fc_vport_create(ha->host, 0, &vid); + if (!vport) + qla_printk(KERN_INFO, ha, "NPIV-Config: Failed to " + "create vport [%02x]: wwpn=%llx wwnn=%llx.\n", cnt, + vid.port_name, vid.node_name); + } +done: + kfree(data); +} + static void qla24xx_unprotect_flash(scsi_qla_host_t *ha) { -- cgit v1.2.3 From cc3ef7bc40bbede7bbd0bb395d6452a575e95efe Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 11 Sep 2008 21:22:51 -0700 Subject: [SCSI] qla2xxx: Fix "occured" spelling errors. Fix "occured" spelling errors. Most of these are in comments, which I wouldn't normally bother with, but a couple are in printks, which irritate me more. So I just fixed them all at the same time. Signed-off-by: Bjorn Helgaas Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_init.c | 2 +- drivers/scsi/qla2xxx/qla_inline.h | 2 +- drivers/scsi/qla2xxx/qla_iocb.c | 6 +++--- drivers/scsi/qla2xxx/qla_isr.c | 8 ++++---- drivers/scsi/qla2xxx/qla_mbx.c | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 20847fb15bdb..a470f2d3270d 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2022,7 +2022,7 @@ qla2x00_configure_loop(scsi_qla_host_t *ha) DEBUG3(printk("%s: exiting normally\n", __func__)); } - /* Restore state if a resync event occured during processing */ + /* Restore state if a resync event occurred during processing */ if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) { if (test_bit(LOCAL_LOOP_UPDATE, &save_flags)) set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 92fafbdbbaab..e90afad120ee 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -52,7 +52,7 @@ to_qla_parent(scsi_qla_host_t *ha) * @ha: HA context * @ha_locked: is function called with the hardware lock * - * Returns non-zero if a failure occured, else zero. + * Returns non-zero if a failure occurred, else zero. */ static inline int qla2x00_issue_marker(scsi_qla_host_t *ha, int ha_locked) diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 9778f0b63ec1..85bc0a48598b 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -267,7 +267,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, * qla2x00_start_scsi() - Send a SCSI command to the ISP * @sp: command to send to the ISP * - * Returns non-zero if a failure occured, else zero. + * Returns non-zero if a failure occurred, else zero. */ int qla2x00_start_scsi(srb_t *sp) @@ -412,7 +412,7 @@ queuing_error: * * Can be called from both normal and interrupt context. * - * Returns non-zero if a failure occured, else zero. + * Returns non-zero if a failure occurred, else zero. */ int __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, @@ -676,7 +676,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, * qla24xx_start_scsi() - Send a SCSI command to the ISP * @sp: command to send to the ISP * - * Returns non-zero if a failure occured, else zero. + * Returns non-zero if a failure occurred, else zero. */ int qla24xx_start_scsi(srb_t *sp) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index bf41887cdd65..92cab5c35be5 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -391,9 +391,9 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) break; case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ - DEBUG2(printk("scsi(%ld): LIP occured (%x).\n", ha->host_no, + DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", ha->host_no, mb[1])); - qla_printk(KERN_INFO, ha, "LIP occured (%x).\n", mb[1]); + qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); @@ -460,7 +460,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n", ha->host_no, mb[1])); qla_printk(KERN_INFO, ha, - "LIP reset occured (%x).\n", mb[1]); + "LIP reset occurred (%x).\n", mb[1]); if (atomic_read(&ha->loop_state) != LOOP_DOWN) { atomic_set(&ha->loop_state, LOOP_DOWN); @@ -543,7 +543,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) case MBA_PORT_UPDATE: /* Port database update */ /* - * If PORT UPDATE is global (recieved LIP_OCCURED/LIP_RESET + * If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET * event etc. earlier indicating loop is down) then process * it. Otherwise ignore it and Wait for RSCN to come in. */ diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index ef15ad580404..36bc6851e23d 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -233,7 +233,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) DEBUG2_3_11(printk("%s(%ld): timeout schedule " "isp_abort_needed.\n", __func__, ha->host_no)); qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occured. Scheduling ISP " + "Mailbox command timeout occurred. Scheduling ISP " "abort.\n"); set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); qla2xxx_wake_dpc(ha); @@ -244,7 +244,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) DEBUG2_3_11(printk("%s(%ld): timeout calling " "abort_isp\n", __func__, ha->host_no)); qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occured. Issuing ISP " + "Mailbox command timeout occurred. Issuing ISP " "abort.\n"); set_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); -- cgit v1.2.3 From 2d136938792ba04bf3b6bb2d3a2807dc05c396fc Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 21:22:52 -0700 Subject: [SCSI] qla2xxx: Additional residual-count corrections during UNDERRUN handling. Add additional tightening of residual-count handling (originally from commit 6acf8190025e9c4ea513d4084ff089d476112816) where the driver should discard any lower SCSI-status during firmware/transport residual-count mismatches. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 92cab5c35be5..9eb6ef333044 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1060,8 +1060,9 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) resid = resid_len; /* Use F/W calculated residual length. */ if (IS_FWI2_CAPABLE(ha)) { - if (scsi_status & SS_RESIDUAL_UNDER && - resid != fw_resid_len) { + if (!(scsi_status & SS_RESIDUAL_UNDER)) { + lscsi_status = 0; + } else if (resid != fw_resid_len) { scsi_status &= ~SS_RESIDUAL_UNDER; lscsi_status = 0; } -- cgit v1.2.3 From 59d72d873ccfaf59e9ceea1487459f5a57c0d504 Mon Sep 17 00:00:00 2001 From: Ravi Anand Date: Thu, 11 Sep 2008 21:22:53 -0700 Subject: [SCSI] qla2xxx: Ignore payload reserved-bits during RSCN processing. As the driver is only interested in bits 0-9 of the 1st RSCN-payload word: rsvd[15:14]RscnEventQualifier[13:10]Fmt[9:8]Domain[7:0] Area[15:8]Alpa[7:0] Signed-off-by: Ravi Anand Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 9eb6ef333044..fc4bfa7f839c 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -589,7 +589,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) "scsi(%ld): RSCN database changed -- %04x %04x %04x.\n", ha->host_no, mb[1], mb[2], mb[3])); - rscn_entry = (mb[1] << 16) | mb[2]; + rscn_entry = ((mb[1] & 0xff) << 16) | mb[2]; host_pid = (ha->d_id.b.domain << 16) | (ha->d_id.b.area << 8) | ha->d_id.b.al_pa; if (rscn_entry == host_pid) { @@ -600,6 +600,8 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) break; } + /* Ignore reserved bits from RSCN-payload. */ + rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2]; rscn_queue_index = ha->rscn_in_ptr + 1; if (rscn_queue_index == MAX_RSCN_COUNT) rscn_queue_index = 0; -- cgit v1.2.3 From 36d1cdcffbbb33b966d6bdd6534bb03c22b3fd0c Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 11 Sep 2008 21:22:54 -0700 Subject: [SCSI] qla2xxx: Update version number to 8.02.01-k8. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 4160e4caa7b9..be5e299df528 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k7" +#define QLA2XXX_VERSION "8.02.01-k8" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 -- cgit v1.2.3 From 4a3e1ea8b2f25f962e91883be880de678bad8e3e Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 18 Sep 2008 04:09:57 +0400 Subject: [SCSI] qla2xxx: fix printk format warnings Signed-off-by: Alexander Beregalov Acked-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_sup.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index e2432ef1ecea..90a13211717f 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -894,15 +894,17 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *ha) vid.node_name = wwn_to_u64(entry->node_name); DEBUG2(qla_printk(KERN_DEBUG, ha, "NPIV[%02x]: wwpn=%llx " - "wwnn=%llx vf_id=0x%x qos=0x%x.\n", cnt, vid.port_name, - vid.node_name, le16_to_cpu(entry->vf_id), - le16_to_cpu(entry->qos))); + "wwnn=%llx vf_id=0x%x qos=0x%x.\n", cnt, + (unsigned long long)vid.port_name, + (unsigned long long)vid.node_name, + le16_to_cpu(entry->vf_id), le16_to_cpu(entry->qos))); vport = fc_vport_create(ha->host, 0, &vid); if (!vport) qla_printk(KERN_INFO, ha, "NPIV-Config: Failed to " "create vport [%02x]: wwpn=%llx wwnn=%llx.\n", cnt, - vid.port_name, vid.node_name); + (unsigned long long)vid.port_name, + (unsigned long long)vid.node_name); } done: kfree(data); -- cgit v1.2.3 From b78ded89156827b50729518b1d13fea6defb46ee Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 22 Sep 2008 14:56:49 -0700 Subject: [SCSI] qla2xxx: remove the unused SCSI_QLOGIC_FC_FIRMWARE option This option was forgotten when the SCSI_QLOGIC_FC driver was removed. Reported-by: Robert P. J. Day Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/Kconfig | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4e0322b1c1ea..d3b211af4e1c 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1325,14 +1325,6 @@ config SCSI_QLOGIC_FAS To compile this driver as a module, choose M here: the module will be called qlogicfas. -config SCSI_QLOGIC_FC_FIRMWARE - bool "Include loadable firmware in driver" - depends on SCSI_QLOGIC_FC - help - Say Y to include ISP2X00 Fabric Initiator/Target Firmware, with - expanded LUN addressing and FcTape (FCP-2) support, in the - qlogicfc driver. This is required on some platforms. - config SCSI_QLOGIC_1280 tristate "Qlogic QLA 1240/1x80/1x160 SCSI support" depends on PCI && SCSI -- cgit v1.2.3 From 650849d71ca05d55a1553fe42fb21af9dce5612b Mon Sep 17 00:00:00 2001 From: "Yanqing_Liu@Dell.com" Date: Thu, 2 Oct 2008 12:18:33 -0500 Subject: [SCSI] scsi_dh: add Dell product information into rdac device handler Add Dell Powervault storage arrays into device list of rdac device handler. Signed-off-by: Yanqing Liu Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh_rdac.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 1ba989e5a25a..50bf95f3b5c4 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -588,6 +588,8 @@ static const struct scsi_dh_devlist rdac_dev_list[] = { {"STK", "OPENstorage D280"}, {"SUN", "CSM200_R"}, {"SUN", "LCSM100_F"}, + {"DELL", "MD3000"}, + {"DELL", "MD3000i"}, {NULL, NULL}, }; -- cgit v1.2.3 From 7ae628d9d21a088b4a2d26a9d39c29c0acd2d03b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 23 Sep 2008 07:58:59 -0700 Subject: [SCSI] scsi_dh: suppress comparison warning On Mon, 2008-09-22 at 14:56 -0700, akpm@linux-foundation.org wrote: > From: Andrew Morton > > s390: > > drivers/scsi/device_handler/scsi_dh_emc.c: In function 'parse_sp_info_reply': > drivers/scsi/device_handler/scsi_dh_emc.c:179: warning: comparison is always false due to limited range of data type > > because chars are unsigned, I assume. Fix by making csdev->buffer explicitly an unsigned char and dropping the < 0 test. Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh_emc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 2c1167541726..8f45570a8a01 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -84,7 +84,7 @@ struct clariion_dh_data { /* * I/O buffer for both MODE_SELECT and INQUIRY commands. */ - char buffer[CLARIION_BUFFER_SIZE]; + unsigned char buffer[CLARIION_BUFFER_SIZE]; /* * SCSI sense buffer for commands -- assumes serial issuance * and completion sequence of all commands for same multipath. @@ -176,7 +176,7 @@ static int parse_sp_info_reply(struct scsi_device *sdev, err = SCSI_DH_DEV_TEMP_BUSY; goto out; } - if (csdev->buffer[4] < 0 || csdev->buffer[4] > 2) { + if (csdev->buffer[4] > 2) { /* Invalid buffer format */ sdev_printk(KERN_NOTICE, sdev, "%s: invalid VPD page 0xC0 format\n", -- cgit v1.2.3 From f331c0296f2a9fee0d396a70598b954062603015 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 3 Sep 2008 09:01:48 +0200 Subject: block: don't depend on consecutive minor space * Implement disk_devt() and part_devt() and use them to directly access devt instead of computing it from ->major and ->first_minor. Note that all references to ->major and ->first_minor outside of block layer is used to determine devt of the disk (the part0) and as ->major and ->first_minor will continue to represent devt for the disk, converting these users aren't strictly necessary. However, convert them for consistency. * Implement disk_max_parts() to avoid directly deferencing genhd->minors. * Update bdget_disk() such that it doesn't assume consecutive minor space. * Move devt computation from register_disk() to add_disk() and make it the only one (all other usages use the initially determined value). These changes clean up the code and will help disk->part dereference fix and extended block device numbers. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 107 +++++++++++++++++++++++++----------- block/ioctl.c | 6 +- drivers/block/pktcdvd.c | 2 +- drivers/block/ps3disk.c | 2 +- drivers/char/random.c | 6 +- drivers/md/dm-ioctl.c | 4 +- drivers/md/dm-stripe.c | 4 +- drivers/md/dm.c | 7 ++- drivers/memstick/core/mspro_block.c | 2 +- drivers/mmc/card/block.c | 2 +- drivers/s390/block/dasd_proc.c | 3 +- drivers/s390/block/dcssblk.c | 4 +- drivers/scsi/sr.c | 2 +- fs/block_dev.c | 2 +- fs/partitions/check.c | 19 ++++--- include/linux/genhd.h | 27 +++++++-- 16 files changed, 132 insertions(+), 67 deletions(-) (limited to 'drivers/scsi') diff --git a/block/genhd.c b/block/genhd.c index dc9ad4c171e2..fa32d09fda24 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -186,13 +186,14 @@ void add_disk(struct gendisk *disk) int retval; disk->flags |= GENHD_FL_UP; - blk_register_region(MKDEV(disk->major, disk->first_minor), - disk->minors, NULL, exact_match, exact_lock, disk); + disk->dev.devt = MKDEV(disk->major, disk->first_minor); + blk_register_region(disk_devt(disk), disk->minors, NULL, + exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); bdi = &disk->queue->backing_dev_info; - bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); + bdi_register_dev(bdi, disk_devt(disk)); retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); WARN_ON(retval); } @@ -205,8 +206,7 @@ void unlink_gendisk(struct gendisk *disk) sysfs_remove_link(&disk->dev.kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); - blk_unregister_region(MKDEV(disk->major, disk->first_minor), - disk->minors); + blk_unregister_region(disk_devt(disk), disk->minors); } /** @@ -225,6 +225,38 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) return kobj ? dev_to_disk(dev) : NULL; } +/** + * bdget_disk - do bdget() by gendisk and partition number + * @disk: gendisk of interest + * @partno: partition number + * + * Find partition @partno from @disk, do bdget() on it. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * Resulting block_device on success, NULL on failure. + */ +extern struct block_device *bdget_disk(struct gendisk *disk, int partno) +{ + dev_t devt = MKDEV(0, 0); + + if (partno == 0) + devt = disk_devt(disk); + else { + struct hd_struct *part = disk->part[partno - 1]; + + if (part && part->nr_sects) + devt = part_devt(part); + } + + if (likely(devt != MKDEV(0, 0))) + return bdget(devt); + return NULL; +} +EXPORT_SYMBOL(bdget_disk); + /* * print a full list of all partitions - intended for places where the root * filesystem can't be mounted and thus to give the victim some idea of what @@ -255,7 +287,7 @@ void __init printk_all_partitions(void) * option takes. */ printk("%02x%02x %10llu %s", - disk->major, disk->first_minor, + MAJOR(disk_devt(disk)), MINOR(disk_devt(disk)), (unsigned long long)get_capacity(disk) >> 1, disk_name(disk, 0, buf)); if (disk->driverfs_dev != NULL && @@ -266,15 +298,15 @@ void __init printk_all_partitions(void) printk(" (driver?)\n"); /* now show the partitions */ - for (n = 0; n < disk->minors - 1; ++n) { - if (disk->part[n] == NULL) - continue; - if (disk->part[n]->nr_sects == 0) + for (n = 0; n < disk_max_parts(disk); ++n) { + struct hd_struct *part = disk->part[n]; + + if (!part || !part->nr_sects) continue; printk(" %02x%02x %10llu %s\n", - disk->major, n + 1 + disk->first_minor, - (unsigned long long)disk->part[n]->nr_sects >> 1, - disk_name(disk, n + 1, buf)); + MAJOR(part_devt(part)), MINOR(part_devt(part)), + (unsigned long long)part->nr_sects >> 1, + disk_name(disk, part->partno, buf)); } } class_dev_iter_exit(&iter); @@ -343,26 +375,27 @@ static int show_partition(struct seq_file *seqf, void *v) char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ - if (!get_capacity(sgp) || - (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) + if (!get_capacity(sgp) || (!disk_max_parts(sgp) && + (sgp->flags & GENHD_FL_REMOVABLE))) return 0; if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) return 0; /* show the full disk and all non-0 size partitions of it */ seq_printf(seqf, "%4d %4d %10llu %s\n", - sgp->major, sgp->first_minor, + MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)), (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); - for (n = 0; n < sgp->minors - 1; n++) { - if (!sgp->part[n]) + for (n = 0; n < disk_max_parts(sgp); n++) { + struct hd_struct *part = sgp->part[n]; + if (!part) continue; - if (sgp->part[n]->nr_sects == 0) + if (part->nr_sects == 0) continue; seq_printf(seqf, "%4d %4d %10llu %s\n", - sgp->major, n + 1 + sgp->first_minor, - (unsigned long long)sgp->part[n]->nr_sects >> 1 , - disk_name(sgp, n + 1, buf)); + MAJOR(part_devt(part)), MINOR(part_devt(part)), + (unsigned long long)part->nr_sects >> 1, + disk_name(sgp, part->partno, buf)); } return 0; @@ -578,7 +611,8 @@ static int diskstats_show(struct seq_file *seqf, void *v) disk_round_stats(gp); preempt_enable(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", - gp->major, gp->first_minor, disk_name(gp, 0, buf), + MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)), + disk_name(gp, 0, buf), disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), (unsigned long long)disk_stat_read(gp, sectors[0]), jiffies_to_msecs(disk_stat_read(gp, ticks[0])), @@ -590,7 +624,7 @@ static int diskstats_show(struct seq_file *seqf, void *v) jiffies_to_msecs(disk_stat_read(gp, time_in_queue))); /* now show all non-0 size partitions of it */ - for (n = 0; n < gp->minors - 1; n++) { + for (n = 0; n < disk_max_parts(gp); n++) { struct hd_struct *hd = gp->part[n]; if (!hd || !hd->nr_sects) @@ -601,8 +635,8 @@ static int diskstats_show(struct seq_file *seqf, void *v) preempt_enable(); seq_printf(seqf, "%4d %4d %s %lu %lu %llu " "%u %lu %lu %llu %u %u %u %u\n", - gp->major, n + gp->first_minor + 1, - disk_name(gp, n + 1, buf), + MAJOR(part_devt(hd)), MINOR(part_devt(hd)), + disk_name(gp, hd->partno, buf), part_stat_read(hd, ios[0]), part_stat_read(hd, merges[0]), (unsigned long long)part_stat_read(hd, sectors[0]), @@ -661,11 +695,22 @@ dev_t blk_lookup_devt(const char *name, int partno) while ((dev = class_dev_iter_next(&iter))) { struct gendisk *disk = dev_to_disk(dev); - if (!strcmp(dev->bus_id, name) && partno < disk->minors) { - devt = MKDEV(MAJOR(dev->devt), - MINOR(dev->devt) + partno); - break; + if (strcmp(dev->bus_id, name)) + continue; + if (partno < 0 || partno > disk_max_parts(disk)) + continue; + + if (partno == 0) + devt = disk_devt(disk); + else { + struct hd_struct *part = disk->part[partno - 1]; + + if (!part || !part->nr_sects) + continue; + + devt = part_devt(part); } + break; } class_dev_iter_exit(&iter); return devt; @@ -755,7 +800,7 @@ void set_disk_ro(struct gendisk *disk, int flag) { int i; disk->policy = flag; - for (i = 0; i < disk->minors - 1; i++) + for (i = 0; i < disk_max_parts(disk); i++) if (disk->part[i]) disk->part[i]->policy = flag; } diff --git a/block/ioctl.c b/block/ioctl.c index d77f5e280a6e..403f7d7e0c28 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -29,7 +29,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user if (bdev != bdev->bd_contains) return -EINVAL; partno = p.pno; - if (partno <= 0 || partno >= disk->minors) + if (partno <= 0 || partno > disk_max_parts(disk)) return -EINVAL; switch (a.op) { case BLKPG_ADD_PARTITION: @@ -47,7 +47,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user mutex_lock(&bdev->bd_mutex); /* overlap? */ - for (i = 0; i < disk->minors - 1; i++) { + for (i = 0; i < disk_max_parts(disk); i++) { struct hd_struct *s = disk->part[i]; if (!s) @@ -96,7 +96,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = bdev->bd_disk; int res; - if (disk->minors == 1 || bdev != bdev->bd_contains) + if (!disk_max_parts(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 29b7a648cc6e..e1a90bbb4747 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2911,7 +2911,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) if (!disk->queue) goto out_mem2; - pd->pkt_dev = MKDEV(disk->major, disk->first_minor); + pd->pkt_dev = MKDEV(pktdev_major, idx); ret = pkt_new_dev(pd, dev); if (ret) goto out_new_dev; diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c index 4b0d6c7f4c66..936466f62afd 100644 --- a/drivers/block/ps3disk.c +++ b/drivers/block/ps3disk.c @@ -541,7 +541,7 @@ static int ps3disk_remove(struct ps3_system_bus_device *_dev) struct ps3disk_private *priv = dev->sbd.core.driver_data; mutex_lock(&ps3disk_mask_mutex); - __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS, + __clear_bit(MINOR(disk_devt(priv->gendisk)) / PS3DISK_MINORS, &ps3disk_mask); mutex_unlock(&ps3disk_mask_mutex); del_gendisk(priv->gendisk); diff --git a/drivers/char/random.c b/drivers/char/random.c index 7ce1ac4baa6d..6af435b89867 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -661,10 +661,10 @@ void add_disk_randomness(struct gendisk *disk) if (!disk || !disk->random) return; /* first major is 1, so we get >= 0x200 here */ - DEBUG_ENT("disk event %d:%d\n", disk->major, disk->first_minor); + DEBUG_ENT("disk event %d:%d\n", + MAJOR(disk_devt(disk)), MINOR(disk_devt(disk))); - add_timer_randomness(disk->random, - 0x100 + MKDEV(disk->major, disk->first_minor)); + add_timer_randomness(disk->random, 0x100 + disk_devt(disk)); } #endif diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index b262c0042de3..c3de311117a1 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -426,7 +426,7 @@ static int list_devices(struct dm_ioctl *param, size_t param_size) old_nl->next = (uint32_t) ((void *) nl - (void *) old_nl); disk = dm_disk(hc->md); - nl->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + nl->dev = huge_encode_dev(disk_devt(disk)); nl->next = 0; strcpy(nl->name, hc->name); @@ -539,7 +539,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) if (dm_suspended(md)) param->flags |= DM_SUSPEND_FLAG; - param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); + param->dev = huge_encode_dev(disk_devt(disk)); /* * Yes, this will be out of date by the time it gets back diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index 4de90ab3968b..b745d8ac625b 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -284,8 +284,8 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio, memset(major_minor, 0, sizeof(major_minor)); sprintf(major_minor, "%d:%d", - bio->bi_bdev->bd_disk->major, - bio->bi_bdev->bd_disk->first_minor); + MAJOR(disk_devt(bio->bi_bdev->bd_disk)), + MINOR(disk_devt(bio->bi_bdev->bd_disk))); /* * Test to see which stripe drive triggered the event diff --git a/drivers/md/dm.c b/drivers/md/dm.c index ace998ce59f6..a78caad29996 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1146,7 +1146,7 @@ static void unlock_fs(struct mapped_device *md); static void free_dev(struct mapped_device *md) { - int minor = md->disk->first_minor; + int minor = MINOR(disk_devt(md->disk)); if (md->suspended_bdev) { unlock_fs(md); @@ -1267,7 +1267,7 @@ static struct mapped_device *dm_find_md(dev_t dev) md = idr_find(&_minor_idr, minor); if (md && (md == MINOR_ALLOCED || - (dm_disk(md)->first_minor != minor) || + (MINOR(disk_devt(dm_disk(md))) != minor) || test_bit(DMF_FREEING, &md->flags))) { md = NULL; goto out; @@ -1318,7 +1318,8 @@ void dm_put(struct mapped_device *md) if (atomic_dec_and_lock(&md->holders, &_minor_lock)) { map = dm_get_table(md); - idr_replace(&_minor_idr, MINOR_ALLOCED, dm_disk(md)->first_minor); + idr_replace(&_minor_idr, MINOR_ALLOCED, + MINOR(disk_devt(dm_disk(md)))); set_bit(DMF_FREEING, &md->flags); spin_unlock(&_minor_lock); if (!dm_suspended(md)) { diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index d2d2318dafa4..82bf649ef138 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -197,7 +197,7 @@ static int mspro_block_bd_open(struct inode *inode, struct file *filp) static int mspro_block_disk_release(struct gendisk *disk) { struct mspro_block_data *msb = disk->private_data; - int disk_id = disk->first_minor >> MSPRO_BLOCK_PART_SHIFT; + int disk_id = MINOR(disk_devt(disk)) >> MSPRO_BLOCK_PART_SHIFT; mutex_lock(&mspro_block_disk_lock); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index ebc8b9d77613..97156b689e82 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -83,7 +83,7 @@ static void mmc_blk_put(struct mmc_blk_data *md) mutex_lock(&open_lock); md->usage--; if (md->usage == 0) { - int devidx = md->disk->first_minor >> MMC_SHIFT; + int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT; __clear_bit(devidx, dev_use); put_disk(md->disk); diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 03c0e40a92ff..e3b5c4d3036e 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -76,7 +76,8 @@ dasd_devices_show(struct seq_file *m, void *v) /* Print kdev. */ if (block->gdp) seq_printf(m, " at (%3d:%6d)", - block->gdp->major, block->gdp->first_minor); + MAJOR(disk_devt(block->gdp)), + MINOR(disk_devt(block->gdp))); else seq_printf(m, " at (???:??????)"); /* Print device name. */ diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 711b3004b3e6..9481e4a3f76e 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -114,7 +114,7 @@ dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info) found = 0; // test if minor available list_for_each_entry(entry, &dcssblk_devices, lh) - if (minor == entry->gd->first_minor) + if (minor == MINOR(disk_devt(entry->gd))) found++; if (!found) break; // got unused minor } @@ -397,7 +397,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char goto unload_seg; } sprintf(dev_info->gd->disk_name, "dcssblk%d", - dev_info->gd->first_minor); + MINOR(disk_devt(dev_info->gd))); list_add_tail(&dev_info->lh, &dcssblk_devices); if (!try_module_get(THIS_MODULE)) { diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 27f5bfd1def3..8dbe3798d5fd 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -878,7 +878,7 @@ static void sr_kref_release(struct kref *kref) struct gendisk *disk = cd->disk; spin_lock(&sr_index_lock); - clear_bit(disk->first_minor, sr_index_bits); + clear_bit(MINOR(disk_devt(disk)), sr_index_bits); spin_unlock(&sr_index_lock); unregister_cdrom(&cd->cdi); diff --git a/fs/block_dev.c b/fs/block_dev.c index de0776cd7215..72e0a2887cb7 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -892,7 +892,7 @@ int check_disk_change(struct block_device *bdev) if (bdops->revalidate_disk) bdops->revalidate_disk(bdev->bd_disk); - if (bdev->bd_disk->minors > 1) + if (disk_max_parts(bdev->bd_disk)) bdev->bd_invalidated = 1; return 1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index b86aab1b0df6..e77fa144a07d 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -134,7 +134,11 @@ char *disk_name(struct gendisk *hd, int partno, char *buf) const char *bdevname(struct block_device *bdev, char *buf) { - int partno = MINOR(bdev->bd_dev) - bdev->bd_disk->first_minor; + int partno = 0; + + if (bdev->bd_part) + partno = bdev->bd_part->partno; + return disk_name(bdev->bd_disk, partno, buf); } @@ -169,7 +173,7 @@ check_partition(struct gendisk *hd, struct block_device *bdev) if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); - state->limit = hd->minors; + state->limit = disk_max_parts(hd) + 1; i = res = err = 0; while (!res && check_part[i]) { memset(&state->parts, 0, sizeof(state->parts)); @@ -416,7 +420,6 @@ void register_disk(struct gendisk *disk) int err; disk->dev.parent = disk->driverfs_dev; - disk->dev.devt = MKDEV(disk->major, disk->first_minor); strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE); /* ewww... some of these buggers have / in the name... */ @@ -440,7 +443,7 @@ void register_disk(struct gendisk *disk) disk_sysfs_add_subdirs(disk); /* No minors to use for partitions */ - if (disk->minors == 1) + if (!disk_max_parts(disk)) goto exit; /* No such device (e.g., media were just removed) */ @@ -463,8 +466,8 @@ exit: kobject_uevent(&disk->dev.kobj, KOBJ_ADD); /* announce possible partitions */ - for (i = 1; i < disk->minors; i++) { - p = disk->part[i-1]; + for (i = 0; i < disk_max_parts(disk); i++) { + p = disk->part[i]; if (!p || !p->nr_sects) continue; kobject_uevent(&p->dev.kobj, KOBJ_ADD); @@ -482,7 +485,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (res) return res; bdev->bd_invalidated = 0; - for (p = 1; p < disk->minors; p++) + for (p = 1; p <= disk_max_parts(disk); p++) delete_partition(disk, p); if (disk->fops->revalidate_disk) disk->fops->revalidate_disk(disk); @@ -545,7 +548,7 @@ void del_gendisk(struct gendisk *disk) int p; /* invalidate stuff */ - for (p = disk->minors - 1; p > 0; p--) { + for (p = disk_max_parts(disk); p > 0; p--) { invalidate_partition(disk, p); delete_partition(disk, p); } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index d1723c0a8600..0ff75329199c 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -111,10 +111,14 @@ struct hd_struct { #define GENHD_FL_FAIL 64 struct gendisk { + /* major, first_minor and minors are input parameters only, + * don't use directly. Use disk_devt() and disk_max_parts(). + */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ + char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor - 1] */ struct block_device_operations *fops; @@ -152,6 +156,21 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) return NULL; } +static inline int disk_max_parts(struct gendisk *disk) +{ + return disk->minors - 1; +} + +static inline dev_t disk_devt(struct gendisk *disk) +{ + return disk->dev.devt; +} + +static inline dev_t part_devt(struct hd_struct *part) +{ + return part->dev.devt; +} + /* * Macros to operate on percpu disk statistics: * @@ -163,7 +182,7 @@ static inline struct hd_struct *disk_map_sector(struct gendisk *gendiskp, { struct hd_struct *part; int i; - for (i = 0; i < gendiskp->minors - 1; i++) { + for (i = 0; i < disk_max_parts(gendiskp); i++) { part = gendiskp->part[i]; if (part && part->start_sect <= sector && sector < part->start_sect + part->nr_sects) @@ -366,6 +385,7 @@ extern void add_disk(struct gendisk *disk); extern void del_gendisk(struct gendisk *gp); extern void unlink_gendisk(struct gendisk *gp); extern struct gendisk *get_gendisk(dev_t dev, int *partno); +extern struct block_device *bdget_disk(struct gendisk *disk, int partno); extern void set_device_ro(struct block_device *bdev, int flag); extern void set_disk_ro(struct gendisk *disk, int flag); @@ -553,11 +573,6 @@ extern void blk_register_region(dev_t devt, unsigned long range, void *data); extern void blk_unregister_region(dev_t devt, unsigned long range); -static inline struct block_device *bdget_disk(struct gendisk *disk, int partno) -{ - return bdget(MKDEV(disk->major, disk->first_minor) + partno); -} - #else /* CONFIG_BLOCK */ static inline void printk_all_partitions(void) { } -- cgit v1.2.3 From f615b48cc7df7cac3865ec76ac1a5bb04d3e07f4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:24 +0900 Subject: sd/ide-disk: apply extended minors to sd and ide Update sd and ide-disk such that they can take advantage of extended minors. ide-disk already has 64 minors per device and currently doesn't use extended minors although after this patch it can be turned on by simply tweaking constants. sd only had 16 minors per device causing problems on certain peculiar configurations. This patch lifts the restriction and enables it to use upto 64 minors. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- drivers/ide/ide-disk.c | 11 ++++++++--- drivers/scsi/sd.c | 9 +++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 07ef88bd109b..7a88de9ada29 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -41,6 +41,10 @@ #include #include +#define IDE_DISK_PARTS (1 << PARTN_BITS) +#define IDE_DISK_MINORS IDE_DISK_PARTS +#define IDE_DISK_EXT_MINORS (IDE_DISK_PARTS - IDE_DISK_MINORS) + struct ide_disk_obj { ide_drive_t *drive; ide_driver_t *driver; @@ -1151,8 +1155,8 @@ static int ide_disk_probe(ide_drive_t *drive) if (!idkp) goto failed; - g = alloc_disk_node(1 << PARTN_BITS, - hwif_to_node(drive->hwif)); + g = alloc_disk_ext_node(IDE_DISK_MINORS, IDE_DISK_EXT_MINORS, + hwif_to_node(drive->hwif)); if (!g) goto out_free_idkp; @@ -1178,7 +1182,8 @@ static int ide_disk_probe(ide_drive_t *drive) } else drive->attach = 1; - g->minors = 1 << PARTN_BITS; + g->minors = IDE_DISK_MINORS; + g->ext_minors = IDE_DISK_EXT_MINORS; g->driverfs_dev = &drive->gendev; g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; set_capacity(g, idedisk_capacity(drive)); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e5e7d7856454..d1bb0e1d2d28 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -86,6 +86,10 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK); MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); +#define SD_PARTS 64 +#define SD_MINORS 16 +#define SD_EXT_MINORS (SD_PARTS - SD_MINORS) + static int sd_revalidate_disk(struct gendisk *); static int sd_probe(struct device *); static int sd_remove(struct device *); @@ -1801,7 +1805,7 @@ static int sd_probe(struct device *dev) if (!sdkp) goto out; - gd = alloc_disk(16); + gd = alloc_disk_ext(SD_MINORS, SD_EXT_MINORS); if (!gd) goto out_free; @@ -1845,7 +1849,8 @@ static int sd_probe(struct device *dev) gd->major = sd_major((index & 0xf0) >> 4); gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); - gd->minors = 16; + gd->minors = SD_MINORS; + gd->ext_minors = SD_EXT_MINORS; gd->fops = &sd_fops; if (index < 26) { -- cgit v1.2.3 From 870d6656126add8e383645732b03df2b7ccd4f94 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:47:25 +0900 Subject: block: implement CONFIG_DEBUG_BLOCK_EXT_DEVT Extended devt introduces non-contiguos device numbers. This patch implements a debug option which forces most devt allocations to be from the extended area and spreads them out. This is enabled by default if DEBUG_KERNEL is set and achieves... 1. Detects code paths in kernel or userland which expect predetermined consecutive device numbers. 2. When something goes wrong, avoid corruption as adding to the minor of earlier partition won't lead to the wrong but valid device. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 38 +++++++++++++++++++++++++++++++++++--- drivers/ide/ide-disk.c | 6 ++++++ drivers/scsi/sd.c | 6 ++++++ lib/Kconfig.debug | 16 ++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/block/genhd.c b/block/genhd.c index ee4b13520e59..67e5a59ced2a 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -298,6 +298,38 @@ EXPORT_SYMBOL(unregister_blkdev); static struct kobj_map *bdev_map; +/** + * blk_mangle_minor - scatter minor numbers apart + * @minor: minor number to mangle + * + * Scatter consecutively allocated @minor number apart if MANGLE_DEVT + * is enabled. Mangling twice gives the original value. + * + * RETURNS: + * Mangled value. + * + * CONTEXT: + * Don't care. + */ +static int blk_mangle_minor(int minor) +{ +#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT + int i; + + for (i = 0; i < MINORBITS / 2; i++) { + int low = minor & (1 << i); + int high = minor & (1 << (MINORBITS - 1 - i)); + int distance = MINORBITS - 1 - 2 * i; + + minor ^= low | high; /* clear both bits */ + low <<= distance; /* swap the positions */ + high >>= distance; + minor |= low | high; /* and set */ + } +#endif + return minor; +} + /** * blk_alloc_devt - allocate a dev_t for a partition * @part: partition to allocate dev_t for @@ -339,7 +371,7 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) return -EBUSY; } - *devt = MKDEV(BLOCK_EXT_MAJOR, idx); + *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx)); return 0; } @@ -361,7 +393,7 @@ void blk_free_devt(dev_t devt) if (MAJOR(devt) == BLOCK_EXT_MAJOR) { mutex_lock(&ext_devt_mutex); - idr_remove(&ext_devt_idr, MINOR(devt)); + idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); mutex_unlock(&ext_devt_mutex); } } @@ -473,7 +505,7 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) struct hd_struct *part; mutex_lock(&ext_devt_mutex); - part = idr_find(&ext_devt_idr, MINOR(devt)); + part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); if (part && get_disk(part_to_disk(part))) { *partno = part->partno; disk = part_to_disk(part); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 7a88de9ada29..a072df5053ae 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -42,7 +42,13 @@ #include #define IDE_DISK_PARTS (1 << PARTN_BITS) + +#if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define IDE_DISK_MINORS IDE_DISK_PARTS +#else +#define IDE_DISK_MINORS 1 +#endif + #define IDE_DISK_EXT_MINORS (IDE_DISK_PARTS - IDE_DISK_MINORS) struct ide_disk_obj { diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d1bb0e1d2d28..280d231a86ed 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -87,7 +87,13 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); #define SD_PARTS 64 + +#if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 +#else +#define SD_MINORS 1 +#endif + #define SD_EXT_MINORS (SD_PARTS - SD_MINORS) static int sd_revalidate_disk(struct gendisk *); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 0b504814e378..5a536f703a83 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -624,6 +624,22 @@ config BACKTRACE_SELF_TEST Say N if you are unsure. +config DEBUG_BLOCK_EXT_DEVT + bool "Force extended block device numbers and spread them" + depends on DEBUG_KERNEL + depends on BLOCK + default y + help + Conventionally, block device numbers are allocated from + predetermined contiguous area. However, extended block area + may introduce non-contiguous block device numbers. This + option forces most block device numbers to be allocated from + the extended space and spreads them to discover kernel or + userland code paths which assume predetermined contiguous + device number allocation. + + Say N if you are unsure. + config LKDTM tristate "Linux Kernel Dump Test Tool Module" depends on DEBUG_KERNEL -- cgit v1.2.3 From 689d6fac40b41c7bf154f362deaf442548e4dc81 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:16 +0900 Subject: block: replace @ext_minors with GENHD_FL_EXT_DEVT With previous changes, it's meaningless to limit the number of partitions. Replace @ext_minors with GENHD_FL_EXT_DEVT such that setting the flag allows the disk to have maximum number of allowed partitions (only limited by the number of entries in parsed_partitions as determined by MAX_PART constant). This kills not-too-pretty alloc_disk_ext[_node]() functions and makes @minors parameter to alloc_disk[_node]() unnecessary. The parameter is left alone to avoid disturbing the users. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 16 +--------------- drivers/ide/ide-disk.c | 14 +++++--------- drivers/scsi/sd.c | 9 ++------- fs/partitions/check.h | 4 +--- include/linux/genhd.h | 16 ++++++++-------- 5 files changed, 17 insertions(+), 42 deletions(-) (limited to 'drivers/scsi') diff --git a/block/genhd.c b/block/genhd.c index c2b14aa69d58..eedab5b4685b 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1024,18 +1024,9 @@ struct gendisk *alloc_disk(int minors) { return alloc_disk_node(minors, -1); } +EXPORT_SYMBOL(alloc_disk); struct gendisk *alloc_disk_node(int minors, int node_id) -{ - return alloc_disk_ext_node(minors, 0, node_id); -} - -struct gendisk *alloc_disk_ext(int minors, int ext_minors) -{ - return alloc_disk_ext_node(minors, ext_minors, -1); -} - -struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) { struct gendisk *disk; @@ -1054,7 +1045,6 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) disk->part_tbl->part[0] = &disk->part0; disk->minors = minors; - disk->ext_minors = ext_minors; rand_initialize_disk(disk); disk_to_dev(disk)->class = &block_class; disk_to_dev(disk)->type = &disk_type; @@ -1065,11 +1055,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) } return disk; } - -EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(alloc_disk_node); -EXPORT_SYMBOL(alloc_disk_ext); -EXPORT_SYMBOL(alloc_disk_ext_node); struct kobject *get_disk(struct gendisk *disk) { diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index a072df5053ae..29c8ae752683 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -41,16 +41,12 @@ #include #include -#define IDE_DISK_PARTS (1 << PARTN_BITS) - #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) -#define IDE_DISK_MINORS IDE_DISK_PARTS +#define IDE_DISK_MINORS (1 << PARTN_BITS) #else #define IDE_DISK_MINORS 1 #endif -#define IDE_DISK_EXT_MINORS (IDE_DISK_PARTS - IDE_DISK_MINORS) - struct ide_disk_obj { ide_drive_t *drive; ide_driver_t *driver; @@ -1161,8 +1157,7 @@ static int ide_disk_probe(ide_drive_t *drive) if (!idkp) goto failed; - g = alloc_disk_ext_node(IDE_DISK_MINORS, IDE_DISK_EXT_MINORS, - hwif_to_node(drive->hwif)); + g = alloc_disk_node(IDE_DISK_MINORS, hwif_to_node(drive->hwif)); if (!g) goto out_free_idkp; @@ -1189,9 +1184,10 @@ static int ide_disk_probe(ide_drive_t *drive) drive->attach = 1; g->minors = IDE_DISK_MINORS; - g->ext_minors = IDE_DISK_EXT_MINORS; g->driverfs_dev = &drive->gendev; - g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; + g->flags |= GENHD_FL_EXT_DEVT; + if (drive->removable) + g->flags |= GENHD_FL_REMOVABLE; set_capacity(g, idedisk_capacity(drive)); g->fops = &idedisk_ops; add_disk(g); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 280d231a86ed..6598024531dd 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -86,16 +86,12 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK); MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); -#define SD_PARTS 64 - #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 #else #define SD_MINORS 1 #endif -#define SD_EXT_MINORS (SD_PARTS - SD_MINORS) - static int sd_revalidate_disk(struct gendisk *); static int sd_probe(struct device *); static int sd_remove(struct device *); @@ -1811,7 +1807,7 @@ static int sd_probe(struct device *dev) if (!sdkp) goto out; - gd = alloc_disk_ext(SD_MINORS, SD_EXT_MINORS); + gd = alloc_disk(SD_MINORS); if (!gd) goto out_free; @@ -1856,7 +1852,6 @@ static int sd_probe(struct device *dev) gd->major = sd_major((index & 0xf0) >> 4); gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); gd->minors = SD_MINORS; - gd->ext_minors = SD_EXT_MINORS; gd->fops = &sd_fops; if (index < 26) { @@ -1880,7 +1875,7 @@ static int sd_probe(struct device *dev) blk_queue_prep_rq(sdp->request_queue, sd_prep_fn); gd->driverfs_dev = &sdp->sdev_gendev; - gd->flags = GENHD_FL_DRIVERFS; + gd->flags = GENHD_FL_EXT_DEVT | GENHD_FL_DRIVERFS; if (sdp->removable) gd->flags |= GENHD_FL_REMOVABLE; diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 17ae8ecd9e8b..98dbe1a84528 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -5,15 +5,13 @@ * add_gd_partition adds a partitions details to the devices partition * description. */ -enum { MAX_PART = 256 }; - struct parsed_partitions { char name[BDEVNAME_SIZE]; struct { sector_t from; sector_t size; int flags; - } parts[MAX_PART]; + } parts[DISK_MAX_PARTS]; int next; int limit; }; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ecf649c3deed..04524c213de1 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -58,6 +58,8 @@ enum { UNIXWARE_PARTITION = 0x63, /* Same as GNU_HURD and SCO Unix */ }; +#define DISK_MAX_PARTS 256 + #include #include #include @@ -112,6 +114,7 @@ struct hd_struct { #define GENHD_FL_CD 8 #define GENHD_FL_UP 16 #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 +#define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ #define BLK_SCSI_MAX_CMDS (256) #define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) @@ -129,15 +132,13 @@ struct disk_part_tbl { }; struct gendisk { - /* major, first_minor, minors and ext_minors are input - * parameters only, don't use directly. Use disk_devt() and - * disk_max_parts(). + /* major, first_minor and minors are input parameters only, + * don't use directly. Use disk_devt() and disk_max_parts(). */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ - int ext_minors; /* number of extended dynamic minors */ char disk_name[32]; /* name of major driver */ @@ -180,7 +181,9 @@ static inline struct gendisk *part_to_disk(struct hd_struct *part) static inline int disk_max_parts(struct gendisk *disk) { - return disk->minors + disk->ext_minors; + if (disk->flags & GENHD_FL_EXT_DEVT) + return DISK_MAX_PARTS; + return disk->minors; } static inline bool disk_partitionable(struct gendisk *disk) @@ -527,9 +530,6 @@ extern void printk_all_partitions(void); extern struct gendisk *alloc_disk_node(int minors, int node_id); extern struct gendisk *alloc_disk(int minors); -extern struct gendisk *alloc_disk_ext_node(int minors, int ext_minrs, - int node_id); -extern struct gendisk *alloc_disk_ext(int minors, int ext_minors); extern struct kobject *get_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk); extern void blk_register_region(dev_t devt, unsigned long range, -- cgit v1.2.3 From 3e1a7ff8a0a7b948f2684930166954f9e8e776fe Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Aug 2008 19:56:17 +0900 Subject: block: allow disk to have extended device number Now that disk and partition handlings are mostly unified, it's easy to allow disk to have extended device number. This patch makes add_disk() use extended device number if disk->minors is zero. Both sd and ide-disk are updated to use this. * sd_format_disk_name() is implemented which can generically determine the drive name. This removes disk number restriction stemming from limited device names. * If sd index goes over SD_MAX_DISKS (which can be increased now BTW), sd simply doesn't initialize minors letting block layer choose extended device number. * If CONFIG_DEBUG_EXT_DEVT is set, both sd and ide-disk always set minors to 0 and use extended device numbers. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/genhd.c | 25 ++++++++++++++++- drivers/ide/ide-disk.c | 2 +- drivers/scsi/sd.c | 74 ++++++++++++++++++++++++++++++++++++-------------- fs/partitions/check.c | 1 + include/linux/genhd.h | 3 +- 5 files changed, 82 insertions(+), 23 deletions(-) (limited to 'drivers/scsi') diff --git a/block/genhd.c b/block/genhd.c index eedab5b4685b..d9de3e482d1e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -478,14 +478,37 @@ static int exact_lock(dev_t devt, void *data) * * This function registers the partitioning information in @disk * with the kernel. + * + * FIXME: error handling */ void add_disk(struct gendisk *disk) { struct backing_dev_info *bdi; + dev_t devt; int retval; + /* minors == 0 indicates to use ext devt from part0 and should + * be accompanied with EXT_DEVT flag. Make sure all + * parameters make sense. + */ + WARN_ON(disk->minors && !(disk->major || disk->first_minor)); + WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT)); + disk->flags |= GENHD_FL_UP; - disk_to_dev(disk)->devt = MKDEV(disk->major, disk->first_minor); + + retval = blk_alloc_devt(&disk->part0, &devt); + if (retval) { + WARN_ON(1); + return; + } + disk_to_dev(disk)->devt = devt; + + /* ->major and ->first_minor aren't supposed to be + * dereferenced from here on, but set them just in case. + */ + disk->major = MAJOR(devt); + disk->first_minor = MINOR(devt); + blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 29c8ae752683..33ea8c048717 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -44,7 +44,7 @@ #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define IDE_DISK_MINORS (1 << PARTN_BITS) #else -#define IDE_DISK_MINORS 1 +#define IDE_DISK_MINORS 0 #endif struct ide_disk_obj { diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 6598024531dd..bcb04b2a7676 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -89,7 +89,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 #else -#define SD_MINORS 1 +#define SD_MINORS 0 #endif static int sd_revalidate_disk(struct gendisk *); @@ -1769,6 +1769,52 @@ static int sd_revalidate_disk(struct gendisk *disk) return 0; } +/** + * sd_format_disk_name - format disk name + * @prefix: name prefix - ie. "sd" for SCSI disks + * @index: index of the disk to format name for + * @buf: output buffer + * @buflen: length of the output buffer + * + * SCSI disk names starts at sda. The 26th device is sdz and the + * 27th is sdaa. The last one for two lettered suffix is sdzz + * which is followed by sdaaa. + * + * This is basically 26 base counting with one extra 'nil' entry + * at the beggining from the second digit on and can be + * determined using similar method as 26 base conversion with the + * index shifted -1 after each digit is computed. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) +{ + const int base = 'z' - 'a' + 1; + char *begin = buf + strlen(prefix); + char *end = buf + buflen; + char *p; + int unit; + + p = end - 1; + *p = '\0'; + unit = base; + do { + if (p == begin) + return -EINVAL; + *--p = 'a' + (index % unit); + index = (index / unit) - 1; + } while (index >= 0); + + memmove(begin, p, end - p); + memcpy(buf, prefix, strlen(prefix)); + + return 0; +} + /** * sd_probe - called during driver initialization and whenever a * new scsi device is attached to the system. It is called once @@ -1821,8 +1867,8 @@ static int sd_probe(struct device *dev) if (error) goto out_put; - error = -EBUSY; - if (index >= SD_MAX_DISKS) + error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN); + if (error) goto out_free_index; sdkp->device = sdp; @@ -1849,24 +1895,12 @@ static int sd_probe(struct device *dev) get_device(&sdp->sdev_gendev); - gd->major = sd_major((index & 0xf0) >> 4); - gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); - gd->minors = SD_MINORS; - gd->fops = &sd_fops; - - if (index < 26) { - sprintf(gd->disk_name, "sd%c", 'a' + index % 26); - } else if (index < (26 + 1) * 26) { - sprintf(gd->disk_name, "sd%c%c", - 'a' + index / 26 - 1,'a' + index % 26); - } else { - const unsigned int m1 = (index / 26 - 1) / 26 - 1; - const unsigned int m2 = (index / 26 - 1) % 26; - const unsigned int m3 = index % 26; - sprintf(gd->disk_name, "sd%c%c%c", - 'a' + m1, 'a' + m2, 'a' + m3); + if (index < SD_MAX_DISKS) { + gd->major = sd_major((index & 0xf0) >> 4); + gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); + gd->minors = SD_MINORS; } - + gd->fops = &sd_fops; gd->private_data = &sdkp->driver; gd->queue = sdkp->device->request_queue; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 772b2ed8d239..0e411603fdf5 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -593,6 +593,7 @@ void del_gendisk(struct gendisk *disk) disk_part_iter_exit(&piter); invalidate_partition(disk, 0); + blk_free_devt(disk_to_dev(disk)->devt); set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 04524c213de1..206cdf96c3a7 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -59,6 +59,7 @@ enum { }; #define DISK_MAX_PARTS 256 +#define DISK_NAME_LEN 32 #include #include @@ -140,7 +141,7 @@ struct gendisk { int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ - char disk_name[32]; /* name of major driver */ + char disk_name[DISK_NAME_LEN]; /* name of major driver */ /* Array of pointers to partitions indexed by partno. * Protected with matching bdev lock but stat and other -- cgit v1.2.3 From a3bce90edd8f6cafe3f63b1a943800792e830178 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:05 +0900 Subject: block: add gfp_mask argument to blk_rq_map_user and blk_rq_map_user_iov Currently, blk_rq_map_user and blk_rq_map_user_iov always do GFP_KERNEL allocation. This adds gfp_mask argument to blk_rq_map_user and blk_rq_map_user_iov so sg can use it (sg always does GFP_ATOMIC allocation). Signed-off-by: FUJITA Tomonori Signed-off-by: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- block/blk-map.c | 20 ++++++++++++-------- block/bsg.c | 5 +++-- block/scsi_ioctl.c | 5 +++-- drivers/cdrom/cdrom.c | 2 +- drivers/scsi/scsi_tgt_lib.c | 2 +- fs/bio.c | 33 +++++++++++++++++++-------------- include/linux/bio.h | 9 +++++---- include/linux/blkdev.h | 5 +++-- 8 files changed, 47 insertions(+), 34 deletions(-) (limited to 'drivers/scsi') diff --git a/block/blk-map.c b/block/blk-map.c index ea1bf53929e4..ac21b7397e15 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -41,7 +41,8 @@ static int __blk_rq_unmap_user(struct bio *bio) } static int __blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned int len) + void __user *ubuf, unsigned int len, + gfp_t gfp_mask) { unsigned long uaddr; unsigned int alignment; @@ -57,9 +58,9 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, uaddr = (unsigned long) ubuf; alignment = queue_dma_alignment(q) | q->dma_pad_mask; if (!(uaddr & alignment) && !(len & alignment)) - bio = bio_map_user(q, NULL, uaddr, len, reading); + bio = bio_map_user(q, NULL, uaddr, len, reading, gfp_mask); else - bio = bio_copy_user(q, uaddr, len, reading); + bio = bio_copy_user(q, uaddr, len, reading, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); @@ -90,6 +91,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * @rq: request structure to fill * @ubuf: the user buffer * @len: length of user data + * @gfp_mask: memory allocation flags * * Description: * Data will be mapped directly for zero copy I/O, if possible. Otherwise @@ -105,7 +107,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * unmapping. */ int blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned long len) + void __user *ubuf, unsigned long len, gfp_t gfp_mask) { unsigned long bytes_read = 0; struct bio *bio = NULL; @@ -132,7 +134,7 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, if (end - start > BIO_MAX_PAGES) map_len -= PAGE_SIZE; - ret = __blk_rq_map_user(q, rq, ubuf, map_len); + ret = __blk_rq_map_user(q, rq, ubuf, map_len, gfp_mask); if (ret < 0) goto unmap_rq; if (!bio) @@ -160,6 +162,7 @@ EXPORT_SYMBOL(blk_rq_map_user); * @iov: pointer to the iovec * @iov_count: number of elements in the iovec * @len: I/O byte count + * @gfp_mask: memory allocation flags * * Description: * Data will be mapped directly for zero copy I/O, if possible. Otherwise @@ -175,7 +178,8 @@ EXPORT_SYMBOL(blk_rq_map_user); * unmapping. */ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, - struct sg_iovec *iov, int iov_count, unsigned int len) + struct sg_iovec *iov, int iov_count, unsigned int len, + gfp_t gfp_mask) { struct bio *bio; int i, read = rq_data_dir(rq) == READ; @@ -194,9 +198,9 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, } if (unaligned || (q->dma_pad_mask & len)) - bio = bio_copy_user_iov(q, iov, iov_count, read); + bio = bio_copy_user_iov(q, iov, iov_count, read, gfp_mask); else - bio = bio_map_user_iov(q, NULL, iov, iov_count, read); + bio = bio_map_user_iov(q, NULL, iov, iov_count, read, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); diff --git a/block/bsg.c b/block/bsg.c index 0aae8d7ba99c..e7a142e9916c 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -283,7 +283,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) next_rq->cmd_type = rq->cmd_type; dxferp = (void*)(unsigned long)hdr->din_xferp; - ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len); + ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len, + GFP_KERNEL); if (ret) goto out; } @@ -298,7 +299,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) dxfer_len = 0; if (dxfer_len) { - ret = blk_rq_map_user(q, rq, dxferp, dxfer_len); + ret = blk_rq_map_user(q, rq, dxferp, dxfer_len, GFP_KERNEL); if (ret) goto out; } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 3aab80a4c484..f49d6a11a69e 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -315,10 +315,11 @@ static int sg_io(struct file *file, struct request_queue *q, } ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count, - hdr->dxfer_len); + hdr->dxfer_len, GFP_KERNEL); kfree(iov); } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len); + ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len, + GFP_KERNEL); if (ret) goto out; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 74031de517e6..e861d24a6d32 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2097,7 +2097,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; - ret = blk_rq_map_user(q, rq, ubuf, len); + ret = blk_rq_map_user(q, rq, ubuf, len, GFP_KERNEL); if (ret) break; diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 257e097c39af..2a4fd820d616 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -362,7 +362,7 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, int err; dprintk("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, (void *)uaddr, len); + err = blk_rq_map_user(q, rq, (void *)uaddr, len, GFP_KERNEL); if (err) { /* * TODO: need to fixup sg_tablesize, max_segment_size, diff --git a/fs/bio.c b/fs/bio.c index 6a637b5c24b5..3d2e9ad24728 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -558,13 +558,14 @@ int bio_uncopy_user(struct bio *bio) * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Prepares and returns a bio for indirect user io, bouncing data * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, - int iov_count, int write_to_vm) + int iov_count, int write_to_vm, gfp_t gfp_mask) { struct bio_map_data *bmd; struct bio_vec *bvec; @@ -587,12 +588,12 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, len += iov[i].iov_len; } - bmd = bio_alloc_map_data(nr_pages, iov_count, GFP_KERNEL); + bmd = bio_alloc_map_data(nr_pages, iov_count, gfp_mask); if (!bmd) return ERR_PTR(-ENOMEM); ret = -ENOMEM; - bio = bio_alloc(GFP_KERNEL, nr_pages); + bio = bio_alloc(gfp_mask, nr_pages); if (!bio) goto out_bmd; @@ -605,7 +606,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, if (bytes > len) bytes = len; - page = alloc_page(q->bounce_gfp | GFP_KERNEL); + page = alloc_page(q->bounce_gfp | gfp_mask); if (!page) { ret = -ENOMEM; break; @@ -647,26 +648,27 @@ out_bmd: * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Prepares and returns a bio for indirect user io, bouncing data * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, - unsigned int len, int write_to_vm) + unsigned int len, int write_to_vm, gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_copy_user_iov(q, &iov, 1, write_to_vm); + return bio_copy_user_iov(q, &iov, 1, write_to_vm, gfp_mask); } static struct bio *__bio_map_user_iov(struct request_queue *q, struct block_device *bdev, struct sg_iovec *iov, int iov_count, - int write_to_vm) + int write_to_vm, gfp_t gfp_mask) { int i, j; int nr_pages = 0; @@ -692,12 +694,12 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, if (!nr_pages) return ERR_PTR(-EINVAL); - bio = bio_alloc(GFP_KERNEL, nr_pages); + bio = bio_alloc(gfp_mask, nr_pages); if (!bio) return ERR_PTR(-ENOMEM); ret = -ENOMEM; - pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL); + pages = kcalloc(nr_pages, sizeof(struct page *), gfp_mask); if (!pages) goto out; @@ -776,19 +778,21 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Map the user space address into a bio suitable for io to a block * device. Returns an error pointer in case of error. */ struct bio *bio_map_user(struct request_queue *q, struct block_device *bdev, - unsigned long uaddr, unsigned int len, int write_to_vm) + unsigned long uaddr, unsigned int len, int write_to_vm, + gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm); + return bio_map_user_iov(q, bdev, &iov, 1, write_to_vm, gfp_mask); } /** @@ -798,18 +802,19 @@ struct bio *bio_map_user(struct request_queue *q, struct block_device *bdev, * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not + * @gfp_mask: memory allocation flags * * Map the user space address into a bio suitable for io to a block * device. Returns an error pointer in case of error. */ struct bio *bio_map_user_iov(struct request_queue *q, struct block_device *bdev, struct sg_iovec *iov, int iov_count, - int write_to_vm) + int write_to_vm, gfp_t gfp_mask) { struct bio *bio; - bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm); - + bio = __bio_map_user_iov(q, bdev, iov, iov_count, write_to_vm, + gfp_mask); if (IS_ERR(bio)) return bio; diff --git a/include/linux/bio.h b/include/linux/bio.h index 13aba20edb2d..200b185c3e83 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -325,11 +325,11 @@ extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, unsigned int, unsigned int); extern int bio_get_nr_vecs(struct block_device *); extern struct bio *bio_map_user(struct request_queue *, struct block_device *, - unsigned long, unsigned int, int); + unsigned long, unsigned int, int, gfp_t); struct sg_iovec; extern struct bio *bio_map_user_iov(struct request_queue *, struct block_device *, - struct sg_iovec *, int, int); + struct sg_iovec *, int, int, gfp_t); extern void bio_unmap_user(struct bio *); extern struct bio *bio_map_kern(struct request_queue *, void *, unsigned int, gfp_t); @@ -337,9 +337,10 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int, gfp_t, int); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -extern struct bio *bio_copy_user(struct request_queue *, unsigned long, unsigned int, int); +extern struct bio *bio_copy_user(struct request_queue *, unsigned long, + unsigned int, int, gfp_t); extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, - int, int); + int, int, gfp_t); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 12df8efeef19..00e388d0e221 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -710,11 +710,12 @@ extern void __blk_stop_queue(struct request_queue *q); extern void __blk_run_queue(struct request_queue *); extern void blk_run_queue(struct request_queue *); extern void blk_start_queueing(struct request_queue *); -extern int blk_rq_map_user(struct request_queue *, struct request *, void __user *, unsigned long); +extern int blk_rq_map_user(struct request_queue *, struct request *, + void __user *, unsigned long, gfp_t); extern int blk_rq_unmap_user(struct bio *); extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t); extern int blk_rq_map_user_iov(struct request_queue *, struct request *, - struct sg_iovec *, int, unsigned int); + struct sg_iovec *, int, unsigned int, gfp_t); extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, -- cgit v1.2.3 From 152e283fdfea0cd11e297d982378b55937842dde Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:06 +0900 Subject: block: introduce struct rq_map_data to use reserved pages This patch introduces struct rq_map_data to enable bio_copy_use_iov() use reserved pages. Currently, bio_copy_user_iov allocates bounce pages but drivers/scsi/sg.c wants to allocate pages by itself and use them. struct rq_map_data can be used to pass allocated pages to bio_copy_user_iov. The current users of bio_copy_user_iov simply passes NULL (they don't want to use pre-allocated pages). Signed-off-by: FUJITA Tomonori Cc: Jens Axboe Cc: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- block/blk-map.c | 26 ++++++++++++-------- block/bsg.c | 7 +++--- block/scsi_ioctl.c | 4 ++-- drivers/cdrom/cdrom.c | 2 +- drivers/scsi/scsi_tgt_lib.c | 2 +- fs/bio.c | 58 ++++++++++++++++++++++++++++++++------------- include/linux/bio.h | 8 ++++--- include/linux/blkdev.h | 12 ++++++++-- 8 files changed, 80 insertions(+), 39 deletions(-) (limited to 'drivers/scsi') diff --git a/block/blk-map.c b/block/blk-map.c index ac21b7397e15..dad6a2907835 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -41,8 +41,8 @@ static int __blk_rq_unmap_user(struct bio *bio) } static int __blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned int len, - gfp_t gfp_mask) + struct rq_map_data *map_data, void __user *ubuf, + unsigned int len, gfp_t gfp_mask) { unsigned long uaddr; unsigned int alignment; @@ -57,10 +57,10 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, */ uaddr = (unsigned long) ubuf; alignment = queue_dma_alignment(q) | q->dma_pad_mask; - if (!(uaddr & alignment) && !(len & alignment)) + if (!(uaddr & alignment) && !(len & alignment) && !map_data) bio = bio_map_user(q, NULL, uaddr, len, reading, gfp_mask); else - bio = bio_copy_user(q, uaddr, len, reading, gfp_mask); + bio = bio_copy_user(q, map_data, uaddr, len, reading, gfp_mask); if (IS_ERR(bio)) return PTR_ERR(bio); @@ -89,6 +89,7 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * blk_rq_map_user - map user data to a request, for REQ_TYPE_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request structure to fill + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @ubuf: the user buffer * @len: length of user data * @gfp_mask: memory allocation flags @@ -107,7 +108,8 @@ static int __blk_rq_map_user(struct request_queue *q, struct request *rq, * unmapping. */ int blk_rq_map_user(struct request_queue *q, struct request *rq, - void __user *ubuf, unsigned long len, gfp_t gfp_mask) + struct rq_map_data *map_data, void __user *ubuf, + unsigned long len, gfp_t gfp_mask) { unsigned long bytes_read = 0; struct bio *bio = NULL; @@ -134,7 +136,8 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, if (end - start > BIO_MAX_PAGES) map_len -= PAGE_SIZE; - ret = __blk_rq_map_user(q, rq, ubuf, map_len, gfp_mask); + ret = __blk_rq_map_user(q, rq, map_data, ubuf, map_len, + gfp_mask); if (ret < 0) goto unmap_rq; if (!bio) @@ -159,6 +162,7 @@ EXPORT_SYMBOL(blk_rq_map_user); * blk_rq_map_user_iov - map user data to a request, for REQ_TYPE_BLOCK_PC usage * @q: request queue where request should be inserted * @rq: request to map data to + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @iov: pointer to the iovec * @iov_count: number of elements in the iovec * @len: I/O byte count @@ -178,8 +182,8 @@ EXPORT_SYMBOL(blk_rq_map_user); * unmapping. */ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, - struct sg_iovec *iov, int iov_count, unsigned int len, - gfp_t gfp_mask) + struct rq_map_data *map_data, struct sg_iovec *iov, + int iov_count, unsigned int len, gfp_t gfp_mask) { struct bio *bio; int i, read = rq_data_dir(rq) == READ; @@ -197,8 +201,9 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, } } - if (unaligned || (q->dma_pad_mask & len)) - bio = bio_copy_user_iov(q, iov, iov_count, read, gfp_mask); + if (unaligned || (q->dma_pad_mask & len) || map_data) + bio = bio_copy_user_iov(q, map_data, iov, iov_count, read, + gfp_mask); else bio = bio_map_user_iov(q, NULL, iov, iov_count, read, gfp_mask); @@ -220,6 +225,7 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, rq->buffer = rq->data = NULL; return 0; } +EXPORT_SYMBOL(blk_rq_map_user_iov); /** * blk_rq_unmap_user - unmap a request with user data diff --git a/block/bsg.c b/block/bsg.c index e7a142e9916c..56cb343c76d8 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -283,8 +283,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) next_rq->cmd_type = rq->cmd_type; dxferp = (void*)(unsigned long)hdr->din_xferp; - ret = blk_rq_map_user(q, next_rq, dxferp, hdr->din_xfer_len, - GFP_KERNEL); + ret = blk_rq_map_user(q, next_rq, NULL, dxferp, + hdr->din_xfer_len, GFP_KERNEL); if (ret) goto out; } @@ -299,7 +299,8 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr, int has_write_perm) dxfer_len = 0; if (dxfer_len) { - ret = blk_rq_map_user(q, rq, dxferp, dxfer_len, GFP_KERNEL); + ret = blk_rq_map_user(q, rq, NULL, dxferp, dxfer_len, + GFP_KERNEL); if (ret) goto out; } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index f49d6a11a69e..c34272a348fe 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -314,11 +314,11 @@ static int sg_io(struct file *file, struct request_queue *q, goto out; } - ret = blk_rq_map_user_iov(q, rq, iov, hdr->iovec_count, + ret = blk_rq_map_user_iov(q, rq, NULL, iov, hdr->iovec_count, hdr->dxfer_len, GFP_KERNEL); kfree(iov); } else if (hdr->dxfer_len) - ret = blk_rq_map_user(q, rq, hdr->dxferp, hdr->dxfer_len, + ret = blk_rq_map_user(q, rq, NULL, hdr->dxferp, hdr->dxfer_len, GFP_KERNEL); if (ret) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e861d24a6d32..d47f2f80accd 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2097,7 +2097,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf, len = nr * CD_FRAMESIZE_RAW; - ret = blk_rq_map_user(q, rq, ubuf, len, GFP_KERNEL); + ret = blk_rq_map_user(q, rq, NULL, ubuf, len, GFP_KERNEL); if (ret) break; diff --git a/drivers/scsi/scsi_tgt_lib.c b/drivers/scsi/scsi_tgt_lib.c index 2a4fd820d616..3117bb106b5d 100644 --- a/drivers/scsi/scsi_tgt_lib.c +++ b/drivers/scsi/scsi_tgt_lib.c @@ -362,7 +362,7 @@ static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd, int err; dprintk("%lx %u\n", uaddr, len); - err = blk_rq_map_user(q, rq, (void *)uaddr, len, GFP_KERNEL); + err = blk_rq_map_user(q, rq, NULL, (void *)uaddr, len, GFP_KERNEL); if (err) { /* * TODO: need to fixup sg_tablesize, max_segment_size, diff --git a/fs/bio.c b/fs/bio.c index 3d2e9ad24728..a2f072647cdf 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -439,16 +439,19 @@ int bio_add_page(struct bio *bio, struct page *page, unsigned int len, struct bio_map_data { struct bio_vec *iovecs; - int nr_sgvecs; struct sg_iovec *sgvecs; + int nr_sgvecs; + int is_our_pages; }; static void bio_set_map_data(struct bio_map_data *bmd, struct bio *bio, - struct sg_iovec *iov, int iov_count) + struct sg_iovec *iov, int iov_count, + int is_our_pages) { memcpy(bmd->iovecs, bio->bi_io_vec, sizeof(struct bio_vec) * bio->bi_vcnt); memcpy(bmd->sgvecs, iov, sizeof(struct sg_iovec) * iov_count); bmd->nr_sgvecs = iov_count; + bmd->is_our_pages = is_our_pages; bio->bi_private = bmd; } @@ -483,7 +486,8 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count, } static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, - struct sg_iovec *iov, int iov_count, int uncopy) + struct sg_iovec *iov, int iov_count, int uncopy, + int do_free_page) { int ret = 0, i; struct bio_vec *bvec; @@ -526,7 +530,7 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, } } - if (uncopy) + if (do_free_page) __free_page(bvec->bv_page); } @@ -545,7 +549,8 @@ int bio_uncopy_user(struct bio *bio) struct bio_map_data *bmd = bio->bi_private; int ret; - ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1); + ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, bmd->nr_sgvecs, 1, + bmd->is_our_pages); bio_free_map_data(bmd); bio_put(bio); @@ -555,6 +560,7 @@ int bio_uncopy_user(struct bio *bio) /** * bio_copy_user_iov - copy user data to bio * @q: destination block queue + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @iov: the iovec. * @iov_count: number of elements in the iovec * @write_to_vm: bool indicating writing to pages or not @@ -564,8 +570,10 @@ int bio_uncopy_user(struct bio *bio) * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ -struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, - int iov_count, int write_to_vm, gfp_t gfp_mask) +struct bio *bio_copy_user_iov(struct request_queue *q, + struct rq_map_data *map_data, + struct sg_iovec *iov, int iov_count, + int write_to_vm, gfp_t gfp_mask) { struct bio_map_data *bmd; struct bio_vec *bvec; @@ -600,13 +608,26 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, bio->bi_rw |= (!write_to_vm << BIO_RW); ret = 0; + i = 0; while (len) { - unsigned int bytes = PAGE_SIZE; + unsigned int bytes; + + if (map_data) + bytes = 1U << (PAGE_SHIFT + map_data->page_order); + else + bytes = PAGE_SIZE; if (bytes > len) bytes = len; - page = alloc_page(q->bounce_gfp | gfp_mask); + if (map_data) { + if (i == map_data->nr_entries) { + ret = -ENOMEM; + break; + } + page = map_data->pages[i++]; + } else + page = alloc_page(q->bounce_gfp | gfp_mask); if (!page) { ret = -ENOMEM; break; @@ -625,16 +646,17 @@ struct bio *bio_copy_user_iov(struct request_queue *q, struct sg_iovec *iov, * success */ if (!write_to_vm) { - ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0); + ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0); if (ret) goto cleanup; } - bio_set_map_data(bmd, bio, iov, iov_count); + bio_set_map_data(bmd, bio, iov, iov_count, map_data ? 0 : 1); return bio; cleanup: - bio_for_each_segment(bvec, bio, i) - __free_page(bvec->bv_page); + if (!map_data) + bio_for_each_segment(bvec, bio, i) + __free_page(bvec->bv_page); bio_put(bio); out_bmd: @@ -645,6 +667,7 @@ out_bmd: /** * bio_copy_user - copy user data to bio * @q: destination block queue + * @map_data: pointer to the rq_map_data holding pages (if necessary) * @uaddr: start of user address * @len: length in bytes * @write_to_vm: bool indicating writing to pages or not @@ -654,15 +677,16 @@ out_bmd: * to/from kernel pages as necessary. Must be paired with * call bio_uncopy_user() on io completion. */ -struct bio *bio_copy_user(struct request_queue *q, unsigned long uaddr, - unsigned int len, int write_to_vm, gfp_t gfp_mask) +struct bio *bio_copy_user(struct request_queue *q, struct rq_map_data *map_data, + unsigned long uaddr, unsigned int len, + int write_to_vm, gfp_t gfp_mask) { struct sg_iovec iov; iov.iov_base = (void __user *)uaddr; iov.iov_len = len; - return bio_copy_user_iov(q, &iov, 1, write_to_vm, gfp_mask); + return bio_copy_user_iov(q, map_data, &iov, 1, write_to_vm, gfp_mask); } static struct bio *__bio_map_user_iov(struct request_queue *q, @@ -1028,7 +1052,7 @@ struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, bio->bi_private = bmd; bio->bi_end_io = bio_copy_kern_endio; - bio_set_map_data(bmd, bio, &iov, 1); + bio_set_map_data(bmd, bio, &iov, 1, 1); return bio; cleanup: bio_for_each_segment(bvec, bio, i) diff --git a/include/linux/bio.h b/include/linux/bio.h index 200b185c3e83..bc386cd5e996 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -327,6 +327,7 @@ extern int bio_get_nr_vecs(struct block_device *); extern struct bio *bio_map_user(struct request_queue *, struct block_device *, unsigned long, unsigned int, int, gfp_t); struct sg_iovec; +struct rq_map_data; extern struct bio *bio_map_user_iov(struct request_queue *, struct block_device *, struct sg_iovec *, int, int, gfp_t); @@ -337,9 +338,10 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int, gfp_t, int); extern void bio_set_pages_dirty(struct bio *bio); extern void bio_check_pages_dirty(struct bio *bio); -extern struct bio *bio_copy_user(struct request_queue *, unsigned long, - unsigned int, int, gfp_t); -extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, +extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *, + unsigned long, unsigned int, int, gfp_t); +extern struct bio *bio_copy_user_iov(struct request_queue *, + struct rq_map_data *, struct sg_iovec *, int, int, gfp_t); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 00e388d0e221..358ac423ed2f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -642,6 +642,12 @@ static inline void blk_queue_bounce(struct request_queue *q, struct bio **bio) } #endif /* CONFIG_MMU */ +struct rq_map_data { + struct page **pages; + int page_order; + int nr_entries; +}; + struct req_iterator { int i; struct bio *bio; @@ -711,11 +717,13 @@ extern void __blk_run_queue(struct request_queue *); extern void blk_run_queue(struct request_queue *); extern void blk_start_queueing(struct request_queue *); extern int blk_rq_map_user(struct request_queue *, struct request *, - void __user *, unsigned long, gfp_t); + struct rq_map_data *, void __user *, unsigned long, + gfp_t); extern int blk_rq_unmap_user(struct bio *); extern int blk_rq_map_kern(struct request_queue *, struct request *, void *, unsigned int, gfp_t); extern int blk_rq_map_user_iov(struct request_queue *, struct request *, - struct sg_iovec *, int, unsigned int, gfp_t); + struct rq_map_data *, struct sg_iovec *, int, + unsigned int, gfp_t); extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, -- cgit v1.2.3 From 10865dfa34e7552c4c64606edcdf1e21a110c985 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:07 +0900 Subject: sg: convert the non-data path to use the block layer This patch converts the non data path to use the block layer functions (blk_get_request, blk_execute_rq_nowait, etc) instead of uses scsi_execute_async(). Signed-off-by: FUJITA Tomonori Signed-off-by: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 661f9f21650a..487c7776cc4e 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -137,6 +137,7 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ volatile char done; /* 0->before bh, 1->before read, 2->read */ + struct request *rq; } Sg_request; typedef struct sg_fd { /* holds the state of a file descriptor */ @@ -176,7 +177,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ static int sg_fasync(int fd, struct file *filp, int mode); /* tasklet or soft irq callback */ static void sg_cmd_done(void *data, char *sense, int result, int resid); -static int sg_start_req(Sg_request * srp); +static int sg_start_req(Sg_request *srp, unsigned char *cmd); static void sg_finish_rem_req(Sg_request * srp); static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, @@ -229,6 +230,11 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) cmd, filp->f_mode & FMODE_WRITE); } +static void sg_rq_end_io(struct request *rq, int uptodate) +{ + sg_cmd_done(rq->end_io_data, rq->sense, rq->errors, rq->data_len); +} + static int sg_open(struct inode *inode, struct file *filp) { @@ -732,7 +738,8 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) cmnd[0], (int) hp->cmd_len)); - if ((k = sg_start_req(srp))) { + k = sg_start_req(srp, cmnd); + if (k) { SCSI_LOG_TIMEOUT(1, printk("sg_common_write: start_req err=%d\n", k)); sg_finish_rem_req(srp); return k; /* probably out of space --> ENOMEM */ @@ -765,6 +772,12 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, hp->duration = jiffies_to_msecs(jiffies); /* Now send everything of to mid-level. The next time we hear about this packet is when sg_cmd_done() is called (i.e. a callback). */ + if (srp->rq) { + srp->rq->timeout = timeout; + blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, + srp->rq, 1, sg_rq_end_io); + return 0; + } if (scsi_execute_async(sdp->device, cmnd, hp->cmd_len, data_dir, srp->data.buffer, hp->dxfer_len, srp->data.k_use_sg, timeout, SG_DEFAULT_RETRIES, srp, sg_cmd_done, @@ -1634,8 +1647,32 @@ exit_sg(void) idr_destroy(&sg_index_idr); } -static int -sg_start_req(Sg_request * srp) +static int __sg_start_req(struct sg_request *srp, struct sg_io_hdr *hp, + unsigned char *cmd) +{ + struct sg_fd *sfp = srp->parentfp; + struct request_queue *q = sfp->parentdp->device->request_queue; + struct request *rq; + int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; + + rq = blk_get_request(q, rw, GFP_ATOMIC); + if (!rq) + return -ENOMEM; + + memcpy(rq->cmd, cmd, hp->cmd_len); + + rq->cmd_len = hp->cmd_len; + rq->cmd_type = REQ_TYPE_BLOCK_PC; + + srp->rq = rq; + rq->end_io_data = srp; + rq->sense = srp->sense_b; + rq->retries = SG_DEFAULT_RETRIES; + + return 0; +} + +static int sg_start_req(Sg_request *srp, unsigned char *cmd) { int res; Sg_fd *sfp = srp->parentfp; @@ -1646,8 +1683,10 @@ sg_start_req(Sg_request * srp) Sg_scatter_hold *rsv_schp = &sfp->reserve; SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); + if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) - return 0; + return __sg_start_req(srp, hp, cmd); + if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (!sfp->parentdp->device->host->unchecked_isa_dma)) { @@ -1678,6 +1717,10 @@ sg_finish_rem_req(Sg_request * srp) sg_unlink_reserve(sfp, srp); else sg_remove_scat(req_schp); + + if (srp->rq) + blk_put_request(srp->rq); + sg_remove_request(sfp, srp); } -- cgit v1.2.3 From 6e5a30cba5e7c03b2cd564e968f1dd667a0f7c42 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 16:17:08 +0900 Subject: sg: convert the direct IO path to use the block layer This patch converts the direct IO path (SG_FLAG_DIRECT_IO) to use the block layer functions (blk_get_request, blk_execute_rq_nowait, blk_rq_map_user, etc) instead of scsi_execute_async(). Signed-off-by: FUJITA Tomonori Signed-off-by: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 173 +++++++++--------------------------------------------- 1 file changed, 27 insertions(+), 146 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 487c7776cc4e..cb6de0752ee1 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -138,6 +138,7 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ volatile char done; /* 0->before bh, 1->before read, 2->read */ struct request *rq; + struct bio *bio; } Sg_request; typedef struct sg_fd { /* holds the state of a file descriptor */ @@ -1679,21 +1680,29 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) sg_io_hdr_t *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; + unsigned long uaddr = (unsigned long)hp->dxferp; Sg_scatter_hold *req_schp = &srp->data; Sg_scatter_hold *rsv_schp = &sfp->reserve; + struct request_queue *q = sfp->parentdp->device->request_queue; + unsigned long alignment = queue_dma_alignment(q) | q->dma_pad_mask; SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return __sg_start_req(srp, hp, cmd); +#ifdef SG_ALLOW_DIO_CODE if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && - (!sfp->parentdp->device->host->unchecked_isa_dma)) { - res = sg_build_direct(srp, sfp, dxfer_len); - if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ - return res; + (!sfp->parentdp->device->host->unchecked_isa_dma) && + !(uaddr & alignment) && !(dxfer_len & alignment)) { + res = __sg_start_req(srp, hp, cmd); + if (!res) + res = sg_build_direct(srp, sfp, dxfer_len); + + return res; } +#endif if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) sg_link_reserve(sfp, srp, dxfer_len); else { @@ -1718,8 +1727,11 @@ sg_finish_rem_req(Sg_request * srp) else sg_remove_scat(req_schp); - if (srp->rq) + if (srp->rq) { + if (srp->bio) + blk_rq_unmap_user(srp->bio); blk_put_request(srp->rq); + } sg_remove_request(sfp, srp); } @@ -1746,151 +1758,23 @@ sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) return tablesize; /* number of scat_gath elements allocated */ } -#ifdef SG_ALLOW_DIO_CODE -/* vvvvvvvv following code borrowed from st driver's direct IO vvvvvvvvv */ - /* TODO: hopefully we can use the generic block layer code */ - -/* Pin down user pages and put them into a scatter gather list. Returns <= 0 if - - mapping of all pages not successful - (i.e., either completely successful or fails) -*/ -static int -st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, - unsigned long uaddr, size_t count, int rw) -{ - unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; - unsigned long start = uaddr >> PAGE_SHIFT; - const int nr_pages = end - start; - int res, i, j; - struct page **pages; - - /* User attempted Overflow! */ - if ((uaddr + count) < uaddr) - return -EINVAL; - - /* Too big */ - if (nr_pages > max_pages) - return -ENOMEM; - - /* Hmm? */ - if (count == 0) - return 0; - - if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_ATOMIC)) == NULL) - return -ENOMEM; - - /* Try to fault in all of the necessary pages */ - down_read(¤t->mm->mmap_sem); - /* rw==READ means read from drive, write into memory area */ - res = get_user_pages( - current, - current->mm, - uaddr, - nr_pages, - rw == READ, - 0, /* don't force */ - pages, - NULL); - up_read(¤t->mm->mmap_sem); - - /* Errors and no page mapped should return here */ - if (res < nr_pages) - goto out_unmap; - - for (i=0; i < nr_pages; i++) { - /* FIXME: flush superflous for rw==READ, - * probably wrong function for rw==WRITE - */ - flush_dcache_page(pages[i]); - /* ?? Is locking needed? I don't think so */ - /* if (!trylock_page(pages[i])) - goto out_unlock; */ - } - - sg_set_page(sgl, pages[0], 0, uaddr & ~PAGE_MASK); - if (nr_pages > 1) { - sgl[0].length = PAGE_SIZE - sgl[0].offset; - count -= sgl[0].length; - for (i=1; i < nr_pages ; i++) - sg_set_page(&sgl[i], pages[i], count < PAGE_SIZE ? count : PAGE_SIZE, 0); - } - else { - sgl[0].length = count; - } - - kfree(pages); - return nr_pages; - - out_unmap: - if (res > 0) { - for (j=0; j < res; j++) - page_cache_release(pages[j]); - res = 0; - } - kfree(pages); - return res; -} - - -/* And unmap them... */ -static int -st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, - int dirtied) -{ - int i; - - for (i=0; i < nr_pages; i++) { - struct page *page = sg_page(&sgl[i]); - - if (dirtied) - SetPageDirty(page); - /* unlock_page(page); */ - /* FIXME: cache flush missing for rw==READ - * FIXME: call the correct reference counting function - */ - page_cache_release(page); - } - - return 0; -} - -/* ^^^^^^^^ above code borrowed from st driver's direct IO ^^^^^^^^^ */ -#endif - - /* Returns: -ve -> error, 0 -> done, 1 -> try indirect */ static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len) { -#ifdef SG_ALLOW_DIO_CODE sg_io_hdr_t *hp = &srp->header; Sg_scatter_hold *schp = &srp->data; - int sg_tablesize = sfp->parentdp->sg_tablesize; - int mx_sc_elems, res; - struct scsi_device *sdev = sfp->parentdp->device; - - if (((unsigned long)hp->dxferp & - queue_dma_alignment(sdev->request_queue)) != 0) - return 1; + int res; + struct request *rq = srp->rq; + struct request_queue *q = sfp->parentdp->device->request_queue; - mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); - if (mx_sc_elems <= 0) { - return 1; - } - res = st_map_user_pages(schp->buffer, mx_sc_elems, - (unsigned long)hp->dxferp, dxfer_len, - (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0); - if (res <= 0) { - sg_remove_scat(schp); - return 1; - } - schp->k_use_sg = res; + res = blk_rq_map_user(q, rq, NULL, hp->dxferp, dxfer_len, GFP_ATOMIC); + if (res) + return res; + srp->bio = rq->bio; schp->dio_in_use = 1; hp->info |= SG_INFO_DIRECT_IO; return 0; -#else - return 1; -#endif } static int @@ -2069,11 +1953,7 @@ sg_remove_scat(Sg_scatter_hold * schp) if (schp->buffer && (schp->sglist_len > 0)) { struct scatterlist *sg = schp->buffer; - if (schp->dio_in_use) { -#ifdef SG_ALLOW_DIO_CODE - st_unmap_user_pages(sg, schp->k_use_sg, TRUE); -#endif - } else { + if (!schp->dio_in_use) { int k; for (k = 0; (k < schp->k_use_sg) && sg_page(sg); @@ -2083,8 +1963,9 @@ sg_remove_scat(Sg_scatter_hold * schp) k, sg_page(sg), sg->length)); sg_page_free(sg_page(sg), sg->length); } + + kfree(schp->buffer); } - kfree(schp->buffer); } memset(schp, 0, sizeof (*schp)); } -- cgit v1.2.3 From 10db10d144c0248f285242f79daf6b9de6b00a62 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 29 Aug 2008 12:32:18 +0200 Subject: sg: convert the indirect IO path to use the block layer This patch converts the indirect IO path (including mmap IO and old struct sg_header) to use the block layer functions (blk_get_request, blk_execute_rq_nowait, blk_rq_map_user, etc) instead of scsi_execute_async(). [Jens: fixed compile error with SCSI logging enabled] Signed-off-by: FUJITA Tomonori Signed-off-by: Douglas Gilbert Cc: Mike Christie Cc: James Bottomley Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 397 +++++++++++++++--------------------------------------- 1 file changed, 105 insertions(+), 292 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index cb6de0752ee1..d6391666502c 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -47,7 +47,6 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #include #include #include -#include #include #include @@ -119,7 +118,8 @@ typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */ unsigned bufflen; /* Size of (aggregate) data buffer */ unsigned b_malloc_len; /* actual len malloc'ed in buffer */ - struct scatterlist *buffer;/* scatter list */ + struct page **pages; + int page_order; char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ unsigned char cmd_opcode; /* first byte of command */ } Sg_scatter_hold; @@ -190,8 +190,6 @@ static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, int read_only, Sg_request **o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking); -static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, - int wr_xf, int *countp, unsigned char __user **up); static int sg_write_xfer(Sg_request * srp); static int sg_read_xfer(Sg_request * srp); static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); @@ -199,8 +197,6 @@ static void sg_remove_scat(Sg_scatter_hold * schp); static void sg_build_reserve(Sg_fd * sfp, int req_size); static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size); static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp); -static struct page *sg_page_malloc(int rqSz, int lowDma, int *retSzp); -static void sg_page_free(struct page *page, int size); static Sg_fd *sg_add_sfp(Sg_device * sdp, int dev); static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); @@ -771,26 +767,11 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, break; } hp->duration = jiffies_to_msecs(jiffies); -/* Now send everything of to mid-level. The next time we hear about this - packet is when sg_cmd_done() is called (i.e. a callback). */ - if (srp->rq) { - srp->rq->timeout = timeout; - blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, - srp->rq, 1, sg_rq_end_io); - return 0; - } - if (scsi_execute_async(sdp->device, cmnd, hp->cmd_len, data_dir, srp->data.buffer, - hp->dxfer_len, srp->data.k_use_sg, timeout, - SG_DEFAULT_RETRIES, srp, sg_cmd_done, - GFP_ATOMIC)) { - SCSI_LOG_TIMEOUT(1, printk("sg_common_write: scsi_execute_async failed\n")); - /* - * most likely out of mem, but could also be a bad map - */ - sg_finish_rem_req(srp); - return -ENOMEM; - } else - return 0; + + srp->rq->timeout = timeout; + blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, + srp->rq, 1, sg_rq_end_io); + return 0; } static int @@ -1206,8 +1187,7 @@ sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) Sg_fd *sfp; unsigned long offset, len, sa; Sg_scatter_hold *rsv_schp; - struct scatterlist *sg; - int k; + int k, length; if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data))) return VM_FAULT_SIGBUS; @@ -1217,15 +1197,14 @@ sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; SCSI_LOG_TIMEOUT(3, printk("sg_vma_fault: offset=%lu, scatg=%d\n", offset, rsv_schp->k_use_sg)); - sg = rsv_schp->buffer; sa = vma->vm_start; - for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); - ++k, sg = sg_next(sg)) { + length = 1 << (PAGE_SHIFT + rsv_schp->page_order); + for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { len = vma->vm_end - sa; - len = (len < sg->length) ? len : sg->length; + len = (len < length) ? len : length; if (offset < len) { - struct page *page; - page = virt_to_page(page_address(sg_page(sg)) + offset); + struct page *page = nth_page(rsv_schp->pages[k], + offset >> PAGE_SHIFT); get_page(page); /* increment page count */ vmf->page = page; return 0; /* success */ @@ -1247,8 +1226,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) Sg_fd *sfp; unsigned long req_sz, len, sa; Sg_scatter_hold *rsv_schp; - int k; - struct scatterlist *sg; + int k, length; if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) return -ENXIO; @@ -1262,11 +1240,10 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return -ENOMEM; /* cannot map more than reserved buffer */ sa = vma->vm_start; - sg = rsv_schp->buffer; - for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); - ++k, sg = sg_next(sg)) { + length = 1 << (PAGE_SHIFT + rsv_schp->page_order); + for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { len = vma->vm_end - sa; - len = (len < sg->length) ? len : sg->length; + len = (len < length) ? len : length; sa += len; } @@ -1310,7 +1287,6 @@ sg_cmd_done(void *data, char *sense, int result, int resid) if (0 != result) { struct scsi_sense_hdr sshdr; - memcpy(srp->sense_b, sense, sizeof (srp->sense_b)); srp->header.status = 0xff & result; srp->header.masked_status = status_byte(result); srp->header.msg_status = msg_byte(result); @@ -1685,34 +1661,51 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) Sg_scatter_hold *rsv_schp = &sfp->reserve; struct request_queue *q = sfp->parentdp->device->request_queue; unsigned long alignment = queue_dma_alignment(q) | q->dma_pad_mask; + struct rq_map_data map_data; SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); + res = __sg_start_req(srp, hp, cmd); + if (res) + return res; + if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) - return __sg_start_req(srp, hp, cmd); + return 0; #ifdef SG_ALLOW_DIO_CODE if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (!sfp->parentdp->device->host->unchecked_isa_dma) && - !(uaddr & alignment) && !(dxfer_len & alignment)) { - res = __sg_start_req(srp, hp, cmd); - if (!res) - res = sg_build_direct(srp, sfp, dxfer_len); - - return res; - } + !(uaddr & alignment) && !(dxfer_len & alignment)) + return sg_build_direct(srp, sfp, dxfer_len); #endif if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) sg_link_reserve(sfp, srp, dxfer_len); - else { + else res = sg_build_indirect(req_schp, sfp, dxfer_len); - if (res) { - sg_remove_scat(req_schp); - return res; - } + + if (!res) { + struct request *rq = srp->rq; + Sg_scatter_hold *schp = &srp->data; + int iovec_count = (int) hp->iovec_count; + + map_data.pages = schp->pages; + map_data.page_order = schp->page_order; + map_data.nr_entries = schp->k_use_sg; + + if (iovec_count) + res = blk_rq_map_user_iov(q, rq, &map_data, hp->dxferp, + iovec_count, + hp->dxfer_len, GFP_ATOMIC); + else + res = blk_rq_map_user(q, rq, &map_data, hp->dxferp, + hp->dxfer_len, GFP_ATOMIC); + + if (!res) + srp->bio = rq->bio; } - return 0; + + return res; } static void @@ -1730,6 +1723,7 @@ sg_finish_rem_req(Sg_request * srp) if (srp->rq) { if (srp->bio) blk_rq_unmap_user(srp->bio); + blk_put_request(srp->rq); } @@ -1739,21 +1733,12 @@ sg_finish_rem_req(Sg_request * srp) static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) { - int sg_bufflen = tablesize * sizeof(struct scatterlist); + int sg_bufflen = tablesize * sizeof(struct page *); gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; - /* - * TODO: test without low_dma, we should not need it since - * the block layer will bounce the buffer for us - * - * XXX(hch): we shouldn't need GFP_DMA for the actual S/G list. - */ - if (sfp->low_dma) - gfp_flags |= GFP_DMA; - schp->buffer = kzalloc(sg_bufflen, gfp_flags); - if (!schp->buffer) + schp->pages = kzalloc(sg_bufflen, gfp_flags); + if (!schp->pages) return -ENOMEM; - sg_init_table(schp->buffer, tablesize); schp->sglist_len = sg_bufflen; return tablesize; /* number of scat_gath elements allocated */ } @@ -1780,11 +1765,10 @@ sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len) static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) { - struct scatterlist *sg; - int ret_sz = 0, k, rem_sz, num, mx_sc_elems; + int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; int sg_tablesize = sfp->parentdp->sg_tablesize; - int blk_size = buff_size; - struct page *p = NULL; + int blk_size = buff_size, order; + gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; if (blk_size < 0) return -EFAULT; @@ -1808,15 +1792,26 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) } else scatter_elem_sz_prev = num; } - for (k = 0, sg = schp->buffer, rem_sz = blk_size; - (rem_sz > 0) && (k < mx_sc_elems); - ++k, rem_sz -= ret_sz, sg = sg_next(sg)) { - + + if (sfp->low_dma) + gfp_mask |= GFP_DMA; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + gfp_mask |= __GFP_ZERO; + + order = get_order(num); +retry: + ret_sz = 1 << (PAGE_SHIFT + order); + + for (k = 0, rem_sz = blk_size; rem_sz > 0 && k < mx_sc_elems; + k++, rem_sz -= ret_sz) { + num = (rem_sz > scatter_elem_sz_prev) ? - scatter_elem_sz_prev : rem_sz; - p = sg_page_malloc(num, sfp->low_dma, &ret_sz); - if (!p) - return -ENOMEM; + scatter_elem_sz_prev : rem_sz; + + schp->pages[k] = alloc_pages(gfp_mask, order); + if (!schp->pages[k]) + goto out; if (num == scatter_elem_sz_prev) { if (unlikely(ret_sz > scatter_elem_sz_prev)) { @@ -1824,12 +1819,12 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) scatter_elem_sz_prev = ret_sz; } } - sg_set_page(sg, p, (ret_sz > num) ? num : ret_sz, 0); SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k=%d, num=%d, " "ret_sz=%d\n", k, num, ret_sz)); } /* end of for loop */ + schp->page_order = order; schp->k_use_sg = k; SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, " "rem_sz=%d\n", k, rem_sz)); @@ -1837,8 +1832,15 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; - return 0; +out: + for (i = 0; i < k; i++) + __free_pages(schp->pages[k], order); + + if (--order >= 0) + goto retry; + + return -ENOMEM; } static int @@ -1846,13 +1848,8 @@ sg_write_xfer(Sg_request * srp) { sg_io_hdr_t *hp = &srp->header; Sg_scatter_hold *schp = &srp->data; - struct scatterlist *sg = schp->buffer; int num_xfer = 0; - int j, k, onum, usglen, ksglen, res; - int iovec_count = (int) hp->iovec_count; int dxfer_dir = hp->dxfer_direction; - unsigned char *p; - unsigned char __user *up; int new_interface = ('\0' == hp->interface_id) ? 0 : 1; if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) || @@ -1866,83 +1863,9 @@ sg_write_xfer(Sg_request * srp) && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) return 0; - SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", - num_xfer, iovec_count, schp->k_use_sg)); - if (iovec_count) { - onum = iovec_count; - if (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum)) - return -EFAULT; - } else - onum = 1; - - ksglen = sg->length; - p = page_address(sg_page(sg)); - for (j = 0, k = 0; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up); - if (res) - return res; - - for (; p; sg = sg_next(sg), ksglen = sg->length, - p = page_address(sg_page(sg))) { - if (usglen <= 0) - break; - if (ksglen > usglen) { - if (usglen >= num_xfer) { - if (__copy_from_user(p, up, num_xfer)) - return -EFAULT; - return 0; - } - if (__copy_from_user(p, up, usglen)) - return -EFAULT; - p += usglen; - ksglen -= usglen; - break; - } else { - if (ksglen >= num_xfer) { - if (__copy_from_user(p, up, num_xfer)) - return -EFAULT; - return 0; - } - if (__copy_from_user(p, up, ksglen)) - return -EFAULT; - up += ksglen; - usglen -= ksglen; - } - ++k; - if (k >= schp->k_use_sg) - return 0; - } - } - - return 0; -} + SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, k_use_sg=%d\n", + num_xfer, schp->k_use_sg)); -static int -sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, - int wr_xf, int *countp, unsigned char __user **up) -{ - int num_xfer = (int) hp->dxfer_len; - unsigned char __user *p = hp->dxferp; - int count; - - if (0 == sg_num) { - if (wr_xf && ('\0' == hp->interface_id)) - count = (int) hp->flags; /* holds "old" input_size */ - else - count = num_xfer; - } else { - sg_iovec_t iovec; - if (__copy_from_user(&iovec, p + ind*SZ_SG_IOVEC, SZ_SG_IOVEC)) - return -EFAULT; - p = iovec.iov_base; - count = (int) iovec.iov_len; - } - if (!access_ok(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count)) - return -EFAULT; - if (up) - *up = p; - if (countp) - *countp = count; return 0; } @@ -1950,21 +1873,18 @@ static void sg_remove_scat(Sg_scatter_hold * schp) { SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); - if (schp->buffer && (schp->sglist_len > 0)) { - struct scatterlist *sg = schp->buffer; - + if (schp->pages && schp->sglist_len > 0) { if (!schp->dio_in_use) { int k; - for (k = 0; (k < schp->k_use_sg) && sg_page(sg); - ++k, sg = sg_next(sg)) { + for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { SCSI_LOG_TIMEOUT(5, printk( - "sg_remove_scat: k=%d, pg=0x%p, len=%d\n", - k, sg_page(sg), sg->length)); - sg_page_free(sg_page(sg), sg->length); + "sg_remove_scat: k=%d, pg=0x%p\n", + k, schp->pages[k])); + __free_pages(schp->pages[k], schp->page_order); } - kfree(schp->buffer); + kfree(schp->pages); } } memset(schp, 0, sizeof (*schp)); @@ -1975,13 +1895,8 @@ sg_read_xfer(Sg_request * srp) { sg_io_hdr_t *hp = &srp->header; Sg_scatter_hold *schp = &srp->data; - struct scatterlist *sg = schp->buffer; int num_xfer = 0; - int j, k, onum, usglen, ksglen, res; - int iovec_count = (int) hp->iovec_count; int dxfer_dir = hp->dxfer_direction; - unsigned char *p; - unsigned char __user *up; int new_interface = ('\0' == hp->interface_id) ? 0 : 1; if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir) @@ -1996,53 +1911,7 @@ sg_read_xfer(Sg_request * srp) return 0; SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", - num_xfer, iovec_count, schp->k_use_sg)); - if (iovec_count) { - onum = iovec_count; - if (!access_ok(VERIFY_READ, hp->dxferp, SZ_SG_IOVEC * onum)) - return -EFAULT; - } else - onum = 1; - - p = page_address(sg_page(sg)); - ksglen = sg->length; - for (j = 0, k = 0; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up); - if (res) - return res; - - for (; p; sg = sg_next(sg), ksglen = sg->length, - p = page_address(sg_page(sg))) { - if (usglen <= 0) - break; - if (ksglen > usglen) { - if (usglen >= num_xfer) { - if (__copy_to_user(up, p, num_xfer)) - return -EFAULT; - return 0; - } - if (__copy_to_user(up, p, usglen)) - return -EFAULT; - p += usglen; - ksglen -= usglen; - break; - } else { - if (ksglen >= num_xfer) { - if (__copy_to_user(up, p, num_xfer)) - return -EFAULT; - return 0; - } - if (__copy_to_user(up, p, ksglen)) - return -EFAULT; - up += ksglen; - usglen -= ksglen; - } - ++k; - if (k >= schp->k_use_sg) - return 0; - } - } - + num_xfer, (int)hp->iovec_count, schp->k_use_sg)); return 0; } @@ -2050,7 +1919,6 @@ static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) { Sg_scatter_hold *schp = &srp->data; - struct scatterlist *sg = schp->buffer; int k, num; SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n", @@ -2058,15 +1926,18 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) if ((!outp) || (num_read_xfer <= 0)) return 0; - for (k = 0; (k < schp->k_use_sg) && sg_page(sg); ++k, sg = sg_next(sg)) { - num = sg->length; + blk_rq_unmap_user(srp->bio); + srp->bio = NULL; + + num = 1 << (PAGE_SHIFT + schp->page_order); + for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { if (num > num_read_xfer) { - if (__copy_to_user(outp, page_address(sg_page(sg)), + if (__copy_to_user(outp, page_address(schp->pages[k]), num_read_xfer)) return -EFAULT; break; } else { - if (__copy_to_user(outp, page_address(sg_page(sg)), + if (__copy_to_user(outp, page_address(schp->pages[k]), num)) return -EFAULT; num_read_xfer -= num; @@ -2101,24 +1972,22 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) { Sg_scatter_hold *req_schp = &srp->data; Sg_scatter_hold *rsv_schp = &sfp->reserve; - struct scatterlist *sg = rsv_schp->buffer; int k, num, rem; srp->res_used = 1; SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); rem = size; - for (k = 0; k < rsv_schp->k_use_sg; ++k, sg = sg_next(sg)) { - num = sg->length; + num = 1 << (PAGE_SHIFT + rsv_schp->page_order); + for (k = 0; k < rsv_schp->k_use_sg; k++) { if (rem <= num) { - sfp->save_scat_len = num; - sg->length = rem; req_schp->k_use_sg = k + 1; req_schp->sglist_len = rsv_schp->sglist_len; - req_schp->buffer = rsv_schp->buffer; + req_schp->pages = rsv_schp->pages; req_schp->bufflen = size; req_schp->b_malloc_len = rsv_schp->b_malloc_len; + req_schp->page_order = rsv_schp->page_order; break; } else rem -= num; @@ -2132,22 +2001,13 @@ static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) { Sg_scatter_hold *req_schp = &srp->data; - Sg_scatter_hold *rsv_schp = &sfp->reserve; SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n", (int) req_schp->k_use_sg)); - if ((rsv_schp->k_use_sg > 0) && (req_schp->k_use_sg > 0)) { - struct scatterlist *sg = rsv_schp->buffer; - - if (sfp->save_scat_len > 0) - (sg + (req_schp->k_use_sg - 1))->length = - (unsigned) sfp->save_scat_len; - else - SCSI_LOG_TIMEOUT(1, printk ("sg_unlink_reserve: BAD save_scat_len\n")); - } req_schp->k_use_sg = 0; req_schp->bufflen = 0; - req_schp->buffer = NULL; + req_schp->pages = NULL; + req_schp->page_order = 0; req_schp->sglist_len = 0; sfp->save_scat_len = 0; srp->res_used = 0; @@ -2405,53 +2265,6 @@ sg_res_in_use(Sg_fd * sfp) return srp ? 1 : 0; } -/* The size fetched (value output via retSzp) set when non-NULL return */ -static struct page * -sg_page_malloc(int rqSz, int lowDma, int *retSzp) -{ - struct page *resp = NULL; - gfp_t page_mask; - int order, a_size; - int resSz; - - if ((rqSz <= 0) || (NULL == retSzp)) - return resp; - - if (lowDma) - page_mask = GFP_ATOMIC | GFP_DMA | __GFP_COMP | __GFP_NOWARN; - else - page_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN; - - for (order = 0, a_size = PAGE_SIZE; a_size < rqSz; - order++, a_size <<= 1) ; - resSz = a_size; /* rounded up if necessary */ - resp = alloc_pages(page_mask, order); - while ((!resp) && order) { - --order; - a_size >>= 1; /* divide by 2, until PAGE_SIZE */ - resp = alloc_pages(page_mask, order); /* try half */ - resSz = a_size; - } - if (resp) { - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - memset(page_address(resp), 0, resSz); - *retSzp = resSz; - } - return resp; -} - -static void -sg_page_free(struct page *page, int size) -{ - int order, a_size; - - if (!page) - return; - for (order = 0, a_size = PAGE_SIZE; a_size < size; - order++, a_size <<= 1) ; - __free_pages(page, order); -} - #ifdef CONFIG_SCSI_PROC_FS static int sg_idr_max_id(int id, void *p, void *data) -- cgit v1.2.3 From 01cfcddd98f09e05a2e36031654ed46643b76f23 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Thu, 28 Aug 2008 15:05:59 +0900 Subject: sg: use blk_rq_aligned helper function Signed-off-by: FUJITA Tomonori Cc: Douglas Gilbert Cc: Jens Axboe Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d6391666502c..ed69292babde 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1656,11 +1656,9 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) sg_io_hdr_t *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; - unsigned long uaddr = (unsigned long)hp->dxferp; Sg_scatter_hold *req_schp = &srp->data; Sg_scatter_hold *rsv_schp = &sfp->reserve; struct request_queue *q = sfp->parentdp->device->request_queue; - unsigned long alignment = queue_dma_alignment(q) | q->dma_pad_mask; struct rq_map_data map_data; SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); @@ -1676,7 +1674,7 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (!sfp->parentdp->device->host->unchecked_isa_dma) && - !(uaddr & alignment) && !(dxfer_len & alignment)) + blk_rq_aligned(q, hp->dxferp, dxfer_len)) return sg_build_direct(srp, sfp, dxfer_len); #endif if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) -- cgit v1.2.3 From fad7f01e61bf737fe8a3740d803f000db57ecac6 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 16:20:20 +0900 Subject: sg: set dxferp to NULL for READ with the older SG interface With the older SG interface, we don't know a user-space address to trasfer data when executing a SCSI command. So we can't pass a user-space address to blk_rq_map_user. This patch fixes sg to pass a NULL user-space address to blk_rq_map_user so that it just sets up a request and bios with page frames propely without data transfer. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ed69292babde..50c07bca727d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -615,7 +615,10 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) else hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; hp->dxfer_len = mxsize; - hp->dxferp = (char __user *)buf + cmd_size; + if (hp->dxfer_direction == SG_DXFER_TO_DEV) + hp->dxferp = (char __user *)buf + cmd_size; + else + hp->dxferp = NULL; hp->sbp = NULL; hp->timeout = old_hdr.reply_len; /* structure abuse ... */ hp->flags = input_size; /* structure abuse ... */ -- cgit v1.2.3 From f98a8cae12f2b2a8f9bfd7a53c990a1a405e880e Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Thu, 4 Sep 2008 14:27:35 -0600 Subject: SCSI sd driver calls revalidate_disk wrapper. Modify the SCSI disk driver to call the revalidate_disk() wrapper. This allows us to do some housekeeping such as accounting for a disk being resized online. The wrapper will call sd_revalidate_disk() at the appropriate time. Signed-off-by: Andrew Patterson Signed-off-by: Jens Axboe --- drivers/scsi/sd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index bcb04b2a7676..cb115d1bf228 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -165,7 +165,7 @@ sd_store_cache_type(struct device *dev, struct device_attribute *attr, sd_print_sense_hdr(sdkp, &sshdr); return -EINVAL; } - sd_revalidate_disk(sdkp->disk); + revalidate_disk(sdkp->disk); return count; } @@ -916,7 +916,7 @@ static void sd_rescan(struct device *dev) struct scsi_disk *sdkp = scsi_disk_get_from_dev(dev); if (sdkp) { - sd_revalidate_disk(sdkp->disk); + revalidate_disk(sdkp->disk); scsi_disk_put(sdkp); } } -- cgit v1.2.3 From 242f9dcb8ba6f68fcd217a119a7648a4f69290e9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 14 Sep 2008 05:55:09 -0700 Subject: block: unify request timeout handling Right now SCSI and others do their own command timeout handling. Move those bits to the block layer. Instead of having a timer per command, we try to be a bit more clever and simply have one per-queue. This avoids the overhead of having to tear down and setup a timer for each command, so it will result in a lot less timer fiddling. Signed-off-by: Mike Anderson Signed-off-by: Jens Axboe --- block/Makefile | 4 +- block/blk-core.c | 7 ++ block/blk-settings.c | 12 +++ block/blk-softirq.c | 30 ++++--- block/blk-timeout.c | 155 +++++++++++++++++++++++++++++++++++ block/blk.h | 24 ++++++ block/elevator.c | 8 ++ drivers/ata/libata-eh.c | 13 +-- drivers/ata/libata.h | 2 +- drivers/scsi/aacraid/aachba.c | 2 +- drivers/scsi/gdth.c | 60 +++++++++----- drivers/scsi/gdth.h | 2 +- drivers/scsi/gdth_proc.c | 66 --------------- drivers/scsi/gdth_proc.h | 3 - drivers/scsi/ibmvscsi/ibmvscsi.c | 2 +- drivers/scsi/ide-scsi.c | 2 +- drivers/scsi/ipr.c | 3 +- drivers/scsi/ips.c | 2 +- drivers/scsi/libiscsi.c | 17 ++-- drivers/scsi/libsas/sas_ata.c | 2 +- drivers/scsi/libsas/sas_internal.h | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 30 +++---- drivers/scsi/megaraid/megaraid_sas.c | 6 +- drivers/scsi/ncr53c8xx.c | 4 +- drivers/scsi/qla1280.c | 4 +- drivers/scsi/qla4xxx/ql4_os.c | 4 +- drivers/scsi/scsi.c | 92 ++++----------------- drivers/scsi/scsi_error.c | 90 +++----------------- drivers/scsi/scsi_lib.c | 17 +++- drivers/scsi/scsi_priv.h | 7 +- drivers/scsi/scsi_sysfs.c | 7 +- drivers/scsi/scsi_transport_fc.c | 6 +- drivers/scsi/sd.c | 9 +- drivers/scsi/sr.c | 5 +- drivers/scsi/sym53c8xx_2/sym_glue.c | 4 +- include/linux/blkdev.h | 20 +++++ include/scsi/scsi_cmnd.h | 3 - include/scsi/scsi_host.h | 9 +- include/scsi/scsi_transport.h | 3 +- 39 files changed, 399 insertions(+), 339 deletions(-) create mode 100644 block/blk-timeout.c (limited to 'drivers/scsi') diff --git a/block/Makefile b/block/Makefile index 0da976ce67dd..bfe73049f939 100644 --- a/block/Makefile +++ b/block/Makefile @@ -4,8 +4,8 @@ obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \ - blk-exec.o blk-merge.o blk-softirq.o ioctl.o genhd.o \ - scsi_ioctl.o cmd-filter.o + blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \ + ioctl.o genhd.o scsi_ioctl.o cmd-filter.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o diff --git a/block/blk-core.c b/block/blk-core.c index f25eb9786d94..d768a8ddc173 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -110,6 +110,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq) memset(rq, 0, sizeof(*rq)); INIT_LIST_HEAD(&rq->queuelist); + INIT_LIST_HEAD(&rq->timeout_list); rq->cpu = -1; rq->q = q; rq->sector = rq->hard_sector = (sector_t) -1; @@ -490,6 +491,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id) } init_timer(&q->unplug_timer); + setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q); + INIT_LIST_HEAD(&q->timeout_list); kobject_init(&q->kobj, &blk_queue_ktype); @@ -897,6 +900,8 @@ EXPORT_SYMBOL(blk_start_queueing); */ void blk_requeue_request(struct request_queue *q, struct request *rq) { + blk_delete_timer(rq); + blk_clear_rq_complete(rq); blk_add_trace_rq(q, rq, BLK_TA_REQUEUE); if (blk_rq_tagged(rq)) @@ -1650,6 +1655,8 @@ static void end_that_request_last(struct request *req, int error) { struct gendisk *disk = req->rq_disk; + blk_delete_timer(req); + if (blk_rq_tagged(req)) blk_queue_end_tag(req->q, req); diff --git a/block/blk-settings.c b/block/blk-settings.c index d70692badcdb..1d0330d0b40a 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -77,6 +77,18 @@ void blk_queue_softirq_done(struct request_queue *q, softirq_done_fn *fn) } EXPORT_SYMBOL(blk_queue_softirq_done); +void blk_queue_rq_timeout(struct request_queue *q, unsigned int timeout) +{ + q->rq_timeout = timeout; +} +EXPORT_SYMBOL_GPL(blk_queue_rq_timeout); + +void blk_queue_rq_timed_out(struct request_queue *q, rq_timed_out_fn *fn) +{ + q->rq_timed_out_fn = fn; +} +EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out); + /** * blk_queue_make_request - define an alternate make_request function for a device * @q: the request queue for the device to be affected diff --git a/block/blk-softirq.c b/block/blk-softirq.c index 3a1af551191e..7ab344afb16f 100644 --- a/block/blk-softirq.c +++ b/block/blk-softirq.c @@ -101,18 +101,7 @@ static struct notifier_block __cpuinitdata blk_cpu_notifier = { .notifier_call = blk_cpu_notify, }; -/** - * blk_complete_request - end I/O on a request - * @req: the request being processed - * - * Description: - * Ends all I/O on a request. It does not handle partial completions, - * unless the driver actually implements this in its completion callback - * through requeueing. The actual completion happens out-of-order, - * through a softirq handler. The user must have registered a completion - * callback through blk_queue_softirq_done(). - **/ -void blk_complete_request(struct request *req) +void __blk_complete_request(struct request *req) { struct request_queue *q = req->q; unsigned long flags; @@ -151,6 +140,23 @@ do_local: local_irq_restore(flags); } + +/** + * blk_complete_request - end I/O on a request + * @req: the request being processed + * + * Description: + * Ends all I/O on a request. It does not handle partial completions, + * unless the driver actually implements this in its completion callback + * through requeueing. The actual completion happens out-of-order, + * through a softirq handler. The user must have registered a completion + * callback through blk_queue_softirq_done(). + **/ +void blk_complete_request(struct request *req) +{ + if (!blk_mark_rq_complete(req)) + __blk_complete_request(req); +} EXPORT_SYMBOL(blk_complete_request); __init int blk_softirq_init(void) diff --git a/block/blk-timeout.c b/block/blk-timeout.c new file mode 100644 index 000000000000..b36d07bf0afb --- /dev/null +++ b/block/blk-timeout.c @@ -0,0 +1,155 @@ +/* + * Functions related to generic timeout handling of requests. + */ +#include +#include +#include + +#include "blk.h" + +/* + * blk_delete_timer - Delete/cancel timer for a given function. + * @req: request that we are canceling timer for + * + */ +void blk_delete_timer(struct request *req) +{ + struct request_queue *q = req->q; + + /* + * Nothing to detach + */ + if (!q->rq_timed_out_fn || !req->deadline) + return; + + list_del_init(&req->timeout_list); + + if (list_empty(&q->timeout_list)) + del_timer(&q->timeout); +} + +static void blk_rq_timed_out(struct request *req) +{ + struct request_queue *q = req->q; + enum blk_eh_timer_return ret; + + ret = q->rq_timed_out_fn(req); + switch (ret) { + case BLK_EH_HANDLED: + __blk_complete_request(req); + break; + case BLK_EH_RESET_TIMER: + blk_clear_rq_complete(req); + blk_add_timer(req); + break; + case BLK_EH_NOT_HANDLED: + /* + * LLD handles this for now but in the future + * we can send a request msg to abort the command + * and we can move more of the generic scsi eh code to + * the blk layer. + */ + break; + default: + printk(KERN_ERR "block: bad eh return: %d\n", ret); + break; + } +} + +void blk_rq_timed_out_timer(unsigned long data) +{ + struct request_queue *q = (struct request_queue *) data; + unsigned long flags, uninitialized_var(next), next_set = 0; + struct request *rq, *tmp; + + spin_lock_irqsave(q->queue_lock, flags); + + list_for_each_entry_safe(rq, tmp, &q->timeout_list, timeout_list) { + if (time_after_eq(jiffies, rq->deadline)) { + list_del_init(&rq->timeout_list); + + /* + * Check if we raced with end io completion + */ + if (blk_mark_rq_complete(rq)) + continue; + blk_rq_timed_out(rq); + } + if (!next_set) { + next = rq->deadline; + next_set = 1; + } else if (time_after(next, rq->deadline)) + next = rq->deadline; + } + + if (next_set && !list_empty(&q->timeout_list)) + mod_timer(&q->timeout, round_jiffies(next)); + + spin_unlock_irqrestore(q->queue_lock, flags); +} + +/** + * blk_abort_request -- Request request recovery for the specified command + * @req: pointer to the request of interest + * + * This function requests that the block layer start recovery for the + * request by deleting the timer and calling the q's timeout function. + * LLDDs who implement their own error recovery MAY ignore the timeout + * event if they generated blk_abort_req. Must hold queue lock. + */ +void blk_abort_request(struct request *req) +{ + blk_delete_timer(req); + blk_rq_timed_out(req); +} +EXPORT_SYMBOL_GPL(blk_abort_request); + +/** + * blk_add_timer - Start timeout timer for a single request + * @req: request that is about to start running. + * + * Notes: + * Each request has its own timer, and as it is added to the queue, we + * set up the timer. When the request completes, we cancel the timer. + */ +void blk_add_timer(struct request *req) +{ + struct request_queue *q = req->q; + unsigned long expiry; + + if (!q->rq_timed_out_fn) + return; + + BUG_ON(!list_empty(&req->timeout_list)); + BUG_ON(test_bit(REQ_ATOM_COMPLETE, &req->atomic_flags)); + + if (req->timeout) + req->deadline = jiffies + req->timeout; + else { + req->deadline = jiffies + q->rq_timeout; + /* + * Some LLDs, like scsi, peek at the timeout to prevent + * a command from being retried forever. + */ + req->timeout = q->rq_timeout; + } + list_add_tail(&req->timeout_list, &q->timeout_list); + + /* + * If the timer isn't already pending or this timeout is earlier + * than an existing one, modify the timer. Round to next nearest + * second. + */ + expiry = round_jiffies(req->deadline); + + /* + * We use ->deadline == 0 to detect whether a timer was added or + * not, so just increase to next jiffy for that specific case + */ + if (unlikely(!req->deadline)) + req->deadline = 1; + + if (!timer_pending(&q->timeout) || + time_before(expiry, q->timeout.expires)) + mod_timer(&q->timeout, expiry); +} diff --git a/block/blk.h b/block/blk.h index de74254cb916..a4f4a50aefaa 100644 --- a/block/blk.h +++ b/block/blk.h @@ -17,6 +17,30 @@ void __blk_queue_free_tags(struct request_queue *q); void blk_unplug_work(struct work_struct *work); void blk_unplug_timeout(unsigned long data); +void blk_rq_timed_out_timer(unsigned long data); +void blk_delete_timer(struct request *); +void blk_add_timer(struct request *); + +/* + * Internal atomic flags for request handling + */ +enum rq_atomic_flags { + REQ_ATOM_COMPLETE = 0, +}; + +/* + * EH timer and IO completion will both attempt to 'grab' the request, make + * sure that only one of them suceeds + */ +static inline int blk_mark_rq_complete(struct request *rq) +{ + return test_and_set_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); +} + +static inline void blk_clear_rq_complete(struct request *rq) +{ + clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags); +} struct io_context *current_io_context(gfp_t gfp_flags, int node); diff --git a/block/elevator.c b/block/elevator.c index 8e3fc3afc77b..a91fc59edd01 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -36,6 +36,8 @@ #include #include +#include "blk.h" + static DEFINE_SPINLOCK(elv_list_lock); static LIST_HEAD(elv_list); @@ -771,6 +773,12 @@ struct request *elv_next_request(struct request_queue *q) */ rq->cmd_flags |= REQ_STARTED; blk_add_trace_rq(q, rq, BLK_TA_ISSUE); + + /* + * We are now handing the request to the hardware, + * add the timeout handler + */ + blk_add_timer(rq); } if (!q->boundary_rq || q->boundary_rq == rq) { diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index c1db2f234d2e..bd0b2bc76f10 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -33,6 +33,7 @@ */ #include +#include #include #include #include @@ -457,29 +458,29 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev, * RETURNS: * EH_HANDLED or EH_NOT_HANDLED */ -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) +enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) { struct Scsi_Host *host = cmd->device->host; struct ata_port *ap = ata_shost_to_port(host); unsigned long flags; struct ata_queued_cmd *qc; - enum scsi_eh_timer_return ret; + enum blk_eh_timer_return ret; DPRINTK("ENTER\n"); if (ap->ops->error_handler) { - ret = EH_NOT_HANDLED; + ret = BLK_EH_NOT_HANDLED; goto out; } - ret = EH_HANDLED; + ret = BLK_EH_HANDLED; spin_lock_irqsave(ap->lock, flags); qc = ata_qc_from_tag(ap, ap->link.active_tag); if (qc) { WARN_ON(qc->scsicmd != cmd); qc->flags |= ATA_QCFLAG_EH_SCHEDULED; qc->err_mask |= AC_ERR_TIMEOUT; - ret = EH_NOT_HANDLED; + ret = BLK_EH_NOT_HANDLED; } spin_unlock_irqrestore(ap->lock, flags); @@ -831,7 +832,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) * Note that ATA_QCFLAG_FAILED is unconditionally set after * this function completes. */ - scsi_req_abort_cmd(qc->scsicmd); + blk_abort_request(qc->scsicmd->request); } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index ade5c75b6144..24f5005478b0 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -152,7 +152,7 @@ extern int ata_bus_probe(struct ata_port *ap); /* libata-eh.c */ extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); extern void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd); -extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); +extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index aa4e77c25273..8abfd06b5a72 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -1139,7 +1139,7 @@ static struct aac_srb * aac_scsi_common(struct fib * fib, struct scsi_cmnd * cmd srbcmd->id = cpu_to_le32(scmd_id(cmd)); srbcmd->lun = cpu_to_le32(cmd->device->lun); srbcmd->flags = cpu_to_le32(flag); - timeout = cmd->timeout_per_command/HZ; + timeout = cmd->request->timeout/HZ; if (timeout == 0) timeout = 1; srbcmd->timeout = cpu_to_le32(timeout); // timeout in seconds diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 822d5214692b..c387c15a2128 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -464,7 +464,6 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd, /* use request field to save the ptr. to completion struct. */ scp->request = (struct request *)&wait; - scp->timeout_per_command = timeout*HZ; scp->cmd_len = 12; scp->cmnd = cmnd; cmndinfo.priority = IOCTL_PRI; @@ -1995,23 +1994,12 @@ static void gdth_putq(gdth_ha_str *ha, Scsi_Cmnd *scp, unchar priority) register Scsi_Cmnd *pscp; register Scsi_Cmnd *nscp; ulong flags; - unchar b, t; TRACE(("gdth_putq() priority %d\n",priority)); spin_lock_irqsave(&ha->smp_lock, flags); - if (!cmndinfo->internal_command) { + if (!cmndinfo->internal_command) cmndinfo->priority = priority; - b = scp->device->channel; - t = scp->device->id; - if (priority >= DEFAULT_PRI) { - if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) || - (b==ha->virt_bus && thdr[t].lock)) { - TRACE2(("gdth_putq(): locked IO ->update_timeout()\n")); - cmndinfo->timeout = gdth_update_timeout(scp, 0); - } - } - } if (ha->req_first==NULL) { ha->req_first = scp; /* queue was empty */ @@ -3899,6 +3887,39 @@ static const char *gdth_info(struct Scsi_Host *shp) return ((const char *)ha->binfo.type_string); } +static enum blk_eh_timer_return gdth_timed_out(struct scsi_cmnd *scp) +{ + gdth_ha_str *ha = shost_priv(scp->device->host); + struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); + unchar b, t; + ulong flags; + enum blk_eh_timer_return retval = BLK_EH_NOT_HANDLED; + + TRACE(("%s() cmd 0x%x\n", scp->cmnd[0], __func__)); + b = scp->device->channel; + t = scp->device->id; + + /* + * We don't really honor the command timeout, but we try to + * honor 6 times of the actual command timeout! So reset the + * timer if this is less than 6th timeout on this command! + */ + if (++cmndinfo->timeout_count < 6) + retval = BLK_EH_RESET_TIMER; + + /* Reset the timeout if it is locked IO */ + spin_lock_irqsave(&ha->smp_lock, flags); + if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha, b)].lock) || + (b == ha->virt_bus && t < MAX_HDRIVES && ha->hdr[t].lock)) { + TRACE2(("%s(): locked IO, reset timeout\n", __func__)); + retval = BLK_EH_RESET_TIMER; + } + spin_unlock_irqrestore(&ha->smp_lock, flags); + + return retval; +} + + static int gdth_eh_bus_reset(Scsi_Cmnd *scp) { gdth_ha_str *ha = shost_priv(scp->device->host); @@ -3992,7 +4013,7 @@ static int gdth_queuecommand(struct scsi_cmnd *scp, BUG_ON(!cmndinfo); scp->scsi_done = done; - gdth_update_timeout(scp, scp->timeout_per_command * 6); + cmndinfo->timeout_count = 0; cmndinfo->priority = DEFAULT_PRI; return __gdth_queuecommand(ha, scp, cmndinfo); @@ -4096,12 +4117,10 @@ static int ioc_lockdrv(void __user *arg) ha->hdr[j].lock = 1; spin_unlock_irqrestore(&ha->smp_lock, flags); gdth_wait_completion(ha, ha->bus_cnt, j); - gdth_stop_timeout(ha, ha->bus_cnt, j); } else { spin_lock_irqsave(&ha->smp_lock, flags); ha->hdr[j].lock = 0; spin_unlock_irqrestore(&ha->smp_lock, flags); - gdth_start_timeout(ha, ha->bus_cnt, j); gdth_next(ha); } } @@ -4539,18 +4558,14 @@ static int gdth_ioctl(struct inode *inode, struct file *filep, spin_lock_irqsave(&ha->smp_lock, flags); ha->raw[i].lock = 1; spin_unlock_irqrestore(&ha->smp_lock, flags); - for (j = 0; j < ha->tid_cnt; ++j) { + for (j = 0; j < ha->tid_cnt; ++j) gdth_wait_completion(ha, i, j); - gdth_stop_timeout(ha, i, j); - } } else { spin_lock_irqsave(&ha->smp_lock, flags); ha->raw[i].lock = 0; spin_unlock_irqrestore(&ha->smp_lock, flags); - for (j = 0; j < ha->tid_cnt; ++j) { - gdth_start_timeout(ha, i, j); + for (j = 0; j < ha->tid_cnt; ++j) gdth_next(ha); - } } } break; @@ -4644,6 +4659,7 @@ static struct scsi_host_template gdth_template = { .slave_configure = gdth_slave_configure, .bios_param = gdth_bios_param, .proc_info = gdth_proc_info, + .eh_timed_out = gdth_timed_out, .proc_name = "gdth", .can_queue = GDTH_MAXCMDS, .this_id = -1, diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index ca92476727cf..1646444e9bd5 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -916,7 +916,7 @@ typedef struct { gdth_cmd_str *internal_cmd_str; /* crier for internal messages*/ dma_addr_t sense_paddr; /* sense dma-addr */ unchar priority; - int timeout; + int timeout_count; /* # of timeout calls */ volatile int wait_for_completion; ushort status; ulong32 info; diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index ce0228e26aec..59349a316e13 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -748,69 +748,3 @@ static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id) } spin_unlock_irqrestore(&ha->smp_lock, flags); } - -static void gdth_stop_timeout(gdth_ha_str *ha, int busnum, int id) -{ - ulong flags; - Scsi_Cmnd *scp; - unchar b, t; - - spin_lock_irqsave(&ha->smp_lock, flags); - - for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { - struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); - if (!cmndinfo->internal_command) { - b = scp->device->channel; - t = scp->device->id; - if (t == (unchar)id && b == (unchar)busnum) { - TRACE2(("gdth_stop_timeout(): update_timeout()\n")); - cmndinfo->timeout = gdth_update_timeout(scp, 0); - } - } - } - spin_unlock_irqrestore(&ha->smp_lock, flags); -} - -static void gdth_start_timeout(gdth_ha_str *ha, int busnum, int id) -{ - ulong flags; - Scsi_Cmnd *scp; - unchar b, t; - - spin_lock_irqsave(&ha->smp_lock, flags); - - for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { - struct gdth_cmndinfo *cmndinfo = gdth_cmnd_priv(scp); - if (!cmndinfo->internal_command) { - b = scp->device->channel; - t = scp->device->id; - if (t == (unchar)id && b == (unchar)busnum) { - TRACE2(("gdth_start_timeout(): update_timeout()\n")); - gdth_update_timeout(scp, cmndinfo->timeout); - } - } - } - spin_unlock_irqrestore(&ha->smp_lock, flags); -} - -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout) -{ - int oldto; - - oldto = scp->timeout_per_command; - scp->timeout_per_command = timeout; - - if (timeout == 0) { - del_timer(&scp->eh_timeout); - scp->eh_timeout.data = (unsigned long) NULL; - scp->eh_timeout.expires = 0; - } else { - if (scp->eh_timeout.data != (unsigned long) NULL) - del_timer(&scp->eh_timeout); - scp->eh_timeout.data = (unsigned long) scp; - scp->eh_timeout.expires = jiffies + timeout; - add_timer(&scp->eh_timeout); - } - - return oldto; -} diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h index 45e6fdacf36e..9b900cc9ebe8 100644 --- a/drivers/scsi/gdth_proc.h +++ b/drivers/scsi/gdth_proc.h @@ -20,9 +20,6 @@ static char *gdth_ioctl_alloc(gdth_ha_str *ha, int size, int scratch, ulong64 *paddr); static void gdth_ioctl_free(gdth_ha_str *ha, int size, char *buf, ulong64 paddr); static void gdth_wait_completion(gdth_ha_str *ha, int busnum, int id); -static void gdth_stop_timeout(gdth_ha_str *ha, int busnum, int id); -static void gdth_start_timeout(gdth_ha_str *ha, int busnum, int id); -static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout); #endif diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 7b1502c0ab6e..87e09f35d3d4 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -756,7 +756,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, init_event_struct(evt_struct, handle_cmd_rsp, VIOSRP_SRP_FORMAT, - cmnd->timeout_per_command/HZ); + cmnd->request->timeout/HZ); evt_struct->cmnd = cmnd; evt_struct->cmnd_done = done; diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 461331d3dc45..81c16cba5417 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -612,7 +612,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd, pc->req_xfer = pc->buf_size = scsi_bufflen(cmd); pc->scsi_cmd = cmd; pc->done = done; - pc->timeout = jiffies + cmd->timeout_per_command; + pc->timeout = jiffies + cmd->request->timeout; if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e7a3a6554425..d30eb7ba018e 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3670,7 +3670,8 @@ static int ipr_slave_configure(struct scsi_device *sdev) sdev->no_uld_attach = 1; } if (ipr_is_vset_device(res)) { - sdev->timeout = IPR_VSET_RW_TIMEOUT; + blk_queue_rq_timeout(sdev->request_queue, + IPR_VSET_RW_TIMEOUT); blk_queue_max_sectors(sdev->request_queue, IPR_VSET_MAX_SECTORS); } if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res)) diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index bc9e6ddf41df..ef683f0d2b5a 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -3818,7 +3818,7 @@ ips_send_cmd(ips_ha_t * ha, ips_scb_t * scb) scb->cmd.dcdb.segment_4G = 0; scb->cmd.dcdb.enhanced_sg = 0; - TimeOut = scb->scsi_cmd->timeout_per_command; + TimeOut = scb->scsi_cmd->request->timeout; if (ha->subsys->param[4] & 0x00100000) { /* If NEW Tape DCDB is Supported */ if (!scb->sg_len) { diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 299e075a7b34..1eca82420aab 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1476,12 +1476,12 @@ static void iscsi_start_tx(struct iscsi_conn *conn) scsi_queue_work(conn->session->host, &conn->xmitwork); } -static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) +static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) { struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_conn *conn; - enum scsi_eh_timer_return rc = EH_NOT_HANDLED; + enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; cls_session = starget_to_session(scsi_target(scmd->device)); session = cls_session->dd_data; @@ -1494,14 +1494,14 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) * We are probably in the middle of iscsi recovery so let * that complete and handle the error. */ - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; goto done; } conn = session->leadconn; if (!conn) { /* In the middle of shuting down */ - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; goto done; } @@ -1513,20 +1513,21 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) */ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + (conn->ping_timeout * HZ), jiffies)) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; /* * if we are about to check the transport then give the command * more time */ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ), jiffies)) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; /* if in the middle of checking the transport then give us more time */ if (conn->ping_task) - rc = EH_RESET_TIMER; + rc = BLK_EH_RESET_TIMER; done: spin_unlock(&session->lock); - debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh"); + debug_scsi("return %s\n", rc == BLK_EH_RESET_TIMER ? + "timer reset" : "nh"); return rc; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48ee8c7f5bdd..837b095ba90d 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -398,7 +398,7 @@ void sas_ata_task_abort(struct sas_task *task) /* Bounce SCSI-initiated commands to the SCSI EH */ if (qc->scsicmd) { - scsi_req_abort_cmd(qc->scsicmd); + blk_abort_request(qc->scsicmd->request); scsi_schedule_eh(qc->scsicmd->device->host); return; } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index b4f9368f116a..0001374bd6b2 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -55,7 +55,7 @@ void sas_unregister_phys(struct sas_ha_struct *sas_ha); int sas_register_ports(struct sas_ha_struct *sas_ha); void sas_unregister_ports(struct sas_ha_struct *sas_ha); -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index a8e3ef309070..744838780ada 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -673,43 +673,43 @@ out: return; } -enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) +enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); unsigned long flags; if (!task) { - cmd->timeout_per_command /= 2; + cmd->request->timeout /= 2; SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n", - cmd, task, (cmd->timeout_per_command ? - "EH_RESET_TIMER" : "EH_NOT_HANDLED")); - if (!cmd->timeout_per_command) - return EH_NOT_HANDLED; - return EH_RESET_TIMER; + cmd, task, (cmd->request->timeout ? + "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED")); + if (!cmd->request->timeout) + return BLK_EH_NOT_HANDLED; + return BLK_EH_RESET_TIMER; } spin_lock_irqsave(&task->task_state_lock, flags); BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", - cmd, task); - return EH_HANDLED; + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: " + "BLK_EH_HANDLED\n", cmd, task); + return BLK_EH_HANDLED; } if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: " - "EH_RESET_TIMER\n", + "BLK_EH_RESET_TIMER\n", cmd, task); - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; } task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n", + SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n", cmd, task); - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) @@ -1039,7 +1039,7 @@ void sas_task_abort(struct sas_task *task) return; } - scsi_req_abort_cmd(sc); + blk_abort_request(sc->request); scsi_schedule_eh(sc->device->host); } diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 97b763378e7d..afe1de998763 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -1167,7 +1167,7 @@ static int megasas_generic_reset(struct scsi_cmnd *scmd) * cmd has not been completed within the timeout period. */ static enum -scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) +blk_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) { struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr; struct megasas_instance *instance; @@ -1175,7 +1175,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) if (time_after(jiffies, scmd->jiffies_at_alloc + (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) { - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } instance = cmd->instance; @@ -1189,7 +1189,7 @@ scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) spin_unlock_irqrestore(instance->host->host_lock, flags); } - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; } /** diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index c57c94c0ffd2..3b7240e40819 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -4170,8 +4170,8 @@ static int ncr_queue_command (struct ncb *np, struct scsi_cmnd *cmd) ** **---------------------------------------------------- */ - if (np->settle_time && cmd->timeout_per_command >= HZ) { - u_long tlimit = jiffies + cmd->timeout_per_command - HZ; + if (np->settle_time && cmd->request->timeout >= HZ) { + u_long tlimit = jiffies + cmd->request->timeout - HZ; if (time_after(np->settle_time, tlimit)) np->settle_time = tlimit; } diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 37f9ba0cd798..b6cd12b2e996 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -2845,7 +2845,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); + pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); @@ -3114,7 +3114,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); + pkt->timeout = cpu_to_le16(cmd->request->timeout/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 88bebb13bc52..de8279ad7d89 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1542,7 +1542,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) DEBUG2(printk(KERN_INFO "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x," "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no, - cmd, jiffies, cmd->timeout_per_command / HZ, + cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); /* FIXME: wait for hba to go online */ @@ -1598,7 +1598,7 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd) DEBUG2(printk(KERN_INFO "scsi%ld: TARGET_DEVICE_RESET cmd=%p jiffies = 0x%lx, " "to=%x,dpc_flags=%lx, status=%x allowed=%d\n", - ha->host_no, cmd, jiffies, cmd->timeout_per_command / HZ, + ha->host_no, cmd, jiffies, cmd->request->timeout / HZ, ha->dpc_flags, cmd->result, cmd->allowed)); stat = qla4xxx_reset_target(ha, ddb_entry); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index ee6be596503d..dbeb86cafc0d 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -291,7 +291,6 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) unsigned long flags; cmd->device = dev; - init_timer(&cmd->eh_timeout); INIT_LIST_HEAD(&cmd->list); spin_lock_irqsave(&dev->list_lock, flags); list_add_tail(&cmd->list, &dev->cmd_list); @@ -652,14 +651,19 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) unsigned long timeout; int rtn = 0; + /* + * We will use a queued command if possible, otherwise we will + * emulate the queuing and calling of completion function ourselves. + */ + atomic_inc(&cmd->device->iorequest_cnt); + /* check if the device is still usable */ if (unlikely(cmd->device->sdev_state == SDEV_DEL)) { /* in SDEV_DEL we error all commands. DID_NO_CONNECT * returns an immediate error upwards, and signals * that the device is no longer present */ cmd->result = DID_NO_CONNECT << 16; - atomic_inc(&cmd->device->iorequest_cnt); - __scsi_done(cmd); + scsi_done(cmd); /* return 0 (because the command has been processed) */ goto out; } @@ -672,6 +676,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) * future requests should not occur until the device * transitions out of the suspend state. */ + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n")); @@ -714,20 +719,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) host->resetting = 0; } - /* - * AK: unlikely race here: for some reason the timer could - * expire before the serial number is set up below. - */ - scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out); - scsi_log_send(cmd); - /* - * We will use a queued command if possible, otherwise we will - * emulate the queuing and calling of completion function ourselves. - */ - atomic_inc(&cmd->device->iorequest_cnt); - /* * Before we queue this command, check if the command * length exceeds what the host adapter can handle. @@ -744,6 +737,12 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) } spin_lock_irqsave(host->host_lock, flags); + /* + * AK: unlikely race here: for some reason the timer could + * expire before the serial number is set up below. + * + * TODO: kill serial or move to blk layer + */ scsi_cmd_get_serial(host, cmd); if (unlikely(host->shost_state == SHOST_DEL)) { @@ -754,12 +753,8 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) } spin_unlock_irqrestore(host->host_lock, flags); if (rtn) { - if (scsi_delete_timer(cmd)) { - atomic_inc(&cmd->device->iodone_cnt); - scsi_queue_insert(cmd, - (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ? - rtn : SCSI_MLQUEUE_HOST_BUSY); - } + scsi_queue_insert(cmd, (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ? + rtn : SCSI_MLQUEUE_HOST_BUSY); SCSI_LOG_MLQUEUE(3, printk("queuecommand : request rejected\n")); } @@ -769,24 +764,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) return rtn; } -/** - * scsi_req_abort_cmd -- Request command recovery for the specified command - * @cmd: pointer to the SCSI command of interest - * - * This function requests that SCSI Core start recovery for the - * command by deleting the timer and adding the command to the eh - * queue. It can be called by either LLDDs or SCSI Core. LLDDs who - * implement their own error recovery MAY ignore the timeout event if - * they generated scsi_req_abort_cmd. - */ -void scsi_req_abort_cmd(struct scsi_cmnd *cmd) -{ - if (!scsi_delete_timer(cmd)) - return; - scsi_times_out(cmd); -} -EXPORT_SYMBOL(scsi_req_abort_cmd); - /** * scsi_done - Enqueue the finished SCSI command into the done queue. * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives @@ -802,42 +779,7 @@ EXPORT_SYMBOL(scsi_req_abort_cmd); */ static void scsi_done(struct scsi_cmnd *cmd) { - /* - * We don't have to worry about this one timing out anymore. - * If we are unable to remove the timer, then the command - * has already timed out. In which case, we have no choice but to - * let the timeout function run, as we have no idea where in fact - * that function could really be. It might be on another processor, - * etc, etc. - */ - if (!scsi_delete_timer(cmd)) - return; - __scsi_done(cmd); -} - -/* Private entry to scsi_done() to complete a command when the timer - * isn't running --- used by scsi_times_out */ -void __scsi_done(struct scsi_cmnd *cmd) -{ - struct request *rq = cmd->request; - - /* - * Set the serial numbers back to zero - */ - cmd->serial_number = 0; - - atomic_inc(&cmd->device->iodone_cnt); - if (cmd->result) - atomic_inc(&cmd->device->ioerr_cnt); - - BUG_ON(!rq); - - /* - * The uptodate/nbytes values don't matter, as we allow partial - * completes and thus will check this in the softirq callback - */ - rq->completion_data = cmd; - blk_complete_request(rq); + blk_complete_request(cmd->request); } /* Move this to a header if it becomes more generally useful */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 39ce3aba1dac..fecefa05cb62 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -111,70 +111,9 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) return ret; } -/** - * scsi_add_timer - Start timeout timer for a single scsi command. - * @scmd: scsi command that is about to start running. - * @timeout: amount of time to allow this command to run. - * @complete: timeout function to call if timer isn't canceled. - * - * Notes: - * This should be turned into an inline function. Each scsi command - * has its own timer, and as it is added to the queue, we set up the - * timer. When the command completes, we cancel the timer. - */ -void scsi_add_timer(struct scsi_cmnd *scmd, int timeout, - void (*complete)(struct scsi_cmnd *)) -{ - - /* - * If the clock was already running for this command, then - * first delete the timer. The timer handling code gets rather - * confused if we don't do this. - */ - if (scmd->eh_timeout.function) - del_timer(&scmd->eh_timeout); - - scmd->eh_timeout.data = (unsigned long)scmd; - scmd->eh_timeout.expires = jiffies + timeout; - scmd->eh_timeout.function = (void (*)(unsigned long)) complete; - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:" - " %d, (%p)\n", __func__, - scmd, timeout, complete)); - - add_timer(&scmd->eh_timeout); -} - -/** - * scsi_delete_timer - Delete/cancel timer for a given function. - * @scmd: Cmd that we are canceling timer for - * - * Notes: - * This should be turned into an inline function. - * - * Return value: - * 1 if we were able to detach the timer. 0 if we blew it, and the - * timer function has already started to run. - */ -int scsi_delete_timer(struct scsi_cmnd *scmd) -{ - int rtn; - - rtn = del_timer(&scmd->eh_timeout); - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p," - " rtn: %d\n", __func__, - scmd, rtn)); - - scmd->eh_timeout.data = (unsigned long)NULL; - scmd->eh_timeout.function = NULL; - - return rtn; -} - /** * scsi_times_out - Timeout function for normal scsi commands. - * @scmd: Cmd that is timing out. + * @req: request that is timing out. * * Notes: * We do not need to lock this. There is the potential for a race @@ -182,9 +121,11 @@ int scsi_delete_timer(struct scsi_cmnd *scmd) * normal completion function determines that the timer has already * fired, then it mustn't do anything. */ -void scsi_times_out(struct scsi_cmnd *scmd) +enum blk_eh_timer_return scsi_times_out(struct request *req) { - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + struct scsi_cmnd *scmd = req->special; + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; scsi_log_completion(scmd, TIMEOUT_ERROR); @@ -196,22 +137,20 @@ void scsi_times_out(struct scsi_cmnd *scmd) eh_timed_out = NULL; if (eh_timed_out) - switch (eh_timed_out(scmd)) { - case EH_HANDLED: - __scsi_done(scmd); - return; - case EH_RESET_TIMER: - scsi_add_timer(scmd, scmd->timeout_per_command, - scsi_times_out); - return; - case EH_NOT_HANDLED: + rtn = eh_timed_out(scmd); + switch (rtn) { + case BLK_EH_NOT_HANDLED: break; + default: + return rtn; } if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { scmd->result |= DID_TIME_OUT << 16; - __scsi_done(scmd); + return BLK_EH_HANDLED; } + + return BLK_EH_NOT_HANDLED; } /** @@ -1793,7 +1732,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) blk_rq_init(NULL, &req); scmd->request = &req; - memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout)); scmd->cmnd = req.cmd; @@ -1804,8 +1742,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) scmd->sc_data_direction = DMA_BIDIRECTIONAL; - init_timer(&scmd->eh_timeout); - spin_lock_irqsave(shost->host_lock, flags); shost->tmf_in_progress = 1; spin_unlock_irqrestore(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 62307bd794a9..e7686500e9dd 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1181,7 +1181,6 @@ int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) cmd->transfersize = req->data_len; cmd->allowed = req->retries; - cmd->timeout_per_command = req->timeout; return BLKPREP_OK; } EXPORT_SYMBOL(scsi_setup_blk_pc_cmnd); @@ -1416,17 +1415,26 @@ static void scsi_kill_request(struct request *req, struct request_queue *q) spin_unlock(shost->host_lock); spin_lock(sdev->request_queue->queue_lock); - __scsi_done(cmd); + blk_complete_request(req); } static void scsi_softirq_done(struct request *rq) { - struct scsi_cmnd *cmd = rq->completion_data; - unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command; + struct scsi_cmnd *cmd = rq->special; + unsigned long wait_for = (cmd->allowed + 1) * rq->timeout; int disposition; INIT_LIST_HEAD(&cmd->eh_entry); + /* + * Set the serial numbers back to zero + */ + cmd->serial_number = 0; + + atomic_inc(&cmd->device->iodone_cnt); + if (cmd->result) + atomic_inc(&cmd->device->ioerr_cnt); + disposition = scsi_decide_disposition(cmd); if (disposition != SUCCESS && time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) { @@ -1675,6 +1683,7 @@ struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) blk_queue_prep_rq(q, scsi_prep_fn); blk_queue_softirq_done(q, scsi_softirq_done); + blk_queue_rq_timed_out(q, scsi_times_out); return q; } diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 79f0f7511204..6cddd5dd323c 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -4,6 +4,7 @@ #include struct request_queue; +struct request; struct scsi_cmnd; struct scsi_device; struct scsi_host_template; @@ -27,7 +28,6 @@ extern void scsi_exit_hosts(void); extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd); extern int scsi_setup_command_freelist(struct Scsi_Host *shost); extern void scsi_destroy_command_freelist(struct Scsi_Host *shost); -extern void __scsi_done(struct scsi_cmnd *cmd); #ifdef CONFIG_SCSI_LOGGING void scsi_log_send(struct scsi_cmnd *cmd); void scsi_log_completion(struct scsi_cmnd *cmd, int disposition); @@ -49,10 +49,7 @@ extern int __init scsi_init_devinfo(void); extern void scsi_exit_devinfo(void); /* scsi_error.c */ -extern void scsi_add_timer(struct scsi_cmnd *, int, - void (*)(struct scsi_cmnd *)); -extern int scsi_delete_timer(struct scsi_cmnd *); -extern void scsi_times_out(struct scsi_cmnd *cmd); +extern enum blk_eh_timer_return scsi_times_out(struct request *req); extern int scsi_error_handler(void *host); extern int scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ab3c71869be5..7f618ee5ecea 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -560,12 +560,15 @@ sdev_rd_attr (vendor, "%.8s\n"); sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); +/* + * TODO: can we make these symlinks to the block layer ones? + */ static ssize_t sdev_show_timeout (struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev; sdev = to_scsi_device(dev); - return snprintf (buf, 20, "%d\n", sdev->timeout / HZ); + return snprintf(buf, 20, "%d\n", sdev->request_queue->rq_timeout / HZ); } static ssize_t @@ -576,7 +579,7 @@ sdev_store_timeout (struct device *dev, struct device_attribute *attr, int timeout; sdev = to_scsi_device(dev); sscanf (buf, "%d\n", &timeout); - sdev->timeout = timeout * HZ; + blk_queue_rq_timeout(sdev->request_queue, timeout * HZ); return count; } static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 56823fd1fb84..9168883d0dfe 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -1950,15 +1950,15 @@ static int fc_vport_match(struct attribute_container *cont, * Notes: * This routine assumes no locks are held on entry. */ -static enum scsi_eh_timer_return +static enum blk_eh_timer_return fc_timed_out(struct scsi_cmnd *scmd) { struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device)); if (rport->port_state == FC_PORTSTATE_BLOCKED) - return EH_RESET_TIMER; + return BLK_EH_RESET_TIMER; - return EH_NOT_HANDLED; + return BLK_EH_NOT_HANDLED; } /* diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index cb115d1bf228..c0cf4acda7de 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -383,7 +383,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) sector_t block = rq->sector; sector_t threshold; unsigned int this_count = rq->nr_sectors; - unsigned int timeout = sdp->timeout; int ret; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { @@ -584,7 +583,6 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq) SCpnt->transfersize = sdp->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = SD_MAX_RETRIES; - SCpnt->timeout_per_command = timeout; /* * This indicates that the command is ready from our end to be @@ -1878,11 +1876,12 @@ static int sd_probe(struct device *dev) sdkp->openers = 0; sdkp->previous_state = 1; - if (!sdp->timeout) { + if (!sdp->request_queue->rq_timeout) { if (sdp->type != TYPE_MOD) - sdp->timeout = SD_TIMEOUT; + blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT); else - sdp->timeout = SD_MOD_TIMEOUT; + blk_queue_rq_timeout(sdp->request_queue, + SD_MOD_TIMEOUT); } device_initialize(&sdkp->dev); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 8dbe3798d5fd..0f17009c99d2 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -331,7 +331,7 @@ static int sr_done(struct scsi_cmnd *SCpnt) static int sr_prep_fn(struct request_queue *q, struct request *rq) { - int block=0, this_count, s_size, timeout = SR_TIMEOUT; + int block = 0, this_count, s_size; struct scsi_cd *cd; struct scsi_cmnd *SCpnt; struct scsi_device *sdp = q->queuedata; @@ -461,7 +461,6 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq) SCpnt->transfersize = cd->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; - SCpnt->timeout_per_command = timeout; /* * This indicates that the command is ready from our end to be @@ -620,6 +619,8 @@ static int sr_probe(struct device *dev) disk->fops = &sr_bdops; disk->flags = GENHD_FL_CD; + blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT); + cd->device = sdev; cd->disk = disk; cd->driver = &sr_template; diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index d39107b7669b..f4e6cde1fd0d 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c @@ -519,8 +519,8 @@ static int sym53c8xx_queue_command(struct scsi_cmnd *cmd, * Shorten our settle_time if needed for * this command not to time out. */ - if (np->s.settle_time_valid && cmd->timeout_per_command) { - unsigned long tlimit = jiffies + cmd->timeout_per_command; + if (np->s.settle_time_valid && cmd->request->timeout) { + unsigned long tlimit = jiffies + cmd->request->timeout; tlimit -= SYM_CONF_TIMER_INTERVAL*2; if (time_after(np->s.settle_time, tlimit)) { np->s.settle_time = tlimit; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9c2549260427..067f28b80072 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -147,6 +147,7 @@ struct request { unsigned int cmd_flags; enum rq_cmd_type_bits cmd_type; + unsigned long atomic_flags; /* Maintain bio traversal state for part by part I/O submission. * hard_* are block layer internals, no driver should touch them! @@ -214,6 +215,8 @@ struct request { void *data; void *sense; + unsigned long deadline; + struct list_head timeout_list; unsigned int timeout; int retries; @@ -266,6 +269,14 @@ typedef void (prepare_flush_fn) (struct request_queue *, struct request *); typedef void (softirq_done_fn)(struct request *); typedef int (dma_drain_needed_fn)(struct request *); +enum blk_eh_timer_return { + BLK_EH_NOT_HANDLED, + BLK_EH_HANDLED, + BLK_EH_RESET_TIMER, +}; + +typedef enum blk_eh_timer_return (rq_timed_out_fn)(struct request *); + enum blk_queue_state { Queue_down, Queue_up, @@ -311,6 +322,7 @@ struct request_queue merge_bvec_fn *merge_bvec_fn; prepare_flush_fn *prepare_flush_fn; softirq_done_fn *softirq_done_fn; + rq_timed_out_fn *rq_timed_out_fn; dma_drain_needed_fn *dma_drain_needed; /* @@ -386,6 +398,10 @@ struct request_queue unsigned int nr_sorted; unsigned int in_flight; + unsigned int rq_timeout; + struct timer_list timeout; + struct list_head timeout_list; + /* * sg stuff */ @@ -770,6 +786,8 @@ extern int blk_end_request_callback(struct request *rq, int error, unsigned int nr_bytes, int (drv_callback)(struct request *)); extern void blk_complete_request(struct request *); +extern void __blk_complete_request(struct request *); +extern void blk_abort_request(struct request *); /* * blk_end_request() takes bytes instead of sectors as a complete size. @@ -811,6 +829,8 @@ extern void blk_queue_dma_alignment(struct request_queue *, int); extern void blk_queue_update_dma_alignment(struct request_queue *, int); extern void blk_queue_softirq_done(struct request_queue *, softirq_done_fn *); extern void blk_queue_set_discard(struct request_queue *, prepare_discard_fn *); +extern void blk_queue_rq_timed_out(struct request_queue *, rq_timed_out_fn *); +extern void blk_queue_rq_timeout(struct request_queue *, unsigned int); extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev); extern int blk_queue_ordered(struct request_queue *, unsigned, prepare_flush_fn *); extern int blk_do_ordered(struct request_queue *, struct request **); diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index f9f6e793575c..855bf95963e7 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -75,7 +75,6 @@ struct scsi_cmnd { int retries; int allowed; - int timeout_per_command; unsigned char prot_op; unsigned char prot_type; @@ -86,7 +85,6 @@ struct scsi_cmnd { /* These elements define the operation we are about to perform */ unsigned char *cmnd; - struct timer_list eh_timeout; /* Used to time out the command. */ /* These elements define the operation we ultimately want to perform */ struct scsi_data_buffer sdb; @@ -139,7 +137,6 @@ extern void scsi_put_command(struct scsi_cmnd *); extern void __scsi_put_command(struct Scsi_Host *, struct scsi_cmnd *, struct device *); extern void scsi_finish_command(struct scsi_cmnd *cmd); -extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd); extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, size_t *offset, size_t *len); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 44a55d1bf530..d123ca84e732 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -43,13 +43,6 @@ struct blk_queue_tags; #define DISABLE_CLUSTERING 0 #define ENABLE_CLUSTERING 1 -enum scsi_eh_timer_return { - EH_NOT_HANDLED, - EH_HANDLED, - EH_RESET_TIMER, -}; - - struct scsi_host_template { struct module *module; const char *name; @@ -347,7 +340,7 @@ struct scsi_host_template { * * Status: OPTIONAL */ - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); /* * Name of proc directory diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index 490bd13a634c..0de32cd4e8a7 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -21,6 +21,7 @@ #define SCSI_TRANSPORT_H #include +#include #include #include @@ -64,7 +65,7 @@ struct scsi_transport_template { * begin counting again * EH_NOT_HANDLED Begin normal error recovery */ - enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *); + enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); /* * Used as callback for the completion of i_t_nexus request -- cgit v1.2.3 From a91a3a20e06621b9931793888583efe37db4e4e8 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:01 +0900 Subject: sg: rename sg_cmd_done sg_rq_end_io old sg_rq_end_io() was used to wrap sg_cmd_done during converting sg to use the block layer (in order to cover the difference scsi_execute_async and blk_execute_rq_nowait). Now we don't need it so let's remove it. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 50c07bca727d..d18f90d1d9ad 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -177,7 +177,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ static int sg_fasync(int fd, struct file *filp, int mode); /* tasklet or soft irq callback */ -static void sg_cmd_done(void *data, char *sense, int result, int resid); +static void sg_rq_end_io(struct request *rq, int uptodate); static int sg_start_req(Sg_request *srp, unsigned char *cmd); static void sg_finish_rem_req(Sg_request * srp); static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); @@ -227,11 +227,6 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) cmd, filp->f_mode & FMODE_WRITE); } -static void sg_rq_end_io(struct request *rq, int uptodate) -{ - sg_cmd_done(rq->end_io_data, rq->sense, rq->errors, rq->data_len); -} - static int sg_open(struct inode *inode, struct file *filp) { @@ -1257,16 +1252,19 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) return 0; } -/* This function is a "bottom half" handler that is called by the - * mid level when a command is completed (or has failed). */ -static void -sg_cmd_done(void *data, char *sense, int result, int resid) +/* + * This function is a "bottom half" handler that is called by the mid + * level when a command is completed (or has failed). + */ +static void sg_rq_end_io(struct request *rq, int uptodate) { - Sg_request *srp = data; + struct sg_request *srp = rq->end_io_data; Sg_device *sdp = NULL; Sg_fd *sfp; unsigned long iflags; unsigned int ms; + char *sense; + int result, resid; if (NULL == srp) { printk(KERN_ERR "sg_cmd_done: NULL request\n"); @@ -1280,6 +1278,9 @@ sg_cmd_done(void *data, char *sense, int result, int resid) return; } + sense = rq->sense; + result = rq->errors; + resid = rq->data_len; SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: %s, pack_id=%d, res=0x%x\n", sdp->disk->disk_name, srp->header.pack_id, result)); -- cgit v1.2.3 From 7e56cb0f7e7a132803ffefa0a5a15fb2079afaf1 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:02 +0900 Subject: sg: remove SG_ALLOW_DIO_CODE define sg had lots of the own functions for the direct IO but now sg uses the block layer functions for it. There are only five lines for the direct IO. SG_ALLOW_DIO_CODE define was used to compile out the direct IO code but we don't need the define. If someone wants to remove the direct IO code, he can do easily without the define. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d18f90d1d9ad..2c30331abbed 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -68,7 +68,6 @@ static void sg_proc_cleanup(void); #endif #define SG_ALLOW_DIO_DEF 0 -#define SG_ALLOW_DIO_CODE /* compile out by commenting this define */ #define SG_MAX_DEVS 32768 @@ -1674,13 +1673,12 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; -#ifdef SG_ALLOW_DIO_CODE if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && (!sfp->parentdp->device->host->unchecked_isa_dma) && blk_rq_aligned(q, hp->dxferp, dxfer_len)) return sg_build_direct(srp, sfp, dxfer_len); -#endif + if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) sg_link_reserve(sfp, srp, dxfer_len); else -- cgit v1.2.3 From fd1c1de0766844af4cfc39298e109ad273e72a9e Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:03 +0900 Subject: sg: remove b_malloc_len in sg_scatter_hold struct It's not used for anything useful after the block layer conversion. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2c30331abbed..ccce31a400ea 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -116,7 +116,6 @@ typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */ unsigned bufflen; /* Size of (aggregate) data buffer */ - unsigned b_malloc_len; /* actual len malloc'ed in buffer */ struct page **pages; int page_order; char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ @@ -1986,7 +1985,6 @@ sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) req_schp->pages = rsv_schp->pages; req_schp->bufflen = size; - req_schp->b_malloc_len = rsv_schp->b_malloc_len; req_schp->page_order = rsv_schp->page_order; break; } else -- cgit v1.2.3 From 44c7b0eaa041007066e30ab4869d5bbf8dad5989 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:04 +0900 Subject: sg: remove __sg_start_req __sg_start_req() was used temporarily to call blk_get_request() during converting sg to use the block layer. Now sg always calls blk_get_request() so we can move blk_get_request() to sg_start_req(). We don't need __sg_start_req anymore. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ccce31a400ea..9a56c0d320bf 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1626,14 +1626,23 @@ exit_sg(void) idr_destroy(&sg_index_idr); } -static int __sg_start_req(struct sg_request *srp, struct sg_io_hdr *hp, - unsigned char *cmd) +static int sg_start_req(Sg_request *srp, unsigned char *cmd) { - struct sg_fd *sfp = srp->parentfp; - struct request_queue *q = sfp->parentdp->device->request_queue; + int res = 0; struct request *rq; + Sg_fd *sfp = srp->parentfp; + sg_io_hdr_t *hp = &srp->header; + int dxfer_len = (int) hp->dxfer_len; + int dxfer_dir = hp->dxfer_direction; + Sg_scatter_hold *req_schp = &srp->data; + Sg_scatter_hold *rsv_schp = &sfp->reserve; + struct request_queue *q = sfp->parentdp->device->request_queue; + struct rq_map_data map_data; int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; + SCSI_LOG_TIMEOUT(4, printk(KERN_INFO "sg_start_req: dxfer_len=%d\n", + dxfer_len)); + rq = blk_get_request(q, rw, GFP_ATOMIC); if (!rq) return -ENOMEM; @@ -1648,27 +1657,6 @@ static int __sg_start_req(struct sg_request *srp, struct sg_io_hdr *hp, rq->sense = srp->sense_b; rq->retries = SG_DEFAULT_RETRIES; - return 0; -} - -static int sg_start_req(Sg_request *srp, unsigned char *cmd) -{ - int res; - Sg_fd *sfp = srp->parentfp; - sg_io_hdr_t *hp = &srp->header; - int dxfer_len = (int) hp->dxfer_len; - int dxfer_dir = hp->dxfer_direction; - Sg_scatter_hold *req_schp = &srp->data; - Sg_scatter_hold *rsv_schp = &sfp->reserve; - struct request_queue *q = sfp->parentdp->device->request_queue; - struct rq_map_data map_data; - - SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); - - res = __sg_start_req(srp, hp, cmd); - if (res) - return res; - if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; -- cgit v1.2.3 From 626710c9d665ff381c7ec666b6a023f064ca5fef Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:05 +0900 Subject: sg: incorporate sg_build_direct into sg_start_req Calling blk_rq_map_user() at a single place is better than at different two places. It makes the code more understandable. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 80 +++++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 9a56c0d320bf..c0b6866eece9 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -202,7 +202,6 @@ static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len); static Sg_device *sg_get_dev(int dev); #ifdef CONFIG_SCSI_PROC_FS static int sg_last_dev(void); @@ -1628,16 +1627,17 @@ exit_sg(void) static int sg_start_req(Sg_request *srp, unsigned char *cmd) { - int res = 0; + int res; struct request *rq; Sg_fd *sfp = srp->parentfp; sg_io_hdr_t *hp = &srp->header; int dxfer_len = (int) hp->dxfer_len; int dxfer_dir = hp->dxfer_direction; + unsigned int iov_count = hp->iovec_count; Sg_scatter_hold *req_schp = &srp->data; Sg_scatter_hold *rsv_schp = &sfp->reserve; struct request_queue *q = sfp->parentdp->device->request_queue; - struct rq_map_data map_data; + struct rq_map_data *md, map_data; int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; SCSI_LOG_TIMEOUT(4, printk(KERN_INFO "sg_start_req: dxfer_len=%d\n", @@ -1660,38 +1660,43 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd) if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) return 0; - if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && - (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && - (!sfp->parentdp->device->host->unchecked_isa_dma) && + if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO && + dxfer_dir != SG_DXFER_UNKNOWN && !iov_count && + !sfp->parentdp->device->host->unchecked_isa_dma && blk_rq_aligned(q, hp->dxferp, dxfer_len)) - return sg_build_direct(srp, sfp, dxfer_len); + md = NULL; + else + md = &map_data; + + if (md) { + if (!sg_res_in_use(sfp) && dxfer_len <= rsv_schp->bufflen) + sg_link_reserve(sfp, srp, dxfer_len); + else { + res = sg_build_indirect(req_schp, sfp, dxfer_len); + if (res) + return res; + } - if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) - sg_link_reserve(sfp, srp, dxfer_len); + md->pages = req_schp->pages; + md->page_order = req_schp->page_order; + md->nr_entries = req_schp->k_use_sg; + } + + if (iov_count) + res = blk_rq_map_user_iov(q, rq, md, hp->dxferp, iov_count, + hp->dxfer_len, GFP_ATOMIC); else - res = sg_build_indirect(req_schp, sfp, dxfer_len); + res = blk_rq_map_user(q, rq, md, hp->dxferp, + hp->dxfer_len, GFP_ATOMIC); if (!res) { - struct request *rq = srp->rq; - Sg_scatter_hold *schp = &srp->data; - int iovec_count = (int) hp->iovec_count; - - map_data.pages = schp->pages; - map_data.page_order = schp->page_order; - map_data.nr_entries = schp->k_use_sg; - - if (iovec_count) - res = blk_rq_map_user_iov(q, rq, &map_data, hp->dxferp, - iovec_count, - hp->dxfer_len, GFP_ATOMIC); - else - res = blk_rq_map_user(q, rq, &map_data, hp->dxferp, - hp->dxfer_len, GFP_ATOMIC); + srp->bio = rq->bio; - if (!res) - srp->bio = rq->bio; + if (!md) { + req_schp->dio_in_use = 1; + hp->info |= SG_INFO_DIRECT_IO; + } } - return res; } @@ -1730,25 +1735,6 @@ sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) return tablesize; /* number of scat_gath elements allocated */ } -/* Returns: -ve -> error, 0 -> done, 1 -> try indirect */ -static int -sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len) -{ - sg_io_hdr_t *hp = &srp->header; - Sg_scatter_hold *schp = &srp->data; - int res; - struct request *rq = srp->rq; - struct request_queue *q = sfp->parentdp->device->request_queue; - - res = blk_rq_map_user(q, rq, NULL, hp->dxferp, dxfer_len, GFP_ATOMIC); - if (res) - return res; - srp->bio = rq->bio; - schp->dio_in_use = 1; - hp->info |= SG_INFO_DIRECT_IO; - return 0; -} - static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) { -- cgit v1.2.3 From c3919af2354fff673026dcbeac6f009d2ce5ceee Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:06 +0900 Subject: sg: remove sg_write_xfer sg_write_xfer was used to copy data from user space for WRITE commands. blk_rq_map_user_iov and blk_rq_map_user do the job so sg_write_xfer does nothing useful. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c0b6866eece9..07bd68331303 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -188,7 +188,6 @@ static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, int read_only, Sg_request **o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking); -static int sg_write_xfer(Sg_request * srp); static int sg_read_xfer(Sg_request * srp); static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); static void sg_remove_scat(Sg_scatter_hold * schp); @@ -736,11 +735,6 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, sg_finish_rem_req(srp); return k; /* probably out of space --> ENOMEM */ } - if ((k = sg_write_xfer(srp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_common_write: write_xfer, bad address\n")); - sg_finish_rem_req(srp); - return k; - } if (sdp->detached) { sg_finish_rem_req(srp); return -ENODEV; @@ -1816,32 +1810,6 @@ out: return -ENOMEM; } -static int -sg_write_xfer(Sg_request * srp) -{ - sg_io_hdr_t *hp = &srp->header; - Sg_scatter_hold *schp = &srp->data; - int num_xfer = 0; - int dxfer_dir = hp->dxfer_direction; - int new_interface = ('\0' == hp->interface_id) ? 0 : 1; - - if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) || - (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { - num_xfer = (int) (new_interface ? hp->dxfer_len : hp->flags); - if (schp->bufflen < num_xfer) - num_xfer = schp->bufflen; - } - if ((num_xfer <= 0) || (schp->dio_in_use) || - (new_interface - && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) - return 0; - - SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, k_use_sg=%d\n", - num_xfer, schp->k_use_sg)); - - return 0; -} - static void sg_remove_scat(Sg_scatter_hold * schp) { -- cgit v1.2.3 From 0b6cb26c6686f1f24607c41f0a6d21ce54191710 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:07 +0900 Subject: sg: remove sg_read_xfer sg_read_xfer was used to copy data to user space for READ commands. blk_rq_unmap_user does the job so sg_read_xfer does nothing useful. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 07bd68331303..df8bf67b171a 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -188,7 +188,6 @@ static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, int read_only, Sg_request **o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking); -static int sg_read_xfer(Sg_request * srp); static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); static void sg_remove_scat(Sg_scatter_hold * schp); static void sg_build_reserve(Sg_fd * sfp, int req_size); @@ -523,8 +522,11 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) err = -EFAULT; goto err_out; } - err = sg_read_xfer(srp); - err_out: + if (srp->bio) { + err = blk_rq_unmap_user(srp->bio); + srp->bio = NULL; + } +err_out: sg_finish_rem_req(srp); return (0 == err) ? count : err; } @@ -1831,31 +1833,6 @@ sg_remove_scat(Sg_scatter_hold * schp) memset(schp, 0, sizeof (*schp)); } -static int -sg_read_xfer(Sg_request * srp) -{ - sg_io_hdr_t *hp = &srp->header; - Sg_scatter_hold *schp = &srp->data; - int num_xfer = 0; - int dxfer_dir = hp->dxfer_direction; - int new_interface = ('\0' == hp->interface_id) ? 0 : 1; - - if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir) - || (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { - num_xfer = hp->dxfer_len; - if (schp->bufflen < num_xfer) - num_xfer = schp->bufflen; - } - if ((num_xfer <= 0) || (schp->dio_in_use) || - (new_interface - && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) - return 0; - - SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", - num_xfer, (int)hp->iovec_count, schp->k_use_sg)); - return 0; -} - static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) { -- cgit v1.2.3 From 4677735f03f5b6b6f2182f457a921855cadfb85b Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Tue, 2 Sep 2008 22:50:08 +0900 Subject: sg: remove unnecessary blk_rq_unmap_user blk_rq_unmap_user in sg_finish_rem_req can take care of all the cases. Signed-off-by: FUJITA Tomonori Signed-off-by: Jens Axboe --- drivers/scsi/sg.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index df8bf67b171a..ba9b9bbd4e73 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -522,10 +522,6 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) err = -EFAULT; goto err_out; } - if (srp->bio) { - err = blk_rq_unmap_user(srp->bio); - srp->bio = NULL; - } err_out: sg_finish_rem_req(srp); return (0 == err) ? count : err; @@ -1844,9 +1840,6 @@ sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) if ((!outp) || (num_read_xfer <= 0)) return 0; - blk_rq_unmap_user(srp->bio); - srp->bio = NULL; - num = 1 << (PAGE_SHIFT + schp->page_order); for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { if (num > num_read_xfer) { -- cgit v1.2.3